Repository navigation
refactor: Secret Manager SDK 제거하고 env 주입 방식으로 전환 - #51
Conversation
📝 WalkthroughWalkthrough로컬 실행 래퍼가 GCP Secret Manager의 dotenv 값을 자식 프로세스에 주입합니다. 애플리케이션은 Changes환경 설정 로딩
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🍹
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/with-env.ts`:
- Around line 41-61: Update scripts/with-env.ts lines 41-61 so local execution
can fall back to the existing local environment when fetchSecret fails, or make
local file mode the default while preserving Secret Manager behavior for
explicitly configured environments. Update .env.example lines 1-6 to include the
required APP_CONFIG_SOURCE=env setting and accurately document the local startup
procedure.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 96a4f825-f867-4737-b573-fd843937f440
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock,!bun.lock
📒 Files selected for processing (10)
.env.example.env.test.github/workflows/deploy.yml.gitignoree2e/setup.tspackage.jsonscripts/db-schema-init.tsscripts/with-env.tssrc/config/secret-env.tssrc/main.ts
| if (env.APP_CONFIG_SOURCE !== "env") { | ||
| const project = env.GCP_PROJECT ?? "team-mino-prod"; | ||
| const secret = env.GCP_ENV_SECRET ?? `team-mino-env-${env.APP_ENV}`; | ||
|
|
||
| let payload: string; | ||
| try { | ||
| payload = await fetchSecret(project, secret); | ||
| } catch (error) { | ||
| console.error(`시크릿을 가져오지 못했습니다: ${secret} (${project})`); | ||
| console.error(error instanceof Error ? error.message : error); | ||
| console.error( | ||
| "ADC 로그인(gcloud auth application-default login)을 확인하거나, APP_CONFIG_SOURCE=env 로 우회하세요.", | ||
| ); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| env[ENV_DOTENV_VAR] = payload; | ||
| for (const [key, value] of Object.entries(parseDotenv(payload))) { | ||
| if (env[key] === undefined) env[key] = value; | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
로컬 .env 대체 경로를 실제로 동작하게 해야 합니다.
APP_ENV는 Line 39에서 기본값 local을 사용합니다. 그러나 APP_CONFIG_SOURCE=env가 없으면 Line 41이 Secret Manager 조회를 시작합니다. ADC가 없으면 Line 48-54가 종료하므로, 기존 로컬 .env만으로는 start:local을 실행할 수 없습니다.
scripts/with-env.ts#L41-L61: 로컬 Secret Manager 조회가 실패하면 기존 로컬 환경으로 자식 명령을 실행하거나, 로컬 파일 모드를 명시적으로 기본 선택해야 합니다..env.example#L1-L6: 로컬 파일 모드가APP_CONFIG_SOURCE=env를 요구한다면 그 값을 포함하고 실행 절차를 정확히 문서화해야 합니다.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] 48-48: Avoid logging sensitive data
Context: console.error(시크릿을 가져오지 못했습니다: ${secret} (${project}))
Note: [CWE-532] Insertion of Sensitive Information into Log File.
(log-sensitive-data-typescript)
📍 Affects 2 files
scripts/with-env.ts#L41-L61(this comment).env.example#L1-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/with-env.ts` around lines 41 - 61, Update scripts/with-env.ts lines
41-61 so local execution can fall back to the existing local environment when
fetchSecret fails, or make local file mode the default while preserving Secret
Manager behavior for explicitly configured environments. Update .env.example
lines 1-6 to include the required APP_CONFIG_SOURCE=env setting and accurately
document the local startup procedure.
labyrinth30
left a comment
There was a problem hiding this comment.
전혀 예상치 못했던 상황인데 선조치해주셔서 감사합니다~
처음엔 SM을 안 쓰는 건가 했는데 서버 입장에서 환경변수를 언제, 어떻게 알 수 있는지를 고친 것이구나라고 이해했어요.
고생했고, 현재는 멀티 라인 환경 변수가 없어서 approve를 했는데, 리뷰에 단 것처럼 멀티 라인 환경 변수는 어떻게 하면 좋을지 간단하게 이야기 해보죠!
| * | ||
| * 부팅 최초 단계(ConfigModule 검증 이전)라 ConfigService가 없으므로, | ||
| * "env는 ConfigService로만" 규칙의 의도적 예외로 process.env를 직접 다룹니다. | ||
| * 시크릿 포맷은 한 줄 = 한 키(여러 줄 값은 base64로 인코딩). |
There was a problem hiding this comment.
'여러 줄 값은 base64로 인코딩' 이라고 되어있는데 parseDotenv 를 봤을 때 어떤 환경변수가 base64로 인코딩된 것인지 알 수 없겠다는 생각이 들었어요.
나중에 여러 줄짜리 환경변수가 생겼을 때는 어떻게 하면 좋을까요? 👀
There was a problem hiding this comment.
아직 별 고민이 없긴 했는데, parseDotenv에서의 처리는 아니고 사용처에서 여러 줄짜리 환경 변수면 decode 해서 쓰면 되지 않을까 했어요. 구체적인 사용 사례를 떠올려보면 pem key 같은 것이긴 합니다. (그 key를 사용하는 쪽에서 그 key만 따로 encode 해서 Secrets Manager에 넣고, 사용할 때에는 decode 해서 사용)
KKardy
left a comment
There was a problem hiding this comment.
확인했습니다 🍀 🚀
다만, 조금 우려가 되는점은 배포가 된 뒤에도 128Mi 로 견뎌줄지가 고민이네요.
지금 상황에서 가장 메모리를 많이 쓰는 부분은 이미지 다운로드하고 GCS에 업로드 하는거라고 생각드는데요..!
그 작업이 진행됐을 때, 얼마나 메모리를 잡아먹는지 테스트해봐야할 거 같습니다.
아니면 CloudRun의 오토 스케일업 정책같은게 있을까요? 👀
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@infra/src/resources/secrets.ts`:
- Around line 56-67: Update the production secret IAM bindings for prodEnvSecret
so developerMember retains only the required read access and no longer receives
roles/secretmanager.secretVersionManager. Grant production
write/version-management access exclusively to the existing dedicated release or
rotation service-account member, while keeping developerMember’s local-secret
access unchanged and ensuring deployments still succeed without developer
permissions.
- Around line 42-55: Align local ADC authentication with the developer service
account used by fetchSecret: configure local setup to obtain ADC with
impersonation of developer.email, or grant the approved local user/group the
minimum secretAccessor permission on localEnvSecret. Ensure the resulting ADC
principal matches an authorized member while preserving the existing developer
write permission.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 6d9113f8-aca1-4951-a565-64c06011c830
📒 Files selected for processing (1)
infra/src/resources/secrets.ts
| const developerMember = pulumi.interpolate`serviceAccount:${developer.email}`; | ||
|
|
||
| new gcp.secretmanager.SecretIamMember("team-mino-env-local-developer-read", { | ||
| secretId: localEnvSecret.secretId, | ||
| project, | ||
| role: "roles/secretmanager.secretAccessor", | ||
| member: developerMember, | ||
| }); | ||
| new gcp.secretmanager.SecretIamMember("team-mino-env-local-developer-write", { | ||
| secretId: localEnvSecret.secretId, | ||
| project, | ||
| role: "roles/secretmanager.secretVersionManager", | ||
| member: developerMember, | ||
| }); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n -C 6 \
'gcloud auth application-default login|GOOGLE_APPLICATION_CREDENTIALS|impersonat|fetchSecret|developer\.email|serviceAccount:' \
--glob '*.ts' --glob '*.yml' --glob '*.yaml' .Repository: mash-up-kr/Team-MINO-Node
Length of output: 13948
🌐 Web query:
Google Cloud SDK gcloud auth application-default login User credentials Service Account impersonation
💡 Result:
To use service account impersonation with Application Default Credentials (ADC) in a local development environment, you can use the gcloud auth application-default login command with the --impersonate-service-account flag [1][2][3]. This command generates a local ADC file that allows client libraries to authenticate using the impersonated service account's identity [1][2][4]. Usage: gcloud auth application-default login --impersonate-service-account=SERVICE_ACCT_EMAIL [1][3] Key requirements and considerations: - Permissions: The user account executing the command must have the Service Account Token Creator (roles/iam.serviceAccountTokenCreator) IAM role on the target service account [2][3]. This role provides the required iam.serviceAccounts.getAccessToken permission [3]. - Language Support: While ADC impersonation works for many use cases, support for credentials generated this way is not universal across all Google Cloud client libraries [2]. It is officially supported for Go, Java, Node.js, and Python client libraries [3]. - Purpose: This approach is intended for local development environments where you need your code to test permissions assigned to a specific service account without using long-lived service account keys [2][5][3]. - Distinction from gcloud CLI Config: Note that there is a difference between using this flag with gcloud auth application-default login (which creates an ADC file for client libraries) and setting the gcloud config property auth/impersonate_service_account [1][3]. The latter configures the gcloud CLI tool itself to impersonate a service account for its own commands, but it does not necessarily create the ADC file used by your application code [1][6].
Citations:
- 1: https://docs.cloud.google.com/docs/authentication/use-service-account-impersonation
- 2: https://docs.cloud.google.com/docs/authentication/set-up-adc-local-dev-environment
- 3: https://docs.cloud.google.com/iam/docs/authentication
- 4: https://docs.cloud.google.com/docs/authentication/application-default-credentials
- 5: https://docs.cloud.google.com/sdk/gcloud/reference/auth/application-default/login
- 6: https://docs.cloud.google.com/sdk/docs/authenticate
로컬 ADC가 developer 서비스 계정과 같은 주체가 되도록 맞춰 주세요.
gcloud auth application-default login만 실행하면 로그인한 사용자 계정의 ADC가 생성되며, 현재 Secret Manager의 roles/secretmanager.secretAccessor 권한은 serviceAccount:${developer.email}에만 있습니다. 이런 상태에서는 fetchSecret이 호출 주체와 권한이 달라 PERMISSION_DENIED에 실패할 수 있습니다. 로컬 실행 시 --impersonate-service-account=${developer.email} ADC를 사용하거나, 승인된 사용자/그룹에 최소 권한을 부여해 주체를 일치시켜 주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infra/src/resources/secrets.ts` around lines 42 - 55, Align local ADC
authentication with the developer service account used by fetchSecret: configure
local setup to obtain ADC with impersonation of developer.email, or grant the
approved local user/group the minimum secretAccessor permission on
localEnvSecret. Ensure the resulting ADC principal matches an authorized member
while preserving the existing developer write permission.
| new gcp.secretmanager.SecretIamMember("team-mino-env-prod-developer-read", { | ||
| secretId: prodEnvSecret.secretId, | ||
| project, | ||
| role: "roles/secretmanager.secretAccessor", | ||
| member: developerMember, | ||
| }); | ||
| new gcp.secretmanager.SecretIamMember("team-mino-env-prod-developer-write", { | ||
| secretId: prodEnvSecret.secretId, | ||
| project, | ||
| role: "roles/secretmanager.secretVersionManager", | ||
| member: developerMember, | ||
| }); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 \
'team-mino-env-prod|secretVersionManager|addSecretVersion|accessSecretVersion|gcloud secrets' \
--glob '*.ts' --glob '*.yml' --glob '*.yaml' .Repository: mash-up-kr/Team-MINO-Node
Length of output: 5851
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '1,200p'
echo "== secrets/identity config =="
for f in infra/src/resources/secrets.ts infra/src/resources/identity.ts infra/src/config.ts scripts/with-env.ts; do
if [ -f "$f" ]; then
echo "--- $f ($(wc -l < "$f") lines) ---"
cat -n "$f"
fi
done
echo "== broad secret/cloud run references =="
rg -n -C 4 \
'with-env|with-env\.ts|envDotenv|ENV_DOTENV_VAR|secretEnv|secrets\.env|DATABASE_URL|secretManager|SecretIamMember|secretVersionManager|secretAccessor|gcloud secrets' .Repository: mash-up-kr/Team-MINO-Node
Length of output: 27991
프로덕션 시크릿 쓰기 권한을 개발자 서비스 계정에서 분리하세요.
infra/src/resources/secrets.ts:56-67에서 developerMember가 team-mino-env-prod의 roles/secretmanager.secretAccessor와 roles/secretmanager.secretVersionManager를 모두 갖습니다. 이 권한으로 개발자가 프로덕션 시크릿을 읽고 버전을 추가·관리할 수 있습니다.
로컬 실행에는 team-mino-env-local의 접근 권한만 사용하세요. 프로덕션 값 갱신이 필요하면 dedicated release/rotation 서비스 계정을 부여하고, 개발자 권한이 없으면 배포가 실패하지 않는지 확인하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infra/src/resources/secrets.ts` around lines 56 - 67, Update the production
secret IAM bindings for prodEnvSecret so developerMember retains only the
required read access and no longer receives
roles/secretmanager.secretVersionManager. Grant production
write/version-management access exclusively to the existing dedicated release or
rotation service-account member, while keeping developerMember’s local-secret
access unchanged and ensuring deployments still succeed without developer
permissions.
배경
서버가 뜰 때
@google-cloud/secret-manager를 사용해서 Secret을 로드하고 있었는데, 이로 인해 바이너리 사이즈와 메모리 사용량이 늘어나서 서버가 뜨지 못하고 있습니다.이를 수정해서, 로컬에서는 별도의 스크립트로
@google-cloud/secret-manager를 사용해 환경 변수를 주입하고, Cloud Run은 인프라 구성으로 외부에서 주입 받도록 수정했습니다. (의존성 제거)변경
APP_ENV_DOTENV에 담긴 dotenv 원문을 파싱해process.env를 채웁니다. 주입이 없으면 아무것도 하지 않으므로 로컬.env만으로도 그대로 시작할 수 있습니다.주입은 환경별로 이렇게 이뤄집니다.
--set-secrets(deploy.yml)scripts/with-env.ts가 조회해서 자식 프로세스에 전달SDK는 이 스크립트에서만 쓰이므로 devDependency로 내렸습니다. 프로덕션 바이너리에는 포함되지 않습니다.
+ 겸사 겸사 테스트 env도
.env.test로 분리했습니다.효과
🤖 (AI) 프로덕션 바이너리는 99 MB에서 69 MB로 줄었고, 런타임 힙도 함께 감소했습니다. Cloud Run 메모리 한도(128MiB)가 빠듯해서 바이너리 축소는 페이지 캐시 완화에도 도움이 됩니다.
(AI) 이 코드는
APP_ENV_DOTENV주입을 전제로 합니다.deploy.yml에--set-secrets가 함께 들어 있어 정상 배포 흐름은 안전하지만, 이미지만 따로 배포하면 env가 비어서 서버 시작에 실패합니다.Summary by CodeRabbit
개선 사항
테스트
문서