Sylwester Lachiewicz created THRIFT-6233:
--------------------------------------------
Summary: Make the peer-address check on TSSLServerSocket opt-in
Key: THRIFT-6233
URL: https://issues.apache.org/jira/browse/THRIFT-6233
Project: Thrift
Issue Type: Improvement
Components: Python - Library
Reporter: Sylwester Lachiewicz
Fix For: 0.25.0
{{TSSLServerSocket}} calls its default {{validate_callback}} with the client
certificate and the address the connection arrived from, and on Python 3.12 and
later that default ({{sslcompat.match_peer_ipaddress}}) accepts the certificate
only when the address appears among its IP subjectAltName records. Python is
the only binding that does this by default: the C++ and D access managers are
installed on client sockets only, and the other bindings with a TLS server
leave client-certificate policy to the TLS configuration or to a callback the
application supplies.
A client checks the server's certificate against the name it meant to reach. A
server has no such reference for its clients: the source address is not
something the client asserts, and NAT, proxies, load balancers and container
networking rewrite it routinely, so a client certificate often cannot carry the
address the server will see. Which certificates may connect is the
application's policy, expressed through a {{validate_callback}} that looks at
the subject or the subjectAltName, or through a CA that issues only to the
clients meant to connect. THRIFT-3599 added the check in 0.10.0 so that a
server would not accept just any certificate its CA had signed; the peer
address is a poor stand-in for that. It only concerns servers that request
client certificates, since {{cert_reqs}} defaults to {{CERT_NONE}}. As master
stands, 0.25.0 would start refusing clients on Python 3.12 and later whose
certificates do not list the address the server sees.
Change:
* {{TSSLServerSocket}} defaults {{validate_callback}} to {{None}}. OpenSSL
still verifies the client certificate against {{ca_certs}} whenever
{{cert_reqs}} asks for one.
* {{thrift.transport.sslcompat.match_peer_ipaddress}} stays as the documented
way to opt back in: {{validate_callback=match_peer_ipaddress}}.
* {{TSSLSocket}} and {{sslcompat._match_hostname}} are unchanged; the client
path still relies on them.
* The {{_match_has_ipaddress}} check that raised {{ValueError}} in
{{TSSLServerSocket.__init__}} goes, since it only made sense while the matcher
was the default.
*
[lib/py/README.md|https://github.com/apache/thrift/blob/master/lib/py/README.md]
(Breaking Changes, 0.25.0) and
[test/keys/README.md|https://github.com/apache/thrift/blob/master/test/keys/README.md]
are updated, with a note for servers on Python 3.11 or earlier that relied on
the check: the opt-in covers addresses listed as IP subjectAltName records, not
the commonName fallback that {{ssl.match_hostname}} also applied.
* Tests go through {{TSSLServerSocket.accept()}}: {{client.crt}}, trusted by
the server but carrying no IP subjectAltName, is accepted by default and
refused with the opt-in; {{client_v3.crt}} is accepted with it.
Follow-up agreed on [PR #3818|https://github.com/apache/thrift/pull/3818],
which keeps the opt-in matcher treating {{::ffff:127.0.0.1}} and {{127.0.0.1}}
as the same address
([THRIFT-6201|https://issues.apache.org/jira/browse/THRIFT-6201]).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)