Docker Swarm上のWebサービスでIPアクセス制限が効かない原因について

Docker

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を参照することとなります。

swarm モード・ルーティング・メッシュを使う — Docker-docs-ja 19.03 ドキュメント

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というデーモンを使うという手もあるようです。
newsnowlabs/docker-ingress-routing-daemon: Docker swarm daemon that modifies ingress mesh routing to expose true client IPs to service containers

こちらは真のクライアントIPがDocker Swarm配下のサービスに伝達されるようになるものらしく、
ルーティング・メッシュの利点を生かしたまま目的を果たせそうです。
ただし、こちらはDockerよりも上位のレイヤーに導入が必要となるため、導入はやや面倒かもしれませんね。

以上。

スポンサーリンク
管理人

システムえんじにゃー🐈
趣味はエレキギターなど。作曲したい。
WoWs/プリコネ
記事に関する質問はお気軽にどうぞ。

surface0をフォローする
surface0をフォローする
Rain or Shine

コメント

タイトルとURLをコピーしました