Domaine, HTTPS et proxys inverses

Placez Antimatter sur un nom de domaine avec HTTPS, en utilisant le profil Caddy du fichier compose ou un proxy inverse de votre choix, et assurez-vous que l'URL du site et les adresses des clients sont correctes.

L'URL du site

L'URL du site (ServiceSettings.SiteURL) est l'adresse utilisée pour accéder à Antimatter, par exemple https://chat.example.com. Le serveur l'utilise dans les liens des e-mails et des notifications, lors des redirections de connexion (OAuth, OpenID Connect, SAML), pour les applications mobiles et de bureau, et pour vérifier l'origine des requêtes. Définissez-la exactement comme l'adresse affichée dans la barre d'adresse du navigateur : schéma, domaine et port (si différent du port par défaut), sans barre oblique finale.

  • Avec le fichier compose du site : SITE_URL dans .env.
  • Ailleurs : AM_SERVICESETTINGS_SITEURL, ou Console système › Environnement › Serveur web › URL de site.

Si l'URL du site est incorrecte, la connexion via un fournisseur d'identité échoue, les liens dans les e-mails pointent vers la mauvaise destination et les applications peuvent ne pas se connecter. Après avoir changé de domaine, mettez à jour cette URL ici ainsi que dans les URL de redirection de chaque fournisseur d'identité.

HTTPS avec le profil Caddy inclus

Le fichier compose.yaml du site inclut un service Caddy dans le profil https. Il obtient un certificat auprès de Let's Encrypt, le renouvelle automatiquement, redirige HTTP vers HTTPS et transfère tout, y compris les websockets, à Antimatter.

  1. Pointez un enregistrement DNS (A, et AAAA pour IPv6) de votre domaine vers le serveur.
  2. Ouvrez les ports 80/tcp, 443/tcp et 443/udp (HTTP/3) dans le pare-feu. Let's Encrypt doit pouvoir accéder au port 80 ou 443 depuis Internet.
  3. Ajoutez le domaine à .env et définissez l'URL du site :
    echo "DOMAIN=chat.example.com" >> .env
    sed -i 's|^SITE_URL=.*|SITE_URL=https://chat.example.com|' .env
  4. Démarrez avec le profil :
    docker compose --profile https up -d

Ajoutez --profile https à chaque commande docker compose up ultérieure, ou placez COMPOSE_PROFILES=https dans .env pour que la simple commande docker compose up -d l'inclue. Caddy conserve ses certificats dans le volume https.

Le port propre à Antimatter reste publié sur l'hôte (HTTP_PORT, 8065 par défaut). Avec Caddy devant, conservez-le uniquement pour le serveur lui-même :

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

Caddy accède à Antimatter via le réseau interne edge, et non par ce port, ce qui ne change rien pour vos utilisateurs.

Avec votre propre proxy inverse (nginx)

Si le serveur exécute déjà nginx ou un autre proxy, désactivez le profil https et pointez le proxy vers le port 8065 d'Antimatter. Le proxy doit :

  • transmettre les mises à niveau websocket (l'application web et les applications mobiles maintiennent une connexion websocket ouverte sur /api/v4/websocket, et certaines applications en ouvrent d'autres) ;
  • accepter des corps de requête au moins aussi volumineux que la plus grande pièce jointe (Taille maximale de fichier, 100 Mo par défaut) ;
  • maintenir les connexions de longue durée (websockets, envois et téléchargements volumineux) ;
  • transmettre l'en-tête Host et l'adresse du client.

Un bloc de serveur complet, adapté du modèle de production d'antimatter-docker. Remplacez le nom de domaine et les chemins des certificats :

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

Vérifiez le fichier avec nginx -t et appliquez-le avec systemctl reload nginx. Le serveur envoie lui-même les en-têtes X-Content-Type-Options, Referrer-Policy, Permissions-Policy et X-Frame-Options ; seul HSTS relève du proxy. Augmentez sa valeur max-age une fois que HTTPS fonctionne, et n'ajoutez includeSubDomains que si tous les sous-domaines utilisent HTTPS.

Avec Docker Compose et nginx sur l'hôte, le upstream ci-dessus pointe vers le port 8065 publié. Publiez-le uniquement sur 127.0.0.1 (HTTP_PORT=127.0.0.1:8065) pour empêcher tout contournement du proxy.

Le dépôt antimatter-docker propose une configuration complète avec nginx dans un conteneur et Let's Encrypt via certbot, dans deploy/compose.

Autres proxies

Tout proxy fonctionne s'il respecte les quatre points ci-dessus. L'audio et la vidéo des appels ne passent jamais par le proxy : ils utilisent leur propre port, voir Appels et vocal.

Adresses des clients derrière un proxy

Derrière un proxy, chaque requête semble provenir du proxy. Pour journaliser l'adresse réelle du client, appliquer des limites de débit par client et utiliser le filtrage par IP, indiquez au serveur quel en-tête contient l'adresse et quels proxies il peut croire. Aucun de ces réglages n'a de champ dans la Console système : configurez-les dans config.json ou avec des variables.

Clé et variableValeur par défautFonction
ServiceSettings.TrustedProxyIPHeader, AM_SERVICESETTINGS_TRUSTEDPROXYIPHEADERvide : le serveur utilise l'adresse de la connexionLes en-têtes qui contiennent l'adresse du client, par exemple X-Forwarded-For. Le premier présent dans une requête est utilisé.
ServiceSettings.TrustedProxyIPs, AM_SERVICESETTINGS_TRUSTEDPROXYIPSvideLes adresses ou plages CIDR de vos proxies, par exemple 172.29.71.0/28. Les en-têtes ne sont pris en compte que pour les requêtes provenant de l'un d'entre eux.

Dans une variable, séparez plusieurs valeurs par des espaces : AM_SERVICESETTINGS_TRUSTEDPROXYIPHEADER="X-Forwarded-For X-Real-IP". Le serveur refuse une configuration contenant une entrée dans TrustedProxyIPs qui n'est ni une adresse IP ni une plage CIDR.

Comment le serveur détermine l'adresse du client

Chaque proxy ajoute à droite de X-Forwarded-For l'adresse à partir de laquelle il a reçu la requête. Ce que le client a lui-même envoyé dans l'en-tête reste à gauche. Le serveur lit donc l'en-tête de droite à gauche, ignore les adresses de vos proxies de confiance et retient la première autre adresse comme étant celle du client. Un client ne peut pas choisir son adresse en envoyant son propre en-tête.

  • Derrière un seul proxy, TrustedProxyIPHeader seul fournit la bonne adresse : le serveur prend l'adresse la plus à droite de l'en-tête. Il journalise un avertissement au démarrage jusqu'à ce que vous définissiez également TrustedProxyIPs. Sans cela, quiconque accède directement au port 8065 peut définir l'en-tête, il est donc recommandé d'exposer ce port uniquement sur 127.0.0.1 ou sur un réseau privé.
  • Derrière plusieurs proxies, par exemple un CDN ou un équilibreur de charge devant votre propre proxy, listez-les tous dans TrustedProxyIPs. Sinon, le serveur prend l'adresse du proxy externe pour celle du client.
  • Le proxy peut remplacer l'en-tête par l'adresse qu'il voit, comme dans l'exemple nginx ci-dessus (X-Forwarded-For $remote_addr), ou l'ajouter à l'en-tête existant (via $proxy_add_x_forwarded_for de nginx). Les deux fonctionnent.

Quelles adresses lister

Listez l'adresse à partir de laquelle les requêtes du proxy proviennent, telle qu'elle est perçue par Antimatter.

  • Avec le fichier compose du site et Caddy, c'est déjà fait : Caddy accède à Antimatter via le réseau edge, et le fichier compose définit TrustedProxyIPHeader sur X-Forwarded-For et TrustedProxyIPs sur la plage de ce réseau, EDGE_SUBNET (172.29.71.0/28 sauf si vous la modifiez dans .env). Modifiez EDGE_SUBNET si elle chevauche l'un de vos réseaux.
  • La configuration de production dans antimatter-docker fait de même pour son conteneur nginx, avec FRONTEND_SUBNET.
  • Avec nginx sur l'hôte et Antimatter dans Docker, les requêtes atteignent Antimatter via le port publié, généralement depuis l'adresse passerelle du réseau du projet compose (visible avec docker network inspect). Définissez AM_SERVICESETTINGS_TRUSTEDPROXYIPS sur cette adresse dans compose.override.yaml.
  • Sans Docker, avec nginx sur le même hôte, il s’agit de 127.0.0.1 (et de ::1 si nginx se connecte via IPv6).

TLS sans proxy

Le serveur peut également fournir lui-même HTTPS, dans Console système › Environnement › Serveur web : Connexion sécurisée réglé sur TLS (ServiceSettings.ConnectionSecurity), avec Fichier du certificat TLS et Fichier de la clé TLS (ServiceSettings.TLSCertFile, ServiceSettings.TLSKeyFile), ou Utiliser Let's Encrypt (ServiceSettings.UseLetsEncrypt). Le serveur doit alors écouter sur le port 443 (Adresse IP du serveur, ServiceSettings.ListenAddress, :8065 par défaut), et ServiceSettings.Forward80To443 redirige le port 80. Un proxy inverse est plus simple à exécuter et à renouveler, et c'est ce que suppose ce guide.

Délais d’expiration du serveur et taille des requêtes

Paramètres du serveur Web propre au serveur, dans Console système › Environnement › Serveur Web :

ParamètreParamètreValeur par défaut
Adresse IP du serveur ServiceSettings.ListenAddress:8065
Délai d'attente de lectureServiceSettings.ReadTimeout300 secondes
Délai d'attente d'écritureServiceSettings.WriteTimeout300 secondes
Délai d’expiration en veille (aucun champ dans la Console système)ServiceSettings.IdleTimeout60 secondes
Taille maximale de la charge utile (octets)ServiceSettings.MaximumPayloadSizeBytesvoir Sécurité

Gardez les délais d'attente du proxy au moins aussi longs que ceux-ci pour les requêtes normales, et sa taille de corps au moins égale à la Taille maximale de fichier.