Public ingress

Place Nginx or HAProxy in front of X2 safely

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

Choose DNS, certificates, and proxy source addresses

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.

Public DNS

Console/API
<CONSOLE_DNS>
S3 endpoint
<S3_DNS>
Virtual-host buckets
*.<S3_DNS>

Proxy identity

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.

Two TLS hops

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.

Upstream certificate name

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.

X2 node platform

Linux

X2 settings

Configure public routing and proxy trust on X2

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.

01

Set the X2 public and trust values

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.

02

Confirm the persisted X2 configuration

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

Streaming reverse-proxy configuration

Put the map and upstream blocks in the Nginx http context and the server block in the enabled virtual host.

Nginx configuration
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

Health-checked TLS load balancing

HAProxy configuration
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

Prove management, S3, and streaming paths

Readiness and UI

curl --fail https://<CONSOLE_DNS>/health/ready
curl --head https://<CONSOLE_DNS>/

S3 host routing

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

Forwarded-header rejection

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.

Operational checks

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.

Proxy validation complete

Admit public traffic only after DNS, certificate names, upstream verification, proxy CIDRs, console login, and signed S3 operations all pass.

Continue to operations →