Integrations¶
Recipes for connecting external systems to gdsgate. Each recipe gives the
configuration, the command, the expected result, and the common failures.
Examples use the resources lab-read (allowed) and lab-denied (denied).
Record the gdsgate version (gdsgate --version) beside each external client
version.
For human identity providers, start with the complete OIDC guide: public-client registration, profile updates, device polling, claim mapping and diagnostics. The recipe below retains its provider-specific test context.
OIDC identity provider (Keycloak)¶
Authority fetches the provider's discovery document and JWKS, verifies issuer, audience, expiry and an asymmetric signing algorithm, and maps sanitized groups to Cedar. HMAC-signed tokens are refused.
Tested with Keycloak 26.0. After a provider upgrade, check discovery, login, group mapping and signing-key rotation again.
Configure gdsgate¶
On the Authority host, take the HTTPS issuer and client ID from the provider owner. Distribute the same complete profile to existing Proxies and clients with the profile update procedure:
Expected result: Authority starts and refreshes discovery and JWKS. A device or browser login succeeds and the principal appears in audit.
| Symptom | Cause | What to do |
|---|---|---|
| Login refused | issuer mismatch, unknown key ID, unsupported algorithm, wrong audience, or expired token | Correct the provider metadata and log in again. |
Configure the Keycloak client¶
In the identity provider:
- Register a public client with Authorization Code + PKCE and Device Authorization.
- Allow only the loopback address as the PKCE redirect.
- Keep the audience equal to the configured client ID.
- Do not enable the password grant.
Expected result: gdsgate login completes the device flow; gdsgate login
--browser completes PKCE. A redirect, audience or issuer mismatch fails the
login; fix the client registration and retry.
Signing-key rotation:
- Publish the new key in JWKS before signing with it.
- Keep the old key in JWKS until tokens signed with it expire.
- Wait for Authority's JWKS refresh, log in, and check the new key is used.
- Remove the old key.
A token with an unknown key ID is refused.
Emit a groups claim¶
By default, the provider emits an identity-token groups array whose names
match the policy. claims.groups.path can select another top-level claim or a
JSON pointer, with an explicit array, string or delimited format. See the
complete payload-to-policy example.
An absent optional claim gives no group memberships; a required absent claim or malformed present value fails mapping. Reserved values are refused by Authority, even if CLI login completed. They are not silently removed to allow the remaining groups. Correct the provider's claim or the mapping and sign in again. Group extraction itself does not grant administrative permissions.
Map groups to access with Cedar¶
On the admin host:
permit (principal in Group::"lab-readers",
action == Action::"view",
resource == Database::"lab-read");
permit (principal in Group::"lab-readers",
action == Action::"connect",
resource == Database::"lab-read");
Expected result: members of lab-readers see and connect to lab-read; they
are denied lab-denied, and users outside the group are denied both. See
Policy.
Headless tokens (CI, scripts)¶
Pass an identity token from the CI platform in an environment variable, set only for the process that needs it:
An expired token or an unaccepted issuer claim is denied before any backend connection. Get a new token. Unset the variable when the job ends. See workload identity.
Native clients per protocol¶
The gdsgate client and the native tool run on the same host. Keep generated configuration fragments readable only by their owner.
ssh (OpenSSH)¶
Generate one fragment and include it before matching host entries:
SSH_FRAGMENT="${SSH_FRAGMENT:-./gdsgate-ssh-fragment.conf}"
SSH_CONFIG="${SSH_CONFIG:-$SSH_FRAGMENT}"
gdsgate --config "$CLIENT_CONFIG" ssh config show '*.gds' > "$SSH_FRAGMENT"
ssh -F "$SSH_CONFIG" user@RESOURCE_NAME
If your SSH configuration includes the fragment, set SSH_CONFIG to that file.
Expected result: an SSH session; audit records the connect decision and the session close.
| Symptom | Cause | What to do |
|---|---|---|
ssh reports a changed host key |
host key mismatch | Verify the host key. |
| A port forward fails | forward not permitted | Request a narrower forward or a policy change. |
| The connection is denied | Cedar connect denial |
Review the policy. |
See User guide.
psql / mysql¶
On the client host:
gdsgate --config "$CLIENT_CONFIG" db proxy lab-read --listen 127.0.0.1:5433 &
DB_PID=$!
trap 'kill "$DB_PID" 2>/dev/null || true' EXIT
psql -h 127.0.0.1 -p 5433 -c 'select count(*) from lab_table;'
Expected result: the query result; audit records connect and, with
per-statement policy, dbQuery. lab-denied fails before the backend
connection. A statement denied by dbQuery returns an error. The backend kind
cockroach is catalogued as postgres.
kubectl¶
kubectl 1.30 or newer. On the client host:
KUBECONFIG="${KUBECONFIG:-./gdsgate-kubeconfig.yaml}"
gdsgate --config "$CLIENT_CONFIG" kube login KUBE_RESOURCE > "$KUBECONFIG"
gdsgate --config "$CLIENT_CONFIG" kube proxy KUBE_RESOURCE --listen 127.0.0.1:6443 &
KUBE_PID=$!
trap 'kill "$KUBE_PID" 2>/dev/null || true' EXIT
kubectl --kubeconfig "$KUBECONFIG" get --raw=/version
Expected result: each call gets a short-lived exec credential and reaches the
API. kubeRequest is evaluated per request and can match verb, resource kind,
namespace, name, subresource and marker. A denied request returns 403 before it
reaches the cluster. Port-forward is refused unless allow_port_forward is
enabled; when enabled, it is audited as an opaque byte stream.
redis-cli and other TCP clients¶
A TCP resource is an opaque byte relay; gdsgate does not parse the protocol.
gdsgate --config "$CLIENT_CONFIG" tcp proxy RESOURCE_NAME --listen 127.0.0.1:6390 &
TCP_PID=$!
trap 'kill "$TCP_PID" 2>/dev/null || true' EXIT
redis-cli -p 6390 ping
Expected result: the native response; audit records connect and byte counts.
A denied connect fails before the backend connection.
MCP clients¶
allowed_tools is a static list. With enforce_tool_policy = true, Cedar also
decides each tool, resource and prompt:
[[connector.backends]]
resource = "tools-dev"
kind = "mcp"
allowed_tools = ["search"]
enforce_tool_policy = true
On the client host, start the proxy and, for a stdio-only client, the bridge:
gdsgate --config "$CLIENT_CONFIG" mcp proxy tools-dev --listen 127.0.0.1:8765 &
MCP_PID=$!
trap 'kill "$MCP_PID" 2>/dev/null || true' EXIT
gdsgate mcp bridge 127.0.0.1:8765
Expected result: tools/list is filtered by viewTools; an allowed search
call returns a JSON-RPC result and an MCP audit row. An unlisted or denied tool,
resource or prompt fails before the backend call.
CI pipelines¶
The job runs the gdsgate client and the native tool with a short-lived token from the CI issuer:
export GDSGATE_ID_TOKEN="$CI_ID_TOKEN"
gdsgate --config "$CLIENT_CONFIG" db proxy lab-read --listen 127.0.0.1:5433 &
DB_PID=$!
trap 'kill "$DB_PID" 2>/dev/null || true' EXIT
psql -h 127.0.0.1 -p 5433 -c 'select count(*) from lab_table;'
Expected result: the query result and the job principal in audit. An expired token or a policy denial fails the job. Use CI groups separate from human groups. See workload identity.
Workload identity (CI and services)¶
The workload host generates its keypair locally; the private key does not leave
the host. Provision-token workloads require the Authority's control keyring,
which holds the revocation key:
When an identity is built from project_id and sha, the issuer must provide
project_id as a number and sha as hexadecimal. Do not put project or ref
names containing / or . segments into a workload ID.
Bootstrap on the workload host with the platform token or a one-time provision token:
Expected result: a short-lived workload certificate and a local keypair;
gdsgate doctor passes and the workload principal appears in audit. Bootstrap
is refused on a missing revocation key, an invalid claim value, a reused
provision token, or a revoked workload. Get a new issuer token or a new
provision token from an administrator.
Renewal without a token works only for provision-token workloads whose certificate is still valid:
An expired or revoked certificate cannot be renewed; bootstrap again. See Policy workload rules.
Delegated agents (autonomous access)¶
See the AI agent guide. Check the agent profile's client version before launch:
gdsgate --config "$CLIENT_CONFIG" delegations create \
--can mcp:search@tools-dev \
--can llm:call@models-dev \
--ttl 900 --sandbox strict --model gateway \
--agent-profile PROFILE_NAME \
--exec ./agent-wrapper -- --single-search-and-single-model-call
Expected result: one search, one model call, and revocation when the wrapper
exits. Audit records delegation.create, sandbox.claim, the two operations,
and a close event. An unlisted tool or model is denied; direct model mode is
refused under a sandbox. With --exec, cleanup is automatic. A bind-key bundle
must be revoked and deleted by its owner.
Observability and SIEM¶
| Stream | Source | Check | Common failure |
|---|---|---|---|
| stderr logs | process decisions and degradation | operation ID and reason | missing log destination |
| metrics | counters and gauges | metrics endpoint or scrape | role not listening |
| health/readiness | role-specific listener state | health and ready response | Connector ready before tunnel |
| OTLP traces | optional spans, configured exporter | trace ID joins request | exporter unavailable |
| audit JSONL | append-only local/export stream | hash-chain verification | missing anchor or tamper |
| SIEM shipper | external transport and retention | delivered event with ID | transport/replay failure |
Run local checks on the role host. Keep the metrics and health listener inside its network zone. A ready Connector may still have no Authority tunnel. Verify the audit chain from a separate workstation whose configuration opens the audit store and names the anchor key:
Expected result: a chain and anchor summary. The command fails on a missing anchor or a broken chain.
Backend kinds¶
The Connector has ten backend kinds:
| Backend kind | Catalog/resource behavior | Gate boundary |
|---|---|---|
| ssh | SshHost | connect, forwarding, optional onward certificate |
| tcp | TcpService | connect and opaque bytes |
| postgres | Database | connect and optional dbQuery |
| mysql | Database | connect and optional dbQuery |
| cockroach | advertised as postgres | PostgreSQL wire/session-role path |
| mcp | McpServer and subresources | connect, viewTools, tool/resource/prompt actions |
| kubernetes | KubernetesCluster | connect and per-request kubeRequest |
| llm | LlmService and LlmModel | per-call llmCall, no connect session |
| web | WebApp | connect and per-request httpRequest |
| web-egress | WebEgress | policy-controlled forward/CONNECT lane |
The catalog has nine resource kinds: cockroach is catalogued as postgres.
The MCP allowed_tools list applies with or without Cedar per-tool policy.
Compatibility record. Record these versions for each integration:
| Integration | Tested with | Record |
|---|---|---|
| Keycloak | 26.0 | provider build; OIDC Discovery 1.0, PKCE, device flow, JWKS rotation |
| OpenSSH client | ssh -V |
|
| PostgreSQL | PostgreSQL 16 | psql --version, TLS mode |
| MySQL client | mysql --version, authentication plugin, TLS mode |
|
| Kubernetes client | kubectl 1.30+ | kubectl version |
| Redis | Redis 7 | redis-cli --version |
| MCP client | protocol revision and transport | |
| CI issuer | claim schema, including project_id and sha |
|
| Telemetry exporter | OTLP protocol and endpoint |