개요
자체 서비스 납품으로 인해 취약점 점검을 진행해야했다.
취약점 점검 서비스는 github 에 연동해서 검사가 가능했는데, 우리팀은 gitlab 을 사용하고 있었다.
gitlab의 프로젝트를 연동시키기 위해서 처음에 고려했던 건 Repository mirroring 이었다.
문제가 하나 있었는데, 특정 브랜치만 연동할 방법이 없다는 것이었다. 따라서 CI/CD 파이프라인을 선택할 수 밖에 없었다.
따라서 GitLab서버에 GitLab Runner를 설치하고 프로젝트 루트 디렉토리에 .gitlab-ci.yml을 작성해서 특정 브랜치에 push가 발생하면 GitHub으로 자동 미러링되도록 했다.
환경
항목 내용
| GitLab 서버 | Rocky Linux 9.6, dev.test.co.kr |
| GitLab Runner | v16.11.0 (shell executor) |
| 동기화 대상 브랜치 | v1_test |
| GitHub 레포지토리 | test-lab/dev01 |
구성 목표
GitLab (dev.test.co.kr)
└── push to v1_test
└── CI/CD Pipeline 실행
└── git push → GitHub (test-lab/dev01) main 브랜치
GitLab Runner 설치 및 등록
Runner 설치 확인
which gitlab-runner && gitlab-runner --version
Runner 등록 명령어
gitlab-runner register \
--non-interactive \
--url https://dev.test.co.kr \
--token glrt-xxxxxxxxxxxxxx \
--executor shell \
--description git-runner
Runner 서비스 등록 및 시작
바이너리로 직접 설치한 경우 systemd 서비스 등록이 필요하다.
gitlab-runner install --user root --working-directory /root
gitlab-runner start
.gitlab-ci.yml 최종 구성
stages:
- sync
github_sync:
stage: sync
only:
- v1_test
variables:
GIT_DEPTH: 0
script:
- git push --force https://test-lab:${GITHUB_TOKEN}@github.com/test-lab/dev01.git HEAD:main
GitLab CI/CD 변수 설정
설정 → CI/CD → Variables에서 아래와 같이 등록한다.
Key Value 옵션
| GITHUB_TOKEN | GitHub PAT 토큰 | Masked ✅, Protected ❌, Expanded ❌ |
주의: Masked를 반드시 체크해야 로그에 토큰이 노출되지 않는다.
트러블슈팅
1. TLS 인증서 오류 — Legacy Common Name
오류 메시지
tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead
원인
Go 1.15 이후부터 SAN(Subject Alternative Name)이 없는 Legacy Common Name 방식의 인증서를 거부한다. 내부망 자체 서명 인증서가 SAN 없이 발급되어 있었다.
조치
기존 인증서를 백업하고 SAN을 포함한 새 인증서를 재발급한다.
# 백업
mkdir -p /etc/gitlab/ssl/backup_$(date +%Y%m%d)
cp /etc/gitlab/ssl/dev.test.co.kr.crt /etc/gitlab/ssl/backup_$(date +%Y%m%d)/
cp /etc/gitlab/ssl/dev.test.co.kr.key /etc/gitlab/ssl/backup_$(date +%Y%m%d)/
# SAN 포함 인증서 재발급
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/gitlab/ssl/dev.test.co.kr.key \
-out /etc/gitlab/ssl/dev.test.co.kr.crt \
-subj '/C=KR/ST=seoul/L=seoul/O=test/OU=dev/CN=dev.test.co.kr' \
-addext 'subjectAltName=DNS:dev.test.co.kr'
# nginx 재시작
gitlab-ctl restart nginx
2. TLS 인증서 오류 — Unknown Authority
오류 메시지
tls: failed to verify certificate: x509: certificate signed by unknown authority
원인
자체 서명 인증서가 시스템 CA 신뢰 저장소에 등록되어 있지 않다.
조치
Rocky Linux의 CA 신뢰 저장소에 인증서를 등록한다.
cp /etc/gitlab/ssl/dev.test.co.kr.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust
3. apk 명령어를 찾을 수 없음
오류 메시지
bash: apk: 명령어를 찾을 수 없음
원인
.gitlab-ci.yml에 image: alpine:latest와 apk add 명령이 작성되어 있었다. image 키는 Docker executor 전용 설정이며, shell executor에서는 무시된다. 따라서 서버 OS인 Rocky Linux에서 Alpine 전용 패키지 매니저 apk를 실행하려 해서 실패한다.
조치
shell executor 환경에서는 서버에 이미 git이 설치되어 있으므로 image와 before_script를 제거한다.
# 변경 전
image: alpine:latest
before_script:
- apk add --no-cache git
# 변경 후 — 불필요하므로 제거
4. GitHub 인증 실패
오류 메시지
remote: Invalid username or token. Password authentication is not supported for Git operations.
원인
GITHUB_TOKEN 변수에 Protected 옵션이 설정되어 있었다. Protected 변수는 Protected 브랜치에서만 주입되므로, 해당 브랜치가 Protected로 설정되지 않은 경우 변수 값이 빈 문자열로 전달된다.
조치
GITHUB_TOKEN 변수 편집 → Protected 체크 해제
5. 토큰 평문 노출
오류 메시지
error: 레퍼런스를 'https://test-lab:ghp_xxxxxxxxxxxx'에 푸시하는데 실패했습니다
원인
GITHUB_TOKEN 변수에 Expanded 옵션이 활성화되어 있어 URL 파싱이 깨지면서 토큰 값이 에러 메시지에 평문으로 출력됐다.
조치
- GitHub에서 노출된 토큰을 즉시 폐기(revoke)하고 새 PAT를 발급한다.
- GitLab CI/CD 변수를 아래 옵션으로 재등록한다.
옵션 설정
| Masked | ✅ 체크 (로그 마스킹) |
| Protected | ❌ 해제 |
| Expanded | ❌ 해제 |
6. shallow clone으로 인한 push 실패
오류 메시지
remote: fatal: did not receive expected object
error: 리모트 묶음 풀기 실패: index-pack failed
원인
GitLab Runner의 기본 GIT_DEPTH가 20으로 설정되어 있어 커밋 히스토리를 20개만 가져온다. GitHub에 push할 때 참조하는 커밋 객체가 shallow clone 범위 밖에 있어 실패한다.
조치
.gitlab-ci.yml에 GIT_DEPTH: 0을 설정해 전체 히스토리를 clone한다.
variables:
GIT_DEPTH: 0
정리
순서 트러블 원인 조치
| 1 | TLS Legacy CN 오류 | SAN 없는 자체 서명 인증서 | SAN 포함 인증서 재발급 |
| 2 | TLS Unknown Authority | CA 신뢰 저장소 미등록 | update-ca-trust |
| 3 | apk 명령어 없음 | shell executor에서 Alpine 명령 사용 | image, before_script 제거 |
| 4 | GitHub 인증 실패 | 변수 Protected 옵션 활성화 | Protected 해제 |
| 5 | 토큰 평문 노출 | 변수 Expanded 옵션 활성화 | 토큰 폐기 후 재발급, Expanded 해제, Masked 체크 |
| 6 | push 실패 (객체 누락) | shallow clone (depth=20) | GIT_DEPTH: 0 설정 |