이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Pipeline Secret과 배포 권한은 왜 build 성공보다 더 엄격하게 관리해야 하는가?
  • CI/CD에서 Secret이 새어 나가는 경로는 무엇인가?
  • 최소 권한, 환경 승인, Secret 회전은 실제 pipeline에 어떻게 반영되는가?

개요

CI/CD pipeline은 운영 권한을 자동화된 job에 위임한다.

이 권한은 registry push, cloud deploy, server SSH, release promotion, database migration 같은 강한 작업을 수행할 수 있다.

따라서 pipeline Secret과 배포 권한은 편의 기능이 아니라 운영 보안 경계다.

백엔드 개발자는 Secret 이름만 아는 것이 아니라 어디에 주입되고, 누가 실행할 수 있고, 노출되면 어떻게 회전하는지 알아야 한다.

Secret 노출 경로

Secret은 생각보다 많은 곳으로 샐 수 있다.

pipeline log에 echo $PASSWORD가 찍힐 수 있다.

Docker build arg로 들어간 값이 image layer에 남을 수 있다.

테스트 실패 dump나 Gradle stacktrace에 연결 문자열이 남을 수 있다.

PR에서 실행되는 workflow가 운영 Secret을 읽을 수 있으면 외부 기여자 코드가 Secret에 접근할 수 있다.

Artifact나 cache에 .env 파일이 포함될 수도 있다.

최소 권한

GitHub Actions 예시에서는 job 단위로 권한을 줄인다.

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
      - run: ./gradlew test
 
  deploy:
    runs-on: ubuntu-latest
    environment: production
    permissions:
      contents: read
      packages: write
      id-token: write
    steps:
      - run: ./scripts/deploy.sh

test job에는 production deploy 권한이 필요 없다.

deploy job에는 필요한 package/cloud 권한만 둔다.

모든 job에 broad token을 넣는 방식은 장애보다 보안 사고에 가깝다.

OIDC와 장기 토큰

가능하면 장기 cloud access key보다 OIDC 기반 임시 자격 증명을 쓴다.

장기 토큰은 유출되면 회전 전까지 계속 사용할 수 있다.

OIDC는 특정 repository, branch, environment 조건에 맞는 job만 cloud role을 받을 수 있게 만들 수 있다.

조건을 잘못 열면 아무 branch나 production role을 받을 수 있으므로 trust policy가 중요하다.

Environment와 승인

production 배포는 protected environment로 묶는다.

환경별 Secret은 staging과 production을 분리한다.

production environment에는 reviewer approval, wait timer, branch rule을 둘 수 있다.

승인은 귀찮은 절차가 아니라 운영 변경의 책임 경계를 남기는 기록이다.

Secret 회전

Secret은 언젠가 노출될 수 있다고 가정하고 회전 절차를 둔다.

회전 절차에는 새 Secret 생성, pipeline 업데이트, staging 검증, production 적용, 이전 Secret 폐기, 실패 시 rollback이 포함된다.

DB password나 외부 API key처럼 애플리케이션 runtime에 쓰이는 값은 Spring profile과 배포 순서까지 함께 고려한다.

Secret만 바꾸고 애플리케이션 재시작이나 connection pool 갱신을 놓치면 인증 실패가 발생할 수 있다.

운영 체크리스트

  • PR CI에서 production Secret에 접근할 수 없는가?
  • job별 permissions가 최소 권한으로 제한되어 있는가?
  • production deploy는 protected environment 승인 아래 실행되는가?
  • 장기 access key 대신 OIDC 임시 자격 증명을 검토했는가?
  • Secret 값이 log, artifact, cache, image layer에 남지 않는가?
  • Secret 소유자와 회전 주기가 문서화되어 있는가?
  • Secret 회전 후 smoke test와 rollback 기준이 있는가?

장애 신호

  • pipeline log에 DB URL, token 일부, password 일부가 노출된다.
  • PR workflow에서 deploy token을 읽을 수 있다.
  • 개인 access token으로 production 배포가 수행된다.
  • 퇴사자 계정이나 오래된 deploy key가 여전히 유효하다.
  • Secret 회전 직후 Spring이 외부 API 인증 실패를 낸다.
  • image layer에서 build arg로 넣은 Secret 흔적이 발견된다.

안전한 완화 조치

Secret 노출이 의심되면 masking이나 로그 삭제만으로 끝내지 않는다.

노출된 Secret은 회전해야 한다.

노출 범위는 CI log, artifact, cache, image registry, 채팅 공유까지 확인한다.

권한이 과도하면 먼저 job 분리와 environment 보호를 적용한다.

회전 후에는 staging smoke test, production deploy, 인증 실패 metric을 확인한다.

운영 장애 중 Secret 값을 확인하려고 로그에 출력하면 사고를 키운다.

인프라 담당자와 공유할 자료

문제: production deploy token 노출 의심
노출 위치: GitHub Actions run 1821 log
영향 범위: repository order-api, production package write
즉시 조치: token revoke, OIDC role로 교체
검증: staging deploy 성공, production deploy approval 적용
추가 확인: artifact/cache/image layer 내 Secret 흔적 없음

Secret 사고 공유에는 값 자체가 아니라 Secret 이름, 권한 범위, 노출 위치, 회전 상태, 검증 결과를 적는다.

실전 팁

  • Secret 값은 확인하지 말고 존재 여부와 source만 확인한다.
  • build arg로 Secret을 넘기지 않는다.
  • production Secret은 protected environment에 둔다.
  • 회전 가능한 구조가 없는 Secret은 기술 부채로 기록한다.
  • 개인 token 기반 배포를 service account나 OIDC로 바꾼다.

위험 신호!

  • 모든 job에 같은 Secret 묶음을 주입한다.
  • PR에서 production deploy 권한이 열린다.
  • Secret을 echo로 확인한다.
  • 노출된 Secret을 “이미 masking되었으니 괜찮다”고 본다.
  • Secret 회전이 애플리케이션 재시작과 연결되어 있지 않다.

확인 질문

확인 질문

  • CI job에 production Secret을 넣으면 왜 위험한가?
    • 테스트 코드나 PR workflow가 운영 권한에 접근할 수 있고, 로그나 artifact를 통해 Secret이 노출될 수 있기 때문이다.
  • OIDC 기반 배포 권한의 장점은 무엇인가?
    • 장기 Secret을 저장하지 않고 특정 repository, branch, environment 조건의 job에만 임시 권한을 줄 수 있기 때문이다.
  • Secret 노출이 의심될 때 masking만으로 부족한 이유는 무엇인가?
    • 이미 로그, artifact, cache, 외부 공유 경로에 복제되었을 수 있으므로 Secret 자체를 회전해야 하기 때문이다.

참고 문서