Secure Service Communication with Consul Service Mesh and Envoy
Consul service mesh secures service-to-service communication with authorization and encryption. Applications can use sidecar proxies in a service mesh configuration to automatically establish TLS connections for inbound and outbound connections without being aware of the network configuration and topology. In addition to securing your services, Consul service mesh can also intercept data about service-to-service communications and surface it to monitoring tools.
In this tutorial, you will register two services and their sidecar proxies in the Consul catalog. You will then start the services and sidecar proxies. Finally, you will demonstrate that the service-to-service communication is going through the proxies by stopping traffic with an "intention".
While this tutorial uses elements that are not suitable for production environments – Consul dev agents and mock services – it will teach you the common process for deploying your own services using Consul service mesh. At the end of this tutorial, we also present additional information about adapting this process to more production-like environments.
Prerequisites
A running Consul server with Consul service mesh enabled. For this tutorial you will use a local dev agent, which enables Consul service mesh by default.
Envoy binary installed on the Consul agents. Consul includes a built-in Layer 4 (L4) proxy for testing and development but also offers first class support for Envoy as a sidecar proxy. This tutorial provides commands for both, with Envoy being the recommended proxy.
Two service applications which need to securely communicate. For this tutorial you will use two example service applications, a counter service and a dashboard. Download and unzip the executables to follow along.
Interactive tutorial
Launch Terminal
This tutorial includes a free interactive command-line lab that lets you follow along on actual cloud infrastructure.
Note
The interactive tutorial does not use the exact commands listed in this tutorial. You can consider the lab as a way to extend the concepts explained in this tutorial.
Install Envoy on the agent
Note
Consul ships with a built-in proxy that you can use to test the Consul service mesh, as well as follow the tutorial. The built-in proxy should only be used where it is not possible to run Envoy, such as an older or unsupported operating system.
You can obtain container-based builds of Envoy directly from the Envoy Website or obtain a package of Envoy binary builds from a third-party project, such as func-e.io.
For your version of Consul,
we recommend installing the latest compatible Envoy version listed in the
Consul documentation.
You can check your version of Consul with the command consul version
.
Set an environment variable with the latest compatible Envoy version string:
Issue the following command to download and install the func-e
utility, which fetches and installs Envoy.
Note
If your machine uses an ARM64 chip (i.e., Apple M1), set the following environment variable before proceeding to the next step.
Issue the following command to download the specified version of Envoy.
Open a new tab in your terminal or stop the Envoy process by pressing Control+C to use the current tab.
Copy the envoy
binary to a location in your $PATH
. This enables Consul to automatically start Envoy without specifying the binary location.
Verify that Envoy is in your $PATH
by running the following command.
Verify Consul agent health
To ensure that Consul is running and accessible from the command line, use the
consul members
command to verify your agent status.
If you receive an error message, verify that a local Consul dev agent is running and try again.
Register the services and sidecar proxies
Services have to be registered with Consul. Consul shares this information around the datacenter so that operators or other services can determine the location of a service. Consul service mesh also uses service registrations to determine where to send proxied traffic to.
There are several ways to register services in Consul:
directly from a Consul-aware application
from an orchestrator, like Nomad or Kubernetes
using configuration files that are loaded at node startup
using the API to register them with a JSON or HCL specification
using the CLI to simplify this submission process
For this tutorial, we will use the consul service register
CLI
command to load them into the catalog.
Create the counting service definition
First, define the Counting service and its sidecar proxy in a file named
counting.hcl
. The definition should include the name of the service, the port
the service listens on, and a connect block with the sidecar_service block.
This block is empty so Consul will use default parameters. The definition also
includes an optional service health check.
Services and sidecar proxies can be defined in either HCL or JSON. There is a JSON version of the service definition in the demo-consul-101 project.
Create the dashboard service definition
Create the Dashboard service and proxy definition in the same way. First,
create a file named dashboard.hcl
.
There is a JSON version of the service definition in the demo-consul-101 project.
Note that the dashboard definition also includes an upstream block. Upstreams
are ports on the local host that will be proxied to the destination service.
The upstream block's local_bind_port
value is the port your service will
communicate with to reach the service you depend on. The destination name is the
Consul service name that the local_bind_port will proxy to.
In our scenario, the dashboard service depends on the counting service. With
this configuration, when dashboard service connects to localhost:5000
it is
proxied across the service mesh to the counting service.
When applying these concepts to your production environments, verify that the values in the proxy configuration's upstreams
block correctly specify the destination service and port number to avoid potential issues.
For example, specifying the same values for the listener's port and target service will result in a proxy loop that will cause the server to crash.
Refer to the Upstream Configuration Reference for additional information.
Register the services and proxies
Finally, you can submit the service definitions to your Consul agent. If you are using the JSON definitions, ensure that the filenames end in ".json" instead of ".hcl".
Challenge
After completing the tutorial, try doing it again using one of the other service registration mechanisms mentioned earlier in the tutorial to register the services.
Verify the services are registered
Now that you have registered your services and sidecar proxies, run consul catalog services
to verify that they are present.
From the output you can notice that two extra services *-sidecar-proxy
are
automatically registered alongside the one defined in the configuration files.
Create a Consul intention
Intentions define authorization policies for services in the service mesh and are used to control which services may establish connections. The default intention behavior is defined by the default ACL policy.
In this tutorial, this step is not necessary since the default ACL policy for the dev agent is "allow all", so connections across the services in the service mesh are automatically allowed as well. However, you will create explicit intentions as a part of deploying service mesh enabled services.
Best Practice
Creating an explicit intention helps protect your service
against changes to the implied permissions. For example, a change in
default_policy
or the introduction of a global deny-all intention would impact
services without explicit intentions defined.
First, create a file for a config entry definition named intention-allow-config.hcl
.
From the same directory where you saved this file, run the following command on your terminal to initialize these intention rules.
Note To learn more about intentions, feel free to check out the Service Intentions Docs.
Start the services and sidecar proxies
Now that you have created all the necessary configuration to describe your
service's connections, it's time to start your services and their sidecar
proxies. We are using the &
operator to run the services as background tasks.
However, because they write to the console, it's best to run them in their own
shell session.
Establish the local URL and start the dashboard service.
Start the counting service.
Next, start the sidecar proxies that will run as sidecar processes along with the service applications.
Start the Envoy sidecar proxy for the counting service.
Start the Envoy sidecar proxy for the dashboard service.
Note
The -sidecar-for
argument takes a Consul service ID, not a service name.
Check the dashboard interface
Open a browser and navigate to http://localhost:9002
.
You should see a screen similar to the following. There is a connection indicator in the top right that will turn green and say "Connected" when the dashboard service is in communication with the counting service.
Warning
If you use curl
, or a browser that does not support
Javascript, the status indicator will always say "Disconnected", even
if the dashboard service is connected to the counting service.
If your application is not connected, check that the Counting service is running
and healthy by viewing it in the Consul UI at http://localhost:8500
.
Test the sidecar proxy connections
To test that traffic is flowing through the sidecar proxies, you will control traffic with an intention.
First, deny the Dashboard service access to the Counting service.
You'll do this by overwritting the previous intention with intention-deny-config.hcl
.
Create the file, then apply the new configuration.
Refresh your browser, the connection indicator in the Dashboard UI will now say "Disconnected"
You can restore communication between the services with the command you ran earlier to initialize your intentions.
Back in the browser, verify that the dashboard reconnects to the counting service.
Clean up
Once you are done with this tutorial, you can begin cleaning up by closing the terminal in which your counting-service, dashboard-service, and proxies are running. This should automatically stop these processes.
Delete the intention from Consul.
Deregister the counting service.
Deregister the dashboard service.
Extend these concepts
When you want to apply this learning to a proof-of-concept or production environment, there are some additional considerations.
Enable Connect and gRPC
When a Consul server is started with the -dev
flag, it will automatically
enable Consul service mesh and provide a default port for gRPC communication.
These have to be configured explicitly for regular Consul agents.
The connect
section in the configuration is only needed for server agents.
Client agents only need to configure the gRPC port.
Next steps
Now that you have completed this tutorial, you have familiarized yourself with a basic service mesh enabled service deployment. You created and registered Consul service definitions that describe how two services communicate with each other. After starting the application and sidecar proxies, you used Consul service mesh intentions to control traffic flow between services. Finally, you learned about additional requirements required to take the concepts to a proof-of-concept environment.