mirror of
https://github.com/anthropics/claude-code.git
synced 2026-08-11 01:03:33 +00:00
Reference artifacts accompanying the Gateway-on-AWS walkthrough (https://code.claude.com/docs/en/claude-apps-gateway-on-aws), sibling to the existing examples/gateway/gcp assets: - setup.sh: scripts the walkthrough end to end via the aws CLI (security groups, IAM roles, ECR image build/push with gateway.yaml baked in, private-subnet RDS for PostgreSQL, Secrets Manager secrets, ECS Fargate service behind an internal ALB) - Dockerfile: distroless runtime image for the gateway; the Claude Code release binary is verified against an operator-supplied sha256 - gateway.yaml.example: config template (Bedrock upstream, Okta IdP) - terraform/: module provisioning the same architecture (ECS track) Provided as a working example to adapt, not a supported production deployment.
175 lines
10 KiB
Plaintext
175 lines
10 KiB
Plaintext
# gateway.yaml.example — Claude apps gateway config template, AWS-shaped (walkthrough §4).
|
|
#
|
|
# Okta IdP + Bedrock upstream, following the walkthrough at
|
|
# https://code.claude.com/docs/en/claude-apps-gateway-on-aws. The active sections
|
|
# below are a strict subset of the full configuration reference at
|
|
# https://code.claude.com/docs/en/claude-apps-gateway-config; optional keys are
|
|
# included commented-out.
|
|
#
|
|
# USAGE — this is the shippable TEMPLATE. Copy it to gateway.yaml and fill it in:
|
|
# cp gateway.yaml.example gateway.yaml
|
|
# setup.sh and terraform/ read gateway.yaml (your filled-in copy, which is
|
|
# gitignored). Unlike the GCP example it is NOT published to a secret store:
|
|
# the Dockerfile bakes it into the image at /etc/claude/gateway.yaml — the
|
|
# container ENTRYPOINT runs `claude gateway --config /etc/claude/gateway.yaml`.
|
|
# It holds no secret values; a config edit means an image rebuild (setup.sh
|
|
# tags images with a hash of this file, so a re-run rebuilds automatically).
|
|
#
|
|
# Secret expansion: ${ENV_VAR} reads an env var; ${file:/path} reads a mounted file.
|
|
# On ECS, the task definition injects the JWT / OIDC / Postgres secrets as ENV
|
|
# VARS via its `secrets` field (valueFrom -> Secrets Manager ARN). On EKS you
|
|
# may mount them as files instead and use ${file:/secrets/...}.
|
|
#
|
|
# BEFORE BUILD — replace every REPLACE_ME placeholder below (setup.sh refuses to
|
|
# build the image while any remain — the config is baked in, so a half-filled
|
|
# config would ship), and create the referenced secrets:
|
|
# gateway-jwt-secret (setup.sh generates this)
|
|
# gateway-oidc-client-secret (from the Okta admin console OIDC web app)
|
|
# gateway-postgres-url (setup.sh generates this)
|
|
|
|
# ── Listener ─────────────────────────────────────────────────────────────────
|
|
listen:
|
|
host: 0.0.0.0
|
|
port: 8080 # the target group forwards ALB :443 -> :8080
|
|
# Required. Fixes the IdP redirect_uri, the OIDC discovery doc, and the
|
|
# gateway-token issuer so none are derived from the client-controlled Host
|
|
# header (X-Forwarded-Host/-Proto are likewise never trusted). Set it to the
|
|
# internal hostname you picked in the prerequisites — the Route 53 private
|
|
# zone name your ACM certificate covers (e.g.
|
|
# https://claude-gateway.internal.example.com). Unlike Cloud Run there is no
|
|
# first-deploy placeholder dance: you choose the hostname up front, alias it
|
|
# to the internal ALB after the deploy, and register the same host's
|
|
# /oauth/callback on the Okta app.
|
|
public_url: REPLACE_ME
|
|
# Register this exact redirect URI on the Okta OIDC web application:
|
|
# https://<public_url host>/oauth/callback
|
|
#
|
|
# Behind the internal ALB every request arrives via the load balancer, so the
|
|
# gateway sees ALB-node peer IPs for all developers — set trusted_proxies so
|
|
# X-Forwarded-For from those proxies is trusted and per-IP rate limiting /
|
|
# audit IPs record the real client. ALB nodes take addresses from the subnets
|
|
# the ALB is attached to, so list those subnets' CIDRs (the private subnets
|
|
# from the prerequisites).
|
|
#
|
|
# NOTE: listing the ALB subnets' CIDRs trusts every host in those subnets as a
|
|
# proxy — any co-located workload that can reach the ALB can then spoof the
|
|
# client IP via X-Forwarded-For (audit logs, per-IP rate limits, IP
|
|
# allowlists). Keep the ALB :443 ingress source (CORP_CIDR / corporate_cidr)
|
|
# from overlapping these subnets, and don't share the subnets with untrusted
|
|
# workloads.
|
|
trusted_proxies: [REPLACE_ME] # e.g. [10.0.1.0/24, 10.0.2.0/24]
|
|
#
|
|
# Alternative — terminate TLS in the gateway itself instead of at the ALB:
|
|
# tls:
|
|
# cert: /certs/gateway.crt
|
|
# key: /certs/gateway.key
|
|
|
|
# ── Identity provider — Okta ─────────────────────────────────────────────────
|
|
oidc:
|
|
issuer: REPLACE_ME # e.g. https://example.okta.com (or your custom auth server URL)
|
|
client_id: REPLACE_ME # Okta OIDC web app client ID (not secret)
|
|
client_secret: ${OIDC_CLIENT_SECRET} # EKS file mounts: ${file:/secrets/oidc-client-secret}
|
|
allowed_email_domains: [REPLACE_ME] # e.g. [example.com] — reject id_tokens outside your org
|
|
# The Okta org authorization server returns a thin id_token that omits email
|
|
# and groups; the gateway fills them from /userinfo.
|
|
userinfo_fallback: true
|
|
# offline_access yields refresh tokens (silent renewal + the deprovision
|
|
# leash); Okta emits groups only when the `groups` scope is requested AND the
|
|
# app's groups claim filter allows them (Okta admin console -> the app's
|
|
# Sign On tab -> OpenID Connect ID Token -> Groups claim filter).
|
|
scopes: [openid, profile, email, offline_access, groups]
|
|
# groups_claim: groups # Okta default. Entra app roles=roles; see the config reference
|
|
# ca_cert_pem: ${file:/secrets/idp-ca.pem} # only for an IdP behind a private CA
|
|
|
|
# ── Sessions ─────────────────────────────────────────────────────────────────
|
|
session:
|
|
jwt_secret: ${GATEWAY_JWT_SECRET} # >= 32 bytes; openssl rand -base64 32
|
|
# Okta issues refresh tokens (offline_access above), so sessions renew
|
|
# silently and this mainly bounds deprovision latency. 8 is a sane default;
|
|
# lower toward 1 for tighter revocation. Array form rotates keys:
|
|
# [new, old] (index 0 signs, all verify).
|
|
ttl_hours: 8
|
|
|
|
# ── Store (REQUIRED — the gateway refuses to boot without it) ─────────────────
|
|
store:
|
|
postgres_url: ${GATEWAY_POSTGRES_URL} # private-subnet RDS; built with ?sslmode=verify-full by setup.sh
|
|
# (the image trusts the RDS CA bundle via NODE_EXTRA_CA_CERTS — see Dockerfile)
|
|
|
|
# ── Upstreams — Amazon Bedrock ───────────────────────────────────────────────
|
|
upstreams:
|
|
- provider: bedrock
|
|
# Must equal the region you provision in (setup.sh's AWS_REGION /
|
|
# terraform's region): the IAM policy's inference-profile ARNs are scoped
|
|
# to that region, and Bedrock model access is enabled there (cross-region
|
|
# us.anthropic.* profiles need access in every spanned region). NOTE: the
|
|
# walkthrough is scoped to US regions — the built-in model catalog maps to
|
|
# us.anthropic.* (US-geo) profiles; a non-US region also needs a models:
|
|
# list below (see the model catalog section).
|
|
region: REPLACE_ME # e.g. us-east-1
|
|
auth: {} # AWS default credential chain: ECS task role / IRSA on EKS (preferred — no static keys)
|
|
# base_url: https://bedrock-runtime.us-east-1.amazonaws.com # bedrock-runtime interface VPC endpoint, to keep model traffic off the public path
|
|
# Add more upstreams for failover (tried top→bottom on 5xx/timeout/501): a
|
|
# second region, or an anthropic/vertex fallback. See
|
|
# https://code.claude.com/docs/en/claude-apps-gateway.
|
|
|
|
# ── Telemetry fan-out (OPTIONAL) ─────────────────────────────────────────────
|
|
# The CLI sends OTLP/HTTP to the gateway; the gateway fans out, stamping
|
|
# user.id/user.email/user.groups server-side. On AWS, point at an OpenTelemetry
|
|
# Collector (e.g. the AWS Distro for OpenTelemetry -> CloudWatch / Managed
|
|
# Prometheus). When forward_to and public_url are both configured the gateway
|
|
# pushes CLAUDE_CODE_ENABLE_TELEMETRY and the OTEL exporter selectors to every
|
|
# client automatically — no per-developer config needed.
|
|
# telemetry:
|
|
# forward_to:
|
|
# - url: https://otel-collector.internal.example.com:4318
|
|
# headers:
|
|
# Authorization: ${file:/secrets/otlp-token}
|
|
# metrics: true # safe aggregate counters (default)
|
|
# logs: false # carries bash commands / tool inputs — opt in deliberately
|
|
# traces: false
|
|
|
|
# ── RBAC + managed settings (OPTIONAL; first-match-wins, top -> bottom) ───────
|
|
# With Okta as IdP, match on the group names the `groups` scope emits (subject
|
|
# to the app's groups claim filter), or on email_domain.
|
|
# managed:
|
|
# policies:
|
|
# - match: { groups: [engineering] }
|
|
# cli:
|
|
# availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]
|
|
# permissions: { deny: ["Read(./.env)", "Read(./secrets/**)"] }
|
|
# - match: {} # catch-all floor — keep LAST
|
|
# cli:
|
|
# availableModels: [claude-sonnet-4-6, claude-haiku-4-5]
|
|
|
|
# ── Admin API (OPTIONAL — enables db-mode runtime config + spend caps) ───────
|
|
# admin_groups needs a groups claim — Okta provides one via the `groups` scope
|
|
# above — or use the bootstrap keys below instead. Named keys for attribution
|
|
# in the audit log; 32-char minimum on key values. On ECS add these to the task
|
|
# definition's `secrets` field (valueFrom -> a Secrets Manager ARN), same as the
|
|
# JWT/OIDC/Postgres secrets above; on EKS you may use ${file:...}.
|
|
# admin:
|
|
# write_keys:
|
|
# - id: terraform
|
|
# key: ${GATEWAY_ADMIN_WRITE_KEY}
|
|
# read_keys:
|
|
# - id: reporting
|
|
# key: ${GATEWAY_ADMIN_READ_KEY}
|
|
# # admin_groups: [platform-finops] # Okta group names via the groups scope
|
|
|
|
# ── Model catalog (OPTIONAL for US regions) ──────────────────────────────────
|
|
# Default true: every built-in Claude model is exposed and auto-translated per
|
|
# upstream (the built-in table already maps to us.anthropic.* cross-region
|
|
# inference profiles). Set false + a models: list to pin IDs (e.g. an
|
|
# application or provisioned-throughput inference-profile ARN).
|
|
# NON-US REGIONS: the built-in us.anthropic.* mappings do not exist outside
|
|
# the US geo — set auto_include_builtin_models: false and list your region's
|
|
# inference profiles (eu.anthropic.*, apac.anthropic.*, ...) here, and widen
|
|
# the geo prefix in the deploy's bedrock-invoke IAM policy to match. See the
|
|
# models: guidance in the config reference:
|
|
# https://code.claude.com/docs/en/claude-apps-gateway-config
|
|
# auto_include_builtin_models: true
|
|
# models:
|
|
# - id: claude-opus-4-8
|
|
# label: Claude Opus 4.8
|
|
# upstream_model: { bedrock: us.anthropic.claude-opus-4-8 }
|