From a5a73f6e52345d648135cee7947c6741ac2a00b3 Mon Sep 17 00:00:00 2001 From: Jonathan Mast Date: Tue, 21 Jul 2020 08:19:13 -0400 Subject: [PATCH 1/3] Basic version of app https Still need to figure out generation of CA cert, but app certs seem to be working well. --- Cargo.lock | 359 ++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 8 +- _wildcard.test-key.pem | 28 ++++ _wildcard.test.pem | 25 +++ src/proxy.rs | 59 ++++--- src/proxy/tls.rs | 175 ++++++++++++++++++++ 6 files changed, 625 insertions(+), 29 deletions(-) create mode 100644 _wildcard.test-key.pem create mode 100644 _wildcard.test.pem create mode 100644 src/proxy/tls.rs diff --git a/Cargo.lock b/Cargo.lock index 8392e6c..2b7e8e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,12 +122,30 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ba35e9565969edb811639dbebfe34edc0368e472c5018474c8eb2543397f81" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2b_simd" version = "0.5.10" @@ -139,6 +157,12 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + [[package]] name = "bytes" version = "0.5.4" @@ -153,9 +177,9 @@ checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72" [[package]] name = "cc" -version = "1.0.53" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" [[package]] name = "cfg-if" @@ -240,9 +264,35 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.2.0" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "der-oid-macro" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788" +checksum = "bd17d13ecf875e704369fdbde242483ac769fc18f6af21e43d5a692a079732fc" +dependencies = [ + "nom", + "num-bigint", + "num-traits", + "proc-macro-hack", +] + +[[package]] +name = "der-parser" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4b1e27396f46037881c39d821660f2ff48797aaa7152a45ded7a93b368a819" +dependencies = [ + "der-oid-macro", + "nom", + "num-bigint", + "num-traits", + "proc-macro-hack", + "rusticata-macros", +] [[package]] name = "dirs" @@ -342,6 +392,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "futures" version = "0.3.12" @@ -620,12 +676,34 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +[[package]] +name = "js-sys" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lexical-core" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.86" @@ -717,6 +795,19 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +dependencies = [ + "bitvec", + "funty", + "lexical-core", + "memchr", + "version_check", +] + [[package]] name = "ntapi" version = "0.3.4" @@ -726,6 +817,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.42" @@ -761,6 +863,15 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" +[[package]] +name = "oid-registry" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2508c8f170e55be68508b1113956a760a82684f42022f8834fb16ca198621211" +dependencies = [ + "der-parser", +] + [[package]] name = "once_cell" version = "1.5.2" @@ -783,14 +894,18 @@ dependencies = [ "listenfd", "nix", "once_cell", + "parking_lot", "rand", + "rcgen", "serde", "serde_json", "shellexpand", "tokio", + "tokio-rustls", "toml", "trust-dns-server", "url", + "webpki", ] [[package]] @@ -818,6 +933,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64 0.13.0", + "once_cell", + "regex", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -912,6 +1038,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "radix_trie" version = "0.2.1" @@ -962,6 +1094,19 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rcgen" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cb7a2dc0e5307189b6933a61290ff06b65b35bdcaae2b2c50a0c3e355cb118e" +dependencies = [ + "chrono", + "pem", + "ring", + "x509-parser", + "yasna", +] + [[package]] name = "redox_syscall" version = "0.1.56" @@ -997,13 +1142,28 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rust-argon2" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" dependencies = [ - "base64", + "base64 0.11.0", "blake2b_simd", "constant_time_eq", "crossbeam-utils", @@ -1015,6 +1175,34 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +[[package]] +name = "rusticata-macros" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7390af60e66c44130b4c5ea85f2555b7ace835d73b4b889c704dc3cb4c0468c8" +dependencies = [ + "nom", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +dependencies = [ + "base64 0.13.0", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustversion" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" + [[package]] name = "ryu" version = "1.0.4" @@ -1027,6 +1215,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.123" @@ -1109,6 +1307,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -1126,6 +1336,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "termcolor" version = "1.1.0" @@ -1214,6 +1430,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + [[package]] name = "tokio-stream" version = "0.1.0" @@ -1426,6 +1653,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.2.1" @@ -1453,6 +1686,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + [[package]] name = "want" version = "0.3.0" @@ -1469,6 +1708,80 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasm-bindgen" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" + +[[package]] +name = "web-sys" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "winapi" version = "0.3.8" @@ -1499,3 +1812,39 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "x509-parser" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7999ae290e75ec1d4dc8e9ff9870e48e3542a8f2e9c1e2e07d7ca02b459e10" +dependencies = [ + "base64 0.13.0", + "chrono", + "data-encoding", + "der-oid-macro", + "der-parser", + "lazy_static", + "nom", + "num-bigint", + "oid-registry", + "ring", + "rusticata-macros", + "rustversion", + "thiserror", +] + +[[package]] +name = "yasna" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de7bff972b4f2a06c85f6d8454b09df153af7e3a4ec2aac81db1b105b684ddb" +dependencies = [ + "chrono", +] diff --git a/Cargo.toml b/Cargo.toml index c464a57..7ba375b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,11 @@ libc = "0.2" once_cell = "1.5.2" color-eyre = "0.5.1" async-stream = "0.3.0" +tokio-rustls = "0.22.0" +rcgen = { version = "0.8.5", features = ["x509-parser"] } +rand = "0.8.3" +parking_lot = "0.11.0" +webpki = "0.21.3" [[bin]] name = "echo-server" @@ -37,6 +42,3 @@ path = "tests/helpers/echo_server.rs" [target.'cfg(target_os = "macos")'.dependencies] trust-dns-server = "0.20.0" - -[dev-dependencies] -rand = "0.8.3" diff --git a/_wildcard.test-key.pem b/_wildcard.test-key.pem new file mode 100644 index 0000000..996784f --- /dev/null +++ b/_wildcard.test-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD52ZaoVc8nMMCN +3FTn7FBYwQNxFFor1OaNCu1dlW9V531Sf4bi1Gdrf5/zWpvsg666lvwoSItDqKEh +qhrKUfJKeOqYzeheIloS4N9ucjH+LO+1EMo3YYJXGoQpQSqiQmTukfJQUcVsGUTc +q+IYJFfzlnJmPllyOD0GqPnJfde9J4DR+fhpvGQ+OOdntx/SFhOZQICMLsVyOQuF +aGbJIIj+zYUzZLKUDV6F7CsDL++fdWWlrcvCLNFkPmNnNOc6CPLYZNJEjWi0BV1v +Zwa8NgjoAXZ+4h9othWCX67AJJFIt39FMl1tQxo395x47tTqyNGNvjDMLBwYlwnC +RgV4cC2VAgMBAAECggEAPTIiSJDb8ElsoFJ7KWMkOtjrsuK9Q0ceQSWQBf/4CR5t +/6rkquJDgnz7/GsRDdkjDui0UlmSYrwG22wCq9NuePcs3shwRb48OauCjlbCD/OJ +stut6+qiNht0i3Y+rwd8GUL+CtY8eMGnsDUZZ7hfInaTBp/24JcNu3ff1o5QLS/n +44tfN8fE232jSMq+Y+r9YudW/9hcAutOwTvGHsim02ktyVZixafuC/nmNA3LgM49 ++qWwr3QzbqqIXuEPOCFK9p0goRtmKRW3kcFB4FePUl+aONDFmwSAN+jm8xWqQ/4m +PCez+Vx64SNyHL9Qe43Ly+wqJ3nA5bg5XBisWz4igQKBgQD/ujiGRRSS/w4LamsT +gUgp3WJoXI4sXr8Lk/dc4iVpVmzEPJVoMNr+lqNL5/l9y4odA5WLC/yoGTmLPc5b +ATPa7K681MV+ke+J1FTDwjjjQYAE1vlDMvU2IP329fG10ZBi9lSUmLMYyuTwH+Qr +DyRDZw8cYcLA9REsM/zzyt0FvQKBgQD6HcOSHejgL6BB5DG2+33VGT+aZl8mIhiP +yXeOS4mss8v4XBwYX5TPw8dmwx1x6faof9fliKBDRlSqFLV9Vt7eQO/UA3XjjgZA +MHXpYtdSyOg0WC9P/z2pybqJGAQTYprb1zljgrWTW57Wcqne0u4/BVIqHGUDHP+I +YzFlDm+ouQKBgD04DloOZYt/JZSUCEgmFel3xxwmtB5pHCEgbgI9XSlneChOPJIx +x+tUkokUYoS72jdx6TXdS8HOMBlmVWUx14EcUgSAhzryor6DJzup3kaBIq2F7Swq +IcuwgDvDyvZ00bTvNXZRS+aug7n8WHn6aPr9y/9GZAIfaNoFJBQUx26dAoGBAO/c +j2uXZ3dn9SZ7svmqoXg16Hsn5ePqGuf567/4zSVkoB2kKAVv1ISTWq1APQK7vyLE +x8WGizs5PYSGq65yGvXGDLmkP/BkibYRQ2L4uUrZBWb9kxIC0536qftDntUAYUan +VpAKEBwrZ159RE8+teCWN7/Oz0h3DNA9YGdrusVxAoGBAN4ctVGm9fvJKIpzqAQW +dEk8utSPWqVGDzZDMacOvvh5IPKTumtVrAmkB0JUcAgReLayLxGkBPxH1o4FYS0s +J+CL9QzGqX73CelrwTUZajIkUuTCuTd0i6+v+5j75F2LVOZgWxlEXR3EQCB/ZZRz +aIPXe0hsM47qN5d44Nby7hZq +-----END PRIVATE KEY----- diff --git a/_wildcard.test.pem b/_wildcard.test.pem new file mode 100644 index 0000000..eb3a135 --- /dev/null +++ b/_wildcard.test.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIETDCCArSgAwIBAgIRAOJNEXKYBsTeh2v5q8jBbB0wDQYJKoZIhvcNAQELBQAw +gYExHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTErMCkGA1UECwwiam9u +bWFzdEBuZXctaG9zdC0zLmhvbWUgKEpvbiBNYXN0KTEyMDAGA1UEAwwpbWtjZXJ0 +IGpvbm1hc3RAbmV3LWhvc3QtMy5ob21lIChKb24gTWFzdCkwHhcNMTkwNjAxMDAw +MDAwWhcNMzAwNjI3MjA0MDEyWjBWMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1l +bnQgY2VydGlmaWNhdGUxKzApBgNVBAsMImpvbm1hc3RAbmV3LWhvc3QtMy5ob21l +IChKb24gTWFzdCkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD52Zao +Vc8nMMCN3FTn7FBYwQNxFFor1OaNCu1dlW9V531Sf4bi1Gdrf5/zWpvsg666lvwo +SItDqKEhqhrKUfJKeOqYzeheIloS4N9ucjH+LO+1EMo3YYJXGoQpQSqiQmTukfJQ +UcVsGUTcq+IYJFfzlnJmPllyOD0GqPnJfde9J4DR+fhpvGQ+OOdntx/SFhOZQICM +LsVyOQuFaGbJIIj+zYUzZLKUDV6F7CsDL++fdWWlrcvCLNFkPmNnNOc6CPLYZNJE +jWi0BV1vZwa8NgjoAXZ+4h9othWCX67AJJFIt39FMl1tQxo395x47tTqyNGNvjDM +LBwYlwnCRgV4cC2VAgMBAAGjaTBnMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAK +BggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFPL/lhTVlX/tC0Js +L2/P+w/NwJP0MBEGA1UdEQQKMAiCBioudGVzdDANBgkqhkiG9w0BAQsFAAOCAYEA +Q8Vn4vtBmWcifQhrSaWu3cFHDvLQ5+8fd20yuF7P7Bv+lOt/49iFVmHUDXXmepJH +yDd/cuq7HMSTyTOCF0gO+sTUHUCFO6RwzIpo5RtHBgJQ/VFmJLOngxAB3oStjuQf +av0qDNOyXugT/RwUX0C4BYl/GiOBJokpixL2zFdx8aJvaas70HfyHSf8wcTa6uco +ZylvDMsTMl2fQ5uvZG49Sar1nmucBNnWVHpESHXmylMklWOp62InfD1H9S8Qm3kH +YH3EK7MM6FCDASzNMNVcccIXz89KIlkUZwL+USYakJL2Ad/bmwPSe6vJD2lsA4Il +DWi8fsushFvOd/+qfbFwsxC+P3UmtWN/FqGaiVmMTVFZPtiyHycDHta/9m7LgEu4 +/kjuleqABL2/idO2alDUFlThjABLnA36azt+5qdOoe5sPWHO7mzwwdUl5t6Ri+RE +kw5eQDa3nK3sDHIOGOEt1AEZqoG4bCkRA08fR9ETZGK9W48/A9L9X0aKJ3yexEAr +-----END CERTIFICATE----- diff --git a/src/proxy.rs b/src/proxy.rs index 3af433f..ef1dd86 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::net::{SocketAddr, TcpListener}; use hyper::service::{make_service_fn, service_fn}; -use hyper::{client::HttpConnector, Body, Client, Request, Response, Server, Uri}; +use hyper::{Body, Client, Request, Response, Server, Uri}; use url::Url; use crate::host_resolver; @@ -10,6 +10,7 @@ use crate::host_resolver; mod autostart_response; mod host_missing; mod meta_server; +mod tls; use crate::{app::App, config::Config, process_manager::ProcessManager}; @@ -32,7 +33,8 @@ async fn error_response(error: &hyper::Error, app: &App) -> Response { } pub async fn start_server(config: Config, shutdown_handler: impl Future) { - let (addr, server): (_, color_eyre::Result<_>) = if let Ok(listener) = get_activation_socket() { + let [http_socket, https_socket] = get_proxy_sockets(); + let (addr, server): (_, color_eyre::Result<_>) = if let Ok(listener) = http_socket { let addr = listener.local_addr().unwrap(); (addr, Server::from_tcp(listener).map_err(|e| e.into())) } else { @@ -44,14 +46,14 @@ pub async fn start_server(config: Config, shutdown_handler: impl Future(service_fn(move |req| { - let client = Client::new(); - handle_request(req, client) - })) - }); + let proxy = + make_service_fn(|_| async move { Ok::<_, eyre::Error>(service_fn(handle_request)) }); let server = server .unwrap() @@ -64,27 +66,40 @@ pub async fn start_server(config: Config, shutdown_handler: impl Future color_eyre::Result { +fn get_proxy_sockets() -> [color_eyre::Result; 2] { let mut listenfd = listenfd::ListenFd::from_env(); - listenfd - .take_tcp_listener(0)? - .ok_or_else(|| eyre::eyre!("No socket provided")) + + [ + listenfd + .take_tcp_listener(0) + .map_err(Into::into) + .and_then(|option| option.ok_or_else(|| eyre::eyre!("No socket provided"))), + listenfd + .take_tcp_listener(1) + .map_err(Into::into) + .and_then(|option| option.ok_or_else(|| eyre::eyre!("No socket provided"))), + ] } #[cfg(target_os = "macos")] pub(crate) mod launchd; #[cfg(target_os = "macos")] -fn get_activation_socket() -> color_eyre::Result { - let result = launchd::get_tcp_socket("HttpSocket"); - - result.map_err(|e| e.into()) +fn get_proxy_sockets() -> [color_eyre::Result; 2] { + [ + launchd::get_tcp_socket("HttpSocket").map_err(Into::into), + launchd::get_tcp_socket("HttpsSocket").map_err(Into::into), + ] } -async fn handle_request( - mut request: Request, - client: Client, -) -> color_eyre::Result> { - let host = request.headers().get("HOST").unwrap().to_str().unwrap(); +async fn handle_request(mut request: Request) -> color_eyre::Result> { + let host = request + .headers() + .get("HOST") + .and_then(|v| v.to_str().ok()) + // HTTP2 requests only set uri, not host header + .or_else(|| request.uri().host()) + .unwrap_or_default(); + eprintln!("Serving request for host {:?}", host); eprintln!("Full req URI {}", request.uri()); @@ -104,12 +119,14 @@ async fn handle_request( let destination_url = app_url(&app, request.uri()); *request.uri_mut() = destination_url; + *request.version_mut() = hyper::Version::HTTP_11; app.touch().await; // Apply header overrides from config request.headers_mut().extend(app.headers().clone()); + let client = Client::new(); let result = client.request(request).await; match result { diff --git a/src/proxy/tls.rs b/src/proxy/tls.rs new file mode 100644 index 0000000..b6e7b6f --- /dev/null +++ b/src/proxy/tls.rs @@ -0,0 +1,175 @@ +use std::collections::HashMap; +use std::convert::TryFrom; +use std::fs::{create_dir, File}; +use std::io::prelude::*; +use std::path::Path; +use std::sync::Arc; + +use async_stream::stream; +use futures::stream::{Stream, StreamExt}; +use hyper::service::{make_service_fn, service_fn}; +use hyper::Server; +use parking_lot::RwLock; +use tokio::net::{TcpListener, TcpStream}; +use tokio_rustls::rustls::{self, sign}; +use tokio_rustls::{server::TlsStream, TlsAcceptor}; +use webpki::{DNSName, DNSNameRef}; + +use crate::config::Config; + +pub(crate) async fn tls_server( + config: &Config, + socket: std::net::TcpListener, +) -> color_eyre::Result<()> { + let proxy = + make_service_fn(|_| async move { Ok::<_, eyre::Error>(service_fn(super::handle_request)) }); + + let incoming_tls_stream = tls_stream(config, socket)?.filter(|s| { + eprintln!("Filtering"); + futures::future::ready(match s { + Ok(_) => { + eprintln!("Done filtering"); + true + } + Err(e) => { + eprintln!("Error in TLS stream:\n{:#}", e); + false + } + }) + }); + + let server = + Server::builder(hyper::server::accept::from_stream(incoming_tls_stream)).serve(proxy); + + server.await?; + Ok(()) +} + +fn tls_stream( + config: &Config, + socket: std::net::TcpListener, +) -> color_eyre::Result>>> { + let config_dir = config.general.config_dir.clone(); + + let tls_cfg = { + let cert_resolver = CertificateResolver::new(root_cert(&config_dir).unwrap()); + + let mut cfg = tokio_rustls::rustls::ServerConfig::new(rustls::NoClientAuth::new()); + cfg.cert_resolver = Arc::new(cert_resolver); + // Configure ALPN to accept HTTP/2, HTTP/1.1 in that order. + cfg.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); + Arc::new(cfg) + }; + + let tls_acceptor = TlsAcceptor::from(tls_cfg); + + eprintln!("Starting TLS server on {}", socket.local_addr().unwrap()); + + let listener = TcpListener::from_std(socket)?; + + let stream = stream! { + loop { + eprintln!("Starting loop"); + match listener.accept().await { + Ok((sock, _addr)) => { + eprintln!("Accepted"); + yield tls_acceptor.accept(sock).await.map_err(Into::into); + }, + Err(e) => yield Err(e.into()), + } + eprintln!("Loop complete"); + } + }; + + Ok(stream) +} + +/// Get root certificate, autogenerating it if it doesn't exist +fn root_cert(config_dir: &Path) -> color_eyre::Result { + let certs_dir = config_dir.join("certs"); + let cert_path = certs_dir.join("root-cert.der"); + let key_path = certs_dir.join("root-key.der"); + + if cert_path.exists() && key_path.exists() { + let mut root_key = Vec::new(); + File::open(key_path)?.read_to_end(&mut root_key)?; + + let mut cert_data = Vec::new(); + File::open(cert_path)?.read_to_end(&mut cert_data)?; + + let key = rcgen::KeyPair::try_from(root_key.as_slice())?; + let cert_params = rcgen::CertificateParams::from_ca_cert_der(&cert_data, key)?; + Ok(rcgen::Certificate::from_params(cert_params)?) + } else { + create_dir(certs_dir).ok(); + let mut cert_params = rcgen::CertificateParams::new(vec!["Oxidux".to_string()]); + cert_params + .distinguished_name + .push(rcgen::DnType::CommonName, "Oxidux CA"); + cert_params + .distinguished_name + .push(rcgen::DnType::OrganizationName, "Oxidux CA"); + cert_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained); + let generated_cert = rcgen::Certificate::from_params(cert_params)?; + + File::create(cert_path)?.write_all(&generated_cert.serialize_der()?)?; + File::create(key_path)?.write_all(&generated_cert.serialize_private_key_der())?; + + Ok(generated_cert) + } +} + +struct CertificateResolver { + root_cert: rcgen::Certificate, + cert_cache: RwLock>, +} + +impl CertificateResolver { + fn new(root_cert: rcgen::Certificate) -> Self { + Self { + root_cert, + cert_cache: RwLock::new(HashMap::default()), + } + } + + /// Generate a new key for the provided domain and sign it with our CA key + fn generate_certified_key(&self, domain: DNSNameRef) -> sign::CertifiedKey { + let domain: &str = domain.into(); + let mut cert_params = rcgen::CertificateParams::new(vec![domain.to_string()]); + cert_params + .distinguished_name + .push(rcgen::DnType::CommonName, domain); + cert_params + .distinguished_name + .push(rcgen::DnType::OrganizationName, "Oxidux CA"); + cert_params.serial_number = Some(rand::random()); + let generated_cert = rcgen::Certificate::from_params(cert_params).unwrap(); + let cert = rustls::Certificate( + generated_cert + .serialize_der_with_signer(&self.root_cert) + .unwrap(), + ); + + let pkey = rustls::PrivateKey(generated_cert.serialize_private_key_der()); + + let signing_key = rustls::sign::any_supported_type(&pkey).unwrap(); + + sign::CertifiedKey::new(vec![cert], Arc::new(signing_key)) + } +} + +impl rustls::ResolvesServerCert for CertificateResolver { + fn resolve(&self, client_hello: rustls::ClientHello) -> Option { + let domain = client_hello.server_name()?.to_owned(); + + if let Some(key) = self.cert_cache.read().get(&domain) { + return Some(key.clone()); + } + + let key = self.generate_certified_key(domain.as_ref()); + + self.cert_cache.write().insert(domain, key.clone()); + + Some(key) + } +} From c32cb6339f89718216767f1b0610286495dde1ce Mon Sep 17 00:00:00 2001 From: Jonathan Mast Date: Tue, 7 Sep 2021 08:57:02 -0400 Subject: [PATCH 2/3] Log current state when failing to restart --- src/process.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/process.rs b/src/process.rs index b0aaed9..962043e 100644 --- a/src/process.rs +++ b/src/process.rs @@ -228,9 +228,13 @@ impl Process { pub async fn restart(&self) { eprintln!("restarting"); - match self.run_state().await { + let state = self.run_state().await; + match state { RunState::Restarting(_) | RunState::Starting => { - eprintln!("Ignoring restart request, process is in invalid state"); + eprintln!( + "Ignoring restart request, process is in invalid state {:?}", + state + ); } RunState::Stopped => self.start().await.unwrap_or_else(|e| eprintln!("{}", e)), RunState::Running(pid) | RunState::Terminating(pid) => { From 240e712f61d03918c23e2f6e16ddc86cc15066a1 Mon Sep 17 00:00:00 2001 From: Jonathan Mast Date: Tue, 7 Sep 2021 08:57:38 -0400 Subject: [PATCH 3/3] Improve restart for multi-process apps --- src/ipc_listener.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ipc_listener.rs b/src/ipc_listener.rs index 0c51c05..b4ce78d 100644 --- a/src/ipc_listener.rs +++ b/src/ipc_listener.rs @@ -115,7 +115,14 @@ async fn restart_app( ) { let process = { let process_manager = ProcessManager::global_read().await; - lookup_process(&process_manager, process_name, directory).await + + if process_name.is_some() { + lookup_process(&process_manager, process_name, directory).await + } else { + let app = process_manager + .find_app_for_directory(directory) + .ok_or_else(|| eyre!("Failed to find app to restart")); + } }; let process = process.ok_or_else(|| eyre!("Failed to find app to restart"));