엔지니어링 글

클라우드 Mac CI의 Git 자격 증명 격리와 정리

클라우드 Mac CI의 Git 자격 증명 격리와 정리

장시간 실행되는 클라우드 Mac에서 여러 저장소의 빌드를 연속으로 수행할 때 가장 위험한 문제는 토큰이 스크립트에 직접 노출되는 것만이 아닙니다. 이전 작업이 자격 증명을 남겨 다음 작업이 이를 조용히 물려받는 상황도 큰 위험입니다. 흔한 잔류 지점으로는 Git 전역 설정, 인증 정보가 포함된 원격 URL, 로그인 키체인, 삭제되지 않은 임시 스크립트가 있습니다. 이 문제를 해결하는 목표는 “빌드가 끝난 뒤 변수 하나를 삭제하는 것”이 아니라, 처음부터 자격 증명이 단일 작업의 경계 안에서만 존재하도록 만드는 것입니다.

자격 증명이 저장되는 위치부터 확인하기

파이프라인부터 서둘러 수정하지 마세요. 먼저 비밀 값을 출력하지 않은 상태에서 Git이 실제로 어떤 설정을 읽는지 확인해야 합니다. --show-origin은 설정의 출처도 함께 표시하므로 시스템, 사용자, 저장소 수준의 설정을 구분할 수 있습니다.

set +x

git config --show-origin --get-all credential.helper || true
git config --show-origin --get-regexp '^(credential|http)\.' || true
git config --show-origin --get-regexp '^url\..*\.insteadof$' || true

다음 세 가지 신호를 중점적으로 확인합니다.

  • credential.helper가 영구 키체인을 가리키는지
  • http.extraHeader가 전역 또는 저장소 설정에 기록되어 있는지
  • url.*.insteadOf가 일반 주소를 인증 정보가 포함된 주소로 바꾸는지

원격 URL도 확인해야 하지만 원문 그대로 로그에 기록해서는 안 됩니다. 프로토콜://사용자정보@호스트 구조가 있는지만 판별하고, 발견하면 전체 주소를 출력하는 대신 즉시 실패하도록 할 수 있습니다.

remote_url="$(git remote get-url origin)"
case "$remote_url" in
  *://*@*)
    printf '%s
' "Remote URL contains embedded credentials" >&2
    exit 1
    ;;
esac

감사 스크립트의 첫 번째 원칙은 “자격 증명이 존재할 가능성”만 보고하는 것입니다. 문제를 입증한다는 이유로 자격 증명 자체를 빌드 로그에 출력해서는 안 됩니다.

작업마다 독립된 HOME 만들기

Git은 HOME을 기준으로 사용자 수준 설정 경로를 결정합니다. 모든 작업이 실행 사용자의 HOME을 공유하면 .gitconfig, credential helper 설정, 여러 도구의 상태까지 함께 공유하게 됩니다. 더 안전한 방법은 작업마다 권한이 700인 임시 HOME을 만들고 Git 전역 설정 파일을 명시적으로 지정하는 것입니다.

set -eu

ORIGINAL_HOME="$HOME"
JOB_HOME="$(mktemp -d "${TMPDIR%/}/git-job.XXXXXX")"
chmod 700 "$JOB_HOME"

export HOME="$JOB_HOME"
export XDG_CONFIG_HOME="$JOB_HOME/.config"
export GIT_CONFIG_GLOBAL="$JOB_HOME/.gitconfig"
export GIT_TERMINAL_PROMPT=0

mkdir -p "$XDG_CONFIG_HOME"

이 방법은 시스템 수준 Git 설정까지 격리하지는 않으므로 helper 체인을 직접 초기화해야 합니다. Git은 여러 credential.helper 값을 지원합니다. 먼저 빈 값을 기록하면 우선순위가 낮은 설정에서 상속된 helper를 지운 뒤 이번 작업에서 사용할 구현만 추가할 수 있습니다.

저장소까지 임시 HOME에 넣지 않기

HOME은 자격 증명과 도구 상태의 경계이며 작업 공간 역할까지 맡을 필요는 없습니다. 소스 코드 디렉터리는 계속 runner의 작업 공간에서 관리해야 용량을 제어하고 산출물을 수집하기 쉽습니다. 두 영역을 분리하면 HOME을 정리할 때 빌드 결과를 실수로 삭제하지 않고, 작업 공간을 정리할 때 사용자 수준 인증 설정을 빠뜨리지 않을 수 있습니다.

파이프라인에 동시 실행이 필요하다면 각 동시 실행 슬롯에서 mktemp를 한 번씩 호출해야 하며, 저장소 이름을 기준으로 고정 디렉터리를 재사용해서는 안 됩니다. 고정 디렉터리는 비정상 종료 후 다음 작업에 상속될 수 있고, 두 작업이 같은 .gitconfig를 동시에 수정하게 만들 수도 있습니다.

Git이 요청할 때만 토큰 제공하기

토큰은 CI의 비밀 변수 기능을 통해 환경에 주입하고, 스크립트에서는 변수 이름만 참조해야 합니다. clone URL에 토큰을 넣거나 git config --global http.extraHeader로 인증 헤더를 장기간 저장해서는 안 됩니다.

다음 helper는 Git의 get 요청에만 응답합니다. 설정 파일에 저장되는 것은 변수를 참조하는 로직이지 토큰 값이 아닙니다.

: "${CI_GIT_USER:?CI_GIT_USER is required}"
: "${CI_GIT_TOKEN:?CI_GIT_TOKEN is required}"

git config --global credential.helper ""
git config --global --add credential.helper \
'!f() {
  if [ "$1" = get ]; then
    printf "username=%s
password=%s
" \
      "$CI_GIT_USER" "$CI_GIT_TOKEN"
  fi
}; f'

명령 에코와 대화형 대체 동작 끄기

비밀 변수를 다루기 전에 반드시 set +x를 실행해야 합니다. 그렇지 않으면 shell이 변수를 확장한 명령을 로그에 기록합니다. GIT_TERMINAL_PROMPT=0도 중요합니다. 토큰이 없거나 유효하지 않으면 작업이 명확하게 실패해야 하며, 보이지 않는 대화형 프롬프트에서 멈춰서는 안 됩니다.

빌드 래퍼가 환경 변수를 자동으로 덤프하는지도 확인해야 합니다. 진단 정보에는 문자열 길이가 0보다 큰지 확인하는 방식으로 변수의 설정 여부만 출력해야 합니다. 변수 내용, 인증 헤더, 전체 원격 URL을 출력해서는 안 됩니다.

쓰기 권한이 필요한 자동화 작업에서는 소스 코드 읽기와 산출물 push에 서로 다른 자격 증명을 사용해야 합니다. checkout만 수행하는 작업에는 쓰기 권한이 필요하지 않습니다. 릴리스 작업에도 대상 저장소와 필요한 작업 범위를 넘어서는 권한을 부여해서는 안 됩니다.

trap으로 모든 종료 경로 처리하기

스크립트 마지막에만 rm -rf를 작성하면 중간 실패, 시간 초과로 인한 종료, 수동 취소를 처리할 수 없습니다. 임시 디렉터리를 만든 직후 trap을 등록하고, 정리 함수에서는 먼저 변수를 비운 다음 작업 HOME을 삭제해야 합니다.

cleanup_git_credentials() {
  set +e
  unset CI_GIT_TOKEN CI_GIT_USER
  if [ -n "${JOB_HOME:-}" ] && [ -d "$JOB_HOME" ]; then
    rm -rf "$JOB_HOME"
  fi
}

trap cleanup_git_credentials EXIT HUP INT TERM

정리 함수는 반복 실행할 수 있어야 하며, 디렉터리가 이미 없어도 오류를 내서는 안 됩니다. 삭제 대상은 변수가 비어 있지 않고 실제로 디렉터리를 가리킨다는 두 조건을 모두 만족해야 합니다. 범위가 넓은 키체인 삭제 명령을 사용하거나 로그인 키체인 전체를 비우지 마세요. 같은 실행 사용자에게 필요한 다른 항목이 들어 있을 수 있습니다.

이전 파이프라인에서 영구 helper를 사용했다면 먼저 호스트와 계정을 기준으로 정확히 조사한 뒤 일회성 마이그레이션을 계획해야 합니다. 새 방식과 기존 방식을 함께 운영하는 동안에는 작업마다 현재 helper의 출처를 확인해야 합니다. 임시 HOME을 활성화했더라도 시스템 래퍼 스크립트가 이전 설정을 다시 복사할 수 있기 때문입니다.

잔류 검사를 빌드 게이트로 만들기

정리 스크립트에서 오류가 발생하지 않았다는 사실만으로 정리가 성공했다고 판단할 수는 없습니다. runner 외부에 종료 후 검사를 추가하거나 executor가 작업을 회수할 때 다음 항목을 검증하는 것이 좋습니다.

  • 임시 HOME이 삭제되었는지
  • 작업 공간의 .git/config에 인증 헤더나 사용자 정보가 포함된 원격 URL이 없는지
  • 작업 로그에 비밀 변수의 알려진 지문이 없는지
  • Git 설정 출처에 예상된 시스템 설정과 이번 임시 설정만 포함되는지
  • 이후 실행되는 빈 작업이 이전 작업의 저장소 자격 증명을 읽을 수 없는지

테스트 토큰에는 권한이 없는 고정 표식 값을 설정할 수 있습니다. 격리된 환경에서 실패 사례를 실행한 뒤 로그와 파일 시스템을 스캔합니다. 여기서 확인하는 것은 “표식이 유출되었는지”이며 실제 토큰의 유효성을 검증하는 것이 아닙니다. 실패 사례에는 최소한 clone 실패, 빌드 명령 종료, 종료 신호 수신, 정리 함수의 반복 실행이 포함되어야 합니다.

여러 팀이 하나의 물리 노드를 공유한다면 실행 사용자를 두 번째 경계로 사용해야 합니다. 임시 HOME은 작업 수준의 잔류 문제를 해결합니다. 독립된 사용자는 서로 다른 신뢰 영역 사이에서 프로세스, 파일 권한, 키체인의 경계를 분리합니다. 두 계층을 함께 사용해야만 스크립트 정리를 유일한 방어선으로 삼지 않을 수 있습니다.

최종 검수 기준은 간단합니다. 작업 시작 전에는 상속할 수 있는 자격 증명이 없어야 하고, 실행 중에는 필요할 때만 제공되어야 하며, 어떤 종료 경로에서도 정리가 수행되어야 합니다. 또한 다음 작업에서는 이전 토큰이 존재했다는 사실조차 확인할 수 없어야 합니다. 이 조건을 충족해야 Git 자격 증명이 장시간 실행되는 클라우드 Mac이 아니라 각 작업에 속한다고 할 수 있습니다.

자주 묻는 질문

작업 종료 시 토큰 환경 변수만 해제하면 충분한가요?

충분하지 않습니다. helper, 원격 URL, 전역 설정 또는 키체인에 이미 저장됐을 수 있으므로 각 위치를 별도로 검사하고 정리해야 합니다.

장기 실행 Mac CI 노드가 로그인 키체인을 공유해도 되나요?

서로 다른 저장소나 신뢰 경계에서는 공유하지 않는 편이 안전합니다. 최소 권한의 단기 토큰을 사용하고 필요하면 실행 사용자나 키체인을 분리하세요.

독점 클라우드 Mac

개발, 빌드 및 원격 데스크톱을 위한 독점 물리 머신을 선택하세요

Apple Silicon 구성 3가지를 비교하고, 주문 시 노드, 대여 기간 및 스토리지 추가 옵션을 선택할 수 있습니다.

대여 플랜 선택