3 pontos por GN⁺ 2024-02-12 | 1 comentários | Compartilhar no WhatsApp
  • Procedimento de implantação para executar o Keycloak isolado com Docker rootless em uma única VM Linux, enquanto o nginx do sistema cuida da terminação TLS e do proxy reverso para o SSO
  • Os pré-requisitos são acesso SSH, um domínio ou subdomínio para o Keycloak, registro A, usuário keycloak sem senha, diretório home em /srv/keycloak e instalação do Docker rootless
  • O docker-compose.yml usa postgres:16 e quay.io/keycloak/keycloak:25.0.1, com o Keycloak vinculado apenas a 127.0.0.1:8080 e senhas e KC_HOSTNAME gerenciados no .env
  • O nginx redireciona requisições HTTP de your.tld.com para HTTPS e encaminha com proxy_pass; depois de emitir o certificado com o Certbot, ativa-se KC_PROXY_HEADERS: xforwarded
  • Se forem necessários um tema personalizado ou execução com --optimized, é possível criar uma imagem própria com um Dockerfile multi-stage; em produção, também dá para ajustar adicionalmente o tamanho do buffer do JGroups e a recuperação de transações do Quarkus

Estrutura de implantação e pré-requisitos

  • O Keycloak é uma opção de IAM open source para usar quando for difícil implementar diretamente o gerenciamento de usuários e SSO em uma aplicação web
  • É interoperável com os principais protocolos de SSO, como OpenID Connect (OIDC), OAuth 2.0 e SAML
  • A configuração foi escrita com base no Keycloak 23.0.6 e testada até a versão 25.0.5
  • A estrutura geral usa o nginx como proxy reverso central, encaminhando o tráfego para localhost dentro de namespaces rootless Docker separados por serviço
    • O tráfego web entra por 0.0.0.0:80 e 0.0.0.0:443, e o nginx o repassa para o Keycloak em 127.0.0.1:8080
    • O objetivo é uma configuração econômica que compartilha os recursos de um único host, mas separa os ambientes dos serviços

Preparação

  • O ambiente básico necessário é uma VM Linux, acesso remoto via SSH e um domínio ou subdomínio para o serviço Keycloak
    • O domínio precisa de um registro A e, opcionalmente, pode ter também um registro AAAA
  • A configuração básica do Docker rootless segue o procedimento de outro post no Mastodon
    • Criar um novo usuário non-root keycloak sem senha
    • Definir o diretório home como /srv/keycloak
    • Adicionar a faixa do usuário keycloak em /etc/subuid e /etc/subgid
    • Instalar o Docker rootless com dockerd-rootless-setuptool.sh
    • Configurar o início automático do serviço para o usuário keycloak

Executando o Keycloak com Docker Compose

  • Ao entrar no usuário keycloak recém-criado, usa-se machinectl shell keycloak@
    • O método sudo -u keycloak -H bash deve ser evitado porque a variável de ambiente XDG_RUNTIME_DIR não fica preparada
  • Primeiro, crie os diretórios de dados persistentes e dos arquivos do Docker
    • O caminho dos dados do PostgreSQL é /srv/keycloak/data/postgres16
    • Os arquivos do Docker ficam em ~/docker
  • O docker-compose.yml usa diretamente a imagem oficial do Keycloak e o PostgreSQL
    • A imagem do PostgreSQL é postgres:16
    • A imagem do Keycloak é quay.io/keycloak/keycloak:25.0.1
    • Em produção, recomenda-se usar uma tag específica da imagem em vez de :latest
  • As principais configurações do contêiner do Keycloak são as seguintes
    • KC_DB: postgres
    • KC_DB_URL: jdbc:postgresql://postgres_db/keycloak
    • KC_HOSTNAME: ${KC_HOSTNAME:-your.tld.com}
    • KC_HTTP_ENABLED: true
    • HTTP_ADDRESS_FORWARDING: true
    • KEYCLOAK_ADMIN: admin
    • A porta é vinculada apenas ao localhost com '127.0.0.1:8080:8080'
  • No .env, ficam os valores sensíveis ou que variam por ambiente
    • POSTGRES_PASSWORD
    • KEYCLOAK_ADMIN_PASSWORD
    • KC_HOSTNAME
  • A sintaxe ${KC_HOSTNAME:-your.tld.com} usa o valor de KC_HOSTNAME no .env se ele existir; caso contrário, usa your.tld.com como padrão
  • /srv/keycloak/data/postgres16 armazena os dados persistentes do PostgreSQL, então deve entrar na rotina de backup periódico
    • Para reinicializar completamente, basta apagar e recriar essa pasta do PostgreSQL; ela será criada novamente na próxima inicialização
  • Se ~/docker for mantido como repositório Git, é possível adicionar .env ao .gitignore e versionar apenas o docker-compose.yml

Teste local

  • Inicie a stack do Docker Compose e acompanhe os logs
docker compose up -d && docker compose logs --follow
  • Para acessar a porta local do Keycloak na VM, crie um túnel SSH reverso
ssh you@111.11.11.11 -L :8080:127.0.0.1:8080 -p 22 -N -v
  • No navegador, abra 127.0.0.1:8080 para verificar a tela de boas-vindas do Keycloak

Proxy reverso nginx e TLS

  • Depois de sair do usuário keycloak, faça a configuração do nginx do sistema
  • Troque your.tld.com pelo domínio real e, no registrador do domínio, adicione o registro A para que as consultas DNS apontem para o IP da VM
  • Crie e ative o arquivo de configuração do site no nginx
nano /etc/nginx/sites-available/your.tld.com.conf
ln -s /etc/nginx/sites-available/your.tld.com.conf /etc/nginx/sites-enabled/
  • A configuração do nginx redireciona requisições HTTP para HTTPS e, no bloco do servidor HTTPS, faz o proxy para o Keycloak
    • proxy_set_header Host $host
    • proxy_set_header X-Real-IP $remote_addr
    • proxy_set_header X-Forwarded-For $remote_addr
    • proxy_set_header X-Forwarded-Proto $scheme
    • proxy_pass http://127.0.0.1:8080
  • Para a configuração SSL, recomenda-se usar o Mozilla SSL configurator for nginx para gerar valores padrão adequados à versão do nginx
  • Após testar a configuração, recarregue o nginx
nginx -t
systemctl reload nginx
  • Ao emitir o certificado com o Certbot, as linhas necessárias em your.tld.com.conf são atualizadas automaticamente
certbot --nginx -d your.tld.com
  • Depois disso, ative ssl_trusted_certificate /etc/letsencrypt/live/your.tld.com/chain.pem; e recarregue o nginx novamente
  • Para funcionar atrás do nginx, ative KC_PROXY_HEADERS: xforwarded no docker-compose.yml e reinicie a stack Docker
docker compose down && docker compose up -d && docker compose logs --follow

Caminho de depuração

  • Depois da configuração, acesse your.tld.com e confirme o login do usuário admin com a senha definida no .env
  • O primeiro ponto de verificação são os logs do Docker Compose
docker compose logs --follow
  • Os logs de acesso e erro do nginx podem ser acompanhados nestes arquivos
tail -f /var/log/nginx/your.tld.com-access.log
tail -f /var/log/nginx/your.tld.com-error.log
  • Se for necessário verificar diretamente o banco de dados do Keycloak, entre no usuário keycloak e execute o psql dentro do contêiner PostgreSQL
machinectl shell keycloak@
cd ~/docker
docker compose exec postgres_db /bin/bash
psql -h localhost -p 5432 -U keycloak keycloak

Imagem personalizada e execução optimized

  • A configuração básica usa a imagem pré-compilada do quay.io
  • Se forem necessários personalização de tema ou execução no modo --optimized, construa uma imagem própria
  • O Dockerfile usa a imagem oficial do Keycloak com build multi-stage
    • Na etapa builder, define ENV KC_DB=postgres
    • Executa /opt/keycloak/bin/kc.sh build
    • Copia /opt/keycloak/ para a imagem final
    • Define ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
  • No docker-compose.yml, faça as seguintes mudanças
    • Remova ou comente a linha image:
    • Ative build: .
    • Adicione command: start --optimized
  • Depois disso, derrube a stack Docker, opcionalmente execute o build explícito e inicie novamente
docker compose down
docker compose build
docker compose up -d && docker compose logs --follow

Ajustes adicionais feitos em produção

  • O aviso de buffer de recepção do MulticastSocket relacionado ao JGroups foi resolvido adicionando valores de buffer no /etc/sysctl.conf do host
net.core.rmem_max = 26214400
net.core.wmem_max = 1048576
  • Para aplicar a configuração, execute sysctl -p
  • O aviso de recuperação de transações XA do Quarkus foi resolvido adicionando um volume mount e uma variável de ambiente ao serviço Keycloak
    • Volume: /srv/keycloak/data/keycloak/ObjectStore/:/ObjectStore/
    • Variável de ambiente: QUARKUS_TRANSACTION_MANAGER_ENABLE_RECOVERY: true
  • Antes da inicialização, crie o diretório ObjectStore no host e, dentro do contêiner, altere o proprietário de /ObjectStore para o user ID 1000

Etapas após a configuração

  • O estado final é uma configuração em que o serviço Keycloak dentro do Docker rootless roda atrás do proxy reverso nginx do sistema, e o nginx faz a terminação SSL
  • Para atualização automática dos contêineres, consulte o procedimento de atualização automática em outro post no Mastodon
  • A próxima etapa de configuração é adicionar e-mail no console administrativo do Keycloak
  • Depois disso, é possível adicionar realms e configurar temas
    • Para temas, podem ser usados keycloakify e keycloakify-starter
    • O Dockerfile inclui uma linha comentada para copiar o JAR de tema do keycloakify para /opt/keycloak/providers/

1 comentários

 
GN⁺ 2024-02-12
Opiniões no Hacker News
  • Recentemente, ao adicionar autenticação ao meu homelab, escolhi o Authelia
    O Keycloak também funciona, mas é grande demais e, para usá-lo com o forward auth do traefik, é preciso mais um serviço adicional
    O Authelia não tem uma UI para edição de usuários e também não faz sincronização bidirecional com um servidor LDAP de backend, mas pode ser configurado apenas com arquivos estáticos e variáveis de ambiente, então se encaixa bem em muitos casos
    Se a ideia é adicionar autenticação a alguns serviços e colocar SSO nos que suportam, vale começar pelo Authelia

    • Trabalho na FusionAuth
      Se você precisa de SSO, configuração fácil e UI administrativa, o FusionAuth também vale uma olhada. A UI/UX tem mesmo um ar de meados dos anos 2000, mas você pode baixá-lo e executá-lo diretamente[0], ele é amigável a Docker[1] e oferece várias formas de configuração[2], como gerenciar OIDC ou outras configurações com Terraform[3]
      Pode ser usado gratuitamente, mas não é open source[4]
      0: https://fusionauth.io/download
      1: https://fusionauth.io/docs/get-started/download-and-install/...
      2: https://fusionauth.io/docs/operate/deploy/configuration-mana...
      3: https://fusionauth.io/docs/operate/deploy/terraform
      4: https://fusionauth.io/license-faq#28
    • Estou rodando o Authelia há mais de 2 anos e configurei a integração LDAP com o LLDAP[0], uma implementação leve de LDAP
      Uso o Caddy como proxy reverso e o integrei ao Authelia[1], e funciona bem
      Coloquei autenticação de dois fatores robusta em todos os serviços e sinto que é suficientemente seguro acessar meus apps auto-hospedados sem VPN
      Porém, como o Authelia não tem um novo release há mais de um ano, fico preocupado do ponto de vista de segurança
      [0] https://github.com/lldap/lldap
      [1] https://www.authelia.com/integration/proxies/caddy/
    • Recentemente segui um caminho parecido e, pessoalmente, escolhi o caddy-security[1], um plugin do Caddy
      [1] https://github.com/greenpau/caddy-security
    • O Authelia tem uma vantagem muito particular: instalações pequenas; Keycloak ou authentik dificilmente se encaixam bem nessa área
      É um caso de uso muito bom para ambientes de homelab, especialmente se você não precisa ou não quer os recursos das soluções maiores
    • Fugindo um pouco do assunto, tenho curiosidade se hoje existem alternativas ao LDAP
      Procurei e não encontrei algo em formatos como schema simples, JSON ou HTTP, e é difícil acreditar que quase todo mundo que reinventou tudo não tenha reinventado o LDAP
      Pelo que sei, na prática o único outro padrão é o Active Directory
      Também tenho curiosidade se existe algum padrão ou protocolo para delegar o controle de acesso para fora
      O Authelia consegue controlar acesso por padrões de URL, mas, por exemplo, no caso de um servidor de arquivos, eu esperaria algo como verificar permissões no servidor LDAP com base no ID do usuário autenticado e em uma chave no banco de dados
      Isso parece ir na direção oposta do OAuth2, em que o servidor obtém permissão para acessar um serviço de terceiros
  • Recentemente, estive pesquisando como configurar um SSO relativamente simples no meu homelab, e o objetivo principal era facilitar o login com autenticação do Google ou do GitHub
    No meu emprego anterior, usei tanto o JetBrains Hub[1] quanto o Keycloak, e ambos eram bem chatos de configurar
    O JetBrains Hub era realmente muito fácil de começar a usar, e minha experiência anterior também foi assim, mas era inconveniente que o registro Docker não tivesse a tag latest
    Sei que fixar versões é uma boa prática, mas para uso pessoal normalmente quero atualizar todos os contêineres Docker de uma vez
    Já o Keycloak foi muito trabalhoso para começar; no modo de desenvolvimento era fácil, mas a configuração de produção travou
    Pelo que me lembro, tinha a ver com um certificado wildcard do Let's Encrypt, e depois de algumas horas desisti
    No fim, escolhi o Dex[2]. Eu tinha adiado por falta de documentação, mas a configuração real foi muito fácil: bastaram um YAML básico, um banco de dados SQLite e um subdomínio
    Combinei o Dex com o excelente OAuth2 Proxy[3] e um template personalizado do Nginx Proxy Manager para transformar isso em uma configuração de SSO de duas linhas para cada serviço interno, e também criei um template Docker do Dex para unRAID[4]
    Fora de casa, acrescentei Cloudflare Access e WAF para reforçar a segurança, e só gostaria de adicionar o CrowdSec para obter um pouco mais de visibilidade

    1. https://www.jetbrains.com/hub/
    2. https://dexidp.io/
    3. https://github.com/oauth2-proxy/oauth2-proxy
    4. https://github.com/alex3305/unraid-docker-templates
    • Estou usando o obligator com armazenamento temporário, sem banco de dados e configuração 100% baseada em código
      Pessoalmente, acho que essa é a opção mais simples
      https://github.com/lastlogin-io/obligator
    • Fico curioso sobre quais recursos o oauth2-proxy oferece que não existem no Dex
  • Criei uma tabela comparativa incompleta de vários servidores OpenID Connect auto-hospedáveis[0]
    Uma das coisas que me surpreendeu foi como a base de código do Keycloak é realmente enorme
    [0]: https://github.com/lastlogin-io/obligator?tab=readme-ov-file...

  • É engraçada a ideia de que o Keycloak resolve problemas de segurança
    Dê uma olhada na lista de CVEs e você vai entender o que quero dizer

  • Pelo menos foi muito útil o artigo mencionar https://www.keycloakify.dev/
    Parece uma ótima alternativa à abordagem padrão de temas

    • É um projeto excelente
      Uso há 2 anos e só tenho coisas boas a dizer
      O mantenedor responde muito rápido, e recentemente houve mudanças tanto no Keycloak quanto no keycloakify para melhorar a compatibilidade futura da abordagem de temas
      Atualmente, oficialmente, só há suporte para apps criados com create-react-app, mas o branch vite está em desenvolvimento e, pessoalmente, já venho usando com vite há algum tempo
    • Trabalho em uma concorrente e estamos refazendo o sistema de temas, então este projeto parece bom para analisar e usar como modelo para a reformulação
  • Estou de olho no authentik[1] e no authelia[2]
    O Authelia parece muito bom, mas fiquei um pouco cauteloso porque o Keycloak tem um conector para Angular, enquanto no Authelia seria preciso configurar manualmente, por exemplo, um plugin OIDC para Angular
    Ainda assim, se houver uma configuração para Keycloak, acho que fica mais fácil começar
    [1] https://goauthentik.io/
    [2] https://www.authelia.com/

    • Para quem estiver considerando o authentik, eu avisaria que é “território perigoso”
      Eu protejo constantemente mais de 10 serviços em Docker e Kubernetes e, se você não gosta de configurar a proteção de cada serviço de forma independente, vai sofrer com o authentik
      O authentik tem um bug grave[0] em que, ao proteger vários subdomínios (app1.example.com, app2.example.com etc.) com uma única configuração, quando o usuário se reautentica após a sessão expirar, ele é redirecionado aleatoriamente para outro serviço
      [0]: https://github.com/goauthentik/authentik/issues/6886
    • O Authentik estragou completamente a implementação do grant de credenciais de cliente do OAuth
      Não dá para corrigir sem uma mudança que quebre compatibilidade, e ele não funciona com muitas ferramentas que usam o grant cc
      Depois de ver isso, risquei totalmente da lista de candidatos
      https://github.com/goauthentik/authentik/issues/6139
    • Sou um dos mantenedores principais do Authelia
      Se houver algo em que eu possa ajudar na configuração do Angular, ficarei feliz em ajudar via GitHub Discussions
    • Estou tentando tomar uma decisão parecida agora
      O Authentik parecia melhor, mas o bug mencionado em outra resposta parece bem ruim
  • O problema do Keycloak é que ele é um projeto antigo e passou por mudanças enormes
    Começou como um projeto JBOSS, e sua utilidade como IdP brilha na autenticação de clusters on-premises, mas acho que para por aí
    Implementei o Keycloak em larga escala no AWS ECS em um departamento de uma Fortune 500, e fazer o clustering funcionar direito pareceu uma Guerra dos Mil Anos
    A descoberta por DNS não funcionava corretamente, e a descoberta de cluster era baseada em UDP, então não funcionava em ambientes de nuvem
    Um login com estado em um servidor não existia em outro, então balanceamento de carga simples era impossível, e sessões fixas eram a única opção
    É fácil subir o Keycloak com docker run e plugá-lo como se fosse o Auth0, mas a sensação é de comprar uma Ford F-150 1996 com 250 mil milhas rodadas
    Ela anda e funciona, mas a manutenção é realmente terrível

    • Fiz isso em escala menor, mas dentro de um Docker Swarm on-premises, e foi realmente doloroso
      Pelo que lembro, o método padrão de descoberta usa multicast, que não vem habilitado em redes de nuvem comuns nem em redes overlay do Swarm/Kubernetes
      Também vi o database ping, que usa um RDBMS como uma espécie de mecanismo de quórum, mas parecia muito frágil e com cara de último recurso
      No fim, consegui usar um driver de cluster Kubernetes que descobria os nós pelo DNS do cluster Swarm
      Foi bem difícil fazer funcionar, mas desde então, até onde sei, está estável
      Hoje em dia parece que também existe um driver de rede EC2 nativo, mas não cheguei a investigá-lo pessoalmente
    • Ouvi dizer que houve melhorias depois da migração completa para Quarkus
      Fico curioso para saber quando foi essa implantação no ECS
    • OAuth e OpenID Connect, e a possibilidade de adicionar apps e clientes a um realm, foram a salvação do Keycloak e a única razão pela qual continuamos com ele
    • Ouvir que a descoberta de cluster era via UDP dói
      Nós só permitimos UDP para DNS
  • O Keycloak é ótimo, mas pode ser bastante confuso para iniciantes
    Tem muitos recursos e a documentação não é das melhores
    Testei recentemente o Zitadel e achei muito mais fácil de usar
    Porém, como não dá para executá-lo com um banco de dados embutido, a hospedagem própria fica um pouco mais difícil

    • Sim, ele precisa do próprio banco de dados
      A direção daqui para frente é usar Postgres como banco de dados, então espero que fique mais fácil implantá-lo junto com outras ferramentas
  • Meu chefe recentemente chamou o Keycloak de “um presente que não para de dar”, mas na verdade queria dizer que ele continua gerando novos tickets no Jira para descobrir como fazer alguma coisa
    Ainda assim, temos Terraform que cria um cluster EKS e implanta o Keycloak, cria clientes SAML/OIDC, adiciona provedores de identidade externos e até configura um AWS IAM Identity Provider
    Depois que você descobre o que dá para fazer, como fazer pela UI e como automatizar com o Terraform provider do mrparkers, o uso em si fica muito fácil

    • Fico me perguntando se esse Terraform está publicado como open source em algum lugar
      Um amigo precisa dele :)
  • Depois de usar o Keycloak por alguns anos, sinceramente me cansei de todos os comportamentos peculiares e comecei a procurar alternativas
    Vários candidatos, como o Authentik, pareciam bons, mas o Zitadel[1] chamou minha atenção, e desde então nunca olhei para trás
    [1] https://zitadel.com/blog/zitadel-vs-keycloak

    • Que bom que você gostou
      Fico curioso para saber o que foi decisivo para te atrair