Domínio, HTTPS e proxies reversos

Coloque o Antimatter num nome de domínio com HTTPS, utilizando o perfil Caddy do ficheiro compose ou um proxy reverso próprio, e configure corretamente a URL do site e os endereços dos clientes.

A URL do site

A URL do site (ServiceSettings.SiteURL) é o endereço que as pessoas utilizam para aceder ao Antimatter, como https://chat.example.com. O servidor utiliza-a nos links dos e-mails e notificações, nos redirecionamentos de início de sessão (OAuth, OpenID Connect, SAML), para as aplicações móveis e de desktop, e para verificar a origem das pedidos. Defina-a exatamente como o endereço na barra de endereços do navegador: esquema, domínio e porta (se não for a predefinida), sem barra final.

  • Com o ficheiro compose do site: SITE_URL no .env.
  • Em qualquer outro lugar: AM_SERVICESETTINGS_SITEURL, ou Consola do sistema › Ambiente › Servidor web › URL do site.

Se a URL do site estiver incorreta, o início de sessão com um provedor de identidade falhará, os links nos e-mails apontarão para o local errado e as aplicações podem não conseguir ligar. Após alterar o domínio, atualize-a aqui e nos URLs de redirecionamento de todos os provedores de identidade.

HTTPS com o perfil Caddy incluído

O ficheiro compose.yaml do site inclui um serviço Caddy no perfil https. Este obtém um certificado da Let's Encrypt, renova-o automaticamente, redireciona o HTTP para HTTPS e encaminha tudo, incluindo websockets, para o Antimatter.

  1. Aponte um registo DNS (A, e AAAA para IPv6) do seu domínio para o servidor.
  2. Abra as portas 80/tcp, 443/tcp e 443/udp (HTTP/3) no firewall. A Let's Encrypt deve conseguir aceder à porta 80 ou 443 a partir da Internet.
  3. Adicione o domínio ao .env e defina a URL do site:
    echo "DOMAIN=chat.example.com" >> .env
    sed -i 's|^SITE_URL=.*|SITE_URL=https://chat.example.com|' .env
  4. Inicie com o perfil:
    docker compose --profile https up -d

Adicione --profile https a todos os docker compose up subsequentes, ou coloque COMPOSE_PROFILES=https no .env para que um docker compose up -d simples o inclua. O Caddy guarda os seus certificados no volume https.

A própria porta do Antimatter continua a ser publicada no host (HTTP_PORT, 8065 por predefinição). Com o Caddy à frente, mantenha-a apenas para o próprio servidor:

echo "HTTP_PORT=127.0.0.1:8065" >> .env
docker compose --profile https up -d

O Caddy acede ao Antimatter através da rede interna edge, não através dessa porta, pelo que isto não altera nada para os seus utilizadores.

Com o seu próprio proxy reverso (nginx)

Se o servidor já executar o nginx ou outro proxy, desative o perfil https e aponte o proxy para a porta 8065 do Antimatter. O proxy deve:

  • transmitir as atualizações de websocket (a aplicação web e as aplicações móveis mantêm um websocket aberto em /api/v4/websocket, e algumas aplicações abrem os seus próprios);
  • aceitar corpos de pedido pelo menos do tamanho do maior carregamento (Tamanho máximo do ficheiro, 100 MB por omissão);
  • manter as ligações de longa duração abertas (websockets, carregamentos e descargas grandes);
  • transmitir o cabeçalho Host e o endereço do cliente.

Um bloco de servidor completo, adaptado do modelo de produção em antimatter-docker. Substitua o domínio e os caminhos dos certificados:

upstream antimatter {
    server 127.0.0.1:8065;
    keepalive 64;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      '';
}

proxy_cache_path /var/cache/nginx/antimatter levels=1:2 keys_zone=antimatter_cache:10m
                 max_size=3g inactive=120m use_temp_path=off;

server {
    listen 80;
    listen [::]:80;
    server_name chat.example.com;
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name chat.example.com;

    ssl_certificate     /etc/ssl/chat.example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/chat.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_timeout 1d;
    ssl_session_cache shared:AntimatterSSL:10m;
    ssl_session_tickets off;

    add_header Strict-Transport-Security "max-age=31536000" always;

    # At least FileSettings.MaxFileSize (100 MB by default).
    client_max_body_size 100M;

    proxy_http_version 1.1;
    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_set_header Upgrade           $http_upgrade;
    proxy_set_header Connection        $connection_upgrade;
    proxy_buffers 256 16k;
    proxy_buffer_size 16k;

    location ~ ^/api/v[0-9]+/(users/)?websocket$ {
        client_body_timeout 60s;
        send_timeout 300s;
        lingering_timeout 5s;
        proxy_connect_timeout 90s;
        proxy_send_timeout 300s;
        proxy_read_timeout 90s;
        proxy_pass http://antimatter;
    }

    location / {
        proxy_read_timeout 600s;
        proxy_cache antimatter_cache;
        proxy_cache_revalidate on;
        proxy_cache_min_uses 2;
        proxy_cache_use_stale timeout;
        proxy_cache_lock on;
        proxy_pass http://antimatter;
    }
}

Verifique o ficheiro com nginx -t e aplique-o com systemctl reload nginx. O servidor envia X-Content-Type-Options, Referrer-Policy, Permissions-Policy e X-Frame-Options por si mesmo; apenas o HSTS fica a cargo do proxy. Aumente o seu max-age quando o HTTPS estiver a funcionar e adicione includeSubDomains apenas se todos os subdomínios usarem HTTPS.

Com Docker Compose e nginx no anfitrião, o upstream acima aponta para a porta 8065 publicada. Publique-a apenas em 127.0.0.1 (HTTP_PORT=127.0.0.1:8065), para que ninguém contorne o proxy.

O repositório antimatter-docker tem uma configuração completa com nginx num contentor e Let's Encrypt através do certbot, em deploy/compose.

Outros proxies

Qualquer proxy funciona se cumprir os quatro pontos acima. As chamadas de áudio e vídeo nunca passam pelo proxy: usam a sua própria porta, veja Chamadas e voz.

Endereços de clientes atrás de um proxy

Atrás de um proxy, todos os pedidos parecem vir do proxy. Para registar o endereço real do cliente, aplicar limites de taxa por cliente e usar filtragem de IP, indique ao servidor qual o cabeçalho que transporta o endereço e quais os proxies em que pode confiar. Nenhuma destas definições tem um campo na Consola do sistema: configure-as em config.json ou com variáveis.

Chave e variávelPadrãoO que faz
ServiceSettings.TrustedProxyIPHeader, AM_SERVICESETTINGS_TRUSTEDPROXYIPHEADERvazio: o servidor usa o endereço da ligaçãoOs cabeçalhos que transportam o endereço do cliente, por exemplo X-Forwarded-For. O primeiro presente num pedido é utilizado.
ServiceSettings.TrustedProxyIPs, AM_SERVICESETTINGS_TRUSTEDPROXYIPSvazioOs endereços ou intervalos CIDR dos seus proxies, por exemplo 172.29.71.0/28. Os cabeçalhos só contam para pedidos que venham de um deles.

Numa variável, separe vários valores com espaços: AM_SERVICESETTINGS_TRUSTEDPROXYIPHEADER="X-Forwarded-For X-Real-IP". O servidor rejeita uma configuração com uma entrada em TrustedProxyIPs que não seja um endereço IP nem um intervalo CIDR.

Como o servidor encontra o endereço do cliente

Cada proxy adiciona o endereço de onde recebeu o pedido à direita de X-Forwarded-For. O que o cliente enviou no próprio cabeçalho permanece à esquerda. Assim, o servidor lê o cabeçalho a partir da direita, ignora os endereços dos seus proxies de confiança e toma o primeiro outro endereço como o do cliente. Um cliente não pode escolher o seu endereço enviando o seu próprio cabeçalho.

  • Atrás de um proxy, apenas TrustedProxyIPHeader fornece o endereço correto: o servidor toma o endereço mais à direita do cabeçalho. Regista um aviso no arranque até também definir TrustedProxyIPs. Sem ele, qualquer pessoa que aceda diretamente à porta 8065 pode definir o cabeçalho, pelo que publique essa porta apenas em 127.0.0.1 ou numa rede privada.
  • Atrás de vários proxies, por exemplo um CDN ou um balanceador de carga à frente do seu próprio proxy, liste todos eles em TrustedProxyIPs. Caso contrário, o servidor toma o endereço do proxy externo como o do cliente.
  • O proxy pode substituir o cabeçalho pelo endereço que vê, como faz o exemplo nginx acima (X-Forwarded-For $remote_addr), ou acrescentar-lhe (o $proxy_add_x_forwarded_for do nginx). Ambos funcionam.

Que endereços listar

Liste o endereço de onde vêm os pedidos do proxy, tal como o Antimatter o vê.

  • Com o ficheiro compose do site e o Caddy, isto já está feito: o Caddy acede ao Antimatter através da rede edge, e o ficheiro compose define TrustedProxyIPHeader como X-Forwarded-For e TrustedProxyIPs como o intervalo dessa rede, EDGE_SUBNET (172.29.71.0/28, a menos que o altere em .env). Altere EDGE_SUBNET se este se sobrepor a uma das suas redes.
  • A configuração de produção em antimatter-docker faz o mesmo para o seu contentor nginx, com FRONTEND_SUBNET.
  • Com nginx no host e Antimatter no Docker, os pedidos chegam ao Antimatter através da porta publicada, normalmente a partir do endereço do gateway da rede do projeto compose (docker network inspect mostra-o). Defina AM_SERVICESETTINGS_TRUSTEDPROXYIPS para esse endereço em compose.override.yaml.
  • Sem Docker, com nginx no mesmo host, é 127.0.0.1 (e ::1 se o nginx se ligar através de IPv6).

TLS sem proxy

O servidor também pode servir HTTPS diretamente, em Consola do sistema › Ambiente › Servidor web: Segurança da ligação definido como TLS (ServiceSettings.ConnectionSecurity), com Ficheiro de certificado TLS e Ficheiro de chave privada TLS (ServiceSettings.TLSCertFile, ServiceSettings.TLSKeyFile), ou Utilizar Let’s Encrypt (ServiceSettings.UseLetsEncrypt). O servidor deve, nesse caso, escutar na porta 443 (Endereço de escuta, ServiceSettings.ListenAddress, :8065 por omissão), e ServiceSettings.Forward80To443 redireciona a porta 80. Um proxy reverso é mais fácil de executar e renovar, e é o que este guia pressupõe.

Tempos limite do servidor e tamanho das pedidos

Definições do próprio servidor web do servidor, em Consola do sistema › Ambiente › Servidor web:

DefiniçãoChavePadrão
Endereço de escutaServiceSettings.ListenAddress:8065
Tempo limite de leituraServiceSettings.ReadTimeout300 segundos
Tempo limite de escritaServiceSettings.WriteTimeout300 segundos
Tempo limite de inatividade (sem campo na Consola do sistema)ServiceSettings.IdleTimeout60 segundos
Tamanho máximo da carga (bytes)ServiceSettings.MaximumPayloadSizeBytesver Segurança

Mantenha os tempos limite do próprio proxy pelo menos tão longos como estes para pedidos normais, e o tamanho do corpo pelo menos Tamanho máximo do ficheiro.