About Blog Notes Photos Portfolio

Adding HTTP Basic Auth for Traefik and Nginx-Proxy Routes

Security ยท Oct 25, 2019

The core function of a reverse proxy is to abstract away a bunch of services placed behind it. It analyzes incoming HTTP requests and forwards them to the right services.

HTTP proxy is a single point of entrance to all of the HTTP traffic which makes it a good place to perform some additional tasks and checks and one of them is limiting access to certain services. Some services might not have their own access control systems and some services might benefit from an additional level of protection. HTTP has many authentication schemes but most HTTP proxies support only RFC7617 “Basic” auth because it’s easy to implement without deviating too much from their main task of redirecting requests.

Illustration by CMDR Shane

Example of a Basic HTTP Auth Flow

Here is an example of how such a scheme may protect a certain endpoint:

Request URL: https://example.com
Request method: GET

Status code: 401 Unauthorized

Response headers:

HTTP/2.0 401 Unauthorized
content-type: text/plain
www-authenticate: Basic realm="traefik"

The most important fields here are status code and www-authenticate header. They tell us that we might access this page only if we can provide a valid username and password combination so your browser will likely ask you to provide this information if it gets such a response and once you enter it, it’ll try to access this endpoint again using the credentials you provided.

More details: HTTP Authentication

More details: WWW-Authenticate

This scheme can be used to prevent unauthorized access to any web page behind a reverse proxy and it can also be used in conjunction with other auth systems. Let’s say that we want to use Adminer, a popular front end for managing relational databases. It has it’s own auth screen which asks you for your DB credentials and uses them to access your database. Your database might have a good password and Adminer might be secure enough so no one can expose the fact that it’s publicly accessible but why let strangers see it and ‘probe’ our private networks? Adding another auth layer might be a good idea in that case.

Both nginx-proxy and Traefik allow us to implement basic HTTP auth for any domain or subdomain. It can also be used to restrict access to specific URI’s. Both of those reverse proxy solutions use Apache htpasswd format when is comes to specifying the list of allowed users and their password hashes.

Adding HTTP Basic Auth for Traefik 2

First of all, it’s important not to use htpasswd generators available on various websites. They can’t be trusted and it’s not hard to generate auth credentials on your own. The only tool that is necessary is htpasswd, which is usually distributed as a part of apache2-utils package. You might want to check if it’s already available on your system and, if not, just run the following command (assuming you have a Debian based system such as Ubuntu, and if you have other Linux system, you probably won’t need detailed instructions anyway):

sudo apt install apache2-utils

Now we can use the following command to generate a new username and password combination:

htpasswd -nBC 10 <username>

It will ask you to enter a password twice and then display the result in a following format:

htpasswd -nBC 10 admin

New password: admin
Re-type new password: admin

admin:$2y$10$zi5n43jq9S63gBqSJwHTH.nCai2vB0SW/ABPGg2jSGmJBVRo0A.ni

The resulting format is quite straightforward: username:hashed_Password

Let’s do a quick review of those -nBC arguments, because it’s not a good idea to execute a command if you don’t know what exactly it supposed to do:

-n

Manpage entry:

Display the results on standard output rather than updating a file. This is useful for generating password records acceptable to Apache for inclusion in non-text data stores. This option changes the syntax of the command line, since the passwdfile argument (usually the first one) is omitted. It cannot be combined with the -c option.

-B

Manpage entry:

Use bcrypt encryption for passwords. This is currently considered to be very secure.

-C

This flag is only allowed in combination with -B (bcrypt encryption). It sets the computing time used for the bcrypt algorithm (higher is more secure but slower, default: 5, valid: 4 to 31).

Please note that 10 is an arbitrary number. I think that the default cost factor of 5 is too weak and was never considered secure. Cost factor of 5 results in 2^5=32 iterations and cost factor of 10 results in 1024 iterations which makes it much harder to steal your password. You should consider increasing the cost factor if you have enough computing power to ‘exchange’ for better security. It’s also important to have a strong password, because cost factor alone can’t protect you if your password is trivial to guess.

Now, since we know how to create a secure combination of login and password, let’s use it to restrict unauthorized access to Traefik endpoints using Docker Compose:

version: "3.7"
services:

  traefik:
    image: traefik:v2.0
    container_name: traefik
    labels:
      - traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_USER}:${TRAEFIK_PASSWORD_HASH}

This example uses environment variables that should be located in the .env file in the same directory with docker-compose.yml:

TRAEFIK_USER=admin
TRAEFIK_PASSWORD_HASH=$2y$10$zi5n43jq9S63gBqSJwHTH.nCai2vB0SW/ABPGg2jSGmJBVRo0A.ni

Now you can add newly created “auth” middleware to any route and that route will be “protected” with basic auth.

Adding HTTP Basic Auth for nginx-proxy

In case of nginx-proxy, we need to store our usernames and passwords in a .htacess file named using a special convention.

Let’s say we want to restrict access to a DB frontend located at db.example.com and we’d like to utilize built-in capabilities of nginx-proxy in order to do that. First of all, let’s generate a file named db.example.com using htpasswd utility:

htpasswd -cBC 10 db.example.com admin

New password: admin
Re-type new password: admin

This should create a file called db.example.com and you can add more users to it if you wish so. I’ve already mentioned why we need BC 10 in the arguments and -c just means “write the results to a file and I will provide the file name”.

Now you need to bind this file to a nginx-proxy container. This file should be located in the /etc/nginx/htpasswd folder in a container, here is an example:

version: "3.7"
services:

  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - type: bind
        source: ./configs/htpasswd # we assume db.example.com file is in this folder on a host computer
        target: /etc/nginx/htpasswd
        read_only: true
      - type: bind
        source: /var/run/docker.sock
        target: /tmp/docker.sock
        read_only: true
    labels:
- "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true"

That’s it, now nginx-proxy should know that db.example.com is a restricted access domain and it should take the list of allowed users from a file located at /etc/nginx/htpasswd/db.example.com

Conclusion

Adding basic HTTP auth is an easy and secure way to restrict access to certain endpoints located behind a reverse proxy. In this post, we covered how to create secure login and password hash combinations using htpasswd and bcrypt and how to add them to popular reverse-proxy implementations such as Traefik and nginx-proxy.

Security   Reverse Proxy   Nginx Proxy   Traefik   HTTP   Authentication   bcrypt

This page doesn't show ads and the reasons are simple:

  • Most people don't want to see ads (what a surprise)
  • Ads can track you and violate your privacy
  • Ads is the main reason why many websites are so slow

If you find this content valuable or you want to see more content like this, you can leave a tip with bitcoin:

34CXtg7c4Vbw8DZjAwFQVsrbu9eDEbTzbA
bitcoin tips QR