Public DNS
- Console/API
<CONSOLE_DNS>- S3 endpoint
<S3_DNS>- Virtual-host buckets
*.<S3_DNS>
Public ingress
Terminate public TLS, preserve S3 signatures and streaming, verify TLS to every X2 upstream, and trust forwarded headers only from the proxy addresses you operate.
Before you begin
Replace every value enclosed in <...>. The console
and S3 endpoints may share one proxy, while the inter-node mesh on
port 9443 must not pass through this public proxy.
<CONSOLE_DNS><S3_DNS>*.<S3_DNS>
Record the source IP or network the proxy uses when connecting
to X2. Configure exact CIDRs such as
<PROXY_IP>/32; never trust
0.0.0.0/0 or ::/0.
Install the public certificate on the proxy. Keep HTTPS between the proxy and X2, and give the proxy the CA that issued the X2 public-listener certificates. Do not disable upstream verification in production.
Every X2 upstream certificate must contain
<X2_UPSTREAM_TLS_NAME>. The initial
x2-node configure certificate includes the host from
--public-url; operator-issued certificates can use a
separate shared internal name.
Linux
X2 settings
Add these options to the complete first-node command from the installation guide. They are inherited by subsequently enrolled nodes. For an existing cluster, use the update command below on every node and drain or stop only one node at a time.
During initial setup, add the following three options to the
full x2-node configure command that also supplies
disks, metadata, runtime root, and administrator credentials.
The abbreviated commands below update an existing
node.yaml.
sudo /usr/lib/x2/x2-node configure \
--config /etc/x2/node.yaml \
--public-url 'https://<CONSOLE_DNS>' \
--s3-hosts '<S3_DNS>' \
--trusted-proxy-cidrs '<PROXY_IP_OR_NETWORK_CIDR>'
& 'C:\Program Files\X2\x2-node.exe' configure `
--config 'C:\ProgramData\X2\config\node.yaml' `
--public-url 'https://<CONSOLE_DNS>' `
--s3-hosts '<S3_DNS>' `
--trusted-proxy-cidrs '<PROXY_IP_OR_NETWORK_CIDR>'
sudo /usr/local/lib/x2/x2-node configure \
--config /usr/local/etc/x2/node.yaml \
--public-url 'https://<CONSOLE_DNS>' \
--s3-hosts '<S3_DNS>' \
--trusted-proxy-cidrs '<PROXY_IP_OR_NETWORK_CIDR>'
For multiple proxy addresses, pass a comma-separated CIDR
list. A same-host proxy normally uses
127.0.0.1/32,::1/128. Trust the proxy connection
address—not browser or S3 client addresses.
Existing nodes also need a public certificate whose SAN
contains <X2_UPSTREAM_TLS_NAME>. Add
--tls-cert '<CERTIFICATE_FILE>' and
--tls-key '<PRIVATE_KEY_FILE>' when replacing
that certificate, then restart the node and verify readiness
before updating the next node.
public:
listen: "0.0.0.0:8443"
base_url: "https://<CONSOLE_DNS>"
s3_endpoint_hosts:
- "<S3_DNS>"
trusted_proxy_cidrs:
- "<PROXY_IP_OR_NETWORK_CIDR>"
X2 rejects any request containing Forwarded or
X-Forwarded-* when the immediate network peer is
not trusted. It also rejects conflicting hosts and any
forwarded public scheme other than HTTPS.
Nginx
Put the map and upstream blocks in the
Nginx http context and the server block in the enabled
virtual host.
map $http_upgrade $x2_connection_upgrade {
default upgrade;
'' '';
}
upstream x2_nodes {
least_conn;
server <X2_NODE_1_IP>:8443 max_fails=3 fail_timeout=10s;
server <X2_NODE_2_IP>:8443 max_fails=3 fail_timeout=10s;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name <CONSOLE_DNS> <S3_DNS> *.<S3_DNS>;
ssl_certificate /etc/nginx/tls/x2-public.crt;
ssl_certificate_key /etc/nginx/tls/x2-public.key;
client_max_body_size 0;
location / {
proxy_pass https://x2_nodes;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Forwarded "";
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $x2_connection_upgrade;
proxy_request_buffering off;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_ssl_server_name on;
proxy_ssl_name <X2_UPSTREAM_TLS_NAME>;
proxy_ssl_trusted_certificate /etc/nginx/tls/x2-upstream-ca.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
}
}
sudo nginx -t
sudo systemctl reload nginx
HAProxy
frontend x2_public
bind :443 ssl crt /etc/haproxy/tls/x2-public.pem alpn h2,http/1.1
mode http
option httplog
http-request del-header Forwarded
http-request del-header X-Forwarded-For
http-request set-header X-Forwarded-Host %[req.hdr(Host)]
http-request set-header X-Forwarded-Proto https
http-request set-header X-Forwarded-For %[src]
default_backend x2_nodes
backend x2_nodes
mode http
balance leastconn
option httpchk GET /health/ready
http-check expect status 200
timeout connect 10s
timeout server 1h
server node1 <X2_NODE_1_IP>:8443 ssl verify required ca-file /etc/haproxy/tls/x2-upstream-ca.crt verifyhost <X2_UPSTREAM_TLS_NAME> check
server node2 <X2_NODE_2_IP>:8443 ssl verify required ca-file /etc/haproxy/tls/x2-upstream-ca.crt verifyhost <X2_UPSTREAM_TLS_NAME> check
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
Verification
curl --fail https://<CONSOLE_DNS>/health/ready
curl --head https://<CONSOLE_DNS>/
xc alias set x2 https://<S3_DNS> <ACCESS_KEY> <SECRET_KEY>
xc mb x2/proxy-smoke
xc cp ./large-object.bin x2/proxy-smoke/large-object.bin
xc stat x2/proxy-smoke/large-object.bin
A request sent directly to X2 with a forged forwarded header from an untrusted address must return HTTP 400. This confirms clients cannot spoof their public host or HTTPS state.
Confirm every upstream passes readiness, large uploads do not buffer to proxy disk, WebSocket upgrades succeed, and requests continue without session stickiness when one X2 node is drained.
Admit public traffic only after DNS, certificate names, upstream verification, proxy CIDRs, console login, and signed S3 operations all pass.
Continue to operations →