도입: Secrets와 Variables를 나누는 한 줄 기준
GitHub Actions에서 노출되면 곤란한 값은 Secrets, 공개되어도 배포 보안에 직접 문제가 없는 설정값은 Variables에 둔다. 둘 다 워크플로에서 값을 재사용하는 기능이지만 보호 방식과 운영 목적이 다르다. API 키를 Variables에 넣거나, 반대로 공개 설정까지 Secrets에 몰아넣으면 보안과 유지보수 모두 어려워진다.
GitHub Actions Secrets와 Variables 핵심 항목
1. 저장 목적
- 정의: Secrets는 토큰·비밀번호·개인 키처럼 민감한 값을 저장하고, Variables는 리전·서비스 이름·빌드 옵션처럼 비민감 설정을 저장한다.
- 원리: GitHub는 Secrets를 워크플로 로그에서 마스킹하려고 처리하지만 Variables 값은 기본적으로 마스킹하지 않는다.
- 예시:
CLOUDFLARE_API_TOKEN은 Secret,R2_BUCKET_NAME과DEPLOY_REGION은 Variable이 적합하다. - 오해: 저장소가 비공개라고 해서 Variables에 인증 정보를 넣어도 안전한 것은 아니다. 워크플로 로그나 서드파티 액션을 통해 값이 노출될 수 있다.
2. 워크플로에서 참조하는 문법
- 정의: Secret은
secrets컨텍스트, 저장소·조직·환경 Variable은vars컨텍스트로 읽는다. - 원리:
${{ secrets.NAME }}와${{ vars.NAME }}는 워크플로가 평가될 때 값으로 치환된다. 단계 안에서 반복 사용하려면env에 옮겨 명령에 전달할 수 있다. - 예시:
jobs:
deploy:
runs-on: ubuntu-latest
env:
BUCKET_NAME: ${{ vars.R2_BUCKET_NAME }}
steps:
- name: Deploy
env:
API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: ./scripts/deploy.sh
- 오해: Secret을
if:조건에서 직접 비교하는 방식은 피해야 한다. 필요한 경우 job 수준 환경 변수에 전달한 뒤 단계에서 빈 값 여부를 검사한다.
3. 범위와 우선순위
- 정의: Secrets와 Variables는 조직, 저장소, Environment 수준으로 나눠 관리할 수 있다.
- 원리: 공통값은 조직에, 프로젝트 값은 저장소에, 운영·스테이징처럼 배포 대상별 값은 Environment에 둔다.
- 예시:
productionEnvironment에는 운영 API 토큰과 운영 버킷 이름을,staging에는 테스트용 값을 각각 등록한다. - 오해: 같은 이름을 여러 범위에 만들면 관리가 쉬워지는 것이 아니다. 어떤 값이 실제로 선택되는지 추적하기 어려워지므로 범위별 이름과 책임을 문서화해야 한다.
4. 로그와 출력에서의 노출
- 정의: Secret 마스킹은 중요한 보호 장치지만 모든 형태의 변형값까지 완벽하게 숨겨주는 데이터 유출 방지 시스템은 아니다.
- 원리: 값을 그대로 출력하지 않고, 디버깅 정보도 존재 여부나 길이처럼 실제 값을 드러내지 않는 방식으로 기록한다.
- 예시:
echo "$API_TOKEN"대신 토큰이 비어 있는지만 검사하고 실패 메시지를 남긴다. - 오해: 로그에서 별표로 보인다는 이유만으로 안전하다고 단정하면 안 된다. 인코딩하거나 일부만 잘라 출력한 값, 외부 명령의 오류 메시지 등은 별도로 주의해야 한다.
5. 외부 기여와 재사용 워크플로
- 정의: Fork에서 시작된 워크플로, Dependabot 이벤트, 재사용 워크플로에는 Secrets 전달이 제한되거나 자동 전달되지 않을 수 있다.
- 원리: 신뢰하지 않는 코드가 인증 정보에 접근하지 못하도록 이벤트 종류와 권한 경계를 확인해야 한다.
- 예시: Pull Request 검증 job은 읽기 전용 권한으로 실행하고, 실제 배포 job은 보호된 branch와 Environment 승인 이후에만 실행한다.
- 오해: 호출하는 워크플로에 Secret이 있다고 해서 재사용 워크플로가 자동으로 모두 사용할 수 있는 것은 아니다. 전달 계약을 명시해야 한다.
6. 장기 키보다 짧은 권한
- 정의: 클라우드 제공자가 OpenID Connect를 지원한다면 장기간 유효한 접근 키 대신 실행 시점에 짧게 발급되는 자격 증명을 사용할 수 있다.
- 원리: 유출된 고정 키가 오랫동안 악용될 위험을 줄이고, 저장해야 할 Secret 수를 줄인다.
- 예시: GitHub의 OIDC 토큰으로 클라우드 역할을 위임받고, 워크플로에는 역할 식별자 같은 비민감 설정만 Variable로 둔다.
- 오해: OIDC를 쓴다고 권한 설계가 자동으로 안전해지는 것은 아니다. 대상 저장소·branch·Environment 조건과 최소 권한 정책을 함께 설정해야 한다.
핵심 원리 정리
값을 등록하기 전에 먼저 “이 값이 로그에 그대로 보여도 괜찮은가?”를 묻는다. 아니면 Secret이다. 다음으로 값의 적용 범위를 정한다. 여러 저장소가 공유하면 조직, 한 저장소만 사용하면 저장소, 배포 대상에 따라 달라지면 Environment가 적합하다. 마지막으로 워크플로의 permissions를 최소화하고 외부 기여 이벤트에서는 배포 자격 증명을 사용할 수 없게 분리한다.
흔한 오해
Secrets는 암호 저장소이지만 잘못 작성한 워크플로까지 안전하게 고쳐주지는 않는다. 신뢰하지 않는 액션에 Secret을 넘기거나 값을 명령행 인수로 노출하면 위험하다. Variables는 단순 문자열이므로 API 토큰을 넣으면 안 된다. 반대로 모든 설정을 Secrets로 숨기면 어떤 환경이 어떤 값을 쓰는지 검토하기 어려워진다.
이해 확인 요약
- 인증 정보와 개인 키는 Secrets에 둔다.
- 공개 가능한 배포 설정은 Variables에 둔다.
- 운영과 스테이징 값은 Environment 단위로 분리한다.
- 로그에는 실제 Secret 값을 출력하지 않는다.
- Fork·Dependabot·재사용 워크플로의 전달 제한을 확인한다.
- 가능하면 OIDC와 최소 권한을 사용한다.
참고 자료
- GitHub Docs, Variables
- GitHub Docs, Using secrets in GitHub Actions
- GitHub Docs, Contexts
