Skip to content

Commit 0103777

Browse files
clawdbot-glitch003glitch003claude
authored
fix(ci): encrypt prod envs client-side before phala envs update (#344)
The phala CLI's internal pubkey lookup passes kms_type "base" instead of a real slug (e.g. kms-base-prod2), so `phala envs update` errors with "Failed to get app env encrypt pubkey" on Safe-owned apps. Mirror the workaround from deploy-prod-1-propose.yml: fetch the pubkey via the correct slug, encrypt with @phala/cloud's encryptEnvVars, then pass the hex blob via `--encrypted-env`. Co-authored-by: Chris Cassano <chris@litprotocol.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent c4e8677 commit 0103777

1 file changed

Lines changed: 85 additions & 17 deletions

File tree

.github/workflows/manual_phala-envs-update-prod.yml

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
# Differences from the next/staging variant:
99
# - Stripe LIVE keys (STRIPE_SECRET_KEY / STRIPE_PUBLISHABLE_KEY), not sandbox
1010
# - Defaults: CERTBOT_DOMAIN=api.chipotle.litprotocol.com, GCP_PROJECT_ID=chipotle-prod
11+
# - Encrypts env vars client-side and pushes via `phala envs update --encrypted-env`.
12+
# The CLI's internal pubkey lookup is broken for Safe-owned apps (it passes
13+
# kms_type "base" instead of a real slug like "kms-base-prod2"); see the
14+
# same workaround in deploy-prod-1-propose.yml:307-383.
1115
#
1216
# Env block mirrors deploy-prod-1-propose.yml:341-372 (encryptEnvVars step).
1317
#
1418
# Required secrets:
1519
# PHALA_CLOUD_API_KEY - Phala Cloud API key
16-
# PHALA_DSTACKAPP_PRIVATE_KEY - DstackApp owner key (unused for prod Safe-owned app,
17-
# kept for parity with `phala envs update` CLI args)
1820
# BASE_CHAIN_RPC - Base mainnet RPC URL
1921
# GCP_SERVICE_ACCOUNT_JSON - GCP service account key
2022
# STRIPE_SECRET_KEY - Stripe LIVE secret key
@@ -48,6 +50,11 @@ on:
4850
required: false
4951
type: string
5052
default: "chipotle-prod"
53+
kms_slug:
54+
description: "KMS slug to fetch the encryption pubkey from (e.g. kms-base-prod2)"
55+
required: false
56+
type: string
57+
default: "kms-base-prod2"
5158
start:
5259
description: "Start the CVM after pushing envs"
5360
required: false
@@ -65,30 +72,91 @@ jobs:
6572
- name: Install Phala CLI
6673
run: npm install -g phala
6774

68-
- name: Push sealed envs to CVM
75+
- name: Resolve CVM app_id
76+
id: cvm
6977
env:
7078
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
71-
PRIVATE_KEY: ${{ secrets.PHALA_DSTACKAPP_PRIVATE_KEY }}
72-
ETH_RPC_URL: ${{ secrets.BASE_CHAIN_RPC }}
73-
BASE_CHAIN_RPC: ${{ secrets.BASE_CHAIN_RPC }}
79+
run: |
80+
set -euo pipefail
81+
APP_ID=$(phala cvms get "${{ inputs.cvm_id }}" --json | jq -r '.app_id')
82+
if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then
83+
echo "::error::Could not resolve app_id for CVM ${{ inputs.cvm_id }}"
84+
exit 1
85+
fi
86+
echo "Resolved app_id: $APP_ID"
87+
echo "app_id=$APP_ID" >> "$GITHUB_OUTPUT"
88+
89+
- name: Fetch CVM encryption public key
90+
id: pubkey
91+
env:
92+
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
93+
KMS_SLUG: ${{ inputs.kms_slug }}
94+
APP_ID: ${{ steps.cvm.outputs.app_id }}
95+
run: |
96+
set -euo pipefail
97+
# Workaround: `phala envs update` passes kms_type ("base") to the
98+
# /kms/{kms}/pubkey/{app_id} API, but the API expects a KMS slug
99+
# (e.g. "kms-base-prod2"). Fetch the pubkey directly via the correct
100+
# slug, then encrypt client-side.
101+
echo "Fetching encryption pubkey via /kms/$KMS_SLUG/pubkey/$APP_ID ..."
102+
PUBKEY_RESPONSE=$(phala api "/kms/$KMS_SLUG/pubkey/$APP_ID" --json)
103+
PUBKEY=$(echo "$PUBKEY_RESPONSE" | jq -r '.public_key')
104+
if [ -z "$PUBKEY" ] || [ "$PUBKEY" = "null" ]; then
105+
echo "::error::Failed to extract public_key from KMS response: $PUBKEY_RESPONSE"
106+
exit 1
107+
fi
108+
echo "Got encryption pubkey: ${PUBKEY:0:16}..."
109+
echo "encrypt_pubkey=$PUBKEY" >> "$GITHUB_OUTPUT"
110+
111+
- name: Encrypt environment variables for CVM
112+
id: encrypt
113+
env:
74114
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
75115
STRIPE_PUBLISHABLE_KEY: ${{ secrets.STRIPE_PUBLISHABLE_KEY }}
76116
GCP_SERVICE_ACCOUNT_JSON: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }}
117+
BASE_CHAIN_RPC: ${{ secrets.BASE_CHAIN_RPC }}
77118
CERTBOT_AWS_ACCESS_KEY_ID: ${{ secrets.CERTBOT_AWS_ACCESS_KEY_ID }}
78119
CERTBOT_AWS_SECRET_ACCESS_KEY: ${{ secrets.CERTBOT_AWS_SECRET_ACCESS_KEY }}
79120
CERTBOT_AWS_ROLE_ARN: ${{ secrets.CERTBOT_AWS_ROLE_ARN }}
121+
CERTBOT_AWS_REGION: ${{ vars.CERTBOT_AWS_REGION }}
122+
GCP_PROJECT_ID: ${{ inputs.gcp_project_id }}
123+
CERTBOT_DOMAIN: ${{ inputs.certbot_domain }}
124+
ENCRYPT_PUBKEY: ${{ steps.pubkey.outputs.encrypt_pubkey }}
125+
run: |
126+
set -euo pipefail
127+
ENCRYPTED_ENV=$(node -e '
128+
const path = require("path");
129+
const prefix = require("child_process").execSync("npm prefix -g").toString().trim();
130+
const { encryptEnvVars } = require(
131+
require.resolve("@phala/cloud", { paths: [path.join(prefix, "lib/node_modules/phala")] })
132+
);
133+
const envs = [
134+
{ key: "STRIPE_SECRET_KEY", value: process.env.STRIPE_SECRET_KEY },
135+
{ key: "STRIPE_PUBLISHABLE_KEY", value: process.env.STRIPE_PUBLISHABLE_KEY },
136+
{ key: "GCP_SERVICE_ACCOUNT_JSON", value: process.env.GCP_SERVICE_ACCOUNT_JSON },
137+
{ key: "GCP_PROJECT_ID", value: process.env.GCP_PROJECT_ID },
138+
{ key: "BASE_CHAIN_RPC", value: process.env.BASE_CHAIN_RPC },
139+
{ key: "CERTBOT_DOMAIN", value: process.env.CERTBOT_DOMAIN },
140+
{ key: "CERTBOT_AWS_ACCESS_KEY_ID", value: process.env.CERTBOT_AWS_ACCESS_KEY_ID },
141+
{ key: "CERTBOT_AWS_SECRET_ACCESS_KEY", value: process.env.CERTBOT_AWS_SECRET_ACCESS_KEY },
142+
{ key: "CERTBOT_AWS_ROLE_ARN", value: process.env.CERTBOT_AWS_ROLE_ARN },
143+
{ key: "CERTBOT_AWS_REGION", value: process.env.CERTBOT_AWS_REGION },
144+
];
145+
encryptEnvVars(envs, process.env.ENCRYPT_PUBKEY).then(hex => process.stdout.write(hex));
146+
')
147+
if [ -z "$ENCRYPTED_ENV" ]; then
148+
echo "::error::encryptEnvVars returned empty output"
149+
exit 1
150+
fi
151+
echo "Encrypted env length: ${#ENCRYPTED_ENV} chars"
152+
echo "encrypted_env=$ENCRYPTED_ENV" >> "$GITHUB_OUTPUT"
153+
154+
- name: Push sealed envs to CVM
155+
env:
156+
PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }}
157+
ENCRYPTED_ENV: ${{ steps.encrypt.outputs.encrypted_env }}
80158
run: |
81-
phala envs update "${{ inputs.cvm_id }}" \
82-
-e "STRIPE_SECRET_KEY=$STRIPE_SECRET_KEY" \
83-
-e "STRIPE_PUBLISHABLE_KEY=$STRIPE_PUBLISHABLE_KEY" \
84-
-e "GCP_SERVICE_ACCOUNT_JSON=$GCP_SERVICE_ACCOUNT_JSON" \
85-
-e "GCP_PROJECT_ID=${{ inputs.gcp_project_id }}" \
86-
-e "BASE_CHAIN_RPC=$BASE_CHAIN_RPC" \
87-
-e "CERTBOT_DOMAIN=${{ inputs.certbot_domain }}" \
88-
-e "CERTBOT_AWS_ACCESS_KEY_ID=$CERTBOT_AWS_ACCESS_KEY_ID" \
89-
-e "CERTBOT_AWS_SECRET_ACCESS_KEY=$CERTBOT_AWS_SECRET_ACCESS_KEY" \
90-
-e "CERTBOT_AWS_ROLE_ARN=$CERTBOT_AWS_ROLE_ARN" \
91-
-e "CERTBOT_AWS_REGION=${{ vars.CERTBOT_AWS_REGION }}"
159+
phala envs update "${{ inputs.cvm_id }}" --encrypted-env "$ENCRYPTED_ENV"
92160
93161
- name: Start CVM
94162
if: inputs.start

0 commit comments

Comments
 (0)