sankantsuのブログ

技術メモ・競プロなど

Docker daemon のプロキシ設定における特殊文字の扱い

概要

自分の勤務先の環境では、社内のサーバーから外部のネットワークと通信する際プロキシを経由する必要がある。

各種アプリケーションを利用する際に個別にプロキシ設定が必要となる場合があるが、 Docker を利用する際も docker pull などで外部のネットワークから docker image を取得するためにプロキシの設定が必要である。 この設定の際に、プロキシ認証のユーザー名に含まれる特殊文字の扱いで詰まったので、対処法をメモしておく。

※ docker および systemd 等については詳しくないので、不正確な記述等あれば教えてください

環境

Ubuntu 20.04

プロキシ設定の基本

まずは、docker に限定せずコマンドラインツール一般でのプロキシ設定について確認しておく。

シェルから起動する多くのコマンドラインツール (curl, wget 等) は、http_proxyhttps_proxy という名前の環境変数からプロキシ設定を取得する。 たとえば、プロキシサーバーが proxy.example.com でプロキシ接続に利用するポートが 8080 である場合

export http_proxy=http://proxy.example.com:8080
export https_proxy=http://proxy.example.com:8080

のように設定すれば良い。

ユーザー名、password の認証がある場合には、

export http_proxy=http://<username>:<password>@proxy.example.com:8080

のような形式にする。

プロキシ設定における特殊文字

認証情報の中に、@ のような認証情報とサーバーのアドレスを区切るのに使われている文字や、その他 HTTP の URL 上で特殊な意味を持つ文字を含む場合は注意を要する。

例えば、弊社ではユーザー名にメールアドレスを使う設定になっているが、 たとえば、メールアドレスが foo@example.com, パスワードが 123 であるとして、

# 正しくない設定
export http_proxy=http://foo@exmaple.com:123@proxy.example.com:8080

のように書いてしまうと、ユーザー名に含まれる @ が認証情報とアドレス部分の区切り文字として解釈され、正しく設定できない。

正しく設定するには、@ を percent encoding して、

export http_proxy=http://foo%40exmaple.com:123@proxy.example.com:8080

のようにする必要がある。

Docker daemon のプロキシ設定

Docker のプロキシ設定と言っても、docker client 側と docker daemon 側それぞれについて設定が必要になる。 外部の registry から docker image をダウンロードしてくるための通信を発行したりするのは daemon 側であり、 今回はこの docker daemon 側のプロキシ設定について注目する。

公式ドキュメント含め、すでに詳しい解説は多くある。

Configure the daemon with systemd | Docker Documentation

プロキシのある環境でDockerを動かす方法 - Qiita

Set proxy on docker - Stack Overflow

簡単にまとめると、systemctl edit docker で docker の設定ファイルを開いて

[Service]
Environment="http_proxy=http://<username>:<password>@proxy.example.com:8080"

の内容で保存し、

systemctl daemon-reload
systemctl restart docker

でデーモンを再起動すれば良い。(sudo は必要に応じてつけること)

特殊文字に対する配慮

ユーザー名 foo@example.com, パスワード 123 で認証情報を設定する場合、一見

# 正しくない設定
[Service]
Environment="http_proxy=http://foo%40example.com:123@proxy.example.com:8080"

とすれば良さそうである。 しかし、実際この設定だと正常に動作しない。

docker の公式 documentをよく見ると、次のような注意書きがある。

Special characters in the proxy value, such as #?!()[]{}, must be double escaped using %%. For example:

[Service]
Environment="HTTP_PROXY=http://domain%%5Cuser:complex%%23pass@proxy.example.com:3128/"

したがって、以下のように設定するのが正しい

# 正しい設定
[Service]
Environment="http_proxy=http://foo%%40example.com:123@proxy.example.com:8080"

少しだけ深堀り

# 正しくない設定
[Service]
Environment="http_proxy=http://foo%40example.com:123@proxy.example.com:8080"

だと何がまずいのか、原因を探ってみる。 この状態で、

systemctl show docker --property=Environment

を叩いて設定を確認すると、Environment= と空になっている様子である。

/var/log/syslog (または /var/log/messages) を確認すると、以下のようなエラーが確認できる。(一部加工している)

Jul 28 10:39:12 <hostname> systemd[1]: /etc/systemd/system/docker.service.d/override.conf:2: Failed to resolve specifiers in http_proxy=http://foo%40example.com:123@proxy.example.com:8080, ignoring: Invalid slot

failed to resolve specifier と、specifier が解釈できないという趣旨のエラーが見れる。

調べてみると、systemd の設定ファイル内では % から始まる文字列が "specifier" として特別に解釈されるらしく、 % そのものの文字を埋め込みたい場合には、%% のように重ねて書く必要があるということらしい。 詳しい documentation は以下にある。

systemd.exec

systemd.unit

ほか参考記事

www.javacodegeeks.com