SpyBara
Go Premium

claude-apps-gateway-on-aws.md 2026-10-06 23:59 UTC to 2026-10-07 19:00 UTC

This page contains 74 additions and 72 deletions.

2026
Wed 7 20:01

Distribuire il gateway delle app Claude su AWS

Un esempio pratico di esecuzione del gateway delle app Claude su AWS: ECS Fargate o EKS, Amazon RDS per PostgreSQL, AWS Secrets Manager e autenticazione basata su ruoli IAM ad Amazon Bedrock.

Questo esempio esegue il provisioning del gateway delle app Claude su AWS con Amazon Bedrock come upstream del modello, utilizzando Amazon ECS su AWS Fargate o Amazon EKS per il calcolo. Okta è il provider di identità (IdP) di esempio, ma qualsiasi IdP conforme a OpenID Connect (OIDC) funziona; consultate Configurazione del provider di identità per i dettagli specifici di ogni IdP.

Architettura

Diagramma del gateway delle app Claude su AWS: i client Claude Code si connettono tramite HTTPS a un Application Load Balancer interno che sta davanti al gateway (ECS Fargate o EKS), che viene eseguito in subnet private insieme a un'istanza Amazon RDS per PostgreSQL per lo stato della sessione. Il gateway accede gli utenti tramite OIDC rispetto all'IdP aziendale, legge i segreti da AWS Secrets Manager, inoltra le richieste del modello ad Amazon Bedrock utilizzando il suo ruolo IAM e estrae la sua immagine da Amazon ECR al momento della distribuzione.

Il gateway viene eseguito come endpoint HTTPS privato sulla vostra rete a cui gli sviluppatori accedono tramite il vostro IdP. Le loro sessioni Claude Code raggiungono i modelli Claude su Amazon Bedrock attraverso il ruolo IAM del gateway, quindi nessuna credenziale del modello finisce sulle macchine degli sviluppatori. La configurazione di riferimento esegue il provisioning di:

  • Servizio Amazon ECS su AWS Fargate o Amazon EKS Deployment che esegue il contenitore del gateway
  • Repository Amazon ECR per l'immagine del gateway
  • Istanza Amazon RDS per PostgreSQL in subnet private, non accessibile pubblicamente, per lo store del gateway
  • Segreti AWS Secrets Manager per la chiave di firma JWT, il segreto del client OIDC e l'URL di Postgres
  • Ruolo IAM con bedrock:InvokeModel, bedrock:InvokeModelWithResponseStream e bedrock:CountTokens, allegato come ruolo di attività ECS o associato tramite IAM Roles for Service Accounts (IRSA) su EKS
  • Application Load Balancer interno per HTTPS

Prerequisiti

La procedura dettagliata crea le risorse proprie del gateway, ma si basa su infrastrutture di rete e identità che già possedete. Prima di iniziare, avete bisogno di:

Impostare le variabili di ambiente

Ogni comando in questa pagina legge quattro valori dalla vostra shell: AWS_REGION, ACCOUNT_ID, VPC_ID e PRIVATE_SUBNETS.

Scegliete una regione US dove Bedrock serve i modelli Claude di cui avete bisogno. La procedura dettagliata si basa sul catalogo dei modelli integrato del gateway, che si risolve in profili di inferenza us.anthropic.*, e la politica IAM concede quegli ARN. In una regione non-US, aggiungete un blocco models: con gli ID del profilo di inferenza di quella geo e cambiate il prefisso ARN della politica IAM per corrispondere.

Se non avete l'ID VPC a portata di mano, elencate i vostri VPC con aws ec2 describe-vpcs, quindi elencate le subnet di quel VPC per trovare due private in diverse zone di disponibilità:

aws ec2 describe-subnets --filters "Name=vpc-id,Values=<your-vpc-id>" \
  --query 'Subnets[].{ID:SubnetId,AZ:AvailabilityZone,CIDR:CidrBlock}' --output table

Esportate tutti e quattro prima di continuare:

export AWS_REGION=us-east-1   # una regione US dove Bedrock serve i modelli Claude di cui avete bisogno
export ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export VPC_ID=<your-vpc-id>
export PRIVATE_SUBNETS="<subnet-id-a> <subnet-id-b>"

Eseguire il deploy del gateway

I passaggi seguenti eseguono il provisioning del deploy completo con comandi aws.

1

Creare i gruppi di sicurezza

Tre gruppi di sicurezza concatenano il percorso del traffico: la tua rete aziendale raggiunge il load balancer sulla porta 443, il load balancer raggiunge il gateway sulla porta 8080 e il gateway raggiunge Postgres sulla porta 5432. Nient'altro è raggiungibile. Il modo in cui li colleghi dipende dal percorso di calcolo:

  • Su ECS Fargate, il passaggio di deploy collega $ALB_SG al load balancer e $GW_SG al servizio.
  • Su EKS, AWS Load Balancer Controller crea il proprio gruppo di sicurezza frontend per l'ALB, quindi $ALB_SG e $GW_SG non vengono utilizzati: l'annotazione inbound-cidrs del passaggio di deploy limita il listener alla tua rete aziendale e il gruppo di sicurezza del database ammette invece il gruppo di sicurezza del cluster.
ALB_SG="$(aws ec2 create-security-group --group-name claude-gateway-alb \
--description "Claude gateway ALB" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
GW_SG="$(aws ec2 create-security-group --group-name claude-gateway-svc \
--description "Claude gateway service" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
DB_SG="$(aws ec2 create-security-group --group-name claude-gateway-db \
--description "Claude gateway Postgres" --vpc-id "$VPC_ID" \
--query GroupId --output text)"

aws ec2 authorize-security-group-ingress --group-id "$ALB_SG" \
--protocol tcp --port 443 --cidr <your-corporate-cidr>
aws ec2 authorize-security-group-ingress --group-id "$GW_SG" \
--protocol tcp --port 8080 --source-group "$ALB_SG"
aws ec2 authorize-security-group-ingress --group-id "$DB_SG" \
--protocol tcp --port 5432 --source-group "$GW_SG"
2

Creare i ruoli IAM e inviare il modulo del caso d'uso

Il gateway viene eseguito con un ruolo di attività dedicato il cui unico permesso è invocare i modelli Claude su Bedrock. Secondo il riferimento upstream Bedrock, la policy deve coprire sia gli ARN dei profili di inferenza cross-region sia gli ARN dei modelli di base sottostanti:

cat > bedrock-invoke.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream", "bedrock:CountTokens"],
"Resource": [
"arn:aws:bedrock:${AWS_REGION}:${ACCOUNT_ID}:inference-profile/us.anthropic.*",
"arn:aws:bedrock:*::foundation-model/anthropic.*"
]
}]
}
EOF
cat > ecs-trust.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF

aws iam create-role --role-name claude-gateway-task \
--assume-role-policy-document file://ecs-trust.json
aws iam put-role-policy --role-name claude-gateway-task \
--policy-name bedrock-invoke --policy-document file://bedrock-invoke.json

ECS ha anche bisogno di un ruolo di esecuzione, che l'agente ECS stesso utilizza per scaricare l'immagine da ECR e iniettare i valori di Secrets Manager creati in seguito. È separato dal ruolo di attività che l'AWS SDK del gateway utilizza in fase di esecuzione:

aws iam create-role --role-name claude-gateway-execution \
--assume-role-policy-document file://ecs-trust.json
aws iam attach-role-policy --role-name claude-gateway-execution \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
cat > secrets-read.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
"Resource": [
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-jwt-secret-??????",
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-oidc-client-secret-??????",
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-postgres-url-??????"
]
}]
}
EOF
aws iam put-role-policy --role-name claude-gateway-execution \
--policy-name read-gateway-secrets --policy-document file://secrets-read.json

La policy specifica un ARN per ogni segreto anziché un semplice wildcard gateway-*, che in un account condiviso corrisponderebbe anche a segreti non correlati; il suffisso finale -?????? corrisponde esattamente al suffisso casuale di sei caratteri che Secrets Manager aggiunge all'ARN di ogni segreto. Un -* finale sarebbe un semplice glob di prefisso e corrisponderebbe anche a nomi più lunghi come gateway-postgres-url-prod.

La policy IAM concede al gateway il permesso di chiamare Bedrock, e Bedrock abilita l'accesso ai modelli per impostazione predefinita nelle regioni commerciali. Il vincolo rimanente a livello di account è il modulo del caso d'uso una tantum di Anthropic: se nessuno nel tuo account lo ha inviato, apri la console Amazon Bedrock, seleziona un modello Anthropic dal catalogo dei modelli e compila il modulo. L'accesso viene concesso immediatamente dopo l'invio; consulta Claude Code su Amazon Bedrock per il modulo AWS Organizations e i permessi IAM di cui ha bisogno chi lo invia.

Il percorso EKS riutilizza entrambi i documenti di policy su un ruolo IRSA al posto dei due ruoli ECS; consulta il passaggio di deploy.

3

Eseguire il provisioning di Amazon RDS per PostgreSQL

L'istanza esegue Postgres 16 nelle subnet private, senza indirizzo pubblico e con la crittografia dell'archiviazione attivata.

Per prima cosa, crea il gruppo di subnet che colloca il database nelle subnet private e un gruppo di parametri con rds.force_ssl=1 in modo che il server rifiuti le connessioni in testo semplice. La versione del motore viene fissata una sola volta perché la famiglia del gruppo di parametri deve corrispondere alla versione principale del motore eseguita dall'istanza:

aws rds create-db-subnet-group --db-subnet-group-name claude-gateway-db \
--db-subnet-group-description "Claude gateway" --subnet-ids $PRIVATE_SUBNETS

PG_VERSION=16
PG_FAMILY="postgres${PG_VERSION}"
aws rds create-db-parameter-group --db-parameter-group-name claude-gateway-db \
--db-parameter-group-family "$PG_FAMILY" \
--description "Claude gateway - require TLS on every connection"
aws rds modify-db-parameter-group --db-parameter-group-name claude-gateway-db \
--parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=immediate"

Quindi crea l'istanza con una password principale generata:

PGPASS="$(openssl rand -hex 24)"
aws rds create-db-instance --db-instance-identifier claude-gateway-db \
--engine postgres --engine-version "$PG_VERSION" \
--db-instance-class db.t4g.micro \
--allocated-storage 20 --db-name claude_gateway \
--master-username gateway --master-user-password "$PGPASS" \
--db-subnet-group-name claude-gateway-db \
--db-parameter-group-name claude-gateway-db \
--vpc-security-group-ids "$DB_SG" \
--no-publicly-accessible --storage-encrypted

L'argomento letterale --master-user-password è visibile nella tabella dei processi e nei log di audit/EDR mentre il comando è in esecuzione, la stessa esposizione trattata nella nota del passaggio dei segreti. Su un host condiviso o monitorato, passa invece la password tramite --cli-input-json da un file 0600, come fa il setup.sh del bundle.

Attendi che l'istanza sia disponibile, il che può richiedere diversi minuti, quindi leggi il suo endpoint privato e componi la stringa di connessione che il gateway utilizzerà:

aws rds wait db-instance-available --db-instance-identifier claude-gateway-db
DB_HOST="$(aws rds describe-db-instances --db-instance-identifier claude-gateway-db \
--query 'DBInstances[0].Endpoint.Address' --output text)"
GATEWAY_POSTGRES_URL="postgres://gateway:${PGPASS}@${DB_HOST}:5432/claude_gateway?sslmode=verify-full"

sslmode=verify-full fa sì che il gateway verifichi la catena e il nome host del certificato del server RDS, e non si limiti a crittografare. L'ancora di fiducia è il bundle di certificati AWS RDS, che il passaggio di build dell'immagine più avanti copia in /etc/claude/rds-global-bundle.pem e considera attendibile tramite NODE_EXTRA_CA_CERTS. Non aggiungere all'URL un parametro sslrootcert= in stile libpq: il driver del gateway legge solo sslmode dalla stringa di query e inoltrerebbe sslrootcert a Postgres come parametro di avvio, che il server rifiuta.

Il servizio ECS o i pod EKS devono essere eseguiti in questo VPC per poter raggiungere l'endpoint privato dell'istanza, e il gruppo di sicurezza claude-gateway-db ammette solo il gruppo di sicurezza del gateway.

4

Scrivere gateway.yaml

Il blocco upstreams punta a Bedrock con auth: {}, quindi il gateway si autentica tramite la catena di credenziali predefinita di AWS, dal ruolo di attività su ECS o dal ruolo IRSA su EKS. Consulta il riferimento di configurazione per ogni campo.

Due campi listen descrivono ciò che sta davanti al gateway:

  • public_url: l'origine esterna https://, obbligatoria per qualsiasi bind non di loopback; consulta il riferimento listen. Il gateway costruisce il redirect_uri dell'IdP e il proprio documento di discovery solo da questo valore, mai dalle intestazioni X-Forwarded-*.
  • trusted_proxies: gli intervalli di origine del front end. Il gateway considera X-Forwarded-For solo quando il peer TCP è in questo elenco, quindi percorre la catena oltre gli hop attendibili, in modo che i rate limit di accesso per IP e gli eventi di audit registrino gli IP degli sviluppatori anziché quelli del load balancer.

Su entrambi i percorsi il front end è un ALB interno, creato direttamente o da AWS Load Balancer Controller, e i nodi di un ALB prendono gli indirizzi dalle subnet a cui è collegato, quindi imposta trusted_proxies sui CIDR di quelle subnet. In questo modo ogni host in quelle subnet viene considerato un proxy attendibile. Evita che l'origine di ingresso dell'ALB, il tuo CIDR aziendale, si sovrapponga a esse, e non condividere le subnet con carichi di lavoro non attendibili che potrebbero falsificare gli IP dei client tramite X-Forwarded-For.

L'attributo di conservazione della porta del client dell'ALB, routing.http.xff_client_port.enabled, può restare su entrambe le impostazioni: se è attivo, l'ALB scrive il client come 203.0.113.7:54321 o [2001:db8::1]:54321, e il gateway legge entrambi i formati scartando la porta.

listen:
host: 0.0.0.0
port: 8080
public_url: https://claude-gateway.internal.example.com
trusted_proxies: [<your-alb-subnet-cidrs>]

oidc:
issuer: https://example.okta.com
client_id: 0oa1example2
client_secret: ${OIDC_CLIENT_SECRET}           # EKS: ${file:/secrets/oidc-client-secret}
allowed_email_domains: [example.com]
# Il server di autorizzazione dell'organizzazione Okta restituisce un id_token ridotto che omette
# email e gruppi; il gateway li ricava da /userinfo.
userinfo_fallback: true
# Okta emette i gruppi solo quando viene richiesto lo scope `groups` e il
# filtro del claim dei gruppi dell'app li consente.
scopes: [openid, profile, email, offline_access, groups]

session:
jwt_secret: ${GATEWAY_JWT_SECRET}              # EKS: ${file:/secrets/jwt-secret}
ttl_hours: 8 # limita la latenza di deprovisioning; abbassa
# verso 1 per una revoca più rapida

store:
postgres_url: ${GATEWAY_POSTGRES_URL}          # EKS: ${file:/secrets/postgres-url}
# readiness_grace_seconds: 300                 # continua a superare il controllo di stato
# durante un failover RDS

upstreams:
- provider: bedrock
region: <your-region>                        # uguale a $AWS_REGION affinché gli ARN della
# policy IAM la coprano
auth: {} # catena di credenziali predefinita di AWS:
# ruolo di attività ECS, o IRSA su EKS
5

Archiviare i segreti in AWS Secrets Manager

Crea tre segreti; il ruolo di esecuzione del passaggio IAM può già leggerli:

aws secretsmanager create-secret --name gateway-jwt-secret \
--secret-string "$(openssl rand -base64 32)"
aws secretsmanager create-secret --name gateway-oidc-client-secret \
--secret-string '<your-okta-client-secret>'
aws secretsmanager create-secret --name gateway-postgres-url \
--secret-string "$GATEWAY_POSTGRES_URL"

Annota l'ARN stampato da ogni chiamata; la definizione di attività ECS fa riferimento ai segreti tramite ARN.

A differenza dei segreti, gateway.yaml non contiene valori segreti, perché ogni credenziale viene risolta all'avvio tramite l'espansione ${VAR} o ${file:...}. Il modo in cui tutto arriva al container varia in base al percorso:

  • Su ECS, la build del passaggio successivo copia gateway.yaml nell'immagine in /etc/claude/gateway.yaml, e la definizione di attività inietta i tre segreti come variabili d'ambiente tramite il suo campo secrets, quindi lo YAML fa riferimento a ${GATEWAY_JWT_SECRET}, ${OIDC_CLIENT_SECRET} e ${GATEWAY_POSTGRES_URL}.
  • Su EKS, monta gateway.yaml da una ConfigMap e i segreti come file in /secrets, referenziati come ${file:/secrets/...}. Ricava i Kubernetes Secrets da Secrets Manager con External Secrets Operator o con il provider AWS del driver CSI Secrets Store, oppure creali direttamente con kubectl.
6

Eseguire la build e il push dell'immagine su Amazon ECR

Esegui la build dell'immagine secondo i requisiti dell'immagine del container, posizionando il binario glibc linux-x64 in ./claude nel contesto di build. Scrivi il tuo Dockerfile secondo questi requisiti oppure parti dal Dockerfile del bundle, che copia il gateway.yaml compilato nei passaggi precedenti nell'immagine in /etc/claude/gateway.yaml. Su ECS è questa copia incorporata a portare la configurazione nel container, ed è per questo che la build viene dopo la scrittura del file. Il percorso EKS monta invece gateway.yaml da una ConfigMap al momento del deploy, quindi lì la copia incorporata non viene utilizzata.

L'immagine contiene anche il bundle di certificati AWS RDS come ancora di fiducia per il sslmode=verify-full della stringa di connessione, quindi scaricalo prima nel contesto di build. AWS aggiorna periodicamente il bundle (vengono aggiunte nuove CA regionali), quindi scaricalo a ogni build anziché fissarne un checksum o eseguirne il commit:

curl -fL --proto '=https' -o rds-global-bundle.pem \
https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem

I requisiti dell'immagine del container non coprono il bundle, quindi se scrivi il tuo Dockerfile, aggiungi le due righe che lo copiano e lo rendono attendibile; il Dockerfile del bundle le include già entrambe:

COPY rds-global-bundle.pem /etc/claude/rds-global-bundle.pem
ENV NODE_EXTRA_CA_CERTS=/etc/claude/rds-global-bundle.pem

Crea il repository ECR ed esegui l'accesso di Docker a esso. I tag immutabili fanno sì che il tag <version> fissato nel passaggio di deploy non possa essere in seguito reindirizzato silenziosamente a un'immagine diversa:

aws ecr create-repository --repository-name claude-gateway \
--image-tag-mutability IMMUTABLE \
--image-scanning-configuration scanOnPush=true
aws ecr get-login-password --region "$AWS_REGION" \
| docker login --username AWS --password-stdin \
"${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"

Esegui la build e il push dell'immagine. La definizione di attività più avanti esegue linux/amd64, quindi la piattaforma deve corrispondere qui; per Fargate su ARM64 (Graviton), esegui invece la build per linux/arm64 con il binario linux-arm64 e imposta cpuArchitecture su ARM64:

docker build --platform=linux/amd64 \
-t "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/claude-gateway:<version>" .
docker push "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/claude-gateway:<version>"
7

Eseguire il deploy

Crea il cluster e un gruppo di log per lo stderr del gateway, che contiene sia gli eventi di audit sia i log operativi. La conservazione richiede una chiamata separata, e senza di essa CloudWatch conserva i log per sempre; allinea i 90 giorni alla tua policy di conservazione dell'audit:

aws ecs create-cluster --cluster-name claude-gateway
aws logs create-log-group --log-group-name /ecs/claude-gateway
aws logs put-retention-policy --log-group-name /ecs/claude-gateway \
--retention-in-days 90

Scrivi la definizione di attività. Il ruolo di attività ha il permesso per Bedrock e il ruolo di esecuzione inietta i segreti; usa gli ARN dei segreti del passaggio Secrets Manager:

{
"family": "claude-gateway",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "1024",
"memory": "2048",
"runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" },
"executionRoleArn": "arn:aws:iam::<account-id>:role/claude-gateway-execution",
"taskRoleArn": "arn:aws:iam::<account-id>:role/claude-gateway-task",
"containerDefinitions": [
{
"name": "gateway",
"image": "<account-id>.dkr.ecr.<region>.amazonaws.com/claude-gateway:<version>",
"portMappings": [{ "containerPort": 8080 }],
"secrets": [
{ "name": "GATEWAY_JWT_SECRET",   "valueFrom": "<gateway-jwt-secret ARN>" },
{ "name": "OIDC_CLIENT_SECRET",   "valueFrom": "<gateway-oidc-client-secret ARN>" },
{ "name": "GATEWAY_POSTGRES_URL", "valueFrom": "<gateway-postgres-url ARN>" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/claude-gateway",
"awslogs-region": "<region>",
"awslogs-stream-prefix": "gateway"
}
}
}
]
}

Registrala:

aws ecs register-task-definition --cli-input-json file://claude-gateway-task.json

Metti davanti un ALB interno con un gruppo di destinazione che verifica lo stato del gateway. --ip-address-type ipv4 è importante: un ALB interno dual-stack pubblica record AAAA di intervallo pubblico, che il controllo della rete privata di /login rifiuta:

ALB_ARN="$(aws elbv2 create-load-balancer --name claude-gateway \
--scheme internal --type application --ip-address-type ipv4 \
--subnets $PRIVATE_SUBNETS --security-groups "$ALB_SG" \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)"

TG_ARN="$(aws elbv2 create-target-group --name claude-gateway \
--protocol HTTP --port 8080 --vpc-id "$VPC_ID" --target-type ip \
--health-check-path /readyz \
--query 'TargetGroups[0].TargetGroupArn' --output text)"

Aggiungi il listener HTTPS. --ssl-policy fissa un livello minimo di TLS moderno, poiché ometterlo fa ricadere sulla policy predefinita legacy ELBSecurityPolicy-2016-08, che accetta ancora TLS 1.0/1.1.

Per impostazione predefinita, l'ALB chiude una connessione dopo 60 secondi senza dati. I ping di keepalive del gateway mantengono i flussi entro questo valore predefinito, quindi aumentare il timeout aggiunge margine rispetto alla cadenza dei ping; la riga di Risoluzione dei problemi sui flussi interrotti descrive il meccanismo e i gateway meno recenti. I comandi seguenti aggiungono il listener e aumentano il timeout:

aws elbv2 create-listener --load-balancer-arn "$ALB_ARN" \
--protocol HTTPS --port 443 \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \
--certificates CertificateArn=<your-acm-certificate-arn> \
--default-actions Type=forward,TargetGroupArn="$TG_ARN"

aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$ALB_ARN" \
--attributes Key=idle_timeout.timeout_seconds,Value=3600

Crea il servizio. Il circuit breaker del deploy riporta all'ultimo stato stabile un deploy le cui attività continuano a fallire, a causa di un'immagine difettosa o di una configurazione che non si avvia, anziché rilanciare all'infinito attività che falliscono:

aws ecs create-service --cluster claude-gateway --service-name claude-gateway \
--task-definition claude-gateway --desired-count 1 --launch-type FARGATE \
--deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true}" \
--health-check-grace-period-seconds 60 \
--network-configuration "awsvpcConfiguration={subnets=[$(echo $PRIVATE_SUBNETS | tr ' ' ',')],securityGroups=[$GW_SG],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=$TG_ARN,containerName=gateway,containerPort=8080"

Il periodo di tolleranza di 60 secondi dà a un'attività avviata a freddo il tempo di scaricare l'immagine, connettersi allo store e rispondere al primo controllo di stato prima che ECS inizi a contare i fallimenti a carico del deploy.

Il controllo di stato del gruppo di destinazione su GET /readyz verifica che lo store sia raggiungibile, quindi un'attività che non riesce a raggiungere Postgres non entra mai in rotazione. Per far sì che le attività continuino a superare il controllo durante una breve interruzione del database, come un failover RDS, imposta store.readiness_grace_seconds come descritto in Comportamento in caso di interruzione, che tratta anche l'alternativa /healthz.

Le attività vengono eseguite in subnet private senza IP pubblico, quindi tutto il traffico in uscita (verso Bedrock, il tuo IdP, Secrets Manager, ECR e CloudWatch Logs) passa attraverso il gateway NAT. Per tenere il traffico Bedrock fuori dal percorso pubblico, crea un endpoint VPC di interfaccia bedrock-runtime e punta il base_url dell'upstream a esso, come mostrato nel riferimento upstream Bedrock; l'IdP ha comunque bisogno di uscita verso Internet.

Per finire, fornisci agli sviluppatori un nome host risolvibile privatamente: in una zona ospitata privata di Route 53, crea un alias dal nome DNS interno del gateway all'ALB e imposta listen.public_url su quel nome host. Il nome *.elb.amazonaws.com dell'ALB si risolve in indirizzi privati su un ALB interno, ma non può usare il tuo certificato ACM, quindi usa un nome tuo.

Aggiorna l'URI di reindirizzamento autorizzato del client OAuth a <public_url>/oauth/callback prima del primo accesso. Dopo aver modificato public_url, esegui di nuovo la build e il push dell'immagine con un nuovo tag, registra una nuova revisione della definizione di attività ed esegui di nuovo il deploy. Su ECS l'impostazione si trova nel gateway.yaml incorporato nell'immagine, e il gateway costruisce la propria origine pubblica solo da quell'impostazione, ignorando X-Forwarded-Host e X-Forwarded-Proto. X-Forwarded-For viene considerato per gli IP dei client solo quando listen.trusted_proxies è impostato.

8

Distribuire l'URL del gateway ai computer degli sviluppatori

Il gateway è ora in esecuzione, ma gli sviluppatori non possono raggiungerlo da /login finché l'URL del gateway non è presente sui loro computer. Imposta forceLoginMethod e forceLoginGatewayUrl nel file delle impostazioni gestite che distribuisci su ogni dispositivo tramite MDM. Nel selettore di accesso non esiste un'opzione gateway che uno sviluppatore possa selezionare manualmente.

Riferimento Terraform

Il bundle complementare a examples/gateway/aws pacchetti questa pagina come codice:

  • setup.sh script la procedura dettagliata di provisioning sopra con gli stessi comandi aws, sul percorso ECS Fargate. È idempotente: le risorse esistenti vengono rilevate e saltate, quindi rieseguirlo è sicuro, e qualsiasi default può essere sovrascritto tramite variabile di ambiente. Voi create comunque il segreto del client OIDC Okta e il certificato ACM voi stessi: un'esecuzione senza di essi salta la distribuzione ECS/ALB, nomina gli input mancanti e stampa il comando create-secret; create entrambi e rieseguite. Il modulo del caso d'uso Bedrock e l'alias Route 53 vengono stampati come passaggi successivi piuttosto che eseguiti automaticamente, e il push MDM del client rimane un passaggio manuale da questa pagina.
  • gateway.yaml.example è il modello di configurazione dal passaggio gateway.yaml, con le chiavi opzionali incluse commentate. Copiatelo in gateway.yaml e sostituite ogni REPLACE_ME prima di compilare.
  • Dockerfile compila l'immagine di runtime dal binario precompilato linux-x64 e copia il vostro gateway.yaml compilato a /etc/claude/gateway.yaml, più il bundle di certificati AWS RDS che ancora la connessione sslmode=verify-full dello store. setup.sh scarica il bundle solo quando non è già nel contesto di compilazione; eliminate il file e ricompilate sotto un nuovo tag per raccogliere una rotazione CA di AWS. Il file di configurazione non contiene valori segreti, poiché ogni credenziale si risolve all'avvio tramite l'espansione ${VAR}. Una modifica della configurazione quindi significa una ricompilazione sotto un nuovo tag; setup.sh automatizza questo taggando le immagini con un hash del file.
  • terraform/ esegue il provisioning dello stesso ambito ECS Fargate in modo dichiarativo: i gruppi di sicurezza, i ruoli IAM, il repository ECR, l'istanza RDS, i segreti di Secrets Manager e il servizio ECS dietro l'ALB interno. Il VPC e le subnet private rimangono prerequisiti, passati come variabili. Terraform crea il repository ECR ma non compila l'immagine, e la definizione del servizio fa riferimento all'immagine, quindi l'apply è due passaggi: un apply mirato per il repository, quindi la compilazione e il push, quindi l'apply completo. Il terraform/README.md del bundle copre le variabili, lo stato remoto e lo smantellamento.

Come questa pagina, il bundle è un esempio funzionante per infrastrutture gestite dal cliente piuttosto che una distribuzione di produzione supportata; esaminate e adattate al vostro ambiente prima di affidarvi ad esso.

Troubleshooting

Per gli errori di avvio del gateway e di accesso, consultate la tabella di troubleshooting indipendente dalla piattaforma. Le voci sottostanti sono specifiche di AWS.

Sintomo Causa Correzione
CLI /login: Gateway hosts must be on your organization's private network; <host> resolves to the public (or unrecognized) address <ip> Il nome del gateway si risolve in almeno un indirizzo pubblico. Un ALB interno dual-stack pubblica record AAAA di intervallo pubblico, e il controllo della rete privata richiede che ogni indirizzo risolto sia privato Create l'ALB con --ip-address-type ipv4, o servite un nome DNS interno separato solo con nessun record AAAA pubblico
Ogni richiesta Bedrock restituisce 502; il log mostra Could not load credentials from any providers L'attività viene eseguita sul tipo di lancio ECS EC2 senza un ruolo di attività, o il pod viene eseguito su un nodo EKS senza IRSA, quindi le credenziali provengono dai metadati dell'istanza, che il limite di hop predefinito di IMDSv2 di 1 ferma dentro un contenitore. Nessuno dei due percorsi in questa pagina è interessato: i ruoli di attività Fargate e IRSA non utilizzano i metadati dell'istanza Preferite i ruoli di attività e IRSA. Dove le credenziali dell'istanza sono inevitabili, aumentate il limite di hop con aws ec2 modify-instance-metadata-options --instance-id <id> --http-put-response-hop-limit 2; la tabella indipendente dalla piattaforma copre i compromessi
Le richieste Bedrock restituiscono 403 AccessDeniedException L'account non ha inviato il modulo del caso d'uso una tantum di Anthropic, l'iscrizione automatica AWS Marketplace che inizia al primo invoke dell'account non ha ancora finito, o la politica del ruolo di attività manca gli ARN del profilo di inferenza o del modello di base Inviate il modulo del caso d'uso dalla catalogo dei modelli della console Bedrock; se è stato appena inviato o questo è il primo invoke dell'account, riprovate dopo alcuni minuti. Concedete bedrock:InvokeModel e bedrock:InvokeModelWithResponseStream su entrambe le famiglie di ARN.
Bedrock restituisce una ValidationException dicendo che la velocità effettiva on-demand non è supportata Una voce models: personalizzata mappa a un ID del modello di base semplice che la regione serve solo tramite profili di inferenza Mappate il modello all'ID del profilo di inferenza cross-region (us.anthropic.*) al posto; il catalogo integrato lo fa già
L'attività ECS si ferma con ResourceInitializationError prima che il gateway registri qualcosa Il ruolo di esecuzione non può leggere i segreti di Secrets Manager, o le subnet private non hanno percorso verso Secrets Manager o ECR Concedete secretsmanager:GetSecretValue sui tre ARN dei segreti gateway- al ruolo di esecuzione, e fornite uscita tramite il gateway NAT, o, senza uno, endpoint dell'interfaccia per Secrets Manager, ECR e CloudWatch Logs, che il driver awslogs ha bisogno nella stessa fase, più un endpoint del gateway S3
L'avvio del gateway esce con un errore di timeout della connessione Postgres Il gruppo di sicurezza del database non ammette il gruppo di sicurezza del gateway sulla porta 5432, o il servizio viene eseguito al di fuori del VPC del database Consentite 5432 dal gruppo di sicurezza del gateway su quello del database, ed eseguite il servizio nello stesso VPC del gruppo di subnet del DB
L'avvio del gateway esce con un errore di verifica del certificato TLS di Postgres La stringa di connessione imposta sslmode=verify-full ma l'immagine non affida il bundle CA di RDS: il bundle non è stato copiato nell'immagine, o NODE_EXTRA_CA_CERTS non punta ad esso Aggiungete le due righe del Dockerfile del passaggio di compilazione che copiano il bundle e impostano NODE_EXTRA_CA_CERTS, quindi ricompilate, spingete sotto un nuovo tag e ridistribuite
Le risposte di streaming si interrompono a metà flusso dopo un periodo tranquillo Un gateway più vecchio di v2.1.229 su un upstream Bedrock o Claude Platform su AWS non invia nulla mentre l'upstream è tranquillo, ad esempio durante il pensiero esteso senza output trasmesso. L'ALB chiude una connessione dopo 60 secondi senza dati per impostazione predefinita, quindi taglia il flusso a quel gap. I gateway v2.1.229 e successivi mantengono un flusso tranquillo sotto quel timeout: su quegli upstream il gateway emette un evento SSE ping una volta che circa 15 secondi passano senza dati di flusso, e su un upstream API Anthropic rilancia i ping propri dell'API Aggiornate il gateway a v2.1.229 o successivo, o impostate l'attributo idle_timeout.timeout_seconds su 3600, tramite modify-load-balancer-attributes o l'annotazione load-balancer-attributes Ingress su EKS

Telemetry

Il gateway fornisce metriche di utilizzo per sviluppatore senza alcuna configurazione OTEL per macchina. Claude Code emette metriche, log e tracce OpenTelemetry (OTLP) opzionali; Monitoring usage copre tutto ciò che il CLI segnala. Nelle sessioni accedute tramite /login, il CLI contrassegna ogni esportazione con gli attributi di identità IdP autenticati user.id, user.email e user.groups, in modo che l'utilizzo si accumuli per sviluppatore.

Il gateway stesso è un relay OTLP autenticato. Impostare telemetry.forward_to insieme a listen.public_url, e spingerà le impostazioni dell'esportatore OTEL a ogni client connesso e inoltrerà il loro traffico OTLP verbatim a ogni destinazione che elencate. Ogni destinazione acconsente a metriche, log e tracce indipendentemente, e l'impostazione predefinita è solo metriche; vedere il riferimento telemetry per i campi per segnale e i loro compromessi di sensibilità. Il gateway non memorizza nel buffer, non aggrega e non archivia telemetria, quindi il luogo in cui i dati arrivano dipende interamente dalla configurazione dell'esportatore del collector.

La telemetria del client è disattivata per impostazione predefinita; configurare telemetry.forward_to è ciò che la attiva per gli sviluppatori connessi, e ogni client interattivo mostra una finestra di dialogo di approvazione della sicurezza per le impostazioni inviate, come descritto nel riferimento di configurazione. Su AWS, ogni segnale viene mappato a una destinazione come segue.

Client metrics, logs, and traces

Puntare telemetry.forward_to a un collector OpenTelemetry, come il AWS Distro for OpenTelemetry (ADOT) collector, ed esportare da lì ad Amazon CloudWatch, Amazon Managed Service for Prometheus, o qualsiasi backend OTLP.

Eseguire il collector come servizio interno proprio raggiungibile su https://; il riferimento telemetry copre l'eccezione di loopback e CLAUDE_GATEWAY_ALLOW_LOOPBACK.

Gateway logs

Su ECS Fargate, nessuna configurazione aggiuntiva: il driver awslogs consegna stderr del gateway, che contiene i suoi eventi di audit e log operativi, al gruppo di log /ecs/claude-gateway creato sopra. Su EKS, i log dei pod non raggiungono CloudWatch per impostazione predefinita, quindi l'audit trail viene perso fino a quando non installate la raccolta dei log: il componente aggiuntivo Amazon CloudWatch Observability con acquisizione dei log dei container abilitata, o un DaemonSet Fluent Bit. Su entrambi i percorsi, interrogare i log con CloudWatch Logs Insights e guidare gli allarmi dai filtri metrici.

Container metrics

Abilitare Container Insights sul cluster con aws ecs update-cluster-settings --cluster claude-gateway --settings name=containerInsights,value=enabled per CPU, memoria e rete per attività. Su EKS, installare il componente aggiuntivo Amazon CloudWatch Observability.

Spend

La telemetria mostra l'utilizzo dopo il fatto; i limiti di spesa sono la vista e l'applicazione live per sviluppatore del gateway.

Attribuzione dei costi

Il gateway firma ogni richiesta Bedrock con il suo principale, il ruolo dell'attività ECS o il ruolo IRSA di EKS, quindi per impostazione predefinita AWS vede tutta quella spesa sotto un unico principale IAM. Ci sono due modi per dividerla nei dati di fatturazione di AWS, e si combinano.

Per sviluppatore con `assume_role`

Creare un secondo ruolo IAM che contiene le autorizzazioni Bedrock e si fida del principale del gateway, concedere a quel principale sts:AssumeRole su di esso, e impostare assume_role con session_name: email sull'upstream Bedrock. Il gateway assume quindi quel ruolo una volta per sviluppatore all'ora con il nome della sessione impostato sulla loro email e firma le loro richieste con il risultato. Richiede un gateway che esegue Claude Code v2.1.281 o successivo. Il ruolo può trovarsi anche in un altro account AWS: vedere Bedrock in un altro account AWS. In Terraform, accanto al ruolo dell'attività nel bundle Terraform:

resource "aws_iam_role" "bedrock_user" {
  name = "claude-gateway-bedrock-user"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{ Effect = "Allow", Action = "sts:AssumeRole", Principal = { AWS = aws_iam_role.task.arn } }]
  })
}
resource "aws_iam_role_policy" "bedrock_user_invoke" {   # same Bedrock policy as the task role's
  role   = aws_iam_role.bedrock_user.id
  policy = aws_iam_role_policy.bedrock_invoke.policy
}
resource "aws_iam_role_policy" "task_assume_bedrock_user" {
  role   = aws_iam_role.task.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{ Effect = "Allow", Action = "sts:AssumeRole", Resource = aws_iam_role.bedrock_user.arn }]
  })
}

Con assume_role impostato il gateway firma ogni chiamata Bedrock, la chiamata gratuita CountTokens per la misurazione della spesa inclusa, con le credenziali del ruolo assunto, quindi il principale del gateway ha bisogno di una politica Bedrock propria solo per un upstream senza assume_role.

Poiché il gateway chiama STS al momento della richiesta, le subnet private hanno bisogno di un percorso verso sts.<region>.amazonaws.com. Il gateway NAT dai prerequisiti fornisce uno, così come un endpoint VPC dell'interfaccia STS che risponde per quel nome host. Ogni sviluppatore attivo costa una chiamata STS all'ora per replica del gateway.

Le richieste di ogni sviluppatore raggiungono AWS come il principale arn:aws:sts::<account>:assumed-role/<role>/<email>. Per visualizzare la spesa per principale, utilizzare un'esportazione di fatturazione che includa i dati del principale IAM; la pagina allocazione dei costi del principale IAM di AWS spiega come abilitarla e quali strumenti di fatturazione la mostrano.

Per team con profili di inferenza dell'applicazione

Questo percorso utilizza solo le sezioni models e managed. Creare un profilo di inferenza dell'applicazione Bedrock per team e modello, taggare ogni profilo con il team, e attivare quel tag come tag di allocazione dei costi. Quindi fornire a ogni team il suo proprio id modello in gateway.yaml e fissare ogni gruppo IdP agli id del suo team:

models:
  - id: platform-claude-opus-4-8
    upstream_model:
      bedrock: arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123
  - id: data-claude-opus-4-8
    upstream_model:
      bedrock: arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/def456
managed:
  policies:
    - match: {groups: [team-platform]}
      cli: {availableModels: [platform-claude-opus-4-8], enforceAvailableModels: true}
    - match: {groups: [team-data]}
      cli: {availableModels: [data-claude-opus-4-8], enforceAvailableModels: true}
    - match: {}
      cli: {availableModels: [claude-opus-4-8, claude-sonnet-4-6], enforceAvailableModels: true}

Dire agli sviluppatori in un team fissato di avviare Claude Code con --model platform-claude-opus-4-8, utilizzando l'id del loro team, perché una sessione avviata senza di esso esegue il modello predefinito, che il gateway rifiuta per loro.

Il gateway applica availableModels su ogni richiesta, non solo nel selezionatore di modelli, e la fatturazione di AWS raggruppa la spesa per il tag che hai attivato. Senza il catch-all match: {}, uno sviluppatore che non corrisponde a nessuna politica ottiene ogni modello nel catalogo e può fatturare il profilo di entrambi i team.

I costi: la configurazione cresce con i team per i modelli, e il ruolo che firma le richieste Bedrock di questo upstream deve anche essere autorizzato a invocare gli ARN application-inference-profile/*. Quel ruolo è il principale del gateway, o con assume_role il ruolo che assume. Vedere pricing per come il misuratore di spesa proprio del gateway prezza questi id.

Passaggi successivi