Harborでプライベートレポジトリを立てる(CentOS、Let'sEncrypt証明書)

さくらのクラウド上にHarborを使ってプライベートレポジトリをたててみたので、そのやり方とハマりポイントをご紹介します。

## 背景・前提

セキュリティ上の観点などからプライベートなレポジトリを作りたいという要求があると思います。OSSで構築する方法としてDocker Registry + Portus という方法もあるようですが、ここではHarborを使う方法について書きます。（Harborも内部でDocker Registryを使っているようなので、両者は構成は似たようなものということですね。）

以下のような構成を想定しています。

- レジストリサーバ ： さくらのクラウド上のVM
- コンテナPushノード ： レジストリサーバ上に同居（別のノードでも全く問題なくいけるはず）
- コンテナPullノード : Raspberry Piで立てたノード

これらのノードには、手元のMacからsshでアクセスして作業するという想定です。

レジストリサーバにはFQDNな名前をふります。更にLet'Encryptを利用してSSL証明書を発行します。（やり方は後述）

Harborは（というかDockerは）コンテナイメージのpushやpullに証明書を必要とするので、仮にIPアドレスのみで運用するにしてもオレオレ証明書の作成が必要です。個人でのお試し試用であればそれでも良いですが、もうちょっと真面目に使うのであればドメイン登録＋Let'sEncryptで証明書発行しておいたほうが良いでしょう。

また使用したHarborのバージョンは作業時点での最新、v1.7.4です。

なお基本的には、下記のような参考資料に書いてあるように作業をすればいけます。
ですのでこの記事では、これらの記事との差異やハマりポイントを中心に記載します。

### 参考にした資料
[Ubuntu16.04LTS上でHarborを使ってちょっぴりSecureなPrivate Docker Registryを構築する] (https://qiita.com/ofumi3/items/d905a555d2bc36e05e4b)

[Rancher + Harbor（プライベートレジストリ）で、簡単Dockerイメージ管理](https://blog.arena.ne.jp/docker/1519)

## VMの準備

さくらのクラウドのコントロールパネルからログインし、新規VMを発行します。
VMスペックは[harborのドキュメント](https://github.com/goharbor/harbor/blob/master/docs/installation_guide.md)にはかなりリッチな要件が書いてありますが、1コア2GBメモリでも動きました。ちょっと使う分には問題なさげです。OSはCentOS7.6を選択しました。

VMのIPアドレスが確定したら、適当なドメインに登録して名前を振っておきます。今回の例では**fogregistry.sample.jp**としておきます。(余談ですが、名前については本件とは全く関係のない理由でfog云々としています。この記事に貼り付けた作業ログを書き直すのが大変なのでそのままで。)

## Harborインストール・設定

### Docker, docker-compose
まずHarborの前提となるDocker, docker-composeをインストールします。Harborはそれ自体がコンテナで動作します。
ハマりポイントは特にありません。

```shell-session
[root@fogregistry ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
[root@fogregistry ~]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
[root@fogregistry ~]# yum makecache fast
[root@fogregistry ~]# yum list docker-ce.x86_64 --showduplicates
[root@fogregistry ~]# yum install docker-ce
[root@fogregistry ~]# systemctl start docker
[root@fogregistry ~]# systemctl status docker
[root@fogregistry ~]# systemctl enable docker
```

docker-composeも同様に。

```shell-session
[root@fogregistry ~]# curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-[root@fogregistry ~]# compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
[root@fogregistry ~]# chmod a+x /usr/local/bin/docker-compose
[root@fogregistry ~]# docker-compose --version
docker-compose version 1.23.2, build 1110ad01
[root@fogregistry ~]#
```

### SSL証明書

上述の通り、harborはssl証明書を必要とするので、Let's Encryptを利用して証明書を発行します。
使ったことがない方には敷居が高いかもしれませんが、とても簡単です。

```shell-session
[root@fogregistry ~]# yum install certbot
[root@fogregistry ~]# certbot certonly --webroot -w /var/www/html -d fogregistry1.sample.jp
```

Let's Encryptの証明書発行エージェントソフトウェアのcertbotをインストールします。インストールできたら、certbotコマンドを、webサーバのドキュメントルートと当該サーバ（レジストリサーバにしたいマシン)のFQDNを引数につけて実行します。

コマンド実行するといくつか質問されるので適切に答えます。うまく実行できれば、/etc/letsencrypt/というディレクトリが作成され、その配下のarchveディレクトリにレジストリサーバのFQDN名でディレクトリが作成され、そこに発行された証明書が置かれます。

```shell-session
[root@fogregistry fogregistry.sample.jp]# pwd
/etc/letsencrypt/archive/fogregistry.sample.jp
[root@fogregistry fogregistry1.kikuzo.jp]# ls -l
合計 20
-rw-r--r-- 1 root root 1927  3月 20 14:36 cert1.pem
-rw-r--r-- 1 root root 1647  3月 20 14:36 chain1.pem
-rw-r--r-- 1 root root 1927  3月 20 14:49 fogregistry.sample.jp.cert
-rw-r--r-- 1 root root 3574  3月 20 14:36 fullchain1.pem
-rw------- 1 root root 1704  3月 20 14:36 privkey1.pem
[root@fogregistry fogregistry.sample.jp]#
```

Let's Encrytpについては、[Let's Encrypt 総合ポータル](https://free-ssl.jp/)に詳しい使い方などがありますので参照して下さい。

### harbor本体のインストール

早速harbor本体のインストールと行きたいところですが、その前に、CentOS標準のhttpサーバを停止させます。これがharborのwebサーバとport80でconflictするからです。

```shell-session
[root@fogregistry ~]# systemctl stop httpd
[root@fogregistry ~]# systemctl disable httpd
```
**ハマりポイント** : この作業は、SSL証明書の発行作業が**終了してから**やって下さい。SSL証明書発行には当該マシンが確かに指定したFQDNでアクセス可能になっているかを確認する処理が含まれ、そのために当該サーバでwebサーバが起動していなければなりません。

で、webサーバを止めたら、harbor本体をダウンロードして展開します。

```shell-session
[root@fogregistry ~]# yum install wget
[root@fogregistry ~]# wget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-online-installer-v1.7.4.tgz
[root@fogregistry ~]# tar zxvf harbor-online-installer-v1.7.4.tgz
```

展開した先に、harbor.cfgというファイルが含まれ、これがharborの設定ファイルになりますので、これを編集します。

```shell-session
[root@fogregistry ~]# cd harbor
[root@fogregistry harbor]# vi harbor.cfg
```

とりあえず使うために変更必要なのは、hostname, ui_url_protocol, ssl_cert, ssl_cert_keyの4行になります。

```harbor.cfg
hostname = fogregistry.sample.jp

ui_url_protocol = https
ssl_cert = /etc/letsencrypt/archive/fogregistry.sample.jp/cert1.pem
ssl_cert_key = /etc/letsencrypt/archive/fogregistry.sample.jp/privkey1.pem

secretkey_path = /data
```

**ハマりポイント** : secretkey_pathはデフォルトの"/data"のまま変更しない方がよいです。このディレクトリでボリュームマウントしてコンテナに設定ファイル等を渡している(docker-compose.yml内でvolume行記述あり)ため、変更するとコンテナの起動でコケます。

設定ファイルができたら、コンテナダウンロードなどが含まれる、インストーラコマンドを実行し、完了したらdocker-compose で起動します。

```shell-session
[root@fogregistry harbor]# ./prepare
[root@fogregistry harbor]# docker-compose up -d
```

無事起動できたら、ブラウザで https://fogregistry.sample.jp/ にアクセスしてみて下さい。

**ハマりポイント** : webUIのページは表示されるのにadminユーザでログインできない場合は、何らかのコンテナの起動に失敗している場合があります。docker-compose logs でログを確認してみて下さい。私の場合は、上述の/dataの行を変更していたためadminserverというコンテナが必要な設定ファイルを読み込めずに起動に失敗していたことがありました。

## レポジトリを使うための準備

### レポジトリ作成等
webUIにアクセスしたら、以下のような作業をします。詳しくは参考文献等を参照して下さい。特に難しくないです。
- 初回ログイン時にadminユーザのパスワード変更
- ユーザの発行
- レポジトリの作成（書き込み権限を持っているユーザが作成する）

なお、以下の例では、fogadvance-exampleというレポジトリを作成しています。（余談：本件の内容に全く関係ない名前ですが、ログを書き換えるのが大変なので。）

### レジストリサーバのCA証明書を使用者に渡す

後述してますが、レポジトリにコンテナイメージをpush/pullするためには、レジストリサーバのCA証明書を利用者のローカル環境に設定する必要があります。そのため、CA証明書を取り出して渡せるように準備しておきます。

CA証明書は /etc/letsencrypt/archive/fogregistry.sample.jp/fogregistry.sample.jp.cert というファイルになりますので、これをca.crtという名前にリネームして、適当な場所に置くなり利用者に送るなりします。

## レポジトリへのpush/pull

以下は、dockerでプライベートレポジトリを使う基本的な操作内容なのですが、参考までにやり方を書いておきます。

### pushする場合

以下の作業は、コンテナイメージを作成する(そしてレポジトリにpushする)ノードで作業する内容です。（今回の例では実際にはレジストリサーバに同居しています。わかりにくくてすみません。）

##### レジストリサーバのCA証明書を開発環境にインストール
dockerでコンテナをレポジトリにpushする際には、レジストリサーバのCA証明書をローカル（開発環境）側に入れておかないと認証エラーになって登録できません。別途入手したレジストリサーバのca.crtを、/etc/docker配下にcert.d/fogregistry.sample.jpというディレクトリを作成し、そこに置きます。

```shell-session
[root@fogregistry ~]# cd /etc/docker
[root@fogregistry docker]# mkdir certs.d
[root@fogregistry docker]# cd certs.d
[root@fogregistry certs.d]# mkdir fogregistry.sample.jp
[root@fogregistry certs.d]# cd fogregistry.sample.jp/
[root@fogregistry certs.d]# cp ~/ca.crt .
[root@fogregistry fogregistry.sample.jp]# ls -la
合計 12
drwxr-xr-x 2 root root 4096  3月 21 02:09 .
drwxr-xr-x 3 root root 4096  3月 21 02:09 ..
-rw-r--r-- 1 root root 1927  3月 21 02:09 ca.crt
[root@fogregistry fogregistry.sample.jp]#
```

##### コンテナへのタグ付け

イメージ作成は別途やっている状態で、レポジトリに登録したいイメージにタグを付与します。

```shell-session
[root@fogregistry ~]# docker tag hello-world:latest fogregistry.sample.jp/fogadvance-example/hello-world:latest
```

この例では、hello-world:latestイメージに、fogregistry.sample.jp/fogadvance-example/hello-world:latestというタグを付与しています。タグは**レジストリサーバFQDN/レポジトリ名/コンテナ名:バージョン**となります。

##### コンテナのpush
タグ付与できたら、pushします。

```shell-session
[root@fogregistry ~]# docker login fogregistry.sample.jp
..レジストリサーバに発行したID/Passwordでログインします..
[root@fogregistry ~]# docker push fogregistry.sample.jp/fogadvance-example/hello-world:latest
```

無事にpushできたら、レポジトリのwebUIにアクセスしてみて下さい！

### コンテナのpull

#### レジストリサーバの公開鍵を開発環境にインストール
コンテナpull時には、レポジトリへのSSL接続ができるように公開鍵を開発マシンにインストールする必要があります。レジストリサーバのca.crtが使えます。これを別途入手しておいて、

```shell-session
pi@raspberrypi:~/test $ cd /usr/local/share/ca-certificates/
pi@raspberrypi:/usr/local/share/ca-certificates $ sudo cp /home/pi/ca.crt ./fogregistry.sample.jp.crt
pi@raspberrypi:/usr/local/share/ca-certificates $ ls -l
total 4
-rw-r--r-- 1 root staff 1927 Mar 26 09:50 fogregistry.sample.jp.crt
pi@raspberrypi:/usr/local/share/ca-certificates $ sudo update-ca-certificates
```

という感じで登録し、さらにdockerを再起動します(簡単なのはraspberry piの再起動)。設置先ディレクトリとupdateコマンドはOS(ディストリビューション)によって異なります。

このあたりについては[dockerのドキュメント](https://docs.docker.com/registry/insecure/)に記述があります。

#### コンテナのpull

```shell-session
pi@raspberrypi:~ $ docker login fogregistry.sample.jp
..レジストリサーバに発行したID/Passwordでログインします...
pi@raspberrypi:~ $ docker pull fogregistry.sample.jp/fogadvance-example/hello-world:latest
```

やってみて下さい！