cmc/cleberg.net

My personal web garden & blog.

clone: git clone https://gitbay.org/cmc/cleberg.net.git

main: content/blog/2023-10-11-self-hosting-authelia.org · raw

  1#+date:        [2023-10-11 Wed 00:00:00]
  2#+title:       Self-Hosting Guide: Authelia
  3#+description: Setting up Authelia for two-factor auth and access control on self-hosted apps.
  4#+slug:        self-hosting-authelia
  5#+filetags:    :linux:security:self-hosting:
  6
  7* Overview
  8
  9[[https://www.authelia.com/][Authelia]] is an open-source authentication
 10service that allows you to place a portal between end users on the
 11internet and self-hosted services on your server.
 12
 13You can require one factor (username+password) or two factor
 14authentication for any such user before allowing them to access a
 15specific service on your domain.
 16
 17This guide will walk through a standard installation of Authelia for
 18=example.com=, using =auth.example.com= as Authelia's authentication
 19domain and =teddit.example.com= as the website we want to protect behind
 20the authentication portal.
 21
 22* Prerequisites
 23
 24This guide assumes you have the following already set-up:
 25
 26- A registered domain with DNS pointing to your server.
 27- A subdomain for Authelia (=auth.example.com=) and a subdomain to
 28  protect via Authelia (=app.example.com=).
 29- A working Nginx web server.
 30- Docker and docker-compose installed.
 31
 32* Installation
 33
 34This guide will walk through each installation step one-by-one, starting
 35with the container and finishing by cleaning up external access via an
 36Nginx reverse proxy.
 37
 38** Docker-Compose
 39
 40To start, create a directory for Authelia and create a
 41=docker-compose.yml= file.
 42
 43#+begin_src sh
 44mkdir ~/authelia
 45nano ~/authelia/docker-compose.yml
 46#+end_src
 47
 48Within this file, paste the following content. If you prefer a different
 49local port, modify the port on the left side of the colon on the
 50=9091:9091= line. Be sure to modify the =TZ= variable to your timezone.
 51
 52#+begin_src yml
 53version: "3.3"
 54
 55services:
 56    authelia:
 57        image: authelia/authelia
 58        container_name: authelia
 59        volumes:
 60            - ./config:/config
 61        ports:
 62            - 9091:9091
 63        environment:
 64            - TZ=America/Chicago
 65#+end_src
 66
 67Start the container with docker-compose:
 68
 69#+begin_src sh
 70sudo docker-compose up -d
 71#+end_src
 72
 73After the first start, the container will automatically exit and require
 74you to modify the app's configuration files before continuing. Read on
 75to learn more.
 76
 77** Authelia Configuration
 78
 79To configure Authelia before we restart the container, we need to open
 80the =config= directory and modify the files. Start by editing the
 81=configuration.yml= file, where all of Authelia's settings are stored.
 82
 83My personal preference is to copy the original configuration file to a
 84backup file and edit a fresh copy.
 85
 86#+begin_src sh
 87sudo cp ~/authelia/config/configuration.yml ~/authelia/config/configuration.yml.bk
 88sudo nano ~/authelia/config/configuration.yml
 89#+end_src
 90
 91Within the blank =configuration.yml= file, paste the following
 92information. You will need to make quite a few updates, so be sure to
 93read each line carefully and modify as necessary.
 94
 95The major required changes are:
 96
 97- Any instances of =example.com= should be replaced by your domain.
 98- =jwt_secret= - Use the =pwgen 40 1= command to generate a secret for
 99  yourself.
100- =access_control= - Set the Authelia domain to bypass here, as well as
101  any subdomains you want to protect.
102- =session= > =secret= - Use the =pwgen 40 1= command to generate a
103  secret for yourself.
104- =regulation= - Set the variables here to restrict login attempts and
105  bans.
106- =storage= > =encryption_key= - Use the =pwgen 40 1= command to
107  generate a secret for yourself.
108- =smtp= - If you have access to an SMTP service, set up the information
109  here to active outgoing emails.
110
111#+begin_src yml
112# yamllint disable rule:comments-indentation
113---
114###############################################################################
115#                           Authelia Configuration                            #
116###############################################################################
117
118theme: dark
119jwt_secret: aiS5iedaiv6eeVaideeLeich5roo6ohvaf3Vee1a # pwgen 40 1
120
121default_redirection_url: https://example.com
122
123server:
124    host: 0.0.0.0
125    port: 9091
126    path: ""
127    read_buffer_size: 4096
128    write_buffer_size: 4096
129    enable_pprof: false
130    enable_expvars: false
131    disable_healthcheck: false
132    tls:
133        key: ""
134        certificate: ""
135
136log:
137    level: debug
138
139totp:
140    issuer: example.com
141    period: 30
142    skew: 1
143
144authentication_backend:
145    disable_reset_password: false
146    refresh_interval: 5m
147    file:
148        path: /config/users_database.yml
149        password:
150            algorithm: argon2id
151            iterations: 1
152            key_length: 32
153            salt_length: 16
154            memory: 1024
155            parallelism: 8
156
157access_control:
158    default_policy: deny
159    rules:
160        - domain:
161              - "auth.example.com"
162          policy: bypass
163        - domain: "teddit.example.com"
164          policy: one_factor
165
166session:
167    name: authelia_session
168    secret: aiS5iedaiv6eeVaideeLeich5roo6ohvaf3Vee1a # pwgen 40 1
169    expiration: 3600
170    inactivity: 300
171    domain: example.com
172
173regulation:
174    max_retries: 5
175    find_time: 10m
176    ban_time: 12h
177
178storage:
179    local:
180        path: /config/db.sqlite3
181    encryption_key: aiS5iedaiv6eeVaideeLeich5roo6ohvaf3Vee1a # pwgen 40 1
182
183notifier:
184    disable_startup_check: true
185    smtp:
186        username: user@example.com
187        password: password
188        host: smtp.example.com
189        port: 465
190        sender: user@example.com
191        identifier: example.com
192        subject: "[Authelia] {title}"
193        startup_check_address: user@example.com
194        disable_require_tls: false
195        disable_html_emails: true
196        tls:
197            skip_verify: false
198            minimum_version: TLS1.2
199#+end_src
200
201** Authelia Users
202
203Next, create the users file for authentication.
204
205#+begin_src sh
206sudo nano ~/authelia/config/users_database.yml
207#+end_src
208
209Within the file, you will need to create an entry for each user that
210needs access to Authelia. The =my_username= entry will be the username
211used on the login page.
212
213To generate the password, go to [[https://argon2.online][Argon2 Hash
214Generator]], generate a random salt, and make sure the rest of the
215settings match the =authentication_backend= section of
216=configuration.yml= file.
217
218#+begin_src yaml
219users:
220    my_username:
221        displayname: "My User"
222        # Generated at https://argon2.online/ -- match the settings in
223        # the `authentication_backend` section of configuration.yml
224        password: ""
225        email: email@example.com
226        groups:
227            - admins
228            - dev
229#+end_src
230
231Once the app is configured, restart the container from scratch.
232
233#+begin_src sh
234cd ~/authelia
235sudo docker-compose down && sudo docker-compose up -d
236#+end_src
237
238** Nginx: Authelia Domain
239
240Once the container is running and configured, the final step is to
241configure external access to the server via Nginx reverse proxy.
242
243Start by creating the Authelia domain.
244
245#+begin_src sh
246sudo nano /etc/nginx/sites-available/auth
247#+end_src
248
249Within this file, paste the following information and be sure to update
250=example.com= to your domain. Make sure the =$upstream_authelia=
251variable matches the location of your Authelia container.
252
253#+begin_src conf
254server {
255    if ($host ~ ^[^.]+\.example\.com$) {
256        return 301 https://$host$request_uri;
257    }
258
259    listen [::]:80;
260    listen 80;
261    server_name auth.example.com;
262    return 404;
263}
264
265server {
266    listen [::]:443 ssl http2;
267    listen 443 ssl http2;
268    server_name auth.example.com;
269    access_log  /var/log/nginx/auth.access.log;
270    error_log   /var/log/nginx/auth.error.log;
271
272    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
273    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
274    include /etc/letsencrypt/options-ssl-nginx.conf;
275    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
276
277    location / {
278        set $upstream_authelia http://127.0.0.1:9091;
279        proxy_pass $upstream_authelia;
280        client_body_buffer_size 128k;
281
282        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
283
284        send_timeout 5m;
285        proxy_read_timeout 360;
286        proxy_send_timeout 360;
287        proxy_connect_timeout 360;
288
289        proxy_set_header Host $host;
290        proxy_set_header X-Real-IP $remote_addr;
291        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
292        proxy_set_header X-Forwarded-Proto $scheme;
293        proxy_set_header X-Forwarded-Host $http_host;
294        proxy_set_header X-Forwarded-Uri $request_uri;
295        proxy_set_header X-Forwarded-Ssl on;
296        proxy_redirect  http://  $scheme://;
297        proxy_http_version 1.1;
298        proxy_set_header Connection "";
299        proxy_cache_bypass $cookie_session;
300        proxy_no_cache $cookie_session;
301        proxy_buffers 64 256k;
302    }
303
304}
305#+end_src
306
307Next, symlink the file and restart Nginx. If there are errors, be sure
308to resolve those before moving on.
309
310#+begin_src sh
311sudo ln -s /etc/nginx/sites-available/auth /etc/nginx/sites-enabled/auth
312sudo systemctl restart nginx.service
313#+end_src
314
315** Nginx: Protected Domain(s)
316
317Now that Authelia is accessible externally, you need to configure the
318domain you intend to protect with Authelia. In this example, I'm
319protecting =teddit.example.com=.
320
321Similar to the process above, paste the content and update the relevant
322variables.
323
324#+begin_src sh
325sudo nano /etc/nginx/sites-available/teddit
326#+end_src
327
328#+begin_src conf
329server {
330    if ($host ~ ^[^.]+\.example\.com$) {
331        return 301 https://$host$request_uri;
332    }
333
334    listen [::]:80;
335    listen 80;
336    server_name teddit.example.com;
337    return 404;
338}
339
340server {
341    listen [::]:443 ssl http2;
342    listen 443 ssl http2;
343    server_name teddit.example.com;
344    access_log  /var/log/nginx/teddit.access.log;
345    error_log   /var/log/nginx/teddit.error.log;
346
347    add_header X-Content-Type-Options "nosniff";
348    add_header X-XSS-Protection "1; mode=block";
349    add_header X-Frame-Options "DENY";
350    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
351    add_header Referrer-Policy "no-referrer";
352
353    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
354    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
355    include /etc/letsencrypt/options-ssl-nginx.conf;
356    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
357
358    location /authelia {
359        internal;
360        set $upstream_authelia http://127.0.0.1:9091/api/verify;
361        proxy_pass_request_body off;
362        proxy_pass $upstream_authelia;
363        proxy_set_header Content-Length "";
364
365        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
366        client_body_buffer_size 128k;
367        proxy_set_header Host $host;
368        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
369        proxy_set_header X-Real-IP $remote_addr;
370        proxy_set_header X-Forwarded-For $remote_addr;
371        proxy_set_header X-Forwarded-Proto $scheme;
372        proxy_set_header X-Forwarded-Host $http_host;
373        proxy_set_header X-Forwarded-Uri $request_uri;
374        proxy_set_header X-Forwarded-Ssl on;
375        proxy_redirect  http://  $scheme://;
376        proxy_http_version 1.1;
377        proxy_set_header Connection "";
378        proxy_cache_bypass $cookie_session;
379        proxy_no_cache $cookie_session;
380        proxy_buffers 4 32k;
381
382        send_timeout 5m;
383        proxy_read_timeout 240;
384        proxy_send_timeout 240;
385        proxy_connect_timeout 240;
386    }
387
388    location / {
389        set $upstream_teddit http://127.0.0.1:8686;
390        proxy_pass $upstream_teddit;
391
392        auth_request /authelia;
393        auth_request_set $target_url https://$http_host$request_uri;
394        auth_request_set $user $upstream_http_remote_user;
395        auth_request_set $email $upstream_http_remote_email;
396        auth_request_set $groups $upstream_http_remote_groups;
397        proxy_set_header Remote-User $user;
398        proxy_set_header Remote-Email $email;
399        proxy_set_header Remote-Groups $groups;
400
401        error_page 401 =302 https://auth.example.com/?rd=$target_url;
402
403        client_body_buffer_size 128k;
404
405        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
406
407        send_timeout 5m;
408        proxy_read_timeout 360;
409        proxy_send_timeout 360;
410        proxy_connect_timeout 360;
411
412        proxy_set_header Host $host;
413        proxy_set_header Upgrade $http_upgrade;
414        proxy_set_header Connection upgrade;
415        proxy_set_header Accept-Encoding gzip;
416        proxy_set_header X-Real-IP $remote_addr;
417        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
418        proxy_set_header X-Forwarded-Proto $scheme;
419        proxy_set_header X-Forwarded-Host $http_host;
420        proxy_set_header X-Forwarded-Uri $request_uri;
421        proxy_set_header X-Forwarded-Ssl on;
422        proxy_redirect  http://  $scheme://;
423        proxy_http_version 1.1;
424        proxy_set_header Connection "";
425        proxy_cache_bypass $cookie_session;
426        proxy_no_cache $cookie_session;
427        proxy_buffers 64 256k;
428    }
429}
430#+end_src
431
432Same as before, symlink the file and restart Nginx.
433
434#+begin_src sh
435sudo ln -s /etc/nginx/sites-available/teddit /etc/nginx/sites-enabled/teddit
436sudo systemctl restart nginx.service
437#+end_src
438
439* Results
440
441When visiting the protected domain, you will now be redirected to your
442authentication domain and presented with the Authelia login portal.
443
444#+caption: Authelia Portal
445#+attr_html: :alt A view of the Authelia login page, requiring a username and password.
446[[https://img.cleberg.net/blog/20231010-authelia/authelia_portal.webp]]
447
448Once you've successfully authenticated, you can visit your
449authentication domain directly and see that you're currently
450authenticated to any domain protected by Authelia.
451
452#+caption: Authelia Success
453#+attr_html: :alt A view of the success page when succesfully authenticated.
454[[https://img.cleberg.net/blog/20231010-authelia/authelia_success.webp]]