2020/11/10
[EKS]NLB+NginxでClientのIPアドレスを取得する
概要
私の担当しているシステムでは以下のような構成を取っています。
- パターン① cloudflareなし
- browser (client) -> ELB [nlb service] -> kube-proxy -> Web Sever (Nginx) [pod] -> kube-proxy [node port service] -> Application [pod]
- パターン② cloudflareつき
- browser (client) -> cloudflare proxy -> ELB [nlb service] -> kube-proxy -> Web Sever (Nginx) [pod] -> kube-proxy [node port service] -> Application [pod]
ratelimitとか、IPアドレスによるアクセス制限とか、そんな類の実装のため、アクセスしているユーザーのIPアドレスを取得したいと言うことが往々にしてあります。
今回は、Network Load BalancerをProxyとして間に挟んだ場合の対応を行いました。
Network Load BalancerはHTTPのプロトコルを解釈できないので、X-Forwarded-forをうまく解釈してNLB自身のIPアドレスをうまくリレーすることができません
ですが、NLBには便利な機能があります。
“If you specify targets by instance ID, the source IP addresses provided to your applications are the client IP addresses. However, if you prefer, you can enable proxy protocol and get the client IP addresses from the proxy protocol header.”
NLBのターゲットグループのターゲットにEC2のインスタンスIDを指定しておけば、接続元IPがapplicationにわたるようになるとのことでした。
インスタンスIDを指定してもしなくても、proxy protocolを有効にして、proxy protocol headerから接続元のIPアドレスを取得することもできとのことです。
ただ注意しなくてはいけないのが、EKSのNLBタイプのserviceを使う場合、NLBとの間に kube-proxy
が挟まってしまうため(だと思っていますが・・)、
パターン①のように、NLBの前にX-Forwarded-forなどをリレーするProxyがない場合は、Proxy ProtocolからIPアドレスを取得する必要がありました。
実装内容
パターン① cloudflareなし
k8s serviceの定義
- EKSでは、
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
のannotationを指定することで、NLBを使ってserviceを作成することができます。 - AWS上でのPROXYプロトコルのサポートにある通り、
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
を指定して、proxy protocolを有効にしておきます。(既存のserviceの場合は、applyしても反映されなかったので、Managementコンソールから直接有効にしました。)
apiVersion: v1
kind: Service
metadata:
name: your-service-name
namespace: your-namespace
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: http
- name: https
port: 443
targetPort: https
selector:
app: your-selector-name
nginxのconfig
以下のような感じになります。
real_ip_header proxy_protocol
を指定することで、proxy_protocolから接続元のIPアドレスを抽出して、remote_addr
変数に設定するようにしています
server {
listen 0.0.0.0:443 proxy_protocol default_server; # proxy_protocolを追加
listen [::]:443 proxy_protocol default_server; # proxy_protocolを追加
server_name _;
...(省略)...
set_real_ip_from XX.XX.XX.XX/XX; # your vpc range
real_ip_header proxy_protocol; # proxy_protocolから接続元IPを取得
real_ip_recursive on;
location / {
...(省略)...
proxy_set_header X-Real-IP $remote_addr; # 接続元IPをX-Real-IPヘッダに設定
proxy_set_header X-Forwarded-For $remote_addr; # 接続元IPをX-Forwarded-Forヘッダに設定
...(省略)...
}
}
パターン② cloudflareつき
cloudflareなどのproxyがNLBの前に配置されている場合は、proxy_protocolから接続元IPを取得する必要はなく、cloudflareのproxyによってX-Forwarded-For
ヘッダが設定されているため
そちらを解釈すれば問題ありません。
k8s serviceの定義
こちらはパターン①と変わりません。
nginxのconfig
server {
listen 0.0.0.0:443 proxy_protocol default_server;
listen [::]:443 proxy_protocol default_server;
server_name _;
...(省略)...
set_real_ip_from XX.XX.XX.XX/XX; # your vpc range
# see https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses-with-mod-cloudflare-
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
real_ip_header X-Forwarded-For; # X-Forwarded-Forから接続元IPを取得
real_ip_recursive on;
location / {
...(省略)...
proxy_set_header X-Real-IP $remote_addr; # 接続元IPをX-Real-IPヘッダに設定
proxy_set_header X-Forwarded-For $remote_addr; # 接続元IPをX-Forwarded-Forヘッダに設定
...(省略)...
}
}
以上になります。
関連する記事
EKSのロードバランサーでIPアドレスのアクセス制限を設定する
EKSで構築しているネットワークロードバランサータイプのServiceにIP Whitelistを設定しました。
EKSのバージョンをアップグレードする
開発に利用しているEKSのバージョンをアップグレードしました
[EKS]NLB+NginxでClientのIPアドレスを取得する
EKS上にdeployしたnginxをNetwork Load Balancerを使って外部公開した時に接続元のIPアドレスを取得する対応をしました
EKSでServiceAccount毎にIAMロールを作成してpod毎に権限を変更する
IAMのリソースをterraformで作成し、service accountに紐付けることによってpodのAWSリソースのアクセス権を細かく定義できるようにしました