spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / examples / gateway / aws / gateway.yaml.example
174 lines · 10.4 KBRaw
1# gateway.yaml.example — Claude apps gateway config template, AWS-shaped (walkthrough §4).
2#
3# Okta IdP + Bedrock upstream, following the walkthrough at
4# https://code.claude.com/docs/en/claude-apps-gateway-on-aws. The active sections
5# below are a strict subset of the full configuration reference at
6# https://code.claude.com/docs/en/claude-apps-gateway-config; optional keys are
7# included commented-out.
8#
9# USAGE — this is the shippable TEMPLATE. Copy it to gateway.yaml and fill it in:
10# cp gateway.yaml.example gateway.yaml
11# setup.sh and terraform/ read gateway.yaml (your filled-in copy, which is
12# gitignored). Unlike the GCP example it is NOT published to a secret store:
13# the Dockerfile bakes it into the image at /etc/claude/gateway.yaml — the
14# container ENTRYPOINT runs `claude gateway --config /etc/claude/gateway.yaml`.
15# It holds no secret values; a config edit means an image rebuild (setup.sh
16# tags images with a hash of this file, so a re-run rebuilds automatically).
17#
18# Secret expansion: ${ENV_VAR} reads an env var; ${file:/path} reads a mounted file.
19# On ECS, the task definition injects the JWT / OIDC / Postgres secrets as ENV
20# VARS via its `secrets` field (valueFrom -> Secrets Manager ARN). On EKS you
21# may mount them as files instead and use ${file:/secrets/...}.
22#
23# BEFORE BUILD — replace every REPLACE_ME placeholder below (setup.sh refuses to
24# build the image while any remain — the config is baked in, so a half-filled
25# config would ship), and create the referenced secrets:
26# gateway-jwt-secret (setup.sh generates this)
27# gateway-oidc-client-secret (from the Okta admin console OIDC web app)
28# gateway-postgres-url (setup.sh generates this)
29
30# ── Listener ─────────────────────────────────────────────────────────────────
31listen:
32 host: 0.0.0.0
33 port: 8080 # the target group forwards ALB :443 -> :8080
34 # Required. Fixes the IdP redirect_uri, the OIDC discovery doc, and the
35 # gateway-token issuer so none are derived from the client-controlled Host
36 # header (X-Forwarded-Host/-Proto are likewise never trusted). Set it to the
37 # internal hostname you picked in the prerequisites — the Route 53 private
38 # zone name your ACM certificate covers (e.g.
39 # https://claude-gateway.internal.example.com). Unlike Cloud Run there is no
40 # first-deploy placeholder dance: you choose the hostname up front, alias it
41 # to the internal ALB after the deploy, and register the same host's
42 # /oauth/callback on the Okta app.
43 public_url: REPLACE_ME
44 # Register this exact redirect URI on the Okta OIDC web application:
45 # https://<public_url host>/oauth/callback
46 #
47 # Behind the internal ALB every request arrives via the load balancer, so the
48 # gateway sees ALB-node peer IPs for all developers — set trusted_proxies so
49 # X-Forwarded-For from those proxies is trusted and per-IP rate limiting /
50 # audit IPs record the real client. ALB nodes take addresses from the subnets
51 # the ALB is attached to, so list those subnets' CIDRs (the private subnets
52 # from the prerequisites).
53 #
54 # NOTE: listing the ALB subnets' CIDRs trusts every host in those subnets as a
55 # proxy — any co-located workload that can reach the ALB can then spoof the
56 # client IP via X-Forwarded-For (audit logs, per-IP rate limits, IP
57 # allowlists). Keep the ALB :443 ingress source (CORP_CIDR / corporate_cidr)
58 # from overlapping these subnets, and don't share the subnets with untrusted
59 # workloads.
60 trusted_proxies: [REPLACE_ME] # e.g. [10.0.1.0/24, 10.0.2.0/24]
61 #
62 # Alternative — terminate TLS in the gateway itself instead of at the ALB:
63 # tls:
64 # cert: /certs/gateway.crt
65 # key: /certs/gateway.key
66
67# ── Identity provider — Okta ─────────────────────────────────────────────────
68oidc:
69 issuer: REPLACE_ME # e.g. https://example.okta.com (or your custom auth server URL)
70 client_id: REPLACE_ME # Okta OIDC web app client ID (not secret)
71 client_secret: ${OIDC_CLIENT_SECRET} # EKS file mounts: ${file:/secrets/oidc-client-secret}
72 allowed_email_domains: [REPLACE_ME] # e.g. [example.com] — reject id_tokens outside your org
73 # The Okta org authorization server returns a thin id_token that omits email
74 # and groups; the gateway fills them from /userinfo.
75 userinfo_fallback: true
76 # offline_access yields refresh tokens (silent renewal + the deprovision
77 # leash); Okta emits groups only when the `groups` scope is requested AND the
78 # app's groups claim filter allows them (Okta admin console -> the app's
79 # Sign On tab -> OpenID Connect ID Token -> Groups claim filter).
80 scopes: [openid, profile, email, offline_access, groups]
81 # groups_claim: groups # Okta default. Entra app roles=roles; see the config reference
82 # ca_cert_pem: ${file:/secrets/idp-ca.pem} # only for an IdP behind a private CA
83
84# ── Sessions ─────────────────────────────────────────────────────────────────
85session:
86 jwt_secret: ${GATEWAY_JWT_SECRET} # >= 32 bytes; openssl rand -base64 32
87 # Okta issues refresh tokens (offline_access above), so sessions renew
88 # silently and this mainly bounds deprovision latency. 8 is a sane default;
89 # lower toward 1 for tighter revocation. Array form rotates keys:
90 # [new, old] (index 0 signs, all verify).
91 ttl_hours: 8
92
93# ── Store (REQUIRED — the gateway refuses to boot without it) ─────────────────
94store:
95 postgres_url: ${GATEWAY_POSTGRES_URL} # private-subnet RDS; built with ?sslmode=verify-full by setup.sh
96 # (the image trusts the RDS CA bundle via NODE_EXTRA_CA_CERTS — see Dockerfile)
97
98# ── Upstreams — Amazon Bedrock ───────────────────────────────────────────────
99upstreams:
100 - provider: bedrock
101 # Must equal the region you provision in (setup.sh's AWS_REGION /
102 # terraform's region): the IAM policy's inference-profile ARNs are scoped
103 # to that region, and Bedrock model access is enabled there (cross-region
104 # us.anthropic.* profiles need access in every spanned region). NOTE: the
105 # walkthrough is scoped to US regions — the built-in model catalog maps to
106 # us.anthropic.* (US-geo) profiles; a non-US region also needs a models:
107 # list below (see the model catalog section).
108 region: REPLACE_ME # e.g. us-east-1
109 auth: {} # AWS default credential chain: ECS task role / IRSA on EKS (preferred — no static keys)
110 # base_url: https://bedrock-runtime.us-east-1.amazonaws.com # bedrock-runtime interface VPC endpoint, to keep model traffic off the public path
111 # Add more upstreams for failover (tried top→bottom on 5xx/timeout/501): a
112 # second region, or an anthropic/vertex fallback. See
113 # https://code.claude.com/docs/en/claude-apps-gateway.
114
115# ── Telemetry fan-out (OPTIONAL) ─────────────────────────────────────────────
116# The CLI sends OTLP/HTTP to the gateway; the gateway fans out, stamping
117# user.id/user.email/user.groups server-side. On AWS, point at an OpenTelemetry
118# Collector (e.g. the AWS Distro for OpenTelemetry -> CloudWatch / Managed
119# Prometheus). When forward_to and public_url are both configured the gateway
120# pushes CLAUDE_CODE_ENABLE_TELEMETRY and the OTEL exporter selectors to every
121# client automatically — no per-developer config needed.
122# telemetry:
123# forward_to:
124# - url: https://otel-collector.internal.example.com:4318
125# headers:
126# Authorization: ${file:/secrets/otlp-token}
127# metrics: true # safe aggregate counters (default)
128# logs: false # carries bash commands / tool inputs — opt in deliberately
129# traces: false
130
131# ── RBAC + managed settings (OPTIONAL; first-match-wins, top -> bottom) ───────
132# With Okta as IdP, match on the group names the `groups` scope emits (subject
133# to the app's groups claim filter), or on email_domain.
134# managed:
135# policies:
136# - match: { groups: [engineering] }
137# cli:
138# availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]
139# permissions: { deny: ["Read(./.env)", "Read(./secrets/**)"] }
140# - match: {} # catch-all floor — keep LAST
141# cli:
142# availableModels: [claude-sonnet-4-6, claude-haiku-4-5]
143
144# ── Admin API (OPTIONAL — enables db-mode runtime config + spend caps) ───────
145# admin_groups needs a groups claim — Okta provides one via the `groups` scope
146# above — or use the bootstrap keys below instead. Named keys for attribution
147# in the audit log; 32-char minimum on key values. On ECS add these to the task
148# definition's `secrets` field (valueFrom -> a Secrets Manager ARN), same as the
149# JWT/OIDC/Postgres secrets above; on EKS you may use ${file:...}.
150# admin:
151# write_keys:
152# - id: terraform
153# key: ${GATEWAY_ADMIN_WRITE_KEY}
154# read_keys:
155# - id: reporting
156# key: ${GATEWAY_ADMIN_READ_KEY}
157# # admin_groups: [platform-finops] # Okta group names via the groups scope
158
159# ── Model catalog (OPTIONAL for US regions) ──────────────────────────────────
160# Default true: every built-in Claude model is exposed and auto-translated per
161# upstream (the built-in table already maps to us.anthropic.* cross-region
162# inference profiles). Set false + a models: list to pin IDs (e.g. an
163# application or provisioned-throughput inference-profile ARN).
164# NON-US REGIONS: the built-in us.anthropic.* mappings do not exist outside
165# the US geo — set auto_include_builtin_models: false and list your region's
166# inference profiles (eu.anthropic.*, apac.anthropic.*, ...) here, and widen
167# the geo prefix in the deploy's bedrock-invoke IAM policy to match. See the
168# models: guidance in the config reference:
169# https://code.claude.com/docs/en/claude-apps-gateway-config
170# auto_include_builtin_models: true
171# models:
172# - id: claude-opus-4-8
173# label: Claude Opus 4.8
174# upstream_model: { bedrock: us.anthropic.claude-opus-4-8 }