feat: ApisixTls support mTLS (#492)

diff --git a/.licenserc.yaml b/.licenserc.yaml
index ad8836c..070b515 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -38,4 +38,6 @@
     - 'pkg/kube/apisix/client/**'
     - '**/zz_generated.deepcopy.go'
     - 'utils/generate-groups.sh'
+    - '**/*.pem'
+    - '**/*.key'
   comment: on-failure
diff --git a/docs/en/latest/practices/mtls.md b/docs/en/latest/practices/mtls.md
new file mode 100644
index 0000000..7651bb4
--- /dev/null
+++ b/docs/en/latest/practices/mtls.md
@@ -0,0 +1,222 @@
+---
+title: Configuring Mutual Authentication via ApisixTls
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+In this practice, we will use mTLS to protect our exposed ingress APIs.
+
+To learn more about mTLS, please refer to [Mutual authentication](https://en.wikipedia.org/wiki/Mutual_authentication)
+
+## Prerequisites
+
+- an available Kubernetes cluster
+- an available APISIX and APISIX Ingress Controller installation
+
+In this guide, we assume that your APISIX is installed in the `apisix` namespace and `ssl` is enabled, which is not enabled by default in the Helm Chart. To enable it, you need to set `gateway.tls.enabled=true` during installation.
+
+Assuming the SSL port is `9443`.
+
+## Deploy httpbin service
+
+We use [kennethreitz/httpbin](https://hub.docker.com/r/kennethreitz/httpbin/) as the service image, See its overview page for details.
+
+Deploy it to the default namespace:
+
+```shell
+kubectl run httpbin --image kennethreitz/httpbin --port 80
+kubectl expose pod httpbin --port 80
+```
+
+## Route the traffic
+
+Since SSL is not configured in ApisixRoute, we can use the config similar to the one in practice [Proxy the httpbin service](./proxy-the-httpbin-service.md).
+
+```yaml
+# route.yaml
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpserver-route
+spec:
+  http:
+    - name: httpbin
+      match:
+        hosts:
+          - mtls.httpbin.local
+        paths:
+          - "/*"
+      backend:
+        serviceName: httpbin
+        servicePort: 80
+```
+
+Please remember the host field is `mtls.httpbin.local`. It will be the domain we are going to use.
+
+Test it:
+
+```bash
+kubectl -n apisix exec -it <APISIX_POD_NAME> -- curl "http://127.0.0.1:9080/ip" -H "Host: mtls.httpbin.local"
+```
+
+It should output:
+
+```json
+{
+  "origin": "127.0.0.1"
+}
+```
+
+## Certificates
+
+Before configuring SSL, we must have certificates. Certificates often authorized by certificate provider, which also known as Certification Authority (CA).
+
+You can use [OpenSSL](https://en.wikipedia.org/wiki/Openssl) to generate self-signed certificates for testing purposes. Some pre-generated certificates for this guide are [here](./mtls).
+
+- `ca.pem`: The root CA.
+- `server.pem` and `server.key`: Server certificate used to enable SSL (https). Contains correct `subjectAltName` matches domain `mtls.httpbin.local`.
+- `user.pem` and `user.key`: Client certificate.
+
+To verify them, use commands below:
+
+```bash
+openssl verify -CAfile ./ca.pem ./server.pem
+openssl verify -CAfile ./ca.pem ./user.pem
+```
+
+## Protect the route using SSL
+
+In APISIX Ingress Controller, we use [ApisixTls](../concepts/apisix_tls.md) resource to protect our routes.
+
+ApisixTls requires a secret which field `cert` and `key` contains the certificate and private key.
+
+A secret yaml containing the certificate mentioned above [is here](./mtls/server-secret.yaml). In this guide, we use this as an example.
+
+```bash
+kubectl apply -f ./mtls/server-secret.yaml -n default
+```
+
+The secret name is `server-secret`, we created it in the `default` namespace. We will reference this secret in `ApisixTls`.
+
+```yaml
+# tls.yaml
+apiVersion: apisix.apache.org/v1
+kind: ApisixTls
+metadata:
+  name: sample-tls
+spec:
+  hosts:
+    - mtls.httpbin.local
+  secret:
+    name: server-secret
+    namespace: default
+```
+
+The `secret` field contains the secret reference.
+
+Please note that the `hosts` field matches our domain `mtls.httpbin.local`.
+
+Apply this yaml, APISIX Ingress Controller will use our certificate to protect the route. Let's test it.
+
+```bash
+kubectl -n apisix exec -it <APISIX_POD_NAME> -- curl --resolve 'mtls.httpbin.local:9443:127.0.0.1' "https://mtls.httpbin.local:9443/ip" -k
+```
+
+Some major changes here:
+
+- Use `--resolve` parameter to resolve our domain.
+  - No `Host` header set explicit.
+- We are using `https` and SSL port `9443`.
+- Parameter `-k` to allow insecure connections when using SSL. Because our self-signed certificate is not trusted.
+
+Without the domain `mtls.httpbin.local`, the request won't succeed.
+
+You can add parameter `-v` to log the handshake process.
+
+Now, we configured SSL successfully.
+
+## Mutual Authentication
+
+Like `server-secret`, we will create a `client-ca-secret` to store the CA that verify the certificate client presents.
+
+```bash
+kubectl apply -f ./mtls/client-ca-secret.yaml -n default
+```
+
+Then, change our ApisixTls and apply it:
+
+```yaml
+# mtls.yaml
+apiVersion: apisix.apache.org/v1
+kind: ApisixTls
+metadata:
+  name: sample-tls
+spec:
+  hosts:
+    - mtls.httpbin.local
+  secret:
+    name: server-secret
+    namespace: default
+  client:
+    caSecret:
+      name: client-ca-secret
+      namespace: default
+    depth: 10
+```
+
+The `client` field references the secret, `depth` indicates the max certificate chain length.
+
+Let's try to connect the route without any chanegs:
+
+```bash
+kubectl -n apisix exec -it <APISIX_POD_NAME> -- curl --resolve 'mtls.httpbin.local:9443:127.0.0.1' "https://mtls.httpbin.local:9443/ip" -k
+```
+
+If everything works properly, it will return a `400 Bad Request`.
+
+From APISIX access log, we could find logs like this:
+
+```log
+2021/05/27 17:20:54 [error] 43#43: *106132 [lua] init.lua:293: http_access_phase(): client certificate was not present, client: 127.0.0.1, server: _, request: "GET /ip HTTP/2.0", host: "mtls.httpbin.local:9443"
+127.0.0.1 - - [27/May/2021:17:20:54 +0000] mtls.httpbin.local:9443 "GET /ip HTTP/2.0" 400 154 0.000 "-" "curl/7.76.1" - - - "http://mtls.httpbin.local:9443"
+```
+
+That means our mutual authentication has been enabled successfully.
+
+Now, we need to transfer our client cert to the APISIX container to verify the mTLS functionality.
+
+```bash
+# Transfer client certificate
+kubectl -n apisix cp ./user.key <APISIX_POD_NAME>:/tmp/user.key
+kubectl -n apisix cp ./user.pem <APISIX_POD_NAME>:/tmp/user.pem
+
+# Test
+kubectl -n apisix exec -it <APISIX_POD_NAME> -- curl --resolve 'mtls.httpbin.local:9443:127.0.0.1' "https://mtls.httpbin.local:9443/ip" -k --cert /tmp/user.pem --key /tmp/user.key
+```
+
+Parameter `--cert` and `--key` indicates our certificate and key path.
+
+It should output normally:
+
+```json
+{
+  "origin": "127.0.0.1"
+}
+```
diff --git a/docs/en/latest/practices/mtls/ca.pem b/docs/en/latest/practices/mtls/ca.pem
new file mode 100644
index 0000000..a0c9587
--- /dev/null
+++ b/docs/en/latest/practices/mtls/ca.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF9zCCA9+gAwIBAgIUFKuzAJZgm/fsFS6JDrd+lcpVZr8wDQYJKoZIhvcNAQEL
+BQAwgZwxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI
+SGFuZ3pob3UxGDAWBgNVBAoMD0FQSVNJWC1UZXN0LUNBXzEYMBYGA1UECwwPQVBJ
+U0lYX0NBX1JPT1RfMRUwEwYDVQQDDAxBUElTSVguUk9PVF8xHDAaBgkqhkiG9w0B
+CQEWDXRlc3RAdGVzdC5jb20wHhcNMjEwNTI3MTMzNjI4WhcNMjIwNTI3MTMzNjI4
+WjCBnDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI
+YW5nemhvdTEYMBYGA1UECgwPQVBJU0lYLVRlc3QtQ0FfMRgwFgYDVQQLDA9BUElT
+SVhfQ0FfUk9PVF8xFTATBgNVBAMMDEFQSVNJWC5ST09UXzEcMBoGCSqGSIb3DQEJ
+ARYNdGVzdEB0ZXN0LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+ALJR0lQW/IBqQTE/Oa0Pi4LlmlYUSGnqtFNqiZyOF0PjVzNeqoD9JDPiM1QRyC8p
+NCd5L/QhtUIMMx0RlDI9DkJ3ALIWdrPIZlwpveDJf4KtW7cz+ea46A6QQwB6xcyV
+xWnqEBkiea7qrEE8NakZOMjgkqkN2/9klg6XyA5FWfvszxtuIHtjcy2Kq8bMC0jd
+k7CqEZe4ct6s2wlcI8t8s9prvMDm8gcX66x4Ah+C2/W+C3lTpMDgGqRqSPyCW7na
+Wgn0tWmTSf1iybwYMydhC+zpM1QJLvfDyqjp1wJhziR5ttVe2Xc+tDC24s+u16yZ
+R93IO0M4lLNjvEKJcMltXyRzrcjvLXOhw3KirSHNL1KfrBEl74lb+DV5eU4pIFCj
+cu18gms5FBYs9tpLujwpHDc2MU+zCvRmSPvUA4yCyoXqom3uiSo3g3ymW9IM8dC8
++Bd1GdM6JbpBukvQybc5TQXo1M75I9iEoQa5tQxAfQ/dfwMjOK7skogowBouOuLv
+BEFKy3Vd57IWWZXC4p/74M6N4fGYTgHY5FQE3R4Y2phk/eaEm1jS1UPuC98QuTfL
+rGuFOIBmK5euOm8uT5m9hnrouG2ZcxEdzHYfjsGDGrLzA0FLu+wtMNBKM4NhsNCa
+d+fycLg7jgxWhaLvD5DfkV7WFQlz5LUceYIwYOyhD/chAgMBAAGjLzAtMAwGA1Ud
+EwQFMAMBAf8wHQYDVR0RBBYwFIISbXRscy5odHRwYmluLmxvY2FsMA0GCSqGSIb3
+DQEBCwUAA4ICAQCNtBmoAc5tv3H38sj9qhTmabvp9RIzZYrQSEcN+A2i3a8FVYAM
+YaugZDXDcTycoWn6rcgblUDneow3NiqZ57yYZmN+e4mE3+Q1sGepV7LoRkHDUT8w
+jAJndcZ/xxJmgH6B7dImTAPsvLGR7E7gffMH+aKCdnkG9x5Vm+cuBwSEBndiHGfr
+yw5cXO6cMUq8M6zJrk2V+1BAucXW2rgLTWy6UTTGD56cgUtbStRO6muOKoElDLbW
+mSj2rNv/evakQkV8dgKVRFgh2NQKYKpXmveMaE6xtFFf/dd9OhDFjUh/ksxn94FT
+xj/wkhXCEPl+t7tENhr2tNyLbCOVcFzqoi7IyoWKxxZQfvArfj4SmahK8E/BXB/T
+4PEmn8kZAxaW7RmGcaekm8MTqGlhCJ3tVJAI2vcYRdd9ZHbXE1jr/4xj0I/Lzglo
+O8v5fd4zHyV1SuZ5AH3XbUd7ndl9yDoN2WSqK9Nd9bws3yrf+GwjJAT1InnDvLg1
+stWM8I+9FZiDFL255/+iAN0jYcGu9i4TNvC+o6qQ1p85i1OHPJZu6wtUWMgDJN46
+uwW3ZLh9sZV6OnhbQJBQaUmcgaPJUQqbXNQmpmpc0NUjET/ltFRZ2hlyvvpf7wwF
+2DLY1HRAknQ69DuT6xpYz1aKZqrlkbCWlMMvdosOg6f7+4NxdYJ/rBeS6Q==
+-----END CERTIFICATE-----
diff --git a/docs/en/latest/practices/mtls/client-ca-secret.yaml b/docs/en/latest/practices/mtls/client-ca-secret.yaml
new file mode 100644
index 0000000..fd119ef
--- /dev/null
+++ b/docs/en/latest/practices/mtls/client-ca-secret.yaml
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+data:
+  cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUY5ekNDQTkrZ0F3SUJBZ0lVRkt1ekFKWmdtL2ZzRlM2SkRyZCtsY3BWWnI4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dad3hDekFKQmdOVkJBWVRBa05PTVJFd0R3WURWUVFJREFoYWFHVnFhV0Z1WnpFUk1BOEdBMVVFQnd3SQpTR0Z1WjNwb2IzVXhHREFXQmdOVkJBb01EMEZRU1ZOSldDMVVaWE4wTFVOQlh6RVlNQllHQTFVRUN3d1BRVkJKClUwbFlYME5CWDFKUFQxUmZNUlV3RXdZRFZRUUREQXhCVUVsVFNWZ3VVazlQVkY4eEhEQWFCZ2txaGtpRzl3MEIKQ1FFV0RYUmxjM1JBZEdWemRDNWpiMjB3SGhjTk1qRXdOVEkzTVRNek5qSTRXaGNOTWpJd05USTNNVE16TmpJNApXakNCbkRFTE1Ba0dBMVVFQmhNQ1EwNHhFVEFQQmdOVkJBZ01DRnBvWldwcFlXNW5NUkV3RHdZRFZRUUhEQWhJCllXNW5lbWh2ZFRFWU1CWUdBMVVFQ2d3UFFWQkpVMGxZTFZSbGMzUXRRMEZmTVJnd0ZnWURWUVFMREE5QlVFbFQKU1ZoZlEwRmZVazlQVkY4eEZUQVRCZ05WQkFNTURFRlFTVk5KV0M1U1QwOVVYekVjTUJvR0NTcUdTSWIzRFFFSgpBUllOZEdWemRFQjBaWE4wTG1OdmJUQ0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCCkFMSlIwbFFXL0lCcVFURS9PYTBQaTRMbG1sWVVTR25xdEZOcWlaeU9GMFBqVnpOZXFvRDlKRFBpTTFRUnlDOHAKTkNkNUwvUWh0VUlNTXgwUmxESTlEa0ozQUxJV2RyUElabHdwdmVESmY0S3RXN2N6K2VhNDZBNlFRd0I2eGN5Vgp4V25xRUJraWVhN3FyRUU4TmFrWk9NamdrcWtOMi85a2xnNlh5QTVGV2Z2c3p4dHVJSHRqY3kyS3E4Yk1DMGpkCms3Q3FFWmU0Y3Q2czJ3bGNJOHQ4czlwcnZNRG04Z2NYNjZ4NEFoK0MyL1crQzNsVHBNRGdHcVJxU1B5Q1c3bmEKV2duMHRXbVRTZjFpeWJ3WU15ZGhDK3pwTTFRSkx2ZkR5cWpwMXdKaHppUjV0dFZlMlhjK3REQzI0cyt1MTZ5WgpSOTNJTzBNNGxMTmp2RUtKY01sdFh5UnpyY2p2TFhPaHczS2lyU0hOTDFLZnJCRWw3NGxiK0RWNWVVNHBJRkNqCmN1MThnbXM1RkJZczl0cEx1andwSERjMk1VK3pDdlJtU1B2VUE0eUN5b1hxb20zdWlTbzNnM3ltVzlJTThkQzgKK0JkMUdkTTZKYnBCdWt2UXliYzVUUVhvMU03NUk5aUVvUWE1dFF4QWZRL2Rmd01qT0s3c2tvZ293Qm91T3VMdgpCRUZLeTNWZDU3SVdXWlhDNHAvNzRNNk40ZkdZVGdIWTVGUUUzUjRZMnBoay9lYUVtMWpTMVVQdUM5OFF1VGZMCnJHdUZPSUJtSzVldU9tOHVUNW05aG5yb3VHMlpjeEVkekhZZmpzR0RHckx6QTBGTHUrd3RNTkJLTTROaHNOQ2EKZCtmeWNMZzdqZ3hXaGFMdkQ1RGZrVjdXRlFsejVMVWNlWUl3WU95aEQvY2hBZ01CQUFHakx6QXRNQXdHQTFVZApFd1FGTUFNQkFmOHdIUVlEVlIwUkJCWXdGSUlTYlhSc2N5NW9kSFJ3WW1sdUxteHZZMkZzTUEwR0NTcUdTSWIzCkRRRUJDd1VBQTRJQ0FRQ050Qm1vQWM1dHYzSDM4c2o5cWhUbWFidnA5Ukl6WllyUVNFY04rQTJpM2E4RlZZQU0KWWF1Z1pEWERjVHljb1duNnJjZ2JsVURuZW93M05pcVo1N3lZWm1OK2U0bUUzK1Exc0dlcFY3TG9Sa0hEVVQ4dwpqQUpuZGNaL3h4Sm1nSDZCN2RJbVRBUHN2TEdSN0U3Z2ZmTUgrYUtDZG5rRzl4NVZtK2N1QndTRUJuZGlIR2ZyCnl3NWNYTzZjTVVxOE02ekpyazJWKzFCQXVjWFcycmdMVFd5NlVUVEdENTZjZ1V0YlN0Uk82bXVPS29FbERMYlcKbVNqMnJOdi9ldmFrUWtWOGRnS1ZSRmdoMk5RS1lLcFhtdmVNYUU2eHRGRmYvZGQ5T2hERmpVaC9rc3huOTRGVAp4ai93a2hYQ0VQbCt0N3RFTmhyMnROeUxiQ09WY0Z6cW9pN0l5b1dLeHhaUWZ2QXJmajRTbWFoSzhFL0JYQi9UCjRQRW1uOGtaQXhhVzdSbUdjYWVrbThNVHFHbGhDSjN0VkpBSTJ2Y1lSZGQ5WkhiWEUxanIvNHhqMEkvTHpnbG8KTzh2NWZkNHpIeVYxU3VaNUFIM1hiVWQ3bmRsOXlEb04yV1NxSzlOZDlid3MzeXJmK0d3akpBVDFJbm5EdkxnMQpzdFdNOEkrOUZaaURGTDI1NS8raUFOMGpZY0d1OWk0VE52QytvNnFRMXA4NWkxT0hQSlp1Nnd0VVdNZ0RKTjQ2CnV3VzNaTGg5c1pWNk9uaGJRSkJRYVVtY2dhUEpVUXFiWE5RbXBtcGMwTlVqRVQvbHRGUloyaGx5dnZwZjd3d0YKMkRMWTFIUkFrblE2OUR1VDZ4cFl6MWFLWnFybGtiQ1dsTU12ZG9zT2c2ZjcrNE54ZFlKL3JCZVM2UT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
+kind: Secret
+metadata:
+  name: client-ca-secret
diff --git a/docs/en/latest/practices/mtls/mtls.yaml b/docs/en/latest/practices/mtls/mtls.yaml
new file mode 100644
index 0000000..da88150
--- /dev/null
+++ b/docs/en/latest/practices/mtls/mtls.yaml
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apisix.apache.org/v1
+kind: ApisixTls
+metadata:
+  name: sample-tls
+spec:
+  hosts:
+    - mtls.httpbin.local
+  secret:
+    name: server-secret
+    namespace: default
+  client:
+    caSecret:
+      name: client-ca-secret
+      namespace: default
+    depth: 10
diff --git a/docs/en/latest/practices/mtls/route.yaml b/docs/en/latest/practices/mtls/route.yaml
new file mode 100644
index 0000000..b50ff4f
--- /dev/null
+++ b/docs/en/latest/practices/mtls/route.yaml
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpserver-route
+spec:
+  http:
+    - name: httpbin
+      match:
+        hosts:
+          - mtls.httpbin.local
+        paths:
+          - "/*"
+      backend:
+        serviceName: httpbin
+        servicePort: 80
diff --git a/docs/en/latest/practices/mtls/server-secret.yaml b/docs/en/latest/practices/mtls/server-secret.yaml
new file mode 100644
index 0000000..bfbedd8
--- /dev/null
+++ b/docs/en/latest/practices/mtls/server-secret.yaml
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: v1
+data:
+  cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUYvVENDQStXZ0F3SUJBZ0lVQmJVUDdHazBXQWIvSmhZWWNCQmdaRWdtaGJFd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dad3hDekFKQmdOVkJBWVRBa05PTVJFd0R3WURWUVFJREFoYWFHVnFhV0Z1WnpFUk1BOEdBMVVFQnd3SQpTR0Z1WjNwb2IzVXhHREFXQmdOVkJBb01EMEZRU1ZOSldDMVVaWE4wTFVOQlh6RVlNQllHQTFVRUN3d1BRVkJKClUwbFlYME5CWDFKUFQxUmZNUlV3RXdZRFZRUUREQXhCVUVsVFNWZ3VVazlQVkY4eEhEQWFCZ2txaGtpRzl3MEIKQ1FFV0RYUmxjM1JBZEdWemRDNWpiMjB3SGhjTk1qRXdOVEkzTVRNek5qSTVXaGNOTWpJd05USTNNVE16TmpJNQpXakNCcFRFTE1Ba0dBMVVFQmhNQ1EwNHhFVEFQQmdOVkJBZ01DRnBvWldwcFlXNW5NUkV3RHdZRFZRUUhEQWhJCllXNW5lbWh2ZFRFY01Cb0dBMVVFQ2d3VFFWQkpVMGxZTFZSbGMzUXRVMlZ5ZG1WeVh6RVhNQlVHQTFVRUN3d08KUVZCSlUwbFlYMU5GVWxaRlVsOHhHekFaQmdOVkJBTU1FbTEwYkhNdWFIUjBjR0pwYmk1c2IyTmhiREVjTUJvRwpDU3FHU0liM0RRRUpBUllOZEdWemRFQjBaWE4wTG1OdmJUQ0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQCkFEQ0NBZ29DZ2dJQkFNWlJQRzhnVXJPR000YXduVjZEOERzMFhiNmpWYmlHa3grMVlzdlB4NW9JRTRBc3dKMGwKeTZ6cWhCRm5wUW96Rkc2M0tmc0NBNlUzNi9EdHkzckliSnpzYk83WWFPTUpJdG9pUWdxZHFGMm5ybVBwbXBDUQp1TEdLYVZ2cmlSQ0Q1NU5FbUZRUHNobFJmY1U1L0VFcmVOS2JSdmUzekVLSFJwQ0RCWjJNeXZycHQzQ0NWeTZECk1iTGxsYmpVdmFlZHJuUXhsbUk1ZDd4M1VDZTRFdW5xOHZuN2MwcDRmckExbjhUeGJYME00WXI5ZzNZRUVxQ3YKUTMvOWpVNGhJNUN2dWpDcCt1NzlFYXZKWmZzYUV2M1JZZ0hrb0VoN3ErT0VrVWFqV1hLajRXeW5penJhV3NIdgorTHZLOXBmSTMwMHAxSFNLSzRGcW9udlc3OWFuUk5iSys4QnFWNFd0NWFCZUZVL3JXMmpIdEp4Y2wxT0xScnJoCndmdENQNVc3dlNqdkplczV3UERaakRFeXY4V1AxQWE2eVdlR0hIdEl3ckFIUHI3NTU2Ri9KQVFTNklQQlFRNVUKWDQ1REQyYU5YTUU5eFpLZEJ0eU1vdkl0alptMzFVVXN2b0YrWXRwQU9tYkVrWDRsTXpuVU8zWFpKak01SFdTcQpXWXl6bUZzdytwSkV3aFhSbzRHZlNmQ0hmaVpRNmltVExKN09zWnpvOWJ2bXh5Zkkwa1ZMZTNoM2lDZStxWWVUCmY1QUpVNnY1dnYzdGhDdGZnYnhZUDJQOGIrME1JcmZyMDVlNmRDRFhiSXJpMXorbnByeldZbXlDclo2SDRoVmsKRHpNa3RGVWxrcWVudm5zSjJpT1YyQVp3MEhsazJid2U0elN1bXpxb0lwOFlrL2t4YmZ4aFFxcjVBZ01CQUFHagpMREFxTUFrR0ExVWRFd1FDTUFBd0hRWURWUjBSQkJZd0ZJSVNiWFJzY3k1b2RIUndZbWx1TG14dlkyRnNNQTBHCkNTcUdTSWIzRFFFQkN3VUFBNElDQVFDRERmRVRDRXBXQi9LUlFabzJKRjhuNE5FRFRlcmFRODVNM0g1bHVKSHAKTmRKTzRvWXEzbjhCMTQ5ZXA0RmNFWWRPMjBwVitUTWVNTldYTWZob1JJcEd4OTVKckx1TGc2cW53NmVOZEVybgpZdXBITUMyT0VvRVdWY21JMDUyTERKY1h1S3NUWFF2VTRPZUVMMmRYNE90TkorbVJPREx5aDQwY2c3ZEEzd3J5CmtHTGlwclJsTFF0aVg4cFNERzMwcVBaZXhMMUxjRnpCUWFqcmlHMDVRVXJKVzZSdmJxMUpUSWx5cDdFMVQ4NmYKWGxqcTBIZHpxeHkrRmtsWWNBVzVaQXhna1FsTW1WZFRsdkRYbEQvaFFMRVFJSEdIaVc2T01McDhXcm5KUDZiMApEMkhxV21Pd3VFenFTZ1hTSzBOODlycGlXUDFGS0NweWlLVmNzYXdETmZPcGVQVnV0aG9tbVZFYzJQeGFjeUhmClVDQzlWME1TMFp6UTYzVG56MlRqYThDNi9rTXlWWDIyNktRS2hjb0R4RG9TMG1Rckk5Ni9WWGNnbHdQNWhNakYKam90aDFUMXFSVnU2K05RbXZGUGFOamJ6V0orajFSOTlibllHaWhQZUxkcURTVXhOb3NWM1VMRzhUNGFONitmOApoQXBpcWcyZGtMSlFyOHpXZjZ2V1hNbFJFZFBFb3ZiMkY3UDBMZm4wVmVPU1JYRFVJZHFjb1JIT05pOGJXTVJzCmZqUHRHVzAwVHY4SmcyMWM5dmM4WmgvdDF3M3drWFFocVlpQk10NWNZZTZXdWVJbFhkakY3aWtTUldBSFR3bHcKQmZ6di92TWZ0TG5ieVNQb3ZDelExUEY1NUQwMUVXUmswbzZQUndVRExmelRRb1YrYkRLeDgyTHhLdFpCdFFFWAp1dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
+  key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBeGxFOGJ5QlNzNFl6aHJDZFhvUHdPelJkdnFOVnVJYVRIN1ZpeTgvSG1nZ1RnQ3pBCm5TWExyT3FFRVdlbENqTVVicmNwK3dJRHBUZnI4TzNMZXNoc25PeHM3dGhvNHdraTJpSkNDcDJvWGFldVkrbWEKa0pDNHNZcHBXK3VKRUlQbmswU1lWQSt5R1ZGOXhUbjhRU3Q0MHB0Rzk3Zk1Rb2RHa0lNRm5ZeksrdW0zY0lKWApMb014c3VXVnVOUzlwNTJ1ZERHV1lqbDN2SGRRSjdnUzZlcnkrZnR6U25oK3NEV2Z4UEZ0ZlF6aGl2MkRkZ1FTCm9LOURmLzJOVGlFamtLKzZNS242N3YwUnE4bGwreG9TL2RGaUFlU2dTSHVyNDRTUlJxTlpjcVBoYktlTE90cGEKd2UvNHU4cjJsOGpmVFNuVWRJb3JnV3FpZTlidjFxZEUxc3I3d0dwWGhhM2xvRjRWVCt0YmFNZTBuRnlYVTR0Rwp1dUhCKzBJL2xidTlLTzhsNnpuQThObU1NVEsveFkvVUJyckpaNFljZTBqQ3NBYyt2dm5ub1g4a0JCTG9nOEZCCkRsUmZqa01QWm8xY3dUM0ZrcDBHM0l5aThpMk5tYmZWUlN5K2dYNWkya0E2WnNTUmZpVXpPZFE3ZGRrbU16a2QKWktwWmpMT1lXekQ2a2tUQ0ZkR2pnWjlKOElkK0psRHFLWk1zbnM2eG5PajF1K2JISjhqU1JVdDdlSGVJSjc2cApoNU4va0FsVHEvbSsvZTJFSzErQnZGZy9ZL3h2N1F3aXQrdlRsN3AwSU5kc2l1TFhQNmVtdk5aaWJJS3Rub2ZpCkZXUVBNeVMwVlNXU3A2ZStld25hSTVYWUJuRFFlV1RadkI3ak5LNmJPcWdpbnhpVCtURnQvR0ZDcXZrQ0F3RUEKQVFLQ0FnQlA2dWk1dDRMY1NaWjJEckk4SmxzbTRLRnVjNC9WdnBXSFQ2Y3lqdGJXNGE1S0ZyN0FGVDBRdjZqZApBckZsZk5RZEViN2ZJaDZwOC9FbXRBMHR1NXJaV2dWRDh2M0JrQ3IxVUp6Z2Zrd2RBYmVyRjdacno0WStOWkxqCnNmVVlMSytqang3N3NSK0tTR2F3bGY5cm04TWl5K1E3YTF2cTYyeXFTOEoxalFrM04vdnVZUGdWREZWNHpFQWIKcmMrSHZtbFE5Ykt1Zm80YjZ0RG9VS3QrakduQ0IyeWNkQlpKbURKOFFQWm9VRXFMb2tIWnl5WmVqb0piRDZoago5Y0xKU2FkMGVPdGdaNmM1WFAyMXhQb21RcnlHR3NYa3I4SEMrK2MzV2hodnRFN2haRnNkS21Vc2hqSHNLNHhYCittRFNUYXNLRTZ3WWlRcFZjWFpSUURMamhBVVMvWXJvMmY0WkZxUW1BVWtzekxDS3FsMEJOWFlzUkdaMDNHdlgKS1krS2ROME1VQkpTVGVKdXV0OStFUkZ4dEJFYThtN1dKam5xTGNqRE04N1BDWWpla3ZnbitCQTUxVTZoTTRkRwpGSmtTZDhUeHh1Z1crZit1em5GbmJ2QkVRNmZvakRMaFhLbGlScnJiV09aUy9scDdObitwTTRUbks1K3F1UUIwCnNTWThMTkQ5MWtrMUhFV2U0RW9jTWhVTTZDcFgxU3QxenJRYkxxNW5veiswMzZuL1ZUL3RZbHJyOUdMaFJNSU4KS0VXbHllUE5TY2VqT2ZYMk8zaWkwSk9JR1NJUWFQd29JYTNycnM1TXBOMEx2dlNOdW9LbDFVcXhYWXhXMy83cgpoVHdRblVMVlRwRHg2QjZYMlp2d2JmN1c4djlOS240Qmp2cXJTMVVJMjA5cVJoL3ZJUUtDQVFFQTZqYjlpc0dTClM1dWEwbjkybm1KemRaUElieTNaZEVhSnV3cVlZUVdDTFEwWmp5MFlZVjBlQW1XWEtxKzFWdGVOYmR4K0NYZWEKdzRIZUhxc2NuS3hsVEZ6OXNiS0YzNEJNaUs1Uk5UWHpIK09za3NJWHB0N3dISnlOczdvWDRNUENlY3pnRnhvQwpxd1lLOVNJYVpZVjc2OHkyVExSaVMvVFdORXAram1Bbkd3MTJValROcTNXTEtMRzd2aEc3U0kzcmgwTHRsR3lOCkV6R0dxMlQ3blBsM29wRXNlMGp0bWJwSmhMN1JYSnVnVHNIbU5Db0VCQitKZk5YR1FFbHdQV0czVGdOQkdIQm0KNTgweHViL0pFR3FkZkptTFp0dEQ3UGFhK2NuRlVYU1RIR21pQy9yOUU3anVNaWUybm9OaVovSmhxckpvM1Z2eApzTy9tUml1S2lBeWtiUUtDQVFFQTJNTjQ2UGpMQWJ1WW42bUFUaVI0eVNsajR0ckV2OVJXa29DbzJwK1N0V0pYClNZcWRLZk9ySU53M3FBeThnWTlDNGoyeUxBcXlQYVFybGhDZW9HLzdHSm4xSk5KdEIyNG1nZnFoQnFpV2krMHEKcHBXRDg1bnViU1JuT2dYdjNJWTJHOVgrK1JSTjE4WS9yaEJGVTZJREpVcHlaNDJHNC9DR2tTL0I1NlkyVXdIUQphc3VETGtybEpmS0xoMm9tZU1SdE9Ia0hJV29NbFFjbmQ2aVNJcTdwams3TDhCSDNhQWlSMXB6Y2g2dGNzYThrCndrd1BGbWZHb2ZkWEU1aGQvU3dXM3REN1g1OHJLbjl5RWJaVElzNjR5K0JQSm9iKys0eFVDamFLNXlQSUNDckYKOE1PUEI4NThUQW03Y245VEZnS1pwdjlkbVVLdzFoVktMOVBLUVgxUlBRS0NBUUVBNHpsNFh3Nk8vTlU0cmZGRgpSa0dqWERXRXBnQW9VSHRDa2Zpa2ZyUVdaOWltckZZR3FpYnB2MCtLQ2JxdnhsR1cvemVEKzNGUzcwdm1ENERZCllGT01ienBrVWVvdG9QamF4MXUrbzAzMDBrSlNvWXExNFltMkR6dis2WmVvSk1JbXdYMzNCZEtSTmhURnVxNWMKUjVQcDlva0RiNFV0UEIyTFZ1M1N2QlFpdkVjaVBIekg4QWs0ZWNGOHI5aUtCc2pROE1nSXNBOWtDblBwQUEwWApZbUpRSTZLT01nazlvZit0NWFBdWc1YmtQcVEwenZUWU1wdmFDZ2RucitUUGhHMXhwYmpZaFhvL0M3SHlCUkJBClk3SGJtZzlvdytBRGxUaG1mK0cxa2VIeit3T3NWODBuaStQRkMxbWwvVURmenBMREdCVEFVY2txd1FydEw3UjgKVUtOYlBRS0NBUUJFK1g1aDg3ajFaakpjcTkwT0FJRUcwY3JkQnV3UWRvck50MjhEa2o5bXhGSXVMcE53SS85UwpSNERXVXFjeE90cjNqdFpCT1c0YU8wRTdVVEtJcnRsaHJLdmErYktENk1NTUhTcGNLZzB0blZ3ekFlU3BBVlJqCkduQldnRWtoRFB2dXc1dU11cTlDZCswUGdGSHZHT0NUWHlza1ZGNlY3WldFWVlQOEtHR2s3RERicXNLbFdtT3MKUFkrMG1VeUFwVkJ6NWQ4ay9NL2dKQlNrK05qM2ZGMEpVWDJIZU5BWEpKTHpqWnFHK1RwWHQvbWtjZnRqRDhhZgpCMHVJQ3JYdHQ3ZlhVdnlLSXVYamNnWmtLSFl2MzBQaWJCQURuSFZLcWc2YjZWc3R6YTc3R2xFK0daeEx5YUszCnQya1VOL3ZDUnpXSmREemVaZUJMWHg3cU5TUm96bTJwQW9JQkFHeGVxaWQzczM2UVkzeHJ1ZlE1VzNNY3RCWHkKRHRmZkgxbHREdEFhSWhFa0ovaWFaTks1RUhWY2FXQXBpTDhxVzdFak9WT0FvZ2xhSlh0VDcvcXk3QVNkNDJOSAozcTUwZ1R3TUY0dzBja0o1VlRnWXFGeEFvU3grdGxBaGRiQndrMGtMVWl4L3RDSzJFdURUVGZGd05obVZKbEJ1CjZVZkJzLzlscGJvV1FSMWdzZU52d3JVVUIyN2gyNmR3SkpUZVFXQ1JZa0EvSWc0dHRjLzc5cUVuOHhWNFA0VGsKdzE3NFJTUW9OTWMrb2RIeG45NW14dFlkWVZFNVBLa3pncmZ4cXltTGE1WTBMTVBDcEtPcTRYQjBwYVpQdHJPdAprMVhib2dTNkVZeUVkYmtURGRYZFVFTnZEclU3aHpKWFNWeEpZQURpcXI0NERHZldtNmhLMGJxOVpQYz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K
+kind: Secret
+metadata:
+  name: server-secret
diff --git a/docs/en/latest/practices/mtls/server.key b/docs/en/latest/practices/mtls/server.key
new file mode 100644
index 0000000..6c0e3d4
--- /dev/null
+++ b/docs/en/latest/practices/mtls/server.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/docs/en/latest/practices/mtls/server.pem b/docs/en/latest/practices/mtls/server.pem
new file mode 100644
index 0000000..b253f81
--- /dev/null
+++ b/docs/en/latest/practices/mtls/server.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIF/TCCA+WgAwIBAgIUBbUP7Gk0WAb/JhYYcBBgZEgmhbEwDQYJKoZIhvcNAQEL
+BQAwgZwxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI
+SGFuZ3pob3UxGDAWBgNVBAoMD0FQSVNJWC1UZXN0LUNBXzEYMBYGA1UECwwPQVBJ
+U0lYX0NBX1JPT1RfMRUwEwYDVQQDDAxBUElTSVguUk9PVF8xHDAaBgkqhkiG9w0B
+CQEWDXRlc3RAdGVzdC5jb20wHhcNMjEwNTI3MTMzNjI5WhcNMjIwNTI3MTMzNjI5
+WjCBpTELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI
+YW5nemhvdTEcMBoGA1UECgwTQVBJU0lYLVRlc3QtU2VydmVyXzEXMBUGA1UECwwO
+QVBJU0lYX1NFUlZFUl8xGzAZBgNVBAMMEm10bHMuaHR0cGJpbi5sb2NhbDEcMBoG
+CSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAMZRPG8gUrOGM4awnV6D8Ds0Xb6jVbiGkx+1YsvPx5oIE4AswJ0l
+y6zqhBFnpQozFG63KfsCA6U36/Dty3rIbJzsbO7YaOMJItoiQgqdqF2nrmPpmpCQ
+uLGKaVvriRCD55NEmFQPshlRfcU5/EEreNKbRve3zEKHRpCDBZ2Myvrpt3CCVy6D
+MbLllbjUvaedrnQxlmI5d7x3UCe4Eunq8vn7c0p4frA1n8TxbX0M4Yr9g3YEEqCv
+Q3/9jU4hI5CvujCp+u79EavJZfsaEv3RYgHkoEh7q+OEkUajWXKj4WynizraWsHv
++LvK9pfI300p1HSKK4FqonvW79anRNbK+8BqV4Wt5aBeFU/rW2jHtJxcl1OLRrrh
+wftCP5W7vSjvJes5wPDZjDEyv8WP1Aa6yWeGHHtIwrAHPr7556F/JAQS6IPBQQ5U
+X45DD2aNXME9xZKdBtyMovItjZm31UUsvoF+YtpAOmbEkX4lMznUO3XZJjM5HWSq
+WYyzmFsw+pJEwhXRo4GfSfCHfiZQ6imTLJ7OsZzo9bvmxyfI0kVLe3h3iCe+qYeT
+f5AJU6v5vv3thCtfgbxYP2P8b+0MIrfr05e6dCDXbIri1z+nprzWYmyCrZ6H4hVk
+DzMktFUlkqenvnsJ2iOV2AZw0Hlk2bwe4zSumzqoIp8Yk/kxbfxhQqr5AgMBAAGj
+LDAqMAkGA1UdEwQCMAAwHQYDVR0RBBYwFIISbXRscy5odHRwYmluLmxvY2FsMA0G
+CSqGSIb3DQEBCwUAA4ICAQCDDfETCEpWB/KRQZo2JF8n4NEDTeraQ85M3H5luJHp
+NdJO4oYq3n8B149ep4FcEYdO20pV+TMeMNWXMfhoRIpGx95JrLuLg6qnw6eNdErn
+YupHMC2OEoEWVcmI052LDJcXuKsTXQvU4OeEL2dX4OtNJ+mRODLyh40cg7dA3wry
+kGLiprRlLQtiX8pSDG30qPZexL1LcFzBQajriG05QUrJW6Rvbq1JTIlyp7E1T86f
+Xljq0Hdzqxy+FklYcAW5ZAxgkQlMmVdTlvDXlD/hQLEQIHGHiW6OMLp8WrnJP6b0
+D2HqWmOwuEzqSgXSK0N89rpiWP1FKCpyiKVcsawDNfOpePVuthommVEc2PxacyHf
+UCC9V0MS0ZzQ63Tnz2Tja8C6/kMyVX226KQKhcoDxDoS0mQrI96/VXcglwP5hMjF
+joth1T1qRVu6+NQmvFPaNjbzWJ+j1R99bnYGihPeLdqDSUxNosV3ULG8T4aN6+f8
+hApiqg2dkLJQr8zWf6vWXMlREdPEovb2F7P0Lfn0VeOSRXDUIdqcoRHONi8bWMRs
+fjPtGW00Tv8Jg21c9vc8Zh/t1w3wkXQhqYiBMt5cYe6WueIlXdjF7ikSRWAHTwlw
+Bfzv/vMftLnbySPovCzQ1PF55D01EWRk0o6PRwUDLfzTQoV+bDKx82LxKtZBtQEX
+uw==
+-----END CERTIFICATE-----
diff --git a/docs/en/latest/practices/mtls/tls.yaml b/docs/en/latest/practices/mtls/tls.yaml
new file mode 100644
index 0000000..bda748a
--- /dev/null
+++ b/docs/en/latest/practices/mtls/tls.yaml
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apisix.apache.org/v1
+kind: ApisixTls
+metadata:
+  name: sample-tls
+spec:
+  hosts:
+    - mtls.httpbin.local
+  secret:
+    name: server-secret
+    namespace: default
diff --git a/docs/en/latest/practices/mtls/user.key b/docs/en/latest/practices/mtls/user.key
new file mode 100644
index 0000000..5e0feba
--- /dev/null
+++ b/docs/en/latest/practices/mtls/user.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/docs/en/latest/practices/mtls/user.pem b/docs/en/latest/practices/mtls/user.pem
new file mode 100644
index 0000000..a6b3746
--- /dev/null
+++ b/docs/en/latest/practices/mtls/user.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGDDCCA/SgAwIBAgIUBbUP7Gk0WAb/JhYYcBBgZEgmhbIwDQYJKoZIhvcNAQEL
+BQAwgZwxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI
+SGFuZ3pob3UxGDAWBgNVBAoMD0FQSVNJWC1UZXN0LUNBXzEYMBYGA1UECwwPQVBJ
+U0lYX0NBX1JPT1RfMRUwEwYDVQQDDAxBUElTSVguUk9PVF8xHDAaBgkqhkiG9w0B
+CQEWDXRlc3RAdGVzdC5jb20wHhcNMjEwNTI3MTMzNjMxWhcNMjIwNTI3MTMzNjMx
+WjCBtDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI
+YW5nemhvdTEfMB0GA1UECgwWQVBJU0lYLVRlc3QtUm9vdC1Vc2VyXzEaMBgGA1UE
+CwwRQVBJU0lYX1JPT1RfVVNFUl8xJDAiBgNVBAMMG0JpZ01pbmdfIDY1NDMxMTEx
+MTExMTExMTExMTEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL41i+W8fgqtKButnkOa0qGApPJ7HSUI
+gxBt6Tb7mR8eka8vno8hIE42hwMXYoIiO1FgM9M9fPN8vzTnWr2iTAXbw/qXdn14
+RT01JrGzj1OU3M9U0RMmDWVs8VOY3ncdcdPoqctMNr2K+6wlN6EpG1DQDUEvB0Yy
+qZpFWpuZP0akzjFqeJNSo3b8hP4J1nsWBxKt6TRInC60jT1mhKCjTB1IsXVC+5SY
+f0FhMN2ZQPgCyIIq+j5S7tHta4Qs+UDNY0whIMBkY3ZAGPzUBFExwrX7bGNbeJYz
+DahNAKiEj5dlieRvzTn5UisSv5EzDUONLSluEnCU1Ghd/dfZtZwlMBU6OIflwm6W
+X7HePD0G8XEDe5qbU/tg/62j+A1hMS2gSlibNisyvNwMNz6Imc3CnIFUL2mTsgl1
+bCBLfOXDfrjte5Txnt26ayN0+WhmVSts0+ln6vnGtgVGqH0bmp95yMRYPej+6ARE
+MtfvrNA7iZORypspv2NVyV11b6Jx3o/pJOAq9dmAsKvJ6hK0IVf/55t3ZtzRa/fY
+w0f78KbWuEc7qmx2tBxnEfppb5iB/RFmI2bDaAEA0lDQgAqmiAu1utetfqsCwA1m
+3VXGp8Vp+lexA/M3Bkb5BHa6C11QDHvGjUVwduOymLAHKumvdgf7TJFnlhpg6jYN
+unO58bRSTI6LAgMBAAGjLDAqMAkGA1UdEwQCMAAwHQYDVR0RBBYwFIISbXRscy5o
+dHRwYmluLmxvY2FsMA0GCSqGSIb3DQEBCwUAA4ICAQCoTBvw11aKah2cuB4XplUB
+nmkrWhfLNFJLVrU9jIISP9Wkl3s3PcM+aEWygb/1roqbMqNOgrzDVgGRRFCiS6qi
+himUuTJhIBI6TF1PE+XW3gTFWBXkAZ7MzpbS8oP1PehlY3XXKNZgxZi3XaDI8Hfw
+5MWBGNbk8tegn8bvYQUz2VxmCo6zufCkj4ADjw2zhiyKBKuHTzg48w66Wn4jLhlK
+p91HHrK0lEOIJ4pFmBUpBsSJMlBMbfrzBF87xQhpDO3ScFfCWUatShmXsPMJU0F0
+DEuTnaHUefUf/F9wUGNcA4yQ4pH8SxVpRHmrWE8U4uSXpz1bx4ChZurJ9mPzrj9h
+U9c/d9F5WndZNPcR1R8Tbzhk/R8GImVR3Lt59cW+SN/+4JVFy+Hye2yslGFn2CAJ
+ofNxjLb9OE6+EE3SWW0B5CZSWBS49gtdTW0ApOjIRJU2zipxcjnNf00GFoIoCxjk
+Z4eBQz9WVUM9KSrJIQSLZQd35tZAOp0BuwWho0+w8lXUchSqT7oRA7+szZldWF0j
+HKPIMJ0iVWmXuZjsS8q8NBIt4DuBcqpevlol5KRXv6tJy4IBVAVEIBdeXotvdxKE
+bncvZ6xo9A/waUU7tEyzv34usxefrWxtSlOA1G0Jj4nb5gKPHjn0XIr9WI2RpovT
+/XpB6QES1zoBQya3QjnDbQ==
+-----END CERTIFICATE-----
diff --git a/pkg/apisix/ssl.go b/pkg/apisix/ssl.go
index 16a9f7c..257117b 100644
--- a/pkg/apisix/ssl.go
+++ b/pkg/apisix/ssl.go
@@ -141,13 +141,7 @@
 	if err := s.cluster.HasSynced(ctx); err != nil {
 		return nil, err
 	}
-	data, err := json.Marshal(v1.Ssl{
-		ID:     obj.ID,
-		Snis:   obj.Snis,
-		Cert:   obj.Cert,
-		Key:    obj.Key,
-		Status: obj.Status,
-	})
+	data, err := json.Marshal(obj)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/ingress/apisix_tls.go b/pkg/ingress/apisix_tls.go
index 643d37b..2ba036d 100644
--- a/pkg/ingress/apisix_tls.go
+++ b/pkg/ingress/apisix_tls.go
@@ -123,13 +123,19 @@
 		c.controller.recordStatus(tls, _resourceSyncAborted, err, metav1.ConditionFalse)
 		return err
 	}
-	log.Debug("got SSL object from ApisixTls",
+	log.Debugw("got SSL object from ApisixTls",
 		zap.Any("ssl", ssl),
 		zap.Any("ApisixTls", tls),
 	)
 
 	secretKey := tls.Spec.Secret.Namespace + "_" + tls.Spec.Secret.Name
-	c.syncSecretSSL(secretKey, ssl, ev.Type)
+	c.syncSecretSSL(secretKey, key, ssl, ev.Type)
+	if tls.Spec.Client != nil {
+		caSecretKey := tls.Spec.Client.CASecret.Namespace + "_" + tls.Spec.Client.CASecret.Name
+		if caSecretKey != secretKey {
+			c.syncSecretSSL(caSecretKey, key, ssl, ev.Type)
+		}
+	}
 
 	if err := c.controller.syncSSL(ctx, ssl, ev.Type); err != nil {
 		log.Errorw("failed to sync SSL to APISIX",
@@ -146,21 +152,21 @@
 	return err
 }
 
-func (c *apisixTlsController) syncSecretSSL(key string, ssl *v1.Ssl, event types.EventType) {
-	if ssls, ok := c.controller.secretSSLMap.Load(key); ok {
+func (c *apisixTlsController) syncSecretSSL(secretKey string, apisixTlsKey string, ssl *v1.Ssl, event types.EventType) {
+	if ssls, ok := c.controller.secretSSLMap.Load(secretKey); ok {
 		sslMap := ssls.(*sync.Map)
 		switch event {
 		case types.EventDelete:
-			sslMap.Delete(ssl.ID)
-			c.controller.secretSSLMap.Store(key, sslMap)
+			sslMap.Delete(apisixTlsKey)
+			c.controller.secretSSLMap.Store(secretKey, sslMap)
 		default:
-			sslMap.Store(ssl.ID, ssl)
-			c.controller.secretSSLMap.Store(key, sslMap)
+			sslMap.Store(apisixTlsKey, ssl)
+			c.controller.secretSSLMap.Store(secretKey, sslMap)
 		}
 	} else if event != types.EventDelete {
 		sslMap := new(sync.Map)
-		sslMap.Store(ssl.ID, ssl)
-		c.controller.secretSSLMap.Store(key, sslMap)
+		sslMap.Store(apisixTlsKey, ssl)
+		c.controller.secretSSLMap.Store(secretKey, sslMap)
 	}
 }
 
diff --git a/pkg/ingress/controller.go b/pkg/ingress/controller.go
index 4568155..04ecfe1 100644
--- a/pkg/ingress/controller.go
+++ b/pkg/ingress/controller.go
@@ -246,6 +246,11 @@
 	}
 }
 
+// recorderEvent recorder events for resources
+func (c *Controller) recorderEventS(object runtime.Object, eventtype, reason string, msg string) {
+	c.recorder.Event(object, eventtype, reason, msg)
+}
+
 func (c *Controller) goAttach(handler func()) {
 	c.wg.Add(1)
 	go func() {
diff --git a/pkg/ingress/secret.go b/pkg/ingress/secret.go
index 8b3aec2..2d8e763 100644
--- a/pkg/ingress/secret.go
+++ b/pkg/ingress/secret.go
@@ -17,12 +17,14 @@
 
 import (
 	"context"
+	"fmt"
 	"sync"
 	"time"
 
 	"go.uber.org/zap"
 	corev1 "k8s.io/api/core/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/util/workqueue"
 
@@ -130,21 +132,62 @@
 		// This secret is not concerned.
 		return nil
 	}
-	cert, ok := sec.Data["cert"]
-	if !ok {
-		return translation.ErrEmptyCert
-	}
-	pkey, ok := sec.Data["key"]
-	if !ok {
-		return translation.ErrEmptyPrivKey
-	}
 	sslMap := ssls.(*sync.Map)
-	sslMap.Range(func(_, v interface{}) bool {
+	sslMap.Range(func(k, v interface{}) bool {
 		ssl := v.(*apisixv1.Ssl)
-		// sync ssl
-		ssl.Cert = string(cert)
-		ssl.Key = string(pkey)
-
+		tlsMetaKey := k.(string)
+		tlsNamespace, tlsName, err := cache.SplitMetaNamespaceKey(tlsMetaKey)
+		if err != nil {
+			log.Errorf("invalid cached ApisixTls key: %s", tlsMetaKey)
+			return true
+		}
+		tls, err := c.controller.apisixTlsLister.ApisixTlses(tlsNamespace).Get(tlsName)
+		if err != nil {
+			log.Warnw("secret related ApisixTls resource not found, skip",
+				zap.String("ApisixTls", tlsMetaKey),
+			)
+			return true
+		}
+		if tls.Spec.Secret.Namespace == sec.Namespace && tls.Spec.Secret.Name == sec.Name {
+			cert, ok := sec.Data["cert"]
+			if !ok {
+				log.Warnw("secret required by ApisixTls invalid",
+					zap.String("ApisixTls", tlsMetaKey),
+					zap.Error(translation.ErrEmptyCert),
+				)
+				return true
+			}
+			pkey, ok := sec.Data["key"]
+			if !ok {
+				log.Warnw("secret required by ApisixTls invalid",
+					zap.String("ApisixTls", tlsMetaKey),
+					zap.Error(translation.ErrEmptyPrivKey),
+				)
+				return true
+			}
+			// sync ssl
+			ssl.Cert = string(cert)
+			ssl.Key = string(pkey)
+		} else if tls.Spec.Client != nil &&
+			tls.Spec.Client.CASecret.Namespace == sec.Namespace && tls.Spec.Client.CASecret.Name == sec.Name {
+			ca, ok := sec.Data["cert"]
+			if !ok {
+				log.Warnw("secret required by ApisixTls invalid",
+					zap.String("resource", tlsMetaKey),
+					zap.Error(translation.ErrEmptyCert),
+				)
+				return true
+			}
+			ssl.Client = &apisixv1.MutualTLSClientConfig{
+				CA: string(ca),
+			}
+		} else {
+			log.Warnw("stale secret cache, ApisixTls doesn't requires target secret",
+				zap.String("ApisixTls", tlsMetaKey),
+				zap.String("secret", key),
+			)
+			return true
+		}
 		// Use another goroutine to send requests, to avoid
 		// long time lock occupying.
 		go func(ssl *apisixv1.Ssl) {
@@ -155,6 +198,13 @@
 					zap.Any("ssl", ssl),
 					zap.Any("secret", sec),
 				)
+				c.controller.recorderEventS(tls, corev1.EventTypeWarning, _resourceSyncAborted,
+					fmt.Sprintf("sync from secret %s changes failed, error: %s", key, err.Error()))
+				c.controller.recordStatus(tls, _resourceSyncAborted, err, metav1.ConditionFalse)
+			} else {
+				c.controller.recorderEventS(tls, corev1.EventTypeNormal, _resourceSynced,
+					fmt.Sprintf("sync from secret %s changes", key))
+				c.controller.recordStatus(tls, _resourceSynced, nil, metav1.ConditionTrue)
 			}
 		}(ssl)
 		return true
diff --git a/pkg/kube/apisix/apis/config/v1/types.go b/pkg/kube/apisix/apis/config/v1/types.go
index 72a4e2c..a8b9181 100644
--- a/pkg/kube/apisix/apis/config/v1/types.go
+++ b/pkg/kube/apisix/apis/config/v1/types.go
@@ -29,6 +29,8 @@
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 // ApisixRoute is used to define the route rules and upstreams for Apache APISIX.
 // The definition closes the Kubernetes Ingress resource.
+// +kubebuilder:resource:shortName=ar
+// +kubebuilder:pruning:PreserveUnknownFields
 type ApisixRoute struct {
 	metav1.TypeMeta   `json:",inline" yaml:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
@@ -268,30 +270,58 @@
 
 // +genclient
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +kubebuilder:resource:shortName=atls
 // +kubebuilder:subresource:status
 // ApisixTls defines SSL resource in APISIX.
 type ApisixTls struct {
 	metav1.TypeMeta   `json:",inline" yaml:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
-	Spec              *ApisixTlsSpec        `json:"spec,omitempty" yaml:"spec,omitempty"`
-	Status            v2alpha1.ApisixStatus `json:"status,omitempty" yaml:"status,omitempty"`
+	Spec              *ApisixTlsSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
+	// +optional
+	Status v2alpha1.ApisixStatus `json:"status,omitempty" yaml:"status,omitempty"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +kubebuilder:printcolumn:name="SNIs",type=string,JSONPath=`.spec.hosts`
+// +kubebuilder:printcolumn:name="Secret Name",type=string,JSONPath=`.spec.secret.name`
+// +kubebuilder:printcolumn:name="Secret Namespace",type=string,JSONPath=`.spec.secret.namespace`
+// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
+// +kubebuilder:printcolumn:name="Client CA Secret Name",type=string,JSONPath=`.spec.client.ca.name`
+// +kubebuilder:printcolumn:name="Client CA Secret Namespace",type=string,JSONPath=`.spec.client.ca.namespace`
 type ApisixTlsList struct {
 	metav1.TypeMeta `json:",inline" yaml:",inline"`
 	metav1.ListMeta `json:"metadata" yaml:"metadata"`
 	Items           []ApisixTls `json:"items,omitempty" yaml:"items,omitempty"`
 }
 
+// +kubebuilder:validation:Pattern="^\\*?[0-9a-zA-Z-.]+$"
+type HostType string
+
 // ApisixTlsSpec is the specification of ApisixSSL.
 type ApisixTlsSpec struct {
-	Hosts  []string     `json:"hosts,omitempty" yaml:"hosts,omitempty"`
-	Secret ApisixSecret `json:"secret,omitempty" yaml:"secret,omitempty"`
+	// +required
+	// +kubebuilder:validation:Required
+	// +kubebuilder:validation:MinItems=1
+	Hosts []HostType `json:"hosts" yaml:"hosts,omitempty"`
+	// +required
+	// +kubebuilder:validation:Required
+	Secret ApisixSecret `json:"secret" yaml:"secret"`
+	// +optional
+	Client *ApisixMutualTlsClientConfig `json:"client,omitempty" yaml:"client,omitempty"`
 }
 
 // ApisixSecret describes the Kubernetes Secret name and namespace.
 type ApisixSecret struct {
-	Name      string `json:"name,omitempty" yaml:"name,omitempty"`
-	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
+	// +kubebuilder:validation:MinLength=1
+	// +kubebuilder:validation:Required
+	Name string `json:"name" yaml:"name"`
+	// +kubebuilder:validation:MinLength=1
+	// +kubebuilder:validation:Required
+	Namespace string `json:"namespace" yaml:"namespace"`
+}
+
+// ApisixMutualTlsClientConfig describes the mutual TLS CA and verify depth
+type ApisixMutualTlsClientConfig struct {
+	CASecret ApisixSecret `json:"caSecret,omitempty" yaml:"caSecret,omitempty"`
+	Depth    int          `json:"depth,omitempty" yaml:"depth,omitempty"`
 }
diff --git a/pkg/kube/apisix/apis/config/v1/zz_generated.deepcopy.go b/pkg/kube/apisix/apis/config/v1/zz_generated.deepcopy.go
index 1927a84..c0f4d32 100644
--- a/pkg/kube/apisix/apis/config/v1/zz_generated.deepcopy.go
+++ b/pkg/kube/apisix/apis/config/v1/zz_generated.deepcopy.go
@@ -96,6 +96,23 @@
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixMutualTlsClientConfig) DeepCopyInto(out *ApisixMutualTlsClientConfig) {
+	*out = *in
+	out.CASecret = in.CASecret
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixMutualTlsClientConfig.
+func (in *ApisixMutualTlsClientConfig) DeepCopy() *ApisixMutualTlsClientConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixMutualTlsClientConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ApisixRoute) DeepCopyInto(out *ApisixRoute) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
@@ -268,10 +285,15 @@
 	*out = *in
 	if in.Hosts != nil {
 		in, out := &in.Hosts, &out.Hosts
-		*out = make([]string, len(*in))
+		*out = make([]HostType, len(*in))
 		copy(*out, *in)
 	}
 	out.Secret = in.Secret
+	if in.Client != nil {
+		in, out := &in.Client, &out.Client
+		*out = new(ApisixMutualTlsClientConfig)
+		**out = **in
+	}
 	return
 }
 
diff --git a/pkg/kube/translation/apisix_ssl.go b/pkg/kube/translation/apisix_ssl.go
index 3dd4f30..7ab54fa 100644
--- a/pkg/kube/translation/apisix_ssl.go
+++ b/pkg/kube/translation/apisix_ssl.go
@@ -19,7 +19,6 @@
 
 	"github.com/apache/apisix-ingress-controller/pkg/id"
 	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
-	apisix "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
 
@@ -44,8 +43,10 @@
 		return nil, ErrEmptyPrivKey
 	}
 	var snis []string
-	snis = append(snis, tls.Spec.Hosts...)
-	ssl := &apisix.Ssl{
+	for _, host := range tls.Spec.Hosts {
+		snis = append(snis, string(host))
+	}
+	ssl := &apisixv1.Ssl{
 		ID:     id.GenID(tls.Namespace + "_" + tls.Name),
 		Snis:   snis,
 		Cert:   string(cert),
@@ -55,5 +56,20 @@
 			"managed-by": "apisix-ingress-controller",
 		},
 	}
+	if tls.Spec.Client != nil {
+		caSecret, err := t.SecretLister.Secrets(tls.Spec.Client.CASecret.Namespace).Get(tls.Spec.Client.CASecret.Name)
+		if err != nil {
+			return nil, err
+		}
+		ca, ok := caSecret.Data["cert"]
+		if !ok {
+			return nil, ErrEmptyCert
+		}
+		ssl.Client = &apisixv1.MutualTLSClientConfig{
+			CA:    string(ca),
+			Depth: tls.Spec.Client.Depth,
+		}
+	}
+
 	return ssl, nil
 }
diff --git a/pkg/types/apisix/v1/types.go b/pkg/types/apisix/v1/types.go
index 393b461..467872e 100644
--- a/pkg/types/apisix/v1/types.go
+++ b/pkg/types/apisix/v1/types.go
@@ -295,12 +295,20 @@
 // Ssl apisix ssl object
 // +k8s:deepcopy-gen=true
 type Ssl struct {
-	ID     string            `json:"id,omitempty" yaml:"id,omitempty"`
-	Snis   []string          `json:"snis,omitempty" yaml:"snis,omitempty"`
-	Cert   string            `json:"cert,omitempty" yaml:"cert,omitempty"`
-	Key    string            `json:"key,omitempty" yaml:"key,omitempty"`
-	Status int               `json:"status,omitempty" yaml:"status,omitempty"`
-	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
+	ID     string                 `json:"id,omitempty" yaml:"id,omitempty"`
+	Snis   []string               `json:"snis,omitempty" yaml:"snis,omitempty"`
+	Cert   string                 `json:"cert,omitempty" yaml:"cert,omitempty"`
+	Key    string                 `json:"key,omitempty" yaml:"key,omitempty"`
+	Status int                    `json:"status,omitempty" yaml:"status,omitempty"`
+	Labels map[string]string      `json:"labels,omitempty" yaml:"labels,omitempty"`
+	Client *MutualTLSClientConfig `json:"client,omitempty" yaml:"client,omitempty"`
+}
+
+// MutualTLSClientConfig apisix SSL client field
+// +k8s:deepcopy-gen=true
+type MutualTLSClientConfig struct {
+	CA    string `json:"ca,omitempty" yaml:"ca,omitempty"`
+	Depth int    `json:"depth,omitempty" yaml:"depth,omitempty"`
 }
 
 // StreamRoute represents the stream_route object in APISIX.
diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go b/pkg/types/apisix/v1/zz_generated.deepcopy.go
index 911eb86..64fcdba 100644
--- a/pkg/types/apisix/v1/zz_generated.deepcopy.go
+++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go
@@ -174,6 +174,22 @@
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MutualTLSClientConfig) DeepCopyInto(out *MutualTLSClientConfig) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MutualTLSClientConfig.
+func (in *MutualTLSClientConfig) DeepCopy() *MutualTLSClientConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(MutualTLSClientConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *RedirectConfig) DeepCopyInto(out *RedirectConfig) {
 	*out = *in
 	return
@@ -276,6 +292,11 @@
 			(*out)[key] = val
 		}
 	}
+	if in.Client != nil {
+		in, out := &in.Client, &out.Client
+		*out = new(MutualTLSClientConfig)
+		**out = **in
+	}
 	return
 }
 
diff --git a/samples/deploy/crd/v1beta1/ApisixTls.yaml b/samples/deploy/crd/v1beta1/ApisixTls.yaml
index b60eea7..c4e1858 100644
--- a/samples/deploy/crd/v1beta1/ApisixTls.yaml
+++ b/samples/deploy/crd/v1beta1/ApisixTls.yaml
@@ -21,23 +21,23 @@
   name: apisixtlses.apisix.apache.org
 spec:
   additionalPrinterColumns:
-    - JSONPath: .spec.hosts
-      name: SNIs
-      type: string
-    - JSONPath: .spec.secret.name
-      name: Secret Name
-      type: string
-    - JSONPath: .spec.secret.namespace
-      name: Secret Namespace
-      type: string
-    - JSONPath: .metadata.creationTimestamp
-      name: Age
-      type: date
+  - JSONPath: .spec.hosts
+    name: SNIs
+    type: string
+  - JSONPath: .spec.secret.name
+    name: Secret Name
+    type: string
+  - JSONPath: .spec.secret.namespace
+    name: Secret Namespace
+    type: string
+  - JSONPath: .metadata.creationTimestamp
+    name: Age
+    type: date
   group: apisix.apache.org
   versions:
-    - name: v1
-      served: true
-      storage: true
+  - name: v1
+    served: true
+    storage: true
   scope: Namespaced
   names:
     plural: apisixtlses
@@ -50,25 +50,61 @@
     status: {}
   validation:
     openAPIV3Schema:
+      description: ApisixTls defines SSL resource in APISIX.
       type: object
       properties:
+        apiVersion:
+          description: 'APIVersion defines the versioned schema of this representation
+            of an object. Servers should convert recognized schemas to the latest
+            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+          type: string
+        kind:
+          description: 'Kind is a string value representing the REST resource this
+            object represents. Servers may infer this from the endpoint the client
+            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+          type: string
+        metadata:
+          type: object
         spec:
+          description: ApisixTlsSpec is the specification of ApisixSSL.
           type: object
           required:
-            - hosts
-            - secret
+          - hosts
+          - secret
           properties:
+            client:
+              description: ApisixMutualTlsClientConfig describes the mutual TLS CA
+                and verify depth
+              type: object
+              properties:
+                caSecret:
+                  description: ApisixSecret describes the Kubernetes Secret name and
+                    namespace.
+                  type: object
+                  required:
+                  - name
+                  - namespace
+                  properties:
+                    name:
+                      type: string
+                      minLength: 1
+                    namespace:
+                      type: string
+                      minLength: 1
+                depth:
+                  type: integer
             hosts:
               type: array
               minItems: 1
               items:
                 type: string
-                pattern: "^\\*?[0-9a-zA-Z-.]+$"
+                pattern: ^\*?[0-9a-zA-Z-.]+$
             secret:
+              description: ApisixSecret describes the Kubernetes Secret name and namespace.
               type: object
               required:
-                - name
-                - namespace
+              - name
+              - namespace
               properties:
                 name:
                   type: string
@@ -76,3 +112,76 @@
                 namespace:
                   type: string
                   minLength: 1
+        status:
+          description: ApisixStatus is the status report for Apisix ingress Resources
+          type: object
+          properties:
+            conditions:
+              type: array
+              items:
+                description: "Condition contains details for one aspect of the current
+                  state of this API Resource. --- This struct is intended for direct
+                  use as an array at the field path .status.conditions.  For example,
+                  type FooStatus struct{     // Represents the observations of a foo's
+                  current state.     // Known .status.conditions.type are: \"Available\",
+                  \"Progressing\", and \"Degraded\"     // +patchMergeKey=type     //
+                  +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                  \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                  patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                  \n     // other fields }"
+                type: object
+                required:
+                - lastTransitionTime
+                - message
+                - reason
+                - status
+                - type
+                properties:
+                  lastTransitionTime:
+                    description: lastTransitionTime is the last time the condition
+                      transitioned from one status to another. This should be when
+                      the underlying condition changed.  If that is not known, then
+                      using the time when the API field changed is acceptable.
+                    type: string
+                    format: date-time
+                  message:
+                    description: message is a human readable message indicating details
+                      about the transition. This may be an empty string.
+                    type: string
+                    maxLength: 32768
+                  observedGeneration:
+                    description: observedGeneration represents the .metadata.generation
+                      that the condition was set based upon. For instance, if .metadata.generation
+                      is currently 12, but the .status.conditions[x].observedGeneration
+                      is 9, the condition is out of date with respect to the current
+                      state of the instance.
+                    type: integer
+                    format: int64
+                    minimum: 0
+                  reason:
+                    description: reason contains a programmatic identifier indicating
+                      the reason for the condition's last transition. Producers of
+                      specific condition types may define expected values and meanings
+                      for this field, and whether the values are considered a guaranteed
+                      API. The value should be a CamelCase string. This field may
+                      not be empty.
+                    type: string
+                    maxLength: 1024
+                    minLength: 1
+                    pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                  status:
+                    description: status of the condition, one of True, False, Unknown.
+                    type: string
+                    enum:
+                    - "True"
+                    - "False"
+                    - Unknown
+                  type:
+                    description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                      --- Many .condition.type values are consistent across resources
+                      like Available, but because arbitrary conditions can be useful
+                      (see .node.status.conditions), the ability to deconflict is
+                      important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                    type: string
+                    maxLength: 316
+                    pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
diff --git a/test/e2e/ingress/secret.go b/test/e2e/ingress/secret.go
index 2c19594..421a6a2 100644
--- a/test/e2e/ingress/secret.go
+++ b/test/e2e/ingress/secret.go
@@ -26,6 +26,7 @@
 	"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
 )
 
+// TODO: FIXME
 var _ = ginkgo.Describe("secret Testing", func() {
 	opts := &scaffold.Options{
 		Name:                  "default",
@@ -153,7 +154,7 @@
 		assert.Nil(ginkgo.GinkgoT(), err, "create tls error")
 		// check ssl in APISIX
 		time.Sleep(10 * time.Second)
-		tls, err := s.ListApisixTls()
+		tls, err := s.ListApisixSsl()
 		assert.Nil(ginkgo.GinkgoT(), err, "list tls error")
 		assert.Len(ginkgo.GinkgoT(), tls, 1, "tls number not expect")
 		assert.Equal(ginkgo.GinkgoT(), cert, tls[0].Cert, "tls cert not expect")
@@ -245,17 +246,17 @@
 8Z1U5/a5LhrDtIKAsCCpRx99P++Eqt2M2YV7jZcTfEbEvxP4XBYcdh30nbq1uEhs
 4zEnK1pMx5PnEljN1mcgmL2TPsMVN5DN9zXHW5eNQ6wfXR8rCfHwVIVcUuaB
 -----END RSA PRIVATE KEY-----`
-		key_compare_update := "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"
+		keyCompareUpdate := "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"
 		// key update compare
 		err = s.NewSecret(secretName, certUpdate, keyUpdate)
 		assert.Nil(ginkgo.GinkgoT(), err, "create secret error")
 		// check ssl in APISIX
 		time.Sleep(10 * time.Second)
-		tlsUpdate, err := s.ListApisixTls()
+		tlsUpdate, err := s.ListApisixSsl()
 		assert.Nil(ginkgo.GinkgoT(), err, "list tlsUpdate error")
 		assert.Len(ginkgo.GinkgoT(), tlsUpdate, 1, "tls number not expect")
 		assert.Equal(ginkgo.GinkgoT(), certUpdate, tlsUpdate[0].Cert, "tls cert not expect")
-		assert.Equal(ginkgo.GinkgoT(), key_compare_update, tlsUpdate[0].Key, "tls key not expect")
+		assert.Equal(ginkgo.GinkgoT(), keyCompareUpdate, tlsUpdate[0].Key, "tls key not expect")
 		// check DP
 		s.NewAPISIXHttpsClient(host).GET("/ip").WithHeader("Host", host).Expect().Status(http.StatusOK).Body().Raw()
 
@@ -264,7 +265,7 @@
 		assert.Nil(ginkgo.GinkgoT(), err, "delete tls error")
 		// check ssl in APISIX
 		time.Sleep(10 * time.Second)
-		tls, err = s.ListApisixTls()
+		tls, err = s.ListApisixSsl()
 		assert.Nil(ginkgo.GinkgoT(), err, "list tls error")
 		assert.Len(ginkgo.GinkgoT(), tls, 0, "tls number not expect")
 	})
diff --git a/test/e2e/ingress/ssl.go b/test/e2e/ingress/ssl.go
index 5511a86..ba8fe7f 100644
--- a/test/e2e/ingress/ssl.go
+++ b/test/e2e/ingress/ssl.go
@@ -16,6 +16,10 @@
 package ingress
 
 import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"net/http"
 	"time"
 
 	"github.com/onsi/ginkgo"
@@ -85,7 +89,7 @@
 		assert.Nil(ginkgo.GinkgoT(), err, "create tls error")
 		// check ssl in APISIX
 		time.Sleep(10 * time.Second)
-		tls, err := s.ListApisixTls()
+		tls, err := s.ListApisixSsl()
 		assert.Nil(ginkgo.GinkgoT(), err, "list tls error")
 		assert.Len(ginkgo.GinkgoT(), tls, 1, "tls number not expect")
 	})
@@ -153,7 +157,7 @@
 
 		// check ssl in APISIX
 		time.Sleep(10 * time.Second)
-		tls, err := s.ListApisixTls()
+		tls, err := s.ListApisixSsl()
 		assert.Nil(ginkgo.GinkgoT(), err, "list tls error")
 		assert.Len(ginkgo.GinkgoT(), tls, 1, "tls number not expect")
 		assert.Equal(ginkgo.GinkgoT(), tls[0].Snis[0], host, "tls host is error")
@@ -221,7 +225,7 @@
 
 		// check ssl in APISIX
 		time.Sleep(10 * time.Second)
-		tls, err := s.ListApisixTls()
+		tls, err := s.ListApisixSsl()
 		assert.Nil(ginkgo.GinkgoT(), err, "list tls error")
 		assert.Len(ginkgo.GinkgoT(), tls, 1, "tls number not expect")
 		assert.Equal(ginkgo.GinkgoT(), tls[0].Snis[0], host, "tls host is error")
@@ -231,8 +235,290 @@
 		assert.Nil(ginkgo.GinkgoT(), err, "delete tls error")
 		// check ssl in APISIX
 		time.Sleep(10 * time.Second)
-		tls, err = s.ListApisixTls()
+		tls, err = s.ListApisixSsl()
 		assert.Nil(ginkgo.GinkgoT(), err, "list tls error")
 		assert.Len(ginkgo.GinkgoT(), tls, 0, "tls number not expect")
 	})
 })
+
+var _ = ginkgo.Describe("ApisixTls mTLS Test", func() {
+	// RootCA -> Server
+	// RootCA -> UserCert
+	// These certs come from mTLS practice
+
+	rootCA := `-----BEGIN CERTIFICATE-----
+MIIF9zCCA9+gAwIBAgIUFKuzAJZgm/fsFS6JDrd+lcpVZr8wDQYJKoZIhvcNAQEL
+BQAwgZwxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI
+SGFuZ3pob3UxGDAWBgNVBAoMD0FQSVNJWC1UZXN0LUNBXzEYMBYGA1UECwwPQVBJ
+U0lYX0NBX1JPT1RfMRUwEwYDVQQDDAxBUElTSVguUk9PVF8xHDAaBgkqhkiG9w0B
+CQEWDXRlc3RAdGVzdC5jb20wHhcNMjEwNTI3MTMzNjI4WhcNMjIwNTI3MTMzNjI4
+WjCBnDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI
+YW5nemhvdTEYMBYGA1UECgwPQVBJU0lYLVRlc3QtQ0FfMRgwFgYDVQQLDA9BUElT
+SVhfQ0FfUk9PVF8xFTATBgNVBAMMDEFQSVNJWC5ST09UXzEcMBoGCSqGSIb3DQEJ
+ARYNdGVzdEB0ZXN0LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+ALJR0lQW/IBqQTE/Oa0Pi4LlmlYUSGnqtFNqiZyOF0PjVzNeqoD9JDPiM1QRyC8p
+NCd5L/QhtUIMMx0RlDI9DkJ3ALIWdrPIZlwpveDJf4KtW7cz+ea46A6QQwB6xcyV
+xWnqEBkiea7qrEE8NakZOMjgkqkN2/9klg6XyA5FWfvszxtuIHtjcy2Kq8bMC0jd
+k7CqEZe4ct6s2wlcI8t8s9prvMDm8gcX66x4Ah+C2/W+C3lTpMDgGqRqSPyCW7na
+Wgn0tWmTSf1iybwYMydhC+zpM1QJLvfDyqjp1wJhziR5ttVe2Xc+tDC24s+u16yZ
+R93IO0M4lLNjvEKJcMltXyRzrcjvLXOhw3KirSHNL1KfrBEl74lb+DV5eU4pIFCj
+cu18gms5FBYs9tpLujwpHDc2MU+zCvRmSPvUA4yCyoXqom3uiSo3g3ymW9IM8dC8
++Bd1GdM6JbpBukvQybc5TQXo1M75I9iEoQa5tQxAfQ/dfwMjOK7skogowBouOuLv
+BEFKy3Vd57IWWZXC4p/74M6N4fGYTgHY5FQE3R4Y2phk/eaEm1jS1UPuC98QuTfL
+rGuFOIBmK5euOm8uT5m9hnrouG2ZcxEdzHYfjsGDGrLzA0FLu+wtMNBKM4NhsNCa
+d+fycLg7jgxWhaLvD5DfkV7WFQlz5LUceYIwYOyhD/chAgMBAAGjLzAtMAwGA1Ud
+EwQFMAMBAf8wHQYDVR0RBBYwFIISbXRscy5odHRwYmluLmxvY2FsMA0GCSqGSIb3
+DQEBCwUAA4ICAQCNtBmoAc5tv3H38sj9qhTmabvp9RIzZYrQSEcN+A2i3a8FVYAM
+YaugZDXDcTycoWn6rcgblUDneow3NiqZ57yYZmN+e4mE3+Q1sGepV7LoRkHDUT8w
+jAJndcZ/xxJmgH6B7dImTAPsvLGR7E7gffMH+aKCdnkG9x5Vm+cuBwSEBndiHGfr
+yw5cXO6cMUq8M6zJrk2V+1BAucXW2rgLTWy6UTTGD56cgUtbStRO6muOKoElDLbW
+mSj2rNv/evakQkV8dgKVRFgh2NQKYKpXmveMaE6xtFFf/dd9OhDFjUh/ksxn94FT
+xj/wkhXCEPl+t7tENhr2tNyLbCOVcFzqoi7IyoWKxxZQfvArfj4SmahK8E/BXB/T
+4PEmn8kZAxaW7RmGcaekm8MTqGlhCJ3tVJAI2vcYRdd9ZHbXE1jr/4xj0I/Lzglo
+O8v5fd4zHyV1SuZ5AH3XbUd7ndl9yDoN2WSqK9Nd9bws3yrf+GwjJAT1InnDvLg1
+stWM8I+9FZiDFL255/+iAN0jYcGu9i4TNvC+o6qQ1p85i1OHPJZu6wtUWMgDJN46
+uwW3ZLh9sZV6OnhbQJBQaUmcgaPJUQqbXNQmpmpc0NUjET/ltFRZ2hlyvvpf7wwF
+2DLY1HRAknQ69DuT6xpYz1aKZqrlkbCWlMMvdosOg6f7+4NxdYJ/rBeS6Q==
+-----END CERTIFICATE-----
+`
+
+	serverCertSecret := `server-secret`
+	serverCert := `-----BEGIN CERTIFICATE-----
+MIIF/TCCA+WgAwIBAgIUBbUP7Gk0WAb/JhYYcBBgZEgmhbEwDQYJKoZIhvcNAQEL
+BQAwgZwxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI
+SGFuZ3pob3UxGDAWBgNVBAoMD0FQSVNJWC1UZXN0LUNBXzEYMBYGA1UECwwPQVBJ
+U0lYX0NBX1JPT1RfMRUwEwYDVQQDDAxBUElTSVguUk9PVF8xHDAaBgkqhkiG9w0B
+CQEWDXRlc3RAdGVzdC5jb20wHhcNMjEwNTI3MTMzNjI5WhcNMjIwNTI3MTMzNjI5
+WjCBpTELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI
+YW5nemhvdTEcMBoGA1UECgwTQVBJU0lYLVRlc3QtU2VydmVyXzEXMBUGA1UECwwO
+QVBJU0lYX1NFUlZFUl8xGzAZBgNVBAMMEm10bHMuaHR0cGJpbi5sb2NhbDEcMBoG
+CSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAMZRPG8gUrOGM4awnV6D8Ds0Xb6jVbiGkx+1YsvPx5oIE4AswJ0l
+y6zqhBFnpQozFG63KfsCA6U36/Dty3rIbJzsbO7YaOMJItoiQgqdqF2nrmPpmpCQ
+uLGKaVvriRCD55NEmFQPshlRfcU5/EEreNKbRve3zEKHRpCDBZ2Myvrpt3CCVy6D
+MbLllbjUvaedrnQxlmI5d7x3UCe4Eunq8vn7c0p4frA1n8TxbX0M4Yr9g3YEEqCv
+Q3/9jU4hI5CvujCp+u79EavJZfsaEv3RYgHkoEh7q+OEkUajWXKj4WynizraWsHv
++LvK9pfI300p1HSKK4FqonvW79anRNbK+8BqV4Wt5aBeFU/rW2jHtJxcl1OLRrrh
+wftCP5W7vSjvJes5wPDZjDEyv8WP1Aa6yWeGHHtIwrAHPr7556F/JAQS6IPBQQ5U
+X45DD2aNXME9xZKdBtyMovItjZm31UUsvoF+YtpAOmbEkX4lMznUO3XZJjM5HWSq
+WYyzmFsw+pJEwhXRo4GfSfCHfiZQ6imTLJ7OsZzo9bvmxyfI0kVLe3h3iCe+qYeT
+f5AJU6v5vv3thCtfgbxYP2P8b+0MIrfr05e6dCDXbIri1z+nprzWYmyCrZ6H4hVk
+DzMktFUlkqenvnsJ2iOV2AZw0Hlk2bwe4zSumzqoIp8Yk/kxbfxhQqr5AgMBAAGj
+LDAqMAkGA1UdEwQCMAAwHQYDVR0RBBYwFIISbXRscy5odHRwYmluLmxvY2FsMA0G
+CSqGSIb3DQEBCwUAA4ICAQCDDfETCEpWB/KRQZo2JF8n4NEDTeraQ85M3H5luJHp
+NdJO4oYq3n8B149ep4FcEYdO20pV+TMeMNWXMfhoRIpGx95JrLuLg6qnw6eNdErn
+YupHMC2OEoEWVcmI052LDJcXuKsTXQvU4OeEL2dX4OtNJ+mRODLyh40cg7dA3wry
+kGLiprRlLQtiX8pSDG30qPZexL1LcFzBQajriG05QUrJW6Rvbq1JTIlyp7E1T86f
+Xljq0Hdzqxy+FklYcAW5ZAxgkQlMmVdTlvDXlD/hQLEQIHGHiW6OMLp8WrnJP6b0
+D2HqWmOwuEzqSgXSK0N89rpiWP1FKCpyiKVcsawDNfOpePVuthommVEc2PxacyHf
+UCC9V0MS0ZzQ63Tnz2Tja8C6/kMyVX226KQKhcoDxDoS0mQrI96/VXcglwP5hMjF
+joth1T1qRVu6+NQmvFPaNjbzWJ+j1R99bnYGihPeLdqDSUxNosV3ULG8T4aN6+f8
+hApiqg2dkLJQr8zWf6vWXMlREdPEovb2F7P0Lfn0VeOSRXDUIdqcoRHONi8bWMRs
+fjPtGW00Tv8Jg21c9vc8Zh/t1w3wkXQhqYiBMt5cYe6WueIlXdjF7ikSRWAHTwlw
+Bfzv/vMftLnbySPovCzQ1PF55D01EWRk0o6PRwUDLfzTQoV+bDKx82LxKtZBtQEX
+uw==
+-----END CERTIFICATE-----
+`
+	serverKey := `-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
+`
+	clientCASecret := `client-ca-secret`
+	clientCert := `-----BEGIN CERTIFICATE-----
+MIIGDDCCA/SgAwIBAgIUBbUP7Gk0WAb/JhYYcBBgZEgmhbIwDQYJKoZIhvcNAQEL
+BQAwgZwxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI
+SGFuZ3pob3UxGDAWBgNVBAoMD0FQSVNJWC1UZXN0LUNBXzEYMBYGA1UECwwPQVBJ
+U0lYX0NBX1JPT1RfMRUwEwYDVQQDDAxBUElTSVguUk9PVF8xHDAaBgkqhkiG9w0B
+CQEWDXRlc3RAdGVzdC5jb20wHhcNMjEwNTI3MTMzNjMxWhcNMjIwNTI3MTMzNjMx
+WjCBtDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI
+YW5nemhvdTEfMB0GA1UECgwWQVBJU0lYLVRlc3QtUm9vdC1Vc2VyXzEaMBgGA1UE
+CwwRQVBJU0lYX1JPT1RfVVNFUl8xJDAiBgNVBAMMG0JpZ01pbmdfIDY1NDMxMTEx
+MTExMTExMTExMTEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL41i+W8fgqtKButnkOa0qGApPJ7HSUI
+gxBt6Tb7mR8eka8vno8hIE42hwMXYoIiO1FgM9M9fPN8vzTnWr2iTAXbw/qXdn14
+RT01JrGzj1OU3M9U0RMmDWVs8VOY3ncdcdPoqctMNr2K+6wlN6EpG1DQDUEvB0Yy
+qZpFWpuZP0akzjFqeJNSo3b8hP4J1nsWBxKt6TRInC60jT1mhKCjTB1IsXVC+5SY
+f0FhMN2ZQPgCyIIq+j5S7tHta4Qs+UDNY0whIMBkY3ZAGPzUBFExwrX7bGNbeJYz
+DahNAKiEj5dlieRvzTn5UisSv5EzDUONLSluEnCU1Ghd/dfZtZwlMBU6OIflwm6W
+X7HePD0G8XEDe5qbU/tg/62j+A1hMS2gSlibNisyvNwMNz6Imc3CnIFUL2mTsgl1
+bCBLfOXDfrjte5Txnt26ayN0+WhmVSts0+ln6vnGtgVGqH0bmp95yMRYPej+6ARE
+MtfvrNA7iZORypspv2NVyV11b6Jx3o/pJOAq9dmAsKvJ6hK0IVf/55t3ZtzRa/fY
+w0f78KbWuEc7qmx2tBxnEfppb5iB/RFmI2bDaAEA0lDQgAqmiAu1utetfqsCwA1m
+3VXGp8Vp+lexA/M3Bkb5BHa6C11QDHvGjUVwduOymLAHKumvdgf7TJFnlhpg6jYN
+unO58bRSTI6LAgMBAAGjLDAqMAkGA1UdEwQCMAAwHQYDVR0RBBYwFIISbXRscy5o
+dHRwYmluLmxvY2FsMA0GCSqGSIb3DQEBCwUAA4ICAQCoTBvw11aKah2cuB4XplUB
+nmkrWhfLNFJLVrU9jIISP9Wkl3s3PcM+aEWygb/1roqbMqNOgrzDVgGRRFCiS6qi
+himUuTJhIBI6TF1PE+XW3gTFWBXkAZ7MzpbS8oP1PehlY3XXKNZgxZi3XaDI8Hfw
+5MWBGNbk8tegn8bvYQUz2VxmCo6zufCkj4ADjw2zhiyKBKuHTzg48w66Wn4jLhlK
+p91HHrK0lEOIJ4pFmBUpBsSJMlBMbfrzBF87xQhpDO3ScFfCWUatShmXsPMJU0F0
+DEuTnaHUefUf/F9wUGNcA4yQ4pH8SxVpRHmrWE8U4uSXpz1bx4ChZurJ9mPzrj9h
+U9c/d9F5WndZNPcR1R8Tbzhk/R8GImVR3Lt59cW+SN/+4JVFy+Hye2yslGFn2CAJ
+ofNxjLb9OE6+EE3SWW0B5CZSWBS49gtdTW0ApOjIRJU2zipxcjnNf00GFoIoCxjk
+Z4eBQz9WVUM9KSrJIQSLZQd35tZAOp0BuwWho0+w8lXUchSqT7oRA7+szZldWF0j
+HKPIMJ0iVWmXuZjsS8q8NBIt4DuBcqpevlol5KRXv6tJy4IBVAVEIBdeXotvdxKE
+bncvZ6xo9A/waUU7tEyzv34usxefrWxtSlOA1G0Jj4nb5gKPHjn0XIr9WI2RpovT
+/XpB6QES1zoBQya3QjnDbQ==
+-----END CERTIFICATE-----
+`
+
+	clientKey := `-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
+`
+
+	s := scaffold.NewDefaultV2Scaffold()
+	ginkgo.It("create a SSL with client CA", func() {
+		// create secrets
+		err := s.NewSecret(serverCertSecret, serverCert, serverKey)
+		assert.Nil(ginkgo.GinkgoT(), err, "create server cert secret error")
+		err = s.NewClientCASecret(clientCASecret, rootCA, "")
+		assert.Nil(ginkgo.GinkgoT(), err, "create client CA cert secret error")
+
+		// create ApisixTls resource
+		tlsName := "tls-with-client-ca"
+		host := "mtls.httpbin.local"
+		err = s.NewApisixTlsWithClientCA(tlsName, host, serverCertSecret, clientCASecret)
+		assert.Nil(ginkgo.GinkgoT(), err, "create ApisixTls with client CA error")
+		// check ssl in APISIX
+		time.Sleep(10 * time.Second)
+		apisixSsls, err := s.ListApisixSsl()
+		assert.Nil(ginkgo.GinkgoT(), err, "list ssl error")
+		assert.Len(ginkgo.GinkgoT(), apisixSsls, 1, "ssl number not expect")
+
+		// create route
+		backendSvc, backendSvcPort := s.DefaultHTTPBackend()
+		apisixRoute := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpbin-route
+spec:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - mtls.httpbin.local
+      paths:
+      - /*
+    backend:
+      serviceName: %s
+      servicePort: %d
+`, backendSvc, backendSvcPort[0])
+		assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(apisixRoute))
+		time.Sleep(10 * time.Second)
+
+		apisixRoutes, err := s.ListApisixRoutes()
+		assert.Nil(ginkgo.GinkgoT(), err, "list routes error")
+		assert.Len(ginkgo.GinkgoT(), apisixRoutes, 1, "route number not expect")
+
+		// Without Client Cert
+		s.NewAPISIXHttpsClient(host).GET("/ip").WithHeader("Host", host).Expect().Status(http.StatusBadRequest).Body().Raw()
+
+		// With client cert
+		caCertPool := x509.NewCertPool()
+		ok := caCertPool.AppendCertsFromPEM([]byte(rootCA))
+		assert.True(ginkgo.GinkgoT(), ok, "Append cert to CA pool")
+
+		cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
+		assert.Nil(ginkgo.GinkgoT(), err, "generate cert")
+
+		s.NewAPISIXHttpsClientWithCertificates(host, true, caCertPool, []tls.Certificate{cert}).
+			GET("/ip").WithHeader("Host", host).Expect().Status(http.StatusOK)
+	})
+})
diff --git a/test/e2e/scaffold/k8s.go b/test/e2e/scaffold/k8s.go
index da5ede6..4f4a912 100644
--- a/test/e2e/scaffold/k8s.go
+++ b/test/e2e/scaffold/k8s.go
@@ -305,8 +305,8 @@
 	return cli.Cluster("").StreamRoute().List(context.TODO())
 }
 
-// ListApisixTls list all ssl from APISIX
-func (s *Scaffold) ListApisixTls() ([]*v1.Ssl, error) {
+// ListApisixSsl list all ssl from APISIX
+func (s *Scaffold) ListApisixSsl() ([]*v1.Ssl, error) {
 	u := url.URL{
 		Scheme: "http",
 		Host:   s.apisixAdminTunnel.Endpoint(),
diff --git a/test/e2e/scaffold/scaffold.go b/test/e2e/scaffold/scaffold.go
index 5e68ff5..2e0998c 100644
--- a/test/e2e/scaffold/scaffold.go
+++ b/test/e2e/scaffold/scaffold.go
@@ -17,6 +17,7 @@
 import (
 	"context"
 	"crypto/tls"
+	"crypto/x509"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -126,6 +127,19 @@
 	return NewScaffold(opts)
 }
 
+// NewDefaultV2Scaffold creates a scaffold with some default options.
+func NewDefaultV2Scaffold() *Scaffold {
+	opts := &Options{
+		Name:                  "default",
+		Kubeconfig:            GetKubeconfig(),
+		APISIXConfigPath:      "testdata/apisix-gw-config.yaml",
+		IngressAPISIXReplicas: 1,
+		HTTPBinServicePort:    80,
+		APISIXRouteVersion:    kube.ApisixRouteV2alpha1,
+	}
+	return NewScaffold(opts)
+}
+
 // KillPod kill the pod which name is podName.
 func (s *Scaffold) KillPod(podName string) error {
 	cli, err := k8s.GetKubernetesClientE(s.t)
@@ -191,7 +205,7 @@
 	})
 }
 
-// NewAPISIXHttpsClient creates the default HTTPs client.
+// NewAPISIXHttpsClient creates the default HTTPS client.
 func (s *Scaffold) NewAPISIXHttpsClient(host string) *httpexpect.Expect {
 	u := url.URL{
 		Scheme: "https",
@@ -214,6 +228,30 @@
 	})
 }
 
+// NewAPISIXHttpsClientWithCertificates creates the default HTTPS client with giving trusted CA and client certs.
+func (s *Scaffold) NewAPISIXHttpsClientWithCertificates(host string, insecure bool, ca *x509.CertPool, certs []tls.Certificate) *httpexpect.Expect {
+	u := url.URL{
+		Scheme: "https",
+		Host:   s.apisixHttpsTunnel.Endpoint(),
+	}
+	return httpexpect.WithConfig(httpexpect.Config{
+		BaseURL: u.String(),
+		Client: &http.Client{
+			Transport: &http.Transport{
+				TLSClientConfig: &tls.Config{
+					InsecureSkipVerify: insecure,
+					ServerName:         host,
+					RootCAs:            ca,
+					Certificates:       certs,
+				},
+			},
+		},
+		Reporter: httpexpect.NewAssertReporter(
+			httpexpect.NewAssertReporter(ginkgo.GinkgoT()),
+		),
+	})
+}
+
 // APISIXGatewayServiceEndpoint returns the apisix http gateway endpoint.
 func (s *Scaffold) APISIXGatewayServiceEndpoint() string {
 	return s.apisixHttpTunnel.Endpoint()
diff --git a/test/e2e/scaffold/ssl.go b/test/e2e/scaffold/ssl.go
index cdde5ab..4b84134 100644
--- a/test/e2e/scaffold/ssl.go
+++ b/test/e2e/scaffold/ssl.go
@@ -32,6 +32,14 @@
   cert: %s
   key: %s
 `
+	_clientCASecretTemplate = `
+apiVersion: v1
+kind: Secret
+metadata:
+  name: %s
+data:
+  cert: %s
+`
 	_api6tlsTemplate = `
 apiVersion: apisix.apache.org/v1
 kind: ApisixTls
@@ -44,6 +52,23 @@
     name: %s
     namespace: %s
 `
+	_api6tlsWithClientCATemplate = `
+apiVersion: apisix.apache.org/v1
+kind: ApisixTls
+metadata:
+  name: %s
+spec:
+  hosts:
+  - %s
+  secret:
+    name: %s
+    namespace: %s
+  client:
+    caSecret:
+      name: %s
+      namespace: %s
+    depth: 10
+`
 )
 
 // NewSecret new a k8s secret
@@ -57,6 +82,16 @@
 	return nil
 }
 
+// NewSecret new a k8s secret
+func (s *Scaffold) NewClientCASecret(name, cert, key string) error {
+	certBase64 := base64.StdEncoding.EncodeToString([]byte(cert))
+	secret := fmt.Sprintf(_clientCASecretTemplate, name, certBase64)
+	if err := k8s.KubectlApplyFromStringE(s.t, s.kubectlOptions, secret); err != nil {
+		return err
+	}
+	return nil
+}
+
 // NewApisixTls new a ApisixTls CRD
 func (s *Scaffold) NewApisixTls(name, host, secretName string) error {
 	tls := fmt.Sprintf(_api6tlsTemplate, name, host, secretName, s.kubectlOptions.Namespace)
@@ -66,6 +101,15 @@
 	return nil
 }
 
+// NewApisixTlsWithClientCA new a ApisixTls CRD
+func (s *Scaffold) NewApisixTlsWithClientCA(name, host, secretName, clientCASecret string) error {
+	tls := fmt.Sprintf(_api6tlsWithClientCATemplate, name, host, secretName, s.kubectlOptions.Namespace, clientCASecret, s.kubectlOptions.Namespace)
+	if err := k8s.KubectlApplyFromStringE(s.t, s.kubectlOptions, tls); err != nil {
+		return err
+	}
+	return nil
+}
+
 // DeleteApisixTls remove ApisixTls CRD
 func (s *Scaffold) DeleteApisixTls(name string, host, secretName string) error {
 	tls := fmt.Sprintf(_api6tlsTemplate, name, host, secretName, s.kubectlOptions.Namespace)