Docker Swarm環境においてクライアントIPを参照したいときの注意点。
例えばnginxサービスとかでリバースプロキシ側で特定のクライアントIPを除いてBasic認証を掛けたいのにうまくいかない場合など。
nginxで特定IPを除いてBasic認証を掛ける場合
例としてHTTPポート(80/443)をフォワードしているnginxサービスがあるとします。
そこで単純に以下のような設定でBasic認証掛けようとすると、指定したIPからのアクセスに対する認証スルーが効きいてくれません。
satisfy any;
# 認証スルーさせたいリモートIPアドレス
allow XXX.XXX.XXX.XXX;
deny all;
auth_basic "basic authentication";
auth_basic_user_file /run/secrets/nginx-proxy-htpasswd;
include /etc/nginx/network_internal.conf;
リモートIPの参照がうまくいかない理由
なぜ設定がうまく動作していないかというとSwarmモードでは、ポートフォワードするとingressルーティング・メッシュという、いわばロードバランサを経由してアクセスを受けるためです。
なので単純にリモートIPを拾おうとすると本来のクライアントIPではなく、そのロードバランサのIPを参照することとなります。
AWSのALBなどではX-Forwarded-For
ヘッダを付加してくれるので、そちらを参照すれば良いのですが、
残念ながらingressルーティング・メッシュはこのような機能を備えていないようです。
ルーティング・メッシュを迂回する
これを回避する方法としては、単純なものとして上記のドキュメントにもあるとおりルーティング・メッシュの迂回
をする必要があります。
サービスのports
の設定を詳細にして、mode: host
にしましょう。
以下はcomposeファイルの書き方例の抜粋です。
ports:
- published: 443
target: 443
protocol: tcp
mode: host
これでホストPCのネットワークに直接フォワードされるようになるので、クライアントIPが正しく判別することが可能になります。
ただし、同時にルーティング・メッシュによるノードを跨いだロードバランシングは効かなくなります。
他の解決方法
docker-ingress-routing-daemonというデーモンを使うという手もあるようです。
こちらは真のクライアントIPがDocker Swarm配下のサービスに伝達されるようになるものらしく、
ルーティング・メッシュの利点を生かしたまま目的を果たせそうです。
ただし、こちらはDockerよりも上位のレイヤーに導入が必要となるため、導入はやや面倒かもしれませんね。
以上。
コメント