Change "Testing mutual TLS" tutorial to "Mutual TLS deep dive" (#1972)

This commit is contained in:
Diem Vu 2018-07-31 16:12:33 -07:00 committed by Martin Taillefer
parent 137e1d13f4
commit 0662e413f1
2 changed files with 107 additions and 92 deletions

View File

@ -1,48 +1,27 @@
---
title: Testing Mutual TLS
title: Mutual TLS Deep-Dive
description: Shows you how to verify and test Istio's automatic mutual TLS authentication.
weight: 10
keywords: [security,mutual-tls]
---
Through this task, you will learn how to:
Through this task, you can have closer look at mutual TLS and learn its settings. This task assumes:
* 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](/docs/setup/kubernetes/helm-install/) with `global.mtls.enabled` to `true`.
> Starting with Istio 0.7, you can use [authentication policy](/docs/concepts/security/#authentication-policies) to configure mutual TLS for all/selected services in a namespace
(repeated for all namespaces to get global setting). See the [authentication policy task](/docs/tasks/security/authn-policy/)
* 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 `default` namespace. If you wish to use a different namespace, please add `-n yournamespace` appropriately to the example commands in the next section.
If you are using [manual sidecar injection](/docs/setup/kubernetes/sidecar-injection/#manual-sidecar-injection), use the following command
* You have completed the [authentication policy](/docs/tasks/security/authn-policy/) task.
* You are familiar with using authentication policy to enable mutual TLS.
* Istio runs on Kubernetes with global mutual TLS enabled. You can follow our [instructions to install Istio](/docs/setup/kubernetes/).
If you already have Istio installed, you can add or modify authentication policies and destination rules to enable mutual TLS as described in this [task](/docs/tasks/security/authn-policy/#globally-enabling-istio-mutual-tls).
* You have deployed the [httpbin]({{< github_tree >}}/samples/httpbin) and [sleep]({{< github_tree >}}/samples/sleep) with Envoy sidecar in the `default` namespace. For example, below is the command to deploy those services with [manual sidecar injection](/docs/setup/kubernetes/sidecar-injection/#manual-sidecar-injection):
{{< 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](/docs/setup/kubernetes/sidecar-injection/#automatic-sidecar-injection) enabled, simply deploy the services using `kubectl`
## Verify Citadel runs properly
{{< 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:
[Citadel](/docs/concepts/security/#pki) is Istio's key management service. Citadel must run properly for mutual TLS to work correctly. Verify the
cluster-level Citadel runs properly with the following command:
{{< text bash >}}
$ kubectl get deploy -l istio=citadel -n istio-system
@ -52,31 +31,9 @@ istio-citadel 1 1 1 1 1m
Citadel is up if the "AVAILABLE" column is 1.
### Verifying service configuration
## Verify keys and certificates installation
* Check installation mode. If mutual TLS is enabled by default (e.g `istio-demo-auth.yaml` was used when installing Istio), you can expect to see uncommented `authPolicy: MUTUAL_TLS` in 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](/docs/reference/config/istio.networking.v1alpha3/#TrafficPolicy) 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 be `MUTUAL_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.
Istio automatically installs necessary keys and certificates for mutual TLS authentication in all sidecar containers. Run command below to confirm key and certificate files exist under `/etc/certs`:
{{< text bash >}}
$ kubectl exec $(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.name}) -c istio-proxy -- ls /etc/certs
@ -108,64 +65,122 @@ $ kubectl exec $(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.na
Please check [Istio identity](/docs/concepts/security/#istio-identity) for more information about _service identity_ in Istio.
## Testing the authentication setup
## Verify mutual TLS configuration
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.
You can use the `istioctl` tool to check the effective mutual TLS settings. To identify the authentication policy and destination rules used for the
`httpbin.default.svc.cluster.local` configuration and the mode employed, use the following command:
1. Request from `sleep` app container to `httpbin` service should succeed (return `200`)
{{< text bash >}}
$ istioctl authn tls-check httpbin.default.svc.cluster.local
{{< /text >}}
In the following example output you can see that:
* Mutual TLS is consistently setup for `httpbin.default.svc.cluster.local` on port 8080.
* Istio uses the mesh-wide `default` authentication policy.
* Istio has the `default` destination rule in the `default` namespace.
{{< text plain >}}
HOST:PORT STATUS SERVER CLIENT AUTHN POLICY DESTINATION RULE
httpbin.default.svc.cluster.local:8080 OK mTLS mTLS default/ default/default
{{< /text >}}
The output shows:
* `STATUS`: whether the TLS settings are consistent between the server, the `httpbin` service in this case, and the client or clients making calls to `httpbin`.
* `SERVER`: the mode used on the server.
* `CLIENT`: the mode used on the client or clients.
* `AUTHN POLICY`: the name and namespace of the authentication policy. If the policy is the mesh-wide policy, namespace is blank, as in this case: `default/`
* `DESTINATION RULE`: the name and namespace of the destination rule used.
To illustrate the case when there are conflicts, add a service-specific destination rule for `httpbin` with incorrect TLS mode:
{{< text bash >}}
$ cat <<EOF | istioctl create -n bar -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "bad-rule"
spec:
host: "httpbin.default.svc.cluster.local"
trafficPolicy:
tls:
mode: DISABLE
EOF
{{< /text >}}
Run the same `istioctl` command as above, you now see the status is `CONFLICT`, as client is in `HTTP` mode while server is in `mTLS`.
{{< text bash >}}
$ istioctl authn tls-check httpbin.default.svc.cluster.local
HOST:PORT STATUS SERVER CLIENT AUTHN POLICY DESTINATION RULE
httpbin.default.svc.cluster.local:8080 CONFLICT mTLS HTTP default/ bad-rule/default
{{< /text >}}
You can also confirm that requests from `sleep` to `httpbin` are now failed:
{{< 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'
000
command terminated with exit code 56
{{< /text >}}
Before you continue, remove the bad destination rule to make mutual TLS work again with the following command:
{{< text bash >}}
$ kubectl delete --ignore-not-found=true bad-rule
{{< /text >}}
## Verify requests
This task shows how a server with mutual TLS enabled responses to requests that are:
* In plain-text
* With TLS but without client certificate
* With TLS with a client certificate
To perform this task, you want to by-pass client proxy. A simplest way to do so is to issue request from `istio-proxy` container.
1. Confirm that plain-text requests fail as TLS is required to talk to `httpbin` with the following command:
{{< 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 >}}
1. Request from `sleep` _proxy_ container to `httpbin` service 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'
$ kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl http://httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n'
000
command terminated with exit code 56
{{< /text >}}
> Note that the exit code is 56. The code translates to a failure to receive network data.
1. Confirm TLS requests without client certificate also fail:
{{< 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'
$ 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' -k
000
command terminated with exit code 77
command terminated with exit code 35
{{< /text >}}
1. However, request will success if client certificate is provided
> This time, exit code is 35, which corresponds to a problem occurred somewhere in the SSL/TLS handshake.
1. Confirm TLS request with client certificate succeed:
{{< 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](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
for service identity, which offers stronger security than service name.
Refer to the [Istio identity docs](/docs/concepts/security/#istio-identity) for more information.
Thus the certificates used in Istio do not have service names, which is the information that `curl` needs to verify server identity.
As a result, we use `curl` option `-k` to prevent the `curl` client from aborting when failing to find
and verify the server name in the certificate the server provides, for example, `httpbin.ns.svc.cluster.local`.
1. Request from pod without sidecar. For this demo, let's install another `sleep` service 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 >}}
1. Wait after the pod status changes to `Running`, issue the familiar `curl` command. 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 >}}
> Istio uses [Kubernetes service accounts](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) as service identity, which
offers stronger security than service name (for more details, see [Istio identity](/docs/concepts/security/#istio-identity)). Thus, the certificates Istio uses do
not have service names, which is the information that `curl` needs to verify server identity. To prevent the `curl` client from aborting, we use `curl`
with the `-k` option. The option prevents the client from verifying and looking for the server name, for example, `httpbin.default.svc.cluster.local` in the
certificate provided by the server.
## 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 >}}

View File

@ -695,7 +695,7 @@ Also note that Istio itself *cannot securely enforce* that all the egress traffi
1. Check if you have [mutual TLS Authentication](/docs/tasks/security/mutual-tls/) enabled in Istio, following the
steps in
[Verifying Istios mutual TLS authentication setup](/docs/tasks/security/mutual-tls/#verifying-istio-s-mutual-tls-authentication-setup).
[Verify mutual TLS configuration](/docs/tasks/security/mutual-tls/#verify-mutual-tls-configuration).
If mutual TLS is enabled, make sure you create the configuration
items accordingly (note the remarks _If you have mutual TLS Authentication enabled in Istio, you must create..._).