TCP Proxying and Protocol Detection

Linkerd is capable of proxying all TCP traffic, including TLS connections, WebSockets, and HTTP tunneling.

In most cases, Linkerd can do this without configuration. To do this, Linkerd performs protocol detection to determine whether traffic is HTTP or HTTP/2 (including gRPC). If Linkerd detects that a connection is HTTP or HTTP/2, Linkerd will automatically provide HTTP-level metrics and routing.

If Linkerd cannot determine that a connection is using HTTP or HTTP/2, Linkerd will proxy the connection as a plain TCP connection, applying mTLS and providing byte-level metrics as usual.

Note

Client-initiated HTTPS will be treated as TCP, not as HTTP, as Linkerd will not be able to observe the HTTP transactions on the connection.

Configuring protocol detection

In some cases, Linkerd’s protocol detection cannot function because it is not provided with enough client data. This can result in a 10-second delay in creating the connection as the protocol detection code waits for more data. This situation is often encountered when using “server-speaks-first” protocols, or protocols where the server sends data before the client does, and can be avoided by supplying Linkerd with some additional configuration.

Note

Regardless of the underlying protocol, client-initiated TLS connections do not require any additional configuration, as TLS itself is a client-speaks-first protocol.

There are two basic mechanisms for configuring protocol detection: opaque ports and skip ports. Marking a port as opaque instructs Linkerd to proxy the connection as a TCP stream and not to attempt protocol detection. Marking a port as skip bypasses the proxy entirely. Opaque ports are generally preferred (as Linkerd can provide mTLS, TCP-level metrics, etc), but crucially, opaque ports can only be used for services inside the cluster.

By default, Linkerd automatically marks some ports as opaque, including the default ports for SMTP, MySQL, PostgresQL, and Memcache. Services that speak those protocols, use the default ports, and are inside the cluster do not need further configuration.

The following table summarizes some common server-speaks-first protocols and the configuration necessary to handle them. The “on-cluster config” column refers to the configuration when the destination is on the same cluster; the “off-cluster config” to when the destination is external to the cluster.

ProtocolDefault port(s)On-cluster configOff-cluster config
SMTP25, 587noneskip ports
MySQL3306noneskip ports
MySQL with Galera replication3306, 4444, 4567, 4568noneskip ports
PostgreSQL5432noneskip ports
Redis6379noneskip ports
ElasticSearch9300noneskip ports
Memcache11211none*skip ports

\ No configuration is required if the standard port is used. If a non-standard port is used, you must mark the port as opaque.*

Marking a port as opaque

You can use the config.linkerd.io/opaque-ports annotation to mark a port as opaque. This instructions Linkerd to skip protocol detection for that port.

This annotation can be set on a workload, service, or namespace. Setting it on a workload tells meshed clients of that workload to skip protocol detection for connections established to the workload, and tells Linkerd to skip protocol detection when reverse-proxying incoming connections. Setting it on a service tells meshed clients to skip protocol detection when proxying connections to the service. Set it on a namespace applies this behavior to all services and workloads in that namespace.

Note

Since this annotation informs the behavior of meshed clients, it can be applied to services that use server-speaks-first protocols even if the service itself is not meshed.

Setting the opaque-ports annotation can be done by using the --opaque-ports flag when running linkerd inject. For example, for a MySQL database running on the cluster using a non-standard port 4406, you can use the commands:

  1. linkerd inject mysql-deployment.yml --opaque-ports=4406 \
  2. | kubectl apply -f -
  3. linkerd inject mysql-service.yml --opaque-ports=4406 \
  4. | kubectl apply -f -

Note

Multiple ports can be provided as a comma-delimited string. The values you provide will replace, not augment, the default list of opaque ports.

Skipping the proxy

Sometimes it is necessary to bypass the proxy altogether. For example, when connecting to a server-speaks-first destination that is outside of the cluster, there is no Service resource on which to set the config.linkerd.io/opaque-ports annotation.

In this case, you can use the --skip-outbound-ports flag when running linkerd inject to configure resources to bypass the proxy entirely when sending to those ports. (Similarly, the --skip-inbound-ports flag will configure the resource to bypass the proxy for incoming connections to those ports.)

Skipping the proxy can be useful for these situations, as well as for diagnosing issues, but otherwise should rarely be necessary.

As with opaque ports, multiple skipports can be provided as a comma-delimited string.