Concepts¶
Architecture reference: the roles, the path a request takes, the identity and trust model, and the boundaries each component enforces. Use the Glossary for the canonical names of roles, objects, and security boundaries. Configuration keys are in Configuration.
Roles¶
gdsgate runs four roles from one binary, selected by subcommand: three long-lived services (Authority, Proxy, Connector) and the client command.
| Role | What it does |
|---|---|
| Authority | The cluster authority. Holds the certificate authorities, evaluates the Cedar policy, issues short-lived access certificates, verifies identity tokens against the provider's signing keys, registers new nodes, and persists the tamper-evident audit log. |
| Proxy | The public, client-facing entry point. Terminates the client's TLS, forwards the authorization request to Authority, and relays an authorized session onto the connector that serves the resource. It keeps no durable state: only in-memory tunnel registrations, the live-session registry, and, where the clientless browser entry is configured, its own table of signed-in browser sessions. |
| Connector | Runs next to the protected resources. Dials out to the Proxy and holds a reverse tunnel open, over which every user session arrives, and dials out to Authority for the control-plane calls it makes per session. Serves the resources declared in its config: SSH, PostgreSQL / MySQL, Kubernetes, MCP, model services, raw TCP. |
| Client | The user's gdsgate invocation: login, then ssh, db proxy, kube ls, tcp proxy, mcp proxy, plus the proxy-ssh transport that native ssh drives through ProxyCommand. |
A state store, PostgreSQL in production and SQLite in development, holds Authority's audit log, the transport CA's private key, the persisted SSH certificate authorities, the registration registry, and the live resource inventory.
flowchart LR
subgraph edge[Edge zone]
client(["Client<br/>login · ssh · psql · kubectl · redis-cli"])
end
subgraph control[Control zone]
proxy[Proxy]
authority["Authority<br/>CA · Cedar policy · audit · store"]
store[("State store<br/>PostgreSQL")]
end
subgraph protected[Protected zone]
connector[Connector]
db[(PostgreSQL / MySQL)]
host["SSH host · TCP · K8s · MCP · model service"]
end
idp([OIDC identity provider])
client -- "public TLS / gRPC" --> proxy
proxy -- "mTLS" --> authority
authority --- store
connector -- "reverse tunnel<br/>(mTLS, outbound)" --> proxy
connector -- "mTLS<br/>(control-plane RPCs)" --> authority
connector --- db
connector --- host
client -. "login (device flow / PKCE)" .-> idp
authority -. "discovery + JWKS" .-> idp
How a request flows¶
sequenceDiagram
actor U as User
participant IdP as IdP
participant P as Proxy
participant A as Authority
participant Ag as Connector
participant R as Resource
U->>IdP: log in (device flow / PKCE)
IdP-->>U: id_token (JWT)
U->>P: RequestAccess(id_token, resource)
P->>A: authorize(id_token, resource, context)
A->>A: verify token (JWKS), map claims, evaluate Cedar
A-->>P: Allow → short-lived certificate (or Deny)
P-->>U: certificate
U->>P: OpenSession(resource, certificate)
P->>Ag: relay over the reverse tunnel
Ag->>Ag: verify certificate against the trust bundle
Ag->>R: connect (TCP / DB / kube) or terminate (SSH)
Ag-->>U: session bytes
- The user signs in to the identity provider and receives an OIDC
id_token. - The client calls
RequestAccesson the Proxy with the token and the target resource id. The Proxy forwards it to Authority. - Authority verifies the token (signature against the provider's JWKS, issuer,
audience, expiry), maps the claims to an internal identity (
subbecomes the principal,groupsbecomes its Cedar group memberships), and evaluates the Cedar policy for(principal, action, resource). The action isconnectfor every protocol; the resource's entity type names the protocol:SshHost,Database,KubernetesCluster,TcpService,McpServer,WebApp,WebEgress. - On Allow, Authority issues a short-lived certificate scoped to that resource and writes the decision to the audit chain before returning it. On Deny it writes the denial and returns an error.
- The client calls
OpenSessionwith the certificate. The Proxy relays the session to the connector that registered the resource. The connector verifies the certificate against its trust bundle, then either forwards the byte stream (TCP, database, Kubernetes) or terminates the protocol (SSH, and HTTP for a model service or a web application) and runs the requested shell, exec or SFTP itself.
The data-plane session is authorized by the certificate rather than the identity token, so the connector never sees or trusts the identity provider.
Five lanes authorize again inside the session: Kubernetes checks every API
request, MCP every tools/call, a model service every call, a web application
every request, and a restricted database session every statement. Those checks run
at the connector and call Authority for the decision; see
finer-grained authorization.
The reverse tunnel¶
Connectors accept no inbound connections. Each connector dials out to the Proxy's
internal listener and holds a multiplexed tunnel open, over gRPC by default, over
a WebSocket as a fallback, and through an HTTP CONNECT proxy where egress
requires one. The Proxy routes an authorized session onto the tunnel of the
connector that registered the resource.
The protected zone therefore needs no inbound firewall rules, only outbound egress to the Proxy, and a backend is reachable only through the connector that fronts it.
The connector holds one further outbound channel, a direct mTLS client to Authority, for the control-plane decisions it makes while serving a session: minting a downstream OpenSSH certificate per connection (SSH model B), checking whether a forward channel is allowed, authorizing each Kubernetes request, MCP tool call and model call, resolving a database session's role and statement categories, and publishing its live resource inventory. All of it is egress from the connector's zone; Authority never holds a route back to the resources.
Identity and authorization¶
Authentication is OIDC. Authority fetches the provider's discovery document and JSON Web Key Set at startup and verifies every identity token's signature, issuer, audience, and expiry. Only asymmetric signing algorithms are accepted, and the accepted set is configurable. A built-in development issuer (HS256) exists for loopback and unit tests only.
Claim mapping turns the verified token into an internal identity: the token's
sub becomes the Cedar principal (User::"<sub>"), and the groups claim
becomes the principal's group memberships (Group::"<name>"). Policies match on
these and on resource attributes.
Authorization is Cedar. gdsgate ships a Cedar schema describing the entity types, actions, and context fields, so a policy is validated against a known shape when it loads. With no policy configured, Authority runs a deny-all bootstrap policy. See Policy for the schema and patterns.
Workload identity¶
A workload is a non-human client: a CI job, a service, or an autonomous agent.
Each workload holds its own short-lived certificate, authorized by the same Cedar
policy as a person. Workload identity is off by default and is enabled with the
[workload] section.
Bootstrap. gdsgate machine-id presents one of two proofs and receives a
certificate:
- A platform token. A GitLab CI job requests an id-token addressed to the gateway; its claims (the project path, the branch) are what the platform vouches for. Authority verifies it against the platform's published keys, as it verifies a human login.
- A provision token. For a host with no platform issuer, an operator mints a
one-time provision token
(
gdsgate authority create-token --role workload).
The key pair is generated locally and only the public half is sent; the private key never leaves the host.
Naming. The issued certificate carries a SPIFFE ID,
spiffe://<trust-domain>/<issuer>/…, and no per-resource scope. The path after
the issuer is templated from the proof's claims, so a GitLab workload becomes
something like spiffe://gds.example/gitlab/project/infra/ref/main.
Authorization. Because the certificate carries no scope, every connection it
opens is authorized on its own against the Cedar Agent principal; see
finer-grained authorization. A workload
certificate is authorized per connect on the SSH, PostgreSQL / MySQL, Kubernetes,
MCP and raw-TCP lanes, and per call on the model lane, which takes no connect
decision. Two derived capabilities refuse a workload: SSH jump-host onward
certificates and -L / -R forwards.
Owner attribution. A workload may carry an owner, either the human it acts for
(from a claim such as GitLab's user_login) or a static service account. The
owner is recorded on every action the workload takes and can be read by policy.
Lifecycle. A workload bootstrapped from a provision token is tracked in a
registry and renews token-lessly, presenting its current certificate and a proof
of possession, until an operator revokes it
(gdsgate authority revoke-workload). A workload bootstrapped from a platform
token keeps no registry row: it re-runs the bootstrap to stay current and is cut
off by policy. Certificate lifetime is minutes in both cases, so no long-lived
machine credential exists.
Delegation¶
Delegation hands an autonomous agent a subset of a person's own access without
sharing a credential or changing policy. The delegator names capabilities
(db:read@prod-pg, ssh:connect@bastion, kube:read@staging,
mcp:search@corp) and the gateway mints the agent a short-lived certificate
bound to the resulting grant. Every action the agent takes is attributed to both
the agent and the delegator.
Delegated capabilities must be a subset of the delegator's currently authorized capabilities: the gateway checks each capability against the delegator's own policy at mint time and refuses any it would deny.
Enforcement is per request. Each action is checked against the grant in addition
to the ordinary policy decision. A db:read@prod-pg grant keeps the agent
read-only on that database even where policy would allow more; an
mcp:search@corp grant admits exactly that one tool, and
mcp:resource:changelog@corp exactly that one resource, and no prompt of the
same name. The grant is read from the registry on each operation, so revocation
or TTL expiry takes effect on the agent's next request; certificate revocation is
not involved.
Owner-wide revocation. gdsgate revoke --owner <user>, or --all for the
caller's own grants, advances that owner's revocation epoch, which invalidates
every grant issued before the new epoch without enumerating grant ids. Grants
issued after the new epoch are unaffected, so the command is safe to repeat, and
it takes effect on each agent's next authorization check. Use it when
de-provisioning a person.
A grant is presented in one of two ways:
- An agent holds its own certificate bound to the grant, written by
gdsgate delegate --bind-keyor served by--exec, and runs unattended. - The delegator narrows their own session: any command run with
--as <grant-id>is restricted to the grant's capabilities.
Delegation is refused until
[audit].delegation_key_path names the key that seals
each grant. See
User guide → Delegating access.
Running an AI agent is this delegation plus two settings: how the process is confined on the host it runs on, and where it obtains model access, which is itself a catalogue resource. See AI agents.
Finer-grained authorization¶
Connect-level authorization decides whether a session may be opened. Five lanes add a second layer, evaluated on every operation inside the session:
- Kubernetes. The connector calls Authority on every API request (action
kubeRequest). A policy can gate by verb, resource kind, namespace and name.context.read_onlyis true for verbs that only read (get,list,watch,log), so a rule can grant reading without granting writing. - MCP. The connector calls Authority on every
tools/call(actionmcpCallTool) and filterstools/listresponses per identity (actionviewTools). A tool the operator marks destructive can be denied by policy regardless of its name. - Model services. The connector decides every call (action
llmCall) before anything reaches the provider. The model name is read from the request body, so a rule can name one model (LlmModel) or a whole service (LlmService), and aforbidon a model overrides apermiton its service. Input and output token counts reach the audit chain on every call. - Databases in restricted mode. The connector classifies each statement (read,
write, DDL, DCL) and checks the category against the policy (action
dbQuery) before it runs. An unrestricted session performs no per-statement check and still records the structured query log. - Web applications. The connector calls Authority on every request (action
httpRequest), after matching the canonicalised path against the operator's named route table.context.routecarries the matched name, or""where no route covers the path, so a rule names a path family rather than parsing the application's own URLs. Aweb-egressresource opens atconnectlike any other session, andhttpRequestthen decides each request inside it against the target the request itself names. ACONNECT, which is howhttpstravels through a way out that does not read its contents, is decided on the host and port alone and costs theweb:tunnelcapability. By default the application sees the caller's own credentials unchanged; identity substitution is per-backend. See Policy → HTTP(S) application routes and Configuration → Telling the application who came.
Each of these runs in addition to the connect-level decision, and each is audited.
SSH backend models¶
A connector serves an SSH resource in one of two models:
- Model A, connector-terminated and recorded. No downstream address is configured; the connector is the SSH server. It terminates the client's session, spawns a PTY under its own operating-system user, and records the terminal stream in asciicast v2. The session runs on the connector host.
- Model B, jump host. A downstream address is configured. The connector
terminates the client's session in order to record it, opens its own SSH
session to the downstream
sshd, and relays every channel: bytes, PTY requests,shell/exec, window-change events, exit status, stderr. It authenticates to the downstreamsshdwith an OpenSSH user certificate Authority signs per connection.
-L and -R forwards work in both models, under
two-layer gating. -A works in model A only, under
the same gating.
Two-layer forward gating¶
ssh -L and ssh -R open extra channels that proxy TCP through the SSH session.
Each channel is gated twice, and both layers must permit it, so that a connector
cannot become an open internal proxy:
- Layer 1, an operator allow-list per backend. Each SSH backend declares
allow_local_forwardandallow_remote_forwardpatterns. With no entries,-Lis restricted to loopback and-Ris disabled. Any non-loopback target requires an explicit entry. This layer bounds what is reachable through the backend at all. - Layer 2, the Cedar policy, per user and session. At channel-open time the
connector calls Authority with the requested
host:port(-L) orbind_host:port(-R) in the context, and the policy decidessshForwardLocal/sshForwardRemotefor that principal in that session.
A channel denied by either layer is refused, and the decision is audited.
ssh -A is gated the same way and is not a third forward. It carries no route:
it puts the client's ssh-agent inside the session, so anything that reaches the
forwarded socket signs as the client wherever the client's keys are trusted,
including root on the connector host. Layer 1 is therefore a switch rather than
a list, allow_agent_forward, off unless an operator turns it on per backend;
Layer 2 is the Cedar action sshForwardAgent. Refused, the connector creates no
socket at all, so no program in the session ever sees SSH_AUTH_SOCK.
PKI and node registration¶
gdsgate runs four certificate authorities, all held by Authority:
- Transport CA (
HostTls). Issues the X.509 identities every node uses for internal mutual TLS and for the Proxy's public TLS listener. All leaves carry the shared namegdsgate.internal, so peers verify by that name independently of the host's DNS. Roles that terminate TLS carry a second name:auth.gdsgate.internalfor the control plane,proxy.gdsgate.internalandtunnel.gdsgate.internalfor the Proxy's two listener groups, which lets a front route all of them by TLS server name on one address. - User SSH CA (
UserSsh). Signs the short-lived access certificates Authority issues on Allow. The Proxy and connectors trust this CA's bundle: the current generation plus the retiring one during a rotation. - Onward SSH CA (
OnwardSsh). Signs the OpenSSH user certificates a connector presents to a downstreamsshdon the model-B path. The connector never holds this CA's private key and requests a fresh certificate per connection. - SPIFFE CA (
Spiffe). Signs the workload certificates. It is carried in the trust bundle, so connectors and proxies verify a workload offline, and it rotates on the same paced double-signing controller.
A standalone Proxy or Connector starts with no internal credentials and obtains
them by registration: an operator generates a one-time registration token with
gdsgate authority create-token, the joining node presents it over Authority's
bootstrap listener with a certificate-signing request, and Authority returns a
signed transport certificate and the trust bundle. Every control-plane hop is
mutually authenticated afterwards, and the node persists its identity for reuse
across restarts. Authority persists the transport CA to the state store, so a
restart keeps already-registered nodes trusted.
Audit¶
Every privileged action is appended to a hash-chained audit log in the state store: authorization decisions on allow and deny, node registrations, administrative operations (CA rotation, policy change, JIT approval, session kill), and sessions (a recorded SSH session, the query-log tap on databases, Kubernetes requests, MCP tool calls). Each record links to the previous record's hash, so tampering or a gap is detectable. Authority refuses to issue a grant whose audit record cannot be made durable first. Records export as canonical JSON, Splunk HEC, or CEF and syslog. See Operations.
Resource discovery¶
A resource must be in the catalog to appear in gdsgate ls and to be reachable:
a request naming a resource the catalog does not hold is refused before the policy
is consulted. The catalog is filled two ways:
- Static. Entries declared under
[discovery]in Authority's config: id, kind, environment, labels. These supply the attributes a policy reads (resource.environment,resource.getTag("team")). - Live. A connector enumerates the databases behind a PostgreSQL entry and
publishes them, so each appears in
gdsgate lsas a connectableDatabase::"<host>.<db>"sub-resource. An MCP backend's tools are discovered too. A tool is a runtime operation rather than a connect target, so tools never appear ingdsgate ls; they filter the MCPtools/listresponse per identity (viewTools) and authorize each call (mcpCallTool).
A live snapshot is observational: it determines what appears in a listing, or in
tools/list for MCP tools, and never whether a connection or call is permitted,
which is authorized separately. Each snapshot is owned by exactly one connector,
so a connector that owns dev-pg cannot publish an inventory for prod-pg. Each
carries a freshness window, after which stale entries fall out, and is sealed with
an HMAC receipt whose key lives outside the state store, so a direct rewrite of
the row fails verification and is dropped. Database listings are filtered per
identity by the Cedar view action.
Just-in-time access¶
A policy can require a just-in-time approval before access is granted. The user
runs gdsgate request-access, one or more approvers run gdsgate approve, and
the policy evaluates to Allow for that user and resource only afterwards. The
number of approvers follows a cascade, narrowest first: a per-resource threshold,
then a per-environment threshold, then the global default. Requests and approvals
are audited.
Live sessions and termination¶
The Proxy keeps an in-memory registry of the sessions open through it. An operator
lists them with gdsgate session list and ends one with gdsgate session kill,
by id or by a --principal / --resource predicate. Each termination is
authorized by the Cedar killSession action, so a policy can permit users to end
their own sessions and an on-call group to end any, and each is recorded in the
audit chain.
Deployment shapes¶
- All-in-one (
gdsgate all). Every role in one process over one state store, for local development and small single-node deployments. The state store is the same one a multi-node cluster uses, so an external connector can register and join an all-in-one cluster. - Multi-node. Separate
authority,proxy, andconnectorprocesses that register for transport identities and run mutual TLS between them. This is the production shape. Authority scales for read load through several instances sharing the store, and HA elects one audit write-leader at a time.
See the Admin guide for both shapes end to end.
Glossary¶
Use these terms consistently across task pages, reference pages, and runbooks. The role and boundary sections above explain the terms in context.
Roles and objects¶
| Term | Canonical meaning |
|---|---|
| Authority | The control-plane service that owns durable identity, policy, catalog, and audit state. |
| Proxy | The client-facing service that terminates the public access path and carries authorized traffic toward a Connector. |
| Connector | The data-plane service that reaches protected backends and offers them through the control-plane catalog. |
| Client | The gdsgate command-line side or a native client using the local access path. |
| service role | A role implemented by a running Authority, Proxy, or Connector process. |
| actor | The human, workload, or delegated agent whose action is evaluated. |
| node | An enrolled Authority, Proxy, or Connector instance with transport identity and lifecycle state. |
| catalog resource | The named, policy-visible target that a user or workload selects. |
| Connector backend | The Connector-side implementation and address that serves a catalog resource. |
| session | A live access lifecycle with an owner, resource, state, and close or kill evidence. |
Security and access¶
| Term | Canonical meaning | Boundary to state |
|---|---|---|
| authentication | Establishing who or what presents an acceptable identity. | It does not grant an action. |
| authorization | Evaluating whether an authenticated actor may perform an action on a resource in context. | It is not the same as network reachability. |
| admission | The connection or protocol gate that decides whether a request may enter a lane or session. | A successful admission does not imply every later request is allowed. |
| policy | The declared rules and evaluation model that produce authorization decisions. | A certificate or grant cannot bypass ordinary policy. |
| audit | Structured evidence of lifecycle, authorization, and operational decisions, with integrity rules where configured. | It is not content recording and does not prove facts outside the gateway path. |
| recording | Optional captured session or request content governed by a configured level and retention policy. | It is more sensitive than a decision row. |
| trust material | CA roots, transport anchors, certificates, keys, fingerprints, and related verification inputs. | It is not a user permission. |
| workload identity | The identity used by a non-human process, independently of a human login. | It is not automatically a delegation. |
| delegation | An owner-authorized, attenuated capability and lifecycle granted to a workload or agent. | It is not a broad copy of a human credential. |
Credential families¶
| Family | Use |
|---|---|
| OIDC identity | An identity-provider token used to establish a human or workload identity through the configured OIDC flow. |
| transport certificate | An enrolled node certificate used for service-to-service transport and renewal. |
| access certificate | A short-lived certificate or equivalent access artifact issued for a user-facing resource path. |
| workload certificate | A certificate representing a non-human workload identity and its lifecycle. |
| delegation certificate | An agent-bound workload identity associated with a delegated grant. The certificate alone is not the capability. |
| onward credential | A credential minted for a downstream connection, such as a Connector-originated backend identity. |
| web credential | A browser or clientless access credential, cookie, or session artifact used only in the web path. |
| IdP MFA | Multi-factor authentication performed by the configured identity provider. |
| gateway step-up | A gateway-controlled factor or challenge required by an access policy after initial authentication. |
| factor-specific step-up | A step-up requirement tied to a particular factor or assurance condition. |
| one-time grant | A grant intended to be consumed once, with its own expiry and replay behavior. |
| JIT approval | Just-in-time approval that creates or enables a narrowly scoped access request after an approver decision. |
Abbreviations¶
| Abbreviation | First-use expansion |
|---|---|
| mTLS | mutual Transport Layer Security |
| OIDC | OpenID Connect |
| JIT | just in time |
| SPIFFE | Secure Production Identity Framework for Everyone |
| TOFU | trust on first use |
| SSRF | server-side request forgery |
| SNI | Server Name Indication |
| MCP | Model Context Protocol |
| OTLP | OpenTelemetry Protocol |
| Cedar | the policy language and authorization engine used by gdsgate |