プライベートDockerレジストリへの認証対応

basecamp/once

Onceがプライベートコンテナレジストリからのイメージプルをサポートしました。ローカルのDockerクレデンシャル設定を自動解決することで、ghcr.io・GCR・ECRなどのプライベートレジストリからも、ユーザーが追加設定なしにデプロイできるようになります。

背景

これまでのOnceは、イメージプル時に常に匿名アクセスを使用していたため、プライベートレジストリからのデプロイが認証エラーで失敗していました。PRの説明によれば、DHHのプレゼンテーション中にもこの問題が顕在化したとのことです。

問題の根源は internal/docker/application.gopullImage メソッドにあり、image.PullOptions{} をそのまま(空のまま)渡していたため、レジストリ認証情報が一切送られていませんでした。docker pull コマンドは ~/.docker/config.json のクレデンシャルを自動的に使用しますが、Onceはその仕組みを利用していませんでした。

技術的な変更

変更の中心は新規追加された internal/docker/registry_auth.go と、それを呼び出すように修正された pullImage メソッドの2点です。

pullImage の変更前後:

変更前:

reader, err := a.namespace.client.ImagePull(ctx, a.Settings.Image, image.PullOptions{})

変更後:

opts := image.PullOptions{RegistryAuth: registryAuthFor(a.Settings.Image)}
reader, err := a.namespace.client.ImagePull(ctx, a.Settings.Image, opts)

新たに追加された registryAuthFor 関数は、イメージ名を受け取り、対応するレジストリの認証情報をBase64エンコードしたJSON文字列として返します。

func registryAuthFor(imageName string) string {
    ref, err := name.ParseReference(imageName)
    if err != nil {
        return ""
    }
    authenticator, err := authn.DefaultKeychain.Resolve(ref.Context())
    if err != nil || authenticator == authn.Anonymous {
        return ""
    }
    cfg, err := authenticator.Authorization()
    if err != nil {
        return ""
    }
    data, err := json.Marshal(cfg)
    if err != nil {
        return ""
    }
    return base64.URLEncoding.EncodeToString(data)
}

認証解決には github.com/google/go-containerregistryauthn.DefaultKeychain を使用しており、これは docker CLI が採用しているのと同じクレデンシャル解決ロジックを持ちます。~/.docker/config.json に記載された静的クレデンシャルだけでなく、credHelpers で指定されたクレデンシャルヘルパーも自動的に利用されます。

依存関係としては、go.mod に以下のパッケージが追加されました:

  • github.com/google/go-containerregistry v0.21.3(直接依存)
  • github.com/docker/cli v29.3.0+incompatible(間接依存)
  • github.com/docker/docker-credential-helpers v0.9.3(間接依存)
  • github.com/mitchellh/go-homedir v1.1.0(間接依存)
  • github.com/sirupsen/logrus v1.9.4(間接依存)

設計判断

エラー時の安全な縮退設計が一貫して採用されています。registryAuthFor はイメージ名のパース失敗・クレデンシャル未設定・認証情報のシリアライズ失敗など、あらゆるエラーケースで空文字列を返します。Docker APIは RegistryAuth が空文字列の場合に匿名アクセスへフォールバックするため、パブリックイメージのデプロイは従来通り動作します。

クレデンシャル解決ロジックを自前実装せず authn.DefaultKeychain に委譲した点も重要な設計判断です。go-containerregistrycredHelpers(ECRやGCRの短期トークン取得)や credsStore といった複数のクレデンシャル解決方式をサポートしており、これを再実装するコストを避けつつ、docker pull と同等の認証体験を実現しています。

テストコード(registry_auth_test.go)では DOCKER_CONFIG 環境変数を使った設定の分離(isolateDockerConfig)や偽のクレデンシャルヘルパースクリプトのインストール(installFakeCredHelper)など、外部環境に依存しないテスト構造が採用されています。これにより、CI環境でも実際のレジストリへの接続なしにクレデンシャル解決ロジックを検証できます。

まとめ

registryAuthFor という単一関数の追加と pullImage への1行の変更により、Onceはdockerコマンドと同じクレデンシャル解決メカニズムを獲得しました。エラー時の安全なフォールバック設計により既存のパブリックイメージデプロイへの影響はなく、ユーザーは docker login 済みの環境であれば追加設定なしでプライベートレジストリからのデプロイが可能になります。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
0a8bce16

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「総論→各論→結論」の構成が明確です。リード文、背景、技術的な変更、設計判断、まとめの各要素がすべて含まれており、理想的な構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```go:ファイルパス)とGitHubのPRリンク記法([#18](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

専門用語が適切に使用されており、対象読者であるエンジニアに適した技術レベルと表現で書かれています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各パラグラフの冒頭にトピックセンテンスが置かれているため、非常に読みやすいです。1段落1トピックの原則も守られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコードブロック(`application.go`、`registry_auth.go`)および依存関係(`go.mod`)の記述は、提供されたDiff情報と完全に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`authn.DefaultKeychain`、`credHelpers`、Base64エンコードなど、関連する技術用語が文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

Docker APIの認証メカニズムや `go-containerregistry` ライブラリの挙動に関する説明は技術的に正確で、コードの変更内容を正しく解説しています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内の主張はすべてPRのDescriptionまたはDiff内のコードで裏付けられており、ハルシネーションは見られません。特に、PRに明記されていない設計判断やテストに関する記述も、コードという一次情報に忠実で優れています。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#18)や依存ライブラリのバージョン番号などがすべて正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「プライベートDockerレジストリへの認証対応」は、PRのタイトル「Support pulling private images via Docker credentials」の内容を的確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に基づかないバージョンのサポート状況やリリース日程などの外部知識の捏造はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「これまでのOnceは〜」や「サポートしました」といった時間表現は、PRの文脈と一致しており正確です。