Migrate your services to service mesh with permissive mTLS
Applications typically consist of many interdependent services spread across independent teams. Traditionally, adopting Consul service mesh required teams to coordinate and migrate all the services to the mesh at once. Otherwise, with transparent proxy enabled, services outside of the mesh will not have the mTLS certificates required to securely communicate with services inside the mesh.
Permissive mTLS mode makes it easier for teams to onboard and migrate their services to Consul. Instead of deploying and configuring ingress gateways and API gateways, operators can temporarily let sidecar proxies allow both mTLS and non-mTLS traffic. This essentially allows any non-mesh services to communicate with services inside the mesh.
In this tutorial, you will:
- Deploy a Kubernetes cluster with Terraform
- Deploy Consul and a demo application on the cluster
- Connect non-mesh services to Consul services using permissive mTLS
- Migrate services to Consul and configuring intentions
- Re-secure the mesh by restricting permissive mTLS
Scenario overview
HashiCups is a coffee shop demo application. It has a microservices architecture and uses Consul service mesh to securely connect the services. At the beginning of this tutorial, the HashiCups backend (products-api
and postgres
) will be on Consul service mesh. First, you will use permissive mTLS to enable non-mesh traffic (public-api
) to seamlessly access mesh services (products-api
). Then, you will migrate the non-mesh services to Consul then disable permissive mTLS to harden your security posture.
HashiCups uses the following microservices:
- The
nginx
service is an NGINX instance that routes requests to the frontend service and serves as a reverse proxy to the public-api service. - The
frontend
service provides a React-based UI. - The
public-api
service is a GraphQL public API that communicates with the products-api and the payments services. - The
product-api
service stores the core HashiCups application logic, including authentication, coffee (product) information, and orders. - The
postgres
service is a Postgres database instance that stores user, product, and order information. - The
payments
service is a gRPC-based Java application service that handles customer payments.
Prerequisites
If you are not familiar with Consul's core functionality, refer to the Consul Getting Started tutorials collection (VMs, Kubernetes) first.
For this tutorial, you will need:
- An AWS account configured for use with Terraform
- aws-cli v2.0 or later
- kubectl v1.21 or later
- git v2.0 or later
- terraform v1.2 or later
- consul-k8s v1.2.0 or later
Tip
Permissive mTLS is only available in Consul v1.16 and later. As a result, you must have consul-k8s
v1.2.0 or later. Since Consul v1.16 and consul-k8s
v1.2 is available as a release candidate, you need to download the binaries from HashiCorp Releases.
Confirm you have the consul-k8s
v1.2.0 or later.
Clone example repository
Clone the GitHub repository containing the configuration files and resources.
Change into the directory with the newly cloned repository.
This repository has the following:
- The Terraform configuration files in the root directory deploys an EKS cluster in
us-east-2
. You will deploy Consul and HashiCups (the demo application) onto this Kubernetes cluster. - The
k8s-yamls
directory contains YAML configuration files that support this tutorial. - The
hashicups
directory contains YAML configuration files for deploying HashiCups.
Deploy Consul and demo application
Initialize your Terraform configuration to download the necessary providers and modules.
Then, create the infrastructure. Confirm the run by entering yes
. This will take about 15 minutes to deploy your infrastructure.
Configure kubectl
Now that you have deployed the Kubernetes cluster, configure kubectl
to interact with it.
Deploy Consul
You will now deploy Consul on your Kubernetes cluster with consul-k8s
. By default, Consul deploys into its own dedicated namespace (consul
). The Consul installation will use the Consul Helm chart file in the k8s-yaml
directory. Permissive mTLS requires Consul v1.16.0+. Deploying Consul on each Kubernetes cluster should only take a few minutes.
Deploy Consul and confirm the installation with a y
.
Warning
Make sure to run the correct version of consul-k8s
otherwise the deployment will fail. This tutorial uses 1.2.x. Refer to the consul-k8s CLI documentation on how to install a specific version on your system.
Verify that you have installed Consul by inspecting the Kubernetes pods in the consul
namespace.
Confirm that the Helm chart version is 1.2.0
or later.
Deploy HashiCups
Deploy the HashiCups services.
Verify that you have deployed the services on Kubernetes.
List the Consul services. Notice that only the backend services products-api
and postgres
are registered to Consul since they are currently the only HashiCups services within the service mesh.
Open HashiCups in your browser. In a new terminal, port-forward the nginx
service to port 8080
.
Open localhost:8080 in your browser to view the HashiCups UI. Notice that it displays no products, since the public-api
cannot connect to the products-api
.
Connect non-mesh services to Consul services
By default, Consul's sidecar proxies reject all non-mTLS traffic. This prevents services outside Consul from connecting to services inside the mesh unless you set up ingress or API gateways. However, setting up and configuring ingress and API gateways increase service mesh onboarding complexity. Permissive mTLS configures sidecar proxies to temporarily accept both incoming mTLS and incoming non-mTLS connections to simplify service mesh onboarding for your services.
In order to enable your sidecar proxies to accept both mTLS and non-mTLS traffic, you need to:
- Allow permissive mTLS at the mesh level: Configure the service mesh to allow the use of permissive mTLS with the
mesh
configuration entry. - Enable permissive mTLS at the service level: Once you have configured your mesh to allow the use of permissive mTLS, set mTLS mode in the
service-defaults
configuration entry. This overrides theproxy-defaults
setting.
Allow permissive mTLS at mesh level
The following file configures the Consul datacenter (partition) to allow permissive mTLS:
Enable permissive mTLS by applying the mesh
configuration entry.
Set permissive mTLS at service level
The following file configures the products-api
service to accept both mTLS and non-mTLS traffic:
Enable products-api
to allow non-mTLS traffic.
You may need to re-forward the port of the nginx
service.
Open localhost:8080 in your browser to view the HashiCups UI. It should show a list of coffees.
Notice that even though there is a deny-all intention on the Consul datacenter, public-api
was able to connect to products-api
. Intentions only take effect for mTLS connections.
Warning
We recommend that you disable permissive mTLS mode after onboarding services to prevent non-mTLS connections to the service. Intentions are not enforced and encryption is not enabled for non-mTLS connections.
Migrate non-mesh services into Consul service mesh
Now, you will migrate the remaining HashiCups services to Consul service mesh.
First, the each service deployment definition, update the consul.hashicorp.com/connect-inject
annotation from false
to true
. You will need to do this for four files:
Once you have done this to all four files, apply the changes. In addition, you will apply a file that creates intentions between these services.
Verify the services appear in Consul.
Set up intentions
Create intentions between the public-api
and products-api
to allow traffic communication between the services.
Restrict permissive mTLS at service level
Restrict products-api
to only accept mTLS traffic.
In k8s-yamls/service-defaults-products-api.yaml
, update mutualTLSMode
to strict
.
Then, apply the changes.
Restrict permissive mTLS at mesh level
In k8s-yamls/mesh-config-entry.yaml
, update allowEnablingPermissiveMutualTLS
to false
.
Disallowing permissive mTLS at the mesh level does not impact permissive mTLS settings at the service level (proxy-defaults
and service-defaults
, MutalTLSMode=permissive
). This lets organizations disallow permissive mTLS moving forward, while services currently in permissive mode can eventually migrate to the mesh at their own pace.
Then, apply the changes.
Confirm that the HashiCups services can still connect to each other. You may need to re-forward the port of the nginx
service.
Open localhost:8080 in your browser to view the HashiCups UI. It should show a list of coffees.
Clean up environment
To destroy the environment, first uninstall Consul from your Kubernetes cluster. Confirm with a y
.
Note
Before running the terraform destroy
command, make sure that all the services in the Consul namespace have been terminated. If you try to perform a destroy before that, your Terraform run will fail and you will have to restart it.
Then, destroy the supporting infrastructure Enter yes
to confirm the destroy operation.
Next steps
In this tutorial, you used the Consul permissive mTLS to let Consul services accept both non-mTLS and mTLS traffic. In the process, you learned how to seamlessly connect non-service mesh traffic to Consul service mesh and migrate services to Consul.
Feel free to explore these tutorials and collections to learn more about Consul service mesh, microservices, and Kubernetes security.