mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-16 00:25:48 +08:00
A service with ssl_ktls keeps its splice() mode. A connection whose TLS sockets all got kernel TLS both ways may splice, as a plain one does when no data filters apply; one where the kernel did not take TLS (nosplice) copies as before. TLS may hold data for a socket that a splice() from it would not see: OpenSSL reads more from a kernel TLS socket than it is asked for. sockmap asks the TLS code (splicetls) and reads that through TLS into the buffer first, once the pipe has drained, so the order is kept, then splices on. A read from a TLS socket counts as large at 4K, TLS handing out a record at a time, and the short read collecting what TLS left from the last one does not end a run of large ones. A splice() refused because a record is not data (an alert, a key update, a ticket) falls back to reading it through TLS instead of ending the connection. The kernel TLS test compares the bodies with the origin's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
2.8 KiB
Python
81 lines
2.8 KiB
Python
"""Kernel TLS (ssl_ktls): a TLS chain works the same whether or not the
|
|
kernel takes the encryption, and the proxy says which it did.
|
|
|
|
Whether it does depends on the TLS library (OpenSSL built with enable-ktls),
|
|
the kernel (the tls module on Linux) and the cipher, so the case checks the
|
|
data and the report, not that the kernel took it.
|
|
"""
|
|
|
|
|
|
def run(t):
|
|
certs = t.certs()
|
|
if not certs:
|
|
t.skip("kernel TLS (openssl is not available to generate certificates)")
|
|
return
|
|
if not certs.verified:
|
|
t.fail("the generated certificate chain verifies", "OK",
|
|
certs.verify_output or "openssl verify failed")
|
|
return
|
|
|
|
origin = t.free_port()
|
|
parent = t.free_port()
|
|
client = t.free_port()
|
|
|
|
server = t.start("ktls", f"""
|
|
log
|
|
auth iponly
|
|
allow *
|
|
http echo * /echo**
|
|
http data * /data
|
|
httpsrv -p{origin}
|
|
|
|
flush
|
|
ssl_ktls
|
|
ssl_server_cert {certs.server}
|
|
ssl_server_key {certs.server_key}
|
|
ssl_serv
|
|
auth iponly
|
|
allow *
|
|
proxy -p{parent}
|
|
|
|
flush
|
|
ssl_noserv
|
|
auth iponly
|
|
allow *
|
|
parent 1000 connects 127.0.0.1 {parent}
|
|
ssl_client_mode 3
|
|
ssl_client_ca_file {certs.ca}
|
|
ssl_client_verify
|
|
ssl_cli
|
|
proxy -p{client}
|
|
""", ports=[origin, parent, client])
|
|
|
|
out = server.output()
|
|
if "Unknown command" in out:
|
|
t.skip("kernel TLS (this build has no SSL support)")
|
|
return
|
|
|
|
through = f"127.0.0.1:{client}"
|
|
r = t.http(f"http://127.0.0.1:{origin}/echo", proxy=through)
|
|
t.eq(200, r.status, "a request through the TLS chain with ssl_ktls arrives")
|
|
t.contains(r, "path=/echo", "the origin sees it")
|
|
|
|
# many records, both ways through both TLS legs; where the kernel took
|
|
# TLS a large body is spliced, so what arrives is compared with the origin
|
|
for size in (100000, 3000000):
|
|
url = f"http://127.0.0.1:{origin}/data?size={size}"
|
|
got = t.http(url, proxy=through)
|
|
t.eq(size, got.length, f"a {size} byte body survives the kernel TLS chain")
|
|
t.ok(f"the {size} byte body arrives unchanged") if got.body == t.http(url).body \
|
|
else t.fail(f"the {size} byte body arrives unchanged", "the origin's bytes",
|
|
"different bytes")
|
|
t.eq(200000, t.http(f"http://127.0.0.1:{origin}/data?size=200000&chunked=1",
|
|
proxy=through).length,
|
|
"a chunked body survives the kernel TLS chain")
|
|
|
|
if "not supported by this TLS library" in server.output():
|
|
t.skip("the kernel TLS report (this TLS library has no kernel TLS)")
|
|
return
|
|
log = t.wait_output(server, "kernel TLS:")
|
|
t.contains(log, "kernel TLS: sending", "the proxy says whether the kernel took TLS")
|