TLS発信機能付きEgress Gateway

外部サービスへのトラフィックについて、Istio でTLS発信元を実行する方法を、エグレストラフィックのTLS発信元の例で示しています。エグレスゲートウェイの設定の例では、専用のエグレスゲートウェイサービスを介してエグレストラフィックをルーティングするようにIstioを設定する方法を示しています。この例では、外部サービスへのトラフィックについて、エグレスゲートウェイでTLS発信元を実行するように設定する方法について説明することで、上記の2つの例を組み合わせます。

始める前に

  • インストールガイドの手順に従ってIstioをセットアップしてください。

  • 外部呼び出しのテストソースとして使用されるcurlサンプルを起動します。

    自動サイドカーインジェクションを有効にしている場合は、以下の操作を行います。

    Zip
    $ kubectl apply -f @samples/curl/curl.yaml@
    

    それ以外の場合は、curlアプリケーションをデプロイする前に、サイドカーを手動でインジェクトする必要があります。

    Zip
    $ kubectl apply -f <(istioctl kube-inject -f @samples/curl/curl.yaml@)
    

    execcurlを実行できるポッドであれば、どれでも使用できます。

  • 外部サービスへのリクエストを送信するためのソースポッドの名前を保持するシェル変数を作成します。curlサンプルを使用した場合、以下のコマンドを実行します。

    $ export SOURCE_POD=$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})
    
  • macOSユーザーの場合、opensslバージョン1.1以降を使用していることを確認してください。

    $ openssl version -a | grep OpenSSL
    OpenSSL 1.1.1g  21 Apr 2020
    

    上記のコマンドでバージョン1.1以降が出力された場合、opensslコマンドはこのタスクの手順で正しく動作します。それ以外の場合は、opensslをアップグレードするか、Linuxマシンなど、別のopensslの実装を試してください。

  • まだ有効にしていない場合は、Envoyのアクセスログを有効にします。たとえば、istioctlを使用します。

    $ istioctl install <flags-you-used-to-install-Istio> --set meshConfig.accessLogFile=/dev/stdout
    
  • Gateway APIの手順を使用していない場合は、Istioエグレスゲートウェイをデプロイしてください。

エグレスゲートウェイを使用したTLS発信の実行

このセクションでは、エグレストラフィックのTLS発信元の例と同じTLS発信元を実行する方法について説明しますが、今回はエグレスゲートウェイを使用します。この場合、TLS発信元はサイドカーではなくエグレスゲートウェイによって実行されることに注意してください。

  1. edition.cnn.comServiceEntryを定義します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: https
        protocol: HTTPS
      resolution: DNS
    EOF
    
  2. http://edition.cnn.com/politicsにリクエストを送信して、ServiceEntryが正しく適用されたことを確認します。

    $ kubectl exec "${SOURCE_POD}" -c curl -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
    HTTP/1.1 301 Moved Permanently
    ...
    location: https://edition.cnn.com/politics
    ...
    

    出力に「301 Moved Permanently」が表示された場合、ServiceEntryは正しく設定されています。

  3. edition.cnn.com、ポート80のエグレスGatewayと、エグレスゲートウェイに転送されるサイドカーリクエストの宛先ルールを作成します。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 80
      name: https-port-for-tls-origination
      protocol: HTTPS
    hosts:
    - edition.cnn.com
    tls:
      mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: cnn
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 80
        tls:
          mode: ISTIO_MUTUAL
          sni: edition.cnn.com
EOF
  1. エグレスゲートウェイを介してトラフィックをルーティングするようにルートルールを設定します。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: cnn
        port:
          number: 80
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 80
    route:
    - destination:
        host: edition.cnn.com
        port:
          number: 443
      weight: 100
EOF
  1. edition.cnn.comへのリクエストに対してTLS発信元を実行するDestinationRuleを定義します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: DestinationRule
    metadata:
      name: originate-tls-for-edition-cnn-com
    spec:
      host: edition.cnn.com
      trafficPolicy:
        loadBalancer:
          simple: ROUND_ROBIN
        portLevelSettings:
        - port:
            number: 443
          tls:
            mode: SIMPLE # initiates HTTPS for connections to edition.cnn.com
    EOF
    
  2. http://edition.cnn.com/politicsにHTTPリクエストを送信します。

    $ kubectl exec "${SOURCE_POD}" -c curl -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
    HTTP/1.1 200 OK
    ...
    

    出力は、エグレストラフィックのTLS発信元の例と同じで、301 Moved PermanentlyメッセージのないTLS発信元になります。

  3. エグレスゲートウェイのプロキシのログを確認します。

Istioがistio-system名前空間にデプロイされている場合、ログを出力するコマンドは次のとおりです。

$ kubectl logs -l istio=egressgateway -c istio-proxy -n istio-system | tail

次のような行が表示されます。

[2020-06-30T16:17:56.763Z] "GET /politics HTTP/2" 200 - "-" "-" 0 1295938 529 89 "10.244.0.171" "curl/7.64.0" "cf76518d-3209-9ab7-a1d0-e6002728ef5b" "edition.cnn.com" "151.101.129.67:443" outbound|443||edition.cnn.com 10.244.0.170:54280 10.244.0.170:8080 10.244.0.171:35628 - -

TLS発信例のクリーンアップ

作成したIstio構成アイテムを削除します。

$ kubectl delete gw istio-egressgateway
$ kubectl delete serviceentry cnn
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule originate-tls-for-edition-cnn-com
$ kubectl delete destinationrule egressgateway-for-cnn

エグレスゲートウェイを使用した相互TLS発信の実行

前のセクションと同様に、このセクションでは、外部サービスに対してTLS発信元を実行するようにエグレスゲートウェイを設定する方法について説明しますが、今回は相互TLSを必要とするサービスを使用します。

この例は、最初に次の操作を行う必要があるため、かなり複雑です。

  1. クライアント証明書とサーバー証明書を生成する
  2. 相互TLSプロトコルをサポートする外部サービスをデプロイする
  3. 必要な相互TLS証明書を使用してエグレスゲートウェイを再デプロイする

その後、TLS発信元を実行するエグレスゲートウェイを介して外部トラフィックを設定できます。

クライアントとサーバーの証明書とキーの生成

このタスクでは、お好みのツールを使用して証明書とキーを生成できます。以下のコマンドではopensslを使用します。

  1. サービスの証明書に署名するためのルート証明書と秘密鍵を作成します。

    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
    
  2. my-nginx.mesh-external.svc.cluster.localの証明書と秘密鍵を作成します。

    $ openssl req -out my-nginx.mesh-external.svc.cluster.local.csr -newkey rsa:2048 -nodes -keyout my-nginx.mesh-external.svc.cluster.local.key -subj "/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization"
    $ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in my-nginx.mesh-external.svc.cluster.local.csr -out my-nginx.mesh-external.svc.cluster.local.crt
    

    必要に応じて、宛先のSAN検証を有効にする場合、証明書にSubjectAltNamesを追加できます。例:

    $ cat > san.conf <<EOF
    [req]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    countryName = US
    [v3_req]
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth, clientAuth
    basicConstraints = critical, CA:FALSE
    subjectAltName = critical, @alt_names
    [alt_names]
    DNS = my-nginx.mesh-external.svc.cluster.local
    EOF
    $
    $ openssl req -out my-nginx.mesh-external.svc.cluster.local.csr -newkey rsa:4096 -nodes -keyout my-nginx.mesh-external.svc.cluster.local.key -subj "/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization" -config san.conf
    $ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in my-nginx.mesh-external.svc.cluster.local.csr -out my-nginx.mesh-external.svc.cluster.local.crt -extfile san.conf -extensions v3_req
    
  3. クライアント証明書と秘密鍵を生成します。

    $ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
    $ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in client.example.com.csr -out client.example.com.crt
    

相互TLSサーバーのデプロイ

相互TLSプロトコルをサポートする実際の外部サービスをシミュレートするには、KubernetesクラスタにNGINXサーバーをデプロイしますが、Istioサービスメッシュの外側(Istioサイドカープロキシインジェクションが有効になっていない名前空間)で実行します。

  1. Istioメッシュ外のサービスを表す名前空間mesh-externalを作成します。自動サイドカーインジェクションが有効になっていないため、この名前空間内のポッドにはサイドカープロキシが自動的にインジェクトされません。

    $ kubectl create namespace mesh-external
    
  2. サーバーとCA証明書を保持するKubernetesシークレットを作成します。

    $ kubectl create -n mesh-external secret tls nginx-server-certs --key my-nginx.mesh-external.svc.cluster.local.key --cert my-nginx.mesh-external.svc.cluster.local.crt
    $ kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=example.com.crt
    
  3. NGINXサーバーの構成ファイルを作成します。

    $ cat <<\EOF > ./nginx.conf
    events {
    }
    
    http {
      log_format main '$remote_addr - $remote_user [$time_local]  $status '
      '"$request" $body_bytes_sent "$http_referer" '
      '"$http_user_agent" "$http_x_forwarded_for"';
      access_log /var/log/nginx/access.log main;
      error_log  /var/log/nginx/error.log;
    
      server {
        listen 443 ssl;
    
        root /usr/share/nginx/html;
        index index.html;
    
        server_name my-nginx.mesh-external.svc.cluster.local;
        ssl_certificate /etc/nginx-server-certs/tls.crt;
        ssl_certificate_key /etc/nginx-server-certs/tls.key;
        ssl_client_certificate /etc/nginx-ca-certs/example.com.crt;
        ssl_verify_client on;
      }
    }
    EOF
    
  4. NGINXサーバーの構成を保持するKubernetesConfigMapを作成します。

    $ kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.conf=./nginx.conf
    
  5. NGINXサーバーをデプロイします。

    $ kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Service
    metadata:
      name: my-nginx
      namespace: mesh-external
      labels:
        run: my-nginx
    spec:
      ports:
      - port: 443
        protocol: TCP
      selector:
        run: my-nginx
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-nginx
      namespace: mesh-external
    spec:
      selector:
        matchLabels:
          run: my-nginx
      replicas: 1
      template:
        metadata:
          labels:
            run: my-nginx
        spec:
          containers:
          - name: my-nginx
            image: nginx
            ports:
            - containerPort: 443
            volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx
              readOnly: true
            - name: nginx-server-certs
              mountPath: /etc/nginx-server-certs
              readOnly: true
            - name: nginx-ca-certs
              mountPath: /etc/nginx-ca-certs
              readOnly: true
          volumes:
          - name: nginx-config
            configMap:
              name: nginx-configmap
          - name: nginx-server-certs
            secret:
              secretName: nginx-server-certs
          - name: nginx-ca-certs
            secret:
              secretName: nginx-ca-certs
    EOF
    

エグレストラフィックに対する相互TLS発信の構成

  1. エグレスゲートウェイがデプロイされているのと同じ名前空間に、クライアントの証明書を保持するKubernetesシークレットを作成します。
$ kubectl create secret -n istio-system generic client-credential --from-file=tls.key=client.example.com.key \
  --from-file=tls.crt=client.example.com.crt --from-file=ca.crt=example.com.crt

さまざまなツールとの統合をサポートするために、Istioはいくつかの異なるシークレット形式をサポートしています。この例では、tls.keytls.crtca.crtキーを持つ単一の汎用シークレットを使用します。

  1. my-nginx.mesh-external.svc.cluster.local、ポート443のエグレスGatewayと、エグレスゲートウェイに転送されるサイドカーリクエストの宛先ルールを作成します。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    hosts:
    - my-nginx.mesh-external.svc.cluster.local
    tls:
      mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-nginx
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: nginx
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 443
        tls:
          mode: ISTIO_MUTUAL
          sni: my-nginx.mesh-external.svc.cluster.local
EOF
  1. エグレスゲートウェイを介してトラフィックをルーティングするようにルートルールを設定します。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: direct-nginx-through-egress-gateway
spec:
  hosts:
  - my-nginx.mesh-external.svc.cluster.local
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: nginx
        port:
          number: 443
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 443
    route:
    - destination:
        host: my-nginx.mesh-external.svc.cluster.local
        port:
          number: 443
      weight: 100
EOF
  1. 相互TLS発信元を実行するDestinationRuleを追加します。
$ kubectl apply -n istio-system -f - <<EOF
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: originate-mtls-for-nginx
spec:
  host: my-nginx.mesh-external.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    portLevelSettings:
    - port:
        number: 443
      tls:
        mode: MUTUAL
        credentialName: client-credential # this must match the secret created earlier to hold client certs
        sni: my-nginx.mesh-external.svc.cluster.local
        # subjectAltNames: # can be enabled if the certificate was generated with SAN as specified in previous section
        # - my-nginx.mesh-external.svc.cluster.local
EOF
  1. 資格情報がエグレスゲートウェイに提供され、アクティブになっていることを確認します。
$ istioctl -n istio-system proxy-config secret deploy/istio-egressgateway | grep client-credential
kubernetes://client-credential            Cert Chain     ACTIVE     true           1                                          2024-06-04T12:46:28Z     2023-06-05T12:46:28Z
kubernetes://client-credential-cacert     Cert Chain     ACTIVE     true           16491643791048004260                       2024-06-04T12:46:28Z     2023-06-05T12:46:28Z
  1. http://my-nginx.mesh-external.svc.cluster.localにHTTPリクエストを送信します。

    $ kubectl exec "$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})" -c curl -- curl -sS http://my-nginx.mesh-external.svc.cluster.local
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    ...
    
  2. エグレスゲートウェイのプロキシのログを確認します。

Istioがistio-system名前空間にデプロイされている場合、ログを出力するコマンドは次のとおりです。

$ kubectl logs -l istio=egressgateway -n istio-system | grep 'my-nginx.mesh-external.svc.cluster.local' | grep HTTP

次のような行が表示されます。

[2018-08-19T18:20:40.096Z] "GET / HTTP/1.1" 200 - 0 612 7 5 "172.30.146.114" "curl/7.35.0" "b942b587-fac2-9756-8ec6-303561356204" "my-nginx.mesh-external.svc.cluster.local" "172.21.72.197:443"

相互TLS発信例のクリーンアップ

  1. NGINX相互TLSサーバーのリソースを削除します。

    $ kubectl delete secret nginx-server-certs nginx-ca-certs -n mesh-external
    $ kubectl delete configmap nginx-configmap -n mesh-external
    $ kubectl delete service my-nginx -n mesh-external
    $ kubectl delete deployment my-nginx -n mesh-external
    $ kubectl delete namespace mesh-external
    
  2. ゲートウェイ構成リソースを削除します。

$ kubectl delete secret client-credential -n istio-system
$ kubectl delete gw istio-egressgateway
$ kubectl delete virtualservice direct-nginx-through-egress-gateway
$ kubectl delete destinationrule -n istio-system originate-mtls-for-nginx
$ kubectl delete destinationrule egressgateway-for-nginx
  1. 証明書と秘密鍵を削除します。

    $ rm example.com.crt example.com.key my-nginx.mesh-external.svc.cluster.local.crt my-nginx.mesh-external.svc.cluster.local.key my-nginx.mesh-external.svc.cluster.local.csr client.example.com.crt client.example.com.csr client.example.com.key
    
  2. この例で使用した生成された構成ファイルを削除します。

    $ rm ./nginx.conf
    

クリーンアップ

curlサービスとデプロイメントを削除します。

Zip
$ kubectl delete -f @samples/curl/curl.yaml@
この情報は役に立ちましたか?
改善点についてご提案があればお聞かせください。

ご意見ありがとうございました!