Enforce a Zero-trust Network with Consul Service Mesh
Injecting the service mesh sidecars is the first step in configuring a zero-trust network and ensuring that the communication between your services is automatically authenticated and encrypted using mutual TLS (mTLS). The second step is to configure authorization policies for inter-service communications.
In this tutorial, you will secure service mesh traffic using Consul intentions.
- A Kubernetes cluster
- kubectl to interact with your Kubernetes cluster and connect to Consul containers.
- This tutorial assumes you have completed the steps in Getting Started with Consul Service Mesh for Kubernetes. Your environment will need to configured according to the instructions in that tutorial in order to be able to test the commands in this tutorial.
Before microservices, authorization of inter-service communication was primarily enforced using firewall rules and routing tables. Consul simplifies the management of inter-service authorization with intentions. Intentions allow operators to define service-to-service communication permissions by service name. This paradigm is a vastly improved operator user experience compared to firewall rules and routing tables. Intentions are one of the cornerstones of zero-trust networking in Consul.
If you have been following along with the tutorials in this collection, you have not enabled Consul ACLs. The default policy for intentions in an environment where ACLs are not enabled is to permit all traffic. This means all the services deployed to the service mesh will be allowed communicate with each other by default. If Consul ACLs are enabled in your environment, then the default intentions policy is inherited from the default ACL policy.
Create a deny all intention
The first intention you will create changes the allow all policy where all traffic is allowed unless denied in specific rules, to a deny all policy where all traffic is denied and only specific connections are enabled.
To create an intention you can register it with a CRD using
you downloaded a git repository.
From the root of that repository, change directories into the directory that contains
the files you will use for this tutorial.
$ cd service-mesh/zero-trust-network
In that directory, you will find two files. The file named
ServiceIntentions CRD that will change your service mesh from a default allow
environment to a default deny environment. This means that no services will be
able to communicate with each other, unless you explicitly allow them to communicate
by defining specific intentions. The contents of the
deny-all.yaml file are listed
here for your convenience.
apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: name: deny-all spec: destination: name: '*' sources: - name: '*' action: deny
By specifying the wildcard character (*) in the
destination field this intention
will prevent all service-to-service communication, therefore, traffic between the
previously deployed HashiCups services will now be prevented.
kubectl to apply the deny all intention.
$ kubectl apply -f deny-all.yaml serviceintentions.consul.hashicorp.com/deny-all created
Now you can use the Consul UI to verify that the intention was created. If you
have closed your
port-forward session for the Consul UI from the previous tutorial,
you can re-open it now with the following command.
$ kubectl port-forward service/consul-ui 18500:80 --address 0.0.0.0
Open the Consul UI by visiting http://localhost:18500 in a separate tab and navigate
Intentions tab in the Consul UI. You can verify the new intention is now
present in the page.
If you have closed your
port-forward session for the HashiCups demo application
from the previous tutorial, you can re-open it now with the following command.
$ kubectl port-forward service/frontend 18080:80 --address 0.0.0.0
Check the UI for the
frontend service, http://localhost:18080.
You will notice that a default error message is displayed.
Allow service communication with intentions
Once you have defined the default policy as deny all, you can authorize
traffic between the different HashiCups services by defining a
CRD for each required service interaction. The
service-to-service.yaml file in
your current working directory has all the necessary YAML configuration, and has
been included here for you to review.
apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: name: frontend-to-public-api spec: destination: name: public-api sources: - name: frontend action: allow --- apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: name: public-api-to-product-api spec: destination: name: product-api sources: - name: public-api action: allow --- apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions metadata: name: product-api-to-postgres spec: destination: name: postgres sources: - name: product-api action: allow
This YAML can be used to create the following intentions:
Once created, these intentions will allow communication between the different services included in the HashiCups demo application.
kubectl to create the intentions.
$ kubectl apply -f service-to-service.yaml serviceintentions.consul.hashicorp.com/frontend-to-public-api created serviceintentions.consul.hashicorp.com/public-api-to-product-api created serviceintentions.consul.hashicorp.com/product-api-to-postgres created
Check the Intentions tab in the Consul UI to verify the new intentions were created.
Check the UI for the HashiCups application at http://localhost:18080. You will notice that the HashiCups UI now reachable again.
Understand the anatomy of an intention
Intentions control which services can communicate with each another and are enforced by the sidecar proxy on inbound connections. The identity of the inbound service is verified by its TLS client certificate. The sidecar proxy then checks if an intention exists that authorizes the inbound service to communicate with the destination service. If the inbound service is not authorized, the connection will be terminated.
An intention has four parts:
- Source service - Specifies the service that initiates the communication. It can be
the full name of a service or
*to refer to all services.
- Destination service - Specifies the service that receives the communication. This
will be the upstream you configured in your service definition. It can be the
full name of a service or
*to refer to all services.
- Permission - Defines whether the communication between source and destination is
permitted. This can be set to either
- Description - Optional metadata field to associate a description with an intention.
For both source and destination, the Consul UI will provide you with a dropdown that lists all the services inside your service mesh. In case you want to define intentions for services that are not already deployed, you can add any service name into the text box, and it will be applied to future services with that name.
In this tutorial, you learned how to create intentions using CRDs. Using intentions, you were able to define traffic rules for your service mesh without the need to manually add firewall rules or use any other traffic shaping software.
In the next tutorial, Manage Traffic with Consul Service Mesh, you will be introduced to the comprehensive traffic management features that Consul provides at the proxy, service, and WAN levels.