v0.9.4
Connections can now be forwarded to upstream targets without TLS termination. The proxy parses the ClientHello to extract SNI and ALPN, then either terminates or passes through the raw TCP stream based on target configuration. Passthrough targets cannot use HTTP actions or client_tls (validated at config load time). A new example is provided in examples/sni_passthrough.yml.
SNI-based routing now supports wildcard patterns (*.example.com) via a new DomainTrie data structure, replacing the previous HashMap<TlsOption, ...> lookup. The trie supports exact matches, wildcard subdomains, dot-shorthand (.example.com), and catch-all (*). A new example is provided in examples/wildcard_sni.yml.
HTTP host header matching supports the same wildcard patterns as SNI routing. A Hostnames variant was added for the Host header config, enabling pattern-based virtual hosting. See examples/host_header_routing.yml.
Both client and server certificate fingerprints (SHA-256) can now be verified:
- Client fingerprints: The server can require connecting clients to present certificates matching configured SHA-256 fingerprints, optionally combined with CA chain validation.
- Server fingerprints: Outbound connections can pin upstream server certificates by fingerprint, with optional WebPKI verification.
Server listeners can require client certificates signed by specified CA certificates via the new client_ca_certs config option. This works alongside or independently of fingerprint-based verification.
Outbound TLS connections now support explicit ALPN protocol negotiation and SNI hostname configuration. SNI can be set to a specific hostname, disabled entirely (YAML null), or left to default behavior.
Both server-side (client-facing) and target-side (upstream) TCP keepalive can be configured with idle and interval durations. Implemented via socket2 using raw file descriptor manipulation. Failures are logged but do not abort connections.
A new hostname_util module provides strict validation for SNI hostnames (per RFC 6066 section 3) and HTTP Host headers. Rejects empty strings, overlength labels, trailing/leading/consecutive dots, non-ASCII bytes in SNI, control characters, and IP address literals in SNI. Pattern normalization strips FQDN trailing dots and validates wildcard placement at deserialization time.
A custom TLS ClientHello parser (tls_parser and tls_reader modules) replaces the previous peek-based TLS detection heuristic. This enables full extraction of SNI and ALPN from the ClientHello before deciding whether to terminate or pass through, and eliminates the polling sleep loop.
The LazyConfigAcceptor-based flow was replaced with direct ClientHello parsing and manual feeding of buffered bytes into the rustls ServerConnection. For non-TLS fallback, already-read bytes are passed as initial_data through to the HTTP parser via a new LineReader::new_with_data() constructor, so no bytes are lost.
The bidirectional copy loop now participates in Tokio's cooperative task budgeting (tokio::task::coop), preventing high-throughput streams from starving other tasks on the runtime.
- Migrated from
ringtoaws_lc_rsas the cryptographic backend. - Updated to the rustls 0.23 API:
CertificateDer<'static>,PrivateKeyDer<'static>, explicitCryptoProvider,dangermodule verifier traits, simplified key/cert loading. - Server config now sets
ignore_client_order = true, preferring the server's cipher suite order. - Max log level is capped in release builds via
Cargo.tomlfeature flags.
TlsOptionsplit into purpose-specificAlpnValueandSniValuetypes, withSniValueperforming hostname validation at deserialization time.required_request_headerschanged fromOption<HashMap<...>>toHashMap<...>(empty map replacesNone).NoneOrSome/OneOrSomeiterators now requireSend/Syncbounds for async task compatibility.- Header path fields may now be empty.
- Added
CONFIG.mdwith full configuration reference. - Updated
README.mdwith sections on wildcard SNI matching, host header routing, and new TLS features. - Added example configs:
host_header_routing.yml,sni_passthrough.yml,wildcard_sni.yml.
- Replaced
std::io::Error::new(ErrorKind::Other, ...)withstd::io::Error::other(...)(Rust 1.74+) throughout the codebase. - Replaced
line.find(x).is_some()withline.contains(x). - Simplified task spawning in
main.rsto idiomatic iterator collect. NoneOrSomenow derivesDefaultvia enum#[default]attribute.- Simplified
is_unspecified()methods usingmatches!()macro.
v0.9.3
Full Changelog: https://github.com/cfal/tobaru/compare/0.9.0...v0.9.3
0.9.0
Full Changelog: https://github.com/cfal/tobaru/compare/0.7.1...0.9.0
Breaking changes: The config format has changed. See https://github.com/cfal/tobaru/blob/master/UPGRADING.md for details.
0.2.0
- Fix for copy_bidirectional causing CLOSE_WAIT sockets
- Defaults to rustls