Featured image of post Authentication: Deploying Authelia for Private Identity Service

Authentication: Deploying Authelia for Private Identity Service

Use Authelia to deploy a private identity service for authentication, supporting OpenID Connect and integration with various applications.

Motivation

I previously thought it was unnecessary to set up my own identity authentication service, as I could simply create users and passwords in the services I deployed. However, while preparing to set up a HeadScale (self-hosted version of Tailscale) server, I found that the available UI options for HeadScale do not support direct user and password creation.

Without an authentication service, I would have to enter an API key every time, which is a long and irregular string that is nearly impossible to remember. If I use an authentication service, I can utilize many existing services like Google, Apple, Microsoft, GitHub, etc. However, since I am already self-hosting various services, why not also set up my own identity authentication service to keep all data under my control?

The setup of identity authentication services can be done in many ways, such as using Authentik, Keycloak, or Authelia. Authentik and Keycloak are more complex and resource-intensive, while I need a simpler service that allows the creation of just a few users. Therefore, I chose Authelia.

Authelia Overview

Authelia is an open-source authentication and single sign-on (SSO) solution that supports various authentication methods, including username and password, two-factor authentication (2FA), WebAuthn, etc. It can integrate with multiple authentication backends such as LDAP, Active Directory, MySQL, PostgreSQL, etc. Authelia also supports various authentication protocols like OAuth2, OpenID Connect, SAML, etc.

When using HeadScale, I primarily need to use the OpenID Connect (OIDC) protocol for authentication. Authelia supports the OpenID Connect protocol and can be integrated with HeadScale.

Authelia’s main features include:

  • Open Source: Authelia is an open-source project that can be freely used and modified.
  • Multiple Authentication Methods: Supports username and password, two-factor authentication (2FA), WebAuthn, and other authentication methods.
  • Multiple Authentication Backends: Supports LDAP, Active Directory, MySQL, PostgreSQL, and other authentication backends.
  • Multiple Authentication Protocols: Supports OAuth2, OpenID Connect, SAML, and other authentication protocols.
  • Single Sign-On (SSO): Supports single sign-on, allowing identity information to be shared across multiple applications.
  • Multiple Authentication Policies: Supports various authentication policies, such as IP-based, time-based, etc.

Authelia’s limitation is that it currently does not provide a graphical management interface; all configurations must be done through YAML files. However, Authelia’s official roadmap plans to introduce a management interface (including both graphical and command-line interfaces) in future versions, as detailed in the Roadmap. We look forward to this feature.

How Authelia Works

Authelia is merely an authentication service and does not provide any applications or services itself. Its working principle is to integrate with other applications or services to achieve authentication. When using Authelia, I primarily integrate it with Nginx. Nginx acts as a reverse proxy server, forwarding user requests to Authelia for authentication and then forwarding the requests to the actual application or service.

Typically, when a user accesses a web application, the process is as follows:

Web Application without Auth

That is:

  1. The user enters the URL of the web application in the browser and accesses the server via HTTP or HTTPS.
  2. The server processes the request with Nginx, which forwards the request to the corresponding application.
  3. The application processes the request and returns a response to Nginx.
  4. Nginx returns the response to the user’s browser.

If the web application has built-in authentication, a login window may pop up when the user accesses the application, prompting the user to enter a username and password. If authentication is successful, the application stores the user’s identity information in a session and allows access to other parts of the application.

However, some web applications do not have built-in authentication. In such cases, if you want to restrict user access, you can add an authentication service layer. Authelia is such an authentication service. If we add Authelia authentication service to a web application, the user’s access process is as follows:

Web Application with Auth

That is:

  1. The user enters the URL of the web application in the browser and accesses the server via HTTP or HTTPS.
  2. The server processes the request with Nginx, which forwards the request to Authelia for authentication.
  3. Authelia verifies the user’s identity information:
    • If authentication is successful, it forwards the request to Nginx. Nginx then forwards the request to the corresponding application. The application processes the request and returns a response to Nginx. Nginx returns the response to the user’s browser.
    • If authentication fails, it returns a 401 Unauthorized response and does not forward the request to the application. The user cannot access the application’s services.

Deploying Authelia with Docker

We can use docker compose to deploy Authelia. We need to prepare a docker-compose.yml file and two configuration files, with the following structure:

1
2
3
4
5
6
authelia/
    ├── config
    │   ├── configuration.yml
    │   └── users.yml
    ├── .env
    └── docker-compose.yml

where:

  • docker-compose.yml is the Docker Compose configuration file that defines the Authelia service, networks, and volumes.
  • The config directory contains the Authelia configuration file configuration.yml and the user configuration file users.yml.
  • .env is the environment variable file used to define Authelia’s environment variables.

docker-compose.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: '3.8'

services:
  authelia:
    image: authelia/authelia:4.39.3
    container_name: authelia
    restart: always
    volumes:
      - ./config:/config:ro
      - ${DATA_DIR}/data:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    ports:
      - "9091:9091"
    networks:
      - authnet
    labels:
      - wud.tag.include=^(\d+\.\d+)\.\d+$$ => $$1

networks:
  authnet:
    driver: bridge

where ${DATA_DIR}/data is an environment variable that specifies the directory where Authelia will store its data. We can define this variable in a .env file, for example:

1
DATA_DIR=/path/to/data

Configuration Files

Authelia’s configuration files include configuration.yml and users.yml. The configuration.yml file contains the main configuration for Authelia, while the users.yml file contains user information. Please refer to the official documentation for detailed configuration options. Below is a simple example of each configuration file.

configuration.yml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

###############################################################
#                   Authelia configuration                    #
###############################################################

server:
  address: tcp://0.0.0.0:9091/
  buffers:
    read: 4096
    write: 4096
  endpoints:
    enable_pprof: false
    enable_expvars: false
  disable_healthcheck: false

# https://www.authelia.com/configuration/miscellaneous/logging/
log:
  level: info
  format: text
  file_path: /data/authelia.log
  keep_stdout: true

# https://www.authelia.com/configuration/second-factor/time-based-one-time-password/
totp:
  issuer: authelia.com
  period: 30
  skew: 1

# AUTHELIA_DUO_PLACEHOLDER

# https://www.authelia.com/reference/guides/passwords/
authentication_backend:
  password_reset:
    disable: false
  refresh_interval: 5m
  file:
    path: /config/users.yml
    password:
      algorithm: argon2id
      iterations: 1
      salt_length: 16
      parallelism: 8
      memory: 256 # blocks this much of the RAM

# https://www.authelia.com/overview/authorization/access-control/
access_control:
  default_policy: deny
  rules:
    # - domain:
    #     - "*.example.com"
    #     - "example.com"
    #   policy: bypass
    #   networks: # bypass authentication for local networks
    #     - 10.0.0.0/8
    #     - 192.168.0.0/16
    #     - 172.16.0.0/12
    - domain:
        - "*.example.com"
        - "example.com"
      policy: one_factor

# https://www.authelia.com/configuration/session/introduction/
session:
  name: authelia_session
  same_site: lax
  expiration: 7h
  inactivity: 5m
  remember_me: 1M
  cookies:
    - domain: 'example.com'
      authelia_url: 'https://authelia.example.com'
      default_redirection_url: 'https://example.com'
  # AUTHELIA_REDIS_PLACEHOLDER

# https://www.authelia.com/configuration/security/regulation/
regulation:
  max_retries: 3
  find_time: 10m
  ban_time: 12h
  
# https://www.authelia.com/configuration/storage/introduction/
storage:
  # For local storage, uncomment lines below and comment out mysql. https://docs.authelia.com/configuration/storage/sqlite.html
  # This is good for the beginning. If you have a busy site then switch to other databases.
  encryption_key: 'some-secret-key-for-storage'
  local:
   path: /data/db.sqlite3

# https://www.authelia.com/configuration/notifications/introduction/
notifier:
  disable_startup_check: false
  # For testing purposes, notifications can be sent in a file. Be sure to map the volume in docker-compose.
  filesystem:
    filename: /data/notifications.txt

# https://www.authelia.com/configuration/identity-validation/introduction/
identity_validation:
  reset_password:
    jwt_secret: 'some-secret-key-for-reset-password'

# https://www.authelia.com/configuration/identity-providers/openid-connect/provider/
identity_providers:
  oidc:
    hmac_secret: 'hmac-secret-key'
    jwks: 
      -
        key_id: 'app-name'
        algorithm: 'RS256'
        use: sig
        key: |
          -----BEGIN PRIVATE KEY-----
          Your private key here
          -----END PRIVATE KEY-----          
    clients:
      - client_id: 'client-id'
        client_name: 'Description of the client'
        client_secret: 'client-secret'
        public: false
        authorization_policy: one_factor
        redirect_uris:
          - 'https://example.com/oidc/callback'
        scopes:
          - openid
          - profile
          - email
        grant_types:
          - authorization_code
        response_types:
          - code
        token_endpoint_auth_method: client_secret_post

Here, identity_providers defines an OIDC identity provider. If other applications need to use OIDC authentication, they can use this provider. We can define multiple OIDC clients in clients, each with a client_id and client_secret for authentication. For detailed explanations of each configuration item, please refer to the comments in the configuration file.

users.yml

1
2
3
4
5
6
7
users:
  user1:
    password: "hash-of-user1-password"
    displayname: "User One"
    email: [email protected]
    groups:
      - admins

Here we define an Authelia user with the username user1. Note that the password is a hashed password, not a plaintext password. We can use Authelia’s command-line tool to generate a hashed password, for example:

1
docker run authelia/authelia:4.39.3 authelia crypto hash generate bcrypt --password 'your-user1-password'

Replace the generated hashed password in the users.yml file. The email address is not important; it can be any valid email address. The groups field indicates the groups to which the user belongs. Authelia supports various groups, such as admins, users, etc. We can define these groups in the configuration file and assign different permissions to each group.

Starting Authelia

After preparing the above, we can start Authelia. In the authelia directory, execute the following command:

1
docker compose up -d

This command will start the Authelia service in the background and bind it to port 9091. We can access Authelia’s web interface at http://localhost:9091.If everything is working correctly, we should see Authelia’s login interface.

Authelia Login Interface

Enter the username and password configured above, which are user1 and your-user1-password. Note that here the password is in plaintext, not a hashed password.

If the login is successful, we will be redirected to the default redirection URL configured above, which is https://example.com.

Integrating Authelia with Other Services

As mentioned above, other applications or services can integrate with Authelia for authentication. Here, we will take a simple Whoami application as an example to demonstrate how to integrate with Authelia.

Whoami Application

Whoami is a simple web application that returns information about the request, such as the IP address, request headers, request method, etc. We can use Whoami to test Authelia’s authentication functionality.

  1. Deploy Whoami Application

    We can use Docker Compose to deploy the Whoami application. We need to prepare a docker-compose.yml file with the following content:

    1
    2
    3
    4
    5
    6
    7
    8
    
    version: '3.8'
    services:
      whoami:
        image: traefik/whoami:v1.11.0
        container_name: whoami
        restart: always
        ports:
          - "2001:80"
    
  2. Start Whoami Application

    In the whoami directory, execute the following command:

    1
    
    docker compose up -d
    

    This command will start the Whoami service in the background and bind it to port 2001. We can access Whoami’s web interface at http://localhost:2001.

    If everything is working correctly, we should see Whoami’s interface.

    Whoami Interface

  3. Configure Nginx Reverse Proxy

    We need to configure Nginx as a reverse proxy to forward user requests to Authelia for authentication and then forward the requests to the Whoami application.

    We need to prepare an Nginx configuration file with the following content:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    upstream <whoami.example.com> {  
        server 127.0.0.1:2001; 
    }
    
    server {
        listen 80;
        server_name  whoami.example.com;
        return 301 https://whoami.example.com$request_uri;
    }
    
    server {
        listen 443 ssl;
        server_name  whoami.example.com;
    
        # ssl 配置
        ssl_certificate     </path/to/fullchain.cer>;
        ssl_certificate_key </path/to/whoami.example.com.key>;
    
        # Step 1: internal location that calls Authelia for authentication
        location /authelia {
            internal;
            proxy_pass http://localhost:9091/api/verify;
            proxy_set_header Content-Length "";
            proxy_pass_request_body off;
            proxy_set_header X-Original-URL $scheme://$host$request_uri;
            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;
        }
    
        # Step 2: protect Whoami via Authelia
        location / {
            # This sends an internal subrequest to Authelia for verification
            auth_request /authelia;
    
            # Redirect to Authelia portal if unauthorized
            error_page 401 =302 https://<authelia.example.com>/?rd=https://$host$request_uri;
    
            proxy_pass http://whoami.example.com;
    
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Ssl on;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Frame-Options SAMEORIGIN;
        }
    }
    

    Here, the server_name needs to be replaced with your own domain or IP address. The ssl_certificate and ssl_certificate_key need to be replaced with the paths to your own SSL certificate and private key.

    Then reload the Nginx configuration:

    1
    
    sudo systemctl reload nginx
    

    Based on the above Nginx configuration, we can see that the process of using Authelia to authenticate users for Whoami is as follows:

    1. The user accesses https://whoami.example.com. Nginx will forward the request to /authelia, which is Authelia for authentication, based on the auth_request directive.
    2. This authentication request is then forwarded to Authelia’s /api/verify endpoint.
    3. Authelia verifies the user’s identity.
      • If the verification is successful, it returns a 200 OK response, and Nginx will forward the request to the Whoami application.
      • If the verification fails, it returns a 401 Unauthorized response, and Nginx will redirect the request to Authelia’s login page.

Testing Authentication

Now we can test the authentication functionality by accessing http://example.com (replace with your own domain or IP address).

If everything is working correctly, we should see Authelia’s login interface: Authelia Login Interface

Enter the username and password, which are user1 and your-user1-password. Note that here the password is in plaintext, not a hashed password.

If the login is successful, we will be redirected to the Whoami application interface.

If the username or password is incorrect, it will prompt “Invalid username or password” and return to Authelia’s login interface.

Summary

We have used Authelia to set up a simple authentication service and demonstrated how to integrate it with a simple Whoami application. If you want to integrate with other applications that have already been deployed, you only need to add the corresponding configuration in Nginx.

comments powered by Disqus