파이프라인이 실패하면 팀은 보통 전체 로그, 테스트 첨부 파일, 결과 번들을 공유 스토리지에 업로드한 뒤 다른 엔지니어에게 문제 분석을 넘깁니다. 실제로 위험한 것은 눈에 잘 띄는 TOKEN= 한 줄이 아니라, 디버그 모드에서 그대로 출력된 명령 인수, 요청 헤더, 임시 서명 경로, 테스트 코드에 포함된 환경 스냅샷입니다. 클라우드 Mac이 장시간 여러 작업을 처리하면 이러한 정보가 캐시와 아카이브에도 들어갈 수 있으므로, 로그 비식별화는 출력 소스, 파이프라인, 업로드 게이트라는 세 지점에서 이루어져야 합니다.
먼저 로그 데이터 흐름 파악하기
모든 텍스트를 한 번에 처리하는 정규식부터 작성하지 마세요. 먼저 정보가 어디에서 유입되고, 어떤 프로세스를 거치며, 최종적으로 어디에 저장되는지 정리해야 합니다.
| 유입 지점 | 일반적인 유출 방식 | 우선 조치 |
|---|---|---|
| Shell | set -x가 확장된 변수를 그대로 출력 |
민감한 단계 전에 추적 비활성화 |
| 명령 인수 | 키와 토큰이 프로세스 인수에 직접 노출 | 권한이 제한된 임시 파일이나 표준 입력 사용 |
| 환경 변수 | 진단 스크립트가 전체 환경을 출력 | 허용 목록에 있는 항목만 출력 |
| 테스트 | 요청 헤더와 계정 데이터가 첨부 파일에 기록 | 테스트 헬퍼 계층에서 일괄 정리 |
| 결과 번들 | 스크린샷, 활동 로그, 진단 파일이 통째로 업로드 | 내보낸 뒤 스캔하고 게시 |
비식별화한다고 이미 유출된 키가 다시 안전해지는 것은 아닙니다. 실제 값이 공유 가능한 로그에 들어간 사실이 확인되면 해당 산출물의 게시를 중단하고, 키 유형에 맞게 즉시 교체해야 합니다.
먼저 OakVM 노드에서 작업마다 별도의 디렉터리를 만들고 현재 사용자만 읽고 쓸 수 있도록 권한을 설정합니다. 로그, 결과 번들, 업로드 대기 파일을 분리해 두어 스캔을 마친 파일과 원본 파일이 섞이지 않게 합니다.
umask 077
run_id="$(date -u +%Y%m%dT%H%M%SZ)-$$"
root="$HOME/ci-runs/$run_id"
mkdir -p "$root/raw" "$root/safe" "$root/results"
출력 소스에서 유출 경로 차단하기
비밀값을 명령 인수에 넣지 않기
프로세스 인수는 진단 도구, 충돌 기록 또는 동일한 사용자가 실행한 스크립트에서 읽힐 수 있습니다. 내부 업로드 스크립트를 호출할 때 --token "$TOKEN"처럼 작성하지 마세요. 더 안전한 방법은 스크립트가 권한이 600인 임시 파일에서 값을 읽도록 하고 사용 직후 삭제하는 것입니다. 도구가 표준 입력을 지원한다면 표준 입력으로 전달할 수도 있습니다.
Shell 추적은 기본적으로 비활성화해야 합니다. 일반 명령을 관찰해야 할 때도 민감한 변수를 다루지 않는 짧은 구간에서만 활성화하고, 자격 증명을 불러오기 전에 set +x를 실행합니다. 전체 환경을 그대로 출력하는 것도 금지하고, 진단 정보는 허용 목록에 있는 항목만 출력합니다.
printf 'PATH=%s
' "$PATH"
printf 'DEVELOPER_DIR=%s
' "$DEVELOPER_DIR"
xcodebuild -version
sw_vers
경로도 민감한 정보로 취급해야 합니다. 사용자 이름, 프로젝트 코드명, 임시 서명 디렉터리는 팀 구조를 드러낼 수 있습니다. 로그를 게시하기 전에 작업 루트 디렉터리를 $WORKSPACE로 치환할 수 있지만, 문제 분석에 필요한 파일 이름과 줄 번호는 유지해야 합니다.
실패를 숨기지 않는 비식별화 파이프라인 구축하기
치환 규칙은 password나 secret 같은 필드 이름만 찾기보다 실제 비밀값을 우선적으로 일치시켜야 합니다. 필드 이름 기반 규칙은 오탐이 발생하기 쉽고, 레이블이 없는 토큰도 차단하지 못합니다. 다음 필터는 접근이 제한된 파일에서 마스킹할 값을 읽고 길이가 긴 값부터 짧은 값 순서로 치환하여, 짧은 값 때문에 긴 값의 일치가 깨지는 것을 방지합니다.
import os
import sys
from pathlib import Path
secret_file = Path(os.environ["REDACT_FILE"])
values = [
line.rstrip("
")
for line in secret_file.read_text(encoding="utf-8").splitlines()
if line.strip()
]
values.sort(key=len, reverse=True)
for line in sys.stdin:
for value in values:
line = line.replace(value, "[REDACTED]")
sys.stdout.write(line)
빌드를 실행할 때는 xcodebuild의 실제 종료 코드를 보존해야 합니다. 파이프라인 끝에 있는 tee의 상태만 확인하면 실패한 빌드가 성공으로 잘못 판정될 수 있습니다.
#!/bin/bash
set -o pipefail
set +x
REDACT_FILE="$root/secrets.txt" \
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-resultBundlePath "$root/results/App.xcresult" \
build 2>&1 |
python3 ci/redact.py |
tee "$root/safe/build.log"
statuses=("${PIPESTATUS[@]}")
exit "${statuses[0]}"
secrets.txt에는 반드시 600 권한을 적용하고 작업 종료 훅에서 삭제해야 합니다. 여러 줄로 된 개인 키는 줄 단위 치환에 적합하지 않습니다. 올바른 방법은 처음부터 출력되지 않게 하고, 텍스트에 나타날 가능성이 있는 안정적인 식별값만 마스킹 파일에 추가하는 것입니다.
결과 번들과 테스트 첨부 파일 스캔하기
텍스트 로그가 검사를 통과했다고 해서 전체 작업을 업로드해도 된다는 뜻은 아닙니다. 결과 번들에는 테스트 스크린샷, 실패 첨부 파일, 활동 로그, 진단 정보가 포함될 수 있습니다. 먼저 격리된 디렉터리로 복사하고, 실제로 공유해야 하는 내용만 내보낸 뒤 내보낸 디렉터리를 스캔하세요. 전체 결과 번들을 기본값으로 게시해서는 안 됩니다.
두 종류의 규칙으로 오탐 줄이기
첫 번째 규칙은 현재 작업의 실제 비밀값과 일치하며, 발견 즉시 차단합니다. 두 번째 규칙은 인증 요청 헤더, 개인 키 경계, 자격 증명이 포함된 URL, 긴 토큰으로 의심되는 문자열 같은 고위험 구조를 검사합니다. 구조 규칙에 일치하는 항목은 사람이 검토하도록 넘겨야 합니다. 소스 파일 이름이나 테스트 설명에도 token이라는 단어가 포함될 수 있으므로, 모든 token 문자열을 자동으로 유출로 판정해서는 안 됩니다.
스캔 보고서에는 파일 경로, 줄 번호, 규칙 번호만 기록하고, 일치한 원문을 보고서에 다시 복사하지 마세요. 바이너리 첨부 파일은 먼저 유형을 식별해야 합니다. 안전하게 분석할 수 없는 파일은 strings로 내용을 추출해 곧바로 공개하지 말고, 기본적으로 공유 산출물에서 제외합니다.
비식별화를 게시 게이트로 만들기
유지보수 가능한 게이트에는 최소한 빌드 종료 코드, 정확한 값 스캔, 구조 규칙 검토, 산출물 허용 목록이라는 네 가지 상태가 포함되어야 합니다. 이 중 하나라도 완료되지 않으면 업로드 단계를 시작해서는 안 됩니다.
각 작업이 끝날 때 다음 항목을 확인하는 것이 좋습니다.
- 원본 디렉터리 권한이 여전히 현재 사용자만 접근할 수 있도록 설정되어 있는지
- 로그에 작업 루트 디렉터리, 인증 헤더 또는 실제 비밀값이 남아 있지 않은지
- 결과 번들에 불필요한 스크린샷, 네트워크 응답, 환경 스냅샷이 포함되어 있지 않은지
- 업로드 목록에 비식별화된 로그, 필요한 보고서, 명시적으로 선택한 첨부 파일만 있는지
- 정리 스크립트가 비밀 파일은 삭제하되, 추가 분석에 필요한 안전한 복사본은 삭제하지 않는지
마지막으로 역방향 테스트를 수행합니다. 임시 작업에 테스트 전용 가짜 토큰을 주입하고 일반 출력, 오류 출력, 테스트 첨부 파일을 각각 통과하게 하여 게이트가 이를 차단하는지 확인합니다. 그런 다음 빌드가 의도적으로 0이 아닌 상태를 반환하게 하여 비식별화 파이프라인이 실패를 성공으로 바꾸지 않는지 검증합니다. 이 두 종류의 테스트를 모두 통과해야 로그 보안 체계가 실제 엔지니어링 프로세스에 정착했다고 볼 수 있습니다.
자주 묻는 질문
로그 업로드 직전에 문자열만 치환하면 충분한가요?
충분하지 않습니다. 셸 출력과 임시 파일에 이미 값이 기록될 수 있으므로 디버그 출력을 끄고 비밀값을 명령행 인자에 넣지 않는 조치가 먼저 필요합니다.
필터 파이프라인에서 xcodebuild 종료 코드는 어떻게 보존하나요?
Bash에서 pipefail을 활성화한 뒤 파이프라인 직후 PIPESTATUS[0]을 저장합니다. 마지막 tee 프로세스의 성공 여부를 빌드 결과로 사용하면 안 됩니다.
텍스트 로그 외에 무엇을 검사해야 하나요?
결과 번들, 테스트 첨부 파일, 진단 압축 파일, 내보내기 설정 사본과 사용자 정의 보고서를 함께 검사해야 합니다.
다음 빌드를 독립 물리 노드에서 실행하세요
OakVM M4 또는 OakVM M4 Pro를 선택하고, 가상화되지 않은 클라우드 Mac을 일·주·월·분기 단위로 대여하세요. 노드의 실제 이용 가능 여부는 콘솔에서 실시간으로 확인할 수 있습니다.