---
# System prepended metadata

title: OpenShift Route Sharding + mTLS

---

-----

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

-----