Azure Kubernetes ServiceのNGINX Ingress Controllerでアクセス元IPアドレスを制限する

公式の以下手順で、Let's Encryptを使ったTLS証明書付きのIngress Controllerを作成しました。
[Azure Kubernetes Service (AKS) の静的パブリック IP アドレスを使用してイングレス コントローラーを作成する](https://docs.microsoft.com/ja-jp/azure/aks/ingress-static-ip)

バックエンドのWebサーバコンテナにインターネットからアクセスする際に、アクセス元IPアドレスを絞りたいと思ったのですが、中々うまくいかずハマったので手順残します。

自分で作ったvNetにデプロイしたAKSを使っています。

# Ingress Controller作成

* Ingress Controllerを作成します。ここがポイントで、「--set controller.service.externalTrafficPolicy=Local」を付与しないと、Ingress Controllerが受信するアクセス元のソースIPが、グローバルアドレスではなくvNetのIPになってしまいうまくIP制御できません。

```
helm install stable/nginx-ingress \
    --namespace [Your Namespace] \
    --set controller.service.loadBalancerIP="[Public IP]"  \
    --set controller.replicaCount=2
    --set controller.service.externalTrafficPolicy=Local
```

# DNS名を構成する

* ここは公式通りです。以下のShellを作成して実行します。

```shell
#!/bin/bash

# Public IP address of your ingress controller
IP="[Public IP]"

# Name to associate with public IP address
DNSNAME="[Your DNS Name]"

# Get the resource-id of the public ip
PUBLICIPID=$(az network public-ip list --query "[?ipAddress!=null]|[?contains(ipAddress, '$IP')].[id]" --output tsv)

# Update public ip address with DNS name
az network public-ip update --ids $PUBLICIPID --dns-name $DNSNAME
```

# cert-manager をインストールする

* ここは公式通りです。kube-systemネームスペースに作ります。

```
helm install stable/cert-manager \
  --namespace kube-system \
  --set ingressShim.defaultIssuerName=letsencrypt-prod \
  --set ingressShim.defaultIssuerKind=ClusterIssuer
```

# CAクラスター発行者を作成する

* ここは公式通りです。yamlを作ってapplyします。kube-systemネームスペースに作ります。

```yaml:cluster-issuer.yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: user@contoso.com
    privateKeySecretRef:
      name: letsencrypt-prod
    http01: {}
```

```
kubectl apply -f cluster-issuer.yaml -n kube-system
```

# 証明書オブジェクトを作成する

* ここは公式通りです。yamlを作ってapplyします。

```yaml:certificates.yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: tls-secret
spec:
  secretName: tls-secret
  dnsNames:
  - [Your DNS Name].japaneast.cloudapp.azure.com
  acme:
    config:
    - http01:
        ingressClass: nginx
      domains:
      - [Your DNS Name].japaneast.cloudapp.azure.com
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
```

```
kubectl apply -f certificates.yaml -n [Your Namespace]
```

# アプリケーションを実行する

* バックエンドのWebアプリケーションを実行します。公式手順にあるデモアプリケーションでもよいです。

# Ingressルートを作成する

* Ingressルートを作成する際のマニフェストで、アノテーション「nginx.ingress.kubernetes.io/whitelist-source-range」を付与して作成します。

```yaml:application.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-world-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/whitelist-source-range: xxx.xxx.xxx.xxx/32,yyy.yyy.yyy.yyy/32
spec:
  tls:
  - hosts:
    - [Your DNS Name].japaneast.cloudapp.azure.com
    secretName: tls-secret
  rules:
  - host: [Your DNS Name].japaneast.cloudapp.azure.com
    http:
      paths:
      - path: /
        backend:
          serviceName: aks-helloworld
          servicePort: 80
      - path: /hello-world-two
        backend:
          serviceName: ingress-demo
          servicePort: 80
```

```
kubectl apply -f application.yaml -n [Your Namespace]
```

# テスト

https://[Your DNS Name].japaneast.cloudapp.azure.com にアクセスして、TLS証明書とIPアドレス制御が動作していることを確認します。