CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서    

사설 Harbor(Private Registry) TLS 인증 오류 해결 방안

사설 인증서(Self-Signed Certificate)를 사용하는 Harbor 환경에서 docker push 및 buildkit 빌드 시 발생하는 x509: certificate signed by unknown authority 오류에 대한 최종 해결 가이드입니다.

1. 증상 (Symptoms)

daemon.json이나 buildkitd.toml에 insecure 설정을 완료했음에도 불구하고, 이미지를 Push하거나 빌드할 때 다음과 같은 에러가 발생하며 중단됩니다.

failed to authorize: failed to fetch oauth token: Post "https://harbor.cnap.dev/service/token": tls: failed to verify certificate: x509: certificate signed by unknown authority

2. 원인 분석 (Root Cause)

insecure 설정만으로 해결되지 않는 이유는 Docker Registry v2의 인증 구조 때문입니다.

실제 요청 흐름 (BuildKit 기준)

  1. BuildKit → Harbor Registry: insecure = true 설정으로 정상 접근
  2. Harbor → Token 발급 요구: WWW-Authenticate 헤더를 반환하며, 여기서 token service URL이 HTTPS로 지정됨
  3. BuildKit → Token Service (HTTPS): 이 단계는 registry insecure 설정과 무관하게 항상 HTTPS로 호출됨
  4. 실패: 사설 CA 인증서를 BuildKit이 신뢰하지 않아 TLS handshake 단계에서 차단됨

핵심 원인

Docker Registry v2 표준은 이미지 전송(Registry)과 권한 확인(Token Service)을 분리합니다. insecure = true는 이미지 pull/push에 해당하는 registry endpoint에만 적용되며, auth/token endpoint는 별개의 HTTPS 요청으로 동작합니다. 사설 CA 인증서는 시스템 기본 신뢰 저장소에 등록되어 있지 않으므로, 토큰 서버 접속 시 TLS 핸드쉐이크가 실패합니다.

데이터 전송 경로는 insecure 설정으로 우회할 수 있지만, 인증 토큰 요청은 시스템의 CA 신뢰 없이는 통과할 수 없습니다.

HTTP 전용으로 Harbor를 구성한 경우

Harbor를 HTTPS 없이 순수 HTTP(포트 80)로 구성한 경우에는 위 문제가 발생하지 않습니다. token service URL도 HTTP로 반환되므로 TLS 핸드쉐이크 자체가 없기 때문입니다. 이 경우 registry 주소를 harbor.example.com:80처럼 포트를 명시하고, insecure = true 설정만으로 정상 동작합니다.

3. 해결 방법: 시스템 레벨 인증서 등록

호스트 OS와 BuildKit 빌더 컨테이너가 해당 사설 인증서를 “신뢰할 수 있는 정식 기관"으로 인식하도록 등록하는 것이 가장 근본적인 해결책입니다.

3.1 호스트 OS 설정 (Docker Daemon용)

Docker 데몬이 구동되는 호스트 머신에 인증서를 등록하여 docker push 문제를 해결합니다.

# 1. 시스템 인증서 경로로 Harbor CA 인증서 복사 (.crt 확장자 필수)
sudo mkdir -p /usr/local/share/ca-certificates
sudo cp ./ca.crt /usr/local/share/ca-certificates/harbor-ca.crt

# 2. 시스템 신뢰 목록 업데이트 (인증서 병합)
sudo update-ca-certificates

# 3. Docker 서비스 재시작 (변경 사항 반영을 위해 필수)
sudo systemctl restart docker

3.2 BuildKit 빌더 설정 (buildx용)

BuildKit은 별도의 컨테이너에서 동작하므로, 해당 컨테이너 내부에도 인증서 설정이 필요합니다.

방법 A: 실행 중인 빌더에 즉시 주입 (임시)

# 빌더 컨테이너로 인증서 복사 및 업데이트
docker cp /usr/local/share/ca-certificates/harbor-ca.crt <빌더_컨테이너명>:/usr/local/share/ca-certificates/
docker exec <빌더_컨테이너명> update-ca-certificates
docker restart <빌더_컨테이너명>

방법 B: 커스텀 이미지를 통한 영구 적용 (권장)

빌더를 재생성하더라도 설정이 유지되도록 인증서가 내장된 전용 이미지를 사용합니다.

1) Dockerfile 작성

FROM moby/buildkit:latest
COPY ./ca.crt /usr/local/share/ca-certificates/harbor-ca.crt
RUN update-ca-certificates

2) 커스텀 빌더 생성

docker build -t custom-buildkit:local .
docker buildx create --name my-builder --driver-opt image=custom-buildkit:local --use

4. 최종 검증 (Validation)

정상적으로 설정되었는지 아래 두 가지 방법으로 확인합니다.

네트워크 레벨 검증:

curl -v https://harbor.cnap.dev/service/token

SSL certificate verify ok 문구가 뜨면 시스템 등록이 성공한 것입니다.

도커 동작 확인:

# 이미지 빌드 및 푸시 테스트
docker buildx build --push -t harbor.cnap.dev/library/test:latest .

x509 에러 없이 진행된다면 모든 인증 체인이 정상적으로 연결된 것입니다.


참고: /etc/docker/certs.d/ 경로 설정이 동작하지 않는다면, 대부분 해당 인증서가 CA 인증서가 아닌 서버 인증서(Self-signed)이거나 시스템 리다이렉션 과정에서 신뢰를 잃었기 때문입니다. 이때는 위 가이드처럼 **시스템 레벨(update-ca-certificates)**에 등록하는 것이 가장 확실합니다.