Skip to content

Authority and Proxy on separate hosts

This recipe uses administrator-chosen manual resource IDs. Keep manual enrollment for its Connector examples. For automatic backends and Authority-assigned names, use publish and migrate resources.

Authority on an internal host, the Proxy on a public host or in a DMZ, connectors beside the resources. Profile: pilot.

Host Addresses Runs
Authority host 192.0.2.10 (internal) Authority, PostgreSQL
Proxy host 203.0.113.10 (public, gate.example.com), 192.0.2.11 (internal) Proxy
Connector hosts anywhere with a route to the Proxy connectors

Before you start

Item How to check Reference
Everything in the single-host checklist, per host VPS: before you start
Inbound ports open on each host before you install. The administrator opens them; without them the Proxy cannot register and connectors cannot join from the peer, after start: nc -vz 192.0.2.10 50051, nc -vz 203.0.113.10 50061 Open the ports
Proxy host reaches Authority on 50050 and 50051 from the Proxy host: nc -vz 192.0.2.10 50051 after Authority starts Traffic
Registration certificate trusted by the Proxy at the address it dials openssl s_client -connect 192.0.2.10:50050 -servername gate.example.com -verify_return_error < /dev/null prints Verify return code: 0 (ok) Certificates
A way to copy one file from the Authority host to the Proxy host scp, or a secret store

Traffic

From To Port Carries
clients Proxy 50061 sessions, gdsgate connect, sign-in settings
connectors Proxy 50062 (50063 WebSocket fallback) reverse tunnels
connectors, tunneled Proxy 50061 registration, forwarded to Authority; renewal, relayed to Authority
connectors, tunneled with --join-relay Proxy 50064 registration and renewal, relayed to Authority
connectors, direct Authority 50050, 50051 registration, renewal, control plane
Proxy Authority 50050 the Proxy's own registration; relayed registration
Proxy Authority 50051 control plane; relayed renewal and tunneled control plane
Authority PostgreSQL 5432 cluster state
Authority identity provider 443 discovery, signing keys
clients identity provider 443 sign-in

What each port carries: Endpoints.

Open the ports

Open the ports before you install. Each host's firewall is the administrator's responsibility: setup prepare, service install and --start do not change it. While a port is closed, the steps that cross it fail: the Proxy's registration at Authority, gdsgate join on a connector host, gdsgate connect on a workstation.

Host Open to every address Open to the Proxy host only
Proxy host 50061, 50062, 50063; 50064 with --join-relay
Authority host 50050, 50051; the connector networks as well on the direct route

Proxy host, per firewall:

Firewall Command
ufw ufw allow 50061:50063/tcp
firewalld firewall-cmd --permanent --add-port=50061-50063/tcp && firewall-cmd --reload
nftables, the inet filter table of /etc/nftables.conf nft insert rule inet filter input tcp dport 50061-50063 accept, and the same rule in /etc/nftables.conf
Windows, elevated PowerShell New-NetFirewallRule -DisplayName gdsgate-proxy -Direction Inbound -Protocol TCP -LocalPort 50061,50062,50063 -Action Allow

Authority host: the nftables table below. On Windows: New-NetFirewallRule -DisplayName gdsgate-authority -Direction Inbound -Protocol TCP -LocalPort 50050,50051 -RemoteAddress 192.0.2.11 -Action Allow.

Nothing dials the Proxy host from Authority's side. Firewall on the Authority host (nftables):

nft -f - <<'EOF'
table inet gdsgate {
  chain input {
    type filter hook input priority 0; policy accept;
    iifname "lo" accept                                    # this host: the admin CLI
    tcp dport { 50050, 50051 } ip saddr 192.0.2.11 accept   # the Proxy
    tcp dport { 50050, 50051 } drop
  }
}
EOF

Without the lo rule the administrative commands on the Authority host hang: they dial 192.0.2.10 from 192.0.2.10. For the direct route add the connector networks to the accept rule. Persist the table in /etc/nftables.conf.

Connector routes

Port purpose and exposure: Listener ports.

tunneled (recommended) tunneled, IP address only direct fronted
Connector egress Proxy only Proxy only Proxy and Authority one address: a front, or the Proxy's client port
Registration https://gate.example.com:50061 https://203.0.113.10:50064 (join relay) https://192.0.2.10:50050 https://gate.example.com:50061
Renewal 50061, server name gdsgate.internal, at most public_renewal_max_connections (default 64) at once join relay 50064 Authority 50051 the registration address, server name gdsgate.internal
Control-plane calls inside the reverse tunnel inside the reverse tunnel Authority 50051 through the front, server name auth.gdsgate.internal
setup prepare no flag --join-relay --no-public-join no flag
Authority file [proxy].public_join = true, [connector].authority_transport = "tunneled" [endpoints].proxy_join, authority_transport = "tunneled" [endpoints].authority is an address connectors reach as tunneled
Proxy file [proxy].public_join = true [endpoints].proxy_join nothing a front routing by server name, or [proxy].public_join = true
Connector file written by gdsgate join written by gdsgate join written by gdsgate join written by gdsgate join --route fronted: the route, [endpoints].authority and [enroll].renew_endpoint = the registration address
Use connectors anywhere; Authority unreachable from them the same, for a gateway without a DNS name connectors inside the internal network one egress address; the Proxy's client port as front carries at most public_admin_max_connections (default 8) connectors

Authority tells each node the route at registration (what Authority tells a joining node). On 50061 a connector checks the Proxy's public_cert_file for the name or address it dials. A connector that registers through the join relay at an IP address passes --enroll-ca with the CA of the registration certificate, which names that address: registration at an IP address. public_join forwards the registration it terminates over TLS when the Proxy's [enroll].endpoint is https:// and transport_sni names the certificate (transport_ca for a private CA); setup prepare writes both. Key reference: authority_transport, proxy_join, public_join.

Configure

The commands below use the tunneled route and a Let's Encrypt pair for gate.example.com on both hosts. For your own CA, one certificate with DNS:gate.example.com serves both.

On the Authority host:

  1. PostgreSQL, the configuration directory, the store password and the certificate files: VPS steps 1–3.

  2. The configuration:

    gdsgate setup prepare --domain gate.example.com --security-profile pilot \
      --role authority --authority-bind 192.0.2.10 \
      --store-url postgres://gdsgate@127.0.0.1:5432/gdsgate \
      --store-password-file /etc/gdsgate/store.password \
      --enroll-cert /etc/gdsgate/tls/fullchain.pem --enroll-key /etc/gdsgate/tls/privkey.pem \
      --oidc-issuer https://idp.example.com/realms/gdsgate --oidc-client-id gdsgate
    cat >> /etc/gdsgate/authority.toml <<'EOF'
    
    [audit]
    export_path = "/var/lib/gdsgate/authority/audit.jsonl"
    EOF
    gdsgate --config /etc/gdsgate/authority.toml service install authority
    gdsgate config check /etc/gdsgate/authority.toml --role authority
    

    --authority-bind is the address Authority binds and the address it gives the Proxy. With --no-public-join it is also the address connectors are told, and a wildcard is refused. service install gives the gdsgate group the files the configuration names under /etc/gdsgate. For a gateway without a DNS name, add --join-relay here and in step 5.

    Result, the keys that differ from the single host:

    [endpoints]
    authority        = "192.0.2.10:50051"            # bound; reachable from the Proxy
    authority_enroll = "192.0.2.10:50050"            # bound; reachable from the Proxy
    proxy_public     = "gate.example.com:50061"     # told to joining nodes; where connectors renew
    proxy_internal   = "gate.example.com:50062"
    proxy_ws         = "gate.example.com:50063"
    
  3. Start, take the administrative identity, and issue the Proxy's token:

    systemctl start gdsgate-authority
    gdsgate --config /etc/gdsgate/authority.toml authority identities bootstrap --role admin \
      --output-dir /etc/gdsgate/admin
    cat >> /etc/gdsgate/authority.toml <<'EOF'
    
    [admin]
    identity_dir = "/etc/gdsgate/admin"
    endpoint     = "https://192.0.2.10:50051"
    EOF
    umask 077
    gdsgate --config /etc/gdsgate/authority.toml authority publication set manual
    gdsgate --config /etc/gdsgate/authority.toml authority tokens create --role proxy > proxy.token
    

    Copy proxy.token to the Proxy host and delete it here. It expires in one hour (--ttl changes that).

On the Proxy host:

  1. The group, the directory, the certificate files (the Let's Encrypt tab on this host), and the token:

    groupadd --system --force gdsgate
    install -d -m 0750 -g gdsgate /etc/gdsgate
    printf 'GDSGATE_ENROLL_TOKEN=%s\n' "$(cat proxy.token)" > /etc/gdsgate/proxy.env
    chmod 0600 /etc/gdsgate/proxy.env && rm proxy.token
    
  2. The configuration:

    gdsgate setup prepare --domain gate.example.com --security-profile pilot \
      --role proxy --authority-host 192.0.2.10 \
      --enroll-cert /etc/gdsgate/tls/fullchain.pem \
      --tls-cert /etc/gdsgate/tls/fullchain.pem --tls-key /etc/gdsgate/tls/privkey.pem \
      --oidc-issuer https://idp.example.com/realms/gdsgate --oidc-client-id gdsgate
    

    --enroll-cert names the certificate Authority's registration listener presents; the file is not read here. The Proxy dials 192.0.2.10 and the certificate names gate.example.com, so setup prepare writes [enroll].transport_sni = "gate.example.com".

    Registration certificate for an internal name only, for example authority.internal:

    Where Setting
    Proxy, after this step gdsgate setup change --set proxy:enroll.transport_sni=authority.internal
    Connectors Through public_join (:50061) no flag is needed. Through the join relay: gdsgate join ... --enroll-endpoint https://gate.example.com:50064 --enroll-sni authority.internal, plus --enroll-ca for a private CA.
  3. Check and start:

    gdsgate --config /etc/gdsgate/proxy.toml service install proxy
    gdsgate config check /etc/gdsgate/proxy.toml --role proxy
    systemctl start gdsgate-proxy
    

    Result. The three listeners bind every interface, as on the single host:

    [endpoints]
    # proxy_public, proxy_internal, proxy_ws: 0.0.0.0:50061 to 0.0.0.0:50063
    authority        = "192.0.2.10:50051"            # dialled
    authority_enroll = "192.0.2.10:50050"            # where registration is forwarded
    
    [proxy]
    public_cert_file = "/etc/gdsgate/tls/fullchain.pem"
    public_key_file  = "/etc/gdsgate/tls/privkey.pem"
    public_join      = true                          # connectors register and renew on 50061
    
    [enroll]
    endpoint      = "https://192.0.2.10:50050"       # the Proxy's own registration
    transport_sni = "gate.example.com"              # the name on the certificate
    state_dir     = "/var/lib/gdsgate/proxy-state"
    
    [oidc]
    issuer    = "https://idp.example.com/realms/gdsgate"
    client_id = "gdsgate"
    
    [security]
    profile = "pilot"
    
    [telemetry]
    metrics_listen = "127.0.0.1:9090"
    

Connectors: VPS, a connector on another host, tunneled tab. The catalogue row and the token come from the Authority host.

Start

Steps 3 and 6 above.

From an elevated PowerShell prompt, files under %ProgramData%\gdsgate.

Authority host: step 2 up to setup prepare, with --start; it starts gdsgate-authority, takes the administrative identity and writes the Proxy token to proxy.token. It does not write the audit export. Add it, restart Authority and check the file:

Add-Content C:\ProgramData\gdsgate\authority.toml "`n[audit]`nexport_path = 'C:\ProgramData\gdsgate\state\authority\audit.jsonl'"
Restart-Service gdsgate-authority
gdsgate config check C:\ProgramData\gdsgate\authority.toml --role authority

Expected: 0 error(s), 0 warning(s).

Proxy host: put the token alone in %ProgramData%\gdsgate\proxy.token and run step 5 with --start. Once it reports the gateway started, the Proxy has registered and holds its identity; remove the token:

Remove-Item C:\ProgramData\gdsgate\proxy.token

Verify

Command Expected If not: cause → fix
Authority host: ss -ltn \| grep 5005 192.0.2.10:50050, 192.0.2.10:50051 127.0.0.1 → --authority-bind missing.
Authority host: curl -s 127.0.0.1:9090/readyz ready journalctl -u gdsgate-authority -n 30.
Proxy host: journalctl -u gdsgate-proxy -n 30 enrolled and persisted a new transport identity, proxy serving node registration on the public listener ... forward="TLS" (proxy serving the join relay with --join-relay), proxy serving gRPC (mTLS (enrolled), public TLS from the configured certificate) certificate refused (...); checked for the name ... → the name or trust named there; a certificate for an internal name: step 5. connect to the enrollment endpoint failed with a timeout → the firewall drops 192.0.2.11.
Proxy host: curl -s 127.0.0.1:9090/readyz ready as above
Authority host: gdsgate --config /etc/gdsgate/authority.toml authority nodes list proxy gdsgate-proxy ... active No row → the Proxy has not registered; the Proxy log names why.
Connector host: gdsgate join gate.example.com ... --enroll-endpoint https://gate.example.com:50061 ok: joined gate.example.com as conn-1 · role connector, then auth tunneled at none, renew https://gate.example.com:50061 UNAVAILABLE → the Proxy cannot reach or verify the registration listener; the Proxy log names why. Otherwise see VPS verify.
Authority host: gdsgate --config /etc/gdsgate/authority.toml authority offers list demo-ssh ssh ... yes Connector log: journalctl -u gdsgate-connector.
A host outside 192.0.2.11: nc -vz -w 3 192.0.2.10 50051 timeout Connected → the firewall rule is not loaded: nft list table inet gdsgate.
Workstation: gdsgate connect gate.example.com ok: wrote ~/.gdsgate/config.toml See Certificates.

After these checks pass, complete the access checks from a client workstation.

Profile

pilot, the same matrix rows as the single host. The registration listener crosses a network here, so registration-tls and enroll-endpoint-tls apply in every profile you choose.