Indice

Install Authelia on Debian without Docker

In this guide I will show how to install Authelia on a Debian system, using Nginx as reverse proxy in front of it.

Installation

Authelia provides a Debian repository. First install the gpg key:

curl -fsSL https://www.authelia.com/keys/authelia-security.gpg -o /etc/apt/keyrings/authelia-security.gpg

Then add the repository:

echo \                 
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/authelia-security.gpg] https://apt.authelia.com stable main" | \
  sudo tee /etc/apt/sources.list.d/authelia.list > /dev/null

Then it's a matter of:

apt update
apt install authelia

Preparing secrets

Before we start editing the configuration file, it's useful to create files containing secrets (kind of a password) to reference in the config file. This way the actual password/secret is not written in plain text inside the configuration file, which may increase security. We will place all secrets in a directory within the Authelia configuration directory:

mkdir /etc/authelia/secrets
cd /etc/authelia/secrets

Secrets are 1-line files containing a long string (recommended is at least 64 characters). The string can be created with the following command:

authelia crypto rand --length 64 --charset alphanumeric

When pasting the string inside the file, make sure that it's surrounded by the charater ' (before and after the string), so that it'll parse properly in the yaml file.

You should create these secrets files:

In addition to these secrets, you should create (if needed) files also for your SMTP user (most definitely needed) and database passwords. In my case:

If you pasted the string in the file with nano, the editor will create a trailing new line that needs to be removed:

printf "%s" "$(< hmac_secret)" > hmac_secret
printf "%s" "$(< jwt_secret)" > jwt_secret
printf "%s" "$(< session_secret)" > session_secret
printf "%s" "$(< storage_secret)" > storage_secret
printf "%s" "$(< smtp_password)" > smtp_password
printf "%s" "$(< mysql_password)" > mysql_password

Generating key pairs

If you want to use Authelia as an OpenID Connector (OIDC) provider, which can be leveraged by many web applications, you will need to generate cryptographic key pairs, at least one for each algorithm that you plan to implement (at least one needs to be an RSA Private key configured with the RS256 algorithm). As a starting point, let's create a key for the default algorithm, and for a more secure one (ES512). As the output file names of the key generation program are the same, we'll also rename them to avoid overwriting them. Inside the secrets directory, issue these commands:

authelia crypto pair rsa generate
mv private.pem private-RS256.pem
mv public.pem public-RS256.pem
authelia crypto pair ecdsa generate -b P521
mv private.pem private-ES512.pem
mv public.pem public-ES512.pem

Configuration file

Authelia's configuration file in /etc/authelia/configuration.yml is extensively commented. I also recommend you read the whole installation and configuration guides on Authelia's website. Below I will report what I have configured on my system, with some comments as to why. I'm going to split the configuration file into sections, and each section go right after the previous in the file. Remember thta this is a yaml file, therefore indentation matters!

Server section

The server section for a normal installation is fairly simple, and you just need to specify the local address for the server. Before the server section you can also define the theme for the login website. The endpoints section and all that comes after is needed for Authelia to work behind an Nginx proxy.

# yamllint disable rule:comments-indentation
---
theme: 'auto'
server:
  address: 'tcp://127.0.0.1:9091/'
  endpoints:
    authz:
      auth-request:
        implementation: 'AuthRequest'

Log section

It may be useful to explicitly declare where you want the log file to be stored, so that you can e.g. read it later with fail2ban for increased security. There are several log levels, which can be written as json or text. keep_stdout: false prevents the log from being written to the standard output in addition to the log file.

log:
  level: 'info'
  format: 'text'
  file_path: '/var/log/authelia.log'
  keep_stdout: false

Webauthn section

The default configuration for webauthn is usually fine, however you can opt to customise the display name for this method (the default name is “authelia”.

webauthn:
  display_name: 'my custom name'

Duo section

Unless you use Duo services (they also have a free plan in case), you can disable this. This configuration disables the service explicitly, but if you don't write anything it's also considered disabled by default.

duo_api:
  disable: true

Identity validation section

This section provides extra security. A part on elevated session (not described here) requires the user to log in again before performing security-changing actions on their profile. The second part enables extra security on password resets, which we will configure here. In particular, we will only add the secret to enable the feature, while we'll use the default values for the other options (in case they change with new Authelia versions to a more secure standard). For the secret, refer to the secret creation that we covered earlier.

identity_validation:
  reset_password:
    jwt_secret: {{fileContent "/etc/authelia/secrets/jwt_secret"}}

Authentication backend section

This section is very important, because it tells Authelia where to look for authorised users. Unless you have an LDAP server, a file containing the user list is the way to go.

authentication_backend:
  password_reset:
    disable: false
  password_change:
    disable: false
  file:
    path: '/etc/authelia/users.yml'

Password policy section

This section sets the minimum password strength that the users can set. The modes are standard (upper/lowercase, number, and special character) and zxcvbn. I feel that the latter is more flexible because it evaluates password strength not only on the presence of certain characters, but as a whole. Setting it to 3 is more secure, but 2 will upset less your less tech-savvy users while still being reasonably secure. You can test password strengths at this page.

password_policy:
  zxcvbn:
    enabled: true
    min_score: 2

Access control section

This is one of the key sections of the configuration file, as it defines the login requirements of each service that Authelia is supposed to protect. The Authelia configuration website recommends to start with something easy (one factor authentication) that can be applied to all services, but here I will show an example with a bit more complex definition. It is important to know that Authelia reads rules from top to bottom, and the first that fully matches is applied. In this example, we will exclude from the check two subdomains, then for all the others we will require that the user named “admin” uses two-factor authentication, while all other users can use one-factor.

access_control:
  default_policy: 'deny'
  rules:
    - domain:
        - 'sub1.mydomain.tld'
        - 'sub2.mydomain.tld'
      policy: 'bypass'
    - domain: '*.mydomain.tld'
      subject:
        - ['user:admin']
      policy: 'two_factor'
    - domain: '*.mydomain.tld'
      policy: 'one_factor'

Session section

This section is equally important because it defines the authentication cookie served by Authelia. We will use the secret generated earlier, and we will also personalise the name of the cookie. By default Authelia saves the user's session in the server's memory. However, if you have a Redis (or Valkey) server, you can choose to use it, which is better.

session:
  secret: {{fileContent "/etc/authelia/secrets/session_secret"}}
  name: 'mydomain.tld_session'
  cookies:
    - domain: 'mydomain.tld'
      authelia_url: 'https://auth.mydomain.tld'
      default_redirection_url: 'https://www.mydomain.tld'
  redis:
    host: '127.0.0.1'
    port: 6379

Regulation section

The regulation section is useful to mitigate brute force attacks. It bans an username and/or an IP address for a length or time after a number or unsuccessful login attempts within a timeframe.

regulation:
  modes:
    - 'user'
    - 'ip'
  max_retries: 3
  find_time: '2m'
  ban_time: '10m'

Storage section

The storage section defines the database in which Authelia will store user preferences, 2FA device handles and secrets, authentication logs, etc. It supports PostgreSQL, MariaDB, and SQLite, with the first being recommended and the latter to be used as a last resource. I don't have Posgres on my server but I have MariaDB, so I will use that. Whether you're using Postgres or MariaDB, make sure to create a dedicated account and related database for Authelia (it's important to read the documentation to configure the database correctly). We will also enter the secret (minimum of 20 characters) created before.

storage:
  encryption_key: {{fileContent "/etc/authelia/secrets/storage_secret"}}
  mysql:
    address: 'tcp://127.0.0.1:3306'
    database: 'authelia'
    username: 'authelia'
    password: {{fileContent "/etc/authelia/secrets/mysql_password"}}

Notifier section

The notifier section specifies how Authelia sends notifications to users (e.g., for password resets or Webauthn notifications). I recommend setting up an email address and using the SMTP method. The filesystem method is only for testing purposes. Your SMTP server must support at least starttls.

notifier:
  smtp:
    address: 'submission://smtp.mydoman.tld:587'
    timeout: '10s'
    username: 'authelia@mydoman.tld'
    password: {{fileContent "/etc/authelia/secrets/smtp_password"}}
    sender: '"Authelia" <authelia@mydoman.tld>'
    identifier: 'auth.mydoman.tld'
    subject: "[Authelia] {title}"
    startup_check_address: 'authelia@mydoman.tld'

Identity providers section

If you want Authelia to act as an OIDC provider, which is recommended because it can be leveraged by many applications, you need to configure this section. You will also need to configure each client application which will use Authelia as OIDC provider (here an example is given for paperless-ngx; as in the access control section, we create a policy that allows single factor authentication for regular users, but forces two factor authentication for the user “admin”). To properly configure this we'll need to provide the hmac_secret and the key pairs created earlier.

identity_providers:
  oidc:
    hmac_secret: {{fileContent "/etc/authelia/secrets/hmac_secret"}}
    jwks:
      - key: {{ secret "/etc/authelia/secrets/private-RS256.pem" | mindent 10 "|" | msquote }}
      - key: {{ secret "/etc/authelia/secrets/private-ES512.pem" | mindent 10 "|" | msquote }}
    authorization_policies:
      onefactor_twoadmin:
        default_policy: 'one_factor'
        rules:
          - policy: 'two_factor'
            subject:
              - ['user:admin']
    clients:
      - client_id: 'paperless'
        client_name: 'paperless-ngx'
        client_secret: 'hashed_secret'
        public: false
        authorization_policy: 'onefactor_twoadmin'
        require_pkce: true
        pkce_challenge_method: 'S256'
        redirect_uris:
          - 'https://paperless.mydomain.tld/accounts/oidc/authelia/login/callback/'
        scopes:
          - 'openid' 
          - 'profile'
          - 'email'  
          - 'groups'   
        response_types:
          - 'code'  
        grant_types:
          - 'authorization_code'
        access_token_signed_response_alg: 'none'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_basic'
...

You can create the client_secret for each client with the following command, which will create the unencrypted secret (to enter in the client's OIDC configuration) and its hashed version to provide to Authelia:

authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986

Test the configuration file

After editing the configuration file, we can test that all the secrets parse correctly by running:

authelia config template --config.experimental.filters=template --config=configuration.yml

which should output the configuration file with all the secrets in plain text and with the correct indentation. If not, modify the configuration file to fix the issues. Before enabling Authelia as a system service, we can do a quick test run to check if it can start correctly. Run this from the command line:

authelia --config /etc/authelia/configuration.yml --config.experimental.filters=template

If it doesn't throw an error, everything should be fine. The first time you execute it you will actually see an error as the program creates the users file, but that's not a problem. Before we can start Authelia as a system service, we need to fix some file permissions so that it can run with the appropriate user:

chown -R authelia:authelia /etc/authelia
chown authelia:authelia /var/log/authelia.log

Enable and configure the systemd service

Authelia comes with a systemd service file that needs to be enabled and then edited to allow for the secrets templating that we used in the configuration file to work. Rather than editing the file directly (which would get overwritten in subsequent updates), we'll use systemd tools to do it properly. First, let's enable the service:

cd /usr/lib/systemd/system
systemctl enable authelia.service
systemctl edit authelia.service

Your default cli text editor will open at this point. We'll need to put our modifications at the top, between the first and the second comment block. This is what to add:

[Service]
Environment=X_AUTHELIA_CONFIG_FILTERS=template

After you're done editing the override line, you can refresh systemd (not always necessary, but it doesn't hurt) and restart the service:

systemctl daemon-reload
systemctl restart authelia

Authelia should be up and running now on port 9091, listening on localhost.

Nginx configuration

Now that Authelia is active, it's time to let it talk to the internet. Go to /etc/nginx/sites-available/ and create an entry for Authelia:

cd /etc/nginx/sites-available/
nano authelia

This is the content of the Nginx configuration file, with extra settings for ssl security:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    ssl_certificate     /path/to/cert.crt;
    ssl_certificate_key /path/to/cert.key;
    server_name auth.mydomain.tld;
    access_log /var/log/nginx/authelia-access.log;
    error_log  /var/log/nginx/authelia-error.log;
 
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1:secp384r1;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;
 
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
 
    set $upstream http://127.0.0.1:9091;
 
    location / {
        # Headers
        proxy_set_header Host $host;
        proxy_set_header X-Original-URL $scheme://$host$request_uri;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-URI $request_uri;
        proxy_set_header X-Forwarded-Ssl on;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Real-IP $remote_addr;
        ## Basic Proxy Configuration
        client_body_buffer_size 128k;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; ## Timeout if the real server is dead.
        proxy_redirect  http://  $scheme://;
        proxy_http_version 1.1;
        proxy_cache_bypass $cookie_session;
        proxy_no_cache $cookie_session;
        proxy_buffers 64 256k;
        # Trusted Proxies Configuration
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        # Advanced Proxy Configuration
        send_timeout 5m;
        proxy_read_timeout 360;
        proxy_send_timeout 360;
        proxy_connect_timeout 360;
        proxy_pass $upstream;
    }
}

After saving the file, you need to enable the configuration and restart the web server:

ln -s /etc/nginx/sites-available/authelia /etc/nginx/sites-enabled/
systemctl restart nginx

Authelia should now be available at https://auth.mydomain.tld