はじめに
色々とあって社内にDocker Hubのようなレジストリサーバーがあったほうがいいよね、ってことになりました。
社内にはすでにLDAPが稼働していて、httpsにしてLDAP認証できたらいいよね、と思いました。
数日溶かしました
調べたところ以下のような方式がありそうでした。
- docker_auth を使う
- ngx_auth_mod を使う
いずれの方法もなんだか思いの外ガチャガチャしないといけなさそうでめんどくさそう。
調べているうちにBasic認証であればLDAPを認証基盤として使うことができ、かなり可能性を感じました。
が、そんな古臭い方式より◯◯のほうがいいんだぜ!っていう内容がたくさん出てくるのですが、それらの内容もゴリゴリすぎてやってられないなぁ…と思ったり。
registryの管理APIを直に叩くのはめんどくさすぎるのでWebコンソールが欲しいし、WebコンソールがLDAPに対応しているぜ!っていうサイトもいくつか見たのですが、設定内容がゴリゴリでそっ閉じを繰り返し、気づけば数時間経っていました。
オンプレにレジストリを建てることが時代錯誤になっているためか出てくる情報がいずれも古く調べている自分も生きた化石なのだろうとか考えていました。
でも、今求められているから。
もっと手軽なものを求めているんだよぉぉ…。
結果、諦めて次の構成にすることにしました。
[認証用のプロキシ]-[Webコンソール]-[レジストリ]
古の技術を前面に使うことで無理やりWebコンソールに認証機能を付与するという…。
Basic認証にアレルギーがなければ社内向けのシステムは全部これでいいじゃん、って思いました。
神(or 紙)構成です。
使用技術
- docker (+compose)
- Apache
- LDAP (既存)
社内のサーバー群はrootless docker + LDAPで運用されているのですが、レジストリサーバー運用者が誰でも同一のコンテナを操作するためにrootful dockerを使用しました。
構築手順
ログインしてレジストリサーバー用のディレクトリを作る
- サーバーにrootでログインする
mkdir local-registry
cd local-registry
自己署名証明書を作る
mkdir certs
cd certs
openssl genrsa 4096 > local-registry.key
-
vi san.txt
subjectAltName=DNS:local-registry.example.com
-
openssl req -new -key local-registry.key > local-registry.csr
- それっぽい内容を入れる
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Osaka
Locality Name (eg, city) []:Osaka
Organization Name (eg, company) [Internet Widgits Pty Ltd]:HOGEHOGE
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
openssl x509 -days 3650 -req -sha256 -signkey local-registry.key -extfile san.txt < local-registry.csr > local-registry.crt
cd ..
apache用の設定ファイルを準備する
mkdir httpd_config
vi httpd_config/httpd.conf
LoadModule headers_module modules/mod_headers.so
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
LoadModule ldap_module modules/mod_ldap.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule unixd_module modules/mod_unixd.so
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>
<IfModule unixd_module>
User daemon
Group daemon
</IfModule>
ServerAdmin you@example.com
ErrorLog /proc/self/fd/2
LogLevel warn
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
CustomLog /proc/self/fd/1 common
</IfModule>
ServerRoot "/usr/local/apache2"
Listen 443
<Directory />
AllowOverride none
Require all denied
</Directory>
<VirtualHost *:443>
ServerName local-registry.example.com
SSLEngine on
SSLCertificateFile /usr/local/apache2/certs/local-registry.crt
SSLCertificateKeyFile /usr/local/apache2/certs/local-registry.key
SSLCompression off
SSLProtocol all -SSLv2 -SSLv3 -TLSv1
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLHonorCipherOrder on
Header always set "Docker-Distribution-Api-Version" "registry/2.0"
Header onsuccess set "Docker-Distribution-Api-Version" "registry/2.0"
RequestHeader set X-Forwarded-Proto "https"
ProxyRequests off
ProxyPreserveHost on
# no proxy for /error/ (Apache HTTPd errors messages)
ProxyPass /error/ !
ProxyPass / http://registry-ui:81/
ProxyPassReverse / http://registry-ui:81/
<Location />
AuthName "LDAP Authentication"
AuthType basic
AuthBasicProvider ldap
AuthLDAPURL ldap://<LDAP server's ip address>/cn=accounts,dc=example,dc=com?uid
AuthLDAPBindDN "uid=admin,cn=users,cn=accounts,dc=example,dc=com"
AuthLDAPBindPassword "<LDAP Administrator password>"
# Read access to authentified users
<Limit GET HEAD>
Require valid-user
</Limit>
# Write access to docker-deployer only
<Limit POST PUT DELETE PATCH>
Require ldap-group cn=registry-operator,cn=groups,cn=accounts,dc=example,dc=com
</Limit>
</Location>
</VirtualHost>
レジストリサーバーを設定する
mkdir -p registry/data
mkdir registry/config
vi registry_config/garbage-collection-config.yaml
version: 0.1
storage:
filesystem:
rootdirectory: /var/lib/registry
準備したもろもろでdocker composeを設定する
vi docker-compose.yaml
version: '3.8'
services:
registry-ui:
image: joxit/docker-registry-ui:main
restart: always
environment:
- SINGLE_REGISTRY=true
- REGISTRY_TITLE=My Local Registry
- DELETE_IMAGES=true
- SHOW_CONTENT_DIGEST=true
- NGINX_LISTEN_PORT=81
- NGINX_PROXY_PASS_URL=http://registry-server:5000
- SHOW_CATALOG_NB_TAGS=true
- CATALOG_MIN_BRANCHES=1
- CATALOG_MAX_BRANCHES=1
- TAGLIST_PAGE_SIZE=100
- REGISTRY_SECURED=false
- CATALOG_ELEMENTS_LIMIT=1000
container_name: registry-ui
registry-server:
image: registry:2.8.2
restart: always
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Origin: '[http://local-registry.example.com]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]'
REGISTRY_HTTP_HEADERS_Access-Control-Credentials: '[true]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]'
REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]'
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
volumes:
- ./registry/data:/var/lib/registry
- ./registry_config:/etc/registry
container_name: registry-server
auth-proxy:
image: httpd:2.4
ports:
- 443:443
volumes:
- ./httpd_config:/usr/local/apache2/conf
- ./certs:/usr/local/apache2/certs
docker compose up -d
要るのか?これ
なんとなくcronにgarbage-collectionを仕込んでおく。
crontab -e
0 0 * * * docker compose exec registry-server registry garbage-collect -m /etc/registry/garbage-collection-config.yaml
試しに動かしてみてもtagがないリポジトリが消えてないような気がする。
容量が削減されていそうなので、良しとしよう。
使い方
初期設定
- 安全でないリポジトリを許可する
-
vi ~/.config/docker/daemon.json
|vi /etc/docker/daemon.json
-
下の内容を既存の設定にマージする。
{
"insecure-registries":[ "local-registry.example.com" ]
}
-
dockerを再起動する
-
systemctl --user restart docker
|systemctl restart docker
-
-
ログインする
docker login local-registry.example.com
LDAPアカウントでログインする、すごい。
Username: <Enter your LDAP account>
Password: <Enter your LDAP account's password>
Login Succeeded
あとは自由にpush/pullする!
docker pull ubuntu:latest
docker tag ubuntu:latest local-registry.example.com/ubuntu:latest
docker push local-registry.example.com/ubuntu:latest
docker pull local-registry.example.com/ubuntu:latest
作業したサーバーにhttps
でアクセスするとWebコンソールが開けます、すごい。
おまけ
独自の都合でプルスルー構成にすることはできませんでした。
制限の無い方は次の設定をdaemon.jsonに追加するともっと幸せになれるそうです。
{
"registry-mirrors": ["https://<my-docker-mirror-host>"]
}
最後に
この内容は数年に一度誰かを喜ばせることが出来るかもしれません。
そうだと嬉しいです。