Kubernetes¶
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 and Proxy in a Kubernetes cluster, from the Helm chart or the
kustomize base. PostgreSQL outside the chart. Connectors outside the cluster or
in it. Profile: pilot.
| Placeholder | Meaning |
|---|---|
gate.example.com |
The name clients and connectors dial. Resolves to the external address of Service gdsgate-proxy. |
oci://registry.example.com/charts/gdsgate |
The Helm chart. Chart version = product version. |
IMAGE_FROM_OWNER |
The supplied matching image. See Running the image. |
postgres://gdsgate@postgres.example.com:5432/gdsgate |
The store. |
https://idp.example.com/realms/gdsgate |
The OIDC issuer. |
Select the matching image, image tag, chart version and source revision from the
edition's deployment handoff. Replace IMAGE_FROM_OWNER and
IMAGE_TAG_FROM_OWNER in the manifests. Helm commands use:
Before you start¶
| Item | How to check | Reference |
|---|---|---|
| Kubernetes 1.25 or later | kubectl version |
|
| Helm 3.8 or later (Helm tab) | helm version |
|
| A LoadBalancer implementation (cloud, MetalLB, k3s ServiceLB) passing TCP through | kubectl get svc -A shows an EXTERNAL-IP on a LoadBalancer Service |
Services |
The gdsgate-proxy ports open inbound to the load balancer before you install. The administrator opens them; without them join and connect from outside the cluster fail |
from outside, after start: nc -vz gate.example.com 50061 |
Open the ports |
| A default StorageClass | kubectl get storageclass prints (default) |
|
| The nodes pull the image | docker pull IMAGE_FROM_OWNER from a node's network |
Running the image |
PostgreSQL, database and role gdsgate, reachable from the pods |
kubectl run psql --rm -it --restart=Never --image=postgres:17 -- psql postgres://gdsgate@postgres.example.com:5432/gdsgate -c 'select 1' |
State store |
A certificate pair for gate.example.com (Let's Encrypt or your own CA) |
openssl x509 -in fullchain.pem -noout -ext subjectAltName |
Certificates |
OIDC client gdsgate |
as on the single host | [oidc] |
gdsgate on the workstation |
gdsgate --version |
Installation |
production only: wrapped keyrings |
[security.integrity] |
Services¶
| Service | Type | Ports | Serves |
|---|---|---|---|
gdsgate-proxy |
LoadBalancer, externalTrafficPolicy: Local |
50061 client port, 50062 tunnels, 50063 tunnels over WebSocket; 50064 join relay when enabled |
clients, connectors. TLS passthrough: the Proxy terminates TLS. |
gdsgate-authority |
ClusterIP |
50050 registration, 50051 control plane |
the Proxy |
gdsgate-authority-headless |
headless | 50050, 50051 |
pod names of the StatefulSet |
An Ingress with TLS passthrough does not serve the client port: it routes by
server name, and renewal and control-plane calls arrive there under
gdsgate.internal.
Open the ports¶
Open the ports of gdsgate-proxy before you install: 50061, 50062,
50063, and 50064 with the join relay. Where they are opened depends on
the load balancer, and it is the administrator's responsibility; the chart
and the base do not change it. While a port is closed, gdsgate join on a
connector host outside the cluster and gdsgate connect on a workstation
fail. What each port carries: Endpoints.
| Load balancer | Where |
|---|---|
| A cloud's | the security group or firewall rule of the load balancer and the nodes |
| MetalLB, k3s ServiceLB | the firewall of each node that announces the address: ufw allow 50061:50063/tcp, firewall-cmd --permanent --add-port=50061-50063/tcp && firewall-cmd --reload, or nft insert rule inet filter input tcp dport 50061-50063 accept |
gdsgate-authority stays inside the cluster; nothing outside needs
50050 or 50051.
Connector routes¶
| Route | gdsgate-proxy ports |
Helm values | kustomize files | Connector | When |
|---|---|---|---|---|---|
tunneled (recommended) |
50061, 50062, 50063 |
route: tunneled |
as in the base | gdsgate join --enroll-endpoint https://gate.example.com:50061 |
Connectors anywhere. Registration and renewal on the client port; control-plane calls in the tunnel. |
tunneled, join relay |
50061–50064 |
route: tunneled, joinRelay: true |
authority.toml: proxy_join = "gate.example.com:50064"; Proxy: env GDSGATE_PROXY_JOIN_ADDR, port 50064 in the Service |
--enroll-endpoint https://gate.example.com:50064 |
A separate port for registration. Renewal goes to the relay. |
fronted |
50061, 50062, 50063 |
route: fronted |
authority.toml: authority_transport = "fronted" |
--enroll-endpoint https://gate.example.com:50061 --route fronted |
Control-plane calls on the client port as well. At most public_admin_max_connections (default 8) connectors reach Authority at once. |
direct |
not with this layout | Authority binds the pod address and tells connectors no address. |
Keys: public_join,
authority_transport,
proxy_join. Full reference:
split deployment, routes.
Configure¶
Steps 1 to 3 are the same for both tabs.
-
The namespace and the store password (the file holds the password alone):
-
The certificate pair as Secret
gdsgate-tls:Source Command Files: Let's Encrypt, or a CA the image trusts kubectl -n gdsgate create secret tls gdsgate-tls --cert=fullchain.pem --key=privkey.pemFiles: your own CA kubectl -n gdsgate create secret generic gdsgate-tls --type=kubernetes.io/tls --from-file=tls.crt=fullchain.pem --from-file=tls.key=privkey.pem --from-file=ca.crt=root.pemcert-manager a CertificatewithsecretName: gdsgate-tlsanddnsNames: [gate.example.com]. A CA issuer writesca.crtas well.fullchain.pem: the leaf, then each intermediate. With your own CA the Proxy verifies Authority's registration listener againstca.crt: Helmtls.caKey: ca.crt, kustomizetransport_cainproxy.toml. A renewed Secret reaches the pods within about a minute; the next connection gets the new pair (renewal). -
The two keyrings, made on the workstation:
install -d -m 0700 gdsgate-keys gdsgate authority init --profile pilot --output gdsgate-keys/authority.toml kubectl -n gdsgate create secret generic gdsgate-keyrings \ --from-file=gdsgate-keys/control.keys --from-file=gdsgate-keys/audit.keysgdsgate-keys/authority.tomlis not used. Keepgdsgate-keysapart from the store backups (Backup). -
The configuration:
values.yaml:domain: gate.example.com profile: pilot route: tunneled store: url: postgres://gdsgate@postgres.example.com:5432/gdsgate oidc: issuer: https://idp.example.com/realms/gdsgate clientId: gdsgate tls: caKey: "" # ca.crt with your own CA policy: accessGroup: adminsValue Default Sets domaingate.example.comAddresses Authority tells clients and connectors; the name the Proxy checks on the registration certificate. profilepilot[security].profileof both roles.routetunneledConnector routes. joinRelayfalseJoin relay on 50064.store.urlrequired store_url. The password comes from Secretgdsgate-store.oidc.issuer,oidc.clientIdunset, gdsgate[oidc]of both roles.tls.secretName,tls.caKeygdsgate-tls, unsetThe certificate pair; the key holding the CA. keyrings.secretNamegdsgate-keyringscontrol.keys,audit.keys.policy.accessGroupadminsThe group the seed policy makes the cluster's owner ( --access-group).workload.trustDomainunset [workload].trust_domainof Authority; unset: workload identity off. Issuers go toauthority.extraConfigas[[workload.issuers]]. A workload outside the cluster bootstraps through the join relay (joinRelay: true):gdsgate identity bootstrap --endpoint https://gate.example.com:50064. The client port50061does not issue workload identities.image.repository,image.tagthe release registry, v<chart version>The image. authority.replicas1Two or more: [ha]on; the Proxy dialsgdsgate-authority-0and fails over to the others. Registration goes to Servicegdsgate-authority; every replica serves registration and renewal.proxy.service.typeLoadBalancerClusterIPbehind a TCP front of your own.proxy.service.annotations,loadBalancerSourceRangesnone Passed to Service gdsgate-proxy.authority.extraConfig,proxy.extraConfignone TOML appended to the file; sections the chart does not write. authority.persistence.size,proxy.persistence.size1GiThe volume at /var/lib/gdsgate.Check:
helm template gdsgate oci://registry.example.com/charts/gdsgate --version "$CHART_VERSION" -f values.yaml > /dev/nullprints nothing.packaging/kustomize/basefrom the release source goes togdsgate/base; the overlay isgdsgate/overlays/prod:gdsgate/ base/ packaging/kustomize/base, unchanged overlays/prod/ kustomization.yaml authority.toml proxy.tomloverlays/prod/kustomization.yaml:apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: gdsgate resources: - ../../base images: - name: registry.example.com/gdsgate newTag: IMAGE_TAG_FROM_OWNER configMapGenerator: - name: gdsgate-authority behavior: replace files: - authority.toml - name: gdsgate-proxy behavior: replace files: - proxy.tomloverlays/prod/authority.toml. Authority binds50050,50051and9090on every interface of the pod (GDSGATE_AUTH_ADDR,GDSGATE_AUTH_ENROLL_ADDR,GDSGATE_TELEMETRY_METRICS_LISTENinbase/authority.yaml):store_url = "postgres://gdsgate@postgres.example.com:5432/gdsgate" store_password_file = "/etc/gdsgate/store.password" # Secret gdsgate-store [security] profile = "pilot" [security.integrity] control_keyring = "/etc/gdsgate/control.keys" # Secret gdsgate-keyrings audit_keyring = "/etc/gdsgate/audit.keys" [endpoints] proxy_public = "gate.example.com:50061" # told to clients and joining nodes proxy_internal = "gate.example.com:50062" proxy_ws = "gate.example.com:50063" [connector] authority_transport = "tunneled" # route told to connectors [proxy] public_join = true # connectors renew at proxy_public [authority] enroll_cert_file = "/etc/gdsgate/tls/fullchain.pem" # Secret gdsgate-tls enroll_key_file = "/etc/gdsgate/tls/privkey.pem" [policy] path = "/var/lib/gdsgate/policy.cedar" # written by the seed-policy init container [oidc] issuer = "https://idp.example.com/realms/gdsgate" client_id = "gdsgate" # [workload] # trust_domain = "gds.example" # workload identity; unset: off [audit] export_path = "/var/lib/gdsgate/audit.jsonl"overlays/prod/proxy.toml. The Proxy binds50061to50063and9090on every interface of the pod (GDSGATE_PROXY_PUBLIC_ADDR,GDSGATE_PROXY_INTERNAL_ADDR,GDSGATE_PROXY_WS_ADDR,GDSGATE_TELEMETRY_METRICS_LISTENinbase/proxy.yaml):[endpoints] authority = "gdsgate-authority:50051" authority_enroll = "gdsgate-authority:50050" [proxy] public_cert_file = "/etc/gdsgate/tls/fullchain.pem" # Secret gdsgate-tls public_key_file = "/etc/gdsgate/tls/privkey.pem" public_join = true # registration and renewal on the client port [enroll] endpoint = "https://gdsgate-authority:50050" transport_sni = "gate.example.com" # the name on the certificate # transport_ca = "/etc/gdsgate/tls/ca.pem" # your own CA: ca.crt of gdsgate-tls state_dir = "/var/lib/gdsgate/proxy-state" [security] profile = "pilot" [oidc] issuer = "https://idp.example.com/realms/gdsgate" client_id = "gdsgate"The seed policy's owner group:
adminsinACCESS_GROUPof theseed-policyinit container; change it with a patch in the overlay.Check:
kubectl kustomize gdsgate/overlays/prod > /dev/nullprints nothing.The pods mount
/etc/gdsgateread-only (the files above and the Secrets), a volume at/var/lib/gdsgateand a memoryemptyDirat/tmp. They run as uid10001, group10001, read-only root file system, no capabilities,RuntimeDefaultseccomp. Key reference:[endpoints],[enroll],[proxy],[authority].
Start¶
-
Install:
-
Authority, the administrative identity, and the Proxy's token:
kubectl -n gdsgate rollout status statefulset/gdsgate-authority kubectl -n gdsgate exec -it statefulset/gdsgate-authority -- bashIn the pod:
gdsgate --config /etc/gdsgate/authority.toml authority identities bootstrap --role admin \ --output-dir /var/lib/gdsgate/admin gdsgate --config /etc/gdsgate/authority.toml authority tokens create --role proxy exitThe token into a Secret, and the Proxy restarted:
kubectl -n gdsgate create secret generic gdsgate-proxy-token --from-literal=token=<token> kubectl -n gdsgate delete pod gdsgate-proxy-0 kubectl -n gdsgate rollout status statefulset/gdsgate-proxyUntil the token Secret exists,
gdsgate-proxy-0restarts:pilotrefuses a Proxy with neither a token nor an identity. The Proxy spends the token once and keeps its identity in its volume. -
Point
gate.example.comat the Service's address:
Administration¶
Every Authority replica serves administrative commands.
| Route | Commands | Recorded as | Requires |
|---|---|---|---|
| Workstation, through the gateway | gdsgate connect gate.example.com, gdsgate login, then gdsgate authority <command> |
you, and your address | a policy permit for your group: the seed policy gives policy.accessGroup connector and Proxy tokens, the catalogue and revocation (--route) |
| An Authority pod, any replica | kubectl -n gdsgate exec -it gdsgate-authority-<n> -- bash, then gdsgate --config /etc/gdsgate/authority.toml authority <command> |
gdsgate-admin |
authority identities bootstrap --role admin --output-dir /var/lib/gdsgate/admin once in that pod; it lasts one day and the same command renews it |
A connector outside the cluster¶
The catalogue row and a token, from the workstation:
gdsgate authority publication set manual
gdsgate authority resources set demo-ssh --kind ssh --onward-auth local
gdsgate authority tokens create --role connector > connector.token
On the connector host, as root, with connector.token copied there:
gdsgate join gate.example.com --name conn-1 \
--enroll-endpoint https://gate.example.com:50061 \
--serve demo-ssh=ssh < connector.token
gdsgate --config /etc/gdsgate/connector.toml service install connector
systemctl start gdsgate-connector
rm connector.token
With your own CA and the root not in the connector host's store, add
--enroll-ca /etc/gdsgate/root.pem. Other routes: the Connector column of
Connector routes. Windows:
VPS, a connector on another host.
A connector in the cluster¶
A StatefulSet beside the resources it serves, registering at Service
gdsgate-proxy. The catalogue row and the token as above, the token into a
Secret:
gdsgate authority resources set orders-db --kind postgres
kubectl -n gdsgate create secret generic gdsgate-connector-token \
--from-literal=token="$(gdsgate authority tokens create --role connector)"
kubectl -n gdsgate apply -f connector.yaml
connector.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: gdsgate-connector
data:
connector.toml: |
[endpoints]
proxy_internal = "gdsgate-proxy:50062"
proxy_ws = "gdsgate-proxy:50063"
[enroll]
endpoint = "https://gdsgate-proxy:50061" # public_join on the client port
renew_endpoint = "https://gdsgate-proxy:50061"
transport_sni = "gate.example.com" # the name on the certificate
# transport_ca = "/etc/gdsgate/tls/ca.pem" # your own CA
state_dir = "/var/lib/gdsgate/connector"
node_name = "conn-k8s"
[connector]
id = "conn-k8s"
authority_transport = "tunneled"
[[connector.backends]]
resource = "orders-db"
kind = "postgres"
addr = "orders-db.orders.svc:5432"
[security]
profile = "pilot"
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: gdsgate-connector
spec:
replicas: 1
serviceName: gdsgate-connector
selector:
matchLabels:
app.kubernetes.io/component: connector
template:
metadata:
labels:
app.kubernetes.io/component: connector
spec:
automountServiceAccountToken: false
enableServiceLinks: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: connector
image: IMAGE_FROM_OWNER
args: ["--config", "/etc/gdsgate/connector.toml", "connector"]
env:
- name: GDSGATE_TELEMETRY_METRICS_LISTEN
value: "0.0.0.0:9090"
- name: GDSGATE_ENROLL_TOKEN
valueFrom:
secretKeyRef:
name: gdsgate-connector-token
key: token
optional: true
readinessProbe:
httpGet:
path: /readyz
port: 9090
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: config
mountPath: /etc/gdsgate
readOnly: true
- name: state
mountPath: /var/lib/gdsgate
- name: tmp
mountPath: /tmp
volumes:
- name: config
projected:
defaultMode: 0440
sources:
- configMap:
name: gdsgate-connector
- secret:
name: gdsgate-tls
optional: true
items:
- key: ca.crt
path: tls/ca.pem
- name: tmp
emptyDir:
medium: Memory
volumeClaimTemplates:
- metadata:
name: state
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
In another namespace, dial gdsgate-proxy.gdsgate.svc and copy ca.crt
there. renew_endpoint is set in the file: a connector started from its own
file renews where the file says. With route: fronted the connector file
carries authority_transport = "fronted" and
[endpoints].authority = "gdsgate-proxy:50061".
Discover resources from inside Kubernetes¶
The Connector StatefulSet above serves declared backends and mounts no
ServiceAccount token. For discovery, add the token and read-only RBAC below.
The Connector must reach both the Kubernetes API and the selected target
addresses. This example discovers web Services in namespace orders without
exposing the Kubernetes API as a gdsgate resource.
First enroll and start the Connector, then obtain its Registry UUID:
For this recipe's manual Connector, create a scope for that UUID:
CONNECTOR_ID='<Registry UUID of conn-k8s>'
DISCOVERY_SCOPE=kube-
gdsgate authority scopes set "$DISCOVERY_SCOPE" \
--node "$CONNECTOR_ID" --kind web --env test --fallback deny
For an automatic Connector, use its assigned prefix instead, such as conn-k8s/,
and skip scope creation. Check that its scope admits web. Several clusters on
one automatic Connector use different source IDs and the same assigned scope.
Apply these objects. The orders namespace and the web application's Service
already exist:
apiVersion: v1
kind: ServiceAccount
metadata:
name: gdsgate-discovery
namespace: gdsgate
automountServiceAccountToken: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: gdsgate-discovery
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gdsgate-discovery
subjects:
- kind: ServiceAccount
name: gdsgate-discovery
namespace: gdsgate
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: gdsgate-discovery
Copy the existing configuration from its ConfigMap, configure a discovery-only source on the workstation, and update that same ConfigMap:
kubectl -n gdsgate get configmap gdsgate-connector \
-o jsonpath='{.data.connector\.toml}' > connector.toml
gdsgate --config connector.toml setup kubernetes configure orders-web \
--purpose discovery --scope "$DISCOVERY_SCOPE" \
--api-url https://kubernetes.default.svc:443 \
--ca-path /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
--token-path /var/run/secrets/kubernetes.io/serviceaccount/token \
--label-selector 'gdsgate.io/expose=true' --sources service
kubectl -n gdsgate create configmap gdsgate-connector \
--from-file=connector.toml --dry-run=client -o yaml | kubectl apply -f -
The probe lists Services across namespaces with the supplied label selector.
Its API request needs cluster-wide list permission; a namespace Role cannot
authorize that request. Label selection controls discovery and does not narrow
the ServiceAccount's RBAC permission. The source creates no API backend. Keep
the Connector's existing identity PVC and enrollment settings.
Save this patch as connector-discovery-patch.yaml and apply it:
kubectl -n gdsgate patch statefulset gdsgate-connector \
--type merge --patch-file connector-discovery-patch.yaml
kubectl -n gdsgate rollout status statefulset/gdsgate-connector
kubectl -n orders label service orders-web gdsgate.io/expose=true --overwrite
kubectl -n orders annotate service orders-web gdsgate.io/kind=web --overwrite
The Service's first port is the HTTP upstream port. Discovered web targets use
HTTP and the web lane; HTTPS or custom upstream trust needs an explicit backend.
Read the Connector log after a probe interval, then inspect
authority resources list and the client's resources list. A manual scope
lists kube-orders-orders-web; automatic publication returns the qualified ID
containing this source and Service identity. Use that returned ID in policy.
Grant view, connect and httpRequest, then verify allowed and denied requests,
audit and token refresh without a Pod restart. list is sufficient for this
polling source; it needs no impersonation rights.
Upgrade¶
The pods are replaced; the volumes stay. The Proxy starts with the identity in its volume and does not read the token. Read the upgrade guide first.
Backup¶
| What | How |
|---|---|
| The store | pg_dump -Fc postgres://gdsgate@postgres.example.com:5432/gdsgate > gdsgate.dump |
| The keyrings | gdsgate-keys/control.keys, gdsgate-keys/audit.keys, kept apart from the store dump; or kubectl -n gdsgate get secret gdsgate-keyrings -o yaml |
| The configuration | values.yaml or overlays/prod |
| The Proxy's identity | volume state-gdsgate-proxy-0. Lost: a new token, and the old proxy row revoked. |
| Synchronized Authority state, when enabled | Each instance's complete keyring-sync directory, receiver, root, cache and journal, with its stable owner and matching store backup |
| Connector state | Its identity PVC, SSH host keys, publication journal and pending recording buffers |
A store dump without the keyrings cannot be verified. See Keeping keyrings out of the database backup.
Verify¶
A is kubectl -n gdsgate exec statefulset/gdsgate-authority -- gdsgate --config /etc/gdsgate/authority.toml.
The gdsgate authority rows run on the workstation.
| Command | Expected | If not: cause → fix |
|---|---|---|
kubectl -n gdsgate get pods |
every gdsgate-authority-<n> and gdsgate-proxy-0 1/1 Running |
CreateContainerConfigError → a Secret from Configure is missing: kubectl -n gdsgate describe pod <pod> names it. Init:CrashLoopBackOff on Authority → kubectl -n gdsgate logs <pod> -c seed-policy. |
kubectl -n gdsgate logs statefulset/gdsgate-authority |
auth service serving gRPC ... enroll_mode="TLS from the configured certificate" |
store errors → store.url, the password in gdsgate-store, or PostgreSQL unreachable from the pod. |
A config check /etc/gdsgate/authority.toml --role authority |
0 error(s), 0 warning(s) |
The finding names the key. |
kubectl -n gdsgate logs gdsgate-proxy-0 |
enrolled and persisted a new transport identity, proxy serving node registration on the public listener, proxy serving gRPC (mTLS (enrolled), public TLS from the configured certificate) |
Refusal naming proxy-internal-mtls → the token Secret is missing: Start, step 2. certificate refused (...); checked for the name ... → the certificate does not carry domain, or your own CA without ca.crt. unknown token → a new token into gdsgate-proxy-token, then kubectl -n gdsgate delete pod gdsgate-proxy-0. |
kubectl -n gdsgate exec gdsgate-proxy-0 -- gdsgate config check /etc/gdsgate/proxy.toml --role proxy |
0 error(s), 0 warning(s) |
|
kubectl -n gdsgate get svc gdsgate-proxy |
an EXTERNAL-IP; ports 50061, 50062, 50063 |
<pending> → no LoadBalancer implementation in the cluster. |
Workstation: gdsgate connect gate.example.com |
ok: wrote ~/.gdsgate/config.toml · gateway <version> |
See Certificates. |
gdsgate authority nodes list |
admin, proxy rows, active |
PermissionDenied → your group has no viewNodes permit. |
Connector host: gdsgate join gate.example.com ... --enroll-endpoint https://gate.example.com:50061 |
ok: joined gate.example.com as conn-1 · role connector |
certificate refused → the connector host does not trust the CA (--enroll-ca). Timeout → gate.example.com does not resolve to the Service address, or the front drops 50061. Connection reset by peer just after install → the load balancer does not forward yet; run join again. |
gdsgate authority offers list |
demo-ssh ssh ... yes, orders-db postgres ... yes |
Connector log: declared a resource the cluster is not giving it persisting → the catalogue row is missing. |
kubectl -n gdsgate logs gdsgate-proxy-0 \| grep 'tunnel registered' |
connector_label=conn-1 bound=["demo-ssh"] |
bound=[] persisting → as the row above. |
kubectl -n gdsgate logs gdsgate-connector-0 |
tunnel transport connected |
certificate refused → transport_sni or transport_ca in connector.toml. |
| Connector log, from one hour before its certificate expires | renewed the transport identity |
node identity revoked → the row is revoked in gdsgate authority nodes list; a new token and join. Connection errors → no Authority pod is Running. |
After an upgrade: kubectl -n gdsgate logs gdsgate-proxy-0 |
reusing persisted transport identity |
enrolled and persisted a new transport identity → the volume was lost; revoke the old proxy row. |
After these checks pass, complete the access checks from a client workstation.
Profile¶
pilot, the same matrix rows as the single host. For
production: keyrings in a key service
(authority init --control-keyring),
oidc.issuer set, profile: production.