セキュアゲートウェイ

イングレストラフィックの制御タスクでは、外部トラフィックにHTTPサービスを公開するようにイングレスゲートウェイを構成する方法について説明しています。このタスクでは、単純なTLSまたは相互TLSを使用して、安全なHTTPSサービスを公開する方法を示します。

始める前に

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

  • httpbinサンプルを開始します。

    Zip
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@
    
  • macOSユーザーの場合は、LibreSSLライブラリでコンパイルされたcurlを使用していることを確認してください。

    $ curl --version | grep LibreSSL
    curl 7.54.0 (x86_64-apple-darwin17.0) libcurl/7.54.0 LibreSSL/2.0.20 zlib/1.2.11 nghttp2/1.24.0
    

    前のコマンドが示すようにLibreSSLのバージョンが出力される場合、このタスクの手順でcurlコマンドが正しく動作するはずです。それ以外の場合は、別のcurlの実装(たとえばLinuxマシン上など)を試してください。

クライアントとサーバーの証明書と鍵を生成する

このタスクでは、以下の例で使用されるいくつかの証明書と鍵のセットが必要です。お気に入りのツールを使用して作成することも、以下のコマンドを使用してopensslで生成することもできます。

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

    $ mkdir example_certs1
    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs1/example.com.key -out example_certs1/example.com.crt
    
  2. httpbin.example.comの証明書と秘密鍵を生成します。

    $ openssl req -out example_certs1/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 0 -in example_certs1/httpbin.example.com.csr -out example_certs1/httpbin.example.com.crt
    
  3. 同じ種類の証明書と鍵の2番目のセットを作成します。

    $ mkdir example_certs2
    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs2/example.com.key -out example_certs2/example.com.crt
    $ openssl req -out example_certs2/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs2/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs2/example.com.crt -CAkey example_certs2/example.com.key -set_serial 0 -in example_certs2/httpbin.example.com.csr -out example_certs2/httpbin.example.com.crt
    
  4. helloworld.example.comの証明書と秘密鍵を生成します。

    $ openssl req -out example_certs1/helloworld.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/helloworld.example.com.key -subj "/CN=helloworld.example.com/O=helloworld organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 1 -in example_certs1/helloworld.example.com.csr -out example_certs1/helloworld.example.com.crt
    
  5. クライアント証明書と秘密鍵を生成します。

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

単一ホストのTLSイングレスゲートウェイを構成する

  1. イングレスゲートウェイのシークレットを作成します。

    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs1/httpbin.example.com.key \
      --cert=example_certs1/httpbin.example.com.crt
    
  2. イングレスゲートウェイを構成します。

最初に、ポート443のservers:セクションを持つゲートウェイを定義し、credentialNameの値をhttpbin-credentialに指定します。値はシークレットの名前と同じです。TLSモードの値はSIMPLEにする必要があります。

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: httpbin-credential # must be the same as secret
    hosts:
    - httpbin.example.com
EOF

次に、対応する仮想サービスを定義して、ゲートウェイのイングレストラフィックルートを構成します。

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - mygateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

最後に、これらの手順に従って、ゲートウェイにアクセスするためのINGRESS_HOSTおよびSECURE_INGRESS_PORT変数を設定します。

  1. HTTPSを介してhttpbinサービスにアクセスするためのHTTPSリクエストを送信します。

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    I'm a teapot!
    ...
    

    httpbinサービスは、418 I’m a Teapotコードを返します。

  2. ゲートウェイのシークレットを削除してから、異なる証明書と鍵を使用して再作成することにより、ゲートウェイの資格情報を変更します。

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs2/httpbin.example.com.key \
      --cert=example_certs2/httpbin.example.com.crt
    
  3. 新しい証明書チェーンを使用してcurlhttpbinサービスにアクセスします。

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs2/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    I'm a teapot!
    ...
    
  4. 以前の証明書チェーンを使用してhttpbinにアクセスしようとすると、試行は失敗します。

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    * 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 (OUT), TLS alert, Server hello (2):
    * curl: (35) error:04FFF06A:rsa routines:CRYPTO_internal:block type is not 01
    

複数ホストのTLSイングレスゲートウェイを構成する

複数のホスト(たとえば、httpbin.example.comhelloworld.example.com)に対してイングレスゲートウェイを構成できます。イングレスゲートウェイは、各ホストに対応する一意の資格情報で構成されます。

  1. 元の証明書と鍵でシークレットを削除して再作成することにより、前の例からhttpbinの資格情報を復元します。

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs1/httpbin.example.com.key \
      --cert=example_certs1/httpbin.example.com.crt
    
  2. helloworld-v1サンプルを開始します。

    ZipZip
    $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l service=helloworld
    $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l version=v1
    
  3. helloworld-credentialシークレットを作成します。

    $ kubectl create -n istio-system secret tls helloworld-credential \
      --key=example_certs1/helloworld.example.com.key \
      --cert=example_certs1/helloworld.example.com.crt
    
  4. ホストhttpbin.example.comhelloworld.example.comを使用してイングレスゲートウェイを構成します。

ポート443に2つのサーバーセクションを持つゲートウェイを定義します。各ポートのcredentialNameの値をそれぞれhttpbin-credentialhelloworld-credentialに設定します。TLSモードをSIMPLEに設定します。

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https-httpbin
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: httpbin-credential
    hosts:
    - httpbin.example.com
  - port:
      number: 443
      name: https-helloworld
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: helloworld-credential
    hosts:
    - helloworld.example.com
EOF

対応する仮想サービスを定義して、ゲートウェイのトラフィックルートを構成します。

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: helloworld
spec:
  hosts:
  - helloworld.example.com
  gateways:
  - mygateway
  http:
  - match:
    - uri:
        exact: /hello
    route:
    - destination:
        host: helloworld
        port:
          number: 5000
EOF
  1. helloworld.example.comにHTTPSリクエストを送信します。

    $ curl -v -HHost:helloworld.example.com --resolve "helloworld.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://helloworld.example.com:$SECURE_INGRESS_PORT/hello"
    ...
    HTTP/2 200
    ...
    
  2. httpbin.example.comにHTTPSリクエストを送信し、HTTP 418を返します。

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    server: istio-envoy
    ...
    

相互TLSイングレスゲートウェイを構成する

相互TLSをサポートするようにゲートウェイの定義を拡張できます。

  1. シークレットを削除して新しいシークレットを作成することにより、イングレスゲートウェイの資格情報を変更します。サーバーはCA証明書を使用してクライアントを検証し、CA証明書を保持するには鍵ca.crtを使用する必要があります。

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret generic httpbin-credential \
      --from-file=tls.key=example_certs1/httpbin.example.com.key \
      --from-file=tls.crt=example_certs1/httpbin.example.com.crt \
      --from-file=ca.crt=example_certs1/example.com.crt
    
  2. イングレスゲートウェイを構成します。

TLSモードをMUTUALに設定するようにゲートウェイの定義を変更します。

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: MUTUAL
      credentialName: httpbin-credential # must be the same as secret
    hosts:
    - httpbin.example.com
EOF
  1. 以前の方法を使用してHTTPSリクエストを送信しようとし、どのように失敗するかを確認します。

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
    --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    * TLSv1.3 (OUT), TLS handshake, Client hello (1):
    * TLSv1.3 (IN), TLS handshake, Server hello (2):
    * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
    * TLSv1.3 (IN), TLS handshake, Request CERT (13):
    * TLSv1.3 (IN), TLS handshake, Certificate (11):
    * TLSv1.3 (IN), TLS handshake, CERT verify (15):
    * TLSv1.3 (IN), TLS handshake, Finished (20):
    * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.3 (OUT), TLS handshake, Certificate (11):
    * TLSv1.3 (OUT), TLS handshake, Finished (20):
    * TLSv1.3 (IN), TLS alert, unknown (628):
    * OpenSSL SSL_read: error:1409445C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required, errno 0
    
  2. クライアント証明書と秘密鍵をcurlに渡し、リクエストを再送信します。--certフラグでクライアントの証明書を、--keyフラグで秘密鍵をcurlに渡します。

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt --cert example_certs1/client.example.com.crt --key example_certs1/client.example.com.key \
      "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    server: istio-envoy
    ...
    I'm a teapot!
    ...
    

詳細情報

鍵のフォーマット

Istioは、cert-managerなどのさまざまなツールとの統合をサポートするために、いくつかの異なるシークレット形式の読み取りをサポートしています。

  • 上記のように、キーtls.keytls.crtを持つTLSシークレット。相互TLSの場合、ca.crtキーを使用できます。
  • キーkeycertを持つ汎用シークレット。相互TLSの場合、cacertキーを使用できます。
  • キーkeycertを持つ汎用シークレット。相互TLSの場合、cacertキーを持つ、<secret>-cacertという名前の別の汎用シークレット。たとえば、httpbin-credentialにはkeycertがあり、httpbin-credential-cacertにはcacertがあります。
  • cacertキーの値は、連結された個々のCA証明書で構成されるCAバンドルにすることができます。

SNIルーティング

HTTPS Gatewayは、リクエストを転送する前に、構成されたホストに対してSNIマッチングを実行します。これにより、一部のリクエストが失敗する可能性があります。詳細については、SNIルーティングの構成を参照してください。

トラブルシューティング

  • INGRESS_HOSTおよびSECURE_INGRESS_PORT環境変数の値を検査します。次のコマンドの出力に従って、有効な値があることを確認します。

    $ kubectl get svc -n istio-system
    $ echo "INGRESS_HOST=$INGRESS_HOST, SECURE_INGRESS_PORT=$SECURE_INGRESS_PORT"
    
  • INGRESS_HOSTの値がIPアドレスであることを確認します。一部のクラウドプラットフォーム(例:AWS)では、代わりにドメイン名を取得する場合があります。このタスクではIPアドレスを想定しているため、次のようなコマンドで変換する必要があります。

    $ nslookup ab52747ba608744d8afd530ffd975cbf-330887905.us-east-1.elb.amazonaws.com
    $ export INGRESS_HOST=3.225.207.109
    
  • エラーメッセージについてゲートウェイコントローラーのログを確認します。

    $ kubectl logs -n istio-system <gateway-service-pod>
    
  • macOSを使用している場合は、始める前にセクションで説明されているように、LibreSSLライブラリでコンパイルされたcurlを使用していることを確認します。

  • シークレットがistio-system名前空間に正常に作成されていることを確認します。

    $ kubectl -n istio-system get secrets
    

    httpbin-credentialhelloworld-credentialがシークレットリストに表示されるはずです。

  • イングレスゲートウェイエージェントが鍵と証明書のペアをイングレスゲートウェイにプッシュしたことを確認するためにログを確認します。

    $ kubectl logs -n istio-system <gateway-service-pod>
    

    ログにhttpbin-credentialシークレットが追加されたことが示されるはずです。相互TLSを使用している場合、httpbin-credential-cacertシークレットも表示されるはずです。ログで、ゲートウェイエージェントがイングレスゲートウェイからSDSリクエストを受信し、リソースの名前がhttpbin-credentialであり、イングレスゲートウェイが鍵と証明書のペアを取得したことを確認します。相互TLSを使用している場合、ログには、鍵と証明書がイングレスゲートウェイに送信され、ゲートウェイエージェントがhttpbin-credential-cacertリソース名でSDSリクエストを受信し、イングレスゲートウェイがルート証明書を取得したことが示されるはずです。

クリーンアップ

  1. ゲートウェイの構成とルートを削除します。
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice httpbin helloworld
  1. シークレット、証明書、鍵を削除します。

    $ kubectl delete -n istio-system secret httpbin-credential helloworld-credential
    $ rm -rf ./example_certs1 ./example_certs2
    
  2. httpbinおよびhelloworldサービスをシャットダウンします。

    $ kubectl delete -f samples/httpbin/httpbin.yaml
    $ kubectl delete deployment helloworld-v1
    $ kubectl delete service helloworld
    
この情報は役に立ちましたか?
改善のための提案はありますか?

フィードバックありがとうございます!