NGINX + 2FA с Authelia

Debian 11

Решил я защитить двухфакторной аутентификацией опубликованные ресурсы.

После поисков в сети нашел бесплатный аутсорсный продукт от Authelia.

Сие творение позволяет подключить парольную, OTP и 2FA к любому разделу на сайте или домену целиком. Подробная информация на сайте первоисточника.

Я настраивал на обратном прокси от NginX, т.к. он мне ближе. Authelia с Redis в контейнерах.

Я прописал в DNS 2 субдомена:

  • auth.example.ru - домен аутентификации;
  • connect.example.ru - защищаемый домен.

Далее всё на чистом Debian 11. Все команды от root.

NginX - сборка

Ставлю зависимости и NginX 1.18.0:

apt update && apt install mc vim htop wget curl build-essential autoconf libpcre++-dev automake pkg-config libtool \
libpcre++-dev libssl-dev apache2-dev libxml2-dev libcurl4-gnutls-dev zlib1g-dev libnginx-mod-http-perl libperl-dev -y && \
timedatectl set-timezone Europe/Moscow && timedatectl set-ntp on && \
mkdir ~/nginx && cd ~/nginx && wget https://nginx.org/download/nginx-1.18.0.tar.gz && \
tar -zxvf nginx-1.18.0.tar.gz && cd nginx-1.18.0

Для корректной работы NginX на этапе компиляции нужно добавить дополнительные модули.
Скачиваем и распаковываем:

wget https://github.com/openresty/set-misc-nginx-module/archive/refs/tags/v0.33.tar.gz && \
wget https://github.com/vision5/ngx_devel_kit/archive/refs/tags/v0.3.2.tar.gz && \
tar xvf v0.33.tar.gz && tar xvf v0.3.2.tar.gz && rm v0.3.2.tar.gz v0.33.tar.gz

Конфигурирование и установка:

./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/bin \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--modules-path=/usr/lib/nginx/modules \
--with-http_gzip_static_module \
--with-http_gunzip_module \
--with-http_realip_module \
--with-http_mp4_module \
--with-http_flv_module \
--with-http_dav_module \
--with-http_secure_link_module \
--with-http_ssl_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_stub_status_module \
--with-http_random_index_module \
--with-http_auth_request_module \
--with-http_slice_module \
--with-http_v2_module \
--with-pcre \
--with-http_perl_module \
--add-module=/root/nginx/nginx-1.18.0/ngx_devel_kit-0.3.2 \
--add-module=/root/nginx/nginx-1.18.0/set-misc-nginx-module-0.33
make && make install

Добавляем сервис NginX:

tee /lib/systemd/system/nginx.service<<EOF
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/bin/nginx -t
ExecStart=/usr/bin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

Разрешаем и запускаем:

systemctl unmask nginx.service && systemctl daemon-reload && mkdir -p /var/cache/nginx/{client,proxy,fastcgi,uwsgi,scgi}_temp && mkdir -p /etc/nginx/{conf,keys,sites-available,sites-enabled,snippets,proxy-confs} && rm -rf /etc/nginx/*.default && systemctl restart nginx && systemctl enable nginx && systemctl status nginx
Authelia

Подразумевается, что у вас есь настроенная DNS-записи auth.example.ru и connect.example.ru
Ваши приложения и службы проксируются либо к вашему корневому домену, либо к некоторым поддоменам.
У вас есть настроенный SSL-сертификат. Я настраиваю с Wildcard сертификатом.

Ставим зависимости и DOCKER:

apt update && apt install -y ca-certificates curl gnupg lsb-release && \
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \
apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose git && systemctl status docker

Создаем папки и YAML:

mkdir -p ~/authelia/authelia && cd ~/authelia && nano docker-compose.yml
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=Europe/Moscow

  redis:
    image: redis:alpine
    container_name: redis_authelia
    volumes:
      - ./redis:/data
    networks:
      - net
    expose:
      - 6379
    restart: unless-stopped
    environment:
      - TZ=Europe/Moscow
      - PUID=1000
      - PGID=1000

Создаем файл конфигурации и поясню пару параметров: policy:
Существует четыре варианта политики: deny - доступ запрещен.
one_factor - вход в систему с именем пользователя и паролем.
two_factor - вход в систему, используя имя пользователя, пароль и 2FA.
bypass - вход в систему не требуется.

В данном примере домен auth.example.ru доступен без аутентификации, а домен connect.example.ru защищается 2FA.

cd authelia/ && nano configuration.yml
theme: dark
server:
  host: 0.0.0.0
  port: 9091
log:
  level: info
jwt_secret: fRfFAHOgLC5qs0nzls6frZsfTEY3grDgq9gIfrZdM4T5T7kFJZis2b7wTmysSlR

## The display name the browser should show the user for when using Webauthn to login/register.
webauthn:
  display_name: MyCompany

authentication_backend:
  file:
    path: /config/users_database.yml

access_control:
  default_policy: deny
  rules:
    - domain:
        - "auth.example.ru"
      policy: bypass
    - domain:
        - "connect.example.ru"
      policy: two_factor

session:
  name: authelia_session
  secret: fRfFAHOgLC5qs0nzls6frZsfTEY3grDgq9gIfrZdM4T5T7kFJZis2b7wTmysSlR
  expiration: 12h           # 12 hours
  inactivity: 45m           # 45 minutes
  remember_me_duration: 1M  # 1 month
  domain: example.ru

  redis:
    host: redis_authelia
    port: 6379

regulation:
  max_retries: 3
  find_time: 5m
  ban_time: 15m

storage:
  encryption_key: fRfFAHOgLC5qs0nzls6frZsfTEY3grDgq9gIfrZdM4T5T7kFJZis2b7wTmysSlR
  local:
    path: /config/db.sqlite3

notifier:
#  filesystem:
#    filename: /config/notification.txt
  smtp:
    username: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
    password: FdeNxyhfGRrz
    host: smtp.yandex.ru
    port: 465
    sender: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.

Генерируем сложный пароль:

head /dev/urandom | tr -dc A-Za-z0-9 | head -c64

и меняем в 3-х местах, а именно:

jwt_secret:
secret:
encryption_key:

Чтобы получить хэшированный пароль, который нам будет необходим для пользователя meadmin с паролем QWERTY выполняем следующую команду:

docker run authelia/authelia:latest authelia hash-password QWERTY

Копируем полученную строку

$argon2id$v=19$m=65536,t=3,p=4$SpdFLfWerL9dA$GtJV3pEv0MvCp/N/WTo9c

и вставляем в файл:

nano users_database.yml
users:
  meadmin:
    displayname: "Administrator"
    # Generate with docker run authelia/authelia:latest authelia hash-password QWERTY
    password: "$argon2id$v=19$m=65536,t=3,p=4$SpdFLfWerL9dA$GtJV3pEv0MvCp/N/WTo9c"
    email: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
    groups:
      - admins

Запускаем composer:

docker-compose up -d

Если всё сделали правильно, то докеры готовы. Если же какой-то контейнер рестартится, то смотрим логи контейнера и выясняем где допущена ошибка:

docker logs authelia

После успешного запуска контейнеров должна быть доступна авторизация:

webserver-ip:9091

Проверьте с именем пользователя и паролем, которые настраивали.

Создаём папки и настраиваем сайт авторизации:

mkdir -p /etc/nginx/sites-available/ /etc/nginx/sites-enabled/ /etc/nginx/snippets/ && \
nano /etc/nginx/sites-available/auth.example.ru
server {
    server_name auth.example.ru;
    listen 80;
    return 301 https://$server_name$request_uri;
}

server {
    server_name auth.example.ru;
    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;
    }
}

Линкуем:

ln -s /etc/nginx/sites-available/auth.example.ru /etc/nginx/sites-enabled/auth.example.ru

Создаем сниппет для Афелии и авторизации:

nano /etc/nginx/snippets/authelia.conf
# 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;
}
nano /etc/nginx/snippets/auth.conf
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
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;
error_page 401 =302 https://auth.example.ru/?rd=$target_url;
Пояснения

Немного поясню конфигурацию Nginx.

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
 worker_connections 1024;
}

http {
	sendfile on;
	tcp_nopush on;
	types_hash_max_size 2048;
	include /etc/nginx/mime.types;
	include /etc/nginx/snippets/ssl.conf;
	default_type application/octet-stream;
	gzip on;
	include /etc/nginx/sites-enabled/*;

server {
	server_name *.example.ru;
	listen 80;
	return 301 https://$server_name$request_uri;
}

server {
	listen 443 ssl http2;
	server_name connect.example.ru;
	ssl_session_cache  builtin:1000  shared:SSL:10m;
    include snippets/authelia.conf; # Authelia auth endpoint
	include 1c/*.conf;

	location / {
		root   html;
		index  index.html index.htm;
		error_page 404 /404.html;
		error_page 500 502 503 504  /50x.html;
	}

	location = /50x.html {
		root   html;
	}

}
}

Нужно включить фрагмент
include snippets/authelia.conf;
в основной блок server, а также фрагмент
include snippets/auth.conf;
в каждый защищаемый location

В приложенной конфигурации NginX я добавил строку
include 1c/*.conf;
и вынес в отдельные файлы отдельные файлы, каждый location.
Для примера я проксирую доступ к серверу 10.10.10.3 с установленный IIS, на котором опубликована база Гилёва.

cat 1c/gilev.conf
location /gilev/ {
	proxy_pass http://10.10.10.3/gilev/;
	include snippets/auth.conf;
}

Готовая конфигурация NginX.


Fuse

Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.