Project: Password Protect a Website with nginx

nginx
nginx logo

I needed to keep a few older websites online for a short little while, but didn’t want to leave them wide open in case older CMS systems were vulnerable – so I decided to protect them with password.

What is nginx?

nginx (pronounced Engine-Ex) is a webserver, reverse-proxy and caching solution powering a massive portion of the Internet websites today. It’s a lightweight web-server with non-locking implementation, meaning it can server impressive amounts of traffic with humble resource requirements.

nginx was acquired by F5 in 2019.

I’ll be writing a lot more about nginx in 2020, simply because I’m finally catching up with my dedicated hosts infrastructure and will be getting the time to document my setup and best practices.

Password Protecting in nginx

There’s a few steps to protecting a website using nginx (steps are similar but implemented differently in Apache web server):

  1. Decide and create/update the passwords file
  2. Decide on the username and password
  3. Generate password hash and add entry to the passwords file
  4. Update webserver configuration to specify password protection

Because websites are configured as directory locations, you have a choice of protecting the whole website like www.unixtutorial.org or just a part (subdirectory) of it, like www.unixtutorial.org/images.

INTERESTING: even though it’s commonly referred to as password protecting websites, what actually happens is you protect with username and password. So when you’re trying to open a protected website, you get a prompt like this, right there in your browser:

Password protection prompt

Password file and username/password Configuration

Most of the time website access is controlled by files named htpasswd. You either create default password file in /etc/nginx/htpasswd location, or create a website specific version like /etc/nginx/unixtutorial.htpasswd.

You can create a file using touch command:

# touch /etc/nginx/unixtutorial.htpasswd

Or better yet, use the htpasswd command to do it. But because htpasswd is part of Apache tools, you may have to install it first:

$ sudo yum install httpd-tools

When you run the htpasswd command, you specify two parameters: the password file name and the username you’ll use for access.

If the password file is missing, you’ll be notified like this:

$ sudo htpasswd /etc/nginx/htpasswd unixtutorial 
htpasswd: cannot modify file /etc/nginx/htpasswd; use '-c' to create it.

And yes, adding the -c option will get the file created:

$ sudo htpasswd -c /etc/nginx/htpasswd unixtutorial
New password:
Re-type new password:
Adding password for user unixtutorial

Now, if we cat the file, it will show the unixtutorial user and the password hash for it:

$ cat /etc/nginx/htpasswd
unixtutorial:$apr1$bExTryjo/$uxRop/uv5UwXvWl4EM5gv0

IMPORTANT: although this file doesn’t contain actual passwords, only their encrypted hashes, it can still be used to guess your passwords on powerful systems – so take the usual measures to protect access to this file.

Update Website Configuration with Password Protection

I’ve got the following setup for this old website in my example:

server {
     listen      *:80;
     server_name forum.reys.net;
     keepalive_timeout    60;

     access_log /var/log/nginx/forum.reys.net/access.log multi_vhost;
     error_log /var/log/nginx/forum.reys.net/error.log;

location / {
     include "/etc/nginx/includes/gzip.conf";
     proxy_pass  http://172.31.31.47:80;

     include "/etc/nginx/includes/proxy.conf";
     include "/etc/nginx/includes/headers.conf";
     }
}

Protection is done on the location level. In this example, location / means my whole website is protected.

So right in front of the proxy_pass entry, I’ll add my password protection part:

auth_basic "Restricted";
auth_basic_user_file /etc/nginx/htpasswd;

As you can see, we’re referring to the password file that we created earlier. The auth_basic “Restricted” part helps you to configure a specific message (instead of word Restricted) that will be shown during username/password prompt.

That’s how the password protected part will look:

location / {
     include "/etc/nginx/includes/gzip.conf";
     proxy_pass  http://172.31.31.47:80;

     auth_basic "Restricted";
     auth_basic_user_file /etc/nginx/htpasswd;

     include "/etc/nginx/includes/proxy.conf";
     include "/etc/nginx/includes/headers.conf";
     }

Save the file and restart nginx:

$ sudo service restart nginx

Now the website https://forum.reys.net is password protected!

See Also




Docker: Stop All Containers

docker-containers-unixtutorial

Now and then, especially when working on a development environment, you need to stop multiple Docker containers. Quite often, you need to stop all of the currently running containers. I’m going to show you one of the possible ways.

Docker: Stop a Container

You need to use a container name or container ID with the docker stop command.

For example, I have an nginx load balancer container:

root@s5:~ # docker ps -f name=nginx
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
32cd3e477546 nginx:latest "nginx -g 'daemon of…" 11 months ago Up About a minute 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp nginx

Based on this output, I can stop my nginx container like this:

root@s5:~ # docker stop nginx
nginx

… or like that:

root@s5:~ # docker stop 32cd3e477546
32cd3e477546

Docker: Stop Multiple Containers

Since I also have a MariaDB container named db, I might need stop it together with nginx.

Here’s the info on the db container:

root@s5:~ # docker ps -f name=db
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c745794419a9 mariadb:latest "docker-entrypoint.s…" 9 months ago Up 4 seconds 3306/tcp db

If I ever decide to stop both nginx and db together, I can do it like this:

root@s5:~ # docker stop nginx db
nginx
db

Docker: Stop All Containers

As you can see from previous examples, docker stop simply takes a list of containers to stop. If there’s more than one container, just use space as a delimiter between container names or IDs.

This also allows us to use a clever shell expansion trick: you can some other command, and pass its output to the docker stop container.

For instance, this shows us the list of all the IDs for currently running Docker containers:

root@s5:~ # docker ps -q
510972d55d8c
1b8b1657736e
c745794419a9
32cd3e477546

What we can do now is pass the result of this command as the parameter for the docker stop command:

root@s5:~ # docker stop $(docker ps -q)
510972d55d8c
1b8b1657736e
c745794419a9
32cd3e477546

And just to check, running docker ps now won’t show any running containers:

root@s5:~ # docker ps -q

IMPORTANT: make sure you double-check what you’re doing! Specifically, run docker ps -q, compare it to docker ps, this kind of thing. Because once containers stopped you may not have an easy way to generate the list of same containers to restart.

In my case, I’m just specifying them manually as the parameters for docker start:

root@s5:~ # docker start 510972d55d8c 1b8b1657736e c745794419a9 32cd3e477546
510972d55d8c
1b8b1657736e
c745794419a9
32cd3e477546

That’s it for today! Hope you enjoyed this quick how-to, let me know if you have any questions, Docker and whatnot!

See Also