-----
# 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
```
-----