セキュアゲートウェイ
イングレストラフィックの制御タスクでは、外部トラフィックにHTTPサービスを公開するようにイングレスゲートウェイを構成する方法について説明しています。このタスクでは、単純なTLSまたは相互TLSを使用して、安全なHTTPSサービスを公開する方法を示します。
始める前に
インストールガイドの手順に従ってIstioをセットアップします。
httpbinサンプルを開始します。
$ 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で生成することもできます。
サービスの証明書に署名するためのルート証明書と秘密鍵を作成します。
$ 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
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
同じ種類の証明書と鍵の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
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
クライアント証明書と秘密鍵を生成します。
$ 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イングレスゲートウェイを構成する
イングレスゲートウェイのシークレットを作成します。
$ kubectl create -n istio-system secret tls httpbin-credential \ --key=example_certs1/httpbin.example.com.key \ --cert=example_certs1/httpbin.example.com.crt
イングレスゲートウェイを構成します。
最初に、ポート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
変数を設定します。
最初に、Kubernetes Gatewayを作成します。
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOF
次に、対応するHTTPRoute
を定義して、ゲートウェイのイングレストラフィックルートを構成します。
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
spec:
parentRefs:
- name: mygateway
namespace: istio-system
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /status
- path:
type: PathPrefix
value: /delay
backendRefs:
- name: httpbin
port: 8000
EOF
最後に、Gateway
リソースからゲートウェイのアドレスとポートを取得します。
$ kubectl wait --for=condition=programmed gtw mygateway -n istio-system
$ export INGRESS_HOST=$(kubectl get gtw mygateway -n istio-system -o jsonpath='{.status.addresses[0].value}')
$ export SECURE_INGRESS_PORT=$(kubectl get gtw mygateway -n istio-system -o jsonpath='{.spec.listeners[?(@.name=="https")].port}')
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コードを返します。ゲートウェイのシークレットを削除してから、異なる証明書と鍵を使用して再作成することにより、ゲートウェイの資格情報を変更します。
$ 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
新しい証明書チェーンを使用して
curl
でhttpbin
サービスにアクセスします。$ 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! ...
以前の証明書チェーンを使用して
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.com
とhelloworld.example.com
)に対してイングレスゲートウェイを構成できます。イングレスゲートウェイは、各ホストに対応する一意の資格情報で構成されます。
元の証明書と鍵でシークレットを削除して再作成することにより、前の例から
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
helloworld-v1
サンプルを開始します。$ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l service=helloworld $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l version=v1
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
ホスト
httpbin.example.com
とhelloworld.example.com
を使用してイングレスゲートウェイを構成します。
ポート443に2つのサーバーセクションを持つゲートウェイを定義します。各ポートのcredentialName
の値をそれぞれhttpbin-credential
とhelloworld-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
ポート443に2つのリスナーを持つGateway
を構成します。各リスナーのcertificateRefs
の値をそれぞれhttpbin-credential
とhelloworld-credential
に設定します。
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https-httpbin
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
- name: https-helloworld
hostname: "helloworld.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: helloworld-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOF
helloworld
サービスのゲートウェイトラフィックルートを構成します。
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: helloworld
spec:
parentRefs:
- name: mygateway
namespace: istio-system
hostnames: ["helloworld.example.com"]
rules:
- matches:
- path:
type: Exact
value: /hello
backendRefs:
- name: helloworld
port: 5000
EOF
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 ...
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をサポートするようにゲートウェイの定義を拡張できます。
シークレットを削除して新しいシークレットを作成することにより、イングレスゲートウェイの資格情報を変更します。サーバーは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
イングレスゲートウェイを構成します。
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
Kubernetes Gateway APIは現在、Gatewayで相互TLS終端をサポートしていないため、Istio固有のオプションgateway.istio.io/tls-terminate-mode: MUTUAL
を使用して構成します。
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
options:
gateway.istio.io/tls-terminate-mode: MUTUAL
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOF
以前の方法を使用して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
クライアント証明書と秘密鍵を
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.key
とtls.crt
を持つTLSシークレット。相互TLSの場合、ca.crt
キーを使用できます。 - キー
key
とcert
を持つ汎用シークレット。相互TLSの場合、cacert
キーを使用できます。 - キー
key
とcert
を持つ汎用シークレット。相互TLSの場合、cacert
キーを持つ、<secret>-cacert
という名前の別の汎用シークレット。たとえば、httpbin-credential
にはkey
とcert
があり、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-credential
とhelloworld-credential
がシークレットリストに表示されるはずです。イングレスゲートウェイエージェントが鍵と証明書のペアをイングレスゲートウェイにプッシュしたことを確認するためにログを確認します。
$ kubectl logs -n istio-system <gateway-service-pod>
ログに
httpbin-credential
シークレットが追加されたことが示されるはずです。相互TLSを使用している場合、httpbin-credential-cacert
シークレットも表示されるはずです。ログで、ゲートウェイエージェントがイングレスゲートウェイからSDSリクエストを受信し、リソースの名前がhttpbin-credential
であり、イングレスゲートウェイが鍵と証明書のペアを取得したことを確認します。相互TLSを使用している場合、ログには、鍵と証明書がイングレスゲートウェイに送信され、ゲートウェイエージェントがhttpbin-credential-cacert
リソース名でSDSリクエストを受信し、イングレスゲートウェイがルート証明書を取得したことが示されるはずです。
クリーンアップ
- ゲートウェイの構成とルートを削除します。
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice httpbin helloworld
$ kubectl delete -n istio-system gtw mygateway
$ kubectl delete httproute httpbin helloworld
シークレット、証明書、鍵を削除します。
$ kubectl delete -n istio-system secret httpbin-credential helloworld-credential $ rm -rf ./example_certs1 ./example_certs2
httpbin
およびhelloworld
サービスをシャットダウンします。$ kubectl delete -f samples/httpbin/httpbin.yaml $ kubectl delete deployment helloworld-v1 $ kubectl delete service helloworld