GitHub Actions 강좌 #3 시크릿·변수·환경: secrets vs vars, environment 승인 관문
#2까지의 워크플로에는 비밀이 없었습니다. 실전 워크플로는 다릅니다. 배포 대상의 자격 증명, API 키, 웹훅 주소 같은 값이 필요하고, 이것을 YAML에 적는 순간 저장소를 읽을 수 있는 모두에게 공개됩니다. 이번 편은 비밀과 설정을 워크플로에 안전하게 공급하는 장치들, 그리고 그 위에 사람의 승인 관문까지 세우는 environment를 다룹니다.
secrets와 vars: 가릴 것과 보일 것 #
값을 저장소 설정에 두고 워크플로에서 참조하는 통로가 둘 있습니다. Settings의 Secrets and variables 메뉴에서 등록합니다.
- secrets: 비밀 값입니다. 등록 후에는 누구도 값을 다시 볼 수 없고(덮어쓰기만 가능), 로그에 마스킹됩니다.
${{ secrets.API_KEY }}로 참조합니다. - vars: 비밀이 아닌 설정값입니다. 값이 보이고 로그에도 그대로 나옵니다.
${{ vars.AWS_REGION }}으로 참조합니다.
구분 기준은 “로그에 찍혀도 되는가"입니다. 리전 이름, 버킷 이름, 기능 플래그처럼 공개되어도 무해한 값을 secrets에 넣으면 로그 디버깅만 어려워지므로 vars로 두고, 유출 시 피해가 있는 값만 secrets에 둡니다. 테라폼 운영 강좌 #1의 워크플로에서 역할 ARN을 vars.TF_PLAN_ROLE_ARN으로 둔 것이 이 기준의 적용 사례입니다. ARN은 비밀이 아니기 때문입니다.
마스킹은 보조 장치입니다 #
secrets 값이 로그에 나타나면 GitHub가 ***로 가려 줍니다. 유용하지만 이것을 안전의 근거로 삼으면 안 됩니다. 마스킹은 알고 있는 값과 정확히 일치하는 문자열을 가리는 것이라, base64로 인코딩해 찍거나 한 글자씩 나눠 찍으면 그대로 노출됩니다. 즉 마스킹은 실수를 줄여 주는 보조 장치이고, 본질적인 안전은 시크릿을 애초에 echo 하지 않는 것, 그리고 #9에서 다룰 “신뢰할 수 없는 코드에 시크릿을 주지 않는 구조"에서 옵니다.
세 계층: 조직, 저장소, environment #
secrets와 vars는 세 곳에 둘 수 있고, 좁은 쪽이 이깁니다.
조직(organization) ← 여러 저장소 공용 (예: 공용 알림 웹훅)
└ 저장소(repository) ← 이 저장소 공용
└ environment ← 특정 환경 전용 (예: prod 배포 역할)같은 이름이 겹치면 environment > 저장소 > 조직 순으로 적용됩니다. 이 구조 덕분에 “dev와 prod가 같은 워크플로를 쓰면서 다른 자격 증명을 받는” 구성이 이름 하나로 표현됩니다. 워크플로 코드는 ${{ secrets.DEPLOY_ROLE }} 하나인데, 어느 environment로 실행되는지에 따라 다른 값이 들어오는 것입니다.
environment: 배포 앞의 관문 #
environment는 시크릿 스코프이면서 동시에 보호 규칙의 단위입니다. Settings의 Environments에서 만들고, 잡에 지정합니다.
jobs:
deploy-prod:
runs-on: ubuntu-latest
environment: production
steps:
- run: ./deploy.sh # production 스코프의 secrets/vars가 적용됨environment에 걸 수 있는 보호 규칙이 실전의 핵심입니다.
- Required reviewers: 지정한 사람·팀이 승인해야 잡이 시작됩니다. 승인 대기 상태의 잡은 Actions 화면에 Review 버튼과 함께 멈춰 있고, 승인·거부가 기록에 남습니다. “배포 버튼은 자동화하되 마지막 판단은 사람"이라는 요구의 표준 답입니다.
- Wait timer: 잡 시작 전 대기 시간을 강제합니다. 배포 예고 후 취소할 시간을 벌어 주는 용도입니다.
- Deployment branches: 이 environment로 배포할 수 있는 브랜치를 제한합니다. production을 main 전용으로 잠그면, 다른 브랜치의 워크플로가 실수로 prod 시크릿을 받는 일 자체가 차단됩니다.
테라폼 운영 강좌 #1에서 “environment 승인으로 prod 앞에 사람의 관문"이라고 예고만 했던 장치의 전모가 이것입니다.
GITHUB_TOKEN: 등록한 적 없는 시크릿 #
secrets 컨텍스트에는 등록하지 않아도 항상 존재하는 값이 하나 있습니다. secrets.GITHUB_TOKEN입니다. 워크플로 실행마다 GitHub가 자동 발급하는 단기 토큰으로, 이 저장소에 대한 API 권한을 갖고 실행이 끝나면 만료됩니다. PR에 코멘트를 달거나, 릴리스를 만들거나, #6에서 볼 GHCR 푸시까지 이 토큰으로 처리되므로, 저장소 안에서 끝나는 일에는 별도 토큰을 만들 필요가 없습니다. 이 토큰이 어떤 권한을 갖는지, 그리고 왜 기본 권한을 줄여야 하는지는 #9 보안 편의 첫 주제입니다.
정리 #
이번 글에서 다룬 내용입니다.
- 구분 기준은 “로그에 찍혀도 되는가"입니다. 비밀만 secrets, 나머지 설정은 vars로 둬야 디버깅이 편합니다
- 마스킹은 정확히 일치하는 문자열만 가리는 보조 장치입니다. 안전의 본질은 시크릿을 찍지 않는 것과 구조적 격리입니다
- secrets·vars는 조직, 저장소, environment 세 계층이고 좁은 쪽이 이깁니다. 같은 코드가 환경마다 다른 값을 받는 기반입니다
- environment는 시크릿 스코프이자 보호 규칙 단위입니다. 승인자, 대기 시간, 브랜치 제한으로 배포 관문을 세웁니다
- GITHUB_TOKEN은 실행마다 자동 발급되는 단기 토큰이며, 저장소 안의 작업은 대부분 이것으로 충분합니다
다음 글(#4 매트릭스·캐시·아티팩트)에서는 실행을 여러 벌로 늘리는 매트릭스와, 반대로 시간과 비용을 줄이는 캐시, 그리고 잡 사이에 파일을 넘기는 아티팩트를 다룹니다.