traefikとnginxでアクセス制限を行う

treafikでIPアドレス制限を行う。

環境

  • traefik 2.8
  • nginx 1.23

ブラックリスト方式

特定のIPアドレスによるアクセスを遮断する。CIDR指定も可能。traefikのmiddlewareとアクセス可否判別用サーバとしてnginxを使う。

以下の3つを参考にした。最初のを検索エンジンで見つけ、あとはそこから辿ったりした:

Traefick how to block a ip
I have a quick write-up on how to use the ForwardAuth middleware to block a list of IPs I really hope this is built in s...
How to block IPs in your Traefik Proxy Server
DevOps and Security, but really all things Linux
Traefik ForwardAuth Documentation - Traefik
In Traefik Proxy, the HTTP ForwardAuth middleware delegates authentication to an external Service. Read the technical do...

これらを参考し、必要な設定を追加したものを書き留めておく。

この方式の流れ

この方式の流れの確認:

  • traefikにWebサーバ宛のリクエストが着く。
  • traefikが一旦アクセス可否チェック用サーバ(のURL)にリクエストを転送する。
  • アクセス可否チェック用サーバで(IPアドレスに基づき)200403などを返す。
  • 200が返されたらtraefikは通常通りWebサーバにリクエストを渡す。
  • 403が返されたらtraefikはそれをそのままリクエスト元に返す。

のような感じ。traefikのforwardAuthというミドルウェアを使うので、これの設定と、アクセス可否チェック用サーバ(nginxを使った)を立てておけばいいことになる。

traefikのミドルウェアの指定はサービス単位で行うので、適応したいサービスごとにアクセス可否を行える。アクセス可否チェック用サーバは(URLのpathで分岐させればいいので)1つでいい。

実装

既存のサービス側での実装と、新規に(もしくは既存の)アクセス可否チェック用サーバを用意する。配置はどのようにしてもいい。独立させておいた方がいい気はする。

サービス側で行うこと

既存サービスではラベルを追加するだけでいい。構成によってはネットワーク追加変更などもする必要がある。

次のようにラベルを追加する:

services:
  awebserver:
    build: ./nginx
    labels:
      - "traefik.http.middlewares.blockiplist.forwardauth.address=http://ipfilter:8080/traefik"
      - "traefik.http.routers.<app_name>.middlewares=blockiplist@docker"
      - "..."
    networks:
      - traefik_network

networks:
  traefik_network:
    external: true

forwardAuthで外部サーバのアドレスとしてhttp://ipfilter:8080/traefikを指定する。アクセス可否の判断をこのアドレスへ委ねる。 そして、サービスがこのミドルウェアを使うように指定する。

ミドルウェアを複数使う場合は、- "traefik.http.routers.<app_name>.middlewares=mware1@docker,mware2@docker"のようにカンマ区切りで指定できる。確認した限りでは、最初に書いたものから実行されているようだ。

ipfilterと通信できるように同一のネットワーク(traefik_network)に接続しておく。

チェック用サーバで行うこと

アクセス可否チェック用サーバは新規に立てるので、上より分量は多め。docker-compose.ymlとnginxのconfファイルを用意する。

まずはdocker-compose.ymlから:

services:
  ipfilter:
    image: nginx:alpine
    container_name: ipfilter
    expose:
      - "8080"
    volumes:
      - ./conf/:/etc/nginx/conf.d/
    networks:
      - traefik_network

networks:
  traefik_network:
    external: true

独立してupするので、traefikに繋がっているexternalなネットワークに接続しておく。ポート番号は上述のサービスで指定したものと一致していればなんでもよく、ここでは8080exposeしておいている。参考元ではportsでホストのポートと繋いでいたと思うが、それは不要のはず。

参考元の通りcontainer_nameを付けているが、もしかしたら不要かもしれない(未実験)。

そして次にconfファイルの指定:

# ./conf/default.conf

server_tokens off;
server {
    listen 8080;

    location /traefik {
        add_header Content-Type "default_type text/plain";

        if ($blocked_ip) {
            return 403;
        }
        return 200 "OK";
    }
}

参考元とそれほど変わらない、シンプルにtraefikのミドルウェアで指定したアドレスに対応するロケーションディレクティブを定義する。$blocked_ipは次ファイルのgeoで定義するアクセス可否用の変数で0以外なら真となる。

次に$blocked_ipを定義するconfファイル:

# ./conf/blocklist.conf

# geo使って可否を判断
#  0を設定すると偽、0以外を設定すると真になる。

geo $blocked_ip {
    default        0;
    # docker network inspect traefik_networkなどで確認できるtraefikのIPアドレスが存在する範囲を指定。
    # proxyを指定していると、XFFを見てくれるようになる。
    # proxy   172.18.0.0/16;
    # 調べるのが面倒ならプライベートIPアドレスでもいいかも…
    proxy       172.16.0.0/12;

    # sample here
    # 127.0.0.1    1;
    # 192.168.0.0/16 1;
    # 10.0.0.0/8     1;

    # ::1            2;
    # 2001:0db8::/32 1;
}

# 異なる制限でのサービスを増やすならgeoディレクティブを増やす。変数名とproxyの指定を忘れないこと。

参考元と異なる点として、proxyを設定している。traefikから送られてくるので、x-forwarded-forにアクセス元IPアドレスがある。geoでは、デフォルトで$remote_addrを基に変数を格納するので、proxyを設定しないとtraefikのIPアドレスを判別に使ってしまう。$proxy_add_x_forwarded_for(だったかな?)を基に変数を決めるようにしても良かったが、カンマ区切りなどで予期しない動作をしないとも限らないので、proxy機能を使ってnginx任せにした。

proxyにはtraefikのIPアドレスを指定する。docker network inspect traefik_networkで調べられる。 この構成ではプライベートIPアドレス内で繋がっているので、それを指定している。本当はもう少し範囲を狭めるべきかもしれない。しかしプライベートIPアドレスに偽装するのは難しいだろうから大丈夫な気はする。

nginxのgeoの詳細については公式ドキュメントを参照:

Module ngx_http_geo_module

その他

他のサービスで異なるフィルタを使いたい場合は、新しいgeoを追加して新しいlocationでそのgeoを使うようにして、そのアドレスをtraefikのforwardauthに指定すればいい。

また、フィルタに引っかかって403を返すときはアクセス可否チェック用サーバが返したレスポンスをそのままクライアントに渡すようになっている。 エラー専用ページを返すなら、アクセス可否チェック用サーバでエラーページを設定する必要がある。

ホワイトリスト方式

上のgeoで返す値を逆転させればホワイトリスト形式になるが、traefikのmiddlewareにそのままのipwhitelistというミドルウェアがある。

公式ドキュメント:

Traefik HTTP Middlewares IPWhiteList - Traefik
Learn how to use IPWhiteList in HTTP middleware for limiting clients to specific IPs in Traefik Proxy. Read the technica...

nginxのgeoのproxyに似たオプションもあるようだ。depthなどで信頼するプロキシの数などを指定できるようだ(実際はdepth-1になりそう)。(未実験)

おわり

ホワイトリストに関しては、全然書いていないがこちらはメインではないので仕方がない。

あまりブラックリスト方式を使いたくはないが必要な場合はあるので今回は取り扱った。先人の方々には感謝しています。

traefikのミドルウェアは眺めてみようと思う。

以上です。


Amazonアソシエイト:

https://amzn.to/3aNkXgd
タイトルとURLをコピーしました