Consul Connect, from Service Discovery to Service Mesh

Mikael Gibert
Teemo Tech Blog
Published in
5 min readJul 9, 2018

--

Service Mesh are all the rage, it is a growing market and a buzzword as popular as Serverless or Kubernetes! During our last Hackathon at Teemo, we tried Consul Connect, which is a big release for Consul as it gives Service Mesh capabilities to this popular Service Discovery!

Consul has now Service Mesh capabilities

Features

Consul Connect features are classic in the Service Mesh world: it connects services, ensures traffic encryption, service identity and can allow or deny traffic depending on the source and destination service identities.

Service Mesh features are integrated into Consul binary. So if you use to run a Consul agent on your instances, it is possible to benefit from Connect capabilities without changing your application. See Proxies.

Connect also has an API so applications more Cloud Native (which do not depend on a local agent) can benefit from its capabilities. See Native Integration.

How does it work ?

Connect can complete the previously exposed feature set thanks to Proxies. Each time you register a service and configure it to be Connect compatible, you configure a proxy for this service. It means that services will connect through proxies instead of calling each other directly.

The connection is using TLS 1.2 with mutual authentication which ensures Traffic Encryption and Service Authentication. If the connection is allowed, you can consider this as an application level firewall, the remote proxy will terminate TLS connection and forward request to its local service.

As you can see, proxies are the central pieces of Consul Connect, as they are the only mechanism to guarantee Service Identities and Traffic Encryption.

Show me the code!

Application

We will deploy two Hello World services written in Python Flask.

from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"

Without Connect

Here is a couple of services connected using standard Consul (understand pre-Connect) mechanisms (e.g Service Discovery).

Service A, which is registered to Consul Service Registry using a JSON definition file in its local agent configuration path.

{
"services": [
{
"name": "service-A",
"port": 80
}
]
}

Service B, which is registered to Consul Service Registry using a JSON definition file in its local agent configuration path.

{
"services": [
{
"name": "service-B",
"port": 80
}
]
}

Service A talks to Service B using service-B.service.consul:80 (using an A record, or find its port dynamically using an SRV record).

With Connect

Here is the same couple of services connected using Connect, so they can authenticate each other and encrypt traffic without changing a single line of code.

Service A now must define a proxy configuration for Service B. It will start a local proxy on port 12000 that can forward requests to real Service B.

{
"services": [
{
"name": "service-A",
"port": 80,
"connect": {
"proxy": {
"config": {
"upstreams": [
{
"destination_name": "service-B",
"local_bind_port": 12000
}
]
}
}
}
}
]
}

Service B, which has no upstream dependencies, has a simpler configuration: it only needs to say it exposes a local proxy to be Connect compatible.

{
"services": [
{
"name": "service-B",
"port": 80,
"connect": {
"proxy": {}
}
}
]
}

With a small change of configuration at Consul side, we were able to move from this:

Without Connect

To the schema below, which let us expose Service A and B only on local interface (127.0.0.1) and their access controlled by the proxy.

With Connect

With Connect, Service A talks to Service B by using its local proxy and related upstream dependencies.

We are now able to connect Service B from Service A by reaching http://localhost:12000. See how we fail to establish a direct connection to our target service and succeed to establish a connection through its proxy.

$ curl http://service-B.service.consul:80
curl: (6) Could not resolve host: service-B.service.consul
$ curl http://localhost:12000
Hello World

If we set a rule to forbid traffic from service-A to service-B using intentions, we obtain:

$ curl http://localhost:12000curl: (7) Failed to connect to localhost port 12000: Connection refused

Sniffing network on Service B side to be sure that everything works as expected, we obtain this capture:

71 5.057093 10.132.0.9 10.132.0.5 TCP 74 41750 → 20165 [SYN] Seq=0 Win=28400 Len=0 MSS=1420 SACK_PERM=1 TSval=200247 TSecr=0 WS=128
72 5.057210 10.132.0.9 10.132.0.5 TCP 66 41750 → 20165 [ACK] Seq=1 Ack=1 Win=28416 Len=0 TSval=200247 TSecr=200384
73 5.057288 10.132.0.9 10.132.0.5 TLSv1.2 201 Client Hello
74 5.057792 10.132.0.9 10.132.0.5 TCP 66 41750 → 20165 [ACK] Seq=136 Ack=958 Win=30336 Len=0 TSval=200248 TSecr=200384
75 5.058715 10.132.0.9 10.132.0.5 TLSv1.2 924 Certificate, Client Key Exchange, Certificate Verify, Change Cipher Spec, Encrypted Handshake Message76 5.060651
76 5.060651 10.132.0.9 10.132.0.5 TLSv1.2 173 Application Data
77 5.063003 10.132.0.9 10.132.0.5 TLSv1.2 97 Encrypted Alert
78 5.063018 10.132.0.9 10.132.0.5 TCP 66 41750 → 20165 [FIN, ACK] Seq=1132 Ack=1205 Win=32256 Len=0 TSval=200249 TSecr=200385
79 5.063184 10.132.0.9 10.132.0.5 TCP 54 41750 → 20165 [RST] Seq=1133 Win=0 Len=0
80 5.063189 10.132.0.9 10.132.0.5 TCP 54 41750 → 20165 [RST] Seq=1133 Win=0 Len=0

Conclusion

We just played with Connect for one day during our quarterly hackathon, but we were seduced by the simplicity to enable and use with an existing Consul Cluster. It gives us several benefits such as internal HTTPS, Services Mutual Authentication and Authorization. In addition, Intention API is a simple way to draw a Service Interaction Map.

Kudos to the Hashicorp team for their reactiveness ! When we tried it, we found a bug on how Consul parses configuration. However, it took them less than a day to fix it!

--

--