mssql lane decodes Microsoft SQL Server’s TDS protocol. Envoy ships no TDS filter of any kind, so an Envoy+OPA layer on its own polices none of this traffic. Postgres at least gets table-and-verb metadata from Envoy’s contrib filter; SQL Server gets nothing.
config.yaml
What the codec reads
A TDS message may span several packets, and the codec reassembles them before parsing: a statement classified from a fragment is the exact failure a policy cannot tolerate. Two message types carry SQL:
The codec hands a batch to the classifier whole and splits it with T-SQL’s own lexical rules (
[ opens a quoted identifier there and is an array subscript in PostgreSQL, so one dialect cannot serve both).
Integrated authentication passes through untouched
TDS gives the SSPI exchange its own packet type (0x11) and the login its own (0x10), so Kerberos works through this codec with no Kerberos code: the AP-REQ the client’s OS minted is opaque bytes in a packet type the codec forwards verbatim, and inspection begins at the first SQLBatch after it. The protocol’s own message typing marks the boundary, so the codec never guesses where login ended. Kerberos and SQL Server covers the full deployment.
The one server reply it refuses
A routingENVCHANGE, the server redirecting the client to another host, would take the connection to a socket the relay does not hold. The codec refuses it and the lane closes the connection rather than forwarding a working bypass.
TLS on each leg
The TDS version the client speaks decides who terminates its TLS, and with it the whole deployment shape:
The hop from the lane to SQL Server stays plaintext: the Linux build accepts no TLS shape the Sidecar can originate, so leave
upstream_tls unset and keep that hop inside the pod or host boundary. The reasoning and every negotiation detail live in Kerberos and SQL Server.
Masking
The codec re-frames TDS result sets: it reads each row against theCOLMETADATA the server declared, rewrites matching values, and rebuilds the token stream around them. Column rules match those declared names.
One limit: if a result set carries a column type whose wire length the codec cannot compute, masking stops for that connection rather than guessing at byte boundaries, because a wrong guess desynchronizes the client. Statements, policy and audit keep running; the rewrite alone stands down, and the lane logs it.
Denials
A denied statement returns a synthesized TDS reply, anERROR token followed by DONE(error), the same shape a real SQL Server error takes. The developer reads the rule’s message in sqlcmd instead of watching the socket drop:
The Envoy lane
TDS 8.0 is TCP, then TLS, then the protocol, so Envoy terminates it with an ordinaryDownstreamTlsContext in front of tcp_proxy:
envoy.yaml
Encrypt=strict. A TDS 7.x client skips Envoy and reaches the lane directly, because its TLS handshake travels inside 0x12 packets Envoy cannot speak.
Two compose stacks run this end to end, split by who terminates: deploy/docker-compose/envoy-stack/mssql/ (SQL Server 2022, TDS 8.0, Envoy terminating, a Samba AD DC and a Kerberos client) and mssql2019/ (TDS 7.4, no Envoy, the encrypted login covered). See local testing.
Next
Kerberos and SQL Server
Integrated auth end to end, TDS version negotiation, and the two local stacks.
Config File Reference
Every listener field, inheritance between lanes, and what startup refuses.