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]]