Private Azure Kubernetes Service 間の Pod 間通信をセキュアにFQDN で実現するべ

# はじめに
皆さんこんにちは。Kazuyaです。
この記事では、AKS(Azure Kubernetes Service)を利用する際に、
VNetを構築して、セキュアな運用をしようとしているときに発生する以下のような問題に対するソリューションについて着眼してみました。

## 問題点
- [AKS-a]にある[Pod-a]から、[AKS-b]にある[Pod-b]に、インターネットを通過せず、且つ【FQDN】でアクセスしたい。
- つまりは以下のような構図にしたいが、実現方法がわからない。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/e7f056ba-738b-2e9b-0b5a-61b7acf5e5c6.png)
- なんだけど、具体的にどう実現するのかよくわからない。。

# というわけで実践。
もう、最初にネタバレじゃないですが、構成図を書いておきます。
これを見るだけで、構築できる人はそれで十分だと思うので、それが参考になったらここまで読んで「イイネ」よろしくです🔥

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/563fe656-ea3c-897d-3c4e-16cd6e18978d.png)

はい！出来上がりです。
これでOKですね～。
一応概略を書くと、たったこれだけです。

### 作業のアウトライン
**[前提]**
**AKS-aにある、Pod-aに、AKS-bにある、Pod-b から接続したい状況とする。** 

**[作業]**
1. Vnet 1つと、サブネット 2つを作成（Vnetを「paks-net」、サブネット1を「snaks1」、サブネット2を「snaks2」とする
1. それぞれのサブネットに、Private AKS を２つ作成して（AKS-a、AKS-bとする）デプロイする。
（別のVnetにデプロイした場合は、ピアリングが必要。今回は割愛）
1. AKS-aに、Internal LoadBalancer(以後、LB)をデプロイする
1. 上記でデプロイした LB の External-IP をメモする
1. Private DNS を作成する。(名前は、「my.com」としておく）
1. Private DNS を 「paks-net」にリンクする
1. Private DNS に、上記4 でメモした 「External-IP」を値として指定した A レコードを追加する。
追加するAレコードの名前は「nginx.svc」とする。
1. これで完成。AKS-b にある、Pod-b に接続して、上記で作成した Private DNS の定義を使って、
「nginx.svc.my.com」の形でアクセス可能。

はい！出来上がりです！これでOKですね～。ここまでで参考になった方はここで終了！

いや、、具体的に説明してくれないと分かんないんだけど・・・という方のために、以下はStep-by-Step～
優しいですね🔥（冗談です。自分が忘れたときのためのものです...

# Step-by-Step
## と、やる前に準備（この例では）

|リソース |設定値 |
|---|---|
|リソースグループ名  |paks |
|場所  |eastus |
|AKS-aの名前  |aksa |
|AKS-bの名前  |aksb |
|Vnetの名前  |paks-net(10.0.0.0/16) |
|Subnet1の名前  |snaks1(10.0.0.0/24) |
|Subnet2の名前  |snaks2(10.0.1.0/24) |

**Private な AKS なので同じ Vnet 内にサブネットをもう一つ作って、そのサブネットに Bastion 用のVM(OSはUbuntuにした）を用意し、そこから 各 AKSは操作。その VM には、Azure-CLIと、AKS-CLI を手動でインストール。**

## 1. まずは VNet と サブネットの作成
ここはポータルからでもできるので、やり方は割愛します。MS の公式ページでもあるので、詳しくはそちらを参照。
出来上がりは以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/e85f9976-d283-664a-cfd3-1a20226879df.png)

## 2. Private な AKS を２つ作成
ここも詳細は割愛しますが、CNIプラグインを使って、上記1で作成した サブネットにそれぞれデプロイする必要があります。実行コマンド例を記載しておきますので、参考にしてください。

```shell:Shell: AKS-aの作成コマンド
az aks create \
    --resource-group paks \
    --name aksa \
    --load-balancer-sku standard \
    --enable-private-cluster \
    --network-plugin azure \
    --vnet-subnet-id /subscriptions/<your_subscripition>/resourcegroups/paks/providers/Microsoft.Network/virtualNetworks/paks-net/subnets/snaks1 \
    --docker-bridge-address 172.17.0.1/16 \
    --dns-service-ip 10.2.0.10 \
    --service-cidr 10.2.0.0/24

```

```shell:Shell: AKS-bの作成コマンド
az aks create \
    --resource-group paks \
    --name aksa \
    --load-balancer-sku standard \
    --enable-private-cluster \
    --network-plugin azure \
    --vnet-subnet-id /subscriptions/<your_subscripition>/resourcegroups/paks/providers/Microsoft.Network/virtualNetworks/paks-net/subnets/snaks2 \
    --docker-bridge-address 172.17.0.1/16 \
    --dns-service-ip 10.2.0.10 \
    --service-cidr 10.2.0.0/24

```

## 3. AKS の マネージド ID に、Vnet を変更する権限を与えておく。
一応、VNetの設定を変えるコマンドが内部的に打たれる場合があるので、以下のコマンドで、AKS の Identity に、リソースグループの Contributor の権限を与えておく。

まずは、Internal Loadbalancerを作る方の AKS に割当たっている、ID を確認。
```shell: ID取得
PID=$(az aks show -g paks -n aksb --query "identity.principalId")
```

```shell: ResourceGroup に対する 権限割り当て
az role assignment create --assignee $PID --role Contributor --scope /subscriptions/<your_subcriptionid>/resourceGroups/paks
```

これでOK。

## 4. 続いて、AKS 内で稼働させる Pod をいったんデプロイ
### AKS-a にて作成(AKS-aに接続した状態にする)
1. NGINX POD
以下のコマンドで作成(yaml不要)
```shell
kubectl run nginx --image=nginx -l app=nginx
```

ついで、このNGINX POD に紐づくように、Internal-LoadBalancer を作成。
```yaml:lb.yaml
apiVersion: v1
kind: Service
metadata:
  name: internal-app3
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
```
**「service.beta.kubernetes.io/azure-load-balancer-internal: "true"」** により、インターナルロードバランサ―として、External-IPにもPrivateIPが割り振られる。

これをデプロイ。
```shell
kubectl apply -f lb.yaml
```
デプロイ後、以下のように<pending>にEXTERNAL-IPがなる。しばらく待ってもIPが割り当てられない場合、権限不足などのエラーがある場合があるので、kubectl describe svc/internal-app3 と実行して、何が起きているのか、Event のログを見て確認しよう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/875238f6-ba67-c0f5-1389-d87593cce9d8.png)


１～２分後：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/fa8aa809-97e5-3f69-f1aa-166ca3283704.png)

- **10.0.0.92**が割当たったことを確認。このIPは後で使うのでメモメモ。
- **app=nginx** が、PodのLabelに付与されていて、Service の Selectorでも、app=nginx が指定されていることが必須。

## 5. AKS-a 内部での疎通確認
デバッグ用 Pod を用意して、同一クラスタ内でのアクセスを確認してみる。

```yaml:debug.yaml
apiVersion: v1
kind: Pod
metadata:
  name: debug-pod
spec:
  containers:
    - name: debug-container
      image: centos
      command: ["/bin/sh"]
      args: ["-c", "while true; do sleep 30; done;"]
```
Curl を使いたいので、centosにしておく。
これがデプロイされてrunningになったら、以下のコマンドを順に実行して、NGINX へのアクセスができることを確認する。

```shell:デバッグポッドをデプロイして、ポッド内に入る
kubectl apply -f debug.yaml
kubectl exec -it pod/debug-pod /bin/sh
```

```bash:上記でポッドに入った後、curlを発行する。HTMLが取得できることを確認。
sh-4.4# curl 10.0.0.92
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

こちらでOK。

## 6. Private DNS を作成していく
つづいて、Private DNS を作成していきます。
手順は簡単で、Azure Portal で、「プライベート DNS ゾーン」を検索してブレードを表示。
画面に表示される「作成」をクリックして、新規作成。

同じリソースグループを選んで、インスタンス名に任意の値を入力。
今回は、「my.com」としたかったが、既存のリソースとの関係で、「my2.com」とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/f041b2d7-3584-a877-d114-b785076663e9.png)


で、レビュー作成 => 作成をクリックして、完成。デプロイ完了を待ち、完了したらリソースに移動。

#### リソースに移動したら、まず、「概要」のページで、A レコードを入れていく。
「＋　レコードセット」をクリックし、名前の欄に「nginx.svc」など、紐づくサービスがわかるような名前を付け、IPアドレスには、４で取得している、Internal-LB の EXTERNAL-IP(この例では、10.0.0.92）を入力する。
そして、OK をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/cf3113b6-67de-72e7-194f-5189756ed7a7.png)

これでOK。

#### ついで、仮想ネットワークに、この Private DNS ゾーン をリンクさせる。
これはプライベート DNS ゾーンの、左側のメニューにある[設定]セクションの下にある「仮想ネットワークリンク」をクリックする。
表示されたページで、「＋　追加」をクリックし、AKS-aとAKS-bがあるVNetを選択する。
リンク名はわかりやすければ何でもOK。（サブスク名はマスクしてます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/51f075c5-4ba8-c94b-7bd0-05609cf4e18e.png)

これでOKをクリック。
これにより、このVnet内でブロードキャストをした時に、この DNS ゾーンから レコードが引っ張ってこれるようになる。（完了までちょっとかかるので末）

<Aレコードの名前>.<DNS ゾーンの名前> という形で、FQDN は構成されるので、
今回で言えば、「nginx.svc.my2.com」となる。これが、Internal-LB 経由で、10.0.0.92 にルーティングされる。

## 7. AKS-b のほうに接続して、いざ試してみる。
AKS-bに接続して、デバッグ用ポッドをデプロイする。デプロイするのは、５で作成しているdebug.yamlをそのまま、AKS-b にもデプロイすればOK。

```shell: AKS-bに接続していることを確認してから実施。これでデバッグポッドに入る
kubectl apply -f debug.yaml
kubectl exec -it pod/debug-pod /bin/sh
```

AKS-aでやったのと同じように、Pod 内で、まずは Curl で Internal-LB のIP にめがけて接続。

```shell:
sh-4.4# curl 10.0.0.92
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```
うんうん。ばっちり。続いて、FQDN で接続

```shell
sh-4.4# curl nginx.svc.my2.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```
**ばっちりですね🔥**


これで、インターネットを経由せず、且つ 自作した FQDN を用いてアクセスすることが、Private な AKS 間で実現することができました。

曲がり間違って、kubectl expose pod/nginx --type=LoadBalancer --port 80 とかってやっちゃうと、単純にEXTERNAL-IP に パブリックIPがフロントにできちゃうのでご注意をと。

といったところで、こんなニーズは、企業内の環境を作っていて、AVD 環境で普段ユーザーが作業しているとなってくると、完全な閉塞空間とかを目指したくなる一方で、AKS一つに用途の違うものをなんでも入れるとかは、分業の理念上できないので、こういった仕組みもぜひ利用したいですね。

役に立ったなーと思っていただければ、イイネをいただけると、励みになります🙏

それではまた会いましょう！

# 参考
https://learn.microsoft.com/ja-jp/azure/aks/internal-lb

# 免責事項
本記事は、著者が独自で調査・検証を行った内容で、所属企業やいずれかの企業の公式見解に基づきません。
また、本記事を参考にして行った作業に関して発生するすべての損害、問題、課題について、当方では一切の責任を負えませんのであらかじめご了承ください。