Skip to content

Certificates

gdsgate does not request certificates from outside. It reads PEM files that an ACME client or a corporate CA writes, and for a gateway reached by IP address the cluster CA issues the registration certificate. This page lists the files each listener reads, three ways to produce them, and how clients, connectors and the Proxy come to trust them.

Listeners and keys

Listener Port Keys Without the keys
Proxy, client-facing; connector registration with public_join 50061 [proxy].public_cert_file, public_key_file Presents the enrolled Proxy identity. Clients and verified Connector discovery accept the approved cluster CA and Proxy TLS name. An explicit registration URL needs that name and CA supplied separately.
Authority, registration 50050 [authority].enroll_cert_file, enroll_key_file Cleartext h2c. Refused at start under pilot and production.
Proxy, browser origin [proxy].web_addr [proxy].web_cert_file, web_key_file Listener off. Browser-trusted certificate required.
Proxy, clientless browser entry [proxy].webapps_addr [proxy].webapps_cert_file, webapps_key_file Entry off. Wildcard *.<webapps_zone>, browser-trusted.

Connector tunnels (50062, 50063) and the Authority control plane (50051) use the cluster's own CA. Nothing on this page changes them.

One pair can serve the Proxy's client-facing listener and the Authority registration listener when both answer to one name. Wherever the Proxy dials Authority by another address (the loopback on one host, an internal address on two), setup prepare --enroll-cert sets [enroll].transport_sni to the gateway name.

File modes: certificate 0644, key 0640 with group gdsgate (systemd units) or owned by uid 10001 (the container image).

Modes

For an internal cluster or a pilot without a DNS name. The client-facing listener presents the enrolled identity. The registration listener presents a certificate the cluster CA issues. setup prepare with an address in --domain and no --enroll-cert sets it up; no openssl:

NODE_IP=192.0.2.10
gdsgate setup prepare --domain 203.0.113.10 --security-profile pilot \
  --authority-bind "$NODE_IP" \
  --store-url postgres://gdsgate@127.0.0.1:5432/gdsgate \
  --store-password-file /etc/gdsgate/store.password \
  --oidc-issuer https://idp.example.com/realms/gdsgate --oidc-client-id gdsgate \
  --join-relay

What the run writes for the registration listener:

File Content
/var/lib/gdsgate/authority/enroll.pem The certificate for the advertised addresses, the Authority bind and enroll.gdsgate.internal, then the cluster CA. Valid 90 days.
/var/lib/gdsgate/authority/enroll.key Its key, owner-only.
/etc/gdsgate/transport-ca.pem The cluster CA, for the Proxy.
/etc/gdsgate/authority.toml [authority]: enroll_cert_from_cluster_ca = true and the two paths above.
/etc/gdsgate/proxy.toml [enroll]: endpoint = "https://192.0.2.10:50050", transport_ca = "/etc/gdsgate/transport-ca.pem".

The run prints transport_ca.fingerprint: the SHA-256 of the cluster CA. Record the approved value through a trusted channel, then set:

CA_FINGERPRINT='<approved SHA-256 fingerprint>'
Who Trusts the registration listener by Trusts the client-facing listener by
Proxy host [enroll].transport_ca, written by setup prepare not applicable
Connector gdsgate join ... --enroll-fingerprint "$CA_FINGERPRINT" --enroll-sni enroll.gdsgate.internal not applicable
Client not applicable gdsgate connect 203.0.113.10:50061 --fingerprint "$CA_FINGERPRINT"

Connectors register through the join relay at the address, which --join-relay binds: gdsgate join 203.0.113.10 --enroll-endpoint https://203.0.113.10:50064 --enroll-fingerprint "$CA_FINGERPRINT" --enroll-sni enroll.gdsgate.internal. Use --enroll-sni enroll.gdsgate.internal to verify the common internal registration name through an HA join relay. The approved CA pin is still required. Without an explicit name the certificate is checked against the dialled address; see registration at an IP address and connector routes.

Renewal: Authority issues the certificate again 30 days before it expires, at start and hourly while it runs. See issued by the cluster CA.

For a registration certificate from your own CA: the "Your own CA" tab with IP:203.0.113.10 in subjectAltName, passed as setup prepare --enroll-cert --enroll-key; connectors then join with --enroll-ca.

For a gateway with a public DNS name. certbot writes the pair under /etc/letsencrypt/live/gate.example.com/, which only root can read. A deploy hook copies it where gdsgate reads it and asks the services to re-read. gdsgate does not use port 80; certbot's standalone mode binds it only while it runs.

Issue with HTTP-01, port 80 reachable from the internet:

certbot certonly --standalone -d gate.example.com -m ops@example.com --agree-tos

Or with DNS-01, no inbound port (Cloudflare shown; other DNS plugins take the same form):

install -d -m 0700 /root/.secrets
printf 'dns_cloudflare_api_token = %s\n' "$CF_API_TOKEN" > /root/.secrets/cloudflare.ini
chmod 0600 /root/.secrets/cloudflare.ini
certbot certonly --dns-cloudflare --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d gate.example.com -m ops@example.com --agree-tos

Copy the pair for gdsgate, once, before setup prepare:

groupadd -f gdsgate
install -d -m 0750 -g gdsgate /etc/gdsgate/tls
install -m 0644 /etc/letsencrypt/live/gate.example.com/fullchain.pem /etc/gdsgate/tls/fullchain.pem
install -m 0640 -g gdsgate /etc/letsencrypt/live/gate.example.com/privkey.pem /etc/gdsgate/tls/privkey.pem

The hook for every renewal, /etc/letsencrypt/renewal-hooks/deploy/gdsgate, mode 0755:

#!/bin/sh
# certbot deploy hook: copy the renewed pair to /etc/gdsgate/tls
set -e
[ "$RENEWED_LINEAGE" = /etc/letsencrypt/live/gate.example.com ] || exit 0
install -m 0644 "$RENEWED_LINEAGE/fullchain.pem" /etc/gdsgate/tls/fullchain.pem
install -m 0640 -g gdsgate "$RENEWED_LINEAGE/privkey.pem" /etc/gdsgate/tls/privkey.pem
systemctl reload gdsgate-authority gdsgate-proxy

Test it: RENEWED_LINEAGE=/etc/letsencrypt/live/gate.example.com /etc/letsencrypt/renewal-hooks/deploy/gdsgate, then look for the reload lines in the log.

Key Value
[proxy].public_cert_file /etc/gdsgate/tls/fullchain.pem (setup prepare --tls-cert)
[proxy].public_key_file /etc/gdsgate/tls/privkey.pem (--tls-key)
[authority].enroll_cert_file /etc/gdsgate/tls/fullchain.pem (--enroll-cert)
[authority].enroll_key_file /etc/gdsgate/tls/privkey.pem (--enroll-key)
[enroll].transport_sni (Proxy) gate.example.com. Written by setup prepare when the Proxy dials Authority by another address.
Who Trusts it by
Proxy host the system CA bundle (ca-certificates)
Connector the system CA bundle; gdsgate join needs no --enroll-ca
Client the system CA bundle; gdsgate connect gate.example.com needs no --fingerprint and writes no trust anchor

The certificate is valid for 90 days. certbot's timer renews it 30 days before expiry and runs the hook after each renewal.

For a corporate PKI. Request a server certificate with:

Field Value
subjectAltName DNS:gate.example.com, the name clients and connectors dial. Add IP:<address> for every address a peer dials by number, for example IP:192.0.2.10 when the Proxy dials Authority by address.
extendedKeyUsage serverAuth
Key EC P-256 or RSA 2048 and up
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -subj "/CN=gate.example.com" -keyout gate.key -out gate.csr \
  -addext "subjectAltName=DNS:gate.example.com,IP:192.0.2.10" \
  -addext "extendedKeyUsage=serverAuth"

The file gdsgate reads holds the leaf first, then each intermediate, and no root:

cat gate.pem issuing-ca.pem > /etc/gdsgate/tls/fullchain.pem
install -g gdsgate -m 0640 gate.key /etc/gdsgate/tls/privkey.pem
openssl verify -CAfile root.pem -untrusted issuing-ca.pem gate.pem

The keys are the ones in the Let's Encrypt tab.

Who Trusts it by
Proxy host the root in the system store (as in the IP tab), or [enroll].transport_ca
Connector the root in the system store, or gdsgate join ... --enroll-ca root.pem
Client, Linux the root in the system store, or connect --fingerprint / terminal confirmation when the public chain includes its CA; SSL_CERT_FILE=/path/root.pem supplies explicit host trust
Client, Windows certutil -addstore Root root.pem from an elevated prompt

A private-CA public pair should include the issuing CA in its accepted chain for fingerprint discovery. A leaf-only file supplies no anchor to confirm; use an installed root or explicit host trust. See connect.

Separate public and private TLS in HA

Authority registration can use cluster-issued TLS in both IP-only and DNS public modes. Each Proxy enrolls against the private Authority endpoint with transport_ca=/etc/gdsgate/transport-ca.pem and transport_sni=enroll.gdsgate.internal.

Public entry Proxy certificate Connector registration trust
IP-only join relay 50064 Enrolled Proxy identity; no external public pair Approved cluster CA pin and enroll.gdsgate.internal
DNS public_join on 50061 Operator-supplied public certificate and key Public trust store or the operator's CA

setup prepare does not create an external public pair at /etc/gdsgate/tls/fullchain.pem and privkey.pem. Those paths are inputs only when the chosen mode needs them. The cluster-issued registration pair lives in Authority state and is renewed by Authority. Follow Proxy preparation and Connector join.

Renewal and re-read

Event What gdsgate does
Certificate or key file replaced Re-read within a second. New connections get the new pair; open ones continue. Log: listener certificate reloaded cert=<path> generation=<n>.
kill -HUP <gdsgate-pid> on Linux Re-reads now. Log: SIGHUP: re-reading the listener certificates (nothing else is reloaded).
sc.exe control gdsgate-proxy paramchange on Windows (also gdsgate-authority) The same as SIGHUP.
Unreadable, malformed or mismatched pair The pair in force stays. Log: listener certificate not reloaded; the one in force stays in force.

Certificate refresh retains the configured paths. To reload supported configuration fields, use gdsgate reload --role ROLE or the installed Linux unit's systemctl reload; see the configuration reload matrix. Listener addresses and certificate paths require restart. Policy changes use the policy commands. Certificate renewal procedure: Renewing a listener certificate.

What each profile requires

Check dev pilot production Satisfied by
registration-tls not checked required required any mode: enroll_cert_file, enroll_key_file
enroll-endpoint-tls not checked required required https:// in every node's [enroll].endpoint
proxy-public-tls not checked required required any mode: an enrolled Proxy, or public_cert_file
Client-facing certificate from a public CA optional optional recommended Let's Encrypt, or your own CA installed on every client

Symptoms

Symptom Cause What to do
Service log: the private key of <cert>: ... Permission denied (os error 13) The key is not readable by the service Under /etc/gdsgate: gdsgate --config <file> service install <role> gives it to the gdsgate group. Elsewhere: chgrp gdsgate <key>; chmod 0640 <key>, and the directories above it searchable by the group. In a container, owner uid 10001.
Proxy log: enrollment endpoint https://...: certificate refused (UnknownIssuer); checked for the name `...` against ... The Proxy does not trust the registration certificate Install the CA in the Proxy host's system store or set [enroll].transport_ca.
Proxy log: certificate refused (NotValidForName...) The name checked, shown in the message, is not in the certificate Set [enroll].transport_sni to a name in the certificate.
gdsgate join: certificate refused at https://<IP>:50064 The certificate does not carry IP:<IP>, or the connector lacks its CA Your own CA: reissue with the address in subjectAltName; pass --enroll-ca. Issued by the cluster CA: the address belongs in Authority's [endpoints]; gdsgate config check names what the certificate lacks, and a restart of Authority issues it again.
gdsgate join: presents a chain from <sha256>, not from <sha256> Wrong --enroll-fingerprint; the token is unspent Take transport_ca.fingerprint from setup prepare, or gdsgate ca fingerprint on the Proxy host.
gdsgate join: presents its certificate without the CA that issued it --enroll-fingerprint against a certificate from outside Pass --enroll-ca with that CA.
gdsgate join: certificate refused at https://<IP>:50061 public_cert_file does not carry IP:<IP>, or the Proxy has none and presents its enrolled identity Add the address to the certificate, or join with --enroll-sni <the name on it>. No DNS name: prepare with --join-relay and join at 50064.
gdsgate connect: published no transport CA anchor, and the certificate authorities this host trusts do not vouch for it either The client does not trust the CA of public_cert_file Install the root, or set SSL_CERT_FILE.
gdsgate connect: trust anchor fingerprint mismatch: expected ..., gateway offered ... Wrong --fingerprint Take the value from gdsgate ca fingerprint on the Proxy host.
Log: listener certificate not reloaded The new pair does not parse or the key does not match openssl x509 -in <cert> -noout -pubkey and openssl pkey -in <key> -pubout must print the same key.