Gateway APIでNamespaceを跨いでロードバランシングする

Microservices的なシステムを作ると、`Ingress`がNamespaceを跨げないことが問題となることがある。
ExternalNameを活用するなどすれば跨げなくもないのだが、セキュリティ的に問題となることもあるらしい。
そのため別のNamespaceの`Service`を参照したい、というニーズに応えるために、Gateway APIでは`ReferencePolicy`というリソースが用意され、これを使うと`HTTPRoute`が別のNamespaceの`Service`を参照できるようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/75518ba0-cfc5-dae1-441d-c112eb86e67d.png)
([ReferenceGrant](https://gateway-api.sigs.k8s.io/api-types/referencegrant/)より引用)

今回は`ReferencePolicy`の動作を確認するために、[前回検証した](https://qiita.com/ipppppei/items/6edb78b513c7bbe55d91#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%ABpc%E3%81%A7gateway-api%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B)nginxとhttbinのNamespaceを変えて以下のような構成で意図通りに通信できるかを検証する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/3cc9af16-6c6e-c286-d06e-dec3cdc4f119.png) 

なお、検証は[前回の環境](https://qiita.com/ipppppei/items/6edb78b513c7bbe55d91#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%ABpc%E3%81%A7gateway-api%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B)を使い回すため、クラスタの準備やGateway API、Gateway Controllerの導入は終わっているところからスタートする。

## `ReferenceGrant`の概要
`ReferenceGrant`は名前の通り参照権限を与えるためのリソースであり、Namespaceをまたいだ場合の各種リソースの権限を管理する。
仕様は[こちら](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.ReferenceGrant)にあるが、実物を見た方がわかりやすいので以下サンプルを示す。
```yaml
kind: ReferenceGrant
metadata:
  name: bar
  namespace: bar
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: foo
  to:
  - group: ""
    kind: Service
```
`spec.from`で権限をリクエストする側を指定し、`spec.to`で参照先を指定する。
リソース名やNamespace名などもここで細かく指定することが出来、権限の付与に関して細かく設定できるようになっている。
なお、ここでは`HTTPRoute`のために`ReferenceGrant`を使っているが、ストレージ（`PVC`）に適用した事例もあるため、ネットワークに限らず様々なリソースに応用することも出来そうだ。

参考：[CNDT 2022、日立のエンジニアによるKubernetesに新機能をマージした経験を語るセッション](https://thinkit.co.jp/article/21458)

## 検証
最初に検証用のnginxを起動する。
```sh
kubectl create ns demo-back
kubectl run nginx --image nginx --port 80 -n demo-back
kubectl expose pod nginx --port 80 -n demo-back
```
httpbinをつなぐ`Service`を作成する。
```sh
kubectl create ns demo-external
kubectl create svc externalname httpbin --external-name httpbin.org -n demo-external
```
`Gateway`リソースを作成する。
```yaml
kubectl create ns demo-front
cat <<EOF | kubectl apply -n demo-front -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
spec:
  gatewayClassName: kong
  listeners:
  - name: http
    port: 80
    protocol: HTTP
EOF
```
`HTTPRoute`を作成する。
```yaml
PROXY_IP=$(kubectl get gateway gateway -n demo -o jsonpath={.status.addresses[].value})
export HOSTNAME="my-service.$(sed "s/\./-/g" <<< $PROXY_IP).nip.io"
cat <<EOF | kubectl apply -n demo-front -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: route
  annotations:
    konghq.com/strip-path: 'true'
spec:
  hostnames: 
  - $HOSTNAME
  parentRefs:
  - name: gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /service
    backendRefs:
    - name: nginx
      kind: Service
      namespace: demo-back
      port: 80
      weight: 50
    - name: httpbin
      kind: Service
      namespace: demo-external
      port: 80
      weight: 50
EOF
```
前回との違いとして、`backendRefs`内の`Service`指定時に`namespace`も指定するようにした。
この状態だとまだ`ReferenceGrant`がないため、参照する権限がない。
試しにサービスのエンドポイントにアクセスしても、`"no existing backendRef provided"`と怒られてしまう。
```sh
$ curl my-service.192-168-64-202.nip.io/service
{"message":"no existing backendRef provided"}
```
ここで`ReferenceGrant`を適用してみる。まずはnginxの`Service`にのみ適用する。
```yaml
cat <<EOF | kubectl apply -f - 
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: nginx-grant
  namespace: demo-back
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: demo-front
  to:
  - group: ""
    kind: Service
EOF
```
この状態でエンドポイントにアクセスすると、nginxへのアクセスのみ発生する。
```html
$ curl my-service.192-168-64-202.nip.io/service
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
：（省略）
```
httpbin側の方も許可を与える。
```yaml
cat <<EOF | kubectl apply -f - 
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: httpbin-grant
  namespace: demo-external
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: demo-front
  to:
  - group: ""
    kind: Service
EOF
```
こうすると、nginx, httpbinそれぞれにアクセスするようになる。
```html
$ curl my-service.192-168-64-202.nip.io/service
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>httpbin.org</title>
：（省略）
```
適切に参照権限が与えられることが確認できた。