はじめに
IBM Kubernetes Service(以降IKSとする)でデプロイしているアプリにIPアドレス制限(アプリに対し特定のIPアドレスを持つユーザーのみからのアクセスを許可する)の要望があり検証を行ったため、手順等をQiitaにまとめてみました。
参考程度に読んでいただければと思います。
IKS検証環境
バージョン1.24.10
クラシック・クラスター(VPCクラスターの場合少し手順が異なるかと思いますのでご注意ください)
前提
- IBM CloudにてIKSクラスターが作成されていること
- IKSクラスター内にIPアドレス制限を実装したいアプリがデプロイされていること(参考:CLI でアプリをデプロイする方法)。
概要
IKSでデプロイしたアプリに対してIPアドレス制限を実装する手段はいくつかあると思いますが、今回自分が試したのは以下の2つです。
まず前提として、アプリはロードバランサーで設定を行うことでIP制限を実装することができます。
IKSではロード・バランサーをServiceかIngressで実装することが可能です。
Serviceでロード・バランサーを実装する場合はネットワーク・ロード・バランサー(NLB)、Ingressでロード・バランサーを実装する場合はアプリケーション・ロード・バランサー(ALB)になります。
よって、IKSでIPアドレス制限を実装する手段は2つあることになります。
参考: アプリの公開サービスの選択
どちらの手段を取るかについては、ほぼイコールでどちらでロード・バランサーを実装するかという判断になります。
ルーティングルールをカスタマイズしないのであれば「1.ServiceでのIPアドレス制限」を選択可能ですが、Ingressを使用してルーティングルールを実装する(アプリをhttps化したい・エラー画面を表示させたいなどの場合)は「2.IngressでのIPアドレス制限」での実装になります。
どちらを取るか迷われた場合は基本的に「2.IngressでのIPアドレス制限」で実装する方針で問題ないかと思います。
ご自身のアプリの要件に沿って実装方針を決め、それぞれの手順を踏んでください。
事前準備
- デプロイに必要なIBM Cloud CLIとKubernetes CLIのセットアップを完了させる(参考:CLI のセットアップ)
- アクセスを許可するIPアドレスとレンジを決めておく
1. ServiceでのIPアドレス制限
まずはServiceでのIPアドレス制限を実装する手順についてです。
こちらは公式のドキュメントにセットアップ手順が書いてあるので、こちらに沿って実装していきます。
手順概要
① アカウントでプロビジョンされているサブネットの確認
② NLBにてIP制限をする設定を組み込んだServiceを作成する
③ Serviceが作成されたことを確認する
④ IPアドレス制限が機能していることを確認する
① アカウントでプロビジョンされているサブネットの確認
以下のコマンドを実行します。
<クラスターID>としている箇所はご自身のクラスターのIDに置き換えた上で実行をしてください。
$ ibmcloud ks cluster get --cluster <クラスターID> --show-resources
出力内容からサブネット情報を確認します。
$ ibmcloud ks cluster get --cluster <クラスターID> --show-resources
クラスター <クラスターID> とそのすべてのリソースを取得中...
OK
名前: sample-cluster
ID: <クラスターID>
状態: normal
状況: All Workers Normal
作成日: 2022-07-06T09:44:43+0000
ロケーション: tok02
...
サブネット VLAN //以下の値を参照する
VLAN ID サブネット CIDR パブリック ユーザー管理
2234945 xxx.xxx.xxx.xxx/xx false false
2234943 xxx.xxx.xxx.xxx/xx true false
② NLBにてIP制限をする設定を組み込んだServiceを作成する
ドキュメントを参考に、以下のようなyamlファイルを作成します。(名前はmynlb.yamlとします)
apiVersion: v1
kind: Service
metadata:
name: myloadbalancer
annotations:
service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: <public_or_private>
service.kubernetes.io/ibm-load-balancer-cloud-provider-vlan: "<vlan_id>"
spec:
type: LoadBalancer
loadBalancerSourceRanges:
- "<アクセスを許可するIPアドレスのレンジ>"
selector:
<selector_key>: <selector_value>
ports:
- protocol: TCP
port: <port>
targetPort: <port> # Optional. By default, the `targetPort` is set to match the `port` value unless specified otherwise.
置き換えが必要な設定値の説明は以下の通りです。
- service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type
NLBのタイプをprivateまたはpublicから選択します。今回サービスはインターネットからのアクセスが想定されるためpublicを選択します。 - service.kubernetes.io/ibm-load-balancer-cloud-provider-vlan
前の手順で出力さえた情報のうち、publicのVLAN IDを記載します。 - loadBalancerSourceRanges
アクセスを許可するIPアドレスのレンジを記載します。 - selector
アプリをデプロイしたyamlファイルの spec.template.metadata.labels セクションで使用した、ラベル・キー(<selector_key>)と値 (<selector_value>)を記載します。 - port
Serviceがlistenするportの番号です。アプリをデプロイしたyamlファイルの、spec.containers.portsセクションのcontainerPortの値を記載します。
例として、port 3000を指定してデプロイしたアプリ「app: sample-app」に対するServiceが「111.222.333.0/24」のIPアドレスのレンジからのアクセスを許可するyamlファイルは以下のようになります。
apiVersion: v1
kind: Service
metadata:
name: myloadbalancer
annotations:
service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: publicc
service.kubernetes.io/ibm-load-balancer-cloud-provider-vlan: "2234943"
spec:
type: LoadBalancer
loadBalancerSourceRanges:
- "111.222.333.0/24"
selector:
app: sample-app
ports:
- protocol: TCP
port: 3000
targetPort: 3000
※ IPアドレスを複数記載する場合は文頭にハイフン(-)をつけた上で改行し追記してください。
yamlファイルができたら以下コマンドにてServiceを作成します。
$ kubectl apply -f mynlb.yaml
③ Serviceが作成されたことを確認する
NLB サービスが正常に作成されたことを確認します。 サービスが作成され、アプリが利用可能になるまでに数分かかることがあります。
$ kubectl describe service myloadbalancer
実行すると以下のような出力があります。
出力内容から、EventにてServiceの作成が完了しているか、yamlにて設定した内容が反映されているかを確認してください。
$ kubectl describe service myloadbalancer
NAME: myloadbalancer
Namespace: default
Labels: <none>
Selector: app=sample-app
Type: LoadBalancer
Location: tok02
IP: 172.21.xxx.xxx
LoadBalancer Ingress: 169.xx.xxx.xxx
Port: <unset> 3000/TCP
NodePort: <unset> 32040/TCP
Endpoints: 172.30.xxx.xxx:8080
Session Affinity: None
External Traffic Policy: Cluster
LoadBalancer Source Ranges: 111.222.333.0/24
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- ---- ------ -------
10s 10s 1 {service-controller } Normal CreatingLoadBalancer Creating load balancer
10s 10s 1 {service-controller } Normal CreatedLoadBalancer Created load balancer
④ IPアドレス制限が機能していることを確認する
yamlファイルにてloadBalancerSourceRangesに記載したアクセスを許可するIPアドレスからアプリにアクセスし(前手順にて出力された"LoadBalancer Ingress"のIPアドレスからアクセスできます)、アクセスができることを確認してください。
続いてloadBalancerSourceRangesに記載されていないIPアドレスからアプリにアクセスすると、接続が拒否されアプリにアクセスができないことを確認してください。
手順は以上です。
補足
EKSでの話のようですが、こちらの記事にて「設定可能なIPアドレスは60件までで、それ以上を設定すると機能しなくなる」との記載がありましたので、たくさんIPアドレスを設定する場合はご注意ください。
2. IngressでのIPアドレス制限
こちらの手順についてはIKSのドキュメントに記載がないため、こちらのEKSでの実装に関する記事を参考させていただき検証を行いました。
事前準備
事前準備としてアプリに接続するためのServiceと、IPアドレス制限前のIngressを作成します。
すでに作成済みのものがあれば、こちらは飛ばして問題ありません。
Serviceの作成
以下のようなService作成用のyamlファイルを作成します。
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
type: ClusterIP
selector:
<selector_key>: <selector_value>
ports:
- protocol: TCP
port: <port>
targetPort: <port> # Optional. By default, the `targetPort` is set to match the `port` value unless specified otherwise.
置き換えが必要な設定値の説明は以下の通りです。
- selector
アプリをデプロイしたyamlファイルの spec.template.metadata.labels セクションで使用した、ラベル・キー(<selector_key>)と値 (<selector_value>)を記載します。 - port
Serviceがlistenするportの番号です。アプリをデプロイしたyamlファイルの、spec.containers.portsセクションのcontainerPortの値を記載します。
「1. ServiceでのIPアドレス制限」で作成したServiceと異なる点は以下の2点です。
- NLBの設定項目が削除されたこと
- Ingressと接続するため、Serviceタイプが"ClusterIP"になっていること
yamlを作成したら、以下のコマンドを実行しSeriviceを作成してください。
$ kubectl apply -f myservice.yaml
Ingressの作成
以下のようなIngress作成用のyamlファイルを作成します。(今回はパブリックALBを前提として定義します)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myingress
annotations:
kubernetes.io/ingress.class: "public-iks-k8s-nginx"
spec:
tls:
- hosts:
- <ドメイン名>
secretName: <TLSシークレット名>
- host: <ドメイン名>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myservice
port:
number: 3000
置き換えが必要な設定値の説明は以下の通りです。(詳細はこちら)
-
tls.hosts/host
IBM提供のIngressサブドメインまたはカスタム・ドメインで置き換えます。 -
tls.secretName
TLS証明書が保管されている Kubernetes シークレットの名前に置き換えます。
yamlを作成したら、以下のコマンドを実行しIngressを作成してください。(現時点のIngressを作成するとアプリが制限なく公開されるのでご注意)
$ kubectl apply -f myingress.yaml
myingress.yaml内のhostに定義したドメインにアクセスし、アプリにアクセスできるか確認してください。
手順概要
① アクセス元のIPアドレスを保持する設定にALBを変更する
② IPアドレス制限設定を行う
③ IPアドレス制限が機能していることを確認する
① アクセス元のIPアドレスを保持する設定にALBを変更する
こちらの手順に関してはIKSのドキュメントに記載があるため、そちらに沿って設定変更を行います。
前動作確認
まず、ALBにてIPアドレス制限をかける場合はアクセス元のIPアドレスは保持する必要がありますが、デフォルトではALBはアクセス元のIPアドレスは保持されません。
よってIKSにデプロイしたアプリ側のバックエンドでリクエストヘッダー内の'x-forwarded-for'を出力すると、実際のアクセス元のIPアドレスではなく、アプリがデプロイされているワーカー・ノードのIPアドレスが表示されます。
以下node.jsのバックエンドサーバーにてIPアドレスを出力する例です。
app.use((req, res, next) => {
const ip = req.headers['x-forwarded-for']||'null';
console.log('IP : ' + ip) //ワーカー・ノードのIPアドレスが出力される
return next()
})
アプリで出力させるのが難しい場合は、ALB Podのログから確認することも可能です。
ALB Podのログを確認するには、まず以下のコマンドにてALB PodのIDを確認します。
$ kubectl get pods -n kube-system | grep alb
続いて、以下のコマンドにて対象のALB Podのログを出力します(コマンドはログの最後20行を出力させるものとなっています)。
<ALB_pod_ID>となっている箇所は前手順で確認したALB PodのIDに置き換えてください。
$ kubectl logs --tail=20 <ALB_pod_ID> nginx-ingress -n kube-system
これらを本来のアクセス元のIPアドレスを保持させたいとなった場合は、ALBの設定変更が必要となります。
設定変更前のIPアドレスの受け渡し状況を確認したら、本題の設定変更に移ります。
ALBの設定変更
今回は単一のALBのアクセス元のIPアドレスを保持をセットアップします。
ALBの設定変更をするために、まず対象となるALBのIDを取得します。
$ kubectl get svc -n kube-system | grep alb
出力例は以下の通りです。
$ kubectl get svc -n kube-system | grep alb
public-xxxxx-alb1 LoadBalancer aaa.aaa.aaa.aaa bbb.bbb.bbb.bbb cc:ccccc/TCP,ddd:ddddd/TCP 44m
対象となるALBのIDを確認したら、以下のコマンドを実行します。
<ALB_ID>としている箇所は、前手順で確認した対象ALBのIDに置き換えて実行してください。
$ kubectl edit svc <ALB_ID> -n kube-system
実行するとALBの設定ファイルをviモードで編集できるようになります。
変更が必要な箇所は、"spec.externalTrafficPolicy"です。こちらの設定値の値がデフォルトで「Cluster」になっているところを「Local」に変更します。
...
clusterIPs:
- xxx.xxx.xxx.xxx
externalTrafficPolicy: Local //この設定値を「Cluster」から「Local」に変更
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
...
編集が完了したら、viモードと同様「:wq」で保存・終了してください。
後動作確認
編集完了直後から設定が反映されます。こちらで確認した限り、ALBのダウンタイムは発生しませんでした。
アプリないしはALB Podのログからきちんと変更が反映されているかを確認してください。
ちなみにこのexternalTrafficPolicyの設定がClusterのままだと後続のIPアドレス制限設定が機能しないので、ご注意ください。
② IPアドレス制限設定を行う
設定変更の反映が確認できたら事前準備で作成したmyingress.yamlにIPアドレス制限の設定を加えます。
以下のように、"nginx.ingress.kubernetes.io/whitelist-source-range"設定を追加したyamlを作成してください。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myingress
annotations:
kubernetes.io/ingress.class: "public-iks-k8s-nginx"
nginx.ingress.kubernetes.io/whitelist-source-range: <アクセスを許可するIPアドレスのレンジ>
spec:
tls:
- hosts:
- <ドメイン名>
secretName: <TLSシークレット名>
- host: <ドメイン名>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myservice
port:
number: 3000
※ IPアドレスを複数記載する場合はコンマ(,)区切りで1文で記載してください。
yamlを作成したら、以下のコマンドを実行しIngressを更新してください。
$ kubectl apply -f myingress.yaml
③ IPアドレス制限が機能していることを確認する
Ingressの更新が完了したら、動作確認をします。
nginx.ingress.kubernetes.io/whitelist-source-rangeで定義した、アクセスを許可するIPアドレスからアプリにアクセスし、アプリにアクセスが可能であることを確認してください。
続いて、nginx.ingress.kubernetes.io/whitelist-source-rangeに定義していないIPアドレスからアプリにアクセスすると、403エラーでアクセスが拒否されることを確認してください。
手順は以上です。
最後に
手順が分かってしまえば簡単にIPアドレス制限を実装できることがわかりました。
アプリの改修をせずに制限を実装できるのも魅力的ですね。
ちなみに、IngressでIPアドレス制限を実装した場合デフォルトではnginxの403エラー画面が表示されますが、こちらのエラー画面のカスタマイズもできることを確認してますので気が向いたらこちらも記事にしたいと思います。
こちらの手順についてはこの記事で割と丁寧に記載があるので気になる方は参考にしていただければと思います。
参考文献
- IBM Kubernetes Service公式ドキュメント
https://cloud.ibm.com/docs/containers?topic=containers-getting-started&interface=ui - サブネットマスクとは
https://www.cman.jp/network/term/subnet/p3/ - EKSのロードバランサーでIPアドレスのアクセス制限を設定する
https://blog.kumano-te.com/activities/set-ip-whitelist-to-eks-nlb-service - NGINX Ingress ControllerによるIPアクセス制御
https://kazkokk.hatenablog.com/entry/2021/06/14/220548 - How to set up a custom HTTP error in Kubernetes
https://stackoverflow.com/questions/60080132/how-to-set-up-a-custom-http-error-in-kubernetes - 送信元アドレスを許可する(ホワイトリスト形式)
https://tips.weseek.co.jp/602344e4b536bb0048568df5
おわり
更新履歴
- 2023/5/12 公開
- 2023/6/5 IPアドレスの複数記載方法を追記