はじめに
https接続でdocker-registryに接続します。
リバースプロキシを経由するパターン、しないパターンをそれぞれ紹介します。
docker-composeを使用します。
前編はこちら
https://qiita.com/not75743/items/2358229fc6f83718e7bf
対象読者
- docker-registryにhttps接続でpush,pullしたい人
- docker-registryにリバースプロキシ(nginx)経由でpush,pullしたい人
- 自己証明書を使用してdocker-registryにhttps接続したい人
前提
- https接続のための証明書が取得済みであること。
- docker,docker-composeが使える環境であること
環境
client
$ cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
$ docker --version
Docker version 20.10.3, build 48d30b5
host
- AWSのEC2
- 使ったAMIはamzn2-ami-hvm-2.0.20210126.0-x86_64-gp2
$ docker --version
Docker version 20.10.4, build d3cb89e
$ docker-compose --version
docker-compose version 1.27.4, build 40524192
構成図
① リバースプロキシ(nginx)を経由するパターン
ディレクトリ構成
.
|-- cert
| |-- fullchain.pem
| `-- privkey.pem
|-- data
|-- docker-compose.yaml
|-- registry
| `-- config.yml
`-- reverseproxy
|-- htpasswd
`-- nginx.conf
docker-compose.yaml
以下を使用しました。
version: "3.9"
services:
reverse-proxy:
image: nginx:1.20
ports:
- 5043:443
volumes:
- ./cert/fullchain.pem:/etc/nginx/conf.d/fullchain.pem:ro
- ./cert/privkey.pem:/etc/nginx/conf.d/privkey.pem:ro
- ./reverseproxy/nginx.conf:/etc/nginx/nginx.conf:ro
- ./reverseproxy/.htpasswd:/etc/nginx/htpasswd:ro
registry:
image: registry:2
volumes:
- ./data:/var/lib/registry
- ./registry/config.yml:/etc/docker/registry/config.yml
直接のアクセスを防ぐため、registryのポートは記載していません。
コンフィグや証明書については適切なパスを指定しましょう。
nginx.conf
以下を使用しました。
ドキュメントに記載のコメントも残しています。
events {
worker_connections 1024;
}
http {
upstream docker-registry {
server registry:5000;
}
## Set a variable to help us decide if we need to add the
## 'Docker-Distribution-Api-Version' header.
## The registry always sets this header.
## In the case of nginx performing auth, the header is unset
## since nginx is auth-ing before proxying.
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
'' 'registry/2.0';
}
server {
listen 443 ssl;
server_name example.test;
# SSL
ssl_certificate /etc/nginx/conf.d/fullchain.pem;
ssl_certificate_key /etc/nginx/conf.d/privkey.pem;
# Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# disable any limits to avoid HTTP 413 for large image uploads
client_max_body_size 0; # ※①
# required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
chunked_transfer_encoding on;
location / {
# Do not allow connections from docker 1.5 and earlier
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
# To add basic authentication to v2 use auth_basic setting.
auth_basic "Registry realm";
auth_basic_user_file /etc/nginx/htpasswd; # ※②
## If $docker_distribution_api_version is empty, the header is not added.
## See the map directive above where this variable is defined.
add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;
proxy_pass http://docker-registry;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
}
以下でポイントについて説明します。
① client_max_body_size 0
詳しくはここを参照
HTTPボディの大きさを制限するパラメータです。
docker-registryには大きめのイメージをpushする場合もあり、このパラメータが設定されている場合、引っかかる場合があります。
引っかかった場合は以下の様に出力されます。
クライアント側
$ docker push example.test:5043/centos:test
The push refers to repository [example.test/centos]
174f56854903: Pushing [==================================================>] 203.9MB/203.9MB
error parsing HTTP 413 response body: invalid character '<' looking for beginning of value: "<html>\r\n<head><title>413 Request Entity Too Large</title></head>\r\n<body>\r\n<center><h1>413 Request Entity Too Large</h1></center>\r\n<hr><center>nginx/1.20.0</center>\r\n</body>\r\n</html>\r\n"
docker-compose側
registry_1 | time="2021-05-30T05:38:32.00570856Z" level=error msg="response completed with error" err.code="blob unknown" err.detail=sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc err.message="blob unknown to registry" go.version=go1.11.2 http.request.host="example.test" http.request.id=d62a7937-e2bf-4b68-9e62-96f9fe7721f9 http.request.method=HEAD http.request.remoteaddr=<クライアントIP> http.request.uri="/v2/centos/blobs/sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc" http.request.useragent="docker/20.10.3 go/go1.13.15 git-commit/46229ca kernel/3.10.0-1160.15.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.3 \(linux\))" http.response.contenttype="application/json; charset=utf-8" http.response.duration=1.422727ms http.response.status=404 http.response.written=157 vars.digest="sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc" vars.name=centos
reverse-proxy_1 | 2021/05/30 05:38:37 [error] 30#30: *9 client intended to send too large chunked body: 1048576+32768 bytes, client: <クライアントIP>, server: example.test, request: "PATCH /v2/centos/blobs/uploads/617a6324-7a93-4212-987e-a48eb501f34d?_state=k8ymp1axNemJM79Vg5G-pRgHuZW_UkkYGREuBH29q5d7Ik5hbWUiOiJjZW50b3MiLCJVVUlEIjoiNjE3YTYzMjQtN2E5My00MjEyLTk4N2UtYTQ4ZWI1MDFmMzRkIiwiT2Zmc2V0IjowLCJTdGFydGVkQXQiOiIyMDIxLTA1LTMwVDA1OjM4OjMyLjQ3NTA4Njg2MVoifQ%3D%3D HTTP/1.1", host: "example.test"
ドキュメントでは client_max_body_size 0;
として制限を無効にしていました。
デフォルトのnginx.conf設定は1M
のため、忘れずに適用しましょう。
② 認証ファイルのフォーマット
詳しくはここを参照
nginxが参照できるパスワードフォーマットはRFC 2307に記載のあるものとのことです。その中にbcryptはなく、対応してないため注意しましょう。
今回は以下の様にMD5でパスワードファイルを作っています。
$ htpasswd -b -c -m htpasswd test test
ちなみに-m
オプションはhtpasswdのデフォルトなのであってもなくてもいいです。
コンテナ立ち上げ
起動します。
$ docker-compose up -d
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------
registry-nginx_registry_1 /entrypoint.sh /etc/docker ... Up 5000/tcp
registry-nginx_reverse-proxy_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:5043->443/tcp, 80/tcp
OK
イメージpush,pull
レジストリにログインします。
$ docker login example.test:5043
Username: test
Password:
WARNING! Your password will be stored unencrypted in /home/test/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
イメージをpushします
$ docker image tag nginx:alpine example.test:5043/test-nginx:alpine
$ docker push example.test:5043/test-nginx:alpine
The push refers to repository [example.test:5043/test-nginx]
4689e8eca613: Pushed
3480549413ea: Pushed
3c369314e003: Pushed
4531e200ac8d: Pushed
ed3fe3f2b59f: Pushed
b2d5eeeaba3a: Pushed
alpine: digest: sha256:480395b7be07df06368c93469df41343c1e6285f5b37d387b5a17f2e8708a2a3 size: 1568
$ curl -u test:test https://example.test:5043/v2/_catalog
{"repositories":["test-nginx"]}
OKです。
次はpullします
### 確認のために既存のイメージを削除
$ docker rmi -f nginx:alpine
$ docker rmi -f example.test:5043/test-nginx:alpine
### pullする
$ docker pull example.test:5043/test-nginx:alpine
alpine: Pulling from test-nginx
Digest: sha256:480395b7be07df06368c93469df41343c1e6285f5b37d387b5a17f2e8708a2a3
Status: Downloaded newer image for example.test:5043/test-nginx:alpine
example.test:5043/test-nginx:alpine
OKです。
では次のパターン...の前に忘れずにコンテナを落としておきましょう。
$ docker-compose down
$ docker-compose ps
Name Command State Ports
------------------------------
$
リバースプロキシ(nginx)を経由しないパターン
ディレクトリ構成
ディレクトリ構成は以下
.
|-- cert
| |-- fullchain.pem
| `-- privkey.pem
|-- data
|-- docker-compose.yaml
`-- registry
|-- htpasswd
`-- no-nginx-config.yml
config.ymlは区別のために名前を変えています。
docker-compose.yaml
version: "3.9"
services:
only-registry:
image: registry:2
ports:
- 5000:5000
volumes:
- ./data:/var/lib/registry
- ./registry/no-nginx-config.yml:/etc/docker/registry/config.yml
- ./registry/htpasswd:/auth/htpasswd:ro
- ./cert/fullchain.pem:/certs/fullchain.pem:ro
- ./cert/privkey.pem:/certs/privkey.pem:ro
no-nginx-config.yml
version: 0.1
log:
accesslog:
disabled: false
level: warn
formatter: text
fields:
environment: test
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
tls: # ※①
certificate: /certs/fullchain.pem
key: /certs/privkey.pem
auth:
htpasswd: # ※②
realm: basic-realm
path: /auth/htpasswd
以下でポイントについて説明します。
①tls
詳しくはここを参照
http:
addr: :5000
tls:
certificate: /certs/fullchain.pem
key: /certs/privkey.pem
docker-registryに直接証明書設定を入れる場合はtlsオプションを設定します。
秘密鍵、証明書のパスを指定します。ホストのパスとごっちゃにならないように注意しましょう
②認証ファイルのフォーマット
詳しくはここを参照
以下の通りbcryptフォーマットしかサポートしていないため、上述したnginxの場合とは異なります。
The htpasswd authentication backed allows you to configure basic authentication using an Apache htpasswd file. The only supported password format is bcrypt. Entries with other hash types are ignored.
以下の様にhtpasswdファイルを作成しましょう。
$ htpasswd -b -B -c test.htpasswd test test
コンテナ立ち上げ
起動します。
$ docker-compose up -d
$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------
registry-only_only-registry_1 /entrypoint.sh /etc/docker ... Up 0.0.0.0:5000->5000/tcp
OK
イメージpush,pull
ログインします。
$ docker login example.test:5000
Username: test
Password:
WARNING! Your password will be stored unencrypted in /home/test/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
pushします
$ docker pull nginx:alpine
$ docker image tag nginx:alpine example.test:5000/test-nginx:alpine
$ docker push example.test:5000/test-nginx:alpine
The push refers to repository [example.test:5000/test-nginx]
058eb06e0efd: Pushed
2f2df3ae0cad: Pushed
2b60f0243825: Pushed
96131b349b16: Pushed
a42a23cd7b07: Pushed
b2d5eeeaba3a: Pushed
alpine: digest: sha256:2e76f4d4e21bae1d012267cff67e032bb006a2cff5c00afa5d4ad011c1855128 size: 1568
$ curl -u test:test https://example.test:5000/v2/_catalog
{"repositories":["test-nginx"]}
OK。pullします。
$ docker pull example.test:5000/test-nginx:alpine
alpine: Pulling from test-nginx
540db60ca938: Already exists
0ae30075c5da: Pull complete
9da81141e74e: Pull complete
b2e41dd2ded0: Pull complete
7f40e809fb2d: Pull complete
758848c48411: Pull complete
Digest: sha256:2e76f4d4e21bae1d012267cff67e032bb006a2cff5c00afa5d4ad011c1855128
Status: Downloaded newer image for example.test:5000/test-nginx:alpine
example.test:5000/test-nginx:alpine
OK!
どちらのパターンでもpush,pullできていますね。
それでは落としておきましょう
$ docker-compose down
$ docker-compose ps
Name Command State Ports
------------------------------
$
自己証明書を使用する
詳しくはここを参照
nginxを経由するしないにかかわらず、自己証明書を使用した場合は以下の様に出力され、registryへログインできません。
$ docker login example.test:5043
Authenticating with existing credentials...
Login did not succeed, error: Error response from daemon: Get https://example.test:5043/v2/: x509: certificate signed by unknown authority
その場合、以下の様に対処します。
### 移動
$ cd /etc/docker/certs.d/
### ディレクトリ作成
$ sudo mkdir example.test:5043
### 証明書ファイル配置
$ ll example.test\:5043/
合計 12
-rw-r--r-- 1 root root 1172 6月 6 14:00 ca.crt
-rw-r--r-- 1 root root 1155 6月 6 14:00 client.cert
-rw-r--r-- 1 root root 1679 6月 6 14:01 client.key
### 接続
$ docker login example.test:5043
Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /home/test/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
無事接続できました。
接続先FQDN + ポート番号でディレクトリを作り、そこに各ファイルを格納しています。
各種ファイルは自己証明書作成時にできたものを持ってきましょう。
ちなみにファイル名が上記以外だとログインに失敗します。ドキュメントに従いましょう
$ sudo mv ca.crt ca.cr
$ sudo mv client.cert client.cet
$ sudo mv client.key client.ky
$ docker login example.test:5043
Authenticating with existing credentials...
Login did not succeed, error: Error response from daemon: Get https://example.test:5043/v2/: x509: certificate signed by unknown authority
終わりに
無事https接続できてよかったです。
次回はhttp APIを叩いてイメージをいろいろ見てみたいと思います。