EKSに展開したIstioでSecure Ingress Gatewayを設定する

# 参考にしたサイト
[Managing TLS keys and certs in Istio using Amazon’s ACM](https://medium.com/faun/managing-tls-keys-and-certs-in-istio-using-amazons-acm-8ff9a0b99033)

[TLS support on AWS - Kubernetes Doc](https://kubernetes.io/docs/concepts/services-networking/service/#ssl-support-on-aws)

# 環境

Kubernetes: v1.16.15
Istio control plane: v1.7.4
Istio data plane: v1.7.4

# 設定

外部からのIngress用のELBにACMを展開します
手順は...

1. **istio-ingressgateway** LoadBalancer Serviceの `annotations` にELBの証明書等を設定
2. GatewayとVirtualServiceをデプロイ
3. Route53にDNSレコード登録

以上

[公式ドキュメント](https://istio.io/v1.7/docs/tasks/traffic-management/ingress/secure-ingress/) には自己署名認証局作って、証明書発行して、ということをしてますが、ここではACM一発で終わらせちゃいます。

ACMの証明書は予め作成しておいてね

## 1. istio-ingressgateway LoadBalancer Service

```yaml:istio-1.7.4/manifests/charts/gateways/istio-ingress/values.yaml
gateways:
  istio-ingressgateway:
...
    serviceAnnotations:
      service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:REGION:ACCOUNT_ID:certificate/ACM_IDENTIFIER"
      service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http"
      service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443,15443"
      service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60"
      # S3にアクセスログを送るなら以下の設定も
      service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
      service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "5"
      service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "my-bucket"
      service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "my-prefix"
```

`service.beta.kubernetes.io/aws-load-balancer-ssl-cert` には作成したACMのARNを入れてください

`service.beta.kubernetes.io/aws-load-balancer-backend-protocol ` は `http` でオッケー
`service.beta.kubernetes.io/aws-load-balancer-ssl-ports` はistio-ingressgatewayサービスでSSL使うポートを指定しましょう

以下コマンドで設定を展開

```
istioctl install --manifests ~/istio-1.7.4/manifests
```

## 2. GatewayとVirtualServiceをデプロイ

```yaml:gateway.yaml
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: my-gateway
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
  - port:
      number: 443
      name: https
      protocol: HTTP
    hosts:
    - "gateway.example.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-virturalservice
spec:
  hosts:
  - "*"
  gateways:
  - my-gateway
  http:
  - match:
    - uri:
        exact: /api/v1/hoge
    route:
    - destination:
        port:
          number: 8080
        host: hoge-service
  - match:
    - uri:
        exact: /api/v1/fuga
    route:
    - destination:
        port:
          number: 9090
        host: fuge-service

```

注意すべきポイントは以下

- Gatewayのselectorは `istio: ingressgateway` を指定してちゃんとistio-ingressgatewayにルーティングすること
- Gatewayのport 443で `protocol: HTTP` を指定すること（バックエンドはhttpプロトコルだから）

2番目が特に要注意です
最初設定したとき、何も考えずに `HTTPS` を指定したせいでPOSTリクエストが 100 Continue から進まずコネクションがタイムアウトしてしまいました
ちゃんと調べず設定してたので、無駄に1時間くらい悩んでました

## 3. Route53にレコード登録

Gatewayのport 443で指定したhost（今回の場合は `gateway.example.com` ね）を、istio-ingressの`EXTERNAL-IP`（ELBのアドレス）にCNAMEしましょう

```
kubectl get svc istio-ingressgateway -n istio-system

NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP                                                               PORT(S)                                                      AGE
istio-ingressgateway   LoadBalancer   xxx.xx.x.xxx   1234567890abcdef1234567890abcdef-1234567890.us-west-2.elb.amazonaws.com   15021:31278/TCP,80:32524/TCP,443:30756/TCP,15443:32071/TCP   143d
```


# 実際にHTTPSリクエスト試してみよう

curlでもなんでも良いので、実際にリクエスト出して試してみよう
TLS handshakeしてますね

```bash
curl -v https://gateway.example.com/api/v1/hoge

...
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
...
```

# おわりに

非常に簡単な設定ですが、変なところでつまずいたのと、意外と日本語でこのユースケースについて記載してある情報が見つからなかったので、書いてみました

誰かの役に立つといいなあ