Pular para o conteúdo principal

Autenticação CAS e SSO no Nginx: Protegendo Qualquer Aplicativo Atrás do Nginx com Casdoor

· Leitura de 12 min
Yang Luo
Casdoor Maintainer

O nginx open-source não possui um módulo CAS, então "autenticação CAS no nginx" na prática significa uma das duas configurações. Ou o nginx consulta um serviço de autenticação sobre cada solicitação através de seu módulo auth_request, e o Casdoor responde (diretamente em /api/forward-auth, ou através de casdoor-forward-auth ou OAuth2 Proxy), ou o aplicativo atrás do nginx é ele próprio um cliente CAS e conversa com os endpoints do servidor CAS do Casdoor. Se você puder recompilar o nginx, o módulo nginx do Casdoor executa o login dentro do nginx, e se você preferir não executar o nginx, o proxy de site do Casdoor pode ficar na frente do aplicativo. Em todos os casos, a página de login é a do Casdoor, então uma sessão SSO, um conjunto de permissões e uma política MFA cobrem todos os aplicativos.

As opções em resumo​

OpçãoOnde a lógica de login rodaO que o nginx precisaProtocolo para o CasdoorQuem entraEscolha quando
Forward auth do CasdoorCasdoor, /api/forward-authauth_requestCookie de sessão do Casdoor no domínio do appPermissões da aplicação, verificadas novamente pelo menos uma vez por minutoO nginx já está na frente e você não quer serviços extras
casdoor-forward-authUm pequeno serviço Goauth_requestCódigo de autorização OIDCFunções ou grupos do Casdoor, por localizaçãoUm cookie de sessão compartilhado por muitos subdomínios
OAuth2 ProxyOAuth2 Proxyauth_requestOIDCallowed_groups, domínios de e-mailVocê já executa o OAuth2 Proxy
casdoor-nginx-moduleO próprio nginxUm módulo compiladoOIDC com PKCE, tokens verificados localmenteQualquer usuário autenticado do appNenhum processo extra, além de tokens bearer para APIs
Cliente CAS no appO aplicativoNada, ele apenas faz proxyCAS 1.0, 2.0, 3.0O que o app fizer com os atributosO app já suporta CAS
Proxy de site do CasdoorCasdoor nas portas 80 e 443Sem nginxSessão do CasdoorPermissões da aplicaçãoNada mais está na frente do app

As três primeiras diferem apenas em quem responde à sub-requisição do nginx.

Por que não existe um "módulo CAS para nginx"​

CAS é uma conversa entre uma aplicação e o servidor CAS: o app envia o navegador para o /login do servidor com uma URL service, recebe um ticket de serviço de volta nessa URL e valida o ticket com uma chamada servidor-a-servidor. O Apache httpd tem o mod_auth_cas da Apereo para fazer isso no servidor web, mas o nginx não tem um módulo CAS entre seus módulos oficiais. Para o nginx, o bloco de construção padrão é ngx_http_auth_request_module: antes de servir uma requisição, o nginx envia uma sub-requisição para uma URL que você escolhe. Uma resposta 2xx permite a passagem da requisição, 401 ou 403 a nega com esse código, e qualquer outro código é um erro.

Para o usuário, um login CAS e um login OIDC parecem iguais: um redirecionamento para a página de login do Casdoor e de volta. Então, para um app sem suporte a CAS, a resposta prática é OIDC na camada do nginx. Informações básicas sobre o protocolo: O que é CAS.

O módulo auth_request não é construído por padrão: adicione --with-http_auth_request_module ao compilar, ou verifique nginx -V em um nginx empacotado.

Opção 1: Forward auth do próprio Casdoor​

O Casdoor pode responder à sub-requisição do nginx sozinho. Você adiciona um Site no Casdoor cujo Domain seja o domínio do app e cujo Casdoor app seja a aplicação onde os usuários fazem login, e adiciona https://app.example.com/caswaf-handler às Redirect URLs dessa aplicação. Host e porta podem permanecer vazios, pois o tráfego não passa pelo Casdoor.

Como auth_request não pode seguir redirecionamentos, o Casdoor responde a uma sub-requisição não autenticada com 401 e coloca a URL de login em Location. O nginx transforma isso em um 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;
}
}

O backend recebe o usuário em X-Forwarded-User, X-Forwarded-Email e X-Forwarded-Groups. O acesso segue uma permissão na aplicação, verificada novamente pelo menos uma vez por minuto, então um usuário desabilitado perde o acesso rapidamente. Os Public paths do site (webhooks, verificações de saúde) passam sem login.

Consulte Usando seu próprio proxy reverso para a referência e Adicionar login a um app sem OIDC para um tutorial do Grafana. O aplicativo só deve ser acessível através do nginx, caso contrário, qualquer pessoa pode enviar X-Forwarded-User por conta própria.

Opção 2: casdoor-forward-auth ou OAuth2 Proxy​

Se você preferir não criar um site Casdoor para cada aplicativo, execute um pequeno serviço que realiza o fluxo OIDC contra o Casdoor e responde à sub-solicitação.

casdoor-forward-auth é o serviço independente do Casdoor para isso (substituiu o traefik-casdoor-auth). Ele mantém o usuário em um cookie assinado, sem estado no servidor. Para o nginx, ele expõe /verify, que responde com 200 e cabeçalhos de identidade ou 401, e o nginx redireciona para seu /login em caso de 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;
}
}

Os parâmetros de consulta roles e groups na URL de verificação restringem cada aplicativo aos detentores de uma função Casdoor ou membros de um grupo; usuários conectados sem essas permissões recebem 403. As funções e grupos são lidos no momento do login e mantidos durante a sessão, portanto, as alterações entram em vigor após o usuário efetuar login novamente. Definir COOKIE_DOMAIN=example.com permite que um único login cubra todos os subdomínios. O Guia do Traefik aborda o mesmo serviço para o Traefik.

O OAuth2 Proxy funciona da mesma maneira com provider = "oidc" e oidc_issuer_url definido como a URL do seu Casdoor. Seu endpoint /oauth2/auth retorna 202 ou 401, os cabeçalhos de usuário e e-mail exigem --set-xauthrequest, e o tratamento de cabeçalhos encaminhados no exemplo de nginx requer --reverse-proxy. Duas configurações do Casdoor são importantes: use o formato de token JWT-Standard, caso contrário, usuários com e-mail não verificado serão rejeitados, e mantenha o escopo profile para que grupos e funções sejam retornados. Consulte OAuth2 Proxy com Casdoor e o guia da edição comunitária do nginx.

Opção 3: o módulo nginx do Casdoor​

O casdoor-nginx-module realiza o login dentro do nginx, sem um processo separado. Navegadores sem sessão são direcionados para a página de login do Casdoor; o módulo troca o código com PKCE, verifica a assinatura do token de acesso, exp, aud e tipo contra o certificado do Casdoor, e mantém o usuário em um cookie assinado. Clientes de API podem enviar Authorization: Bearer com um token de acesso do Casdoor, verificado localmente sem uma chamada ao Casdoor.

Ele requer nginx 1.22 ou mais recente e OpenSSL 1.1.1 ou mais recente. Você pode compilá-lo como um módulo dinâmico para o nginx que já executa:

./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;
}
}
}

Substitua os marcadores de posição pelos valores do aplicativo. A sessão não é verificada novamente com o Casdoor até expirar, então desabilitar um usuário entra em vigor quando a sessão termina; casdoor_session_timeout reduz esse intervalo. O módulo não possui verificações de função ou grupo. No NGINX Plus, consulte o guia do NGINX Plus em vez disso.

Opção 4: o aplicativo é um cliente CAS​

Se o aplicativo já fala CAS, como um aplicativo Java com o cliente Apereo CAS, o nginx apenas faz proxy e o Casdoor é o servidor CAS. Cada aplicação do Casdoor tem endpoints CAS sob /cas/<organization>/<application>: /login e /logout, /validate para CAS 1.0, /serviceValidate, /proxyValidate e /proxy para 2.0, /p3/serviceValidate e /p3/proxyValidate para 3.0, e /samlValidate para validação SAML 1.1.

O aplicativo envia o navegador para /login?service=..., o Casdoor redireciona de volta com ticket=ST-..., e o aplicativo valida isso:

curl "https://door.example.com/cas/my-org/my-app/p3/serviceValidate?ticket=ST-1234567890&service=https%3A%2F%2Fapp.example.com%2F"

Uma resposta bem-sucedida é um cas:serviceResponse com cas:user e atributos como e-mail, nome de exibição e memberOf; adicione format=json para JSON. Pontos que costumam causar confusão:

  • Cada URL de service deve estar listada nos Redirect URLs da aplicação, e o service na validação deve corresponder exatamente àquele para o qual o ticket foi emitido.
  • Os tickets funcionam uma única vez e expiram após 5 minutos.
  • Os tickets são mantidos na memória, então, com várias réplicas do Casdoor, a validação deve alcançar a réplica que emitiu o ticket (sessões pegajosas).
  • Atrás do nginx, passe Host e X-Forwarded-Proto para que o aplicativo construa a mesma URL de service que o navegador viu.

A configuração, incluindo os filtros do cliente Java para cada versão do CAS, está em Conectar um cliente CAS.

Opção 5: deixe o Casdoor ser o proxy reverso​

Quando nada mais está na frente do aplicativo, o Casdoor pode servi-lo diretamente: o proxy do site escuta em gatewayHttpPort e gatewayHttpsPort (80 e 443 por padrão) assim que um site existe. Defina o Host do site para o backend, escolha um certificado, defina Casdoor app e aponte o DNS para o Casdoor. Login, permissões e cabeçalhos se comportam como na opção 1. Consulte Sites.

Adicionando MFA​

MFA não precisa de nenhuma mudança no nginx ou no aplicativo. O Casdoor solicita o segundo fator em sua própria página de login antes de emitir um código de autorização OAuth ou um ticket de serviço CAS, então todas as opções acima o obtêm. Os usuários podem usar um aplicativo TOTP, SMS, e-mail ou RADIUS, com códigos de recuperação, e os administradores podem exigir MFA para uma organização com Itens MFA. Isso também responde a "CAS 2FA": com o Casdoor como servidor CAS, o segundo fator faz parte do login CAS.

MFA é aplicado no login, não por solicitação: uma sessão que o nginx ou um serviço forward-auth já possui dura até expirar, e o MFA remember time da organização permite que os usuários pulem o segundo fator em um dispositivo confiável por um tempo. Consulte o Guia MFA e O que é MFA.

Depois de conectado ao Casdoor, os usuários acessam outros aplicativos que os enviam para o Casdoor sem um novo prompt de senha; com Auto signin, o Casdoor também pula o seletor de contas. Consulte single sign-on e Single logout.

Qual escolher​

  • Sem login no aplicativo e nginx já na frente: autenticante direto do Casdoor.
  • Um login em vários subdomínios, ou verificações de função por local: casdoor-forward-auth (ou OAuth2 Proxy se você já o utiliza).
  • Você compila o nginx por conta própria e também protege APIs com tokens bearer: casdoor-nginx-module.
  • O aplicativo suporta CAS: endpoints CAS do Casdoor, nginx como proxy simples.
  • Nenhum proxy ainda: proxy de site do Casdoor.

Referências​