メインコンテンツまでスキップ

Docker Swarm でユーザーの IP アドレスを取得できない問題

Docker の Swarm mode には、ルーティングメッシュと呼ばれる機能があります。この機能により、外部からのアクセスをロードバランシングすることができます。ルーティングメッシュにより、接続先のノード自身がタスクを実行していなくても、タスクが利用可能なノード上へリクエストを転送してくれるようになります。

この機能は便利であり、Swarm を冗長構成にするのであれば、必要不可欠な機能の一つです。ただし、ルーティングメッシュが有効になっている場合、アクセス元の IP アドレスを取得できない可能性がある問題が存在しています。正確には取得できますが、接続元のIPアドレスではなく、アクセスを受け付けたノードの IP アドレスが取得されます。

最初に提示した画像を元に説明を行います。

仮に 203.0.113.10 から Node-03(192.168.10.102:8080)へアクセスがあったとします。Node-03 上に利用可能なタスクがないため、全てのリクエストは利用可能なタスクの存在する異なるノードへ転送されます。そのため Node-01 もしくは Node-02 の nginx.1 がアクセスを受けます。この時、アクセスを受けた nginx.1 でアクセスログを確認すると、接続元の IP アドレスは Node-03 の IP アドレスである 192.168.10.102 が記録されています。

という訳で、ルーティングメッシュによりリクエストが転送された場合、転送元のノードの IP アドレスが記録されてしまいます。

一般的に元のIPアドレスを取得場合、http では X-Forwarded-For と呼ばれるプロキシを通した際に元の IP アドレスを保持する仕組みを利用し、L4 では Proxy Protocol と呼ばれるロードバランサを通した際に元の IP アドレスを保持する仕組みを利用します。

しかしながら、ルーティングメッシュによって転送されたリクエストは、X-Forwarded-For や Proxy Protocol が対応してないんですね。類する代替手段が用意されている訳でもないため、Docker Swarm のみで元の IP を取得することは不可能です。

この問題はすでに把握されており、issue は立ち上げられています。しかし、2016年から開かれ続けており、すぐに解決される問題ではない気がします。

Unable to retrieve user’s IP address in docker swarm mode · Issue #25526 · moby/moby

Proxy Protocol のサポートも同様に issue は立ち上がっているものの、すぐに解決される問題ではなさそうです。

Proxy Protocol support in Swarm ingress · Issue #39465 · moby/moby

対処法1

この問題に対する対処法として、ルーティングメッシュの無効化があります。しかし、Overlay Network の利点を失うことになる点は注意してください。

Docker-compose の場合、long syntax でポートを以下のように指定することでルーティングメッシュを無効化することができます。

- target: 80
published: 80
protocol: tcp
mode: host
- target: 53
published: 53
protocol: udp
mode: host

mode を省略した場合、もしくは、ingress を設定した場合はルーティングメッシュが有効になります。

Docker Swarm を使う意味がよく分からなくなりますが、クラスタの外側に HAProxy のようなロードバランサを設置してあげることで、ルーティングメッシュみたいなことはできるようになります。

対処法2

docker-ingress-routing-daemon なるものを利用することで、元のIPアドレスを取得できるようになるらしいですが、使ったことがないので詳しいことはよく分かりません。少なくとも、私は複雑になり過ぎるので使おうと思いませんでした。

GitHub - newsnowlabs/docker-ingress-routing-daemon: Docker swarm daemon that modifies ingress mesh routing to expose true client IPs to service containers