Apache HTTP Server
Déployez RustFS derrière Apache HTTP Server avec une terminaison TLS pour des noms d’hôte distincts de l’API S3 et de la Console.
Utilisez Apache HTTP Server (httpd) pour terminer le TLS et acheminer des noms d’hôte distincts vers l’API S3 et la Console de RustFS. Ce déploiement exécute httpd et une instance RustFS à nœud unique avec Docker Compose. Vous avez besoin de Docker Engine, de Docker Compose, de deux enregistrements DNS et d’un certificat TLS qui couvre les deux noms d’hôte.
Ce guide utilise ces noms d’hôte d’exemple :
s3.example.compour l’API S3console.example.compour la Console
Remplacez-les par des noms d’hôte qui pointent vers l’hôte Docker.
Servir l’API S3 depuis le chemin racine
Ne publiez pas l’API S3 sous un chemin tel que /s3/. AWS Signature Version 4 inclut le chemin et l’hôte de la requête, de sorte que la réécriture de l’une ou l’autre de ces valeurs peut invalider les requêtes signées. Apache réécrit les URL transmises par le proxy, sauf si vous désactivez la canonicalisation, ce que cette configuration fait avec nocanon.
Créer les répertoires de déploiement
Créez les répertoires de la configuration httpd et du certificat TLS :
mkdir -p rustfs-httpd/config rustfs-httpd/certs
cd rustfs-httpdCopiez votre chaîne de certificats et votre clé privée dans certs/ :
rustfs-httpd/
├── certs/
│ ├── fullchain.pem
│ └── privkey.pem
└── config/Restreignez l’accès à la clé privée :
chmod 600 certs/privkey.pemLe certificat doit couvrir les deux noms d’hôte publics.
Définir les identifiants RustFS
Créez un fichier d’environnement et remplacez les deux valeurs indicatives d’identifiants :
RUSTFS_ACCESS_KEY=<your-access-key>
RUSTFS_SECRET_KEY=<your-secret-key>N’ajoutez pas ce fichier au dépôt.
Construire l’image httpd
L’image httpd d’origine est livrée avec ses modules de proxy et de TLS commentés. Construisez une petite image qui les active et charge les hôtes virtuels RustFS :
FROM httpd:2.4-alpine
# The stock httpd image disables the proxy and TLS modules. Enable the modules
# required to terminate TLS and reverse-proxy RustFS, then load the RustFS
# virtual hosts.
RUN sed -i \
-e 's|^#\(LoadModule socache_shmcb_module\)|\1|' \
-e 's|^#\(LoadModule proxy_module\)|\1|' \
-e 's|^#\(LoadModule proxy_http_module\)|\1|' \
-e 's|^#\(LoadModule proxy_balancer_module\)|\1|' \
-e 's|^#\(LoadModule slotmem_shm_module\)|\1|' \
-e 's|^#\(LoadModule ssl_module\)|\1|' \
-e 's|^#\(LoadModule http2_module\)|\1|' \
-e 's|^#\(LoadModule lbmethod_byrequests_module\)|\1|' \
/usr/local/apache2/conf/httpd.conf \
&& printf '\nInclude conf/rustfs.conf\n' >> /usr/local/apache2/conf/httpd.confproxy_balancer, slotmem_shm et lbmethod_byrequests ne sont utilisés que par la configuration multi-nœuds. Les charger sur un déploiement à nœud unique ne pose aucun problème.
Configurer Apache HTTP Server
Créez la configuration httpd avec un hôte virtuel par point de terminaison RustFS :
# Global settings shared by the RustFS virtual hosts.
ServerName localhost
# The stock image listens on port 80 only. Open port 443 for TLS.
Listen 443
# S3 transfers can run for hours; do not cut idle-looking connections.
Timeout 600
ProxyTimeout 600
# Share TLS sessions across connections to speed up repeated handshakes.
SSLSessionCache "shmcb:logs/ssl_scache(512000)"
# Drop only genuinely stalled clients: MinRate keeps slow-but-steady uploads
# alive while data keeps flowing.
RequestReadTimeout header=30-60,MinRate=500 body=60,MinRate=512
# Accept request bodies of any size (the default, stated explicitly).
LimitRequestBody 0
# Redirect every plain-HTTP request to HTTPS.
<VirtualHost *:80>
ServerName s3.example.com
Redirect permanent / https://s3.example.com/
</VirtualHost>
<VirtualHost *:80>
ServerName console.example.com
Redirect permanent / https://console.example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName s3.example.com
Protocols h2 http/1.1
SSLEngine on
SSLCertificateFile /usr/local/apache2/certs/fullchain.pem
SSLCertificateKeyFile /usr/local/apache2/certs/privkey.pem
# AWS Signature Version 4 signs the Host header and the request path.
# Keep the client's Host, leave the path uncanonicalized, and allow
# encoded slashes so that signed requests stay valid.
ProxyPreserveHost On
ProxyRequests Off
AllowEncodedSlashes NoDecode
# Apache does not set X-Forwarded-Proto; declare it explicitly.
# ProxyAddHeaders (default On) adds X-Forwarded-For/Host/Server.
RequestHeader set X-Forwarded-Proto "https"
# nocanon passes the request path to RustFS unchanged.
ProxyPass / http://rustfs:9000/ nocanon
ProxyPassReverse / http://rustfs:9000/
</VirtualHost>
<VirtualHost *:443>
ServerName console.example.com
Protocols h2 http/1.1
SSLEngine on
SSLCertificateFile /usr/local/apache2/certs/fullchain.pem
SSLCertificateKeyFile /usr/local/apache2/certs/privkey.pem
ProxyPreserveHost On
ProxyRequests Off
RequestHeader set X-Forwarded-Proto "https"
# upgrade=websocket tunnels Console WebSocket upgrades end to end;
# regular requests continue to use plain HTTP forwarding.
ProxyPass / http://rustfs:9001/ upgrade=websocket
ProxyPassReverse / http://rustfs:9001/
</VirtualHost>Listen 443 est obligatoire, car le fichier httpd.conf d’origine n’ouvre que le port 80 ; les hôtes virtuels à eux seuls n’ouvrent pas de ports. Les deux blocs VirtualHost *:80 redirigent tout le trafic HTTP en clair vers HTTPS avec des noms d’hôte cibles explicites.
Créer le fichier Compose
Créez les services httpd et RustFS :
services:
httpd:
build: .
restart: unless-stopped
depends_on:
rustfs:
condition: service_healthy
ports:
- "80:80"
- "443:443"
volumes:
- ./config/rustfs.conf:/usr/local/apache2/conf/rustfs.conf:ro
- ./certs:/usr/local/apache2/certs:ro
networks:
- rustfs
rustfs:
image: rustfs/rustfs:latest
restart: unless-stopped
environment:
RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY}
RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY}
RUSTFS_CONSOLE_ENABLE: "true"
RUSTFS_ADDRESS: ":9000"
RUSTFS_CONSOLE_ADDRESS: ":9001"
RUSTFS_OBS_LOGGER_LEVEL: error
RUSTFS_OBS_LOG_DIRECTORY: /logs
expose:
- "9000"
- "9001"
volumes:
- rustfs-data:/data
healthcheck:
test: ["CMD-SHELL", "curl --fail http://127.0.0.1:9000/health/ready && curl --fail http://127.0.0.1:9001/rustfs/console/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- rustfs
volumes:
rustfs-data:
networks:
rustfs:Seul httpd publie des ports sur l’hôte. Les ports RustFS 9000 et 9001 restent accessibles à l’intérieur du réseau Compose.
Répertoire de journaux RustFS
RUSTFS_OBS_LOG_DIRECTORY doit pointer vers un répertoire que le conteneur peut créer. L’image RustFS s’exécute sous un utilisateur non root : le chemin /logs fonctionne, alors que les chemins sous /var/log font quitter le conteneur au démarrage avec Permission denied.
Valider et démarrer le déploiement
Affichez la configuration Compose résolue et démarrez RustFS :
docker compose config
docker compose up -d rustfsValidez la configuration httpd avec la même image que celle utilisée par le déploiement :
docker compose run --rm --no-deps httpd httpd -tDémarrez httpd et vérifiez les deux services :
docker compose up -d httpd
docker compose psSi un service n’atteint pas l’état sain, inspectez ses journaux :
docker compose logs httpd
docker compose logs rustfsVérifier les deux points de terminaison
Vérifiez l’API et la Console au moyen de leurs noms d’hôte HTTPS publics :
curl --fail https://s3.example.com/health/ready
curl --fail https://console.example.com/rustfs/console/healthConfigurez les clients S3 avec https://s3.example.com comme point de terminaison et activez l’adressage de type chemin (path-style). Ouvrez https://console.example.com/rustfs/console/ pour vous connecter à la Console.
Chemin racine de la Console
Dans RustFS v2.x, la Console est servie sous le préfixe de chemin /rustfs/console/. Une requête vers le chemin racine du nom d’hôte de la Console renvoie une réponse XML de refus d’accès de style S3, ce qui est attendu.
Lorsque vous remplacez un certificat renouvelé ou une clé renouvelée dans certs/, validez la configuration et rechargez httpd en douceur, sans interrompre les transferts en cours :
docker compose exec httpd httpd -t
docker compose exec httpd httpd -k gracefulComment cette configuration préserve les signatures S3
AWS Signature Version 4 signe la méthode HTTP, l’en-tête Host et le chemin brut de la requête. Chacune des directives ci-dessous conserve l’une de ces valeurs identique, octet pour octet, entre le client et RustFS :
ProxyPreserveHost Onconserve l’en-têteHostdu client. Avec la valeur par défaut (Off), Apache réécritHostvers l’hôte du backend et RustFS répondSignatureDoesNotMatch.nocanonsur la ligneProxyPasstransmet le chemin de la requête à RustFS sans canonicalisation d’URL. Sans cette option, Apache réécrit les chemins pendant la transmission (par exemple en contractant//), et plus aucun chemin signé ne correspond.AllowEncodedSlashes NoDecodepermet aux clés d’objet contenant%2Fd’atteindre les gestionnaires du proxy au lieu de renvoyer404, et conserve l’encodage intact.RequestHeader set X-Forwarded-Proto "https"indique à RustFS le schéma d’origine.ProxyAddHeadersd’Apache (valeur par défautOn) ajouteX-Forwarded-For,X-Forwarded-HostetX-Forwarded-Server, mais pasX-Forwarded-Proto.
Les URL présignées, les chargements partitionnés et les chargements POST du navigateur reposent sur la même conservation du chemin et de l’hôte, et n’exigent aucune directive supplémentaire.
L’hôte virtuel de la Console ajoute upgrade=websocket à sa ligne ProxyPass afin que les mises à niveau WebSocket soient tunnelisées de bout en bout. Depuis la version 2.4.47, mod_proxy_http gère lui-même la mise à niveau et ne tunnelise que les connexions que le backend accepte avec 101 Switching Protocols ; le module plus ancien mod_proxy_wstunnel est obsolète.
Corps de requête, objets volumineux et délais d’expiration
Apache transmet les corps de requête à RustFS sans limite de taille et sans les écrire sur le disque du proxy. Les corps qui arrivent avec un Content-Length (ce qu’envoient l’AWS CLI, les SDK AWS et mc) passent en flux à mesure qu’ils arrivent. Les corps qui arrivent avec Transfer-Encoding: chunked sont également transmis en encodage fragmenté (chunked), ce que RustFS rejette — voir les avertissements ci-dessous.
Les directives de délai d’expiration cèdent une part de sécurité pour permettre les transferts longs :
Timeout 600couvre les E/S de la connexion dans les deux sens, de sorte que les téléchargements et les chargements lents ne sont pas coupés en plein transfert.ProxyTimeout 600applique le même budget à la connexion avec RustFS.RequestReadTimeoutlimite les clients lents sans pénaliser les clients réguliers : une requête n’est abandonnée que lorsque les en-têtes restent bloqués ou que le débit du corps passe sousMinRate.LimitRequestBody 0énonce explicitement la valeur par défaut : les corps de requête de toute taille sont acceptés.
Les chargements doivent comporter un Content-Length
RustFS rejette les corps de requête transmis en Transfer-Encoding: chunked avec 400 UnexpectedContent (This request does not support content.). Les clients doivent charger avec un Content-Length. Tous les principaux clients S3 le font sans réglage supplémentaire, de sorte que cela ne concerne que les scripts HTTP écrits à la main qui envoient un flux depuis un tube (pipe). Le traitement par défaut des corps dans Apache convertit en mémoire les petits corps fragmentés en Content-Length, mais transmet les corps volumineux sous forme fragmentée : un tel client peut donc sembler fonctionner pour les petits fichiers et échouer pour les gros. SetEnv proxy-sendcl 1 convertit les corps fragmentés en Content-Length en solution de repli — au prix d’écrire d’abord chaque corps sur le disque du proxy.
URL présignées et en-tête Content-Type
Une URL présignée fixe les en-têtes couverts par sa signature. Si l’URL a été générée sans Content-Type, le chargement ne doit pas en ajouter — RustFS répond 403 SignatureDoesNotMatch pour l’en-tête non signé. Notez que curl -d et curl --data-binary ajoutent d’eux-mêmes Content-Type: application/x-www-form-urlencoded. Lorsque le chargement envoie un Content-Type, présignez l’URL avec la même valeur (le paramètre ContentType des SDK AWS) et faites correspondre la requête exactement à cette valeur.
Réglages inadaptés à RustFS
Les valeurs par défaut et les options Apache suivantes cassent la sémantique S3 ou gaspillent des ressources. Cette configuration les évite toutes.
| Réglage | Effet sur RustFS |
|---|---|
ProxyPreserveHost Off (valeur par défaut) | Réécrit Host ; les requêtes signées échouent avec SignatureDoesNotMatch. |
ProxyPass sans nocanon | Canonicalise le chemin de la requête ; la vérification des chemins signés échoue. |
AllowEncodedSlashes Off (valeur par défaut) | Renvoie 404 pour les clés d’objet contenant %2F. |
Préfixes de chemin (ProxyPass /s3/, Alias, règles de réécriture) | Réécrit le chemin de la requête ; conservez l’API S3 sur le chemin racine. |
ProxyErrorOverride On | Remplace les erreurs XML S3 de RustFS par des pages d’erreur Apache ; les SDK S3 ne peuvent pas les analyser. |
SetEnv proxy-sendcl 1, SetEnv force-proxy-request-1.0 1 | Force un Content-Length vers l’amont et écrit sur disque les corps de requête fragmentés avant de les transmettre. Cela sauve les clients qui n’envoient que du contenu fragmenté, mais ajoute des E/S disque aux chargements volumineux. |
SetEnv proxy-sendchunked 1 | Force un encodage fragmenté vers l’amont, que RustFS rejette avec 400 UnexpectedContent. |
mod_proxy_wstunnel (ProxyPass ... ws://) | Obsolète depuis la version 2.4.47 ; utilisez upgrade=websocket sur mod_proxy_http à la place. |
Deux limites propres à RustFS méritent d’être signalées, car aucun réglage de proxy ne peut les lever :
- Les clés d’objet qui contiennent des segments de chemin vides (
//) ou constitués d’un point (.) sont rejetées avecInvalidArgument, même sans proxy. - Dans RustFS v2.x, la Console est servie sous
/rustfs/console/; le chemin racine de la Console répond avec du XML de style S3.
Backends multi-nœuds
Pour un déploiement RustFS distribué, remplacez chaque cible ProxyPass unique par un équilibreur qui liste tous les nœuds RustFS. Remplacez le bloc de proxy de l’hôte virtuel S3 par :
<Proxy "balancer://s3">
BalancerMember http://node1.example.net:9000 route=node1
BalancerMember http://node2.example.net:9000 route=node2
BalancerMember http://node3.example.net:9000 route=node3
BalancerMember http://node4.example.net:9000 route=node4
ProxySet lbmethod=byrequests
</Proxy>
ProxyPass / balancer://s3/ nocanon
ProxyPassReverse / balancer://s3/Remplacez le bloc de proxy de l’hôte virtuel de la Console par :
# Keep an in-progress login on the node that created its state.
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<Proxy "balancer://console">
BalancerMember http://node1.example.net:9001 route=node1 upgrade=websocket
BalancerMember http://node2.example.net:9001 route=node2 upgrade=websocket
BalancerMember http://node3.example.net:9001 route=node3 upgrade=websocket
BalancerMember http://node4.example.net:9001 route=node4 upgrade=websocket
ProxySet stickysession=ROUTEID
</Proxy>
ProxyPass / balancer://console/
ProxyPassReverse / balancer://console/Le cookie ROUTEID maintient une connexion OpenID Connect en cours sur le nœud RustFS qui a créé son state. Conservez upgrade=websocket sur chaque ligne BalancerMember ; ProxySet n’accepte pas ce paramètre. Laissez le port 9000 ouvert directement entre les nœuds RustFS, car le RPC interne des nœuds utilise le même écouteur.