Автентифікація CAS та SSO через Nginx: захист будь-якого додатка за допомогою Casdoor та Nginx
У відкритому коді nginx немає модуля CAS, тому "автентифікація CAS через nginx" на практиці означає одну з двох конфігурацій. Або nginx запитує сервіс автентифікації про кожен запит через свій модуль auth_request, а Casdoor відповідає (безпосередньо на /api/forward-auth або через casdoor-forward-auth чи OAuth2 Proxy), або сам додаток за nginx є CAS-клієнтом і спілкується з ендпоінтами CAS-сервера Casdoor. Якщо ви можете перебудувати nginx, модуль Casdoor для nginx виконує вхід всередині nginx, а якщо ви не хочете взагалі використовувати nginx, проксі-сайт Casdoor може стояти перед додатком. В усіх випадках сторінка входу належить Casdoor, тому одна сесія SSO, один набір дозволів та одна політика MFA покривають всі додатки.
Огляд варіантів
| Варіант | Де виконується логіка входу | Що потрібно для nginx | Протокол до Casdoor | Хто отримує доступ | Коли обирати |
|---|---|---|---|---|---|
| Forward auth Casdoor | Casdoor, /api/forward-auth | auth_request | Сесійна cookie Casdoor на домені додатка | Дозволи додатка, перевіряються щонайменше раз на хвилину | nginx вже стоїть попереду і ви не хочете зайвих сервісів |
| casdoor-forward-auth | Невеликий сервіс на Go | auth_request | Authorization code flow для OIDC | Ролі або групи Casdoor, для кожного location | Одна сесійна cookie спільна для багатьох субдоменів |
| OAuth2 Proxy | OAuth2 Proxy | auth_request | OIDC | allowed_groups, домени email | Ви вже використовуєте OAuth2 Proxy |
| casdoor-nginx-module | Сам nginx | Скомпільований модуль | OIDC з PKCE, токени перевіряються локально | Будь-який увійшовший користувач додатка | Без додаткового процесу, плюс bearer tokens для API |
| CAS-клієнт у додатку | Додаток | Нічого, він лише проксує | CAS 1.0, 2.0, 3.0 | Те, що додаток робить з атрибутами | Додаток уже підтримує CAS |
| Проксі-сайт Casdoor | Casdoor на портах 80 і 443 | Без nginx | Сесія Casdoor | Дозволи додатка | Перед додатком нічого іншого немає |
Перші три варіанти відрізняються лише тим, хто відповідає на підрядник (subrequest) nginx.
Чому немає "модуля CAS для nginx"
CAS — це діалог між додатком та CAS-сервером: додаток перенаправляє браузер на /login сервера з URL service, отримує service ticket назад на цей URL і валідує тикет через запит сервер-сервер. Apache httpd має mod_auth_cas від Apereo для цього на рівні веб-сервера, але у nginx немає модуля CAS серед офіційних модулів. Для nginx стандартним будівельним блоком є ngx_http_auth_request_module: перед обслуговуванням запиту nginx надсилає підрядник (subrequest) на URL, який ви обираєте. Відповідь 2xx пропускає запит, 401 або 403 відхиляє його з цим кодом, а будь-який інший код є помилкою.
Для користувача вхід через CAS та OIDC виглядають однаково: перенаправлення на сторінку входу Casdoor і назад. Тому для додатка без підтримки CAS практичним рішенням є OIDC на рівні nginx. Інформація про протокол: Що таке CAS.
Модуль auth_request не вбудовано за замовчуванням: додайте --with-http_auth_request_module при компіляції або перевірте nginx -V для пакетної версії nginx.
Варіант 1: власний forward auth Casdoor
Casdoor може сам відповідати на підрядники (subrequests) nginx. Додайте Site у Casdoor, у якого Domain — це домен додатка, а Casdoor app — додаток, у який входять користувачі, і додайте https://app.example.com/caswaf-handler до Redirect URLs цього додатка. Host і port можуть залишитися порожніми, оскільки трафік не проходить через Casdoor.
Оскільки auth_request не може слідувати за перенаправленнями, Casdoor відповідає на неавтентифікований підрядник кодом 401 і поміщає URL входу в Location. nginx перетворює це на 302:
server {
server_name app.example.com;
proxy_buffer_size 16k;
proxy_buffers 8 16k;
location = /caswaf-handler {
proxy_pass http://casdoor:8000/api/forward-auth;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
}
location = /casdoor-auth {
internal;
proxy_pass http://casdoor:8000/api/forward-auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
auth_request /casdoor-auth;
auth_request_set $casdoor_redirect $upstream_http_location;
auth_request_set $casdoor_user $upstream_http_x_forwarded_user;
auth_request_set $casdoor_email $upstream_http_x_forwarded_email;
auth_request_set $casdoor_groups $upstream_http_x_forwarded_groups;
error_page 401 =302 $casdoor_redirect;
proxy_set_header X-Forwarded-User $casdoor_user;
proxy_set_header X-Forwarded-Email $casdoor_email;
proxy_set_header X-Forwarded-Groups $casdoor_groups;
proxy_pass http://app:3000;
}
}
Бекенд отримує користувача в X-Forwarded-User, X-Forwarded-Email та X-Forwarded-Groups. Доступ визначається дозволом на додатку, який перевіряється щонайменше раз на хвилину, тому деактивований користувач швидко втрачає доступ. Public paths сайту (webhooks, health checks) проходять без входу.
Дивіться Using your own reverse proxy для довідки та Add login to an app without OIDC для покрокового посібника з Grafana. Додаток має бути доступний лише через nginx, інакше будь-хто зможе самостійно надсилати заголовок X-Forwarded-User.
Варіант 2: casdoor-forward-auth або OAuth2 Proxy
Якщо ви не хочете створювати сайт Casdoor для кожного додатка, запустіть невеликий сервіс, який виконує OIDC-потік відносно Casdoor і відповідає на підзапит (subrequest).
casdoor-forward-auth — це окремий сервіс Casdoor для цієї мети (він замінив traefik-casdoor-auth). Він зберігає дані користувача в підписаному cookie без серверного стану. Для nginx він надає ендпоінт /verify, який повертає статус 200 із заголовками ідентифікації або 401; nginx перенаправляє запит на його /login при отриманні 401:
server {
server_name app.example.com;
location = /_casdoor_verify {
internal;
proxy_pass http://casdoor-forward-auth:9999/verify?groups=built-in/dev;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
location @casdoor_login {
return 302 https://auth.example.com/login?rd=$scheme://$http_host$request_uri;
}
location / {
auth_request /_casdoor_verify;
error_page 401 = @casdoor_login;
auth_request_set $casdoor_user $upstream_http_x_forwarded_user;
auth_request_set $casdoor_roles $upstream_http_x_forwarded_roles;
proxy_set_header X-Forwarded-User $casdoor_user;
proxy_set_header X-Forwarded-Roles $casdoor_roles;
proxy_pass http://app:8080;
}
}
Параметри запиту roles та groups в URL перевірки обмежують кожен додаток тримачами певної ролі Casdoor або учасниками групи; авторизовані користувачі без цих прав отримають помилку 403. Ролі та групи зчитуються під час входу й зберігаються протягом сесії, тому зміни набудуть чинності після повторного входу користувача. Встановлення COOKIE_DOMAIN=example.com дозволяє одній сесії входу охоплювати всі піддомени. Посібник Traefik guide описує використання того ж сервісу для Traefik.
OAuth2 Proxy працює аналогічно з налаштуванням provider = "oidc" та oidc_issuer_url, що вказує на ваш URL Casdoor. Його ендпоінт /oauth2/auth повертає 202 або 401, для передачі заголовків користувача та email потрібен прапорець --set-xauthrequest, а обробка заголовків переадресації у прикладі конфігурації nginx вимагає прапорця --reverse-proxy. Важливі два параметри Casdoor: використовуйте формат токенів JWT-Standard, інакше користувачі з непідтвердженою адресою email будуть відхилені, та збережіть scope profile, щоб поверталися групи та ролі. Дивіться OAuth2 Proxy with Casdoor та посібник nginx community edition guide.
Варіант 3: модуль Casdoor для nginx
casdoor-nginx-module виконує вхід безпосередньо всередині nginx, без окремого процесу. Браузери без активної сесії перенаправляються на сторінку входу Casdoor; модуль обмінює код із PKCE, перевіряє підпис access token, поля exp, aud та тип токена відповідно до сертифіката Casdoor і зберігає користувача в підписаному cookie. API-клієнти можуть надсилати заголовок Authorization: Bearer з access token Casdoor, який перевіряється локально без звернення до Casdoor.
Потрібні nginx версії 1.22 або новішої та OpenSSL версії 1.1.1 або новішої. Ви можете зібрати його як динамічний модуль для вже запущеного вами nginx:
./configure --with-compat --with-http_ssl_module --add-dynamic-module=/path/to/casdoor-nginx-module
make modules
cp objs/ngx_http_casdoor_module.so /etc/nginx/modules/
load_module modules/ngx_http_casdoor_module.so;
events {}
http {
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/nginx/app.crt;
ssl_certificate_key /etc/nginx/app.key;
casdoor_endpoint https://door.example.com;
casdoor_client_id <client-id>;
casdoor_client_secret <client-secret>;
casdoor_certificate /etc/nginx/casdoor.pem;
casdoor_redirect_uri https://app.example.com/casdoor/callback;
casdoor_token_location /_casdoor/token;
location = /_casdoor/token {
internal;
proxy_pass https://door.example.com/api/login/oauth/access_token;
proxy_ssl_server_name on;
}
location = /casdoor/callback {
casdoor_callback;
}
location / {
casdoor on;
proxy_set_header X-User $casdoor_user;
proxy_pass http://127.0.0.1:3000;
}
}
}
Замініть заповнювачі значеннями вашого додатка. Сесія не перевіряється повторно з Casdoor до її закінчення терміну дії, тому блокування користувача набирає чинності лише після завершення сесії; параметр casdoor_session_timeout скорочує цей проміжок часу. Модуль не містить перевірок ролей чи груп. Для NGINX Plus дивіться натомість the NGINX Plus guide.
Варіант 4: додаток є CAS-клієнтом
Якщо додаток уже використовує протокол CAS (наприклад, Java-додаток із клієнтом Apereo CAS), nginx лише проксує трафік, а Casdoor виступає CAS-сервером. Кожен додаток Casdoor має CAS-ендпоінти за шляхом /cas/<organization>/<application>: /login та /logout, /validate для CAS 1.0, /serviceValidate, /proxyValidate та /proxy для CAS 2.0, /p3/serviceValidate та /p3/proxyValidate для CAS 3.0, а також /samlValidate для валідації SAML 1.1.
Додаток перенаправляє браузер на /login?service=..., Casdoor повертає редирект із ticket=ST-..., і додаток валідує цей тікет:
curl "https://door.example.com/cas/my-org/my-app/p3/serviceValidate?ticket=ST-1234567890&service=https%3A%2F%2Fapp.example.com%2F"
Успішна відповідь містить елемент cas:serviceResponse з cas:user та атрибутами, такими як email, відображуване ім'я та memberOf; додайте format=json для отримання JSON. Моменти, які часто спричиняють проблеми:
- Кожен URL
serviceмає бути зазначений у полі Redirect URLs додатка, а значенняserviceпід час валідації повинно точно збігатися з тим, для якого було видано тікет. - Тікети можна використати лише один раз, і вони стають недійсними через 5 хвилин.
- Тікети зберігаються в оперативній пам'яті, тому при наявності кількох реплік Casdoor валідація має потрапляти на ту репліку, яка видала тікет (необхідні sticky sessions).
- За nginx передавайте заголовки
HostтаX-Forwarded-Proto, щоб додаток формував той самий URLservice, який бачив браузер.
Інструкція з налаштування, включаючи фільтри Java-клієнта для кожної версії CAS, наведена у статті Connect a CAS client.
Варіант 5: використовуйте Casdoor як зворотний проксі
Якщо перед додатком немає інших компонентів, Casdoor може обслуговувати його напряму: проксі сайту слухає порти gatewayHttpPort та gatewayHttpsPort (за замовчуванням 80 та 443) після створення сайту. Встановіть поле Host сайту на бекенд, оберіть сертифікат, задайте Casdoor app і спрябуйте DNS на Casdoor. Поведінка входу, прав доступу та заголовків така сама, як у варіанті 1. Дивіться Sites.
Додавання MFA
MFA не потребує змін у конфігурації nginx або додатка. Casdoor запитує другий фактор на своїй сторінці входу перед видачею коду авторизації OAuth або тікета служби CAS, тому всі наведені вище варіанти його підтримують. Користувачі можуть використовувати TOTP-застосунок, SMS, email або RADIUS із відновлювальними кодами, а адміністратори можуть вимагати MFA для організації за допомогою MFA items. Це також відповідає на питання «CAS 2FA»: коли Casdoor виступає CAS-сервером, другий фактор є частиною входу CAS.
MFA застосовується під час входу, а не для кожного запиту: сесія, яку вже утримує nginx або сервіс forward-auth, діє до закінчення терміну її експірації, а параметр MFA remember time організації дозволяє користувачам пропускати другий фактор на довіреному пристрої протягом певного часу. Дивіться посібник MFA guide та статтю What is MFA.
Після входу в Casdoor користувачі можуть отримати доступ до інших додатків, які перенаправляють їх на Casdoor, без повторного введення пароля; якщо увімкнено Auto signin, Casdoor також пропускає вибір акаунта. Дивіться single sign-on та Single logout.
Який варіант обрати
- Немає вбудованого входу в додаток, і nginx вже стоїть попереду: використовуйте forward auth Casdoor.
- Один вхід для багатьох піддоменів або перевірка ролей за розташуванням: casdoor-forward-auth (або OAuth2 Proxy, якщо ви вже його використовуєте).
- Ви збираєте nginx самостійно і також захищаєте API за допомогою bearer-токенів: casdoor-nginx-module.
- Додаток підтримує CAS: кінцеві точки CAS Casdoor, nginx як простий проксі.
- Проксі ще не налаштовано: проксі сайту Casdoor.
Посилання
- nginx: ngx_http_auth_request_module
- OAuth2 Proxy: інтеграція з nginx
- Apereo: Специфікація протоколу CAS, mod_auth_cas
- Casdoor: casdoor-forward-auth, casdoor-nginx-module
- Документація Casdoor: Сайти, Підключення клієнта CAS, Багатофакторна автентифікація