安全閘道
控制入口流量任務說明如何配置入口閘道,以將 HTTP 服務公開給外部流量。此任務說明如何使用簡單或相互 TLS 公開安全 HTTPS 服務。
開始之前
請依照安裝指南中的說明設定 Istio。
啟動 httpbin 範例
$ kubectl apply -f @samples/httpbin/httpbin.yaml@
對於 macOS 使用者,請確認您使用的
curl
是使用 LibreSSL 函式庫編譯的$ 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
建立第二組相同種類的憑證和金鑰
$ 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
設定入口閘道
首先,定義一個閘道,其中包含 port 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 閘道
$ 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 請求以透過 HTTPS 存取
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" ... 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
設定入口閘道
定義一個閘道,其中包含兩個 port 443 的 server 區段。將每個連接埠上的 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
設定一個 Gateway
,其中包含兩個 port 443 的監聽器。將每個監聽器上的 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
傳送 HTTPS 請求至
helloworld.example.com
$ 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 ...
傳送 HTTPS 請求至
httpbin.example.com
,並仍然收到 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.crt
來保留 CA 憑證。$ 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 閘道 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,則使用名為<secret>-cacert
的單獨通用密鑰,並帶有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>
確認密鑰已在
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