Skip to content

Commit ddce6d0

Browse files
Garandorclaude
andcommitted
feat: add dstack-ingress for custom domain TLS (CPL-152) (#181)
* feat: add dstack-ingress for custom domain TLS (CPL-152) Add dstack-ingress service to docker-compose.phala.yml for attestation- bound TLS termination inside the TEE. On main branch, deploys with DOMAIN=api.chipotle.litprotocol.com using Let's Encrypt DNS-01 via Route 53. On next branch, dstack-ingress is removed from the deploy compose file (no custom domain). - docker-compose.phala.yml: add dstack-ingress service behind "custom-domain" profile with Route 53 DNS provider config - deploy-phala.yml: add domain output, substitute TLS secrets in deploy step, conditionally strip profiles or remove service block Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: use test.chipotle.litprotocol.com for next branch Reserve api.chipotle.litprotocol.com for main branch deployments. The next branch gets its own custom domain at test.chipotle.litprotocol.com. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename Route 53 secret refs to AWS_*_CERTBOT The GitHub secrets for the certbot Route 53 IAM credentials are named AWS_ACCESS_KEY_ID_CERTBOT and AWS_SECRET_ACCESS_KEY_CERTBOT. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove compose profiles, strip dstack-ingress when no domain Phala deploy doesn't support compose profiles, so dstack-ingress is now unconditionally defined. The deploy workflow strips the entire service block when DOMAIN is empty. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename secrets to CERTBOT_AWS_ACCESS_KEY_ID prefix convention Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename AWS credential placeholders to CERTBOT_ prefix Consistent naming end-to-end: compose file placeholders, workflow sed patterns, and GitHub secret references all use CERTBOT_AWS_ACCESS_KEY_ID and CERTBOT_AWS_SECRET_ACCESS_KEY. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: note dstack-ingress expected env var names with Phala docs link Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename DOMAIN placeholder to CERTBOT_DOMAIN for consistency All dstack-ingress secrets now use CERTBOT_ prefix in the compose file (CERTBOT_DOMAIN, CERTBOT_AWS_ACCESS_KEY_ID, CERTBOT_AWS_SECRET_ACCESS_KEY). The deploy workflow maps them to the values dstack-ingress expects internally. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace dead Phala docs link with working blog post URL Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: link to dstack-ingress DNS_PROVIDERS.md for Route 53 docs The previous link pointed to a Cloudflare-focused blog post. The actual Route 53 env var documentation lives in the Dstack-TEE examples repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: show optional AWS_ROLE_ARN/AWS_REGION for Route 53 role assumption Commented out since the IAM user has direct Route 53 permissions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add missing GATEWAY_DOMAIN for dstack-ingress Required common variable per dstack-ingress docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore underscore prefix in GATEWAY_DOMAIN GATEWAY_DOMAIN should be _.dstack-base-prod5.phala.network per dstack-ingress docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: CERTBOT_AWS_ACCESS_KEY_ID is a var, not a secret Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove optional domain logic — custom domain is mandatory Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: hardcode CERTBOT_EMAIL to admin@litprotocol.com Not sensitive — it's public in the ACME registration anyway. Remove the secret reference and sed substitution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: derive base_url and api_root_url from domain Set DOMAIN as a shell variable per branch, then derive all URL outputs from it — removes redundant hardcoded URLs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b22b478 commit ddce6d0

2 files changed

Lines changed: 44 additions & 4 deletions

File tree

.github/workflows/deploy-phala.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jobs:
4949
instance_type: ${{ steps.set.outputs.instance_type }}
5050
gcp_project_id: ${{ steps.set.outputs.gcp_project_id }}
5151
node_config: ${{ steps.set.outputs.node_config }}
52+
domain: ${{ steps.set.outputs.domain }}
5253
steps:
5354
- name: Set deployment target
5455
id: set
@@ -58,19 +59,20 @@ jobs:
5859
echo "instance_type=tdx.large" >> "$GITHUB_OUTPUT"
5960
echo "gcp_project_id=chipotle-dev" >> "$GITHUB_OUTPUT"
6061
echo "node_config=NodeConfig.main.toml" >> "$GITHUB_OUTPUT"
61-
BASE_URL="https://f8fce543471dc9f5f5643aa217422398c36e5edc-8000.dstack-base-prod5.phala.network"
62+
DOMAIN="api.chipotle.litprotocol.com"
6263
elif [ "${{ github.ref }}" = "refs/heads/next" ]; then
6364
echo "phala_app_name=chipotle-next" >> "$GITHUB_OUTPUT"
6465
echo "instance_type=tdx.small" >> "$GITHUB_OUTPUT"
6566
echo "gcp_project_id=chipotle-next" >> "$GITHUB_OUTPUT"
6667
echo "node_config=NodeConfig.next.toml" >> "$GITHUB_OUTPUT"
67-
BASE_URL="https://969a8c14c9e13420705b19c7246aeed27897e7ea-8000.dstack-base-prod5.phala.network"
68+
DOMAIN="test.chipotle.litprotocol.com"
6869
else
6970
echo "Unsupported branch for deployment"
7071
exit 1
7172
fi
72-
echo "base_url=$BASE_URL" >> "$GITHUB_OUTPUT"
73-
echo "api_root_url=${BASE_URL}/core/v1" >> "$GITHUB_OUTPUT"
73+
echo "domain=${DOMAIN}" >> "$GITHUB_OUTPUT"
74+
echo "base_url=https://${DOMAIN}" >> "$GITHUB_OUTPUT"
75+
echo "api_root_url=https://${DOMAIN}/core/v1" >> "$GITHUB_OUTPUT"
7476
7577
build:
7678
needs: [determine-target]
@@ -145,11 +147,15 @@ jobs:
145147
DIGEST_LIT_ACTIONS=$(cat digest-lit-actions.txt | tr -d '\n' | sed 's/}[}]*$//')
146148
DIGEST_LIT_API_SERVER=$(cat digest-lit-api-server.txt | tr -d '\n' | sed 's/}[}]*$//')
147149
DIGEST_OTEL_COLLECTOR=$(cat digest-otel-collector.txt | tr -d '\n' | sed 's/}[}]*$//')
150+
DOMAIN="${{ needs.determine-target.outputs.domain }}"
148151
sed \
149152
-e "s|\${DOCKER_IMAGE_LIT_ACTIONS}|${{ vars.DOCKER_IMAGE }}-lit-actions@${DIGEST_LIT_ACTIONS}|g" \
150153
-e "s|\${DOCKER_IMAGE_LIT_API_SERVER}|${{ vars.DOCKER_IMAGE }}-lit-api-server@${DIGEST_LIT_API_SERVER}|g" \
151154
-e "s|\${DOCKER_IMAGE_OTEL_COLLECTOR}|${{ vars.DOCKER_IMAGE }}-otel-collector@${DIGEST_OTEL_COLLECTOR}|g" \
152155
-e "s|\${GCP_PROJECT_ID}|${{ needs.determine-target.outputs.gcp_project_id }}|g" \
156+
-e "s|\${CERTBOT_DOMAIN}|${DOMAIN}|g" \
157+
-e "s|\${CERTBOT_AWS_ACCESS_KEY_ID}|${{ vars.CERTBOT_AWS_ACCESS_KEY_ID }}|g" \
158+
-e "s|\${CERTBOT_AWS_SECRET_ACCESS_KEY}|${{ secrets.CERTBOT_AWS_SECRET_ACCESS_KEY }}|g" \
153159
docker-compose.phala.yml > docker-compose.deploy.yml
154160
cat docker-compose.deploy.yml
155161

docker-compose.phala.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
# Required secrets (set as encrypted Phala CVM environment variables):
1414
# GCP_SERVICE_ACCOUNT_JSON - GCP service account key (raw JSON or base64-encoded)
1515
# GCP_PROJECT_ID - GCP project ID (e.g. "my-gcp-project")
16+
# CERTBOT_DOMAIN - Custom domain for TLS (e.g. "api.chipotle.litprotocol.com")
17+
# CERTBOT_AWS_ACCESS_KEY_ID - Route 53 IAM credentials for DNS-01 challenge
18+
# CERTBOT_AWS_SECRET_ACCESS_KEY - Route 53 IAM credentials for DNS-01 challenge
1619

1720
# RUST_LOG filter shared by lit-actions and lit-api-server.
1821
# App code stays at trace; per-module overrides suppress low-value internals:
@@ -92,5 +95,36 @@ services:
9295
- '/sys:/host/sys:ro'
9396
restart: unless-stopped
9497

98+
# dstack-ingress — attestation-bound TLS termination inside the TEE.
99+
# Obtains a Let's Encrypt cert for DOMAIN via DNS-01 (Route 53), then
100+
# reverse-proxies HTTPS :443 → lit-api-server :8000.
101+
dstack-ingress:
102+
image: dstacktee/dstack-ingress:1.4@sha256:11c0481ca2e3566f514a1c8a2cc69af1e0bb9dab2e4ea49b469c81ec8e7c5c72
103+
ports:
104+
- "443:443"
105+
environment:
106+
# dstack-ingress expects DOMAIN, AWS_ACCESS_KEY_ID, and AWS_SECRET_ACCESS_KEY internally.
107+
# Route 53 provider docs (env vars, optional role assumption):
108+
# https://github.com/Dstack-TEE/dstack-examples/blob/main/custom-domain/dstack-ingress/DNS_PROVIDERS.md
109+
DOMAIN: "${CERTBOT_DOMAIN}"
110+
GATEWAY_DOMAIN: "_.dstack-base-prod5.phala.network"
111+
DNS_PROVIDER: "route53"
112+
TARGET_ENDPOINT: "http://lit-api-server:8000"
113+
CERTBOT_EMAIL: "admin@litprotocol.com"
114+
SET_CAA: "true"
115+
AWS_ACCESS_KEY_ID: "${CERTBOT_AWS_ACCESS_KEY_ID}"
116+
AWS_SECRET_ACCESS_KEY: "${CERTBOT_AWS_SECRET_ACCESS_KEY}"
117+
# Optional: for STS role assumption instead of direct IAM keys
118+
# AWS_ROLE_ARN: "${CERTBOT_AWS_ROLE_ARN}"
119+
# AWS_REGION: "${CERTBOT_AWS_REGION}"
120+
volumes:
121+
- /var/run/dstack.sock:/var/run/dstack.sock
122+
- cert-data:/etc/letsencrypt
123+
depends_on:
124+
lit-api-server:
125+
condition: service_started
126+
restart: unless-stopped
127+
95128
volumes:
96129
lit-socket:
130+
cert-data:

0 commit comments

Comments
 (0)