Harborによるプライベートコンテナレジストリの保護:脆弱性スキャンの自動化、Cosignによる署名、およびデプロイ制限の適用

Security tutorial - IT technology blog
Security tutorial - IT technology blog

クイックスタート(5分):堅牢化されたHarborインスタンスの立ち上げ

追跡されていないベースイメージは、常にプライベートレジストリへと紛れ込むリスクがあります。監視されていないレジストリへ生ビルドを直接プッシュすることは、クラスターを既知のエクスプロイトの格好の標的にしてしまう原因になります。

Harborはこのワークフローを遮断します。標準的なOCIレジストリストレージに対し、きめ細かなアクセス制御、自動化されたTrivyセキュリティスキャン、ネイティブなイメージ署名機能を付加します。Docker Composeを使用すれば、わずか5分未満でローカルの動作インスタンスを立ち上げることができます。

# Harborのオフラインインストーラーをダウンロード
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xzvf harbor-offline-installer-v2.10.0.tgz
cd harbor

# パラメーターを設定
cp harbor.yml.tmpl harbor.yml

harbor.ymlを開き、ホスト名と初期認証情報を調整します:

hostname: registry.internal.lan
http:
  port: 80
# 本番環境でTLSを使用する場合は、httpsブロックのコメントを解除して証明書パスを指定してください
harbor_admin_password: "ChangeMe_Complex_Secret!"

新しいサーバーで管理者パスワードやデータベースの認証情報を設定する際は、脆弱なデフォルト値やbash履歴への露出を避けることが肝要です。toolcraft.app/ja/tools/security/password-generatorのようなクライアントサイドのジェネレーターを利用すれば、ブラウザ内で直接高エントロピーな文字列を生成できます。秘密情報が作業端末の外へ送信されることはありません。生成された機密情報をパスワードマネージャーに保存したら、Trivyスキャナーを有効化してインストーラーを実行します:

sudo ./install.sh --with-trivy

コンテナが稼働していることを確認します:

docker compose ps

コマンドラインからレジストリにログインします:

docker login registry.internal.lan -u admin

詳細解説:レジストリの脆弱性と根本原因

課題:アップストリームのベースイメージに対する盲目的信頼

レジストリセキュリティにおける大半の問題は、根拠のない過信から生じます。開発者がpython:3.11-slimやnode:20-alpineをプルし、社内コードを重ねてビルドをプッシュします。その3週間後、libwebpのCVE-2023-4863のように、アップストリームのglibcやOpenSSLの欠陥が表面化します。その結果、誰も気づかないうちにクラスター上で侵害されたバイナリが実行される事態に陥ります。

根本原因:ポイントインタイムのスキャンとミュータブルなアーティファクト

脆弱なイメージが本番環境に到達してしまう主な盲点は2つあります:

  • パイプライン内での一時的なスキャン:CI実行時にのみイメージをスキャンする手法は、誤った安心感を生み出します。イメージがデプロイされるまでストレージに20日間置かれている場合、ビルド後に開示されたすべてのCVEを見逃すことになります。
  • ミュータブルなタグと未検証の出所(来歴)::latestや:v1.2といった可変タグは、プッシュ権限を持つ者なら誰でも上書き可能です。署名検証がなければ、攻撃者や誤作動したパイプラインスクリプトによって、信頼されたイメージが侵害されたレイヤーで上書きされる恐れがあります。

解決策:Harborが提供する3層防御

Harborは、連携する3つのメカニズムによって自動化されたゼロトラストゲートを適用します:

  1. 継続的なバックグラウンドスキャン:Trivyが新規プッシュを即座にスキャンします。さらに、夜間のcronスケジュールによって保管中のイメージも定期的に再スキャンします。
  2. Cosignによる暗号化アテストステーション:Sigstore Cosignにより、イメージが認可されたCIランナーによって作成され、改ざんされていないことを検証します。
  3. プル前の受付制御(アドミッションコントロール):脆弱性が定義された重大度の上限を超えている場合、Harborはイメージのプルリクエストを拒否します。

実践応用:自動スキャン、Cosign署名、およびプル遮断

1. プッシュ時スキャンと定時スキャンの有効化

手動スキャンは人の記憶に依存してしまいます。代わりにプロジェクトレベルで自動脆弱性チェックを強制しましょう:

  1. Harborコンソールにログインします。
  2. プロジェクト → 対象プロジェクト(例:backend-services)を選択 → 設定 を開きます。
  3. プッシュ時にイメージを自動スキャン にチェックを入れます。
  4. 脆弱性 の項目で、脆弱性のあるイメージの実行を防止 にチェックを入れ、しきい値として High(高) または Critical(緊急) を選択します。

既存のイメージに対して新たに開示されたCVEを検知するには、管理 → インテロゲーションサービス → 脆弱性 へ移動します。毎晩UTC午前2:00に再スキャンを実行するようスケジュールを設定します:

0 0 2 * * *

2. Cosignによるイメージ署名

タグは上書きできますが、暗号署名は上書きできません。Cosignは、個別のNotaryサーバーを必要とせず、Harbor内で直接OCIアーティファクトに署名します。

# Cosignのインストール
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign

# CIパイプラインランナー用のキーペアを生成
cosign generate-key-pair

cosign.keyをCIのシークレット管理システム(HashiCorp VaultやGitHub Secretsなど)に保存します。イメージをプッシュした後、直ちにそのダイジェストに対して署名を行います:

# ビルドおよびプッシュ
docker build -t registry.internal.lan/backend-services/api:1.0.4 .
docker push registry.internal.lan/backend-services/api:1.0.4

# イメージへの署名
COSIGN_PASSWORD="your-private-key-password" cosign sign \
  --key cosign.key \
  registry.internal.lan/backend-services/api:1.0.4

Cosignは署名アーティファクトをHarborへ直接プッシュします。Harborはこれを検知し、イメージダイジェストの横に緑色の検証済みバッジを表示します。

3. Harborにおけるコンテンツトラストの適用

Harborのプロジェクト設定で 設定 を開き、署名済みイメージのみプルを許可 にチェックを入れます。これにより、署名されていないイメージのダウンロード試行はすべてレジストリの境界で拒否されます:

$ docker pull registry.internal.lan/backend-services/api:unsigned-test
Error response from daemon: unknown: The image is not signed by Cosign or Notary, and cannot be pulled.

4. KyvernoによるKubernetes内での署名検証の適用

レジストリレベルの遮断はエンドポイントを保護しますが、クラスター内でのコンプライアンス順守はアドミッションコントローラーが担います。HarborとKyvernoを組み合わせることで、署名されていないPodがスケジュールされる前に排除できます:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  rules:
    - name: verify-cosign
      match:
        resources:
          kinds:
            - Pod
      verifyImages:
        - imageReferences:
            - "registry.internal.lan/backend-services/*"
          key: |-
            -----BEGIN PUBLIC KEY-----
            MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7p+YV6bEx...
            -----END PUBLIC KEY-----

本番環境でHarborを運用するための実践Tips

  • SemVerタグの固定化:プロジェクト設定で タグの不変性ルール(Tag Immutability Rules) を有効にします。v[0-9]+\.[0-9]+\.[0-9]+のような正規表現パターンを適用することで、パイプラインスクリプトや人為的ミスによって確立されたリリースが上書きされるのを防ぎます。
  • 孤立ブロブの毎週のプルーニング:CIランナーはスプリントごとに数十ものタグなしレイヤーを生成します。開発者が30人いるチームでは、毎月250GBもの孤立したイメージレイヤーが簡単に蓄積されます。管理 → クリーンアップ からガベージコレクションを定期実行するよう設定し、まずはドライランでテストしてください。
  • ロボットアカウントの分離:CI/CDシステムに管理者権限を共有してはなりません。権限範囲を限定した専用のロボットアカウントを作成し、CIビルダーにはPush/Pull権限を、Kubernetesクラスターのプルシークレットには読み取り専用のPull権限を割り当てます。
  • スコープを絞ったCVE許可リストの活用:curlのCVE-2023-38545のようなゼロデイ脆弱性では、アップストリームベンダーによるパッチが即座に提供されない場合があります。このような際もデプロイ防止ゲート自体をオフにしてはいけません。対象のCVE IDをプロジェクトレベルの CVE許可リスト に14日間の有効期限付きで追加し、パッチを待つ間もリリースを滞りなく進められるようにします。
Share: