TL;DR

See example code here.

What is TCP-MD5?

Although it may seem dry, a great explanation can be found in RFC 2385 where TCP-MD5 is defined. The abstract describes it as a TCP option (there are others, like timestamps, and selective ACKs) which adds an MD5-based signature to each packet - or segment, in proper protocol terminology. This incorporates the source and destination address as well as the actual payload data. This signature requires a preshared key known to the sender and receiver, preventing any man in the middle from altering or injecting packets into the stream.

Why would anyone use this?

When a connection is protected by TCP-MD5, only senders and receivers who know the preshared key can communicate. Packets without the correct signature are dropped and ignored at either end. This prevents MITM attacks and the protocol has been used for years to protect the integrity of BGP sessions on the internet.

Even though the MD5 algorithm is no longer safe to use for purposes like verifying passwords, when used in the context of TCP-MD5 to protect the integrity and ensure the authenticity of TCP streams it can still offer some protection from certain attacks. For example, if the listening TCP socket for SSH on a server used TCP-MD5, it would not be found by port scanners because the packets sent would lack the required signature. This can be an alternative to port knocking. It is worth saying, however, that neither port knocking nor TCP-MD5 should be considered adequate protection for any service on their own. It can be useful as an additional layer of defense though.

If I had to come up with a killer reason for using this, it would be protection from forged TCP resets. That is where an attacker injects a RST packet into the connection stream, causing the recipient to end the connection. The Great Firewall of China has used (PDF link) this attack to censor the internet by breaking access to some sites.

Another advantage is that no specific application support is needed. You could wrap connections in an stunnel-like wrapper for TCP-MD5.

What should I be aware of?

Aside from the concerns around the MD5 digest algorithm, using TCP-MD5 is not without its drawbacks. Support and documentation are quite lacking but it works fine on Linux with kernel 4.13+ (I tested my code below on Ubuntu 18.04). Remember that this only provides a signature on each packet - it does not encrypt any data. Use a secure application layer protocol on top of TCP to encrypt data, such as TLS or SSH. That way, you will gain protection from port scanners and forged RST packets while maintaining confidentiality.

You will need to consider key rotation yourself. As it says in the RFC, you can change the key during a connection and it will work fine as long as the other end rotates to the same new key at the same time (but be careful with retransmitted packets).

How can I start using it in my application?

If you are using Golang, check out my gist to see a client and server example. You need to call setsockopt on a TCP socket to enable the option with a secret key, which has to be configured on both the client and server ends. To learn more and see an example in C++, visit Thomas Habets' blog here.