NGINX Ingress ControllerによるIPアクセス制御

Kubernetes（AKS）に複数のWebアプリケーションがデプロイされていて、それぞれに対し特定のIPアドレスからのみアクセスを許可する方法を試してみました。

大まかな手順は下のとおりです。

1. 事前準備として、HelmでIngress Controllerをデプロイする。
2. アクセス制御として、ホワイトリストを指定したIngressリソースをデプロイする。

## 事前準備

### Helmのインストール

公式の手順に従い、インストールします。

```
sudo apt-get update
curl https://helm.baltorepo.com/organization/signing.asc | sudo apt-key add -
sudo apt-get install apt-transport-https --yes
echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
```

### Ingress Controllerのデプロイ

Ingress用のnamespaceを作っておきます。

```
kubectl create namespace ingress-basic
```

HelmでNGINX Ingress Controllerをデプロイします。AKSの公式手順ではnodeSelectorを指定していますが、Nodeの種類が複数あるわけではないので省き、レプリカも1に減らしています。

またこのとき **externalTrafficPolicy=Local** を指定することが重要です。これによってリクエストの送信元IPを扱うことができるようになります。指定しない場合、送信元IPによるアクセス制御はできません。

```
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install nginx-ingress ingress-nginx/ingress-nginx \
    --namespace ingress-basic \
    --set controller.replicaCount=1 \
    --set controller.service.externalTrafficPolicy=Local
```

デプロイされたリソースを確認してみます。

```
kubectl --namespace ingress-basic get deployments

NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
nginx-ingress-ingress-nginx-controller   1/1     1            1           17h
```

```
kubectl --namespace ingress-basic get services

NAME                                               TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE
nginx-ingress-ingress-nginx-controller             LoadBalancer   10.0.190.67   xx.xx.xx.xx   80:31238/TCP,443:32076/TCP   17h
nginx-ingress-ingress-nginx-controller-admission   ClusterIP      10.0.155.88   <none>        443/TCP                      17h
```

### 検証用アプリケーションのデプロイ

2つのnamespaceを作成し、それぞれにWebサーバーのPodを立ち上げ、Serviceでクラスター内に公開しておきます。

```
kubectl create namespace app1
kubectl create namespace app2
kubectl -n app1 run webapp1 --image=nginx
kubectl -n app2 run webapp2 --image=httpd
kubectl -n app1 expose pod webapp1 --port=80
kubectl -n app2 expose pod webapp2 --port=80
```

## アクセス制御の設定

### Ingressリソースのデプロイ

それぞれのnamespaceに、WebサーバーServiceへのルーティングを定義したIngressを作成します。このときannotationsのwhitelist-source-rangeで、送信元IPのホワイトリストを指定できます。

注意点としてspec.rulesのbackend.service.nameでは、ServiceのFQDNを指定できません。したがってIngressを作成するnamespaceは、Serviceと同じである必要があります。

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/whitelist-source-range: aa.aa.aa.aa/32
  name: ingress1
  namespace: app1
spec:
  rules:
    - http:
        paths:
          - backend:
              service:
                name: webapp1
                port: 
                  number: 80
            path: /app1
            pathType: Prefix
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/whitelist-source-range: bb.bb.bb.bb/32
  name: ingress2
  namespace: app2
spec:
  rules:
    - http:
        paths:
          - backend:
              service:
                name: webapp2
                port: 
                  number: 80
            path: /app2
            pathType: Prefix
```

### 動作確認

app1にaa.aa.aa.aaのIPアドレスからアクセスします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1647270/5233ac4d-0a31-1d56-03ce-f8c9f6085882.png)

app2にbb.bb.bb.bbのIPアドレスからアクセスします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1647270/3c5c7b9c-8de2-4dcb-6ddd-08753c36a196.png)

app2に許可されていないaa.aa.aa.aaのIPアドレスからアクセスします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1647270/19042681-2d8a-c02f-9690-02cff60db933.png)

許可されていないIPアドレスからのアクセスは、NGINX Ingress Controllerがエラーを返していることがわかりますね！