8.7 KiB
| title | description | weight | keywords | ||
|---|---|---|---|---|---|
| Testing Mutual TLS | Shows you how to verify and test Istio's automatic mutual TLS authentication. | 10 |
|
Through this task, you will learn how to:
-
Verify the Istio mutual TLS Authentication setup
-
Manually test the authentication
Before you begin
This task assumes you have a Kubernetes cluster:
-
Installed Istio with global mutual TLS enabled using Helm:
Use Helm with
global.mtls.enabledtotrue.
Starting with Istio 0.7, you can use authentication policy to configure mutual TLS for all/selected services in a namespace (repeated for all namespaces to get global setting). See the authentication policy task
-
For demo, deploy [httpbin]({{< github_tree >}}/samples/httpbin) and [sleep]({{< github_tree >}}/samples/sleep) with Envoy sidecar. For simplicity, the demo is setup in the
defaultnamespace. If you wish to use a different namespace, please add-n yournamespaceappropriately to the example commands in the next section.If you are using manual sidecar injection, use the following command
{{< text bash >}} $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) {{< /text >}}
If you are using a cluster with automatic sidecar injection enabled, simply deploy the services using
kubectl{{< text bash >}} $ kubectl apply -f @samples/httpbin/httpbin.yaml@ $ kubectl apply -f @samples/sleep/sleep.yaml@ {{< /text >}}
Verifying Istio's mutual TLS authentication setup
Verifying Citadel is running
Verify the cluster-level Citadel is running:
{{< text bash >}} $ kubectl get deploy -l istio=citadel -n istio-system NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE istio-citadel 1 1 1 1 1m {{< /text >}}
Citadel is up if the "AVAILABLE" column is 1.
Verifying service configuration
-
Check installation mode. If mutual TLS is enabled by default (e.g
istio-demo-auth.yamlwas used when installing Istio), you can expect to see uncommentedauthPolicy: MUTUAL_TLSin the configmap.{{< text bash >}} $ kubectl get configmap istio -o yaml -n istio-system | grep authPolicy | head -1 {{< /text >}}
-
Check authentication policies. Mutual TLS can also be enabled (or disabled) per service(s) by authentication policy. A policy, if exist, will overwrite the configmap setting for the targeted services. Unfortunately, there is no quick way to get relevant policies for a service, other than examining all policies in the applicable namespace:
{{< text bash >}} $ kubectl get policies.authentication.istio.io -n default -o yaml {{< /text >}}
-
Check destination rule. Starting with Istio 0.8, destination rule's traffic policy is used to configure client side to use (or not use) mutual TLS. For backward compatibility, the default traffic policy is inferred from configmap flag (i.e, if
authPolicy: MUTUAL_TLS, default traffic policy also beMUTUAL_TLS). If there is authentication policy overrules this setting for some services, it should accompany with the appropriate destination rule(s). Similar to authentication policy, the only way to verify the settings is to manually check all rules:{{< text bash >}} $ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml {{< /text >}}
Note that the destination rules scoping model is not limited to namespaces. Thus, it's necessary to examine rules in all namespaces.
Verifying keys and certificates installation
Istio automatically installs necessary keys and certificates for mutual TLS authentication in all sidecar containers.
{{< text bash >}}
kubectl exec(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.name}) -c istio-proxy -- ls /etc/certs
cert-chain.pem
key.pem
root-cert.pem
{{< /text >}}
cert-chain.pemis Envoy's cert that needs to be presented to the other side.key.pemis Envoy's private key paired with Envoy's cert incert-chain.pem.root-cert.pemis the root cert to verify the peer's cert. In this example, we only have one Citadel in a cluster, so all Envoys have the sameroot-cert.pem.
Use the oppenssl tool to check if certificate is valid (current time should be in between Not Before and Not After)
{{< text bash >}}
kubectl exec(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.name}) -c istio-proxy -- cat /etc/certs/cert-chain.pem | openssl x509 -text -noout | grep Validity -A 2
Validity
Not Before: May 17 23:02:11 2018 GMT
Not After : Aug 15 23:02:11 2018 GMT
{{< /text >}}
You can also check the identity of the client certificate:
{{< text bash >}}
kubectl exec(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.name}) -c istio-proxy -- cat /etc/certs/cert-chain.pem | openssl x509 -text -noout | grep 'Subject Alternative Name' -A 1
X509v3 Subject Alternative Name:
URI:spiffe://cluster.local/ns/default/sa/default
{{< /text >}}
Please check secure naming for more information about service identity in Istio.
Testing the authentication setup
Assuming mutual TLS authentication is properly turned on, it should not affect communication from one service to another when both sides have the Envoy sidecar. However, requests from pod without sidecar, or requests directly from sidecar without a client certificate should fail. Examples below illustrates this behavior.
-
Request from
sleepapp container tohttpbinservice should succeed (return200){{< text bash >}}
kubectl exec(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c sleep -- curl httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n' 200 {{< /text >}} -
Request from
sleepproxy container tohttpbinservice on the other hand fails, as request does not use TLS nor provide a client certificate{{< text bash >}}
kubectl exec(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n' 000 command terminated with exit code 56 {{< /text >}}{{< text bash >}}
kubectl exec(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl https://httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n' 000 command terminated with exit code 77 {{< /text >}} -
However, request will success if client certificate is provided
{{< text bash >}}
kubectl exec(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl https://httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n' --key /etc/certs/key.pem --cert /etc/certs/cert-chain.pem --cacert /etc/certs/root-cert.pem -k 200 {{< /text >}}Istio uses Kubernetes service accounts as service identity, which offers stronger security than service name (refer here for more information). Thus the certificates used in Istio do not have service names, which is the information that
curlneeds to verify server identity. As a result, we usecurloption-kto prevent thecurlclient from aborting when failing to find and verify the server name (i.e., httpbin.ns.svc.cluster.local) in the certificate provided by the server. -
Request from pod without sidecar. For this demo, let's install another
sleepservice without sidecar. To avoid name conflicts, we put it in different namespace.{{< text bash >}} $ kubectl create ns legacy $ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy {{< /text >}}
-
Wait after the pod status changes to
Running, issue the familiarcurlcommand. The request should fail as the pod doesn't have a sidecar to help initiate TLS communication.{{< text bash >}}
kubectl exec(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n legacy) -c sleep -n legacy -- curl httpbin.default:8000/headers -o /dev/null -s -w '%{http_code}\n' 000 command terminated with exit code 56 {{< /text >}}
Cleanup
{{< text bash >}} $ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@ $ kubectl delete --ignore-not-found=true -f @samples/sleep/sleep.yaml@ $ kubectl delete --ignore-not-found=true ns legacy {{< /text >}}