Skip to main content
A mysql lane decodes the MySQL client/server protocol: the SQL a client sends over COM_QUERY, the statements behind every prepared-statement id, and the result sets the server returns in both the text and the binary encoding.
config.yaml

What the codec reads

A MySQL byte changes meaning with the negotiated capabilities and the command that produced the reply. The codec keeps one connection state across both directions.
  • Capabilities. The client’s handshake response decides whether result sets end with EOF or OK packets and whether the packet framing remains inspectable.
  • Command order. Clients pipeline commands. The codec pairs each reply with a FIFO entry, while skipping commands such as COM_STMT_CLOSE that have no reply.
  • Multi-statements. Connector/J and many ORMs send SELECT 1; DROP TABLE users as one COM_QUERY. The MySQL lexer handles backtick identifiers, # comments and backslash escapes before the codec classifies each statement.

Prepared statements and cursors

COM_STMT_EXECUTE carries a numeric statement id instead of SQL. The codec records the text from COM_STMT_PREPARE and uses it for policy and audit when the client executes that id. A server-side cursor returns column definitions when it opens, then sends binary rows through COM_STMT_FETCH without repeating those definitions. The codec retains one metadata copy for inspection and one for masking. It removes each copy after its walker passes the fetch that preceded COM_STMT_CLOSE or COM_STMT_RESET, even when the fetch lacks SERVER_STATUS_LAST_ROW_SENT. The codec refuses a fetch when it did not observe the cursor open. Forwarding such rows would leave column-based masking with no column names.

What it refuses

Several protocol features replace the framing or metadata the codec needs. The lane closes the connection when a client negotiates one: The codec closes the connection before opaque bytes reach the database. If it forwarded them, policy and audit would lose visibility without reporting the gap.

TLS on each leg

MySQL sends its greeting before either peer can request TLS. The client and database legs use separate settings. Use --ssl-mode=DISABLED for a client command that must work with either upstream mode. The lane removes CLIENT_SSL from the client greeting when upstream_tls is configured. A client using --ssl-mode=PREFERRED continues in plaintext. --ssl-mode=REQUIRED stops because the client-facing connection does not offer TLS. The lane handles the server-facing exchange itself when upstream_tls is present. It reads the greeting, sends a MySQL SSLRequest, verifies the certificate, and completes authentication inside TLS. The lane closes the connection when the server omits TLS support, certificate verification fails, or the handshake fails. It does not downgrade to plaintext.

RSA authentication on the client leg

MySQL clients using caching_sha2_password or sha256_password encrypt the password when the connection has no TLS. They choose one of two key flows:
  • A client with no saved key asks the lane for one. The lane generates an ephemeral key, decrypts the response, and sends the recovered password inside the verified upstream TLS connection.
  • A client with a saved key skips that request and sends ciphertext at once. Set mysql_auth_key_file to a stable RSA private key, then give the public half to the client.
Generate both files:
Mount mysql-auth.key into the Sidecar and keep it out of client containers. Mount mysql-auth.pub into clients that pin the key. The MySQL CLI uses --server-public-key-path:
Do not give clients the backend server’s public key. The Sidecar needs the matching private key so it can recover the password before it enters the upstream TLS session. A missing or mismatched relay key returns MySQL error 1045 (28000) and the Sidecar does not forward the ciphertext. The MySQL example stack generates this key pair, mounts each half on the correct side, and runs the client with --server-public-key-path. Confirm the database leg from the same client session:
MySQL returns a non-empty value when the database leg uses TLS. Remove the upstream_tls block when the backend connection must remain plaintext.

Masking

The codec re-frames result sets in both encodings, because the two share nothing:
  • Text protocol rows are length-encoded strings, and the codec rewrites them value by value.
  • Binary protocol rows, the encoding prepared statements return, carry a NULL bitmap and type-driven values. The codec rewrites string-typed columns and leaves a numeric column alone rather than corrupting it.
A NULL survives masking as a NULL: re-encoding it as an empty string would turn “no value” into “the empty string” and change what the client computes. Column rules match the names the server declared in the result set’s column definitions.

Denials

A denied statement returns a native ERR_Packet, and the session stays usable afterwards:
Dropping the socket instead would print “Lost connection to MySQL server during query”: an outage message for a policy decision, which sends the developer to support instead of to their own query.

The Envoy lane

Envoy’s MySQL filter parses no SQL, so the lane is plain tcp_proxy, same shape as the Postgres one with the cluster pointed at the mysql listener’s port:
envoy.yaml
Verify the lane from a client:

Next

Config File Reference

Every listener field, inheritance between lanes, and what startup refuses.

Data Masking

Strategies, entity types, and the column-versus-detection tradeoff.