Appearance
Cloud gateways
By default Caddy on the server terminates TLS on 443 (GATEWAY=builtin). You can put a cloud load balancer or Cloudflare Tunnel in front instead, for a static address, cloud DDoS protection or a web application firewall. Terraform modules for each are in deploy/gateways/ of the Theseus source.
Two kinds of traffic reach a stack, and a gateway handles them differently:
- HTTPS on one host name (
THESEUS_HOST): browsers, installers, enrolment, the Theseus agent channel. Any layer-7 proxy can carry it: agent requests are authenticated by signatures, not by TLS client certificates. Idle timeouts must be at least 60 seconds (the modules use 120 s), and/ai-analyst/wsis a WebSocket. - Wazuh agent traffic on TCP 1514. It needs TCP passthrough; an HTTP proxy cannot carry it. In every layer-7 mode, agents need a second name,
AGENT_HOST, that resolves to a TCP path.
Options
| Option | Certificate | WAF | TCP 1514 | GATEWAY |
|---|---|---|---|---|
| Built-in (no module) | Caddy: Let's Encrypt or the stack's CA | none | host port | builtin |
AWS variant=nlb | Caddy | none | same NLB | builtin |
AWS variant=alb | ACM | WAFv2 (AWS managed rules, rate limit) | separate NLB | aws |
Azure l7=none with lb_https_passthrough | Caddy | none | Standard Load Balancer | builtin |
Azure l7=appgw | Key Vault certificate or PFX | WAF_v2 | Standard Load Balancer | azure |
Azure l7=frontdoor | Azure-managed | Front Door WAF policy | Standard Load Balancer | azure |
GCP enable_l7=true | Google-managed | Cloud Armor | passthrough NLB | gcp |
GCP enable_l7=false with nlb_https_passthrough | Caddy | none | same NLB | builtin |
| Cloudflare Tunnel | Cloudflare edge | Cloudflare managed rulesets (Pro plan or higher) | not through the tunnel: a DNS-only record, or Spectrum (Enterprise) | cloudflare |
The passthrough variants only move TCP: Caddy keeps the certificate and the settings are the same as built-in.
Which one
- Built-in. Any VM with a public IP or a port forward. Start here.
- Passthrough load balancer. You want a cloud load balancer (static IP, health checks, hiding the VM) but are happy with Caddy's certificate.
- Layer 7 with WAF. Internet-facing production. Start the WAF in count or detection mode: installers, agent results and log uploads have large request bodies.
- Cloudflare Tunnel. No inbound HTTPS port on the server at all. TCP 1514 must still reach the server directly (or through Spectrum).
Use a module
Each module's README has a complete example. The flow is the same for all:
Fill in the module's variables (
terraform.tfvars).Create the resources:
bashterraform init && terraform applyPrint the settings the stack needs, and apply them on the server (or send them to your provider):
bashterraform output -raw theseus_envOn the server: add the lines to
theseus.env, then./theseus config apply.
| Module | Required variables | Notes |
|---|---|---|
aws | vpc_id, subnet_ids, target_id, hostname | variant nlb (default) or alb; enable_waf; certificate_arn or route53_zone_id for ACM; deletion protection on by default |
azure | resource_group_name, location, vm_nic_id, hostname | l7 appgw (default), frontdoor or none |
gcp | project, region, zone, hostname | The managed certificate takes 15 to 60 minutes to become active |
cloudflare | account_id, zone_id, hostname | agent_origin_ip adds a DNS-only record for agents. Run the tunnel connector with deploy/gateways/cloudflare/compose.cloudflared.yml |
For Cloudflare, start the connector on the server with the tunnel token:
bash
export TUNNEL_TOKEN=$(terraform output -raw tunnel_token)
docker compose -f deploy/gateways/cloudflare/compose.cloudflared.yml up -dSettings per mode
| Setting | builtin | aws (ALB) | azure | gcp (ALB) | cloudflare |
|---|---|---|---|---|---|
TLS_MODE | acme or internal | upstream | upstream | upstream | upstream |
AGENT_HOST | = THESEUS_HOST | name of the NLB | Load Balancer IP | passthrough NLB IP | DNS-only name to the host IP |
UPSTREAM_HTTP_PORT | - | 8088 | 8088 | 8088 | 8088, not published |
TRUSTED_PROXY_CIDRS | empty | ALB subnets | App Gateway subnet, or Front Door ranges | 130.211.0.0/22,35.191.0.0/16,<lb-ip>/32 | the stack's Docker network |
CLIENT_IP_HEADER | - | X-Forwarded-For | X-Forwarded-For | X-Forwarded-For | CF-Connecting-IP |
GATEWAY_REQUIRE_HEADER | - | - | Front Door: X-Azure-FDID: <profile id> | - | - |
In the cloud modes the server publishes no 443 or 80: it accepts plain HTTP on 8088 from the trusted gateway addresses only, and the install commands for endpoints use the public certificate chain instead of a pin.
Changing the gateway after enrolment
Endpoints enrolled with the internal CA pin its certificate. Moving such a stack behind a gateway with a public certificate changes what they connect to; plan it with your provider.