----- # OpenShift Route Sharding + mTLS ### 摘要 這份筆記記錄了如何在 OpenShift 4.16 中實作路由分片(Route Sharding)和 mTLS (Mutual TLS),以區分需要 mTLS 認證的應用程式流量與一般流量。 需留意: 1. OpenShift只能針對IngressController啟用mTLS, 無法針對個別的Route啟用。因此為了不影響OpenShift預設的IngressController,需要使用到Route Sharding功能 2. 新增的IngressController需要指定domain,且不可以與預設的domain衝突 ----- ## 1\. 啟用路由分片(Route Sharding) 此步驟將 OpenShift 路由流量導向特定的 Worker Node,以隔離不同類型的流量。 ### 1.1 設定 Worker Node 角色(Role) 首先,為 Worker Node 分配不同的角色標籤(label),將 `default-route` 標籤用於處理一般路由,`mtls-route` 標籤用於處理 mTLS 路由。 ```bash [quickcluster@upi-0 route-mtls]$ oc get node NAME STATUS ROLES AGE VERSION master-0.cubtest.lab.psi.pnq2.redhat.com Ready control-plane,master 5h11m v1.29.14+29b5494 master-1.cubtest.lab.psi.pnq2.redhat.com Ready control-plane,master 5h11m v1.29.14+29b5494 master-2.cubtest.lab.psi.pnq2.redhat.com Ready control-plane,master 5h11m v1.29.14+29b5494 worker-0.cubtest.lab.psi.pnq2.redhat.com Ready default-route,worker 4h54m v1.29.14+29b5494 worker-1.cubtest.lab.psi.pnq2.redhat.com Ready default-route,worker 4h54m v1.29.14+29b5494 worker-2.cubtest.lab.psi.pnq2.redhat.com Ready mtls-route,worker 4h54m v1.29.14+29b5494 ``` ### 1.2 修改預設 Ingress Controller 修改預設的 Ingress Controller (`default`),讓它只處理帶有 `default-route` 標籤的節點,並排除特定命名空間(例如 `mtls-test`)的路由。 ```yaml apiVersion: operator.openshift.io/v1 kind: IngressController metadata: name: default namespace: openshift-ingress-operator spec: clientTLS: clientCA: name: "" clientCertificatePolicy: "" httpCompression: {} httpEmptyRequestsPolicy: Respond httpErrorCodePages: name: "" namespaceSelector: matchExpressions: - key: name operator: NotIn values: - mtls-test nodePlacement: nodeSelector: matchLabels: node-role.kubernetes.io/default-route: "" tolerations: - effect: NoSchedule operator: Exists replicas: 2 tuningOptions: reloadInterval: 0s unsupportedConfigOverrides: null ``` 完成修改後,檢查預設的路由器 Pod 是否已被排程到 `worker-0` 和 `worker-1` 上。 ```bash [quickcluster@upi-0 route-mtls]$ oc get pod -n openshift-ingress -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES router-default-7cd655dcb5-9pttq 1/1 Running 0 3h40m 10.74.215.49 worker-0.cubtest.lab.psi.pnq2.redhat.com <none> <none> router-default-7cd655dcb5-kzgzz 1/1 Running 0 3h40m 10.74.215.26 worker-1.cubtest.lab.psi.pnq2.redhat.com <none> <none> ``` ----- ## 2\. 建立 mTLS 憑證 此步驟將產生自簽署的根憑證(Root CA)和客戶端憑證,用於 mTLS 認證。 ### 2.1 建立 Root CA 憑證 首先,建立一個用於簽署其他憑證的根金鑰。**請注意:** 此金鑰極為重要,應妥善保管。 ```bash openssl genrsa -out rootCA.key 4096 ``` 接著,使用該金鑰自簽署根憑證。 ```bash openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt ``` 依照提示輸入憑證資訊: ``` Country Name (2 letter code) [XX]:US State or Province Name (full name) []:NC Locality Name (eg, city) [Default City]: Organization Name (eg, company) [Default Company Ltd]:Security Organizational Unit Name (eg, section) []:OpenShift Common Name (eg, your name or your server's hostname) []:redhat.com Email Address []: ``` ### 2.2 建立客戶端憑證 此憑證將用於客戶端進行 mTLS 驗證。 首先,建立客戶端憑證的金鑰。 ```bash openssl genrsa -out redhat.com.key 2048 ``` 接著,建立憑證簽署請求(CSR)。`Common Name` 必須與你的應用程式網域名稱一致。 ```bash openssl req -new -key redhat.com.key -out redhat.com.csr ``` 依照提示輸入憑證資訊: ``` Country Name (2 letter code) [XX]:US State or Province Name (full name) []:NC ... Common Name (eg, your name or your server's hostname) []:redhat.com ... ``` 最後,使用前面建立的 Root CA 簽署客戶端憑證。 ```bash openssl x509 -req -in redhat.com.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out redhat.com.crt -days 500 -sha256 ``` ### 2.3 驗證憑證 你可以使用以下指令驗證產生的憑證內容。 驗證 CSR: ```bash openssl req -in redhat.com.csr -noout -text ``` 驗證憑證: ```bash openssl x509 -in redhat.com.crt -text -noout ``` ----- ## 3\. 部署 mTLS Ingress Controller 此步驟將為需要 mTLS 認證的應用程式建立一個專屬的 Ingress Controller。 ### 3.1 匯入 Root CA 至 OpenShift 首先,將 Root CA 憑證匯入 OpenShift 的 `openshift-config` 命名空間,供 Ingress Controller 使用。 ```bash oc create configmap mtls-router-ca --from-file=ca-bundle.pem=rootCA.crt -n openshift-config ``` ### 3.2 建立新的 Ingress Controller 建立一個名為 `mtls-router` 的 Ingress Controller。此設定檔將: - 指定 `clientTLS`,要求客戶端提供憑證,並使用前面匯入的 `mtls-router-ca` 進行驗證。 - 將路由部署在帶有 `mtls-route` 標籤的節點上。 - 使用 `NodePortService` 暴露服務。 - 使用 `routeSelector` 篩選帶有 `type: sharded` 標籤的路由。 <!-- end list --> ```yaml apiVersion: operator.openshift.io/v1 kind: IngressController metadata: name: mtls-router namespace: openshift-ingress-operator spec: clientTLS: clientCA: name: mtls-router-ca clientCertificatePolicy: Required domain: jace.apps.cubtest.lab.psi.pnq2.redhat.com endpointPublishingStrategy: type: NodePortService nodePlacement: nodeSelector: matchLabels: node-role.kubernetes.io/mtls-route: "" replicas: 1 routeSelector: matchLabels: type: sharded ``` 檢查新的 Ingress Controller Pod 是否已成功部署,以及是否暴露了 NodePort。 ```bash [quickcluster@upi-0 route-mtls]$ oc get pod,svc -n openshift-ingress NAME READY STATUS RESTARTS AGE pod/router-default-7cd655dcb5-9pttq 1/1 Running 0 3h44m pod/router-default-7cd655dcb5-kzgzz 1/1 Running 0 3h44m pod/router-mtls-router-77bd5bf66d-9zhxr 1/1 Running 0 21m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/router-internal-default ClusterIP 172.30.228.102 <none> 80/TCP,443/TCP,1936/TCP 5h23m service/router-internal-mtls-router ClusterIP 172.30.146.131 <none> 80/TCP,443/TCP,1936/TCP 21m service/router-nodeport-mtls-router NodePort 172.30.158.102 <none> 80:31494/TCP,443:31688/TCP,1936:30606/TCP 21m ``` ----- ## 4\. 測試 mTLS 應用程式 此步驟將部署一個測試應用程式並建立路由,以驗證 mTLS 設定是否生效。 ### 4.1 建立應用程式 建立一個新的命名空間 `mtls-test`,並部署一個簡單的 `hostnames` 應用程式和對應的 Service。 ```bash oc new-project mtls-test ``` ```yaml cat << 'EOF' | oc apply -f - apiVersion: v1 kind: Service metadata: name: hostnames-svc spec: ports: - port: 9376 protocol: TCP selector: app: hostnames sessionAffinity: None --- apiVersion: apps/v1 kind: Deployment metadata: name: hostnames spec: replicas: 1 selector: matchLabels: app: hostnames template: metadata: labels: app: hostnames spec: containers: - name: hostnames image: gcr.io/google_containers/serve_hostname:1.3 ports: - containerPort: 9376 EOF ``` ### 4.2 建立路由 為 `hostnames` 服務建立一個路由,並加入 `type: sharded` 標籤,使其被 `mtls-router` 處理。 ```yaml [quickcluster@upi-0 route-mtls]$ cat route.yaml apiVersion: route.openshift.io/v1 kind: Route metadata: name: hostnames-svc namespace: mtls-test labels: type: sharded spec: tls: termination: edge insecureEdgeTerminationPolicy: Redirect subdomain: jace port: targetPort: 9376 to: kind: Service name: hostnames-svc weight: 100 ``` ### 4.3 測試連線 - **未使用憑證的連線測試:** 不帶憑證嘗試連接,`mtls-router` 會因為缺少客戶端憑證而拒絕連線。請注意,由於沒有 DNS 設定,需要手動指定主機和 NodePort。 ```bash [quickcluster@upi-0 route-mtls]$ curl https://10.74.213.61:31688 -H "Host: jace.jace.apps.cubtest.lab.psi.pnq2.redhat.com" -k curl: (56) OpenSSL SSL_read: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0 ``` - **使用憑證的連線測試:** 帶上前面簽發的客戶端憑證和金鑰,連線將會成功。 ```bash [quickcluster@upi-0 route-mtls]$ curl https://10.74.213.61:31688 -H "Host: jace.jace.apps.cubtest.lab.psi.pnq2.redhat.com" --cert redhat.com.crt --key redhat.com.key -k hostnames-65f7c499c8-vqjjx ``` -----