Kubernetes でクラスタ外からのアクセス IP 制限

## はじめに
Kubernetes リソースと外部との通信制限を実施するには `Network Policy` と `loadBalancerSourceRanges` を使用する方法がある

それぞれの方法で Web サーバ(nginx)へ 1クライアントからのみアクセス許可する設定を試したので記載する

## Network 制限試験

- 試験環境 ([構築内容](https://qiita.com/suzuyui/items/969f07659cf6db9f94df))
    - Kubernetes : v1.25.4
    - Loadbalancer : MetalLB (BGP) v0.12.1

- 試験内容
    - Nginx で Web サーバを立てて、Loadbalancer で IP を公開する
    - 外部からの IP アクセスを `192.168.129.12`の試験サーバからのみアクセス可能にする
    - 試験方法は`Network Policy`と`loadBalancerSourceRanges`の2パターンで実施する

```yaml:nginx.yaml
---
kind: Namespace
apiVersion: v1
metadata:
  name: test-restrict-external-ip
  labels:
    name: test-restrict-external-ip
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: test-restrict-external-ip
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.22.1
        ports:
        - name: http
          containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: test-restrict-external-ip
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer
  loadBalancerIP: 192.168.131.1
```

## Network Policy
[Network Policy](https://kubernetes.io/ja/docs/concepts/services-networking/network-policies/) ではクラスタ内外のトラフィック制限が IP/Port, Ingress/Egress で可能

`spec.ingress`でクライアントからの通信、`spec.egress`でサーバ発の通信を許可する

ここでは`spec.ingress`でクライアントIP(`192.168.129.12`)を`ingress.from.ipBlock`で許可する

```yaml:test-nwpolicy.yaml
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: test-restrict-external-ip
spec:
  podSelector:
    matchLabels:
      app: nginx
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 192.168.129.12/32
    - podSelector:
        matchLabels:
          app: nginx
    ports:
    - protocol: TCP
      port: 80
```

上記 yaml を適用して制限する

```sh
kubectl apply -f test-nwpolicy.yaml
```

制限された状態でアクセスすると、下記の通り許可サーバからはアクセスできるが、許可サーバ外からは`Connection timed out`となる

```sh:access_test
# 許可されたクライアントからのアクセス
[suzuyu@rocky01 ~]$ ip addr show | grep 192.168
    inet 192.168.129.12/24 brd 192.168.129.255 scope global dynamic noprefixroute ens192
[suzuyu@rocky01 ~]$ curl 192.168.131.1
<!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>
[suzuyu@rocky01 ~]$ 

# 許可されてないクライアントからのアクセス
suzuyu@Xubuntu01:~$ ip addr show | grep 192.
    inet 192.168.129.91/24 brd 192.168.129.255 scope global dynamic noprefixroute ens160
suzuyu@Xubuntu01:~$ curl 192.168.131.1
curl: (7) Failed to connect to 192.168.131.1 port 80: Connection timed out
suzuyu@Xubuntu01:~$ 

```

設定マニフェストを削除して切り戻す

```sh:設定切り戻し
kubectl delete -f test-nwpolicy.yaml
```

## loadBalancerSourceRanges
`spec.type.Loadbalancer` で送信元 IP 制限が実装可能
Network Policy と違い、実装によっては外部 Loadbalancer 側へ処理をオフロードできるのでレイテンシを気にする場合はこちらを利用する
`spec.loadBalancerSourceRanges`で許可する IP レンジを設定する

```diff_yaml:loadBalancerSourceRangesの追加
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: test-restrict-external-ip
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer
+ loadBalancerSourceRanges:
+   - 192.168.129.12/32
```

下記コマンドで追加設定も可能

```sh
kubectl patch -n test-restrict-external-ip service nginx -p '{"spec":{"loadBalancerSourceRanges":["192.168.129.12/32"]}}'
```

前回同様にアクセス試験を実施

```sh:access_test(結果はNetworkPolicyと同じ)
# 許可されたクライアントからのアクセス
[suzuyu@rocky01 ~]$ ip addr show | grep 192.168
    inet 192.168.129.12/24 brd 192.168.129.255 scope global dynamic noprefixroute ens192
[suzuyu@rocky01 ~]$ curl 192.168.131.1
<!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>
[suzuyu@rocky01 ~]$ 

# 許可されてないクライアントからのアクセス
suzuyu@Xubuntu01:~$ ip addr show | grep 192.
    inet 192.168.129.91/24 brd 192.168.129.255 scope global dynamic noprefixroute ens160
suzuyu@Xubuntu01:~$ curl 192.168.131.1
curl: (7) Failed to connect to 192.168.131.1 port 80: Connection timed out
suzuyu@Xubuntu01:~$

```

下記で設定切り戻し可能

```sh
kubectl patch -n test-restrict-external-ip service nginx --type=json -p='[{"op": "remove", "path": "/spec/loadBalancerSourceRanges"}]'
```

## まとめ
クラスタ外からのアクセス制御に`Network Policy`と`loadBalancerSourceRanges`での制御方法を試した
`Network Policy`はクラスタ内や`Egress`の通信制御にも使用可能なので別途深掘りが必要

## 参照
https://kubernetes.io/ja/docs/concepts/services-networking/network-policies/