外部MongoDBサービスの利用

IstioのBookinfo例に基づいたシンプルなシナリオについて説明します。

2018年11月16日 | Vadim Eisenberg

外部TCPサービスの利用のブログ記事では、TCP経由でメッシュ内のIstioアプリケーションが外部サービスを利用する方法について説明しました。この記事では、外部MongoDBサービスの利用方法を示します。 Istio Bookinfoサンプルアプリケーション(本の評価データがMongoDBデータベースに永続化されているバージョン)を使用し、このデータベースをクラスタの外部にデプロイし、 *ratings* マイクロサービスがそれを利用するように構成します。外部MongoDBサービスへのトラフィック制御の複数のオプションとその長所と短所について学習します。

外部評価データベースを使用したBookinfo

まず、本の評価データを保持するMongoDBデータベースインスタンスをKubernetesクラスタの外部にセットアップします。次に、 Istio Bookinfoサンプルアプリケーション を修正して、このデータベースを使用します。

評価データベースのセットアップ

このタスクでは、 MongoDB のインスタンスをセットアップします。任意のMongoDBインスタンスを使用できます。私は Compose for MongoDB を使用しました。

  1. admin ユーザーのパスワードの環境変数を設定します。パスワードがBash履歴に保存されないように、コマンドを実行した直後に history -d を使用して履歴からコマンドを削除します。

    $ export MONGO_ADMIN_PASSWORD=<your MongoDB admin password>
    
  2. 作成する新しいユーザー(bookinfo)のパスワードの環境変数を設定します。history -d を使用して履歴からコマンドを削除します。

    $ export BOOKINFO_PASSWORD=<password>
    
  3. MongoDBサービスの環境変数MONGODB_HOSTMONGODB_PORTを設定します。

  4. bookinfo ユーザーの作成

    $ cat <<EOF | mongo --ssl --sslAllowInvalidCertificates $MONGODB_HOST:$MONGODB_PORT -u admin -p $MONGO_ADMIN_PASSWORD --authenticationDatabase admin
    use test
    db.createUser(
       {
         user: "bookinfo",
         pwd: "$BOOKINFO_PASSWORD",
         roles: [ "read"]
       }
    );
    EOF
    
  5. 評価を保持する *コレクション* を作成します。次のコマンドは、両方の評価を1に設定し、データベースがBookinfo *ratings* サービスで使用されている場合に視覚的な手がかりを提供します(デフォルトのBookinfo *ratings* は45です)。

    $ cat <<EOF | mongo --ssl --sslAllowInvalidCertificates $MONGODB_HOST:$MONGODB_PORT -u admin -p $MONGO_ADMIN_PASSWORD --authenticationDatabase admin
    use test
    db.createCollection("ratings");
    db.ratings.insert(
      [{rating: 1},
       {rating: 1}]
    );
    EOF
    
  6. bookinfo ユーザーが評価を取得できることを確認します

    $ cat <<EOF | mongo --ssl --sslAllowInvalidCertificates $MONGODB_HOST:$MONGODB_PORT -u bookinfo -p $BOOKINFO_PASSWORD --authenticationDatabase test
    use test
    db.ratings.find({});
    EOF
    

    出力は次のようになります。

    MongoDB server version: 3.4.10
    switched to db test
    { "_id" : ObjectId("5b7c29efd7596e65b6ed2572"), "rating" : 1 }
    { "_id" : ObjectId("5b7c29efd7596e65b6ed2573"), "rating" : 1 }
    bye
    

Bookinfoアプリケーションの初期設定

外部データベースの使用シナリオを示すために、 Istioがインストールされた Kubernetesクラスタから始めます。次に、 Istio Bookinfoサンプルアプリケーション をデプロイし、 デフォルトの宛先ルールを適用しIstioをブロックするデフォルトポリシーに変更します

このアプリケーションは、ratings マイクロサービスを使用して本の評価(1〜5の数字)を取得します。評価は、各レビューごとに星として表示されます。ratings マイクロサービスにはいくつかのバージョンがあります。次のセクションでは、 MongoDB を評価データベースとして使用するバージョンをデプロイします。

このブログ記事の例のコマンドは、Istio 1.0で動作します。

念のため、 Bookinfoサンプルアプリケーション のアプリケーションのエンドツーエンドアーキテクチャを次に示します。

The original Bookinfo application
元のBookinfoアプリケーション

Bookinfoアプリケーションで外部データベースを使用する

  1. MongoDBデータベース( *ratings v2* )を使用する *ratings* マイクロサービスの仕様をデプロイします

    Zip
    $ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2.yaml@
    serviceaccount "bookinfo-ratings-v2" created
    deployment "ratings-v2" created
    
  2. MONGO_DB_URL 環境変数をMongoDBの値に更新します

    $ kubectl set env deployment/ratings-v2 "MONGO_DB_URL=mongodb://bookinfo:$BOOKINFO_PASSWORD@$MONGODB_HOST:$MONGODB_PORT/test?authSource=test&ssl=true"
    deployment.extensions/ratings-v2 env updated
    
  3. *reviews* サービス宛てのすべてのトラフィックをその *v3* バージョンにルーティングします。これは、 *reviews* サービスが常に *ratings* サービスを呼び出すことを保証するためです。さらに、 *ratings* サービス宛てのすべてのトラフィックを、データベースを使用する *ratings v2* にルーティングします。

    2つの 仮想サービス を追加して、上記の両方のサービスのルーティングを指定します。これらの仮想サービスは、Istioリリースアーカイブのsamples/bookinfo/networking/virtual-service-ratings-mongodb.yamlに指定されています。 *重要:*次のコマンドを実行する前に、 デフォルトの宛先ルールを適用したことを確認してください。

    Zip
    $ kubectl apply -f @samples/bookinfo/networking/virtual-service-ratings-db.yaml@
    

更新されたアーキテクチャを以下に示します。メッシュ内の青い矢印は、追加した仮想サービスに従って構成されたトラフィックを示しています。仮想サービスに従って、トラフィックは *reviews v3* と *ratings v2* に送信されます。

The Bookinfo application with ratings v2 and an external MongoDB database
ratings v2と外部MongoDBデータベースを使用したBookinfoアプリケーション

MongoDBデータベースはIstioサービスメッシュの外、より正確にはKubernetesクラスタの外にあることに注意してください。サービスメッシュの境界は破線で示されています。

ウェブページへのアクセス

イングレスIPとポートを決定した後、アプリケーションのウェブページにアクセスします。

まだ出力トラフィック制御を構成していないため、MongoDBサービスへのアクセスはIstioによってブロックされています。そのため、評価の星のかわりに、「評価サービスは現在使用できません」というメッセージが各レビューの下に表示されます。

The Ratings service error messages
評価サービスのエラーメッセージ

以降のセクションでは、Istioでの出力制御のさまざまなオプションを使用して、外部MongoDBサービスへの出力アクセスを構成します。

TCPのエグレス制御

MongoDBワイヤプロトコル はTCP上に実行されるため、MongoDBへのエグレストラフィックを他の 外部TCPサービス と同様に制御できます。TCPトラフィックを制御するには、MongoDBホストのIPアドレスを含む CIDR 表記のIPブロックを指定する必要があります。ここで注意すべきは、MongoDBホストのIPが安定していない、または事前にわからない場合があることです。

MongoDBホストのIPが安定していない場合、エグレストラフィックは TLSトラフィックとして制御するか、または 直接 ルーティングしてIstioサイドカープロキシをバイパスできます。

MongoDBデータベースインスタンスのIPアドレスを取得します。オプションとして、 host コマンドを使用できます。

$ export MONGODB_IP=$(host $MONGODB_HOST | grep " has address " | cut -d" " -f4)

ゲートウェイなしでのTCPエグレストラフィックの制御

たとえば、メッシュから出るすべてのトラフィックをゲートウェイ経由で送信する必要がない場合など、 エグレスゲートウェイ を介してトラフィックを直接送信する必要がない場合は、このセクションの手順に従ってください。または、エグレスゲートウェイ経由でトラフィックを直接送信する場合は、 エグレスゲートウェイ経由でのTCPエグレストラフィックの直接送信 に進んでください。

  1. TCPメッシュ外部サービスエントリの定義

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: mongo
    spec:
      hosts:
      - my-mongo.tcp.svc
      addresses:
      - $MONGODB_IP/32
      ports:
      - number: $MONGODB_PORT
        name: tcp
        protocol: TCP
      location: MESH_EXTERNAL
      resolution: STATIC
      endpoints:
      - address: $MONGODB_IP
    EOF
    

    トラフィックが MongoDBプロトコルがTLS上で実行される場合 に暗号化される可能性があるため、プロトコルにTCPを指定し、MONGOを指定しないことに注意してください。トラフィックが暗号化されている場合、暗号化されたMongoDBプロトコルはIstioプロキシによって解析できません。

    プレーンなMongoDBプロトコルが暗号化なしで使用されていることがわかっている場合は、プロトコルをMONGOとして指定し、Istioプロキシに MongoDB関連の統計情報 を生成させることができます。また、プロトコルTCPが指定されている場合、構成はMongoDBに固有のものではなく、TCP上のプロトコルを持つ他のデータベースでも同じであることに注意してください。

    MongoDBのホストはTCPルーティングでは使用されないため、my-mongo.tcp.svcなどの任意のホストを使用できることに注意してください。STATIC解決とMongoDBサービスのIPを持つエンドポイントに注意してください。このようなエンドポイントを定義すると、ドメイン名を持たないMongoDBサービスにアクセスできます。

  2. アプリケーションのウェブページを更新します。これで、アプリケーションはエラーなしで評価を表示するはずです。

    Book Ratings Displayed Correctly
    正しく表示された本の評価

    予想どおり、表示された両方のレビューに星1つの評価が表示されます。外部データベースが実際に使用されていることを確認するために、評価を星1つに設定しました。

  3. エグレスゲートウェイ経由でトラフィックを直接送信する場合は、次のセクションに進んでください。それ以外の場合は、 クリーンアップ を実行します。

エグレスゲートウェイ経由でのTCPエグレストラフィックの直接送信

このセクションでは、 エグレスゲートウェイ を介してトラフィックを直接送信する必要がある場合を処理します。サイドカープロキシは、MongoDBホストのIP(長さ32のCIDRブロック)に一致させることで、MongoDBクライアントからのTCP接続をエグレスゲートウェイにルーティングします。エグレスゲートウェイは、ホスト名でMongoDBホストにトラフィックを転送します。

  1. Istioエグレスゲートウェイのデプロイ.

  2. 前のセクション の手順を実行していない場合は、ここで実行します。

  3. MongoDBクライアントのサイドカープロキシとエグレスゲートウェイ間で、 相互TLS認証 を有効にして、エグレスゲートウェイがソースポッドのIDを監視し、そのIDに基づいてMixerポリシーの適用を有効にすることができます。相互TLSを有効にすると、トラフィックも暗号化されます。相互TLSを有効にしたくない場合は、 サイドカープロキシとエグレスゲートウェイ間の相互TLS セクションに進んでください。それ以外の場合は、次のセクションに進んでください。

サイドカーからエグレスゲートウェイへのTCPトラフィックの構成

  1. エグレスゲートウェイ経由でトラフィックを直接送信するためのポート(例:7777)を保持するEGRESS_GATEWAY_MONGODB_PORT環境変数を定義します。メッシュ内の他のサービスで使用されていないポートを選択する必要があります。

    $ export EGRESS_GATEWAY_MONGODB_PORT=7777
    
  2. 選択したポートをistio-egressgatewayサービスに追加します。Istioのインストールに使用したのと同じ値を使用する必要があります。具体的には、以前に構成したistio-egressgatewayサービスのすべてのポートを指定する必要があります。

    $ helm template install/kubernetes/helm/istio/ --name istio-egressgateway --namespace istio-system -x charts/gateways/templates/deployment.yaml -x charts/gateways/templates/service.yaml --set gateways.istio-ingressgateway.enabled=false --set gateways.istio-egressgateway.enabled=true --set gateways.istio-egressgateway.ports[0].port=80 --set gateways.istio-egressgateway.ports[0].name=http --set gateways.istio-egressgateway.ports[1].port=443 --set gateways.istio-egressgateway.ports[1].name=https --set gateways.istio-egressgateway.ports[2].port=$EGRESS_GATEWAY_MONGODB_PORT --set gateways.istio-egressgateway.ports[2].name=mongo | kubectl apply -f -
    
  3. istio-egressgatewayサービスに選択したポートが実際に存在することを確認します

    $ kubectl get svc istio-egressgateway -n istio-system
    NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                   AGE
    istio-egressgateway   ClusterIP   172.21.202.204   <none>        80/TCP,443/TCP,7777/TCP   34d
    
  4. istio-egressgatewayサービスの相互TLS認証を無効にします

    $ kubectl apply -f - <<EOF
    apiVersion: authentication.istio.io/v1alpha1
    kind: Policy
    metadata:
      name: istio-egressgateway
      namespace: istio-system
    spec:
      targets:
      - name: istio-egressgateway
    EOF
    
  5. MongoDBサービスのエグレスGateway、宛先ルール、および仮想サービスを作成して、エグレスゲートウェイ経由で、そしてエグレスゲートウェイから外部サービスへのトラフィックを直接送信します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: istio-egressgateway
    spec:
      selector:
        istio: egressgateway
      servers:
      - port:
          number: $EGRESS_GATEWAY_MONGODB_PORT
          name: tcp
          protocol: TCP
        hosts:
        - my-mongo.tcp.svc
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: egressgateway-for-mongo
    spec:
      host: istio-egressgateway.istio-system.svc.cluster.local
      subsets:
      - name: mongo
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: mongo
    spec:
      host: my-mongo.tcp.svc
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: direct-mongo-through-egress-gateway
    spec:
      hosts:
      - my-mongo.tcp.svc
      gateways:
      - mesh
      - istio-egressgateway
      tcp:
      - match:
        - gateways:
          - mesh
          destinationSubnets:
          - $MONGODB_IP/32
          port: $MONGODB_PORT
        route:
        - destination:
            host: istio-egressgateway.istio-system.svc.cluster.local
            subset: mongo
            port:
              number: $EGRESS_GATEWAY_MONGODB_PORT
      - match:
        - gateways:
          - istio-egressgateway
          port: $EGRESS_GATEWAY_MONGODB_PORT
        route:
        - destination:
            host: my-mongo.tcp.svc
            port:
              number: $MONGODB_PORT
          weight: 100
    EOF
    
  6. エグレストラフィックがエグレスゲートウェイ経由で直接送信されていることを確認します.

サイドカープロキシとエグレスゲートウェイ間の相互TLS

  1. 以前の設定を削除します。

    $ kubectl delete gateway istio-egressgateway --ignore-not-found=true
    $ kubectl delete virtualservice direct-mongo-through-egress-gateway --ignore-not-found=true
    $ kubectl delete destinationrule egressgateway-for-mongo mongo --ignore-not-found=true
    $ kubectl delete policy istio-egressgateway -n istio-system --ignore-not-found=true
    
  2. istio-egressgateway サービスに対して相互TLS認証を強制します。

    $ kubectl apply -f - <<EOF
    apiVersion: authentication.istio.io/v1alpha1
    kind: Policy
    metadata:
      name: istio-egressgateway
      namespace: istio-system
    spec:
      targets:
      - name: istio-egressgateway
      peers:
      - mtls: {}
    EOF
    
  3. MongoDBサービスのエグレスGateway、宛先ルール、および仮想サービスを作成して、エグレスゲートウェイ経由で、そしてエグレスゲートウェイから外部サービスへのトラフィックを直接送信します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: istio-egressgateway
    spec:
      selector:
        istio: egressgateway
      servers:
      - port:
          number: 443
          name: tls
          protocol: TLS
        hosts:
        - my-mongo.tcp.svc
        tls:
          mode: MUTUAL
          serverCertificate: /etc/certs/cert-chain.pem
          privateKey: /etc/certs/key.pem
          caCertificates: /etc/certs/root-cert.pem
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: egressgateway-for-mongo
    spec:
      host: istio-egressgateway.istio-system.svc.cluster.local
      subsets:
      - name: mongo
        trafficPolicy:
          loadBalancer:
            simple: ROUND_ROBIN
          portLevelSettings:
          - port:
              number: 443
            tls:
              mode: ISTIO_MUTUAL
              sni: my-mongo.tcp.svc
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: mongo
    spec:
      host: my-mongo.tcp.svc
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: direct-mongo-through-egress-gateway
    spec:
      hosts:
      - my-mongo.tcp.svc
      gateways:
      - mesh
      - istio-egressgateway
      tcp:
      - match:
        - gateways:
          - mesh
          destinationSubnets:
          - $MONGODB_IP/32
          port: $MONGODB_PORT
        route:
        - destination:
            host: istio-egressgateway.istio-system.svc.cluster.local
            subset: mongo
            port:
              number: 443
      - match:
        - gateways:
          - istio-egressgateway
          port: 443
        route:
        - destination:
            host: my-mongo.tcp.svc
            port:
              number: $MONGODB_PORT
          weight: 100
    EOF
    
  4. 次のセクションに進みます。

エグレストラフィックがエグレスゲートウェイ経由で直接送信されていることを確認します

  1. アプリケーションのウェブページを再度更新し、レーティングが正しく表示されていることを確認します。

  2. Envoyのアクセスログを有効にします。

  3. エグレスゲートウェイのEnvoyのログを確認し、MongoDBサービスへのリクエストに対応する行を確認します。Istioがistio-system名前空間にデプロイされている場合、ログを出力するコマンドは次のとおりです。

    $ kubectl logs -l istio=egressgateway -n istio-system
    [2019-04-14T06:12:07.636Z] "- - -" 0 - "-" 1591 4393 94 - "-" "-" "-" "-" "<Your MongoDB IP>:<your MongoDB port>" outbound|<your MongoDB port>||my-mongo.tcp.svc 172.30.146.119:59924 172.30.146.119:443 172.30.230.1:59206 -
    

TCPエグレストラフィック制御のクリーンアップ

$ kubectl delete serviceentry mongo
$ kubectl delete gateway istio-egressgateway --ignore-not-found=true
$ kubectl delete virtualservice direct-mongo-through-egress-gateway --ignore-not-found=true
$ kubectl delete destinationrule egressgateway-for-mongo mongo --ignore-not-found=true
$ kubectl delete policy istio-egressgateway -n istio-system --ignore-not-found=true

TLSのエグレス制御

現実世界では、外部サービスへの通信の大部分は暗号化されている必要があり、MongoDBプロトコルはTLS上で動作します。また、TLSクライアントは通常、ハンドシェイクの一部としてサーバー名インジケーション(SNI)を送信します。MongoDBサーバーがTLSを実行し、MongoDBクライアントがハンドシェイクの一部としてSNIを送信する場合は、他のTLS with SNIトラフィックと同様にMongoDBエグレストラフィックを制御できます。TLSとSNIを使用すると、MongoDBサーバーのIPアドレスを指定する必要はありません。代わりにホスト名を指定します。これは、IPアドレスの安定性に依存する必要がないため、より便利です。また、ホスト名のプレフィックスとしてワイルドカードを指定することもできます。たとえば、*.comドメインの任意のサーバーへのアクセスを許可することができます。

MongoDBサーバーがTLSをサポートしているかどうかを確認するには、次のコマンドを実行します。

$ openssl s_client -connect $MONGODB_HOST:$MONGODB_PORT -servername $MONGODB_HOST

上記のコマンドがサーバーから返された証明書を出力した場合、サーバーはTLSをサポートしています。そうでない場合は、前のセクションで説明されているように、TCPレベルでMongoDBエグレストラフィックを制御する必要があります。

ゲートウェイなしでのTLSエグレストラフィックの制御

もしエグレスゲートウェイが不要な場合は、このセクションの手順に従ってください。エグレスゲートウェイを介してトラフィックを転送する場合は、エグレスゲートウェイを介した直接TCPエグレストラフィックに進みます。

  1. MongoDBサービスのServiceEntryを作成します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: mongo
    spec:
      hosts:
      - $MONGODB_HOST
      ports:
      - number: $MONGODB_PORT
        name: tls
        protocol: TLS
      resolution: DNS
    EOF
    
  2. アプリケーションのウェブページを更新します。アプリケーションはエラーなしでレーティングを表示するはずです。

TLSのエグレス設定のクリーンアップ

$ kubectl delete serviceentry mongo

エグレスゲートウェイを介した直接TLSエグレストラフィック

このセクションでは、エグレスゲートウェイを介してトラフィックを転送する必要がある場合の処理について説明します。サイドカープロキシは、MongoDBホストのSNIに一致させることで、MongoDBクライアントからのTLS接続をエグレスゲートウェイにルーティングします。エグレスゲートウェイはトラフィックをMongoDBホストに転送します。サイドカープロキシは送信先ポートを443に書き換えることに注意してください。エグレスゲートウェイはポート443でMongoDBトラフィックを受け入れ、SNIによってMongoDBホストに一致し、ポートをMongoDBサーバーのポートに再度書き換えます。

  1. Istioエグレスゲートウェイのデプロイ.

  2. MongoDBサービスのServiceEntryを作成します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: mongo
    spec:
      hosts:
      - $MONGODB_HOST
      ports:
      - number: $MONGODB_PORT
        name: tls
        protocol: TLS
      - number: 443
        name: tls-port-for-egress-gateway
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
    EOF
    
  3. アプリケーションのウェブページを更新し、レーティングが正しく表示されていることを確認します。

  4. MongoDBサービスのエグレスGateway、およびトラフィックをエグレスゲートウェイを介して、そしてエグレスゲートウェイから外部サービスに転送するための宛先ルールと仮想サービスを作成します。

    アプリケーションポッドのサイドカープロキシとエグレスゲートウェイ間の相互TLS認証を有効にする場合は、次のコマンドを使用します。(エグレスゲートウェイにソースポッドのIDを監視させ、そのIDに基づいてMixerポリシーの適用を有効にするために、相互TLSを有効にしたい場合があります。)

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: istio-egressgateway
    spec:
      selector:
    istio: egressgateway
      servers:
      - port:
      number: 443
      name: tls
      protocol: TLS
    hosts:
    - $MONGODB_HOST
    tls:
      mode: MUTUAL
      serverCertificate: /etc/certs/cert-chain.pem
      privateKey: /etc/certs/key.pem
      caCertificates: /etc/certs/root-cert.pem
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: egressgateway-for-mongo
    spec:
      host: istio-egressgateway.istio-system.svc.cluster.local
      subsets:
      - name: mongo
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 443
        tls:
          mode: ISTIO_MUTUAL
          sni: $MONGODB_HOST
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: direct-mongo-through-egress-gateway
    spec:
      hosts:
      - $MONGODB_HOST
      gateways:
      - mesh
      - istio-egressgateway
      tls:
      - match:
    - gateways:
      - mesh
      port: $MONGODB_PORT
      sni_hosts:
      - $MONGODB_HOST
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: mongo
        port:
          number: 443
      tcp:
      - match:
    - gateways:
      - istio-egressgateway
      port: 443
    route:
    - destination:
        host: $MONGODB_HOST
        port:
          number: $MONGODB_PORT
      weight: 100
    EOF
    
  5. トラフィックがエグレスゲートウェイを介して転送されていることを確認します。

エグレスゲートウェイを介したTLSエグレストラフィック転送のクリーンアップ

$ kubectl delete serviceentry mongo
$ kubectl delete gateway istio-egressgateway
$ kubectl delete virtualservice direct-mongo-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-mongo

任意のワイルドカードドメインへのMongoDB TLSエグレストラフィックの有効化

場合によっては、同じドメインから複数のホスト名へのエグレストラフィック、たとえば*.<your company domain>.comからのすべてのMongoDBサービスへのトラフィックを設定したい場合があります。会社内のすべてのMongoDBサービスごとに設定項目を作成したくありません。単一の設定で同じドメインからのすべての外部サービスへのアクセスを設定するには、ワイルドカードホストを使用します。

このセクションでは、ワイルドカードドメインのエグレストラフィックを設定します。私はcomposedb.comドメインのMongoDBインスタンスを使用したので、*.comのエグレストラフィックの設定はうまく機能しました(*.composedb.comも使用できました)。MongoDBホストに応じてワイルドカードドメインを選択できます。

ワイルドカードドメインのエグレスゲートウェイトラフィックを設定するには、まず、追加のSNIプロキシを使用してカスタムエグレスゲートウェイをデプロイする必要があります。これは、標準のIstioエグレスゲートウェイで使用されるプロキシであるEnvoyの現在の制限によるものです。

SNIプロキシ付きの新しいエグレスゲートウェイの準備

この小節では、標準のIstio Envoyプロキシに加えて、SNIプロキシ付きのエグレスゲートウェイをデプロイします。任意の事前に設定されていないSNI値に従ってトラフィックをルーティングできるSNIプロキシを使用できます。この機能を実現するためにNginxを使用しました。

  1. Nginx SNIプロキシの設定ファイルを作成します。必要に応じて、追加のNginx設定を指定するためにファイルを編集できます。

    $ cat <<EOF > ./sni-proxy.conf
    user www-data;
    
    events {
    }
    
    stream {
      log_format log_stream '\$remote_addr [\$time_local] \$protocol [\$ssl_preread_server_name]'
      '\$status \$bytes_sent \$bytes_received \$session_time';
    
      access_log /var/log/nginx/access.log log_stream;
      error_log  /var/log/nginx/error.log;
    
      # tcp forward proxy by SNI
      server {
        resolver 8.8.8.8 ipv6=off;
        listen       127.0.0.1:$MONGODB_PORT;
        proxy_pass   \$ssl_preread_server_name:$MONGODB_PORT;
        ssl_preread  on;
      }
    }
    EOF
    
  2. Nginx SNIプロキシの設定を保持するためのKubernetes ConfigMapを作成します。

    $ kubectl create configmap egress-sni-proxy-configmap -n istio-system --from-file=nginx.conf=./sni-proxy.conf
    
  3. 次のコマンドは、編集してデプロイするistio-egressgateway-with-sni-proxy.yamlを生成します。

    $ cat <<EOF | helm template install/kubernetes/helm/istio/ --name istio-egressgateway-with-sni-proxy --namespace istio-system -x charts/gateways/templates/deployment.yaml -x charts/gateways/templates/service.yaml -x charts/gateways/templates/serviceaccount.yaml -x charts/gateways/templates/autoscale.yaml -x charts/gateways/templates/role.yaml -x charts/gateways/templates/rolebindings.yaml --set global.mtls.enabled=true --set global.istioNamespace=istio-system -f - > ./istio-egressgateway-with-sni-proxy.yaml
    gateways:
      enabled: true
      istio-ingressgateway:
        enabled: false
      istio-egressgateway:
        enabled: false
      istio-egressgateway-with-sni-proxy:
        enabled: true
        labels:
          app: istio-egressgateway-with-sni-proxy
          istio: egressgateway-with-sni-proxy
        replicaCount: 1
        autoscaleMin: 1
        autoscaleMax: 5
        cpu:
          targetAverageUtilization: 80
        serviceAnnotations: {}
        type: ClusterIP
        ports:
          - port: 443
            name: https
        secretVolumes:
          - name: egressgateway-certs
            secretName: istio-egressgateway-certs
            mountPath: /etc/istio/egressgateway-certs
          - name: egressgateway-ca-certs
            secretName: istio-egressgateway-ca-certs
            mountPath: /etc/istio/egressgateway-ca-certs
        configVolumes:
          - name: sni-proxy-config
            configMapName: egress-sni-proxy-configmap
        additionalContainers:
        - name: sni-proxy
          image: nginx
          volumeMounts:
          - name: sni-proxy-config
            mountPath: /etc/nginx
            readOnly: true
    EOF
    
  4. 新しいエグレスゲートウェイをデプロイします。

    $ kubectl apply -f ./istio-egressgateway-with-sni-proxy.yaml
    serviceaccount "istio-egressgateway-with-sni-proxy-service-account" created
    role "istio-egressgateway-with-sni-proxy-istio-system" created
    rolebinding "istio-egressgateway-with-sni-proxy-istio-system" created
    service "istio-egressgateway-with-sni-proxy" created
    deployment "istio-egressgateway-with-sni-proxy" created
    horizontalpodautoscaler "istio-egressgateway-with-sni-proxy" created
    
  5. 新しいエグレスゲートウェイが実行されていることを確認します。ポッドには2つのコンテナがあります(1つはEnvoyプロキシ、もう1つはSNIプロキシです)。

    $ kubectl get pod -l istio=egressgateway-with-sni-proxy -n istio-system
    NAME                                                  READY     STATUS    RESTARTS   AGE
    istio-egressgateway-with-sni-proxy-79f6744569-pf9t2   2/2       Running   0          17s
    
  6. 127.0.0.1(localhost)に等しい静的アドレスを持つServiceEntryを作成し、新しいServiceEntryに転送されるトラフィックで相互TLSを無効にします。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: sni-proxy
    spec:
      hosts:
      - sni-proxy.local
      location: MESH_EXTERNAL
      ports:
      - number: $MONGODB_PORT
        name: tcp
        protocol: TCP
      resolution: STATIC
      endpoints:
      - address: 127.0.0.1
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: disable-mtls-for-sni-proxy
    spec:
      host: sni-proxy.local
      trafficPolicy:
        tls:
          mode: DISABLE
    EOF
    

新しいエグレスゲートウェイを使用して*.comへのアクセスを設定します。

  1. *.comServiceEntryを定義します。

    $ cat <<EOF | kubectl create -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: mongo
    spec:
      hosts:
      - "*.com"
      ports:
      - number: 443
        name: tls
        protocol: TLS
      - number: $MONGODB_PORT
        name: tls-mongodb
        protocol: TLS
      location: MESH_EXTERNAL
    EOF
    
  2. *.com、ポート443、プロトコルTLSのエグレスGateway、ゲートウェイのSNIを設定する宛先ルール、および悪意のあるアプリケーションによるSNIの改ざんを防ぐEnvoyフィルター(フィルターは、アプリケーションによって発行されたSNIがMixerに報告されたSNIであることを確認します)を作成します。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: istio-egressgateway-with-sni-proxy
    spec:
      selector:
        istio: egressgateway-with-sni-proxy
      servers:
      - port:
          number: 443
          name: tls
          protocol: TLS
        hosts:
        - "*.com"
        tls:
          mode: MUTUAL
          serverCertificate: /etc/certs/cert-chain.pem
          privateKey: /etc/certs/key.pem
          caCertificates: /etc/certs/root-cert.pem
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: mtls-for-egress-gateway
    spec:
      host: istio-egressgateway-with-sni-proxy.istio-system.svc.cluster.local
      subsets:
        - name: mongo
          trafficPolicy:
            loadBalancer:
              simple: ROUND_ROBIN
            portLevelSettings:
            - port:
                number: 443
              tls:
                mode: ISTIO_MUTUAL
    ---
    # The following filter is used to forward the original SNI (sent by the application) as the SNI of the mutual TLS
    # connection.
    # The forwarded SNI will be reported to Mixer so that policies will be enforced based on the original SNI value.
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: forward-downstream-sni
    spec:
      filters:
      - listenerMatch:
          portNumber: $MONGODB_PORT
          listenerType: SIDECAR_OUTBOUND
        filterName: forward_downstream_sni
        filterType: NETWORK
        filterConfig: {}
    ---
    # The following filter verifies that the SNI of the mutual TLS connection (the SNI reported to Mixer) is
    # identical to the original SNI issued by the application (the SNI used for routing by the SNI proxy).
    # The filter prevents Mixer from being deceived by a malicious application: routing to one SNI while
    # reporting some other value of SNI. If the original SNI does not match the SNI of the mutual TLS connection, the
    # filter will block the connection to the external service.
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: egress-gateway-sni-verifier
    spec:
      workloadLabels:
        app: istio-egressgateway-with-sni-proxy
      filters:
      - listenerMatch:
          portNumber: 443
          listenerType: GATEWAY
        filterName: sni_verifier
        filterType: NETWORK
        filterConfig: {}
    EOF
    
  3. *.com宛てのトラフィックをエグレスゲートウェイに、そしてエグレスゲートウェイからSNIプロキシにルーティングします。

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: direct-mongo-through-egress-gateway
    spec:
      hosts:
      - "*.com"
      gateways:
      - mesh
      - istio-egressgateway-with-sni-proxy
      tls:
      - match:
        - gateways:
          - mesh
          port: $MONGODB_PORT
          sni_hosts:
          - "*.com"
        route:
        - destination:
            host: istio-egressgateway-with-sni-proxy.istio-system.svc.cluster.local
            subset: mongo
            port:
              number: 443
          weight: 100
      tcp:
      - match:
        - gateways:
          - istio-egressgateway-with-sni-proxy
          port: 443
        route:
        - destination:
            host: sni-proxy.local
            port:
              number: $MONGODB_PORT
          weight: 100
    EOF
    
  4. アプリケーションのウェブページを再度更新し、レーティングが正しく表示されていることを確認します。

  5. Envoyのアクセスログを有効にします。

  6. エグレスゲートウェイのEnvoyプロキシのログを確認します。Istioがistio-system名前空間にデプロイされている場合、ログを出力するコマンドは次のとおりです。

    $ kubectl logs -l istio=egressgateway-with-sni-proxy -c istio-proxy -n istio-system
    

    次のような行が表示されるはずです。

    [2019-01-02T17:22:04.602Z] "- - -" 0 - 768 1863 88 - "-" "-" "-" "-" "127.0.0.1:28543" outbound|28543||sni-proxy.local 127.0.0.1:49976 172.30.146.115:443 172.30.146.118:58510 <your MongoDB host>
    [2019-01-02T17:22:04.713Z] "- - -" 0 - 1534 2590 85 - "-" "-" "-" "-" "127.0.0.1:28543" outbound|28543||sni-proxy.local 127.0.0.1:49988 172.30.146.115:443 172.30.146.118:58522 <your MongoDB host>
    
  7. SNIプロキシのログを確認します。Istioがistio-system名前空間にデプロイされている場合、ログを出力するコマンドは次のとおりです。

    $ kubectl logs -l istio=egressgateway-with-sni-proxy -n istio-system -c sni-proxy
    127.0.0.1 [23/Aug/2018:03:28:18 +0000] TCP [<your MongoDB host>]200 1863 482 0.089
    127.0.0.1 [23/Aug/2018:03:28:18 +0000] TCP [<your MongoDB host>]200 2590 1248 0.095
    

何が起こったのかを理解する

このセクションでは、ワイルドカードドメインを使用してMongoDBホストへのエグレストラフィックを設定しました。単一のMongoDBホストの場合、ワイルドカードドメインを使用しても利点はありません(正確なホスト名を指定できます)が、クラスタ内のアプリケーションがいくつかのワイルドカードドメインに一致する複数のMongoDBホストにアクセスする場合に役立つ可能性があります。たとえば、アプリケーションがmongodb1.composedb.commongodb2.composedb.com、およびmongodb3.composedb.comにアクセスする場合、エグレストラフィックはワイルドカードドメイン*.composedb.comの単一の設定で設定できます。

このセクションで使用されているワイルドカードドメインに一致するホスト名を持つ別のMongoDBインスタンスを使用するようにアプリを設定する場合、追加のIstio設定は不要であることを確認する演習を、読者にお任せします。

任意のワイルドカードドメインへのMongoDB TLSエグレストラフィックの設定のクリーンアップ

  1. *.comの設定項目を削除します。

    $ kubectl delete serviceentry mongo
    $ kubectl delete gateway istio-egressgateway-with-sni-proxy
    $ kubectl delete virtualservice direct-mongo-through-egress-gateway
    $ kubectl delete destinationrule mtls-for-egress-gateway
    $ kubectl delete envoyfilter forward-downstream-sni egress-gateway-sni-verifier
    
  2. egressgateway-with-sni-proxyデプロイメントの設定項目を削除します。

    $ kubectl delete serviceentry sni-proxy
    $ kubectl delete destinationrule disable-mtls-for-sni-proxy
    $ kubectl delete -f ./istio-egressgateway-with-sni-proxy.yaml
    $ kubectl delete configmap egress-sni-proxy-configmap -n istio-system
    
  3. 作成した設定ファイルを削除します。

    $ rm ./istio-egressgateway-with-sni-proxy.yaml
    $ rm ./nginx-sni-proxy.conf
    

クリーンアップ

  1. bookinfoユーザーを削除します。

    $ cat <<EOF | mongo --ssl --sslAllowInvalidCertificates $MONGODB_HOST:$MONGODB_PORT -u admin -p $MONGO_ADMIN_PASSWORD --authenticationDatabase admin
    use test
    db.dropUser("bookinfo");
    EOF
    
  2. ratingsコレクションを削除します。

    $ cat <<EOF | mongo --ssl --sslAllowInvalidCertificates $MONGODB_HOST:$MONGODB_PORT -u admin -p $MONGO_ADMIN_PASSWORD --authenticationDatabase admin
    use test
    db.ratings.drop();
    EOF
    
  3. 使用した環境変数をクリアします。

    $ unset MONGO_ADMIN_PASSWORD BOOKINFO_PASSWORD MONGODB_HOST MONGODB_PORT MONGODB_IP
    
  4. 仮想サービスを削除します。

    Zip
    $ kubectl delete -f @samples/bookinfo/networking/virtual-service-ratings-db.yaml@
    Deleted config: virtual-service/default/reviews
    Deleted config: virtual-service/default/ratings
    
  5. ratings v2-mongodbをアンデプロイします。

    Zip
    $ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2.yaml@
    deployment "ratings-v2" deleted
    

結論

このブログ投稿では、MongoDBエグレストラフィック制御のさまざまなオプションを示しました。適用可能な場合は、TCPまたはTLSレベルでMongoDBエグレストラフィックを制御できます。TCPとTLSの両方の場合、組織のセキュリティ要件に従って、サイドカープロキシから外部MongoDBホストに直接トラフィックを転送するか、エグレスゲートウェイを介してトラフィックを転送できます。後者の場合、サイドカープロキシとエグレスゲートウェイ間の相互TLS認証を適用または無効にすることも決定できます。*.comのようなワイルドカードドメインを指定してTLSレベルでMongoDBエグレストラフィックを制御し、エグレスゲートウェイを介してトラフィックを転送する必要がある場合は、SNIプロキシ付きのカスタムエグレスゲートウェイをデプロイする必要があります。

このブログ投稿でMongoDBについて説明されている設定と考慮事項は、TCP/TLS上の他の非HTTPプロトコルについてもほぼ同じです。

この記事を共有する