Skip to main content
Version: 0.9.12

AWS security setup

platform v0.9.11verified 2026-05-14

What an AWS-backed Delphi deployment needs in terms of IAM, parameter-store layout, secrets-manager structure, ECR access, and instance-profile patterns.

Parameter and secret layout

All variables live under ${NAMESPACE} (e.g. voiceai/staging, voiceai/prod). Within that:

SSM Parameter Store
/${NAMESPACE}/<service>/<VAR> String, non-sensitive
/${NAMESPACE}/media/MEDIA_TLS_FULLCHAIN_B64 etc.

Secrets Manager
${NAMESPACE}/<service>/secrets JSON object with the sensitive keys
${NAMESPACE}/media/secrets shares MEDIA_TLS_PRIVKEY_B64 + MEDIA_UPLOAD_TOKEN

common/fetch-env.sh on each host reads the per-service vars.yaml, fetches the keys, validates required-ness, and emits export VAR=... lines that get evald into the shell. Values stay in process memory; nothing lands on disk.

IAM roles

Instance profile (per service host)

Each service host needs at minimum:

PermissionResource
ssm:GetParameter*, ssm:DescribeParametersarn:aws:ssm:<region>:<account>:parameter/${NAMESPACE}/<service>/*
secretsmanager:GetSecretValuearn:aws:secretsmanager:<region>:<account>:secret:${NAMESPACE}/<service>/*
ecr:GetAuthorizationToken*
ecr:BatchGetImage, ecr:GetDownloadUrlForLayerarn:aws:ecr:<region>:<account>:repository/voiceai-*
s3:GetObject on CONFIG_BUCKETarn:aws:s3:::${CONFIG_BUCKET}/*

Ops Tasker (SES on EC2)

When EMAIL_TRANSPORT=ses:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ses:SendEmail", "ses:SendRawEmail"],
"Resource": "arn:aws:ses:<region>:<account>:identity/<verified-domain-or-email>"
}
]
}

Delphi Platform role (TelWeb)

When customer-side AWS features are enabled (read-only metadata calls), TelWeb assumes a cross-account role via STS:

  • TelWeb env: DELPHI_PLATFORM_ROLE_ARN, DELPHI_EXTERNAL_ID.
  • Customer side: a role with the matching external ID, scoped to the metadata the platform reads.

This replaces the legacy password-based platform setup; never re-introduce shared-secret patterns.

ECR

  • Login — service init.sh runs aws ecr get-login-password | docker login using the instance-profile credentials.
  • Repositoriesvoiceai-telapi, voiceai-telweb, voiceai-telphi, voiceai-telsys, voiceai-telpro, voiceai-rtpengine, voiceai-webrtc, voiceai-audioproc, voiceai-scaler, voiceai-tasker, voiceai-media-server.
  • TagsECR_TAG is the release version (e.g. v0.9.11). Don't use latest in production deployments.

EC2 IMDS

When apps run with HTTP_PROXY / HTTPS_PROXY (every private-network service does):

  • Add 169.254.169.254 to NO_PROXY so IMDS calls aren't proxied and fail silently.

  • Set the IMDS hop limit to 2 so containers on the Docker bridge can reach IMDS:

    aws ec2 modify-instance-metadata-options \
    --instance-id i-xxxxxxxx \
    --http-put-response-hop-limit 2 \
    --http-tokens required

If both static keys and the instance profile are set, the AWS SDK default chain prefers static keys and the instance-profile intent is defeated. Unset static keys when the instance profile is the intended source.

See also