콘텐츠로 이동






03.Harbor 설치가이드

Harbor v2 설치 및 운영 가이드

  1. 대상: 단일 Linux 서버에서 Docker Compose 기반으로 Harbor를 설치하는 경우
  2. 범위: 사설 CA 기반 HTTPS, Trivy 온라인·오프라인 DB 관리, 신뢰 CA 배포, Podman 구성
  3. 설치 전 Harbor Releases에서 최신 안정 버전과 릴리스 노트를 확인한다.

개요 및 사전 준비

  1. Harbor는 OCI/Docker 이미지, Helm OCI Chart, SBOM 등 클라우드 네이티브 아티팩트를 저장·배포하는 컨테이너 레지스트리다.
  2. 프로젝트별 권한, Robot Account, Trivy 취약점 스캔, 이미지 복제, Retention Policy 등의 기능을 제공한다.
  3. 이 문서는 Docker Compose 기반 단일 노드 설치를 다룬다. 고가용성 구성이 필요하면 Docker Compose 대신 Harbor 공식 Helm Chart로 Kubernetes에 설치한다.

이전 Harbor 1.x 문서와 차이점

기존 구성 최신 권장 구성
harbor.cfg harbor.yml
Clair Trivy
--with-clair --with-trivy
ChartMuseum Helm OCI Registry
--with-chartmuseum 사용하지 않음
Docker Compose v1 바이너리 Docker Compose v2 (docker compose)
Docker iptables: false 설정하지 않음

Info

Docker의 "iptables": false 설정은 Docker가 컨테이너 NAT 및 포트 포워딩 규칙을 생성하지 못하게 할 수 있으므로 일반적인 Harbor 환경에서는 사용하지 않는다.

권장 사양

항목 최소 사양 권장 사양
CPU 2 vCPU 4 vCPU 이상
Memory 4 GB 8 GB 이상
Disk 40 GB 160 GB 이상
OS Docker 지원 Linux Rocky Linux/RHEL/Ubuntu 최신 LTS
Docker Engine 20.10.10-ce 이상 최신 안정 버전
Docker Compose v1.18 이상 Compose v2 plugin
  1. 이미지, PostgreSQL, Redis, 로그, Trivy DB가 누적되므로 운영 환경에서는 OS 루트 파티션과 분리한 데이터 볼륨을 사용한다.

  2. Docker와 Compose를 확인한다.

    $> docker version
    $> docker compose version
    

포트 및 방화벽

포트 프로토콜 용도
80 HTTP HTTP 리다이렉션 또는 테스트
443 HTTPS Portal, API, Docker/OCI Registry
9090 HTTP Prometheus 메트릭 사용 시

설치 진행하기

  1. 도구 및 디렉터리 준비
    $> sudo dnf install -y curl tar openssl ca-certificates
    
    # Ubuntu/Debian
    # sudo apt-get update
    # sudo apt-get install -y curl tar openssl ca-certificates
    
    $> sudo mkdir -p /opt/harbor
    $> sudo mkdir -p /data/harbor
    $> sudo mkdir -p /data/cert
    $> sudo chmod 700 /data/cert
    

사설 CA 인증서 생성 및 trust-ca 등록 (필요시)

  1. 운영 환경에서는 HTTPS를 사용해야 한다. 이 절에서는 10년 유효기간의 사설 Root CA를 만들고, 해당 CA로 Harbor 서버 인증서를 발급한다.

Info

사설 CA를 신뢰하지 않는 Docker 또는 Kubernetes 노드에서는 x509: certificate signed by unknown authority 오류가 발생한다. Harbor 서버뿐 아니라 이미지를 Push/Pull하는 모든 노드에 Root CA를 배포해야 한다.

  1. 환경 변수 설정을 위해 아래 값은 실제 환경에 맞게 변경한다. HARBOR_FQDN은 DNS에서 Harbor IP로 해석되어야 한다.
    $> export HARBOR_FQDN="harbor.example.internal"
    $> export HARBOR_IP="10.10.10.20"
    $> export CERT_DIR="/data/cert"
    
    # 10년
    $> export CA_DAYS=3650
    $> export SERVER_DAYS=3650
    
    $> sudo mkdir -p "${CERT_DIR}"
    

Info

공인 CA 인증서는 브라우저 및 CA 정책상 짧은 유효기간이 요구될 수 있다. 여기의 10년 인증서는 사설 PKI 및 내부 신뢰 환경을 전제로 한다.

Info

Root CA의 개인 키인 harbor-root-ca.key는 Harbor 서버에 둘 필요가 없다. 별도의 PKI 서버에서 인증서를 발급했다면 최종 서버 인증서와 서버 개인 키만 /data/cert에 전달한다.

  1. 10년 Root CA 생성

    • Root CA 개인 키는 유출 시 전체 신뢰 체인이 무효화되므로, 가능하면 Harbor 서버와 분리된 보안 저장소 또는 PKI 서버에서 보관한다.
      $> cd "${CERT_DIR}"
      $> sudo openssl genrsa -out harbor-root-ca.key 4096
      $> sudo chmod 600 harbor-root-ca.key
      $> sudo openssl req -x509 -new -nodes \
        -key harbor-root-ca.key \
        -sha256 \
        -days "${CA_DAYS}" \
        -out harbor-root-ca.crt \
        -subj "/C=KR/ST=Seoul/L=Seoul/O=Example/OU=Platform/CN=Example Harbor Root CA"
      
  2. 생성 결과를 확인한다.

    $> openssl x509 -in harbor-root-ca.crt -noout -subject -issuer -dates
    

  3. SAN 포함 Harbor 서버 인증서 생성

    • 현대 TLS 클라이언트는 CN만으로 호스트를 검증하지 않고 SAN(Subject Alternative Name) 을 확인한다. Harbor FQDN과 IP를 SAN에 포함한다.
      $> cd "${CERT_DIR}"
      
      $> sudo openssl genrsa -out "${HARBOR_FQDN}.key" 4096
      $> sudo chmod 600 "${HARBOR_FQDN}.key"
      
      sudo tee "${HARBOR_FQDN}.cnf" > /dev/null <<EOF
      [req]
      distinguished_name = req_distinguished_name
      req_extensions = v3_req
      prompt = no
      
      [req_distinguished_name]
      C = KR
      ST = Seoul
      L = Seoul
      O = Example
      OU = Platform
      CN = ${HARBOR_FQDN}
      
      [v3_req]
      keyUsage = critical, digitalSignature, keyEncipherment
      extendedKeyUsage = serverAuth
      subjectAltName = @alt_names
      
      [alt_names]
      DNS.1 = ${HARBOR_FQDN}
      IP.1 = ${HARBOR_IP}
      EOF
      
      $> sudo openssl req -new \
        -key "${HARBOR_FQDN}.key" \
        -out "${HARBOR_FQDN}.csr" \
        -config "${HARBOR_FQDN}.cnf"
      
      $> sudo openssl x509 -req \
        -in "${HARBOR_FQDN}.csr" \
        -CA harbor-root-ca.crt \
        -CAkey harbor-root-ca.key \
        -CAcreateserial \
        -out "${HARBOR_FQDN}.crt" \
        -days "${SERVER_DAYS}" \
        -sha256 \
        -extensions v3_req \
        -extfile "${HARBOR_FQDN}.cnf"
      
  4. 인증서의 SAN, 서명 체인, 유효기간을 확인한다.

    $> openssl x509 -in "${HARBOR_FQDN}.crt" -noout -text | \
      grep -A1 -E "Subject:|Subject Alternative Name"
    
    $> openssl verify \
      -CAfile harbor-root-ca.crt \
      "${HARBOR_FQDN}.crt"
    

  5. Harbor 서버 인증서 배치

    $> sudo chmod 644 "/data/cert/${HARBOR_FQDN}.crt"
    $> sudo chmod 600 "/data/cert/${HARBOR_FQDN}.key"
    

  6. Rocky Linux/RHEL trust-ca 등록

    1. Harbor 서버와 모든 Docker/Podman 클라이언트 노드에 적용한다.

      $> sudo cp /data/cert/harbor-root-ca.crt \
        /etc/pki/ca-trust/source/anchors/harbor-root-ca.crt
      
      $> sudo update-ca-trust extract
      

    2. 등록 여부를 확인한다.

      $> trust list | grep -i -A2 "Example Harbor Root CA"
      

    3. Ubuntu/Debian trust-ca 등록
      $> sudo cp /data/cert/harbor-root-ca.crt \
        /usr/local/share/ca-certificates/harbor-root-ca.crt
      
      $> sudo update-ca-certificates
      
  7. Docker는 시스템 trust store 외에 Registry FQDN별 인증서 경로를 사용하기 때문에 Registry 전용 CA 등록이 필요하다

    $> export HARBOR_FQDN="harbor.example.internal"
    $> sudo mkdir -p "/etc/docker/certs.d/${HARBOR_FQDN}"
    $> sudo cp /data/cert/harbor-root-ca.crt \
      "/etc/docker/certs.d/${HARBOR_FQDN}/ca.crt"
    $> sudo systemctl restart docker
    
  8. Harbor 주소에 포트가 포함되면 디렉터리에도 포트를 넣는다.

    /etc/docker/certs.d/harbor.example.internal:8443/ca.crt
    

Docker 로그인으로 검증한다.

$> docker login "${HARBOR_FQDN}"

  1. Kubernetes 런타임으로 containerd를 사용하는 경우 containerd Kubernetes 노드 인증서 등록절차가 필요하다
    $> export HARBOR_FQDN="harbor.example.internal"
    $> sudo mkdir -p "/etc/containerd/certs.d/${HARBOR_FQDN}"
    $> sudo cp /data/cert/harbor-root-ca.crt \
      "/etc/containerd/certs.d/${HARBOR_FQDN}/ca.crt"
    $> sudo tee "/etc/containerd/certs.d/${HARBOR_FQDN}/hosts.toml" > /dev/null <<EOF
    server = "https://${HARBOR_FQDN}"
    [host."https://${HARBOR_FQDN}"]
      capabilities = ["pull", "resolve", "push"]
      ca = "/etc/containerd/certs.d/${HARBOR_FQDN}/ca.crt"
    EOF
    $> sudo systemctl restart containerd
    

Harbor 설치와 기본 운영

  1. Harbor Installer 다운로드 수행

    $> export HARBOR_VERSION="v2.14.0"
    $> cd /opt/harbor
    $> sudo curl -fLO \
      "https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-online-installer-${HARBOR_VERSION}.tgz"
    $> sudo tar xzvf "harbor-online-installer-${HARBOR_VERSION}.tgz"
    $> cd harbor
    

    • 폐쇄망 환경에서는 인터넷 연결 구간에서 아래 파일을 내려받아 반입한다.
      $> sudo tar xzvf "harbor-offline-installer-${HARBOR_VERSION}.tgz"
      $> cd harbor
      
  2. harbor.yml 설정

    $> cd /opt/harbor/harbor
    
    $> sudo cp harbor.yml.tmpl harbor.yml
    $> sudo vi harbor.yml
    

  3. HTTPS 및 Trivy를 포함한 설정 예시다.

    hostname: harbor.example.internal
    
    https:
      port: 443
      certificate: /data/cert/harbor.example.internal.crt
      private_key: /data/cert/harbor.example.internal.key
    
    harbor_admin_password: "CHANGE_THIS_ADMIN_PASSWORD"
    
    database:
      password: "CHANGE_THIS_DATABASE_PASSWORD"
      max_idle_conns: 50
      max_open_conns: 100
    
    data_volume: /data/harbor
    
    trivy:
      ignore_unfixed: false
      security_check: vuln,config,secret
      skip_update: false
      insecure: false
    
    jobservice:
      max_job_workers: 10
    
    notification:
      webhook_job_max_retry: 10
    
    log:
      level: info
      local:
        rotate_count: 50
        rotate_size: 200M
        location: /var/log/harbor
    
    metric:
      enabled: true
      port: 9090
      path: /metrics
    

  • hostname에는 외부에서 접근 가능한 FQDN 또는 IP를 지정한다. localhost, 127.0.0.1, 0.0.0.0은 사용할 수 없다. harbor_admin_password는 최초 설치 시에만 적용된다.
  1. Trivy 포함 설치

    $> cd /opt/harbor/harbor
    $> sudo ./install.sh --with-trivy
    

  2. 설치 상태를 확인한다.

    $> docker compose ps
    $> docker compose logs -f --tail=100
    $> docker compose logs -f --tail=100 trivy-adapter
    

  3. 브라우저에서 접속한다.

    https://harbor.example.internal
    

  4. 초기 로그인 계정은 다음과 같다.

    ID: admin
    Password: harbor.yml의 harbor_admin_password 값
    

이미지 Push/Pull 테스트

  1. 먼저 Harbor Portal에서 Projects → + New Project로 이동해 platform 프로젝트를 생성한다.

    $> export HARBOR_FQDN="harbor.example.internal"
    $> docker login "${HARBOR_FQDN}"
    $> docker tag nginx:1.27 \
      "${HARBOR_FQDN}/platform/nginx:1.27"
    $> docker push "${HARBOR_FQDN}/platform/nginx:1.27"
    $> docker pull "${HARBOR_FQDN}/platform/nginx:1.27"
    

  2. Helm OCI Chart 사용 (ChartMuseum 방식 대신 Helm OCI Registry 방식을 사용한다.)

    $> helm registry login harbor.example.internal
    $> helm package mychart
    $> helm push mychart-0.1.0.tgz oci://harbor.example.internal/platform
    $> helm pull  oci://harbor.example.internal/platform/mychart --version 0.1.0
    

  3. 프로젝트 권한

    역할 주요 권한
    Limited Guest 이미지 Pull 가능, 프로젝트 정보 접근 제한
    Guest 읽기 및 Pull
    Developer Push 및 Pull
    Maintainer Developer 권한 및 일부 프로젝트 운영 권한
    Project Admin 멤버, 정책, Webhook 등 프로젝트 관리
  • CI/CD에는 개인 계정이나 admin 대신 Robot Account를 생성하고 최소 권한을 부여한다.

Trivy 온라인·오프라인 DB 관리

  1. Trivy 스캔의 정확도는 취약점 DB 최신성에 의존한다.

    데이터 용도 필요 여부
    Vulnerability DB OS 및 패키지 CVE 탐지 필수
    Java DB JAR 기반 Java 패키지 식별 Java 스캔 시 권장
    Checks Bundle IaC 및 설정 오류 검사 config 검사 시 필요
  2. 온라인 환경 DB 다운로드시 harbor.yml에서 다음을 설정한다.

    trivy:
      skip_update: false
      security_check: vuln,config,secret
    
    
    Trivy Adapter는 필요한 DB를 자동으로 내려받는다. 프록시 환경이라면 다음을 추가한다.
    
    yaml
    proxy:
      http_proxy: http://proxy.example.internal:3128
      https_proxy: http://proxy.example.internal:3128
      no_proxy: 127.0.0.1,localhost,core,registry,harbor.example.internal
    
    
    방화벽 또는 프록시에서는 다음 통신을 검토한다.
    
    text
    mirror.gcr.io
    ghcr.io
    pkg-containers.githubusercontent.com
    github.com
    github-releases.githubusercontent.com
    

  3. Trivy DB 다운로드 상태는 로그로 확인한다.

    $> cd /opt/harbor/harbor
    $> docker compose logs --tail=300 trivy-adapter | egrep -i "download|update|db|error"
    

오프라인 환경에서 DB관리

  1. 인터넷이 되는 중계 서버에서 Trivy CLI로 DB만 미리 내려받을 수 있다.

    $> mkdir -p ~/trivy-cache
    $> trivy image \
      --cache-dir ~/trivy-cache \
      --download-db-only
    

  2. 일반적인 핵심 파일 경로는 다음과 같다.

    ~/trivy-cache/db/trivy.db
    ~/trivy-cache/db/metadata.json
    

  3. Java 아티팩트 스캔이 필요하면 Java DB도 받는다.

    $> trivy image \
      --cache-dir ~/trivy-cache \
      --download-java-db-only
    

  4. 반입 전에 실제 파일 경로를 확인한다.

    $> find ~/trivy-cache -maxdepth 3 -type f
    

오프라인 환경 DB 다운로드 및 반입

  1. 폐쇄망 Harbor에서는 자동 다운로드를 비활성화하고, 인터넷 연결 구간에서 취약점 DB를 내려받아 반입한다.

  2. 인터넷 연결 구간 작업

    $> export TRIVY_EXPORT_DIR="$HOME/trivy-offline"
    
    $> rm -rf "${TRIVY_EXPORT_DIR}"
    $> mkdir -p "${TRIVY_EXPORT_DIR}/db"
    
    $> trivy image \
      --cache-dir "${TRIVY_EXPORT_DIR}" \
      --download-db-only
    
    # Java JAR 스캔이 필요할 때만 수행
    $> trivy image \
      --cache-dir "${TRIVY_EXPORT_DIR}" \
      --download-java-db-only
    
    $> tar czvf "trivy-db-$(date +%F).tar.gz" \
      -C "${TRIVY_EXPORT_DIR}" .
    
    $> sha256sum "trivy-db-$(date +%F).tar.gz" \
      > "trivy-db-$(date +%F).tar.gz.sha256"
    

폐쇄망 Harbor 서버 작업

  1. 반입한 tarball의 무결성을 먼저 검증한다.

    $> sha256sum -c trivy-db-YYYY-MM-DD.tar.gz.sha256
    

  2. Trivy DB를 저장할 디렉터리를 준비한다.

    $> sudo mkdir -p /data/trivy/db
    $> sudo tar xzvf trivy-db-YYYY-MM-DD.tar.gz -C /data/trivy
    

  3. trivy.db와 metadata.json의 실제 경로를 확인한다.

    $> sudo find /data/trivy -type f \( -name trivy.db -o -name metadata.json \)
    

  4. Harbor Adapter가 읽을 경로로 파일을 배치한다.

    $> sudo find /data/trivy -name trivy.db -exec cp -f {} /data/trivy/db/trivy.db \;
    $> sudo find /data/trivy -name metadata.json -exec cp -f {} /data/trivy/db/metadata.json \;
    

오프라인 Harbor 설정

  1. harbor.yml에서 자동 업데이트를 끈다.

    trivy:
      skip_update: true
      security_check: vuln,config,secret
    

  2. 설정 파일을 다시 생성한다.

    $> cd /opt/harbor/harbor
    $> sudo docker compose down -v
    $> sudo ./prepare --with-trivy
    

  3. 생성된 docker-compose.yml에서 trivy-adapter 서비스에 read-only 볼륨을 추가한다.

    services:
      trivy-adapter:
        volumes:
          - /data/trivy/db:/home/scanner/.cache/trivy/db:ro
    

  4. Harbor를 시작한다.

    $> sudo docker compose up -d
    $> sudo docker compose logs -f --tail=200 trivy-adapter
    

Info

./prepare를 실행하거나 Harbor를 업그레이드하면 docker-compose.yml이 다시 생성될 수 있다. 따라서 오프라인 Trivy DB mount 설정은 Ansible, ```shell Script, GitOps 등 구성 관리 절차에 포함한다.

정기 Trivy 업데이트

  1. 온라인 환경

    1. 온라인 환경은 skip_update: false를 유지한다. DB 갱신 실패가 의심되면 Trivy Adapter를 재시작하고 로그를 확인한다.
      $> cd /opt/harbor/harbor
      $> sudo docker compose restart trivy-adapter
      $> sudo docker compose logs -f --tail=200 trivy-adapter
      
  2. 새 DB 기준으로 기존 이미지를 다시 평가하려면 Harbor Portal에서 전체 재스캔 일정을 설정한다

    Administration
    → Interrogation Services
    → Vulnerability
    → Schedule to scan all
    

    작업 권장 주기
    Trivy DB 업데이트 자동 또는 매일
    전체 이미지 재스캔 매일 또는 매주
    Trivy Adapter 로그 점검 매일
    취약점 정책 검토 월 1회 또는 릴리스 시
  3. 오프라인 환경

    1. 폐쇄망에서는 DB 반입과 이미지 재스캔을 별도로 관리한다.

      작업 권장 주기 설명
      인터넷 구간 DB 다운로드 매일 신규 CVE 반영
      반입 파일 SHA-256 검증 매 반입 무결성 검증
      폐쇄망 DB 교체 매일 또는 주 1회 보안 정책에 맞춤
      전체 이미지 재스캔 매일 또는 주 1회 새 DB 기반 결과 갱신
  4. 오프라인 DB 갱신 예시다.

$> cd /opt/harbor/harbor

# 1. 반입 파일 검증
$> sha256sum -c trivy-db-YYYY-MM-DD.tar.gz.sha256

# 2. 기존 DB 백업
$> sudo mv /data/trivy/db \
  "/data/trivy/db.bak.$(date +%F-%H%M%S)"

$> sudo mkdir -p /data/trivy/db

# 3. 새 DB 압축 해제
$> sudo mkdir -p /data/trivy/import

$> sudo tar xzvf trivy-db-YYYY-MM-DD.tar.gz \
  -C /data/trivy/import

# 4. 필수 DB 파일 배치
$> sudo find /data/trivy/import -name trivy.db \
  -exec cp -f {} /data/trivy/db/trivy.db \;

$> sudo find /data/trivy/import -name metadata.json \
  -exec cp -f {} /data/trivy/db/metadata.json \;

$> sudo rm -rf /data/trivy/import

# 5. 새 DB를 읽도록 Trivy Adapter 재기동
$> sudo docker compose restart trivy-adapter
$> sudo docker compose logs --tail=200 trivy-adapter

Harbor 설치 전 호환성 테스트

  1. Harbor 설치 전에 Podman socket과 Compose 연결을 검증한다.

    $> export DOCKER_HOST="unix:///run/podman/podman.sock"
    
    $> docker version
    $> docker compose version
    
    $> docker compose -f - up -d <<'EOF'
    services:
      test:
        image: docker.io/library/alpine:3.20
        command: ["sh", "-c", "sleep 60"]
    EOF
    
    $> docker compose -f - ps
    $> docker compose -f - down
    
  • 위 테스트가 실패하면 Harbor 설치를 진행하지 않는다. Podman socket 경로, Docker CLI, Compose provider, DOCKER_HOST, SELinux AVC 로그를 먼저 점검한다.



작성일: 2026년 8월 11일 ,  마지막 업데이트: 2026년 8월 11일