Add tcp-tls transport and tunnel protocol disguised as HTTPS

Implement the `tcp-tls` protocol crate and generic tunnel codec abstractions
alongside `wg-quic` to circumvent deep packet inspection (DPI/TSPU), UDP
blocking, and QUIC throttling. Traffic uses standard TCP on port 443 with
TLS 1.3 framing, looking identical to regular HTTPS traffic to network
observers.

Architectural highlights:
- TunnelCodec & GenericTunnelPlugin: Provide an extensible plugin abstraction
  in `crates/tsunagi/src/dataplane/tunnel.rs` that encapsulates background task
  lifecycles, per-peer channels, and sink dispatch, so new transport plugins
  only define cryptographic transformations and handshake payloads.
- MultiPacketTransport & Protocol Priority Selection: Support multiple
  simultaneous transports in `AgentConfig` and dynamically dispatch `open()`
  calls by protocol identifier. In `ensure_links`, choose the highest-priority
  mutually supported protocol per peer to avoid duplicate links.
- TLS 1.3 & Certificate Pinning: Generate self-signed Ed25519 X.509 certificates
  derived from the persistent `DeviceIdentity` secret. Clients pin peer
  certificates using `PinnedEndpointVerifier` against `iroh::EndpointId`.
- End-to-end Encryption: `PeerCipher` uses X25519 static-static ECDH, HKDF-SHA256,
  and ChaCha20-Poly1305 with incrementing 64-bit nonces. Relayed traffic across
  intermediate nodes remains confidential and tamper-proof.
- Privilege handling: Default to TCP port 443 with fallback to 8443 on
  `PermissionDenied`, providing an actionable message recommending:
  `sudo setcap cap_net_admin,cap_net_bind_service+p <path>`.

Validation on Linux:
- `cargo fmt --all -- --check` passed.
- `cargo clippy --locked --workspace --all-targets -- -D warnings` passed.
- `cargo test --locked --workspace --all-targets` passed (all workspace unit,
  integration, CLI, and new `tsunagi-tcp-tls` tests passed).
This commit is contained in:
Aleksandr Bogomiakov committed 2026-09-30 16:41:51 +01:00
1 parent db9015b26c
commit fac3bd7bb9
20 files changed
+2195 -36

No files matched your search

Generated
+256 -2
View File
@@ -126,6 +126,45 @@ version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "asn1-rs"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
"displaydoc",
"nom",
"num-traits",
"rusticata-macros",
"thiserror 2.0.20",
"time",
]
[[package]]
name = "asn1-rs-derive"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
"synstructure 0.13.2",
]
[[package]]
name = "asn1-rs-impl"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "async-broadcast"
version = "0.7.2"
@@ -201,6 +240,29 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "aws-lc-rs"
version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e"
dependencies = [
"aws-lc-sys",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
"pkg-config",
]
[[package]]
name = "backon"
version = "1.6.0"
@@ -236,6 +298,15 @@ version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "bit-vec"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.13.2"
@@ -357,6 +428,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
@@ -476,6 +549,15 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486"
[[package]]
name = "cmake"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
dependencies = [
"cc",
]
[[package]]
name = "cmov"
version = "0.5.4"
@@ -722,6 +804,20 @@ dependencies = [
"zeroize",
]
[[package]]
name = "der-parser"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
dependencies = [
"asn1-rs",
"displaydoc",
"nom",
"num-bigint",
"num-traits",
"rusticata-macros",
]
[[package]]
name = "deranged"
version = "0.5.8"
@@ -844,6 +940,12 @@ dependencies = [
"litrs",
]
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "dyn-clone"
version = "1.0.20"
@@ -1035,6 +1137,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures"
version = "0.3.34"
@@ -1856,6 +1964,16 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "jobserver"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
"getrandom 0.4.3",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.105"
@@ -2026,6 +2144,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "mio"
version = "1.2.3"
@@ -2330,6 +2454,16 @@ dependencies = [
"libc",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "noq"
version = "1.3.0"
@@ -2401,12 +2535,31 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "num-bigint"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-integer"
version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -2528,6 +2681,15 @@ dependencies = [
"objc2-security",
]
[[package]]
name = "oid-registry"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
dependencies = [
"asn1-rs",
]
[[package]]
name = "once_cell"
version = "1.21.4"
@@ -2899,6 +3061,19 @@ dependencies = [
"rand_core 0.10.1",
]
[[package]]
name = "rcgen"
version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8"
dependencies = [
"ring",
"rustls-pki-types",
"time",
"x509-parser",
"yasna",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -3044,6 +3219,15 @@ dependencies = [
"semver",
]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom",
]
[[package]]
name = "rustix"
version = "1.1.5"
@@ -3063,6 +3247,7 @@ version = "0.23.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
"ring",
@@ -3127,6 +3312,7 @@ version = "0.103.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
@@ -3527,6 +3713,17 @@ dependencies = [
"futures-core",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "synstructure"
version = "0.14.0"
@@ -3964,9 +4161,38 @@ dependencies = [
"tracing",
"tracing-subscriber",
"tsunagi",
"tsunagi-tcp-tls",
"tsunagi-wg-quic",
]
[[package]]
name = "tsunagi-tcp-tls"
version = "0.1.0"
dependencies = [
"bytes",
"chacha20poly1305",
"hex",
"hkdf",
"iroh",
"netdev 0.46.3",
"postcard",
"rand 0.10.3",
"rcgen",
"rustls",
"serde",
"sha2",
"subtle",
"tempfile",
"thiserror 2.0.20",
"tokio",
"tokio-rustls",
"tracing",
"tracing-subscriber",
"tsunagi",
"x25519-dalek",
"zeroize",
]
[[package]]
name = "tsunagi-wg-quic"
version = "0.1.0"
@@ -4631,6 +4857,34 @@ dependencies = [
"zeroize",
]
[[package]]
name = "x509-parser"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
dependencies = [
"asn1-rs",
"data-encoding",
"der-parser",
"lazy_static",
"nom",
"oid-registry",
"ring",
"rusticata-macros",
"thiserror 2.0.20",
"time",
]
[[package]]
name = "yasna"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282"
dependencies = [
"bit-vec",
"time",
]
[[package]]
name = "yoke"
version = "0.8.3"
@@ -4651,7 +4905,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 3.0.6",
"synstructure",
"synstructure 0.14.0",
]
[[package]]
@@ -4757,7 +5011,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 3.0.6",
"synstructure",
"synstructure 0.14.0",
]
[[package]]