Skip to content

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.

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:

[oidc]
issuer = "OIDC_ISSUER_FROM_OWNER"
client_id = "CLIENT_ID_FROM_OWNER"

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:

  1. Publish the new key in JWKS before signing with it.
  2. Keep the old key in JWKS until tokens signed with it expire.
  3. Wait for Authority's JWKS refresh, log in, and check the new key is used.
  4. Remove the old key.

A token with an unknown key ID is refused.

Emit a groups claim

The provider emits a groups claim with plain group names (no path prefixes). Authority maps each value to a Cedar Group entity. A missing, duplicated or reserved group does not grant access; the user still logs in. To fix, change the provider's claim mapping and get a new token.

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:

export GDSGATE_ID_TOKEN="$ISSUER_TOKEN"
gdsgate --config "$CLIENT_CONFIG" ls

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 '*.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:

[security.integrity]
control_keyring = "/etc/gdsgate/control.keys"

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:

gdsgate --config "$WORKLOAD_CONFIG" machine-id --token "$PLATFORM_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:

gdsgate --config "$WORKLOAD_CONFIG" machine-id --renew

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" delegate \
  --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:

gdsgate --config "$AUDIT_VERIFY_CONFIG" authority verify-audit

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