diff --git a/content/en/docs/tasks/security/authentication/mtls-migration/index.md b/content/en/docs/tasks/security/authentication/mtls-migration/index.md index 1652ec5524..fb5fb6d694 100644 --- a/content/en/docs/tasks/security/authentication/mtls-migration/index.md +++ b/content/en/docs/tasks/security/authentication/mtls-migration/index.md @@ -7,104 +7,34 @@ aliases: - /docs/tasks/security/mtls-migration/ --- -This task shows how to migrate your existing Istio services' traffic from plain -text to mutual TLS without breaking live traffic. +This task shows how to ensure your workloads only communicate using mutual TLS as they are migrated to +Istio. -In the scenario where there are many services communicating over the network, it -may be desirable to gradually migrate them to Istio. During the migration, some services have Envoy -sidecars while some do not. For a service with a sidecar, if you enable -mutual TLS on the service, the connections from legacy clients (i.e., clients without -Envoy) will lose communication since they do not have Envoy sidecars and client certificates. -To solve this issue, Istio authentication policy provides a `PERMISSIVE` mode to solve -this problem. When `PERMISSIVE` mode is enabled, a service can take both HTTP -and mutual TLS traffic. +Istio automatically configures workload sidecars to use [mutual TLS](/docs/tasks/security/authentication/authn-policy/#auto-mutual-tls) when calling other workloads. By default, Istio configures the destination workloads using `PERMISSIVE` mode. +When `PERMISSIVE` mode is enabled, a service can accept both plain text and mutual TLS traffic. In order to only allow +mutual TLS traffic, the configuration needs to be changed to `STRICT` mode. -You can configure Istio services to send mutual -TLS traffic to that service while connections from legacy services will not -lose communication. Moreover, you can use the -[Grafana dashboard](/docs/tasks/observability/metrics/using-istio-dashboard/) to check which services are -still sending plaintext traffic to the service in `PERMISSIVE` mode and choose to lock +You can use the [Grafana dashboard](/docs/tasks/observability/metrics/using-istio-dashboard/) to +check which workloads are still sending plaintext traffic to the workloads in `PERMISSIVE` mode and choose to lock them down once the migration is done. ## Before you begin + + * Understand Istio [authentication policy](/docs/concepts/security/#authentication-policies) and related [mutual TLS authentication](/docs/concepts/security/#mutual-tls-authentication) concepts. * Read the [authentication policy task](/docs/tasks/security/authentication/authn-policy) to learn how to configure authentication policy. -* Have a Kubernetes cluster with Istio installed, without global mutual TLS enabled (e.g use the demo configuration profile as described in -[installation steps](/docs/setup/getting-started), or set the `global.mtls.enabled` installation option to false). +* Have a Kubernetes cluster with Istio installed, without global mutual TLS enabled (e.g use the demo configuration profile as described in [installation steps](/docs/setup/getting-started). -* Ensure that your cluster is in `PERMISSIVE` mode before migrating to mutual TLS. - Run the following command to check: - - {{< text bash >}} - $ kubectl get meshpolicy default -o yaml - ... - spec: - peers: - - mtls: - mode: PERMISSIVE - {{< /text >}} - - If the output is the same as above, you can skip the next step. - -* Run the following command to enable `PERMISSIVE` mode for the cluster. In general, this operation - does not cause any interruption to your workloads, but note the warning message below. - - {{< warning >}} - In `PERMISSIVE` mode, the Envoy sidecar relies on the - [ALPN](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation) value `istio` to - decide whether to terminate the mutual TLS traffic. If your workloads (without Envoy sidecar) - have enabled mutual TLS directly to the services with Envoy sidecars, enabling `PERMISSIVE` mode - may cause these connections to fail. - {{< /warning >}} - - {{< text bash >}} - $ kubectl apply -f - <}} - -{{< tip >}} -You can use the following command to show the mutual TLS configuration of all connections from one pod: - -{{< text bash >}} -$ istioctl authn tls-check -n -{{< /text >}} - -{{< /tip >}} - -The rest of this task is divided into two parts. - -* If you want to enable mutual TLS for your workloads one by one, refer to -[gradually enable mutual TLS for services](/docs/tasks/security/authentication/mtls-migration/#option-1-gradually-enable-mutual-tls-for-services), -which describes the process using simple examples. - -* If you want to enable mutual TLS for the entire cluster, refer to -[globally enable mutual TLS for the cluster](/docs/tasks/security/authentication/mtls-migration/#option-2-globally-enable-mutual-tls-for-the-cluster). - -## Option 1: gradually enable mutual TLS for services - -In this section, you can try out the migration process by creating sample workloads and modifying +In this task, you can try out the migration process by creating sample workloads and modifying the policies to enforce STRICT mutual TLS between the workloads. -### Set up the cluster +## Set up the cluster -* Create the following namespaces and deploy [httpbin]({{< github_tree >}}/samples/httpbin) and [sleep]({{< github_tree >}}/samples/sleep) with sidecars on both of them. - * `foo` - * `bar` - -* Create the following namespace and deploy [sleep]({{< github_tree >}}/samples/sleep) without a sidecar - * `legacy` +* Create two namespaces, `foo` and `bar`, and deploy [httpbin]({{< github_tree >}}/samples/httpbin) and [sleep]({{< github_tree >}}/samples/sleep) with sidecars on both of them: {{< text bash >}} $ kubectl create ns foo @@ -113,6 +43,11 @@ the policies to enforce STRICT mutual TLS between the workloads. $ kubectl create ns bar $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n bar $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n bar + {{< /text >}} + +* Create another namespace, `legacy`, and deploy [sleep]({{< github_tree >}}/samples/sleep) without a sidecar: + + {{< text bash >}} $ kubectl create ns legacy $ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy {{< /text >}} @@ -120,179 +55,88 @@ the policies to enforce STRICT mutual TLS between the workloads. * Verify setup by sending an http request (using curl command) from any sleep pod (among those in namespace `foo`, `bar` or `legacy`) to `httpbin.foo`. All requests should success with HTTP code 200. {{< text bash >}} - $ for from in "foo" "bar" "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.foo: %{http_code}\n"; done + $ for from in "foo" "bar" "legacy"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done sleep.foo to httpbin.foo: 200 + sleep.foo to httpbin.bar: 200 sleep.bar to httpbin.foo: 200 + sleep.bar to httpbin.bar: 200 sleep.legacy to httpbin.foo: 200 + sleep.legacy to httpbin.bar: 200 {{< /text >}} -* Also verify that there are no authentication policies or destination rules (except control plane's) in the system: +* Also verify that there are no authentication policies or destination rules (except control plane ones) in the system: {{< text bash >}} - $ kubectl get policies.authentication.istio.io --all-namespaces - NAMESPACE NAME AGE - istio-system grafana-ports-mtls-disabled 3m + $ kubectl get peerauthentication --all-namespaces + No resources found {{< /text >}} {{< text bash >}} $ kubectl get destinationrule --all-namespaces - NAMESPACE NAME HOST AGE - istio-system istio-multicluster-destinationrule *.global 35s - istio-system istio-policy istio-policy.istio-system.svc.cluster.local 35s - istio-system istio-telemetry istio-telemetry.istio-system.svc.cluster.local 33s + No resources found {{< /text >}} -### Configure clients to send mutual TLS traffic +## Lock down to mutual TLS by namespace -Configure Istio services to send mutual TLS traffic by setting `DestinationRule`. +After migrating all clients to Istio and injecting the Envoy sidecar, you can lock down workloads in the `foo` namespace +to only accept mutual TLS traffic. {{< text bash >}} -$ cat <}} -`sleep.foo` and `sleep.bar` should start sending mutual TLS traffic to `httpbin.foo`. And `sleep.legacy` still sends plaintext -traffic to `httpbin.foo` since it does not have sidecar thus `DestinationRule` does not apply. - -Now we confirm all requests to `httpbin.foo` still succeed. +Now, you should see the request from `sleep.legacy` to `httpbin.foo` failing. {{< text bash >}} -$ for from in "foo" "bar" "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.foo: %{http_code}\n"; done +$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done sleep.foo to httpbin.foo: 200 +sleep.foo to httpbin.bar: 200 sleep.bar to httpbin.foo: 200 -sleep.legacy to httpbin.foo: 200 +sleep.bar to httpbin.bar: 200 +sleep.legacy to httpbin.foo: 000 +command terminated with exit code 56 +sleep.legacy to httpbin.bar: 200 {{< /text >}} -You can also specify a subset of the clients' request to use `ISTIO_MUTUAL` mutual TLS in -[`DestinationRule`](/docs/reference/config/networking/destination-rule/). -After verifying it works by checking [Grafana to monitor](/docs/tasks/observability/metrics/using-istio-dashboard/), -then increase the rollout scope and finally apply to all Istio client services. - -### Lock down to mutual TLS - -After migrating all clients to Istio services and injecting the Envoy sidecar, we can lock down the `httpbin.foo` to only accept mutual TLS traffic. - -{{< text bash >}} -$ cat <}} - -Now, you should see the request from `sleep.legacy` fail. - -{{< text bash >}} -$ for from in "foo" "bar" "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.foo: %{http_code}\n"; done -sleep.foo to httpbin.foo: 200 -sleep.bar to httpbin.foo: 200 -sleep.legacy to httpbin.foo: 503 -{{< /text >}} - -If you can't migrate all your services to Istio (injecting Envoy sidecar), you have to stay at `PERMISSIVE` mode. +If you can't migrate all your services to Istio (i.e., inject Envoy sidecar in all of them), you will need to continue to use `PERMISSIVE` mode. However, when configured with `PERMISSIVE` mode, no authentication or authorization checks will be performed for plaintext traffic by default. We recommend you use [Istio Authorization](/docs/tasks/security/authorization/authz-http/) to configure different paths with different authorization policies. -### Clean up the example +## Lock down mutual TLS for the entire mesh -To remove all resources created in this section: +{{< text bash >}} +$ kubectl apply -n istio-system -f - << EOF +apiVersion: "security.istio.io/v1beta1" +kind: "PeerAuthentication" +metadata: + name: "default" +spec: + mtls: + mode: STRICT +EOF +{{< /text >}} + +Now, both the `foo` and `bar` namespaces enforce mutual TLS only traffic, so you should see requests from `sleep.legacy` +failing for both. + +{{< text bash >}} +$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.${to}:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done +{{< /text >}} + +## Clean up the example + +To remove all resources created in this task: {{< text bash >}} $ kubectl delete ns foo bar legacy Namespaces foo bar legacy deleted. +$ kubectl delete peerauthentication --all-namespaces --all {{< /text >}} - -## Option 2: globally enable mutual TLS for the cluster - -This section describes how to apply the configuration to enforce mutual TLS for a cluster. For more -details, see the -[Authentication policy](/docs/tasks/security/authentication/authn-policy/#globally-enabling-istio-mutual-tls-in-strict-mode) -task. - -{{< warning >}} -If you have special TLS configurations for your services or you have -services without Envoy sidecars, we recommend that you -[enable mutual TLS service by service](/docs/tasks/security/authentication/mtls-migration/#option-1-gradually-enable-mutual-tls-for-services). -{{< /warning >}} - -### Configure all clients to send mutual TLS traffic - -Run the following command to enable all Envoy sidecars to send mutual TLS traffic to the servers. - -{{< text bash >}} -$ kubectl apply -f - <}} - -The connections between services should not be interrupted. - -### Lock down to mutual TLS for the entire cluster - -Run the following command to enforce all Envoy sidecars to only receive mutual TLS traffic. - -{{< text bash >}} -$ kubectl apply -f - <}} - -The connections between services should not be interrupted. - -### Clean up global mutual TLS configuration - -Choose one of the following, depending on the mode you want to switch to: - -* To switch to `PERMISSIVE` mode for the cluster: - - {{< text bash >}} - $ kubectl apply -f - <}} - -* To disable the global mutual TLS and switch to plaintext only: - - {{< text bash >}} - $ kubectl delete meshpolicy default - $ kubectl delete destinationrule default -n istio-system - {{< /text >}}