/////

GitHub Actions Reusable Workflow Failure Modes: workflow_call, Secrets Inheritance, Matrix Outputs, and Concurrency Cancellation

GitHub Actions reusable workflow(workflow call)는 CI/CD 표준화에 유용하지만, 호출자(caller)와 호출 대상(called workflow) 사이의 경계가 명확하지 않으면 보안·배포·디버깅 실패가 발생하기 쉽다. 특히 secrets: inherit, permissions 권한 축소, matrix reusable workflow의 output 수집 방식, concurrency.cancel-in-progress의 취소 동작

/////

Summary#

GitHub Actions reusable workflow(workflow_call)는 CI/CD 표준화에 유용하지만, 호출자(caller)와 호출 대상(called workflow) 사이의 경계가 명확하지 않으면 보안·배포·디버깅 실패가 발생하기 쉽다. 특히 secrets: inherit, permissions 권한 축소, matrix reusable workflow의 output 수집 방식, concurrency.cancel-in-progress의 취소 동작은 겉보기 YAML은 정상이어도 운영 환경에서 “왜 값이 비었는지”, “왜 토큰 권한이 부족한지”, “왜 배포가 취소됐는지”를 추적하기 어렵게 만든다.

핵심 운영 원칙은 다음과 같다.

  1. reusable workflow는 권한을 “상향”시킬 수 없고, 호출 체인에서 권한은 유지되거나 더 제한된다.
  2. secrets: inherit는 편하지만 비밀값 전달 범위를 넓히므로, 신뢰 경계가 다른 workflow에는 명시적 secret 매핑이 더 안전하다.
  3. matrix로 호출된 reusable workflow의 workflow-level output은 모든 matrix job 결과를 배열처럼 자동 fan-in하지 않는다.
  4. concurrency는 같은 그룹의 pending/running job 또는 workflow run을 취소·대체할 수 있으므로, 배포 workflow에서는 group key 설계가 안전성에 직접 연결된다.

Key Points#

1. workflow_call secrets 전달 실패 모드#

Reusable workflow는 on.workflow_call로 정의되고, caller workflow에서 uses:로 호출된다. 이때 secret은 자동으로 모두 전달되지 않는다. 일반적으로는 caller가 called workflow에 전달할 secret을 명시해야 하며, 같은 organization 또는 enterprise 내부 reusable workflow에는 secrets: inherit를 사용할 수 있다.

실패 모드:

  • called workflow에서 secret이 비어 있음
  • caller가 secrets: 매핑을 빠뜨렸거나,
  • called workflow의 on.workflow_call.secrets 정의와 caller의 secret 이름이 맞지 않거나,
  • secrets: inherit가 적용되지 않는 경계에서 사용됐을 수 있다.

  • 환경 secret(environment secret) 오해

  • GitHub 문서에 따르면 on.workflow_callenvironment 키워드를 지원하지 않으며, reusable workflow 안의 job에서 environment를 지정하면 caller에서 전달한 secret이 아니라 해당 environment의 secret이 사용될 수 있다.
  • 따라서 deployment environment secret과 reusable workflow input/secret을 혼합하면 예상과 다른 secret이 선택될 수 있다.

  • pull_request_target와 secret 노출 위험

  • pull_request_target는 fork에서 온 pull request에도 base repository context에서 실행될 수 있어, checkout 대상과 실행 코드가 안전하게 통제되지 않으면 secret 노출 위험이 커진다.
  • reusable workflow 자체가 안전하더라도, caller가 untrusted code를 checkout하거나 실행하면 secret 전달 정책이 우회적으로 위험해질 수 있다.

권장 패턴:

jobs:
  call-deploy:
    uses: org/repo/.github/workflows/deploy.yml@v1
    secrets:
      deploy_token: ${{ secrets.PROD_DEPLOY_TOKEN }}

secrets: inherit는 내부 신뢰 경계가 명확하고 called workflow가 필요한 secret만 참조한다는 확신이 있을 때 제한적으로 사용한다.


2. permissions 권한 축소와 token capability 오해#

Reusable workflow에서 자주 발생하는 장애는 GITHUB_TOKEN 권한 부족이다. GitHub Actions의 permissions는 workflow 또는 job 단위로 설정할 수 있으며, reusable workflow 호출 체인에서는 권한을 상승시킬 수 없고 유지하거나 더 제한할 수 있다.

실패 모드:

  • called workflow가 contents: write, packages: write, id-token: write 등을 요구하지만 실패
  • caller workflow의 top-level 또는 job-level permissions가 더 낮게 설정되어 있으면 called workflow가 더 높은 권한을 선언해도 실제 권한은 부족할 수 있다.

  • OIDC 배포 실패

  • cloud federation 배포에서 id-token: write가 필요한데 caller 쪽에서 빠뜨리면 called workflow 내부의 인증 단계가 실패한다.

  • 보안 강화를 위해 caller에서 permissions: read-all 또는 {}를 설정한 뒤 reusable workflow가 깨짐

  • 중앙 reusable workflow가 release, tag, package publish, deployment status update를 수행한다면 caller의 최소 권한 정책과 충돌한다.

권장 패턴:

permissions:
  contents: read
  id-token: write

jobs:
  deploy:
    uses: org/platform/.github/workflows/deploy.yml@v1

reusable workflow 문서에는 “필요 권한 계약”을 명시해야 한다.

예:

Required caller permissions:
- contents: read
- id-token: write
- deployments: write, if GitHub deployments are updated

3. Matrix reusable workflow outputs: fan-out/fan-in 착각#

Reusable workflow는 on.workflow_call.outputs로 workflow output을 정의할 수 있다. 하지만 matrix strategy로 reusable workflow를 여러 번 호출할 때 output 처리는 일반적인 배열 fan-in처럼 동작하지 않는다.

GitHub 문서에 따르면 matrix로 실행된 reusable workflow가 output을 설정하면, reusable workflow output은 성공적으로 완료된 matrix 실행 중 실제 값을 설정한 마지막 완료 workflow의 값이 된다. 마지막 완료 workflow가 빈 문자열을 설정하면, 그 이전에 값을 설정한 workflow의 output이 사용될 수 있다.

실패 모드:

  • matrix 결과 전체가 필요한데 output 하나만 남음
  • 예: service=a,b,c별 deploy URL을 모두 모으고 싶지만 needs.deploy.outputs.url에는 하나의 값만 남는다.

  • 완료 순서에 따라 output이 바뀌는 것처럼 보임

  • matrix job 완료 순서는 고정되지 않으므로, “마지막 값” 의존은 비결정적으로 보일 수 있다.

  • 빈 output과 누락 output 디버깅 어려움

  • called workflow 내부 step output → job output → workflow output 연결 중 하나라도 빠지면 caller의 needs context에서는 빈 값처럼 보일 수 있다.

비권장 예:

jobs:
  deploy:
    strategy:
      matrix:
        service: [api, web, worker]
    uses: org/repo/.github/workflows/deploy-service.yml@v1
    with:
      service: ${{ matrix.service }}

  summarize:
    runs-on: ubuntu-latest
    needs: deploy
    steps:
      - run: echo "${{ needs.deploy.outputs.url }}"

위 구조는 api, web, worker의 URL 전체 목록을 안정적으로 제공하지 않는다.

대안:

  • matrix job별 artifact 업로드 후 별도 aggregation job에서 다운로드
  • 외부 상태 저장소에 service별 결과 기록
  • reusable workflow 대신 caller workflow 안에서 matrix job을 직접 정의하고 aggregation job을 명시
  • JSON 파일을 artifact로 모아 fan-in 처리

4. concurrency.cancel-in-progress와 배포 취소/race condition#

GitHub Actions concurrency는 같은 concurrency group에 대해 동시에 실행되는 workflow 또는 job을 제한한다. 같은 group에는 running 1개와 pending 1개만 존재할 수 있으며, 새 run이 들어오면 기존 pending run은 취소된다. cancel-in-progress: true를 사용하면 running run도 취소할 수 있다.

실패 모드:

  • 배포 중 새 push가 들어와 기존 배포가 취소됨
  • blue/green, migration, rollback-sensitive deployment에서는 중간 취소가 위험할 수 있다.

  • reusable workflow 내부와 caller 양쪽에서 concurrency를 설정해 의도치 않은 직렬화 발생

  • caller는 branch 단위 group을 쓰고, called workflow는 environment 단위 group을 쓰는 경우 서로 다른 레벨의 lock이 겹쳐 디버깅이 어려워질 수 있다.

  • group key가 너무 넓음

  • 예: production 하나로 모든 서비스 배포를 묶으면 서로 독립적인 서비스 배포도 취소 또는 대기된다.

  • group key가 너무 좁음

  • 예: commit SHA를 group에 넣으면 사실상 concurrency 보호가 사라진다.

위험한 예:

concurrency:
  group: production
  cancel-in-progress: true

더 안전한 예:

concurrency:
  group: deploy-${{ github.ref_name }}-${{ inputs.service }}-${{ inputs.environment }}
  cancel-in-progress: false

또는 PR 검증처럼 최신 run만 의미 있는 경우에는 다음이 적절할 수 있다.

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

배포에서는 “최신 run만 남기는 것”이 항상 안전하지 않다. 특히 database migration, external lock, partial rollout, deployment approval이 있는 workflow에서는 취소 가능 지점을 명시적으로 설계해야 한다.


5. Debugging checklist#

Reusable workflow 장애를 조사할 때는 다음 순서가 유용하다.

  1. caller job의 uses: 대상 확인 - branch/tag/SHA가 기대한 reusable workflow 버전인지 확인한다. - 보안상 mutable branch보다 tag 또는 SHA pinning을 고려한다.

  2. with input과 secrets 매핑 확인 - called workflow의 on.workflow_call.inputs - called workflow의 on.workflow_call.secrets - caller의 with: / secrets: 이름 일치 여부

  3. 권한 확인 - caller top-level permissions - caller job-level permissions - called workflow job-level permissions - 필요한 권한이 caller에서 이미 차단되지 않았는지 확인

  4. output chain 확인 - step output - job output - workflow output - caller needs.<job_id>.outputs.<name>

  5. matrix output 여부 확인 - 여러 matrix 결과를 하나의 workflow output으로 모으려는 설계인지 확인 - 필요하면 artifact 기반 aggregation으로 전환

  6. concurrency group 확인 - caller와 called workflow 양쪽의 concurrency - group key의 범위 - cancel-in-progress 조건 - deployment environment protection rule과의 상호작용

Cautions#

  • 이 초안은 공개 GitHub Docs 중심으로 정리한 것이다. 실제 동작은 GitHub Enterprise Server 버전, repository visibility, organization policy, environment protection rule, fork 정책에 따라 달라질 수 있다.
  • secrets: inherit의 적용 범위와 제한은 GitHub 제품 업데이트에 따라 바뀔 수 있으므로, 운영 전 현재 문서를 확인해야 한다.
  • pull_request_target 관련 위험은 reusable workflow 자체의 문제가 아니라, untrusted pull request code를 어떤 권한과 secret context에서 실행하는지의 문제다.
  • matrix reusable workflow output은 “전체 결과 수집” 용도로 설계된 기능이 아니므로, 결과 목록이 필요하면 artifact 또는 별도 aggregation 단계를 설계하는 편이 안전하다.
  • concurrency.cancel-in-progress: true는 CI 검증에는 유용하지만, production deployment에는 위험할 수 있다. 취소되어도 안전한 단계와 취소되면 안 되는 단계를 구분해야 한다.
  • 공개 문서만으로는 모든 race condition 사례를 재현할 수 없다. 조직별 reusable workflow wrapper, deployment tool, cloud provider 인증 방식, custom action 구현에 따라 추가 실패 모드가 생길 수 있다.

Sources#

  • https://docs.github.com/en/actions/using-workflows/reusing-workflows
  • https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions
  • https://docs.github.com/en/actions/learn-github-actions/contexts
  • https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency
  • https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions
  • https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs

Sagwan Revalidation 2026-06-15T03:27:08Z#

  • verdict: ok
  • note: GitHub Actions reusable workflow 주요 실패 모드 설명이 현재 문서와 부합함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1