Tanzu アプリケーションのモダナイゼーション

Harbor + Route53 + Let’s Encrypt で作る証明書付きコンテナレジストリ

はじめまして。VMware の伊藤です。Tanzu 製品のプラットフォームアーキテクトとして働いており、開発と運用双方の経験があります。この記事では Docker や Kubernetes で必要不可欠なコンテナレジストリをプライベート環境に証明書付きで作成する手順のサンプルを公開します。自己証明書のレジストリに比べると利用が簡単というメリットがあります。

パブリック証明書 vs 自己証明書

コンテナイメージのレジストリは Docker-Hub などが有名ですが、一般的にインターネット上のレジストリは同一環境上にあるプライベートなコンテナレジストリに比べて高速なアクセス性やセキュリティの面で劣ります。そのため、多くの組織ではオンプレミスやパブリッククラウドに限らずプライベートレジストリを構築しています。

Docker や Kubernetes がコンテナレジストリにアクセスする際は HTTPS をデフォルトで利用しますが、HTTPS の証明書を正しく設定しなければ「x509: certificate signed by unknown authority」などとして接続に失敗します。この問題を解決するために一般的には自己証明書のレジストリを作成するというかたが多いようですが、これは Docker や Kubernetes 側に追加設定が必要となる問題があります。

この追加設定はコンテナ環境の運用においてトラブルとなることが多いため、外部にアクセスできる環境であれば自己証明書ではなく「証明書認証局」が発行した正しい証明書(パブリックな証明書)を用いることが望ましいです。以下の図にに自己証明書を使うレジストリを Plan-A、パブリックな証明書を使うレジストリを Plan-B として対比します。

 

図にあるようにレジストリのユーザーである Docker や Kubernetes の視点では、パブリック証明書を持つレジストリは簡単に利用できます。自己証明書を使う場合は Docker の設定を変更したり、Kubernetes にいたっては増減する Worker Node 上に自己証明書を正しく配置し続ける必要性などがあり、非常に手間がかかります。

なお、名前解決でインターネットにアクセスできないという場合はパブリック証明書方式を採用できません。分かりやすく組織内のマシンが直接パブリックネームサービスを参照してもよいですが、図にあるように組織内のネームサーバを経由して間接的に参照する場合もパブリック証明書方式を採用できます。

 

構築するパブリック証明書方式のコンテナレジストリ

以下にこれから構築するパブリック証明書方式のコンテナレジストリの構成図を記載します。

 

証明書に関する詳しい説明は本記事では割愛しますが、おおまかには以下の構成となります。

  • インターネット上で使える DNS エントリを作成: 本記事では AWS の Route53 を利用
  • 証明書認証局によるパブリック証明書の発行: 本記事では Let’s Encrypt を利用
  • 証明書認証局が発行したパブリック証明書を持つコンテナレジストリ: 本記事では Harbor を利用

まず、いちばん重要なのは証明書ですが、これには証明書が利用するドメイン名が必要となります。この記事では「iyuichi-vmware.com」のサブドメインである「harbor99.lab32.iyuichi-vmware.com」を使います。これを登録するためにドメイン登録が必要になりますが、今回は AWS の Route53 を利用します。

どのネームレジストラを利用するかは自由ですが、後ほど実施する「DNS のエントリを作成」という作業を問題なくおこなえるサービスを選択する必要があります。AWS Route53 は問題なく利用できましたが、お名前.com などはプライベートアドレスで DNS A レコードの作成ができないため注意が必要です (つまり、プライベート IP のパブリック証明書を作成できない)。

プライベートレジストリのドメイン名を作成したらそれを証明書認証局で登録して証明書を発行しますが、今回は無償でそれをおこなえる Let’s Encrypt というサービスを使います。図の右下にある certbot というソフトウェア経由で CLI によりその作業をおこないますが、その最中にレジストラ(Route53)にて「このドメインは私のものです」という証明をするための作業が発生します。これはレジストラ上に決められた DNS エントリを作成するというものです。それにパスすると証明書が発行されて利用できるようになります。

最後に発行された証明書を使ってコンテナレジストリを作成するば、Docker や Kubernetes で証明書の設定なしに使えるコンテナレジストリが完成します。

 

作業の流れ

この記事では以下の順で作業を実施します。

  1. ドメインの登録(有償)
  2. Ubuntu 20.04 LTS のVMを作成 (今回は 2 core, 4GB で作成。ストレージは多めを推奨)
  3. Ubuntu にパッケージをインストールし、Docker を利用できるようにする
  4. Route53 に A レコードを作成し、インターネット上で名前解決できるようにする
  5. dig コマンドにて名前解決に問題ないか確認
  6. certbot を使って Let’s Encrypt のパブリック証明書を発行
  7. コンテナレジストリの Harbor のインストーラーをダウンロード
  8. Harbor の設定を更新
  9. Harbor のインストールと起動
  10. ブラウザで Harbor にアクセスできるか確認
  11. Docker で Harbor にアクセスできるか確認

 

AWSでの事前作業

ドメインの登録をします。ここでは「iyuichi-vmware.com」というドメインを登録しました。

 

登録作業をおこなってから利用できるようになるまで 1-2 時間程度がかかる場合がありますので、実際の作業前に取得しておくとよいかもしれません。なお、他のネームレジストラで取得したドメインを AWS の Route53 で利用することも可能です。お名前.com などで取得したアドレスがある場合はそのようにしてください。

 

コンテナレジストリの基盤の準備

コンテナジストリの基盤に今回は Ubuntu と Docker を利用します。Kubernetes 上に Harbor を構築することもできますので、高い可用性が必要な環境では Kubernetes 上に構築することをご検討ください。一般的な環境であればここで紹介するように、1台の VM 上に Harborを構築し、それをVMとして保護(HA やバックアップ)するだけで十分かと思います。

まず固定IPで Ubuntu 20.04 LTS を作成し、必要なパッケージをインストールして Docker を自動起動する設定にします。

Docker がきちんと起動しているかを「docker info」などで確認してください。

 

インターネット上での名前解決の設定と確認

証明書を得るためにはインターネット上で名前解決できる必要があります。自社内でしか解決できない社内のネームサーバ(Bind や Active Directory など)に定義したエントリ(ドメイン名)では証明書を作成できないのでご注意ください。

今回は Route53 にて DNS A レコードで利用したいドメイン名「harbor99.lab32.iyuichi-vmware.com」にたいして、Harbor のホストIPである「192.168.64.99」を与えています。

 

レコード登録できたら、それを「Docker や Kubernetes が参照するネームサーバー(今回は 192.168.32.5)」と、インターネット上のネームサーバー(今回は8.8.8.8)の両方で名前解決できるか dig コマンドで確認します。

問題なく解決できていることが確認できました。

 

certbotによるパブリック証明書の作成

証明書認証局である Let’s Encrypt に証明書を作成する作業を実施します。

Ubuntu に certbot を apt でインストールしていますので、certbot の DNS チャレンジ方式で証明書を作成します。以下のコマンドのドメイン名は実際にご利用になるドメイン名に置き換えてください。

 

この作業ログの途中にある「Please deploy a DNS TXT record under the name ….」というところで、スクリプトが一旦とまります。英語に説明があるように DNS TXTレコードをレジストラに登録する作業を実施します。今回であれば、以下のようになります。

 

このレコードを作成したあとで「Press Enter to Continue」にしたがってエンターキーを入力すると、certbot が正しい DNS レコードが存在するかチェックしにいき、レコードが正しく作成されていれば証明書が Let’s Encrypt のサービス上とローカルマシン上(今回は /etc/letsencrypt/live/harbor99.lab32.iyuichi-vmware.com ディレクトリ上)に作成されます。なお、「このドメインは certbot を動かしたユーザーのもので間違いないか」という確認は、DNSのレコードを作成する(オーナーでなければできない)ことで実施しています。

注意が必要なのは、この証明書の期限は90日しかないので、証明書の期限が切れる前に証明書の更新が必要なことです。この証明書の更新作業は Cron で実施するのが定番となっています。週末の深夜などに Harbor を一時停止 (docker-composeで停止可能) し、certbot で証明書を更新し、Harborの設定更新後に再度デプロイすれば、更新された新しい証明書で Harbor が利用できます。コンテナイメージは永続領域に保存されていますので、停止/更新したとしても既存イメージは消えないことが期待されますし、certbot が生成する証明書のパスも以前と同じまま(ソフトリンクを利用している)なので Harbor の設定ファイルの更新は必要ありません。

 

コンテナレジストリ Harbor のインストール

証明書の準備が整ったので、いよいよ Harbor のインストールを実施します。インストールはインストーラーを Harbor のページ(GitHub) からダウンロードして、設定をおこない、インストールするだけです。

https://github.com/goharbor/harbor

 

以下のようにダウンロードして展開します。この記事の執筆時点より新しいイメージがあると思いますので、ダウンロードページにアクセスしてURLを実際にご確認ください。

 

ダウンロードした tgz ファイルを解凍し、harbor.yml.tmpl から harbor.yml ファイルを cp コマンドなどで作成してください。harbor.yml が Harbor の設定ファイルとなります。

設定ファイル harbor.yml を更新します。

 

通常は設定箇所は多くありません。

今回は上記の

  • hostname
  • certificate
  • private_key

のみを変更しています。hostnameにはドメイン名を指定し、certificateとprivate_keyには先ほど certbot が作成した証明書とプライベートキーをそれぞれ指定しています。これらの設定より下部に Harbor のパスワード設定もあり、デフォルトは「Harbor12345」となっています。必要があればパスワードも変更してください。今回はデフォルトの Harbor12345 をそのまま使っています。

なお、certbot が作成した証明書のファイル名に注意してください。fullchain.pem は証明書と中間証明書の両方を持っていますが、cert.pem は証明書しか含んでいません。cert.pem を設定の certificate の項目に指定すると、docker や Kubernete でレジストリにアクセスする際に証明書エラーが発生するので注意してください。

 

準備が整ったので、用意されたスクリプトでインストールを実施します。インストール完了後にコンテナが正常に動作しているかも確認してください。

インストールに失敗したり、コンテナが正常に起動されていない場合は証明書や設定に間違いがないかチェックしてください。

 

ブラウザとDockerでの接続確認

以上でパブリック証明書を持つコンテナレジストリ Harbor が展開できました。

ブラウザでアクセスします。デフォルトのユーザー名は「admin」でパスワードは「Harbor12345」です。以下はログイン後の画面となります。

 

ブラウザの証明書アイコンで確認できるように、正しく証明書が設定されているので「この接続は保護されています」と表示されています。自己証明書ですとブラウザでアクセスした際に「信頼できません」といった旨のメッセージが表示されます。

 

次に Docker でコンテナレジストリにログインと Push 作業を実施します。自己証明書を利用するのであれば証明書の登録作業などが必要になってきますが、ここではなにもせずにインストールしたままの Docker で作業を実施しています。

 

この Harbor は Let’s Encrypt の証明書を持つ信頼できるコンテナレジストリですので、普通に Docker ログインと Push ができました。もし自己証明書を使っており、Docker にその証明書登録をしていなければ「x509: certificate signed by unknown authority」というエラーでログインその他の操作が一切おこなえません。

 

このようにパブリック証明書を使ったイメージレジストリの作成は「パブリック証明書を作成する」という小さな手間と金銭的コスト(AWS であればドメイン代が年額 12$)により、Docker や Kubernetes 側でプライベートレジストリを利用する手間を大幅に減らすことができます。本番/開発環境に関わらずに無理に自己証明書で頑張るのではなく、パブリック証明書の利用をご検討いただくのがよいのではないかと思います。