Self-Hosted SSO with Authelia and NGINX
Authelia is an open-source authentication and authorization server. In conjunction with an NGINX proxy, all pf your proxied apps and services can use the the same login credentials and login session - that is sign in once and have access to all you services without signing in again. It also offers 2FA via email, Google Authenticator, Duo, and Yubikey.
Scenario
This guide outlines setting up Authelia in the following scenario:
- On a webserver running Ubuntu 18.04s.
- NGINX is used to proxy a number of apps and services.
- Authelia will be run in a docker container.
- Authelia will be deployed in the "light" deployment.
Prerequisites
This guide assumes you have done or know how to do the following:
- You have created a DNS entry for the subdomain
auth.example.compointing to your server. - Your apps and services are proxied to either your root domain or some subdomains.
- You are familiar with adding SSL certificates to NGINX (If not, check out Let's Encrypt).
Authelia Configuration & Installation
-
The first step is to install the
docker,docker-composeandgitpackages to prepare for the installation of Authelia.We want to install Docker from the official Docker repository to ensure we get the latest version. The following set of commands prepares the repository.
sudo apt update sudo apt install apt-transport-https ca-certificates \ curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] \ https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" sudo apt-get updateNow to actually install the packages:
sudo apt-get install docker-ce docker-ce-cli docker-compose gitAll that's left to do is add your user to the
dockergroup so you can manage docker without having to use sudo all the time:sudo usermod -aG docker ${USER} su - ${USER} -
Now we'll begin installing Authelia. First we'll create a directory to contain all of Authelia's configuration files and any dependencies. In my case it will be in my users home directory.
mkdir ~/authelia cd ~/authelia -
To get started were going to make a
docker-compose.ymlto define the docker containers we're creating. Create a file nameddocker-compose.ymlin your editor of choice and paste the following:version: '3.3' networks: net: driver: bridge services: authelia: image: authelia/authelia container_name: authelia volumes: - ./authelia:/config networks: - net ports: - 9091:9091 restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=America/Vancouver redis: image: redis:alpine container_name: redis_authelia volumes: - ./redis:/data networks: - net expose: - 6379 restart: unless-stopped environment: - TZ=America/Vancouver - PUID=1000 - PGID=1000Now we'll work through the contents of this file and I'll point out the changes you'll need to make depending on your setup.
The first several lines just specify the oldest version of docker engine this file will work with, and the type of network driver docker should use. You probably won't need to change anything here.
Under
servicesyou will find Authelia itself. As well as redis, a database system Authelia uses. As-is in the file each service will have
their respective files in subdirectories of the directorydocker-compose.ymlis is. If you prefer some other setup, those can be changed.Change PUID and GUID on lines 19, 20, 35, and 36 to your users id's (find your own id's with
id $user).Change
America\Vancouveron lines 21 and 34 to your local timezone (find a list of timezones known to you system withtimedatectl list-timezones). -
Now we'll make the first of Authelia's configuration files.
mkdir ~/authelia/authelia cd ~/authelia/autheliaCreate and open
configuration.ymlin your editor of choice and paste the following:server: host: 0.0.0.0 port: 9091 log: level: info jwt_secret: <your-secret-key> default_redirection_url: https://apps.example.com authentication_backend: file: path: /config/users_database.yml access_control: default_policy: deny rules: - domain: - "auth.example.com" policy: bypass - domain: - "example.com" - "*.example.com" - "app.example.com" policy: one_factor session: name: authelia_session secret: <your-secret-key> expiration: 12h # 12 hours inactivity: 45m # 45 minutes remember_me_duration: 1M # 1 month domain: example.com redis: host: redis_authelia port: 6379 regulation: max_retries: 3 find_time: 5m ban_time: 15m storage: encryption_key: <your-encryption-key> local: path: /config/db.sqlite3 notifier: #filesystem: #filename: /config/notification.txt smtp: username: test@gmail.com password: <gmail_password> host: smtp.gmail.com port: 587 sender: test@gmail.comThe first thing to replace in
configuration.ymlis<your-secret-key>afterjwt_secreton line 6,secreton line 27, and<your-encryption-key>on line 43. You should replace these with unique strings. If you want, the command below can be used to generate random seeds. Just make usre to use unique strings for each of the three screts.head /dev/urandom | tr -dc A-Za-z0-9 | head -c64The rest of the options you'll likely want to change are:
default_redirection_url: The URL users who navigate directly to the login page will be redirected to after login.access_control: This is the area of the configuration where you set what kind of authentication is required for different subdomains. Rules are defined as a list of domains and the policy they follow.- In the configuration template the
default_policyisdenyand there are two domain rules:- The first is a
bypassrule allowing anyone to the auth page and the root of the domain. Change these to match your domain.
- The first is a
policy: There are four policy options:deny- No access is permitted.one_factor- Login with username and password.two_factor- Login with username, password, and 2FA.bypass- No login required.
- For more details on the access control list, see the docs.
- In the configuration template the
session: This is where cookie setting are set.expiration: How long before the cookie expires.inactivity: Mow much inactivity will cause the cookie to expire.remember_me_duration: How long you will stay signed in for after ticking "remember me".
regulation: If there aremax_retriesfailed login attempts infind_time, you will be banned forban_time.notifier: This is how 2FA codes will be delivered. The template config is setup for emails to be sent from a Gmail address. You can fill this out with your Gmail information, or some other email services smtp settings. Alternatively, comment out all the email settings and uncomment the filesystem ones. This way 2FA codes will just be saved to the listed file.
For more information or additional configuration options, you can refer to the docs or the sample config.
-
The last bit of configuration is creating the user data base. In the same directory as the
configuration.ymlfile, create a new file calledusers_database.ymland put the following into it.users: <username>: displayname: "Your Name" # Generate with docker run authelia/authelia:latest authelia hash-password <your-password> password: "<hashed-password>" email: <your-email> groups: - adminsReplace
<username>with your desired username,Your Namewith your full name and<your-email>with your email. To get the hashed-password to replace<hashed-password>with, use the following command.docker run authelia/authelia:latest authelia hash-password <your-password>You can add as many users as you like under the
users:key. You can also add yourself or any other users to more groups if you want to do access control based on groups. -
We are now ready to spin up Authelia.
cdback to the directory containingdocker-compose.ymland run the following command to create and run the containers.docker-compose up -dIf everything went smoothly, you should be able to navigate to
<webserver-ip>:9091and be presented with Authelia's login prompt. Try the username and password you setup in step 5!
Adding Authelia to NGINX
Now that Authelia is setup and running, it's time to tie it into NGINX.
-
The first thing we need to do is create a new site configuration for
auth.example.com. For example:sudo vim /etc/sudo vim /etc/nginx/sites-available/auth.example.comOf course, replace
example.comwith your domain name. Paste the following into the new configuration file:server { server_name auth.example.com; listen 80; return 301 https://$server_name$request_uri; } server { server_name auth.example.com; listen 443 ssl http2; location / { set $upstream_authelia http://127.0.0.1:9091; proxy_pass $upstream_authelia; client_body_buffer_size 128k; #Timeout if the real server is dead proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Advanced Proxy Config send_timeout 5m; proxy_read_timeout 360; proxy_send_timeout 360; proxy_connect_timeout 360; # Basic Proxy Config proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Uri $request_uri; proxy_set_header X-Forwarded-Ssl on; proxy_redirect http:// $scheme://; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_cache_bypass $cookie_session; proxy_no_cache $cookie_session; proxy_buffers 64 256k; # If behind reverse proxy, forwards the correct IP set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.0.0.0/8; set_real_ip_from 192.168.0.0/16; set_real_ip_from fc00::/7; real_ip_header X-Forwarded-For; real_ip_recursive on; } }Replace all occurrences of
example.comwith your domain name. Then to enable this new site, run:sudo ln -s /etc/nginx/sites-available/auth.example.com /etc/nginx/sites-enabled/auth.example.com sudo systemctl reload nginxNow is a good time to get an SSL certificate for
auth.example.com. An example of how to do this with letsencrypt's certbot:sudo certbot run -d auth.example.com --nginxNow try navigating to
auth.example.com. If all went well, you should again see the Authelia login prompt. -
Now we'll make a couple configuration snippets to make adding Authelia to our sites easier. Create and open a file
/etc/nginx/snippets/authelia.confand the past the following into it.# Virtual endpoint created by nginx to forward auth requests. location /authelia { internal; set $upstream_authelia http://127.0.0.1:9091/api/verify; proxy_pass_request_body off; proxy_pass $upstream_authelia; proxy_set_header Content-Length ""; # Timeout if the real server is dead proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # [REQUIRED] Needed by Authelia to check authorizations of the resource. # Provide either X-Original-URL and X-Forwarded-Proto or # X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Uri or both. # Those headers will be used by Authelia to deduce the target url of the user. # Basic Proxy Config client_body_buffer_size 128k; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Uri $request_uri; proxy_set_header X-Forwarded-Ssl on; proxy_redirect http:// $scheme://; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_cache_bypass $cookie_session; proxy_no_cache $cookie_session; proxy_buffers 4 32k; # Advanced Proxy Config send_timeout 5m; proxy_read_timeout 240; proxy_send_timeout 240; proxy_connect_timeout 240; }This snippet will be added to the server block of any site we secure with Authelia. We need one more snippet. Create and open
/etc/nginx/snippets/auth.conf, then paste the following in.# Basic Authelia Config # Send a subsequent request to Authelia to verify if the user is authenticated # and has the right permissions to access the resource. auth_request /authelia; # Set the `target_url` variable based on the request. It will be used to build the portal # URL with the correct redirection parameter. auth_request_set $target_url $scheme://$http_host$request_uri; # Set the X-Forwarded-User and X-Forwarded-Groups with the headers # returned by Authelia for the backends which can consume them. # This is not safe, as the backend must make sure that they come from the # proxy. In the future, it's gonna be safe to just use OAuth. auth_request_set $user $upstream_http_remote_user; auth_request_set $groups $upstream_http_remote_groups; auth_request_set $name $upstream_http_remote_name; auth_request_set $email $upstream_http_remote_email; proxy_set_header Remote-User $user; proxy_set_header Remote-Groups $groups; proxy_set_header Remote-Name $name; proxy_set_header Remote-Email $email; # If Authelia returns 401, then nginx redirects the user to the login portal. # If it returns 200, then the request pass through to the backend. # For other type of errors, nginx will handle them as usual. error_page 401 =302 https://auth.example.com/?rd=$target_url;All you need to do here is replace
example.comon the last line with your domain. -
Now we can edit our existing
example.comNGINX site config to add Authelia.- You will include the snippet
authelia.confinto the main server block. - You will include the snippet
auth.confinto any location block you want to protect.
For example:
server { server_name app.example.com; listen 80; return 301 https://$server_name$request_uri; } server { server_name app.example.com; listen 443 ssl http2; include snippet/ssl.conf; include snippets/authelia.conf; # Authelia auth endpoint location / { proxy_pass http://proxied:service; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; include snippets/auth.conf; # Protect this endpoint } }You should just be able to add the snippets in the correct locations to your existing configuration.
Test the configuration and then reload NGINX:
sudo nginx -t sudo systemctl reload nginxCongratulations! If everything your done!
- You will include the snippet
Test It Out
Try navigating to any protected endpoint you configured, you should be presented with the login prompt. After entering your credentials, you should be redirected to your destination. If you setup multiple endpoints, going to a second one should not ask for a password because you are still logged in.
If you want to logout, you can navigate to auth.example.com and you will be presented with a logout button. auth.example.com/logout will also accomplish the same.
Down the line: Updating
Because we used docker-compose to bring up this system updating any part of it is very simple. Just run the following commands to pull the latest images and then rebuild and start the containers.
cd ~/authelia
docker-compose pull
docker-compose up --force-recreate --build -d
docker image prune -f
Resources
- You can download all the configuration templates here.
- See the official Authelia docs here.
- Have a look at the official, complete sample config here.
Email me with any suggestions or feedback!