diff --git a/.github/workflows/backend.yaml b/.github/workflows/backend.yaml index 17bcfa9d35..dd0255a3c4 100644 --- a/.github/workflows/backend.yaml +++ b/.github/workflows/backend.yaml @@ -5,7 +5,7 @@ on: workflow_dispatch: env: - RUST_VERSION: "1.62.1" + RUST_VERSION: "1.67.1" ENVIRONMENT: "dev" jobs: diff --git a/backend/Cargo.lock b/backend/Cargo.lock index c6fc37f7cd..60eec36b25 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ "gimli", ] @@ -53,9 +53,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "arrayref" @@ -128,10 +128,10 @@ checksum = "bc4c00309ed1c8104732df4a5fa9acc3b796b6f8531dfbd5ce0078c86f997244" dependencies = [ "darling 0.10.2", "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -175,9 +175,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -186,9 +186,9 @@ version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -206,7 +206,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -228,9 +228,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", @@ -265,6 +265,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.5.3" @@ -316,8 +322,8 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "regex", "rustc-hash", "shlex", @@ -362,9 +368,9 @@ checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403" [[package]] name = "bitvec" -version = "0.22.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", @@ -374,13 +380,13 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec", - "constant_time_eq", + "constant_time_eq 0.2.4", ] [[package]] @@ -481,9 +487,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -493,15 +499,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.74" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cexpr" @@ -526,16 +532,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi", ] @@ -665,18 +671,18 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" dependencies = [ "crossbeam-utils", ] [[package]] name = "const-oid" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" [[package]] name = "constant_time_eq" @@ -684,6 +690,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "constant_time_eq" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" + [[package]] name = "convert_case" version = "0.4.0" @@ -760,18 +772,18 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "crc32fast" @@ -784,9 +796,9 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -794,9 +806,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if 1.0.0", ] @@ -891,9 +903,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.80" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +checksum = "90d59d9acd2a682b4e40605a242f6670eaa58c5957471cbf85e8aa6a0b97a5e8" dependencies = [ "cc", "cxxbridge-flags", @@ -903,34 +915,34 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.80" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +checksum = "ebfa40bda659dd5c864e65f4c9a2b0aff19bea56b017b9b77c73d3766a453a38" dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "scratch", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] name = "cxxbridge-flags" -version = "1.0.80" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" +checksum = "457ce6757c5c70dc6ecdbda6925b958aae7f959bda7d8fb9bde889e34a09dc03" [[package]] name = "cxxbridge-macro" -version = "1.0.80" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +checksum = "ebf883b7aacd7b2aeb2a7b338648ee19f57c140d4ee8e52c68979c6b2f7f2263" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -955,12 +967,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" dependencies = [ - "darling_core 0.14.2", - "darling_macro 0.14.2", + "darling_core 0.14.3", + "darling_macro 0.14.3", ] [[package]] @@ -971,10 +983,10 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "strsim 0.9.3", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -985,24 +997,24 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "strsim 0.10.0", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] name = "darling_core" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "strsim 0.10.0", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -1012,8 +1024,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core 0.10.2", - "quote 1.0.21", - "syn 1.0.103", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -1023,19 +1035,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.21", - "syn 1.0.103", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" dependencies = [ - "darling_core 0.14.2", - "quote 1.0.21", - "syn 1.0.103", + "darling_core 0.14.3", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -1048,14 +1060,14 @@ dependencies = [ "hashbrown", "lock_api", "once_cell", - "parking_lot_core 0.9.4", + "parking_lot_core 0.9.7", ] [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "data-url" @@ -1121,16 +1133,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05520711837dd592d2861319ea3cf2dfd81e39bb92e41758ee9172f3623daebd" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "der" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", "pem-rfc7468", @@ -1144,10 +1156,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "rustc_version 0.4.0", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -1167,9 +1179,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "const-oid", @@ -1254,18 +1266,30 @@ dependencies = [ "der", "elliptic-curve", "rfc6979", - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature 2.0.0", ] [[package]] name = "ed25519" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "pkcs8", "serde", - "signature", + "signature 1.6.4", ] [[package]] @@ -1285,9 +1309,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" dependencies = [ "serde", ] @@ -1301,10 +1325,12 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "digest 0.10.5", + "digest 0.10.6", "ff", "generic-array", "group", + "pem-rfc7468", + "pkcs8", "rand_core 0.6.4", "sec1", "subtle", @@ -1333,7 +1359,7 @@ dependencies = [ "cookie", "cookie_store 0.19.0", "current_platform", - "digest 0.10.5", + "digest 0.10.6", "digest 0.9.0", "divrem", "ed25519", @@ -1351,6 +1377,8 @@ dependencies = [ "hyper-ws-listener", "imbl 2.0.0", "indexmap", + "ipnet", + "iprange", "isocountry", "itertools 0.10.5", "josekit", @@ -1361,12 +1389,13 @@ dependencies = [ "log", "mbrman", "models", - "nix 0.25.0", - "nom 7.1.1", + "nix 0.25.1", + "nom 7.1.3", "num", "num_enum", "openssh-keys", "openssl", + "p256 0.12.0", "patch-db", "pbkdf2", "pin-project", @@ -1385,7 +1414,7 @@ dependencies = [ "scopeguard", "serde", "serde_json", - "serde_with 2.0.1", + "serde_with 2.2.0", "serde_yaml", "sha2 0.10.6", "sha2 0.9.9", @@ -1411,6 +1440,7 @@ dependencies = [ "typed-builder", "url", "uuid", + "zeroize", ] [[package]] @@ -1422,7 +1452,7 @@ dependencies = [ "futures", "helpers", "imbl 2.0.0", - "nix 0.25.0", + "nix 0.25.1", "procfs", "serde", "serde_json", @@ -1442,7 +1472,7 @@ source = "git+https://github.com/Start9Labs/emver-rs.git#61cf0bc96711b4d6f3f30df dependencies = [ "either", "fp-core", - "nom 7.1.1", + "nom 7.1.3", "serde", ] @@ -1463,9 +1493,9 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if 1.0.0", ] @@ -1482,10 +1512,10 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ - "heck 0.4.0", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "heck 0.4.1", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -1495,9 +1525,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b940da354ae81ef0926c5eaa428207b8f4f091d3956c891dfbd124162bed99" dependencies = [ "pmutil", - "proc-macro2 1.0.47", + "proc-macro2 1.0.51", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -1515,9 +1545,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", "humantime 2.1.0", @@ -1578,7 +1608,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef0f547e1d79e058664f2ea7d3a6d82b2ddd5fea4a6650b97b70c38979f34db3" dependencies = [ - "nix 0.24.2", + "nix 0.24.3", ] [[package]] @@ -1593,14 +1623,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1611,9 +1641,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", "miniz_oxide", @@ -1665,9 +1695,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0981e470d2ab9f643df3921d54f1952ea100c39fdb6a3fdc820e20d2291df6c" dependencies = [ "pmutil", - "proc-macro2 1.0.47", + "proc-macro2 1.0.51", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -1682,15 +1712,15 @@ dependencies = [ [[package]] name = "funty" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -1703,9 +1733,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -1713,15 +1743,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -1741,38 +1771,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -1820,9 +1850,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" [[package]] name = "git-version" @@ -1841,16 +1871,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "group" @@ -1917,9 +1947,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" dependencies = [ "unicode-segmentation", ] @@ -1950,6 +1980,21 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -1981,7 +2026,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -1992,7 +2037,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.4", + "itoa 1.0.5", ] [[package]] @@ -2035,9 +2080,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.22" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -2048,7 +2093,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.4", + "itoa 1.0.5", "pin-project-lite", "socket2", "tokio", @@ -2078,7 +2123,7 @@ checksum = "b3fb761e568fdb34ff794d284bfe1769efc5d16b9d716286451ab3a70ab82bea" dependencies = [ "anyhow", "base64 0.13.1", - "env_logger 0.9.1", + "env_logger 0.9.3", "futures", "hyper", "log", @@ -2201,9 +2246,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -2219,17 +2264,48 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "internment" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a798d7677f07d6f1e77be484ea8626ddb1566194de399f1206306820c406371" +dependencies = [ + "ahash", + "dashmap", + "hashbrown", + "once_cell", + "parking_lot 0.12.1", + "serde", +] + [[package]] name = "io-lifetimes" -version = "0.7.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] [[package]] name = "ipnet" -version = "2.5.1" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +dependencies = [ + "serde", +] + +[[package]] +name = "iprange" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37209be0ad225457e63814401415e748e2453a5297f9b637338f5fb8afa4ec00" +dependencies = [ + "ipnet", + "serde", +] [[package]] name = "is-macro" @@ -2239,9 +2315,21 @@ checksum = "1c068d4c6b922cd6284c609cfa6dec0e41615c9c5a1a4ba729a970d8daba05fb" dependencies = [ "Inflector", "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "is-terminal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -2280,15 +2368,15 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "josekit" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee6af62ad98bdf699ad2ecc8323479a1fdc7aa5faa6043d93119d83f6c5fca8" +checksum = "9ef871a7a5f162afa718c416e9cbdd54241a58c922e07870e898ebad2425d8d8" dependencies = [ "anyhow", "base64 0.13.1", @@ -2304,9 +2392,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -2385,9 +2473,12 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] [[package]] name = "lalrpop" @@ -2521,9 +2612,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" @@ -2543,18 +2634,18 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] [[package]] name = "linux-raw-sys" -version = "0.0.46" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "lock_api" @@ -2586,15 +2677,15 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "mbrman" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdee9caaf6ebb0bae8ae1cb6b591e5553b6cf5a34c7ea07bb6f24c1a80619819" +checksum = "a4b239f4755d00466e3ac1d55ddeaf77a66c7580352fc6cbc40d56c218fc94a9" dependencies = [ "bincode", "bitvec", @@ -2620,7 +2711,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -2652,9 +2743,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] @@ -2679,10 +2770,14 @@ dependencies = [ "color-eyre", "ed25519-dalek", "emver", + "internment", + "ipnet", + "lazy_static", "mbrman", "openssl", "patch-db", "rand 0.8.5", + "regex", "rpc-toolkit", "serde", "serde_json", @@ -2729,9 +2824,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.23.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags", "cc", @@ -2742,9 +2837,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2754,9 +2849,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ "autocfg", "bitflags", @@ -2778,14 +2873,23 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2841,9 +2945,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", ] @@ -2893,49 +2997,49 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -2958,9 +3062,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2977,9 +3081,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2990,18 +3094,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.24.0+1.1.1s" +version = "111.25.0+1.1.1t" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3498f259dab01178c6228c6b00dcef0ed2a2d5e20d648c017861227773ea4abd" +checksum = "3173cd3626c43e3854b1b727422a276e568d9ec5fe8cec197822cf52cfb743d6" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.77" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ "autocfg", "cc", @@ -3013,9 +3117,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.1" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "overload" @@ -3035,18 +3139,30 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "ecdsa", + "ecdsa 0.14.8", "elliptic-curve", "sha2 0.10.6", ] +[[package]] +name = "p256" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49c124b3cbce43bcbac68c58ec181d98ed6cc7e6d0aa7c3ba97b2563410b0e55" +dependencies = [ + "ecdsa 0.15.1", + "elliptic-curve", + "primeorder", + "sha2 0.10.6", +] + [[package]] name = "p384" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "ecdsa", + "ecdsa 0.14.8", "elliptic-curve", "sha2 0.10.6", ] @@ -3059,7 +3175,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", ] [[package]] @@ -3069,14 +3185,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.4", + "parking_lot_core 0.9.7", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if 1.0.0", "instant", @@ -3088,15 +3204,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3112,9 +3228,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "patch-db" @@ -3127,7 +3243,7 @@ dependencies = [ "json-patch", "json-ptr", "lazy_static", - "nix 0.23.1", + "nix 0.23.2", "patch-db-macro", "serde", "serde_cbor 0.11.1", @@ -3143,8 +3259,8 @@ name = "patch-db-macro" version = "0.1.0" dependencies = [ "patch-db-macro-internals", - "proc-macro2 1.0.47", - "syn 1.0.103", + "proc-macro2 1.0.51", + "syn 1.0.107", ] [[package]] @@ -3152,9 +3268,9 @@ name = "patch-db-macro-internals" version = "0.1.0" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -3163,7 +3279,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", "hmac 0.12.1", "password-hash", "sha2 0.10.6", @@ -3192,9 +3308,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", "indexmap", @@ -3230,9 +3346,9 @@ dependencies = [ "phf_generator", "phf_shared", "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -3265,9 +3381,9 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -3316,9 +3432,9 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -3335,34 +3451,42 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettytable-rs" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f375cb74c23b51d23937ffdeb48b1fbf5b6409d4b9979c1418c1de58bc8f801" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" dependencies = [ - "atty", "csv", "encode_unicode", + "is-terminal", "lazy_static", "term", "unicode-width", ] +[[package]] +name = "primeorder" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b54f7131b3dba65a2f414cf5bd25b66d4682e4608610668eae785750ba4c5b2" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" @@ -3375,18 +3499,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] [[package]] name = "procfs" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfb6451c91904606a1abe93e83a8ec851f45827fa84273f256ade45dc095818" +checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69" dependencies = [ "bitflags", "byteorder", @@ -3399,9 +3523,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" dependencies = [ "bit-set", "bitflags", @@ -3415,6 +3539,7 @@ dependencies = [ "regex-syntax", "rusty-fork", "tempfile", + "unarray", ] [[package]] @@ -3467,18 +3592,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2 1.0.47", + "proc-macro2 1.0.51", ] [[package]] name = "radium" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "radix_trie" @@ -3607,9 +3732,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -3642,11 +3767,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ - "base64 0.13.1", + "base64 0.21.0", "bytes", "cookie", "cookie_store 0.16.1", @@ -3666,7 +3791,6 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "proc-macro-hack", "serde", "serde_json", "serde_urlencoded", @@ -3678,6 +3802,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "winreg", ] @@ -3723,11 +3848,12 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.1.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c9f5d2a0c3e2ea729ab3706d22217177770654c3ef5056b68b69d07332d3f5" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" dependencies = [ "libc", + "rtoolbox", "winapi", ] @@ -3759,9 +3885,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8e4b9cb00baf2d61bcd35e98d67dcb760382a3b4540df7e63b38d053c8a7b8b" dependencies = [ - "proc-macro2 1.0.47", + "proc-macro2 1.0.51", "rpc-toolkit-macro-internals", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -3770,9 +3896,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e2ce21b936feaecdab9c9a8e75b9dca64374ccc11951a58045ad6559b75f42" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -3782,7 +3908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "094052d5470cbcef561cb848a7209968c9f12dfa6d668f4bca048ac5de51099c" dependencies = [ "byteorder", - "digest 0.10.5", + "digest 0.10.6", "num-bigint-dig", "num-integer", "num-iter", @@ -3790,12 +3916,22 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core 0.6.4", - "signature", + "signature 1.6.4", "smallvec", "subtle", "zeroize", ] +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "rust-argon2" version = "1.0.0" @@ -3804,7 +3940,7 @@ checksum = "b50162d19404029c1ceca6f6980fe40d45c8b369f6f44446fa14bb39573b5bb9" dependencies = [ "base64 0.13.1", "blake2b_simd", - "constant_time_eq", + "constant_time_eq 0.1.5", "crossbeam-utils", ] @@ -3835,28 +3971,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.14", + "semver 1.0.16", ] [[package]] name = "rustix" -version = "0.35.13" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -3866,18 +4002,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.13.1", + "base64 0.21.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "rusty-fork" @@ -3893,18 +4029,17 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -3921,9 +4056,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "sct" @@ -3951,9 +4086,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -3964,9 +4099,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -3983,9 +4118,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "semver-parser" @@ -3995,9 +4130,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] @@ -4013,9 +4148,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.7" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" dependencies = [ "serde", ] @@ -4040,23 +4175,23 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "indexmap", - "itoa 1.0.4", + "itoa 1.0.5", "ryu", "serde", ] @@ -4068,7 +4203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.4", + "itoa 1.0.5", "ryu", "serde", ] @@ -4097,9 +4232,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f2d60d049ea019a84dcd6687b0d1e0030fe663ae105039bdf967ed5e6a9a7" +checksum = "30d904179146de381af4c93d3af6ca4984b3152db687dacb9c3c35e86f39809c" dependencies = [ "base64 0.13.1", "chrono", @@ -4107,7 +4242,7 @@ dependencies = [ "indexmap", "serde", "serde_json", - "serde_with_macros 2.0.1", + "serde_with_macros 2.2.0", "time 0.3.17", ] @@ -4118,31 +4253,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "serde_with_macros" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ccadfacf6cf10faad22bbadf55986bdd0856edfb5d9210aa1dcf1f516e84e93" +checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e" dependencies = [ - "darling 0.14.2", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "darling 0.14.3", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "serde_yaml" -version = "0.9.14" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da" +checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" dependencies = [ "indexmap", - "itoa 1.0.4", + "itoa 1.0.5", "ryu", "serde", "unsafe-libyaml", @@ -4150,13 +4285,13 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -4167,7 +4302,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -4191,7 +4326,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -4223,9 +4358,9 @@ checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -4236,7 +4371,17 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.5", + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest 0.10.6", "rand_core 0.6.4", ] @@ -4326,12 +4471,12 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" dependencies = [ "itertools 0.10.5", - "nom 7.1.1", + "nom 7.1.3", "unicode_categories", ] @@ -4373,7 +4518,7 @@ dependencies = [ "hkdf", "hmac 0.12.1", "indexmap", - "itoa 1.0.4", + "itoa 1.0.5", "libc", "log", "md-5 0.10.5", @@ -4407,17 +4552,17 @@ checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" dependencies = [ "dotenvy", "either", - "heck 0.4.0", + "heck 0.4.1", "hex", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "serde", "serde_json", "sha2 0.10.6", "sqlx-core", "sqlx-rt", - "syn 1.0.103", + "syn 1.0.107", "url", ] @@ -4450,13 +4595,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "288d8f5562af5a3be4bda308dd374b2c807b940ac370b5efa1c99311da91d9a1" dependencies = [ "ed25519-dalek", - "p256", + "p256 0.11.1", "p384", "rand_core 0.6.4", "rsa", "sec1", "sha2 0.10.6", - "signature", + "signature 1.6.4", "ssh-encoding", "zeroize", ] @@ -4502,8 +4647,8 @@ checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ "phf_generator", "phf_shared", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", ] [[package]] @@ -4513,10 +4658,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "994453cd270ad0265796eb24abf5540091ed03e681c5f3c12bc33e4db33253e1" dependencies = [ "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -4610,10 +4755,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb64bc03d90fd5c90d6ab917bb2b1d7fbd31957df39e31ea24a3f554b4372251" dependencies = [ "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -4658,10 +4803,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59949619b2ef45eedb6c399d05f2c3c7bc678b5074b3103bb670f9e05bb99042" dependencies = [ "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -4742,10 +4887,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18712e4aab969c6508dff3540ade6358f1e013464aa58b3d30da2ab2d9fcbbed" dependencies = [ "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -4859,9 +5004,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c8f200a2eaed938e7c1a685faaa66e6d42fa9e17da5f62572d3cbc335898f5e" dependencies = [ "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -4871,9 +5016,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5dca3f08d02da4684c3373150f7c045128f81ea00f0c434b1b012bc65a6cce3" dependencies = [ "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -4894,10 +5039,10 @@ checksum = "c3b9b72892df873972549838bf84d6c56234c7502148a7e23b5a3da6e0fedfb8" dependencies = [ "Inflector", "pmutil", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "swc_macros_common", - "syn 1.0.103", + "syn 1.0.107", ] [[package]] @@ -4913,12 +5058,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "unicode-ident", ] @@ -4928,9 +5073,9 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", "unicode-xid 0.2.4", ] @@ -5011,22 +5156,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -5042,18 +5187,19 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if 1.0.0", "once_cell", ] [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -5066,7 +5212,7 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa 1.0.4", + "itoa 1.0.5", "serde", "time-core", "time-macros", @@ -5107,15 +5253,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.23.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -5133,20 +5279,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -5217,9 +5363,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -5231,13 +5377,30 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "torut" version = "0.2.1" @@ -5282,9 +5445,9 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -5445,9 +5608,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" @@ -5471,9 +5634,9 @@ dependencies = [ [[package]] name = "typed-arena" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typed-builder" @@ -5481,22 +5644,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-id" @@ -5506,9 +5675,9 @@ checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -5521,9 +5690,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -5560,9 +5729,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" +checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" [[package]] name = "untrusted" @@ -5590,9 +5759,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" dependencies = [ "getrandom 0.2.8", ] @@ -5607,7 +5776,7 @@ dependencies = [ "fslock", "lazy_static", "libc", - "which 4.3.0", + "which 4.4.0", ] [[package]] @@ -5679,9 +5848,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -5689,24 +5858,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5716,38 +5885,51 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.21", + "quote 1.0.23", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -5765,9 +5947,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki", ] @@ -5783,9 +5965,9 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -5794,11 +5976,10 @@ dependencies = [ [[package]] name = "whoami" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3" dependencies = [ - "bumpalo", "wasm-bindgen", "web-sys", ] @@ -5834,19 +6015,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -5854,85 +6022,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.0" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows-targets" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" +name = "windows_aarch64_gnullvm" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] -name = "windows_i686_gnu" -version = "0.36.1" +name = "windows_aarch64_msvc" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -5945,9 +6107,9 @@ dependencies = [ [[package]] name = "wyz" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129e027ad65ce1453680623c3fb5163cbf7107bfe1aa32257e7d0e63f9ced188" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] @@ -5995,12 +6157,12 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", "synstructure", ] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 1b45c8efae..972d76a13a 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -90,6 +90,8 @@ hyper = { version = "0.14.20", features = ["full"] } hyper-ws-listener = "0.2.0" imbl = "2.0.0" indexmap = { version = "1.9.1", features = ["serde"] } +ipnet = { version = "2.7.1", features = ["serde"] } +iprange = { version = "0.6.7", features = ["serde"] } isocountry = "0.3.2" itertools = "0.10.3" josekit = "0.8.1" @@ -109,10 +111,11 @@ openssl = { version = "0.10.41", features = ["vendored"] } patch-db = { version = "*", path = "../patch-db/patch-db", features = [ "trace", ] } +p256 = { version = "0.12.0", features = ["pem"] } pbkdf2 = "0.11.0" pin-project = "1.0.11" pkcs8 = { version = "0.9.0", features = ["std"] } -prettytable-rs = "0.9.0" +prettytable-rs = "0.10.0" proptest = "1.0.0" proptest-derive = "0.3.0" rand = { version = "0.8.5", features = ["std"] } @@ -158,6 +161,7 @@ trust-dns-server = "0.22.0" typed-builder = "0.10.0" url = { version = "2.2.2", features = ["serde"] } uuid = { version = "1.1.2", features = ["v4"] } +zeroize = "1.5.7" [profile.test] opt-level = 3 diff --git a/backend/migrations/20230109181507_AccountSshKey.sql b/backend/migrations/20230109181507_AccountSshKey.sql deleted file mode 100644 index f86fd029d1..0000000000 --- a/backend/migrations/20230109181507_AccountSshKey.sql +++ /dev/null @@ -1,21 +0,0 @@ --- Add migration script here -CREATE EXTENSION pgcrypto; - -ALTER TABLE - account -ADD - COLUMN ssh_key BYTEA CHECK (length(ssh_key) = 32); - -UPDATE - account -SET - ssh_key = gen_random_bytes(32) -WHERE - id = 0; - -ALTER TABLE - account -ALTER COLUMN - ssh_key -SET - NOT NULL; \ No newline at end of file diff --git a/backend/migrations/20230118185232_NetworkKeys.sql b/backend/migrations/20230118185232_NetworkKeys.sql new file mode 100644 index 0000000000..44779d82b2 --- /dev/null +++ b/backend/migrations/20230118185232_NetworkKeys.sql @@ -0,0 +1,62 @@ +-- Add migration script here +CREATE EXTENSION pgcrypto; + +ALTER TABLE + account +ADD + COLUMN server_id TEXT, +ADD + COLUMN hostname TEXT, +ADD + COLUMN network_key BYTEA CHECK (length(network_key) = 32), +ADD + COLUMN root_ca_key_pem TEXT, +ADD + COLUMN root_ca_cert_pem TEXT; + +UPDATE + account +SET + network_key = gen_random_bytes(32), + root_ca_key_pem = ( + SELECT + priv_key_pem + FROM + certificates + WHERE + id = 0 + ), + root_ca_cert_pem = ( + SELECT + certificate_pem + FROM + certificates + WHERE + id = 0 + ) +WHERE + id = 0; + +ALTER TABLE + account +ALTER COLUMN + tor_key DROP NOT NULL, +ALTER COLUMN + network_key +SET + NOT NULL, +ALTER COLUMN + root_ca_key_pem +SET + NOT NULL, +ALTER COLUMN + root_ca_cert_pem +SET + NOT NULL; + +CREATE TABLE IF NOT EXISTS network_keys ( + package TEXT NOT NULL, + interface TEXT NOT NULL, + key BYTEA NOT NULL CHECK (length(key) = 32), + PRIMARY KEY (package, interface) +); \ No newline at end of file diff --git a/backend/sqlx-data.json b/backend/sqlx-data.json index 3e82660360..84afcfdb64 100644 --- a/backend/sqlx-data.json +++ b/backend/sqlx-data.json @@ -1,6 +1,6 @@ { "db": "PostgreSQL", - "094882d4d46d52e814f9aaf5fae172a5dd745b06cbde347f47b18e6498167269": { + "1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e": { "describe": { "columns": [], "nullable": [], @@ -8,35 +8,11 @@ "Left": [ "Text", "Text", - "Text" - ] - } - }, - "query": "UPDATE certificates SET priv_key_pem = $1, certificate_pem = $2, updated_at = now() WHERE lookup_string = $3" - }, - "165daa7d6a60cb42122373b2c5ac7d39399bcc99992f0002ee7bfef50a8daceb": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [] - } - }, - "query": "DELETE FROM certificates WHERE id = 0 OR id = 1;" - }, - "1f7936d27d63f01118ecd6f824e8a79607ed2b6e6def23f3e2487466dd2ddfe1": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" + "Bytea" ] } }, - "query": "INSERT INTO certificates (priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES ($1, $2, $3, now(), now())" + "query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING" }, "21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930": { "describe": { @@ -50,20 +26,6 @@ }, "query": "DELETE FROM ssh_keys WHERE fingerprint = $1" }, - "22613628ff50341fdc35366e194fdcd850118824763cfe0dfff68dadc72167e9": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Bytea" - ] - } - }, - "query": "INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET key = $3" - }, "28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec": { "describe": { "columns": [ @@ -102,39 +64,6 @@ }, "query": "SELECT hostname, path, username, password FROM cifs_shares WHERE id = $1" }, - "2f615764532e975c964f1d0e063a02110d781644b0eaae1ff85a7d6ed903bfe5": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int4", - "Text", - "Bytea", - "Bytea" - ] - } - }, - "query": "INSERT INTO account (id, password, tor_key, ssh_key) VALUES ($1, $2, $3, $4) ON CONFLICT (id) DO UPDATE SET password = $2, tor_key = $3, ssh_key = $4" - }, - "3502e58f2ab48fb4566d21c920c096f81acfa3ff0d02f970626a4dcd67bac71d": { - "describe": { - "columns": [ - { - "name": "tor_key", - "ordinal": 0, - "type_info": "Bytea" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [] - } - }, - "query": "SELECT tor_key FROM account" - }, "4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997": { "describe": { "columns": [ @@ -167,32 +96,6 @@ }, "query": "SELECT * FROM ssh_keys WHERE fingerprint = $1" }, - "46815a4ac2c43e1dfbab3c0017ed09d5c833062e523205db4245a5218b2562b8": { - "describe": { - "columns": [ - { - "name": "priv_key_pem", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "certificate_pem", - "ordinal": 1, - "type_info": "Text" - } - ], - "nullable": [ - false, - false - ], - "parameters": { - "Left": [ - "Text" - ] - } - }, - "query": "SELECT priv_key_pem, certificate_pem FROM certificates WHERE lookup_string = $1" - }, "4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96": { "describe": { "columns": [ @@ -253,42 +156,6 @@ }, "query": "UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = $1" }, - "548448e8ed8bcdf9efdc813d65af2cc55064685293b936f0f09e07f91a328eb9": { - "describe": { - "columns": [ - { - "name": "setval", - "ordinal": 0, - "type_info": "Int8" - } - ], - "nullable": [ - null - ], - "parameters": { - "Left": [] - } - }, - "query": "SELECT setval('certificates_id_seq', GREATEST(MAX(id) + 1, nextval('certificates_id_seq') - 1)) FROM certificates" - }, - "5c0ea94081695dba827e525ecc0c555757b43ea513c2c93f9c7f7f8c174d36bf": { - "describe": { - "columns": [ - { - "name": "ssh_key", - "ordinal": 0, - "type_info": "Bytea" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [] - } - }, - "query": "SELECT ssh_key FROM account" - }, "629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a": { "describe": { "columns": [ @@ -328,41 +195,39 @@ }, "query": "SELECT key FROM tor WHERE package = $1 AND interface = $2" }, - "6c96d76bffcc5f03290d8d8544a58521345ed2a843a509b17bbcd6257bb81821": { + "6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = $1 AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP" + }, + "770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0": { "describe": { "columns": [ { - "name": "priv_key_pem", + "name": "key", "ordinal": 0, - "type_info": "Text" - }, - { - "name": "certificate_pem", - "ordinal": 1, - "type_info": "Text" + "type_info": "Bytea" } ], "nullable": [ - false, false ], - "parameters": { - "Left": [] - } - }, - "query": "SELECT priv_key_pem, certificate_pem FROM certificates WHERE id = 1;" - }, - "6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca": { - "describe": { - "columns": [], - "nullable": [], "parameters": { "Left": [ - "Text" + "Text", + "Text", + "Bytea" ] } }, - "query": "UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = $1 AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP" + "query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET package = EXCLUDED.package RETURNING key" }, "7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210": { "describe": { @@ -427,6 +292,23 @@ }, "query": "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications WHERE id < $1 ORDER BY id DESC LIMIT $2" }, + "7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bytea", + "Text", + "Text" + ] + } + }, + "query": "\n INSERT INTO account (\n id,\n server_id,\n hostname,\n password,\n network_key,\n root_ca_key_pem,\n root_ca_cert_pem\n ) VALUES (\n 0, $1, $2, $3, $4, $5, $6\n ) ON CONFLICT (id) DO UPDATE SET\n server_id = EXCLUDED.server_id,\n hostname = EXCLUDED.hostname,\n password = EXCLUDED.password,\n network_key = EXCLUDED.network_key,\n root_ca_key_pem = EXCLUDED.root_ca_key_pem,\n root_ca_cert_pem = EXCLUDED.root_ca_cert_pem\n " + }, "7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576": { "describe": { "columns": [], @@ -615,19 +497,6 @@ }, "query": "UPDATE cifs_shares SET hostname = $1, path = $2, username = $3, password = $4 WHERE id = $5" }, - "cec8112be0ebc02ef7e651631be09efe26d1677b5b8aa95ceb3a92aff1afdbcc": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Text", - "Text" - ] - } - }, - "query": "INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (1, $1, $2, NULL, now(), now())" - }, "d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca": { "describe": { "columns": [ @@ -663,19 +532,6 @@ }, "query": "INSERT INTO notifications (package_id, code, level, title, message, data) VALUES ($1, $2, $3, $4, $5, $6)" }, - "df4428ccb891bd791824bcd7990550cc9651e1cfaab1db33833ff7959d113b2c": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Text", - "Text" - ] - } - }, - "query": "INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (0, $1, $2, NULL, now(), now())" - }, "e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7": { "describe": { "columns": [], @@ -688,17 +544,23 @@ }, "query": "DELETE FROM notifications WHERE id = $1" }, - "e25e53c45c5a494a45cdb4d145de507df6f322ac6706e71b86895f1c64195f41": { + "e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0": { "describe": { - "columns": [], - "nullable": [], + "columns": [ + { + "name": "tor_key", + "ordinal": 0, + "type_info": "Bytea" + } + ], + "nullable": [ + true + ], "parameters": { - "Left": [ - "Text" - ] + "Left": [] } }, - "query": "UPDATE account SET password = $1" + "query": "SELECT tor_key FROM account WHERE id = 0" }, "e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d": { "describe": { @@ -714,21 +576,33 @@ }, "query": "INSERT INTO session (id, user_agent, metadata) VALUES ($1, $2, $3)" }, - "e85749336fce4afaf16627bee8cfcb70be6f189ea7d1f05f9a26bead4be11839": { + "e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed": { "describe": { "columns": [ { - "name": "interface", + "name": "package", "ordinal": 0, "type_info": "Text" }, { - "name": "key", + "name": "interface", "ordinal": 1, + "type_info": "Text" + }, + { + "name": "key", + "ordinal": 2, + "type_info": "Bytea" + }, + { + "name": "tor_key?", + "ordinal": 3, "type_info": "Bytea" } ], "nullable": [ + false, + false, false, false ], @@ -738,7 +612,7 @@ ] } }, - "query": "SELECT interface, key FROM tor WHERE package = $1" + "query": "\n SELECT\n network_keys.package,\n network_keys.interface,\n network_keys.key,\n tor.key AS \"tor_key?\"\n FROM\n network_keys\n LEFT JOIN\n tor\n ON\n network_keys.package = tor.package\n AND\n network_keys.interface = tor.interface\n WHERE\n network_keys.package = $1\n " }, "eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a": { "describe": { @@ -775,42 +649,96 @@ }, "query": "INSERT INTO cifs_shares (hostname, path, username, password) VALUES ($1, $2, $3, $4) RETURNING id" }, - "ed848affa5bf92997cd441e3a50b3616b6724df3884bd9d199b3225e0bea8a54": { + "f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + } + }, + "query": "INSERT INTO ssh_keys (fingerprint, openssh_pubkey, created_at) VALUES ($1, $2, $3)" + }, + "f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5": { "describe": { "columns": [ { - "name": "priv_key_pem", + "name": "network_key", "ordinal": 0, - "type_info": "Text" - }, - { - "name": "certificate_pem", - "ordinal": 1, - "type_info": "Text" + "type_info": "Bytea" } ], "nullable": [ - false, false ], "parameters": { "Left": [] } }, - "query": "SELECT priv_key_pem, certificate_pem FROM certificates WHERE id = 0;" + "query": "SELECT network_key FROM account WHERE id = 0" }, - "f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556": { + "fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f": { "describe": { - "columns": [], - "nullable": [], + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int4" + }, + { + "name": "password", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "tor_key", + "ordinal": 2, + "type_info": "Bytea" + }, + { + "name": "server_id", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "hostname", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "network_key", + "ordinal": 5, + "type_info": "Bytea" + }, + { + "name": "root_ca_key_pem", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "root_ca_cert_pem", + "ordinal": 7, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + true, + true, + true, + false, + false, + false + ], "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] + "Left": [] } }, - "query": "INSERT INTO ssh_keys (fingerprint, openssh_pubkey, created_at) VALUES ($1, $2, $3)" + "query": "SELECT * FROM account WHERE id = 0" } } \ No newline at end of file diff --git a/backend/src/account.rs b/backend/src/account.rs new file mode 100644 index 0000000000..daae7bee72 --- /dev/null +++ b/backend/src/account.rs @@ -0,0 +1,120 @@ +use ed25519_dalek::{ExpandedSecretKey, SecretKey}; +use models::ResultExt; +use openssl::pkey::{PKey, Private}; +use openssl::x509::X509; +use sqlx::PgExecutor; + +use crate::hostname::{generate_hostname, generate_id, Hostname}; +use crate::net::keys::Key; +use crate::net::ssl::{generate_key, make_root_cert}; +use crate::Error; + +fn hash_password(password: &str) -> Result { + argon2::hash_encoded( + password.as_bytes(), + &rand::random::<[u8; 16]>()[..], + &argon2::Config::default(), + ) + .with_kind(crate::ErrorKind::PasswordHashGeneration) +} + +#[derive(Debug, Clone)] +pub struct AccountInfo { + pub server_id: String, + pub hostname: Hostname, + pub password: String, + pub key: Key, + pub root_ca_key: PKey, + pub root_ca_cert: X509, +} +impl AccountInfo { + pub fn new(password: &str) -> Result { + let server_id = generate_id(); + let hostname = generate_hostname(); + let root_ca_key = generate_key()?; + let root_ca_cert = make_root_cert(&root_ca_key, &hostname)?; + Ok(Self { + server_id, + hostname, + password: hash_password(password)?, + key: Key::new(None), + root_ca_key, + root_ca_cert, + }) + } + + pub async fn load(secrets: impl PgExecutor<'_>) -> Result { + let r = sqlx::query!("SELECT * FROM account WHERE id = 0") + .fetch_one(secrets) + .await?; + + let server_id = r.server_id.unwrap_or_else(generate_id); + let hostname = r.hostname.map(Hostname).unwrap_or_else(generate_hostname); + let password = r.password; + let network_key = SecretKey::from_bytes(&r.network_key)?; + let tor_key = if let Some(k) = &r.tor_key { + ExpandedSecretKey::from_bytes(k)? + } else { + ExpandedSecretKey::from(&network_key) + }; + let key = Key::from_pair(None, network_key.to_bytes(), tor_key.to_bytes()); + let root_ca_key = PKey::private_key_from_pem(r.root_ca_key_pem.as_bytes())?; + let root_ca_cert = X509::from_pem(r.root_ca_cert_pem.as_bytes())?; + + Ok(Self { + server_id, + hostname, + password, + key, + root_ca_key, + root_ca_cert, + }) + } + + pub async fn save(&self, secrets: impl PgExecutor<'_>) -> Result<(), Error> { + let server_id = self.server_id.as_str(); + let hostname = self.hostname.0.as_str(); + let password = self.password.as_str(); + let network_key = self.key.as_bytes(); + let network_key = network_key.as_slice(); + let root_ca_key = String::from_utf8(self.root_ca_key.private_key_to_pem_pkcs8()?)?; + let root_ca_cert = String::from_utf8(self.root_ca_cert.to_pem()?)?; + + sqlx::query!( + r#" + INSERT INTO account ( + id, + server_id, + hostname, + password, + network_key, + root_ca_key_pem, + root_ca_cert_pem + ) VALUES ( + 0, $1, $2, $3, $4, $5, $6 + ) ON CONFLICT (id) DO UPDATE SET + server_id = EXCLUDED.server_id, + hostname = EXCLUDED.hostname, + password = EXCLUDED.password, + network_key = EXCLUDED.network_key, + root_ca_key_pem = EXCLUDED.root_ca_key_pem, + root_ca_cert_pem = EXCLUDED.root_ca_cert_pem + "#, + server_id, + hostname, + password, + network_key, + root_ca_key, + root_ca_cert, + ) + .execute(secrets) + .await?; + + Ok(()) + } + + pub fn set_password(&mut self, password: &str) -> Result<(), Error> { + self.password = hash_password(password)?; + Ok(()) + } +} diff --git a/backend/src/action.rs b/backend/src/action.rs index 54f0cad02e..29ea978268 100644 --- a/backend/src/action.rs +++ b/backend/src/action.rs @@ -4,13 +4,13 @@ use clap::ArgMatches; use color_eyre::eyre::eyre; use indexmap::IndexSet; pub use models::ActionId; +use models::ImageId; use rpc_toolkit::command; use serde::{Deserialize, Serialize}; use tracing::instrument; use crate::config::{Config, ConfigSpec}; use crate::context::RpcContext; -use crate::id::ImageId; use crate::procedure::docker::DockerContainers; use crate::procedure::{PackageProcedure, ProcedureName}; use crate::s9pk::manifest::PackageId; diff --git a/backend/src/auth.rs b/backend/src/auth.rs index 4e54556513..75ed13b4cb 100644 --- a/backend/src/auth.rs +++ b/backend/src/auth.rs @@ -364,31 +364,6 @@ impl SetPasswordReceipt { } } -pub async fn set_password( - db: &mut Db, - receipt: &SetPasswordReceipt, - secrets: &mut Ex, - password: &str, -) -> Result<(), Error> -where - for<'a> &'a mut Ex: Executor<'a, Database = Postgres>, -{ - let password = argon2::hash_encoded( - password.as_bytes(), - &rand::random::<[u8; 16]>()[..], - &argon2::Config::default(), - ) - .with_kind(crate::ErrorKind::PasswordHashGeneration)?; - - sqlx::query!("UPDATE account SET password = $1", password,) - .execute(secrets) - .await?; - - receipt.0.set(db, password).await?; - - Ok(()) -} - #[command( rename = "reset-password", custom_cli(cli_reset_password(async, context(CliContext))), @@ -403,14 +378,22 @@ pub async fn reset_password( let old_password = old_password.unwrap_or_default().decrypt(&ctx)?; let new_password = new_password.unwrap_or_default().decrypt(&ctx)?; - let mut secrets = ctx.secret_store.acquire().await?; - check_password_against_db(&mut secrets, &old_password).await?; - - let mut db = ctx.db.handle(); - - let set_password_receipt = SetPasswordReceipt::new(&mut db).await?; - - set_password(&mut db, &set_password_receipt, &mut secrets, &new_password).await?; + let mut account = ctx.account.write().await; + if !argon2::verify_encoded(&account.password, old_password.as_bytes()) + .with_kind(crate::ErrorKind::IncorrectPassword)? + { + return Err(Error::new( + eyre!("Incorrect Password"), + crate::ErrorKind::IncorrectPassword, + )); + } + account.set_password(&new_password)?; + account.save(&ctx.secret_store).await?; + crate::db::DatabaseModel::new() + .server_info() + .password_hash() + .put(&mut ctx.db.handle(), &account.password) + .await?; Ok(()) } diff --git a/backend/src/backup/backup_bulk.rs b/backend/src/backup/backup_bulk.rs index e9d1d7e763..75bd9584cc 100644 --- a/backend/src/backup/backup_bulk.rs +++ b/backend/src/backup/backup_bulk.rs @@ -5,21 +5,15 @@ use chrono::Utc; use clap::ArgMatches; use color_eyre::eyre::eyre; use helpers::AtomicFile; -use openssl::pkey::{PKey, Private}; -use openssl::x509::X509; use patch_db::{DbHandle, LockType, PatchDbHandle}; -use rand::random; use rpc_toolkit::command; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use ssh_key::private::Ed25519PrivateKey; use tokio::io::AsyncWriteExt; -use torut::onion::TorSecretKeyV3; use tracing::instrument; use super::target::BackupTargetId; use super::PackageBackupReport; use crate::auth::check_password_against_db; +use crate::backup::os::OsBackup; use crate::backup::{BackupReport, ServerBackupReport}; use crate::context::RpcContext; use crate::db::model::BackupProgress; @@ -34,109 +28,6 @@ use crate::util::serde::IoFormat; use crate::version::VersionT; use crate::{Error, ErrorKind, ResultExt}; -#[derive(Debug)] -pub struct OsBackup { - pub tor_key: TorSecretKeyV3, - pub ssh_key: Ed25519PrivateKey, - pub root_ca_key: PKey, - pub root_ca_cert: X509, - pub ui: Value, -} -impl<'de> Deserialize<'de> for OsBackup { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(rename = "kebab-case")] - struct OsBackupDe { - tor_key: String, - ssh_key: Option, - root_ca_key: String, - root_ca_cert: String, - ui: Value, - } - fn vec_from_base32(base32: &str, len: usize) -> Result, E> { - let key_vec = base32::decode(base32::Alphabet::RFC4648 { padding: true }, base32) - .ok_or_else(|| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(base32), - &"an RFC4648 encoded string", - ) - })?; - if key_vec.len() != 64 { - return Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Str(base32), - &"a 64 byte value encoded as an RFC4648 string", - )); - } - Ok(key_vec) - } - let int = OsBackupDe::deserialize(deserializer)?; - let tor_key = { - let mut key_slice = [0; 64]; - key_slice.clone_from_slice(&vec_from_base32(&int.tor_key, 64)?); - TorSecretKeyV3::from(key_slice) - }; - let ssh_key = int - .ssh_key - .as_ref() - .map(|ssh_key| { - let mut key_slice = [0; 32]; - key_slice.clone_from_slice(&vec_from_base32(ssh_key, 32)?); - Ok(Ed25519PrivateKey::from_bytes(&key_slice)) - }) - .transpose()? - .unwrap_or_else(|| Ed25519PrivateKey::from_bytes(&random())); - let root_ca_key = PKey::::private_key_from_pem(int.root_ca_key.as_bytes()) - .map_err(serde::de::Error::custom)?; - let root_ca_cert = - X509::from_pem(int.root_ca_cert.as_bytes()).map_err(serde::de::Error::custom)?; - Ok(OsBackup { - tor_key, - ssh_key, - root_ca_key, - root_ca_cert, - ui: int.ui, - }) - } -} -impl Serialize for OsBackup { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - #[derive(Serialize)] - #[serde(rename = "kebab-case")] - struct OsBackupSer<'a> { - tor_key: String, - root_ca_key: String, - root_ca_cert: String, - ui: &'a Value, - } - OsBackupSer { - tor_key: base32::encode( - base32::Alphabet::RFC4648 { padding: true }, - &self.tor_key.as_bytes(), - ), - root_ca_key: String::from_utf8( - self.root_ca_key - .private_key_to_pem_pkcs8() - .map_err(serde::ser::Error::custom)?, - ) - .map_err(serde::ser::Error::custom)?, - root_ca_cert: String::from_utf8( - self.root_ca_cert - .to_pem() - .map_err(serde::ser::Error::custom)?, - ) - .map_err(serde::ser::Error::custom)?, - ui: &self.ui, - } - .serialize(serializer) - } -} - fn parse_comma_separated(arg: &str, _: &ArgMatches) -> Result, Error> { arg.split(',') .map(|s| s.trim().parse().map_err(Error::from)) @@ -317,7 +208,6 @@ async fn perform_backup( package_ids: &BTreeSet, ) -> Result, Error> { let mut backup_report = BTreeMap::new(); - for package_id in crate::db::DatabaseModel::new() .package_data() .keys(&mut db) @@ -445,11 +335,12 @@ async fn perform_backup( tx.save().await?; } - crate::db::DatabaseModel::new() - .lock(&mut db, LockType::Write) - .await?; + let ui = crate::db::DatabaseModel::new() + .ui() + .get(&mut db) + .await? + .into_owned(); - let (root_ca_key, root_ca_cert) = ctx.net_controller.ssl.export_root_ca().await?; let mut os_backup_file = AtomicFile::new( backup_guard.as_ref().join("os-backup.cbor"), None::, @@ -457,19 +348,10 @@ async fn perform_backup( .await .with_kind(ErrorKind::Filesystem)?; os_backup_file - .write_all( - &IoFormat::Cbor.to_vec(&OsBackup { - tor_key: ctx.net_controller.tor.embassyd_tor_key().await, - ssh_key: crate::ssh::os_key(&mut ctx.secret_store.acquire().await?).await?, - root_ca_key, - root_ca_cert, - ui: crate::db::DatabaseModel::new() - .ui() - .get(&mut db) - .await? - .into_owned(), - })?, - ) + .write_all(&IoFormat::Cbor.to_vec(&OsBackup { + account: ctx.account.read().await.clone(), + ui, + })?) .await?; os_backup_file .save() diff --git a/backend/src/backup/mod.rs b/backend/src/backup/mod.rs index e178ad2fa9..a283b18cee 100644 --- a/backend/src/backup/mod.rs +++ b/backend/src/backup/mod.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use chrono::{DateTime, Utc}; use color_eyre::eyre::eyre; use helpers::AtomicFile; +use models::ImageId; use patch_db::{DbHandle, HasModel}; use reqwest::Url; use rpc_toolkit::command; @@ -15,19 +16,20 @@ use tracing::instrument; use self::target::PackageBackupInfo; use crate::context::RpcContext; use crate::dependencies::reconfigure_dependents_with_live_pointers; -use crate::id::ImageId; use crate::install::PKG_ARCHIVE_DIR; use crate::net::interface::{InterfaceId, Interfaces}; +use crate::net::keys::Key; use crate::procedure::docker::DockerContainers; use crate::procedure::{NoOutput, PackageProcedure, ProcedureName}; use crate::s9pk::manifest::PackageId; -use crate::util::serde::IoFormat; +use crate::util::serde::{Base32, Base64, IoFormat}; use crate::util::Version; use crate::version::{Current, VersionT}; use crate::volume::{backup_dir, Volume, VolumeId, Volumes, BACKUP_DIR}; use crate::{Error, ErrorKind, ResultExt}; pub mod backup_bulk; +pub mod os; pub mod restore; pub mod target; @@ -61,7 +63,10 @@ pub fn package_backup() -> Result<(), Error> { #[derive(Deserialize, Serialize)] struct BackupMetadata { pub timestamp: DateTime, - pub tor_keys: BTreeMap, + #[serde(default)] + pub network_keys: BTreeMap>, + #[serde(default)] + pub tor_keys: BTreeMap>, // DEPRECATED pub marketplace_url: Option, } @@ -117,17 +122,17 @@ impl BackupActions { .await? .map_err(|e| eyre!("{}", e.1)) .with_kind(crate::ErrorKind::Backup)?; - let tor_keys = interfaces - .tor_keys(&mut ctx.secret_store.acquire().await?, pkg_id) + let (network_keys, tor_keys) = Key::for_package(&ctx.secret_store, pkg_id) .await? .into_iter() - .map(|(id, key)| { - ( - id, - base32::encode(base32::Alphabet::RFC4648 { padding: true }, &key.as_bytes()), - ) + .filter_map(|k| { + let interface = k.interface().map(|(_, i)| i)?; + Some(( + (interface.clone(), Base64(k.as_bytes())), + (interface, Base32(k.tor_key().as_bytes())), + )) }) - .collect(); + .unzip(); let marketplace_url = crate::db::DatabaseModel::new() .package_data() .idx_model(pkg_id) @@ -170,6 +175,7 @@ impl BackupActions { outfile .write_all(&IoFormat::Cbor.to_vec(&BackupMetadata { timestamp, + network_keys, tor_keys, marketplace_url, })?) diff --git a/backend/src/backup/os.rs b/backend/src/backup/os.rs new file mode 100644 index 0000000000..5cca8532db --- /dev/null +++ b/backend/src/backup/os.rs @@ -0,0 +1,121 @@ +use crate::account::AccountInfo; +use crate::hostname::{generate_hostname, generate_id, Hostname}; +use crate::net::keys::Key; +use crate::util::serde::Base64; +use crate::Error; +use openssl::pkey::PKey; +use openssl::x509::X509; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +pub struct OsBackup { + pub account: AccountInfo, + pub ui: Value, +} +impl<'de> Deserialize<'de> for OsBackup { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let tagged = OsBackupSerDe::deserialize(deserializer)?; + match tagged.version { + 0 => serde_json::from_value::(tagged.rest) + .map_err(serde::de::Error::custom)? + .project() + .map_err(serde::de::Error::custom), + 1 => serde_json::from_value::(tagged.rest) + .map_err(serde::de::Error::custom)? + .project() + .map_err(serde::de::Error::custom), + v => Err(serde::de::Error::custom(&format!( + "Unknown backup version {v}" + ))), + } + } +} +impl Serialize for OsBackup { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + OsBackupSerDe { + version: 1, + rest: serde_json::to_value( + &OsBackupV1::unproject(self).map_err(serde::ser::Error::custom)?, + ) + .map_err(serde::ser::Error::custom)?, + } + .serialize(serializer) + } +} + +#[derive(Deserialize, Serialize)] +struct OsBackupSerDe { + #[serde(default)] + version: usize, + #[serde(flatten)] + rest: Value, +} + +/// V0 +#[derive(Deserialize)] +#[serde(rename = "kebab-case")] +struct OsBackupV0 { + // tor_key: Base32<[u8; 64]>, + root_ca_key: String, // PEM Encoded OpenSSL Key + root_ca_cert: String, // PEM Encoded OpenSSL X509 Certificate + ui: Value, // JSON Value +} +impl OsBackupV0 { + fn project(self) -> Result { + Ok(OsBackup { + account: AccountInfo { + server_id: generate_id(), + hostname: generate_hostname(), + password: Default::default(), + key: Key::new(None), + root_ca_key: PKey::private_key_from_pem(self.root_ca_key.as_bytes())?, + root_ca_cert: X509::from_pem(self.root_ca_cert.as_bytes())?, + }, + ui: self.ui, + }) + } +} + +/// V1 +#[derive(Deserialize, Serialize)] +#[serde(rename = "kebab-case")] +struct OsBackupV1 { + server_id: String, // uuidv4 + hostname: String, // embassy-- + net_key: Base64<[u8; 32]>, // Ed25519 Secret Key + root_ca_key: String, // PEM Encoded OpenSSL Key + root_ca_cert: String, // PEM Encoded OpenSSL X509 Certificate + ui: Value, // JSON Value + // TODO add more +} +impl OsBackupV1 { + fn project(self) -> Result { + Ok(OsBackup { + account: AccountInfo { + server_id: self.server_id, + hostname: Hostname(self.hostname), + password: Default::default(), + key: Key::from_bytes(None, self.net_key.0), + root_ca_key: PKey::private_key_from_pem(self.root_ca_key.as_bytes())?, + root_ca_cert: X509::from_pem(self.root_ca_cert.as_bytes())?, + }, + ui: self.ui, + }) + } + fn unproject(backup: &OsBackup) -> Result { + Ok(Self { + server_id: backup.account.server_id.clone(), + hostname: backup.account.hostname.0.clone(), + net_key: Base64(backup.account.key.as_bytes()), + root_ca_key: String::from_utf8(backup.account.root_ca_key.private_key_to_pem_pkcs8()?)?, + root_ca_cert: String::from_utf8(backup.account.root_ca_cert.to_pem()?)?, + ui: backup.ui.clone(), + }) + } +} diff --git a/backend/src/backup/restore.rs b/backend/src/backup/restore.rs index 2ce0255f7d..f1ae4028b2 100644 --- a/backend/src/backup/restore.rs +++ b/backend/src/backup/restore.rs @@ -11,12 +11,13 @@ use futures::{FutureExt, StreamExt}; use openssl::x509::X509; use patch_db::{DbHandle, PatchDbHandle}; use rpc_toolkit::command; +use sqlx::Connection; use tokio::fs::File; use torut::onion::OnionAddressV3; use tracing::instrument; use super::target::BackupTargetId; -use crate::backup::backup_bulk::OsBackup; +use crate::backup::os::OsBackup; use crate::backup::BackupMetadata; use crate::context::rpc::RpcContextConfig; use crate::context::{RpcContext, SetupContext}; @@ -24,11 +25,10 @@ use crate::db::model::{PackageDataEntry, StaticFiles}; use crate::disk::mount::backup::{BackupMountGuard, PackageBackupMountGuard}; use crate::disk::mount::filesystem::ReadWrite; use crate::disk::mount::guard::TmpMountGuard; -use crate::hostname::{get_hostname, Hostname}; +use crate::hostname::Hostname; use crate::init::init; use crate::install::progress::InstallProgress; use crate::install::{download_install_s9pk, PKG_PUBLIC_DIR}; -use crate::net::ssl::SslManager; use crate::notifications::NotificationLevel; use crate::s9pk::manifest::{Manifest, PackageId}; use crate::s9pk::reader::S9pkReader; @@ -184,7 +184,7 @@ pub async fn recover_full_embassy( .await?; let os_backup_path = backup_guard.as_ref().join("os-backup.cbor"); - let os_backup: OsBackup = + let mut os_backup: OsBackup = IoFormat::Cbor.from_slice(&tokio::fs::read(&os_backup_path).await.with_ctx(|_| { ( crate::ErrorKind::Filesystem, @@ -192,31 +192,17 @@ pub async fn recover_full_embassy( ) })?)?; - let password = argon2::hash_encoded( + os_backup.account.password = argon2::hash_encoded( embassy_password.as_bytes(), &rand::random::<[u8; 16]>()[..], &argon2::Config::default(), ) .with_kind(crate::ErrorKind::PasswordHashGeneration)?; - let tor_key_bytes = os_backup.tor_key.as_bytes().to_vec(); - let ssh_key_bytes = os_backup.ssh_key.to_bytes().to_vec(); + let secret_store = ctx.secret_store().await?; - sqlx::query!( - "INSERT INTO account (id, password, tor_key, ssh_key) VALUES ($1, $2, $3, $4) ON CONFLICT (id) DO UPDATE SET password = $2, tor_key = $3, ssh_key = $4", - 0, - password, - tor_key_bytes, - ssh_key_bytes, - ) - .execute(&mut secret_store.acquire().await?) - .await?; - SslManager::import_root_ca( - secret_store.clone(), - os_backup.root_ca_key, - os_backup.root_ca_cert.clone(), - ) - .await?; + os_backup.account.save(&secret_store).await?; + secret_store.close().await; let cfg = RpcContextConfig::load(ctx.config_path.clone()).await?; @@ -224,12 +210,7 @@ pub async fn recover_full_embassy( init(&cfg).await?; let rpc_ctx = RpcContext::init(ctx.config_path.clone(), disk_guid.clone()).await?; - let mut db = rpc_ctx.db.handle(); - - let receipts = crate::hostname::HostNameReceipt::new(&mut db).await?; - let hostname = get_hostname(&mut db, &receipts).await?; - drop(db); let mut db = rpc_ctx.db.handle(); let ids = backup_guard @@ -274,9 +255,9 @@ pub async fn recover_full_embassy( Ok(( disk_guid, - hostname, - os_backup.tor_key.public().get_onion_address(), - os_backup.root_ca_cert, + os_backup.account.hostname, + os_backup.account.key.tor_address(), + os_backup.account.root_ca_cert, )) } @@ -414,23 +395,32 @@ async fn restore_package<'a>( metadata_path.display().to_string(), ) })?)?; + + let mut secrets = ctx.secret_store.acquire().await?; + let mut secrets_tx = secrets.begin().await?; + for (iface, key) in metadata.network_keys { + let k = key.0.as_slice(); + sqlx::query!( + "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING", + *id, + *iface, + k, + ) + .execute(&mut secrets_tx).await?; + } + // DEPRECATED for (iface, key) in metadata.tor_keys { - let key_vec = base32::decode(base32::Alphabet::RFC4648 { padding: true }, &key) - .ok_or_else(|| { - Error::new( - eyre!("invalid base32 string"), - crate::ErrorKind::Deserialization, - ) - })?; + let k = key.0.as_slice(); sqlx::query!( - "INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET key = $3", - *id, - *iface, - key_vec, - ) - .execute(&ctx.secret_store) - .await?; + "INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING", + *id, + *iface, + k, + ) + .execute(&mut secrets_tx).await?; } + secrets_tx.commit().await?; + drop(secrets); let len = tokio::fs::metadata(&s9pk_path) .await diff --git a/backend/src/bin/embassy-init.rs b/backend/src/bin/embassy-init.rs index 7194421326..7c3608c02f 100644 --- a/backend/src/bin/embassy-init.rs +++ b/backend/src/bin/embassy-init.rs @@ -8,13 +8,7 @@ use embassy::disk::fsck::RepairStrategy; use embassy::disk::main::DEFAULT_PASSWORD; use embassy::disk::REPAIR_DISK_PATH; use embassy::init::STANDBY_MODE_PATH; -use embassy::net::embassy_service_http_server::EmbassyServiceHTTPServer; -#[cfg(feature = "avahi")] -use embassy::net::mdns::MdnsController; -use embassy::net::net_utils::ResourceFqdn; -use embassy::net::static_server::{ - diag_ui_file_router, install_ui_file_router, setup_ui_file_router, -}; +use embassy::net::web_server::WebServer; use embassy::shutdown::Shutdown; use embassy::sound::CHIME; use embassy::util::logger::EmbassyLogger; @@ -26,30 +20,9 @@ use tracing::instrument; #[instrument] async fn setup_or_init(cfg_path: Option) -> Result<(), Error> { if tokio::fs::metadata("/cdrom").await.is_ok() { - #[cfg(feature = "avahi")] - let _mdns = MdnsController::init().await?; - let ctx = InstallContext::init(cfg_path).await?; - let embassy_ip_fqdn: ResourceFqdn = ResourceFqdn::IpAddr; - let embassy_fqdn: ResourceFqdn = "embassy.local".parse()?; - - let localhost_fqdn = ResourceFqdn::LocalHost; - - let install_ui_handler = install_ui_file_router(ctx.clone()).await?; - - let mut install_http_server = - EmbassyServiceHTTPServer::new([0, 0, 0, 0].into(), 80, None).await?; - install_http_server - .add_svc_handler_mapping(embassy_ip_fqdn, install_ui_handler.clone()) - .await?; - install_http_server - .add_svc_handler_mapping(embassy_fqdn, install_ui_handler.clone()) - .await?; - - install_http_server - .add_svc_handler_mapping(localhost_fqdn, install_ui_handler.clone()) - .await?; + let server = WebServer::install(([0, 0, 0, 0], 80).into(), ctx.clone()).await?; tokio::time::sleep(Duration::from_secs(1)).await; // let the record state that I hate this CHIME.play().await?; @@ -59,7 +32,9 @@ async fn setup_or_init(cfg_path: Option) -> Result<(), Error> { .recv() .await .expect("context dropped"); - install_http_server.shutdown.send(()).unwrap(); + + server.shutdown().await; + Command::new("reboot") .invoke(embassy::ErrorKind::Unknown) .await?; @@ -67,29 +42,9 @@ async fn setup_or_init(cfg_path: Option) -> Result<(), Error> { .await .is_err() { - #[cfg(feature = "avahi")] - let _mdns = MdnsController::init().await?; - let ctx = SetupContext::init(cfg_path).await?; - let embassy_ip_fqdn: ResourceFqdn = ResourceFqdn::IpAddr; - let embassy_fqdn: ResourceFqdn = "embassy.local".parse()?; - let localhost_fqdn = ResourceFqdn::LocalHost; - - let setup_ui_handler = setup_ui_file_router(ctx.clone()).await?; - - let mut setup_http_server = - EmbassyServiceHTTPServer::new([0, 0, 0, 0].into(), 80, None).await?; - setup_http_server - .add_svc_handler_mapping(embassy_ip_fqdn, setup_ui_handler.clone()) - .await?; - setup_http_server - .add_svc_handler_mapping(embassy_fqdn, setup_ui_handler.clone()) - .await?; - - setup_http_server - .add_svc_handler_mapping(localhost_fqdn, setup_ui_handler.clone()) - .await?; + let server = WebServer::setup(([0, 0, 0, 0], 80).into(), ctx.clone()).await?; tokio::time::sleep(Duration::from_secs(1)).await; // let the record state that I hate this CHIME.play().await?; @@ -98,7 +53,9 @@ async fn setup_or_init(cfg_path: Option) -> Result<(), Error> { .recv() .await .expect("context dropped"); - setup_http_server.shutdown.send(()).unwrap(); + + server.shutdown().await; + tokio::task::yield_now().await; if let Err(e) = Command::new("killall") .arg("firefox-esr") @@ -178,8 +135,6 @@ async fn inner_main(cfg_path: Option) -> Result, Error tracing::error!("{}", e.source); tracing::debug!("{}", e.source); embassy::sound::BEETHOVEN.play().await?; - #[cfg(feature = "avahi")] - let _mdns = MdnsController::init().await?; let ctx = DiagnosticContext::init( cfg_path, @@ -200,28 +155,12 @@ async fn inner_main(cfg_path: Option) -> Result, Error ) .await?; - let embassy_ip_fqdn: ResourceFqdn = ResourceFqdn::IpAddr; - let embassy_fqdn: ResourceFqdn = "embassy.local".parse()?; - - let localhost_fqdn = ResourceFqdn::LocalHost; + let server = WebServer::diagnostic(([0, 0, 0, 0], 80).into(), ctx.clone()).await?; - let diag_ui_handler = diag_ui_file_router(ctx.clone()).await?; - - let mut diag_http_server = - EmbassyServiceHTTPServer::new([0, 0, 0, 0].into(), 80, None).await?; - diag_http_server - .add_svc_handler_mapping(embassy_ip_fqdn, diag_ui_handler.clone()) - .await?; - diag_http_server - .add_svc_handler_mapping(embassy_fqdn, diag_ui_handler.clone()) - .await?; + let shutdown = ctx.shutdown.subscribe().recv().await.unwrap(); - diag_http_server - .add_svc_handler_mapping(localhost_fqdn, diag_ui_handler.clone()) - .await?; + server.shutdown().await; - let shutdown = ctx.shutdown.subscribe().recv().await.unwrap(); - diag_http_server.shutdown.send(()).unwrap(); Ok(shutdown) } .await diff --git a/backend/src/bin/embassyd.rs b/backend/src/bin/embassyd.rs index 308302fce3..aec0e122a1 100644 --- a/backend/src/bin/embassyd.rs +++ b/backend/src/bin/embassyd.rs @@ -3,12 +3,7 @@ use std::sync::Arc; use color_eyre::eyre::eyre; use embassy::context::{DiagnosticContext, RpcContext}; -use embassy::net::embassy_service_http_server::EmbassyServiceHTTPServer; -#[cfg(feature = "avahi")] -use embassy::net::mdns::MdnsController; -use embassy::net::net_controller::NetController; -use embassy::net::net_utils::ResourceFqdn; -use embassy::net::static_server::diag_ui_file_router; +use embassy::net::web_server::WebServer; use embassy::shutdown::Shutdown; use embassy::system::launch_metrics_task; use embassy::util::logger::EmbassyLogger; @@ -19,7 +14,7 @@ use tracing::instrument; #[instrument] async fn inner_main(cfg_path: Option) -> Result, Error> { - let (rpc_ctx, shutdown) = { + let (rpc_ctx, server, shutdown) = { let rpc_ctx = RpcContext::init( cfg_path, Arc::new( @@ -30,7 +25,8 @@ async fn inner_main(cfg_path: Option) -> Result, Error ), ) .await?; - NetController::setup_embassy_ui(rpc_ctx.clone()).await?; + embassy::hostname::sync_hostname(&*rpc_ctx.account.read().await).await?; + let server = WebServer::main(([0, 0, 0, 0], 80).into(), rpc_ctx.clone()).await?; let mut shutdown_recv = rpc_ctx.shutdown.subscribe(); @@ -62,12 +58,6 @@ async fn inner_main(cfg_path: Option) -> Result, Error .expect("send shutdown signal"); }); - { - let mut db = rpc_ctx.db.handle(); - let receipts = embassy::context::rpc::RpcSetHostNameReceipts::new(&mut db).await?; - embassy::hostname::sync_hostname(&mut db, &receipts.hostname_receipts).await?; - } - let metrics_ctx = rpc_ctx.clone(); let metrics_task = tokio::spawn(async move { launch_metrics_task(&metrics_ctx.metrics_cache, || { @@ -95,8 +85,9 @@ async fn inner_main(cfg_path: Option) -> Result, Error sig_handler.abort(); - (rpc_ctx, shutdown) + (rpc_ctx, server, shutdown) }; + server.shutdown().await; rpc_ctx.shutdown().await?; Ok(shutdown) @@ -125,12 +116,10 @@ fn main() { match inner_main(cfg_path.clone()).await { Ok(a) => Ok(a), Err(e) => { - (|| async { + async { tracing::error!("{}", e.source); tracing::debug!("{:?}", e.source); embassy::sound::BEETHOVEN.play().await?; - #[cfg(feature = "avahi")] - let _mdns = MdnsController::init().await?; let ctx = DiagnosticContext::init( cfg_path, if tokio::fs::metadata("/media/embassy/config/disk.guid") @@ -150,24 +139,18 @@ fn main() { ) .await?; - let embassy_ip_fqdn: ResourceFqdn = ResourceFqdn::IpAddr; - let embassy_fqdn: ResourceFqdn = "embassy.local".parse()?; + let server = + WebServer::diagnostic(([0, 0, 0, 0], 80).into(), ctx.clone()).await?; - let diag_ui_handler = diag_ui_file_router(ctx.clone()).await?; + let mut shutdown = ctx.shutdown.subscribe(); - let mut diag_http_server = - EmbassyServiceHTTPServer::new([0, 0, 0, 0].into(), 80, None).await?; - diag_http_server - .add_svc_handler_mapping(embassy_ip_fqdn, diag_ui_handler.clone()) - .await?; - diag_http_server - .add_svc_handler_mapping(embassy_fqdn, diag_ui_handler) - .await?; + let shutdown = + shutdown.recv().await.with_kind(crate::ErrorKind::Unknown)?; - let mut shutdown = ctx.shutdown.subscribe(); + server.shutdown().await; - shutdown.recv().await.with_kind(crate::ErrorKind::Unknown) - })() + Ok::<_, Error>(shutdown) + } .await } } diff --git a/backend/src/config/action.rs b/backend/src/config/action.rs index 4ee7d54f46..84360b7b14 100644 --- a/backend/src/config/action.rs +++ b/backend/src/config/action.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use color_eyre::eyre::eyre; +use models::ImageId; use nix::sys::signal::Signal; use patch_db::HasModel; use serde::{Deserialize, Serialize}; @@ -9,7 +10,6 @@ use tracing::instrument; use super::{Config, ConfigSpec}; use crate::context::RpcContext; use crate::dependencies::Dependencies; -use crate::id::ImageId; use crate::procedure::docker::DockerContainers; use crate::procedure::{PackageProcedure, ProcedureName}; use crate::s9pk::manifest::PackageId; diff --git a/backend/src/config/spec.rs b/backend/src/config/spec.rs index f8b2d37e3b..7aafad1b33 100644 --- a/backend/src/config/spec.rs +++ b/backend/src/config/spec.rs @@ -25,6 +25,7 @@ use super::{Config, MatchError, NoMatchWithPath, TimeoutError, TypeOf}; use crate::config::ConfigurationError; use crate::context::RpcContext; use crate::net::interface::InterfaceId; +use crate::net::keys::Key; use crate::s9pk::manifest::{Manifest, PackageId}; use crate::Error; @@ -2059,22 +2060,19 @@ impl TorKeyPointer { ValueSpecPointer::Package(PackagePointerSpec::TorKey(self.clone())), )); } - let x = sqlx::query!( - "SELECT key FROM tor WHERE package = $1 AND interface = $2", - *self.package_id, - *self.interface + let key = Key::for_interface( + &mut secrets + .acquire() + .await + .map_err(|e| ConfigurationError::SystemError(e.into()))?, + Some((self.package_id.clone(), self.interface.clone())), ) - .fetch_optional(secrets) .await - .map_err(|e| ConfigurationError::SystemError(e.into()))?; - if let Some(x) = x { - Ok(Value::String(base32::encode( - base32::Alphabet::RFC4648 { padding: false }, - &x.key, - ))) - } else { - Ok(Value::Null) - } + .map_err(ConfigurationError::SystemError)?; + Ok(Value::String(base32::encode( + base32::Alphabet::RFC4648 { padding: false }, + &key.tor_key().as_bytes(), + ))) } } impl fmt::Display for TorKeyPointer { diff --git a/backend/src/context/rpc.rs b/backend/src/context/rpc.rs index 2d4da5cf31..85c286314c 100644 --- a/backend/src/context/rpc.rs +++ b/backend/src/context/rpc.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, VecDeque}; +use std::collections::BTreeMap; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::ops::Deref; use std::path::{Path, PathBuf}; @@ -10,7 +10,7 @@ use bollard::Docker; use helpers::to_tmp_path; use josekit::jwk::Jwk; use patch_db::json_ptr::JsonPointer; -use patch_db::{DbHandle, LockReceipt, LockType, PatchDb, Revision}; +use patch_db::{DbHandle, LockReceipt, LockType, PatchDb}; use reqwest::Url; use rpc_toolkit::Context; use serde::Deserialize; @@ -19,10 +19,10 @@ use sqlx::PgPool; use tokio::sync::{broadcast, oneshot, Mutex, RwLock}; use tracing::instrument; +use crate::account::AccountInfo; use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation}; use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry}; use crate::disk::OsPartitionInfo; -use crate::hostname::HostNameReceipt; use crate::init::{init_postgres, pgloader}; use crate::install::cleanup::{cleanup_failed, uninstall, CleanupFailedReceipts}; use crate::manager::ManagerMap; @@ -31,7 +31,6 @@ use crate::net::net_controller::NetController; use crate::net::ssl::SslManager; use crate::net::wifi::WpaCli; use crate::notifications::NotificationManager; -use crate::setup::password_hash; use crate::shutdown::Shutdown; use crate::status::{MainStatus, Status}; use crate::util::config::load_config_from_paths; @@ -76,26 +75,14 @@ impl RpcContextConfig { .as_deref() .unwrap_or_else(|| Path::new("/embassy-data")) } - pub async fn db(&self, secret_store: &PgPool) -> Result { + pub async fn db(&self, account: &AccountInfo) -> Result { let db_path = self.datadir().join("main").join("embassy.db"); let db = PatchDb::open(&db_path) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, db_path.display().to_string()))?; if !db.exists(&::default()).await { - db.put( - &::default(), - &Database::init( - &crate::net::tor::os_key(&mut secret_store.acquire().await?).await?, - password_hash(&mut secret_store.acquire().await?).await?, - &crate::ssh::os_key(&mut secret_store.acquire().await?).await?, - &SslManager::init(secret_store.clone(), &mut db.handle()) - .await? - .export_root_ca() - .await? - .1, - ), - ) - .await?; + db.put(&::default(), &Database::init(account)) + .await?; } Ok(db) } @@ -131,11 +118,10 @@ pub struct RpcContextSeed { pub disk_guid: Arc, pub db: PatchDb, pub secret_store: PgPool, + pub account: RwLock, pub docker: Docker, - pub net_controller: NetController, + pub net_controller: Arc, pub managers: ManagerMap, - pub revision_cache_size: usize, - pub revision_cache: RwLock>>, pub metrics_cache: RwLock>, pub shutdown: broadcast::Sender>, pub tor_socks: SocketAddr, @@ -184,37 +170,6 @@ impl RpcCleanReceipts { } } -pub struct RpcSetHostNameReceipts { - pub hostname_receipts: HostNameReceipt, - #[allow(dead_code)] - server_info: LockReceipt, -} - -impl RpcSetHostNameReceipts { - pub async fn new(db: &'_ mut impl DbHandle) -> Result { - let mut locks = Vec::new(); - - let setup = Self::setup(&mut locks); - Ok(setup(&db.lock_all(locks).await?)?) - } - - pub fn setup( - locks: &mut Vec, - ) -> impl FnOnce(&patch_db::Verifier) -> Result { - let hostname_receipts = HostNameReceipt::setup(locks); - let server_info = crate::db::DatabaseModel::new() - .server_info() - .make_locker(LockType::Read) - .add_to_keys(locks); - move |skeleton_key| { - Ok(Self { - hostname_receipts: hostname_receipts(skeleton_key)?, - server_info: server_info.verify(skeleton_key)?, - }) - } - } -} - #[derive(Clone)] pub struct RpcContext(Arc); impl RpcContext { @@ -232,25 +187,26 @@ impl RpcContext { let (shutdown, _) = tokio::sync::broadcast::channel(1); let secret_store = base.secret_store().await?; tracing::info!("Opened Pg DB"); - let db = base.db(&secret_store).await?; + let account = AccountInfo::load(&secret_store).await?; + let db = base.db(&account).await?; tracing::info!("Opened PatchDB"); let mut docker = Docker::connect_with_unix_defaults()?; docker.set_timeout(Duration::from_secs(600)); tracing::info!("Connected to Docker"); - let net_controller = NetController::init( - ([0, 0, 0, 0], 80).into(), - crate::net::tor::os_key(&mut secret_store.acquire().await?).await?, - base.tor_control - .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), - base.dns_bind - .as_ref() - .map(|v| v.as_slice()) - .unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]), - secret_store.clone(), - &mut db.handle(), - None, - ) - .await?; + let net_controller = Arc::new( + NetController::init( + base.tor_control + .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), + base.dns_bind + .as_ref() + .map(|v| v.as_slice()) + .unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]), + SslManager::new(&account)?, + &account.hostname, + &account.key, + ) + .await?, + ); tracing::info!("Initialized Net Controller"); let managers = ManagerMap::default(); let metrics_cache = RwLock::new(None); @@ -265,11 +221,10 @@ impl RpcContext { disk_guid, db, secret_store, + account: RwLock::new(account), docker, net_controller, managers, - revision_cache_size: base.revision_cache_size.unwrap_or(512), - revision_cache: RwLock::new(VecDeque::new()), metrics_cache, shutdown, tor_socks: tor_proxy, diff --git a/backend/src/context/setup.rs b/backend/src/context/setup.rs index 9d1701efa4..0f8cf96417 100644 --- a/backend/src/context/setup.rs +++ b/backend/src/context/setup.rs @@ -14,11 +14,11 @@ use tokio::sync::broadcast::Sender; use tokio::sync::RwLock; use tracing::instrument; +use crate::account::AccountInfo; use crate::db::model::Database; use crate::disk::OsPartitionInfo; use crate::init::{init_postgres, pgloader}; -use crate::net::ssl::SslManager; -use crate::setup::{password_hash, SetupStatus}; +use crate::setup::SetupStatus; use crate::util::config::load_config_from_paths; use crate::{Error, ResultExt}; @@ -111,26 +111,14 @@ impl SetupContext { }))) } #[instrument(skip(self))] - pub async fn db(&self, secret_store: &PgPool) -> Result { + pub async fn db(&self, account: &AccountInfo) -> Result { let db_path = self.datadir.join("main").join("embassy.db"); let db = PatchDb::open(&db_path) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, db_path.display().to_string()))?; if !db.exists(&::default()).await { - db.put( - &::default(), - &Database::init( - &crate::net::tor::os_key(&mut secret_store.acquire().await?).await?, - password_hash(&mut secret_store.acquire().await?).await?, - &crate::ssh::os_key(&mut secret_store.acquire().await?).await?, - &SslManager::init(secret_store.clone(), &mut db.handle()) - .await? - .export_root_ca() - .await? - .1, - ), - ) - .await?; + db.put(&::default(), &Database::init(account)) + .await?; } Ok(db) } diff --git a/backend/src/db/model.rs b/backend/src/db/model.rs index efcbee6b47..b0fe6f6b59 100644 --- a/backend/src/db/model.rs +++ b/backend/src/db/model.rs @@ -4,21 +4,19 @@ use std::sync::Arc; use chrono::{DateTime, Utc}; use emver::VersionRange; +use ipnet::{Ipv4Net, Ipv6Net}; use isocountry::CountryCode; use itertools::Itertools; use openssl::hash::MessageDigest; -use openssl::x509::X509; use patch_db::json_ptr::JsonPointer; use patch_db::{HasModel, Map, MapModel, OptionModel}; use reqwest::Url; use serde::{Deserialize, Serialize}; use serde_json::Value; -use ssh_key::private::Ed25519PrivateKey; use ssh_key::public::Ed25519PublicKey; -use torut::onion::TorSecretKeyV3; +use crate::account::AccountInfo; use crate::config::spec::{PackagePointerSpec, SystemPointerSpec}; -use crate::hostname::{generate_hostname, generate_id}; use crate::install::progress::InstallProgress; use crate::net::interface::InterfaceId; use crate::net::net_utils::{get_iface_ipv4_addr, get_iface_ipv6_addr}; @@ -39,26 +37,19 @@ pub struct Database { pub ui: Value, } impl Database { - pub fn init( - tor_key: &TorSecretKeyV3, - password_hash: String, - ssh_key: &Ed25519PrivateKey, - cert: &X509, - ) -> Self { - let id = generate_id(); - let my_hostname = generate_hostname(); - let lan_address = my_hostname.lan_address().parse().unwrap(); + pub fn init(account: &AccountInfo) -> Self { + let lan_address = account.hostname.lan_address().parse().unwrap(); // TODO Database { server_info: ServerInfo { - id, + id: account.server_id.clone(), version: Current::new().semver().into(), - hostname: Some(my_hostname.0), + hostname: Some(account.hostname.no_dot_host_name()), last_backup: None, last_wifi_region: None, eos_version_compat: Current::new().compat().clone(), lan_address, - tor_address: format!("http://{}", tor_key.public().get_onion_address()) + tor_address: format!("http://{}", account.key.tor_address()) .parse() .unwrap(), ip_info: BTreeMap::new(), @@ -77,11 +68,12 @@ impl Database { tor: Vec::new(), clearnet: Vec::new(), }, - password_hash, - pubkey: ssh_key::PublicKey::from(Ed25519PublicKey::from(ssh_key)) + password_hash: account.password.clone(), + pubkey: ssh_key::PublicKey::from(Ed25519PublicKey::from(&account.key.ssh_key())) .to_openssh() .unwrap(), - ca_fingerprint: cert + ca_fingerprint: account + .root_ca_cert .digest(MessageDigest::sha256()) .unwrap() .iter() @@ -130,14 +122,20 @@ pub struct ServerInfo { #[derive(Debug, Deserialize, Serialize, HasModel)] #[serde(rename_all = "kebab-case")] pub struct IpInfo { - ipv4: Option, - ipv6: Option, + pub ipv4_range: Option, + pub ipv4: Option, + pub ipv6_range: Option, + pub ipv6: Option, } impl IpInfo { pub async fn for_interface(iface: &str) -> Result { + let (ipv4, ipv4_range) = get_iface_ipv4_addr(iface).await?.unzip(); + let (ipv6, ipv6_range) = get_iface_ipv6_addr(iface).await?.unzip(); Ok(Self { - ipv4: get_iface_ipv4_addr(iface).await?, - ipv6: get_iface_ipv6_addr(iface).await?, + ipv4_range, + ipv4, + ipv6_range, + ipv6, }) } } diff --git a/backend/src/hostname.rs b/backend/src/hostname.rs index c8a492e446..ed90379684 100644 --- a/backend/src/hostname.rs +++ b/backend/src/hostname.rs @@ -1,10 +1,9 @@ use patch_db::DbHandle; use rand::{thread_rng, Rng}; -use sqlx::Connection; use tokio::process::Command; use tracing::instrument; -use crate::context::RpcContext; +use crate::account::AccountInfo; use crate::util::Invoke; use crate::{Error, ErrorKind}; #[derive(Clone, serde::Deserialize, serde::Serialize, Debug)] @@ -33,33 +32,11 @@ impl Hostname { } } -pub async fn get_current_ip(eth: String) -> Result { - let cmd = format!(r"ifconfig {} | awk '/inet / {{print $2}}'", eth); - - let out = Command::new("bash") - .arg("-c") - .arg(cmd) - .invoke(ErrorKind::ParseSysInfo) - .await?; - let out_string = String::from_utf8(out)?; - Ok(out_string.trim().to_owned()) -} - -pub async fn get_embassyd_tor_addr(rpc_ctx: RpcContext) -> Result { - let mut secrets_handle = rpc_ctx.secret_store.acquire().await?; - - let mut secrets_tx = secrets_handle.begin().await?; - - let tor_key = crate::net::tor::os_key(&mut secrets_tx).await?; - - Ok(tor_key.public().get_onion_address().to_string()) -} - pub fn generate_hostname() -> Hostname { let mut rng = thread_rng(); let adjective = &ADJECTIVES[rng.gen_range(0..ADJECTIVES.len())]; let noun = &NOUNS[rng.gen_range(0..NOUNS.len())]; - Hostname(format!("embassy-{adjective}-{noun}")) + Hostname(format!("{adjective}-{noun}")) } pub fn generate_id() -> String { @@ -87,83 +64,9 @@ pub async fn set_hostname(hostname: &Hostname) -> Result<(), Error> { Ok(()) } -#[instrument(skip(handle, receipts))] -pub async fn get_id( - handle: &mut Db, - receipts: &HostNameReceipt, -) -> Result { - let id = receipts.id.get(handle).await?; - Ok(id) -} - -pub async fn get_hostname( - handle: &mut Db, - receipts: &HostNameReceipt, -) -> Result { - if let Ok(hostname) = receipts.hostname.get(handle).await { - if let Some(hostname) = hostname.to_owned() { - return Ok(Hostname(hostname)); - } - } - let id = get_id(handle, receipts).await?; - if id.len() != 8 { - return Ok(generate_hostname()); - } - return Ok(Hostname(format!("embassy-{}", id))); -} - -pub async fn ensure_hostname_is_set( - handle: &mut Db, - receipts: &HostNameReceipt, -) -> Result<(), Error> { - let hostname = get_hostname(handle, &receipts).await?; - receipts.hostname.set(handle, Some(hostname.0)).await?; - Ok(()) -} - -#[derive(Clone)] -pub struct HostNameReceipt { - hostname: patch_db::LockReceipt, ()>, - pub id: patch_db::LockReceipt, -} - -impl HostNameReceipt { - pub async fn new<'a>(db: &'a mut impl DbHandle) -> Result { - let mut locks = Vec::new(); - - let setup = Self::setup(&mut locks); - setup(&db.lock_all(locks).await?) - } - - pub fn setup( - locks: &mut Vec, - ) -> impl FnOnce(&patch_db::Verifier) -> Result { - use patch_db::LockType; - let hostname = crate::db::DatabaseModel::new() - .server_info() - .hostname() - .make_locker(LockType::Write) - .add_to_keys(locks); - let id = crate::db::DatabaseModel::new() - .server_info() - .id() - .make_locker(LockType::Write) - .add_to_keys(locks); - move |skeleton_key| { - Ok(Self { - hostname: hostname.verify(skeleton_key)?, - id: id.verify(skeleton_key)?, - }) - } - } -} - -#[instrument(skip(handle, receipts))] -pub async fn sync_hostname( - handle: &mut Db, - receipts: &HostNameReceipt, -) -> Result<(), Error> { - set_hostname(&get_hostname(handle, receipts).await?).await?; +#[instrument] +pub async fn sync_hostname(account: &AccountInfo) -> Result<(), Error> { + set_hostname(&account.hostname).await?; Command::new("systemctl") .arg("restart") .arg("avahi-daemon") diff --git a/backend/src/init.rs b/backend/src/init.rs index 2f5e82ef75..7c3e6a800e 100644 --- a/backend/src/init.rs +++ b/backend/src/init.rs @@ -13,6 +13,7 @@ use rand::random; use sqlx::{Pool, Postgres}; use tokio::process::Command; +use crate::account::AccountInfo; use crate::context::rpc::RpcContextConfig; use crate::db::model::{IpInfo, ServerStatus}; use crate::install::PKG_ARCHIVE_DIR; @@ -240,7 +241,8 @@ pub async fn init(cfg: &RpcContextConfig) -> Result { crate::ssh::sync_keys_from_db(&secret_store, "/home/start9/.ssh/authorized_keys").await?; tracing::info!("Synced SSH Keys"); - let db = cfg.db(&secret_store).await?; + let account = AccountInfo::load(&secret_store).await?; + let db = cfg.db(&account).await?; tracing::info!("Opened PatchDB"); let mut handle = db.handle(); crate::db::DatabaseModel::new() @@ -249,6 +251,16 @@ pub async fn init(cfg: &RpcContextConfig) -> Result { .await?; let receipts = InitReceipts::new(&mut handle).await?; + // write to ca cert store + tokio::fs::write( + "/usr/local/share/ca-certificates/embassy-root-ca.crt", + account.root_ca_cert.to_pem()?, + ) + .await?; + Command::new("update-ca-certificates") + .invoke(crate::ErrorKind::OpenSsl) + .await?; + if let Some(wifi_interface) = &cfg.wifi_interface { crate::net::wifi::synchronize_wpa_supplicant_conf( &cfg.datadir().join("main"), @@ -392,7 +404,7 @@ pub async fn init(cfg: &RpcContextConfig) -> Result { .set(&mut handle, time().await?) .await?; - crate::version::init(&mut handle, &receipts).await?; + crate::version::init(&mut handle, &secret_store, &receipts).await?; if should_rebuild { match tokio::fs::remove_file(SYSTEM_REBUILD_PATH).await { diff --git a/backend/src/install/mod.rs b/backend/src/install/mod.rs index 35d313a34b..4a6a94871f 100644 --- a/backend/src/install/mod.rs +++ b/backend/src/install/mod.rs @@ -45,7 +45,7 @@ use crate::s9pk::reader::S9pkReader; use crate::status::{MainStatus, Status}; use crate::util::io::{copy_and_shutdown, response_to_reader}; use crate::util::serde::{display_serializable, Port}; -use crate::util::{display_none, AsyncFileExt, Version}; +use crate::util::{assure_send, display_none, AsyncFileExt, Version}; use crate::version::{Current, VersionT}; use crate::volume::{asset_dir, script_dir}; use crate::{Error, ErrorKind, ResultExt}; @@ -1116,13 +1116,7 @@ pub async fn install_s9pk( tracing::info!("Install {}@{}: Installed interfaces", pkg_id, version); tracing::info!("Install {}@{}: Creating manager", pkg_id, version); - ctx.managers - .add( - ctx.clone(), - manifest.clone(), - manifest.interfaces.tor_keys(&mut sql_tx, pkg_id).await?, - ) - .await?; + ctx.managers.add(ctx.clone(), manifest.clone()).await?; tracing::info!("Install {}@{}: Created manager", pkg_id, version); let static_files = StaticFiles::local(pkg_id, version, manifest.assets.icon_type()); diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 0f02f92f16..0e76a6d903 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -15,6 +15,7 @@ lazy_static::lazy_static! { }; } +pub mod account; pub mod action; pub mod auth; pub mod backup; @@ -29,7 +30,6 @@ pub mod diagnostic; pub mod disk; pub mod error; pub mod hostname; -pub mod id; pub mod init; pub mod inspect; pub mod install; diff --git a/backend/src/manager/mod.rs b/backend/src/manager/mod.rs index ca749f9c15..d42034a1e1 100644 --- a/backend/src/manager/mod.rs +++ b/backend/src/manager/mod.rs @@ -12,23 +12,22 @@ use embassy_container_init::{ProcessGroupId, SignalGroupParams}; use helpers::UnixRpcClient; use nix::sys::signal::Signal; use patch_db::DbHandle; -use sqlx::{Executor, Postgres}; +use sqlx::{Connection, Executor, Postgres}; use tokio::sync::watch::error::RecvError; use tokio::sync::watch::{channel, Receiver, Sender}; use tokio::sync::{oneshot, Notify, RwLock}; -use torut::onion::TorSecretKeyV3; use tracing::instrument; use crate::context::RpcContext; use crate::manager::sync::synchronizer; -use crate::net::interface::InterfaceId; -use crate::net::GeneratedCertificateMountPoint; +use crate::net::net_controller::NetService; use crate::procedure::docker::{DockerContainer, DockerProcedure, LongRunning}; #[cfg(feature = "js_engine")] use crate::procedure::js_scripts::JsProcedure; use crate::procedure::{NoOutput, PackageProcedure, ProcedureName}; use crate::s9pk::manifest::{Manifest, PackageId}; use crate::util::{ApplyRef, Container, NonDetachingJoinHandle, Version}; +use crate::volume::Volume; use crate::Error; pub mod health; @@ -70,10 +69,9 @@ impl ManagerMap { continue; }; - let tor_keys = man.interfaces.tor_keys(secrets, &package).await?; res.insert( (package, man.version.clone()), - Arc::new(Manager::create(ctx.clone(), man, tor_keys).await?), + Arc::new(Manager::create(ctx.clone(), man).await?), ); } *self.0.write().await = res; @@ -81,12 +79,7 @@ impl ManagerMap { } #[instrument(skip(self, ctx))] - pub async fn add( - &self, - ctx: RpcContext, - manifest: Manifest, - tor_keys: BTreeMap, - ) -> Result<(), Error> { + pub async fn add(&self, ctx: RpcContext, manifest: Manifest) -> Result<(), Error> { let mut lock = self.0.write().await; let id = (manifest.id.clone(), manifest.version.clone()); if let Some(man) = lock.remove(&id) { @@ -94,10 +87,7 @@ impl ManagerMap { man.exit().await?; } } - lock.insert( - id, - Arc::new(Manager::create(ctx, manifest, tor_keys).await?), - ); + lock.insert(id, Arc::new(Manager::create(ctx, manifest).await?)); Ok(()) } @@ -162,7 +152,6 @@ struct ManagerSeed { ctx: RpcContext, manifest: Manifest, container_name: String, - tor_keys: BTreeMap, } pub struct ManagerSharedState { @@ -190,13 +179,8 @@ async fn run_main( state: &Arc, ) -> Result, Error> { let rt_state = state.clone(); - let interfaces = main_interfaces(&*state.seed)?; - let generated_certificate = generate_certificate(&*state.seed, &interfaces).await?; - let mut runtime = NonDetachingJoinHandle::from(tokio::spawn(start_up_image( - rt_state, - generated_certificate, - ))); + let mut runtime = NonDetachingJoinHandle::from(tokio::spawn(start_up_image(rt_state))); let ip = match state.persistent_container.is_some() { false => Some(match get_running_ip(state, &mut runtime).await { GetRunningIp::Ip(x) => x, @@ -206,9 +190,11 @@ async fn run_main( true => None, }; - if let Some(ip) = ip { - add_network_for_main(&*state.seed, ip, interfaces, generated_certificate).await?; - } + let svc = if let Some(ip) = ip { + Some(add_network_for_main(&*state.seed, ip).await?) + } else { + None + }; set_commit_health_true(state); let health = main_health_check_daemon(state.clone()); @@ -218,8 +204,8 @@ async fn run_main( _ = health => Err(Error::new(eyre!("Health check daemon exited!"), crate::ErrorKind::Unknown)), _ = state.killer.notified() => Ok(Err((137, "Killed".to_string()))) }; - if let Some(ip) = ip { - remove_network_for_main(&*state.seed, ip).await?; + if let Some(svc) = svc { + remove_network_for_main(svc).await?; } res } @@ -228,7 +214,6 @@ async fn run_main( /// Note for _generated_certificate: Needed to know that before we start the state we have generated the certificate async fn start_up_image( rt_state: Arc, - _generated_certificate: GeneratedCertificateMountPoint, ) -> Result, Error> { rt_state .seed @@ -248,17 +233,12 @@ async fn start_up_image( impl Manager { #[instrument(skip(ctx))] - async fn create( - ctx: RpcContext, - manifest: Manifest, - tor_keys: BTreeMap, - ) -> Result { + async fn create(ctx: RpcContext, manifest: Manifest) -> Result { let (on_stop, recv) = channel(OnStop::Sleep); let seed = Arc::new(ManagerSeed { ctx, container_name: DockerProcedure::container_name(&manifest.id, None), manifest, - tor_keys, }); let persistent_container = PersistentContainer::init(&seed).await?; let shared = Arc::new(ManagerSharedState { @@ -479,8 +459,6 @@ async fn spawn_persistent_container( let mut send_inserter: Option>>> = Some(send_inserter); loop { if let Err(e) = async { - let interfaces = main_interfaces(&*seed)?; - let generated_certificate = generate_certificate(&*seed, &interfaces).await?; let (mut runtime, inserter) = long_running_docker(&seed, &container).await?; @@ -493,7 +471,7 @@ async fn spawn_persistent_container( return Ok(()); } }; - add_network_for_main(&*seed, ip, interfaces, generated_certificate).await?; + let svc = add_network_for_main(&*seed, ip).await?; if let Some(inserter_send) = inserter_send.as_mut() { let _ = inserter_send.send(Arc::new(inserter)); @@ -509,7 +487,7 @@ async fn spawn_persistent_container( a = runtime.running_output => a.map_err(|_| Error::new(eyre!("Manager runtime panicked!"), crate::ErrorKind::Docker)).map(|_| ()), }; - remove_network_for_main(&*seed, ip).await?; + remove_network_for_main(svc).await?; res }.await { @@ -540,16 +518,8 @@ async fn long_running_docker( .await } -async fn remove_network_for_main(seed: &ManagerSeed, ip: std::net::Ipv4Addr) -> Result<(), Error> { - seed.ctx - .net_controller - .remove( - &seed.manifest.id, - ip, - seed.manifest.interfaces.0.keys().cloned(), - ) - .await?; - Ok(()) +async fn remove_network_for_main(svc: NetService) -> Result<(), Error> { + svc.remove_all().await } fn fetch_starting_to_running(state: &Arc) { @@ -592,18 +562,32 @@ fn set_commit_health_true(state: &Arc) { async fn add_network_for_main( seed: &ManagerSeed, ip: std::net::Ipv4Addr, - interfaces: Vec<( - InterfaceId, - &crate::net::interface::Interface, - TorSecretKeyV3, - )>, - generated_certificate: GeneratedCertificateMountPoint, -) -> Result<(), Error> { - seed.ctx +) -> Result { + let mut svc = seed + .ctx .net_controller - .add(&seed.manifest.id, ip, interfaces, generated_certificate) + .create_service(seed.manifest.id.clone(), ip) .await?; - Ok(()) + // DEPRECATED + let mut secrets = seed.ctx.secret_store.acquire().await?; + let mut tx = secrets.begin().await?; + for (id, interface) in &seed.manifest.interfaces.0 { + for (external, internal) in interface.lan_config.iter().flatten() { + svc.add_lan(&mut tx, id.clone(), external.0, internal.internal, false) + .await?; + } + for (external, internal) in interface.tor_config.iter().flat_map(|t| &t.port_mapping) { + svc.add_tor(&mut tx, id.clone(), external.0, internal.0) + .await?; + } + } + for volume in seed.manifest.volumes.values() { + if let Volume::Certificate { interface_id } = volume { + svc.export_cert(&mut tx, interface_id, ip.into()).await?; + } + } + tx.commit().await?; + Ok(svc) } enum GetRunningIp { @@ -716,49 +700,6 @@ async fn container_inspect( .await } -async fn generate_certificate( - seed: &ManagerSeed, - interfaces: &Vec<( - InterfaceId, - &crate::net::interface::Interface, - TorSecretKeyV3, - )>, -) -> Result { - seed.ctx - .net_controller - .generate_certificate_mountpoint(&seed.manifest.id, interfaces) - .await -} - -fn main_interfaces( - seed: &ManagerSeed, -) -> Result< - Vec<( - InterfaceId, - &crate::net::interface::Interface, - TorSecretKeyV3, - )>, - Error, -> { - seed.manifest - .interfaces - .0 - .iter() - .map(|(id, info)| { - Ok(( - id.clone(), - info, - seed.tor_keys - .get(id) - .ok_or_else(|| { - Error::new(eyre!("interface {} missing key", id), crate::ErrorKind::Tor) - })? - .clone(), - )) - }) - .collect::, Error>>() -} - async fn wait_for_status(shared: &ManagerSharedState, status: Status) { let mut recv = shared.status.0.subscribe(); while { diff --git a/backend/src/migration.rs b/backend/src/migration.rs index dae7138a39..620433ead1 100644 --- a/backend/src/migration.rs +++ b/backend/src/migration.rs @@ -4,12 +4,12 @@ use color_eyre::eyre::eyre; use emver::VersionRange; use futures::{Future, FutureExt}; use indexmap::IndexMap; +use models::ImageId; use patch_db::HasModel; use serde::{Deserialize, Serialize}; use tracing::instrument; use crate::context::RpcContext; -use crate::id::ImageId; use crate::procedure::docker::DockerContainers; use crate::procedure::{PackageProcedure, ProcedureName}; use crate::s9pk::manifest::PackageId; diff --git a/backend/src/net/cert_resolver.rs b/backend/src/net/cert_resolver.rs deleted file mode 100644 index f9ac8966ff..0000000000 --- a/backend/src/net/cert_resolver.rs +++ /dev/null @@ -1,215 +0,0 @@ -use std::collections::BTreeMap; -use std::io; -use std::pin::Pin; -use std::str::FromStr; -use std::sync::{Arc, RwLock}; -use std::task::{Context, Poll}; - -use color_eyre::eyre::eyre; -use futures::{ready, Future}; -use hyper::server::accept::Accept; -use hyper::server::conn::{AddrIncoming, AddrStream}; -use openssl::pkey::{PKey, Private}; -use openssl::x509::X509; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tokio_rustls::rustls::server::ResolvesServerCert; -use tokio_rustls::rustls::sign::{any_supported_type, CertifiedKey}; -use tokio_rustls::rustls::{Certificate, PrivateKey, ServerConfig}; - -use crate::net::net_utils::ResourceFqdn; -use crate::Error; - -enum State { - Handshaking(tokio_rustls::Accept), - Streaming(tokio_rustls::server::TlsStream), -} - -// tokio_rustls::server::TlsStream doesn't expose constructor methods, -// so we have to TlsAcceptor::accept and handshake to have access to it -// TlsStream implements AsyncRead/AsyncWrite handshaking tokio_rustls::Accept first -pub struct TlsStream { - state: State, -} - -impl TlsStream { - fn new(stream: AddrStream, config: Arc) -> TlsStream { - let accept = tokio_rustls::TlsAcceptor::from(config).accept(stream); - TlsStream { - state: State::Handshaking(accept), - } - } -} - -impl AsyncRead for TlsStream { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context, - buf: &mut ReadBuf, - ) -> Poll> { - let pin = self.get_mut(); - match pin.state { - State::Handshaking(ref mut accept) => match ready!(Pin::new(accept).poll(cx)) { - Ok(mut stream) => { - let result = Pin::new(&mut stream).poll_read(cx, buf); - pin.state = State::Streaming(stream); - result - } - Err(err) => Poll::Ready(Err(err)), - }, - State::Streaming(ref mut stream) => Pin::new(stream).poll_read(cx, buf), - } - } -} - -impl AsyncWrite for TlsStream { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let pin = self.get_mut(); - match pin.state { - State::Handshaking(ref mut accept) => match ready!(Pin::new(accept).poll(cx)) { - Ok(mut stream) => { - let result = Pin::new(&mut stream).poll_write(cx, buf); - pin.state = State::Streaming(stream); - result - } - Err(err) => Poll::Ready(Err(err)), - }, - State::Streaming(ref mut stream) => Pin::new(stream).poll_write(cx, buf), - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.state { - State::Handshaking(_) => Poll::Ready(Ok(())), - State::Streaming(ref mut stream) => Pin::new(stream).poll_flush(cx), - } - } - - fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.state { - State::Handshaking(_) => Poll::Ready(Ok(())), - State::Streaming(ref mut stream) => Pin::new(stream).poll_shutdown(cx), - } - } -} - -impl ResolvesServerCert for EmbassyCertResolver { - fn resolve( - &self, - client_hello: tokio_rustls::rustls::server::ClientHello, - ) -> Option> { - let hostname_raw = client_hello.server_name(); - - match hostname_raw { - Some(hostname_str) => { - let full_fqdn = match ResourceFqdn::from_str(hostname_str) { - Ok(fqdn) => fqdn, - Err(_) => { - tracing::error!("Error converting {} to fqdn struct", hostname_str); - return None; - } - }; - let lock = self.cert_mapping.read(); - - match lock { - Ok(lock) => lock - .get(&full_fqdn) - .map(|cert_key| Arc::new(cert_key.to_owned())), - Err(err) => { - tracing::error!("resolve fn Error: {}", err); - None - } - } - } - None => None, - } - } -} - -#[derive(Clone, Default)] -pub struct EmbassyCertResolver { - cert_mapping: Arc>>, -} - -impl EmbassyCertResolver { - pub fn new() -> Self { - Self::default() - } - pub async fn add_certificate_to_resolver( - &mut self, - service_resource_fqdn: ResourceFqdn, - package_cert_data: (PKey, Vec), - ) -> Result<(), Error> { - let x509_cert_chain = package_cert_data.1; - let private_keys = package_cert_data - .0 - .private_key_to_der() - .map_err(|err| Error::new(eyre!("{}", err), crate::ErrorKind::OpenSsl))?; - - let mut full_rustls_certs = Vec::new(); - for cert in x509_cert_chain.iter() { - let cert = Certificate( - cert.to_der() - .map_err(|err| Error::new(eyre!("{}", err), crate::ErrorKind::OpenSsl))?, - ); - - full_rustls_certs.push(cert); - } - - let pre_sign_key = PrivateKey(private_keys); - let actual_sign_key = any_supported_type(&pre_sign_key) - .map_err(|err| Error::new(eyre!("{}", err), crate::ErrorKind::OpenSsl))?; - - let cert_key = CertifiedKey::new(full_rustls_certs, actual_sign_key); - - let mut lock = self - .cert_mapping - .write() - .map_err(|err| Error::new(eyre!("{}", err), crate::ErrorKind::Network))?; - lock.insert(service_resource_fqdn, cert_key); - - Ok(()) - } - - pub async fn remove_cert(&mut self, hostname: ResourceFqdn) -> Result<(), Error> { - let mut lock = self - .cert_mapping - .write() - .map_err(|err| Error::new(eyre!("{}", err), crate::ErrorKind::Network))?; - - lock.remove(&hostname); - - Ok(()) - } -} - -pub struct TlsAcceptor { - config: Arc, - incoming: AddrIncoming, -} - -impl TlsAcceptor { - pub fn new(config: Arc, incoming: AddrIncoming) -> TlsAcceptor { - TlsAcceptor { config, incoming } - } -} - -impl Accept for TlsAcceptor { - type Conn = TlsStream; - type Error = io::Error; - - fn poll_accept( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>> { - let pin = self.get_mut(); - match ready!(Pin::new(&mut pin.incoming).poll_accept(cx)) { - Some(Ok(sock)) => Poll::Ready(Some(Ok(TlsStream::new(sock, pin.config.clone())))), - Some(Err(e)) => Poll::Ready(Some(Err(e))), - None => Poll::Ready(None), - } - } -} diff --git a/backend/src/net/dhcp.rs b/backend/src/net/dhcp.rs index 17c5eda9db..5d48145a86 100644 --- a/backend/src/net/dhcp.rs +++ b/backend/src/net/dhcp.rs @@ -1,7 +1,9 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; +use std::net::IpAddr; use futures::TryStreamExt; use rpc_toolkit::command; +use tokio::sync::RwLock; use crate::context::RpcContext; use crate::db::model::IpInfo; @@ -9,6 +11,32 @@ use crate::net::net_utils::{iface_is_physical, list_interfaces}; use crate::util::display_none; use crate::Error; +lazy_static::lazy_static! { + static ref CACHED_IPS: RwLock> = RwLock::new(BTreeSet::new()); +} + +async fn _ips() -> Result, Error> { + Ok(init_ips() + .await? + .values() + .flat_map(|i| { + std::iter::empty() + .chain(i.ipv4.map(IpAddr::from)) + .chain(i.ipv6.map(IpAddr::from)) + }) + .collect()) +} + +pub async fn ips() -> Result, Error> { + let ips = CACHED_IPS.read().await.clone(); + if !ips.is_empty() { + return Ok(ips); + } + let ips = _ips().await?; + *CACHED_IPS.write().await = ips.clone(); + Ok(ips) +} + pub async fn init_ips() -> Result, Error> { let mut res = BTreeMap::new(); let mut ifaces = list_interfaces(); @@ -29,15 +57,23 @@ pub async fn dhcp() -> Result<(), Error> { #[command(display(display_none))] pub async fn update(#[context] ctx: RpcContext, #[arg] interface: String) -> Result<(), Error> { if iface_is_physical(&interface).await { + let ip_info = IpInfo::for_interface(&interface).await?; crate::db::DatabaseModel::new() .server_info() .ip_info() .idx_model(&interface) - .put( - &mut ctx.db.handle(), - &IpInfo::for_interface(&interface).await?, - ) + .put(&mut ctx.db.handle(), &ip_info) .await?; + let mut cached = CACHED_IPS.write().await; + if cached.is_empty() { + *cached = _ips().await?; + } else { + cached.extend( + std::iter::empty() + .chain(ip_info.ipv4.map(IpAddr::from)) + .chain(ip_info.ipv6.map(IpAddr::from)), + ); + } } Ok(()) } diff --git a/backend/src/net/dns.rs b/backend/src/net/dns.rs index 60c33fcb0c..ad97364682 100644 --- a/backend/src/net/dns.rs +++ b/backend/src/net/dns.rs @@ -1,9 +1,10 @@ use std::borrow::Borrow; use std::collections::BTreeMap; use std::net::{Ipv4Addr, SocketAddr}; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use std::time::Duration; +use color_eyre::eyre::eyre; use futures::TryFutureExt; use helpers::NonDetachingJoinHandle; use models::PackageId; @@ -13,39 +14,52 @@ use tokio::sync::RwLock; use trust_dns_server::authority::MessageResponseBuilder; use trust_dns_server::client::op::{Header, ResponseCode}; use trust_dns_server::client::rr::{Name, Record, RecordType}; -use trust_dns_server::proto::rr::rdata::a; use trust_dns_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo}; use trust_dns_server::ServerFuture; use crate::util::Invoke; -use crate::{Error, ErrorKind, ResultExt, HOST_IP}; +use crate::{Error, ErrorKind, ResultExt}; pub struct DnsController { - services: Arc>>>, + services: Weak, BTreeMap>>>>, #[allow(dead_code)] dns_server: NonDetachingJoinHandle>, } struct Resolver { - services: Arc>>>, + services: Arc, BTreeMap>>>>, } impl Resolver { async fn resolve(&self, name: &Name) -> Option> { match name.iter().next_back() { Some(b"embassy") => { if let Some(pkg) = name.iter().rev().skip(1).next() { - if let Some(ip) = self - .services - .read() - .await - .get(std::str::from_utf8(pkg).unwrap_or_default()) - { - Some(ip.iter().copied().collect()) + if let Some(ip) = self.services.read().await.get(&Some( + std::str::from_utf8(pkg) + .unwrap_or_default() + .parse() + .unwrap_or_default(), + )) { + Some( + ip.iter() + .filter(|(_, rc)| rc.strong_count() > 0) + .map(|(ip, _)| *ip) + .collect(), + ) } else { None } } else { - Some(vec![HOST_IP.into()]) + if let Some(ip) = self.services.read().await.get(&None) { + Some( + ip.iter() + .filter(|(_, rc)| rc.strong_count() > 0) + .map(|(ip, _)| *ip) + .collect(), + ) + } else { + None + } } } _ => None, @@ -162,26 +176,47 @@ impl DnsController { .into(); Ok(Self { - services, + services: Arc::downgrade(&services), dns_server, }) } - pub async fn add(&self, pkg_id: &PackageId, ip: Ipv4Addr) { - let mut writable = self.services.write().await; - let mut ips = writable.remove(pkg_id).unwrap_or_default(); - ips.push(ip); - writable.insert(pkg_id.clone(), ips); + pub async fn add(&self, pkg_id: Option, ip: Ipv4Addr) -> Result, Error> { + if let Some(services) = Weak::upgrade(&self.services) { + let mut writable = services.write().await; + let mut ips = writable.remove(&pkg_id).unwrap_or_default(); + let rc = if let Some(rc) = Weak::upgrade(&ips.remove(&ip).unwrap_or_default()) { + rc + } else { + Arc::new(()) + }; + ips.insert(ip, Arc::downgrade(&rc)); + writable.insert(pkg_id, ips); + Ok(rc) + } else { + Err(Error::new( + eyre!("DNS Server Thread has exited"), + crate::ErrorKind::Network, + )) + } } - pub async fn remove(&self, pkg_id: &PackageId, ip: Ipv4Addr) { - let mut writable = self.services.write().await; - let mut ips = writable.remove(pkg_id).unwrap_or_default(); - if let Some((idx, _)) = ips.iter().copied().enumerate().find(|(_, x)| *x == ip) { - ips.swap_remove(idx); - } - if !ips.is_empty() { - writable.insert(pkg_id.clone(), ips); + pub async fn gc(&self, pkg_id: Option, ip: Ipv4Addr) -> Result<(), Error> { + if let Some(services) = Weak::upgrade(&self.services) { + let mut writable = services.write().await; + let mut ips = writable.remove(&pkg_id).unwrap_or_default(); + if let Some(rc) = Weak::upgrade(&ips.remove(&ip).unwrap_or_default()) { + ips.insert(ip, Arc::downgrade(&rc)); + } + if !ips.is_empty() { + writable.insert(pkg_id, ips); + } + Ok(()) + } else { + Err(Error::new( + eyre!("DNS Server Thread has exited"), + crate::ErrorKind::Network, + )) } } } diff --git a/backend/src/net/embassy_service_http_server.rs b/backend/src/net/embassy_service_http_server.rs deleted file mode 100644 index 37f12141d0..0000000000 --- a/backend/src/net/embassy_service_http_server.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::collections::BTreeMap; -use std::net::{IpAddr, SocketAddr}; -use std::sync::Arc; - -use helpers::NonDetachingJoinHandle; -use http::StatusCode; -use hyper::server::conn::AddrIncoming; -use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Error as HyperError, Response, Server}; -use tokio::sync::oneshot; -use tokio_rustls::rustls::ServerConfig; -use tracing::error; - -use crate::net::cert_resolver::TlsAcceptor; -use crate::net::net_utils::{host_addr_fqdn, ResourceFqdn}; -use crate::net::HttpHandler; -use crate::Error; - -static RES_NOT_FOUND: &[u8] = b"503 Service Unavailable"; -static NO_HOST: &[u8] = b"No host header found"; - -pub struct EmbassyServiceHTTPServer { - pub svc_mapping: Arc>>, - pub shutdown: oneshot::Sender<()>, - pub handle: NonDetachingJoinHandle<()>, - pub ssl_cfg: Option>, -} - -impl EmbassyServiceHTTPServer { - pub async fn new( - listener_addr: IpAddr, - port: u16, - ssl_cfg: Option>, - ) -> Result { - let (tx, rx) = tokio::sync::oneshot::channel::<()>(); - - let listener_socket_addr = SocketAddr::from((listener_addr, port)); - - let server_service_mapping = Arc::new(tokio::sync::RwLock::new(BTreeMap::< - ResourceFqdn, - HttpHandler, - >::new())); - - let server_service_mapping1 = server_service_mapping.clone(); - - let bare_make_service_fn = move || { - let server_service_mapping = server_service_mapping.clone(); - - async move { - Ok::<_, HyperError>(service_fn(move |req| { - let mut server_service_mapping = server_service_mapping.clone(); - - async move { - server_service_mapping = server_service_mapping.clone(); - - let host = host_addr_fqdn(&req); - match host { - Ok(host_uri) => { - let res = { - let mapping = server_service_mapping.read().await; - - let opt_handler = mapping.get(&host_uri).cloned(); - - opt_handler - }; - match res { - Some(opt_handler) => { - let response = opt_handler(req).await; - - match response { - Ok(resp) => Ok::, hyper::Error>(resp), - Err(err) => Ok(respond_hyper_error(err)), - } - } - None => Ok(res_not_found()), - } - } - Err(e) => Ok(no_host_found(e)), - } - } - })) - } - }; - - let inner_ssl_cfg = ssl_cfg.clone(); - let handle = tokio::spawn(async move { - match inner_ssl_cfg { - Some(cfg) => { - let incoming = AddrIncoming::bind(&listener_socket_addr).unwrap(); - - let server = Server::builder(TlsAcceptor::new(cfg, incoming)) - .http1_preserve_header_case(true) - .http1_title_case_headers(true) - .serve(make_service_fn(|_| bare_make_service_fn())) - .with_graceful_shutdown({ - async { - rx.await.ok(); - } - }); - - if let Err(e) = server.await { - error!("Spawning hyper server errorr: {}", e); - } - } - None => { - let server = Server::bind(&listener_socket_addr) - .http1_preserve_header_case(true) - .http1_title_case_headers(true) - .serve(make_service_fn(|_| bare_make_service_fn())) - .with_graceful_shutdown({ - async { - rx.await.ok(); - } - }); - if let Err(e) = server.await { - error!("Spawning hyper server errorr: {}", e); - } - } - }; - }); - - Ok(Self { - svc_mapping: server_service_mapping1, - handle: handle.into(), - shutdown: tx, - ssl_cfg, - }) - } - - pub async fn add_svc_handler_mapping( - &mut self, - fqdn: ResourceFqdn, - svc_handle: HttpHandler, - ) -> Result<(), Error> { - let mut mapping = self.svc_mapping.write().await; - - mapping.insert(fqdn.clone(), svc_handle); - - Ok(()) - } - - pub async fn remove_svc_handler_mapping(&mut self, fqdn: ResourceFqdn) -> Result<(), Error> { - let mut mapping = self.svc_mapping.write().await; - - mapping.remove(&fqdn); - - Ok(()) - } -} - -/// HTTP status code 503 -fn res_not_found() -> Response { - Response::builder() - .status(StatusCode::SERVICE_UNAVAILABLE) - .body(RES_NOT_FOUND.into()) - .unwrap() -} - -fn no_host_found(err: Error) -> Response { - let err_txt = format!("{}: Error {}", String::from_utf8_lossy(NO_HOST), err); - Response::builder() - .status(StatusCode::BAD_REQUEST) - .body(err_txt.into()) - .unwrap() -} - -fn respond_hyper_error(err: hyper::Error) -> Response { - let err_txt = format!("{}: Error {}", String::from_utf8_lossy(NO_HOST), err); - Response::builder() - .status(StatusCode::BAD_REQUEST) - .body(err_txt.into()) - .unwrap() -} diff --git a/backend/src/net/interface.rs b/backend/src/net/interface.rs index e59d4c2ce2..ca8b9719b5 100644 --- a/backend/src/net/interface.rs +++ b/backend/src/net/interface.rs @@ -1,9 +1,6 @@ use std::collections::BTreeMap; -use color_eyre::eyre::eyre; -use futures::TryStreamExt; use indexmap::IndexSet; -use itertools::Either; pub use models::InterfaceId; use serde::{Deserialize, Deserializer, Serialize}; use sqlx::{Executor, Postgres}; @@ -11,7 +8,6 @@ use torut::onion::TorSecretKeyV3; use tracing::instrument; use crate::db::model::{InterfaceAddressMap, InterfaceAddresses}; -use crate::id::Id; use crate::s9pk::manifest::PackageId; use crate::util::serde::Port; use crate::{Error, ResultExt}; @@ -81,36 +77,6 @@ impl Interfaces { } Ok(interface_addresses) } - - #[instrument(skip(secrets))] - pub async fn tor_keys( - &self, - secrets: &mut Ex, - package_id: &PackageId, - ) -> Result, Error> - where - for<'a> &'a mut Ex: Executor<'a, Database = Postgres>, - { - Ok(sqlx::query!( - "SELECT interface, key FROM tor WHERE package = $1", - **package_id - ) - .fetch_many(secrets) - .map_err(Error::from) - .try_filter_map(|qr| async move { - Ok(if let Either::Right(r) = qr { - let mut buf = [0; 64]; - buf.clone_from_slice(r.key.get(0..64).ok_or_else(|| { - Error::new(eyre!("Invalid Tor Key Length"), crate::ErrorKind::Database) - })?); - Some((InterfaceId::from(Id::try_from(r.interface)?), buf.into())) - } else { - None - }) - }) - .try_collect() - .await?) - } } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/backend/src/net/keys.rs b/backend/src/net/keys.rs new file mode 100644 index 0000000000..d406df9c0c --- /dev/null +++ b/backend/src/net/keys.rs @@ -0,0 +1,272 @@ +use color_eyre::eyre::eyre; +use ed25519_dalek::{ExpandedSecretKey, SecretKey}; +use models::{Id, InterfaceId, PackageId}; +use openssl::pkey::{PKey, Private}; +use openssl::sha::Sha256; +use openssl::x509::X509; +use p256::elliptic_curve::pkcs8::EncodePrivateKey; +use sqlx::PgExecutor; +use ssh_key::private::Ed25519PrivateKey; +use torut::onion::{OnionAddressV3, TorSecretKeyV3}; +use zeroize::Zeroize; + +use crate::net::ssl::CertPair; +use crate::Error; + +// TODO: delete once we may change tor addresses +async fn compat( + secrets: impl PgExecutor<'_>, + interface: &Option<(PackageId, InterfaceId)>, +) -> Result, Error> { + if let Some((package, interface)) = interface { + if let Some(r) = sqlx::query!( + "SELECT key FROM tor WHERE package = $1 AND interface = $2", + **package, + **interface + ) + .fetch_optional(secrets) + .await? + { + Ok(Some(ExpandedSecretKey::from_bytes(&r.key)?)) + } else { + Ok(None) + } + } else { + if let Some(key) = sqlx::query!("SELECT tor_key FROM account WHERE id = 0") + .fetch_one(secrets) + .await? + .tor_key + { + Ok(Some(ExpandedSecretKey::from_bytes(&key)?)) + } else { + Ok(None) + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Key { + interface: Option<(PackageId, InterfaceId)>, + base: [u8; 32], + tor_key: [u8; 64], // Does NOT necessarily match base +} +impl Key { + pub fn interface(&self) -> Option<(PackageId, InterfaceId)> { + self.interface.clone() + } + pub fn as_bytes(&self) -> [u8; 32] { + self.base + } + pub fn internal_address(&self) -> String { + self.interface + .as_ref() + .map(|(pkg_id, _)| format!("{}.embassy", pkg_id)) + .unwrap_or_else(|| "embassy".to_owned()) + } + pub fn tor_key(&self) -> TorSecretKeyV3 { + ed25519_dalek::ExpandedSecretKey::from_bytes(&self.tor_key) + .unwrap() + .to_bytes() + .into() + } + pub fn tor_address(&self) -> OnionAddressV3 { + self.tor_key().public().get_onion_address() + } + pub fn base_address(&self) -> String { + self.tor_key() + .public() + .get_onion_address() + .get_address_without_dot_onion() + } + pub fn local_address(&self) -> String { + self.base_address() + ".local" + } + pub fn openssl_key_ed25519(&self) -> PKey { + PKey::private_key_from_raw_bytes(&self.base, openssl::pkey::Id::ED25519).unwrap() + } + pub fn openssl_key_nistp256(&self) -> PKey { + let mut buf = self.base; + loop { + if let Ok(k) = p256::SecretKey::from_be_bytes(&buf) { + return PKey::private_key_from_pkcs8(&*k.to_pkcs8_der().unwrap().as_bytes()) + .unwrap(); + } + let mut sha = Sha256::new(); + sha.update(&buf); + buf = sha.finish(); + } + } + pub fn ssh_key(&self) -> Ed25519PrivateKey { + Ed25519PrivateKey::from_bytes(&self.base) + } + pub(crate) fn from_pair( + interface: Option<(PackageId, InterfaceId)>, + bytes: [u8; 32], + tor_key: [u8; 64], + ) -> Self { + Self { + interface, + tor_key, + base: bytes, + } + } + pub fn from_bytes(interface: Option<(PackageId, InterfaceId)>, bytes: [u8; 32]) -> Self { + Self::from_pair( + interface, + bytes, + ExpandedSecretKey::from(&SecretKey::from_bytes(&bytes).unwrap()).to_bytes(), + ) + } + pub fn new(interface: Option<(PackageId, InterfaceId)>) -> Self { + Self::from_bytes(interface, rand::random()) + } + pub(super) fn with_certs(self, certs: CertPair, int: X509, root: X509) -> KeyInfo { + KeyInfo { + key: self, + certs, + int, + root, + } + } + pub async fn for_package( + secrets: impl PgExecutor<'_>, + package: &PackageId, + ) -> Result, Error> { + sqlx::query!( + r#" + SELECT + network_keys.package, + network_keys.interface, + network_keys.key, + tor.key AS "tor_key?" + FROM + network_keys + LEFT JOIN + tor + ON + network_keys.package = tor.package + AND + network_keys.interface = tor.interface + WHERE + network_keys.package = $1 + "#, + **package + ) + .fetch_all(secrets) + .await? + .into_iter() + .map(|row| { + let interface = Some(( + package.clone(), + InterfaceId::from(Id::try_from(row.interface)?), + )); + let bytes = row.key.try_into().map_err(|e: Vec| { + Error::new( + eyre!("Invalid length for network key {} expected 32", e.len()), + crate::ErrorKind::Database, + ) + })?; + Ok(match row.tor_key { + Some(tor_key) => Key::from_pair( + interface, + bytes, + tor_key.try_into().map_err(|e: Vec| { + Error::new( + eyre!("Invalid length for tor key {} expected 64", e.len()), + crate::ErrorKind::Database, + ) + })?, + ), + None => Key::from_bytes(interface, bytes), + }) + }) + .collect() + } + pub async fn for_interface( + secrets: &mut Ex, + interface: Option<(PackageId, InterfaceId)>, + ) -> Result + where + for<'a> &'a mut Ex: PgExecutor<'a>, + { + let tentative = rand::random::<[u8; 32]>(); + let actual = if let Some((pkg, iface)) = &interface { + let k = tentative.as_slice(); + let actual = sqlx::query!( + "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET package = EXCLUDED.package RETURNING key", + **pkg, + **iface, + k, + ) + .fetch_one(&mut *secrets) + .await?.key; + let mut bytes = tentative; + bytes.clone_from_slice(actual.get(0..32).ok_or_else(|| { + Error::new( + eyre!("Invalid key size returned from DB"), + crate::ErrorKind::Database, + ) + })?); + bytes + } else { + let actual = sqlx::query!("SELECT network_key FROM account WHERE id = 0") + .fetch_one(&mut *secrets) + .await? + .network_key; + let mut bytes = tentative; + bytes.clone_from_slice(actual.get(0..32).ok_or_else(|| { + Error::new( + eyre!("Invalid key size returned from DB"), + crate::ErrorKind::Database, + ) + })?); + bytes + }; + let mut res = Self::from_bytes(interface, actual); + if let Some(tor_key) = compat(secrets, &res.interface).await? { + res.tor_key = tor_key.to_bytes(); + } + Ok(res) + } +} +impl Drop for Key { + fn drop(&mut self) { + self.base.zeroize(); + self.tor_key.zeroize(); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct KeyInfo { + key: Key, + certs: CertPair, + int: X509, + root: X509, +} +impl KeyInfo { + pub fn key(&self) -> &Key { + &self.key + } + pub fn certs(&self) -> &CertPair { + &self.certs + } + pub fn int_ca(&self) -> &X509 { + &self.int + } + pub fn root_ca(&self) -> &X509 { + &self.root + } + pub fn fullchain_ed25519(&self) -> Vec<&X509> { + vec![&self.certs.ed25519, &self.int, &self.root] + } + pub fn fullchain_nistp256(&self) -> Vec<&X509> { + vec![&self.certs.nistp256, &self.int, &self.root] + } +} + +#[test] +pub fn test_keygen() { + let key = Key::new(None); + key.tor_key(); + key.openssl_key_nistp256(); +} diff --git a/backend/src/net/mdns.rs b/backend/src/net/mdns.rs index 74c7f5552d..1ce2432b04 100644 --- a/backend/src/net/mdns.rs +++ b/backend/src/net/mdns.rs @@ -1,13 +1,11 @@ use std::collections::BTreeMap; use std::net::Ipv4Addr; +use std::sync::{Arc, Weak}; use color_eyre::eyre::eyre; use tokio::process::{Child, Command}; use tokio::sync::Mutex; -use torut::onion::TorSecretKeyV3; -use super::interface::InterfaceId; -use crate::s9pk::manifest::PackageId; use crate::util::Invoke; use crate::{Error, ResultExt}; @@ -39,25 +37,17 @@ impl MdnsController { MdnsControllerInner::init().await?, ))) } - pub async fn add<'a, I: IntoIterator>( - &self, - pkg_id: &PackageId, - interfaces: I, - ) -> Result<(), Error> { - self.0.lock().await.add(pkg_id, interfaces).await + pub async fn add(&self, alias: String) -> Result, Error> { + self.0.lock().await.add(alias).await } - pub async fn remove>( - &self, - pkg_id: &PackageId, - interfaces: I, - ) -> Result<(), Error> { - self.0.lock().await.remove(pkg_id, interfaces).await + pub async fn gc(&self, alias: String) -> Result<(), Error> { + self.0.lock().await.gc(alias).await } } pub struct MdnsControllerInner { alias_cmd: Option, - services: BTreeMap<(PackageId, InterfaceId), TorSecretKeyV3>, + services: BTreeMap>, } impl MdnsControllerInner { @@ -76,35 +66,30 @@ impl MdnsControllerInner { self.alias_cmd = Some( Command::new("avahi-alias") .kill_on_drop(true) - .args(self.services.iter().map(|(_, key)| { - key.public() - .get_onion_address() - .get_address_without_dot_onion() - })) + .args( + self.services + .iter() + .filter(|(_, rc)| rc.strong_count() > 0) + .map(|(s, _)| s), + ) .spawn()?, ); Ok(()) } - async fn add<'a, I: IntoIterator>( - &mut self, - pkg_id: &PackageId, - interfaces: I, - ) -> Result<(), Error> { - self.services.extend( - interfaces - .into_iter() - .map(|(interface_id, key)| ((pkg_id.clone(), interface_id), key)), - ); + async fn add(&mut self, alias: String) -> Result, Error> { + let rc = if let Some(rc) = Weak::upgrade(&self.services.remove(&alias).unwrap_or_default()) + { + rc + } else { + Arc::new(()) + }; + self.services.insert(alias, Arc::downgrade(&rc)); self.sync().await?; - Ok(()) + Ok(rc) } - async fn remove>( - &mut self, - pkg_id: &PackageId, - interfaces: I, - ) -> Result<(), Error> { - for interface_id in interfaces { - self.services.remove(&(pkg_id.clone(), interface_id)); + async fn gc(&mut self, alias: String) -> Result<(), Error> { + if let Some(rc) = Weak::upgrade(&self.services.remove(&alias).unwrap_or_default()) { + self.services.insert(alias, Arc::downgrade(&rc)); } self.sync().await?; Ok(()) diff --git a/backend/src/net/mod.rs b/backend/src/net/mod.rs index 4096feed4e..795a5d2bdb 100644 --- a/backend/src/net/mod.rs +++ b/backend/src/net/mod.rs @@ -1,54 +1,33 @@ -use std::collections::BTreeMap; use std::sync::Arc; use futures::future::BoxFuture; use hyper::{Body, Error as HyperError, Request, Response}; -use indexmap::IndexSet; use rpc_toolkit::command; -use self::interface::InterfaceId; -use crate::net::interface::LanPortConfig; -use crate::util::serde::Port; use crate::Error; -pub mod cert_resolver; pub mod dhcp; pub mod dns; -pub mod embassy_service_http_server; pub mod interface; +pub mod keys; #[cfg(feature = "avahi")] pub mod mdns; pub mod net_controller; pub mod net_utils; -pub mod proxy_controller; pub mod ssl; pub mod static_server; pub mod tor; pub mod vhost_controller; +pub mod web_server; pub mod wifi; -const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl"; +pub const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl"; #[command(subcommands(tor::tor, dhcp::dhcp))] pub fn net() -> Result<(), Error> { Ok(()) } -#[derive(Default)] -struct PackageNetInfo { - interfaces: BTreeMap, -} -pub struct InterfaceMetadata { - pub fqdn: String, - pub lan_config: BTreeMap, - pub protocols: IndexSet, -} - -/// Indicates that the net controller has created the -/// SSL keys -#[derive(Clone, Copy)] -pub struct GeneratedCertificateMountPoint(()); - pub type HttpHandler = Arc< dyn Fn(Request) -> BoxFuture<'static, Result, HyperError>> + Send + Sync, >; diff --git a/backend/src/net/net_controller.rs b/backend/src/net/net_controller.rs index c02dc5dace..2bcd3c917f 100644 --- a/backend/src/net/net_controller.rs +++ b/backend/src/net/net_controller.rs @@ -1,278 +1,346 @@ -use std::net::{Ipv4Addr, SocketAddr}; -use std::path::PathBuf; -use std::str::FromStr; +use std::collections::BTreeMap; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::{Arc, Weak}; +use color_eyre::eyre::eyre; use models::InterfaceId; -use openssl::pkey::{PKey, Private}; -use openssl::x509::X509; -use patch_db::DbHandle; -use sqlx::PgPool; -use torut::onion::{OnionAddressV3, TorSecretKeyV3}; +use sqlx::PgExecutor; use tracing::instrument; -use crate::context::RpcContext; -use crate::hostname::{get_embassyd_tor_addr, get_hostname, HostNameReceipt}; +use crate::error::ErrorCollection; +use crate::hostname::Hostname; use crate::net::dns::DnsController; -use crate::net::interface::{Interface, TorConfig}; +use crate::net::keys::Key; #[cfg(feature = "avahi")] use crate::net::mdns::MdnsController; -use crate::net::net_utils::ResourceFqdn; -use crate::net::proxy_controller::ProxyController; -use crate::net::ssl::SslManager; +use crate::net::ssl::{export_cert, SslManager}; use crate::net::tor::TorController; -use crate::net::{ - GeneratedCertificateMountPoint, HttpHandler, InterfaceMetadata, PACKAGE_CERT_PATH, -}; +use crate::net::vhost_controller::VHostController; use crate::s9pk::manifest::PackageId; -use crate::Error; +use crate::volume::cert_dir; +use crate::{Error, HOST_IP}; pub struct NetController { - pub tor: TorController, + pub(super) tor: TorController, #[cfg(feature = "avahi")] - pub mdns: MdnsController, - pub proxy: ProxyController, - pub ssl: SslManager, - pub dns: DnsController, + pub(super) mdns: MdnsController, + pub(super) vhost: VHostController, + pub(super) dns: DnsController, + pub(super) ssl: Arc, + pub(super) os_bindings: Vec>, } impl NetController { - #[instrument(skip(db, db_handle))] - pub async fn init( - embassyd_addr: SocketAddr, - embassyd_tor_key: TorSecretKeyV3, + #[instrument] + pub async fn init( tor_control: SocketAddr, dns_bind: &[SocketAddr], - db: PgPool, - db_handle: &mut Db, - import_root_ca: Option<(PKey, X509)>, + ssl: SslManager, + hostname: &Hostname, + os_key: &Key, ) -> Result { - let receipts = HostNameReceipt::new(db_handle).await?; - let embassy_host_name = get_hostname(db_handle, &receipts).await?; - let embassy_name = embassy_host_name.local_domain_name(); - - let fqdn_name = ResourceFqdn::from_str(&embassy_name)?; - - let ssl = match import_root_ca { - None => SslManager::init(db.clone(), db_handle).await, - Some(a) => SslManager::import_root_ca(db.clone(), a.0, a.1).await, - }?; - Ok(Self { - tor: TorController::init(embassyd_addr, embassyd_tor_key, tor_control).await?, + let ssl = Arc::new(ssl); + let mut res = Self { + tor: TorController::init(tor_control).await?, #[cfg(feature = "avahi")] mdns: MdnsController::init().await?, - proxy: ProxyController::init(embassyd_addr, fqdn_name, ssl.clone()).await?, - ssl, + vhost: VHostController::new(ssl.clone()), dns: DnsController::init(dns_bind).await?, - }) - } - - pub async fn setup_embassy_ui(rpc_ctx: RpcContext) -> Result<(), Error> { - NetController::setup_embassy_http_ui_handle(rpc_ctx.clone()).await?; - NetController::setup_embassy_https_ui_handle(rpc_ctx.clone()).await?; - - Ok(()) + ssl, + os_bindings: Vec::new(), + }; + res.add_os_bindings(hostname, os_key).await?; + Ok(res) } - async fn setup_embassy_https_ui_handle(rpc_ctx: RpcContext) -> Result<(), Error> { - let host_name = rpc_ctx.net_controller.proxy.get_hostname().await; - - let host_name_fqdn: ResourceFqdn = host_name.parse()?; - - let handler: HttpHandler = - crate::net::static_server::main_ui_server_router(rpc_ctx.clone()).await?; - - let eos_pkg_id: PackageId = "embassy".parse().unwrap(); + async fn add_os_bindings(&mut self, hostname: &Hostname, key: &Key) -> Result<(), Error> { + // Internal DNS + self.vhost + .add( + key.clone(), + Some("embassy".into()), + 443, + ([127, 0, 0, 1], 80).into(), + false, + ) + .await?; + self.os_bindings + .push(self.dns.add(None, HOST_IP.into()).await?); + + // LAN IP + self.os_bindings.push( + self.vhost + .add(key.clone(), None, 443, ([127, 0, 0, 1], 80).into(), false) + .await?, + ); - if let ResourceFqdn::Uri { - full_uri: _, - root, - tld: _, - } = host_name_fqdn.clone() - { - let root_cert = rpc_ctx - .net_controller - .ssl - .certificate_for(&root, &eos_pkg_id) - .await?; + // localhost + self.os_bindings.push( + self.vhost + .add( + key.clone(), + Some("localhost".into()), + 443, + ([127, 0, 0, 1], 80).into(), + false, + ) + .await?, + ); + self.os_bindings.push( + self.vhost + .add( + key.clone(), + Some(hostname.no_dot_host_name()), + 443, + ([127, 0, 0, 1], 80).into(), + false, + ) + .await?, + ); - rpc_ctx - .net_controller - .proxy - .add_certificate_to_resolver(host_name_fqdn.clone(), root_cert.clone()) - .await?; + // LAN mDNS + self.os_bindings.push( + self.vhost + .add( + key.clone(), + Some(hostname.local_domain_name()), + 443, + ([127, 0, 0, 1], 80).into(), + false, + ) + .await?, + ); - rpc_ctx - .net_controller - .proxy - .add_handle(443, host_name_fqdn.clone(), handler.clone(), true) - .await?; - }; + // Tor (http) + self.os_bindings.push( + self.tor + .add(&key.tor_key(), 80, ([127, 0, 0, 1], 80).into()) + .await?, + ); - // serving ip https is not yet supported + // Tor (https) + self.os_bindings.push( + self.vhost + .add( + key.clone(), + Some(key.tor_address().to_string()), + 443, + ([127, 0, 0, 1], 80).into(), + false, + ) + .await?, + ); + self.os_bindings.push( + self.tor + .add(&key.tor_key(), 443, ([127, 0, 0, 1], 443).into()) + .await?, + ); Ok(()) } - async fn setup_embassy_http_ui_handle(rpc_ctx: RpcContext) -> Result<(), Error> { - let host_name = rpc_ctx.net_controller.proxy.get_hostname().await; - - let embassy_tor_addr = get_embassyd_tor_addr(rpc_ctx.clone()).await?; - let embassy_tor_fqdn: ResourceFqdn = embassy_tor_addr.parse()?; - let host_name_fqdn: ResourceFqdn = host_name.parse()?; - let ip_fqdn: ResourceFqdn = ResourceFqdn::IpAddr; - - let localhost_fqdn = ResourceFqdn::LocalHost; - - let handler: HttpHandler = - crate::net::static_server::main_ui_server_router(rpc_ctx.clone()).await?; - - rpc_ctx - .net_controller - .proxy - .add_handle(80, embassy_tor_fqdn.clone(), handler.clone(), false) - .await?; - - rpc_ctx - .net_controller - .proxy - .add_handle(80, host_name_fqdn.clone(), handler.clone(), false) - .await?; + #[instrument(skip(self))] + pub async fn create_service( + self: &Arc, + package: PackageId, + ip: Ipv4Addr, + ) -> Result { + let dns = self.dns.add(Some(package.clone()), ip).await?; + + Ok(NetService { + id: package, + ip, + dns, + controller: Arc::downgrade(self), + tor: BTreeMap::new(), + lan: BTreeMap::new(), + }) + } - rpc_ctx - .net_controller - .proxy - .add_handle(80, ip_fqdn.clone(), handler.clone(), false) - .await?; + async fn add_tor( + &self, + key: &Key, + external: u16, + target: SocketAddr, + ) -> Result>, Error> { + let mut rcs = Vec::with_capacity(1); + rcs.push(self.tor.add(&key.tor_key(), external, target).await?); + Ok(rcs) + } - rpc_ctx - .net_controller - .proxy - .add_handle(80, localhost_fqdn.clone(), handler.clone(), false) - .await?; + async fn remove_tor(&self, key: &Key, external: u16, rcs: Vec>) -> Result<(), Error> { + drop(rcs); + self.tor.gc(&key.tor_key(), external).await + } - Ok(()) + async fn add_lan( + &self, + key: Key, + external: u16, + target: SocketAddr, + connect_ssl: bool, + ) -> Result>, Error> { + let mut rcs = Vec::with_capacity(2); + rcs.push( + self.vhost + .add( + key.clone(), + Some(key.local_address()), + external, + target.into(), + connect_ssl, + ) + .await?, + ); + #[cfg(feature = "avahi")] + rcs.push(self.mdns.add(key.base_address()).await?); + Ok(rcs) } - pub fn ssl_directory_for(pkg_id: &PackageId) -> PathBuf { - PathBuf::from(PACKAGE_CERT_PATH).join(pkg_id) + async fn remove_lan(&self, key: &Key, external: u16, rcs: Vec>) -> Result<(), Error> { + drop(rcs); + #[cfg(feature = "avahi")] + self.mdns.gc(key.base_address()).await?; + self.vhost.gc(Some(key.local_address()), external).await } +} - #[instrument(skip(self, interfaces, _generated_certificate))] - pub async fn add<'a, I>( - &self, - pkg_id: &PackageId, - ip: Ipv4Addr, - interfaces: I, - _generated_certificate: GeneratedCertificateMountPoint, +pub struct NetService { + id: PackageId, + ip: Ipv4Addr, + dns: Arc<()>, + controller: Weak, + tor: BTreeMap<(InterfaceId, u16), (Key, Vec>)>, + lan: BTreeMap<(InterfaceId, u16), (Key, Vec>)>, +} +impl NetService { + fn net_controller(&self) -> Result, Error> { + Weak::upgrade(&self.controller).ok_or_else(|| { + Error::new( + eyre!("NetController is shutdown"), + crate::ErrorKind::Network, + ) + }) + } + pub async fn add_tor( + &mut self, + secrets: &mut Ex, + id: InterfaceId, + external: u16, + internal: u16, ) -> Result<(), Error> where - I: IntoIterator + Clone, - for<'b> &'b I: IntoIterator, + for<'a> &'a mut Ex: PgExecutor<'a>, { - let interfaces_tor = interfaces - .clone() - .into_iter() - .filter_map(|i| match i.1.tor_config.clone() { - None => None, - Some(cfg) => Some((i.0, cfg, i.2)), - }) - .collect::>(); - let (tor_res, _, proxy_res, _) = tokio::join!( - self.tor.add(pkg_id, ip, interfaces_tor), - { - #[cfg(feature = "avahi")] - let mdns_fut = self.mdns.add( - pkg_id, - interfaces - .clone() - .into_iter() - .map(|(interface_id, _, key)| (interface_id, key)), - ); - - #[cfg(not(feature = "avahi"))] - let mdns_fut = futures::future::ready(()); - mdns_fut - }, - { - let interfaces = - interfaces - .clone() - .into_iter() - .filter_map(|(id, interface, tor_key)| { - interface.lan_config.as_ref().map(|cfg| { - ( - id, - InterfaceMetadata { - fqdn: OnionAddressV3::from(&tor_key.public()) - .get_address_without_dot_onion() - + ".local", - lan_config: cfg.clone(), - protocols: interface.protocols.clone(), - }, - ) - }) - }); - self.proxy - .add_docker_service(pkg_id.clone(), ip, interfaces) - }, - self.dns.add(pkg_id, ip), + let key = Key::for_interface(secrets, Some((self.id.clone(), id.clone()))).await?; + let ctrl = self.net_controller()?; + let tor_idx = (id, external); + let mut tor = self + .tor + .remove(&tor_idx) + .unwrap_or_else(|| (key.clone(), Vec::new())); + tor.1.append( + &mut ctrl + .add_tor(&key, external, SocketAddr::new(self.ip.into(), internal)) + .await?, ); - tor_res?; - proxy_res?; - + self.tor.insert(tor_idx, tor); Ok(()) } - - #[instrument(skip(self, interfaces))] - pub async fn remove + Clone>( - &self, - pkg_id: &PackageId, - ip: Ipv4Addr, - interfaces: I, - ) -> Result<(), Error> { - let (tor_res, _, proxy_res, _) = tokio::join!( - self.tor.remove(pkg_id, interfaces.clone()), - { - #[cfg(feature = "avahi")] - let mdns_fut = self.mdns.remove(pkg_id, interfaces); - #[cfg(not(feature = "avahi"))] - let mdns_fut = futures::future::ready(()); - mdns_fut - }, - self.proxy.remove_docker_service(pkg_id), - self.dns.remove(pkg_id, ip), + pub async fn remove_tor(&mut self, id: InterfaceId, external: u16) -> Result<(), Error> { + let ctrl = self.net_controller()?; + if let Some((key, rcs)) = self.tor.remove(&(id, external)) { + ctrl.remove_tor(&key, external, rcs).await?; + } + Ok(()) + } + pub async fn add_lan( + &mut self, + secrets: &mut Ex, + id: InterfaceId, + external: u16, + internal: u16, + connect_ssl: bool, + ) -> Result<(), Error> + where + for<'a> &'a mut Ex: PgExecutor<'a>, + { + let key = Key::for_interface(secrets, Some((self.id.clone(), id.clone()))).await?; + let ctrl = self.net_controller()?; + let lan_idx = (id, external); + let mut lan = self + .lan + .remove(&lan_idx) + .unwrap_or_else(|| (key.clone(), Vec::new())); + lan.1.append( + &mut ctrl + .add_lan( + key, + external, + SocketAddr::new(self.ip.into(), internal), + connect_ssl, + ) + .await?, ); - tor_res?; - proxy_res?; + self.lan.insert(lan_idx, lan); Ok(()) } - - pub async fn generate_certificate_mountpoint<'a, I>( + pub async fn remove_lan(&mut self, id: InterfaceId, external: u16) -> Result<(), Error> { + let ctrl = self.net_controller()?; + if let Some((key, rcs)) = self.lan.remove(&(id, external)) { + ctrl.remove_lan(&key, external, rcs).await?; + } + Ok(()) + } + pub async fn export_cert( &self, - pkg_id: &PackageId, - interfaces: &I, - ) -> Result + secrets: &mut Ex, + id: &InterfaceId, + ip: IpAddr, + ) -> Result<(), Error> where - I: IntoIterator + Clone, - for<'b> &'b I: IntoIterator, + for<'a> &'a mut Ex: PgExecutor<'a>, { - tracing::info!("Generating SSL Certificate mountpoints for {}", pkg_id); - let package_path = PathBuf::from(PACKAGE_CERT_PATH).join(pkg_id); - tokio::fs::create_dir_all(&package_path).await?; - for (id, _, key) in interfaces { - let dns_base = OnionAddressV3::from(&key.public()).get_address_without_dot_onion(); - let ssl_path_key = package_path.join(format!("{}.key.pem", id)); - let ssl_path_cert = package_path.join(format!("{}.cert.pem", id)); - let (key, chain) = self.ssl.certificate_for(&dns_base, pkg_id).await?; - tokio::try_join!( - crate::net::ssl::export_key(&key, &ssl_path_key), - crate::net::ssl::export_cert(&chain, &ssl_path_cert) - )?; + let key = Key::for_interface(secrets, Some((self.id.clone(), id.clone()))).await?; + let ctrl = self.net_controller()?; + let cert = ctrl.ssl.with_certs(key, ip).await?; + export_cert(&cert.fullchain_nistp256(), &cert_dir(&self.id, id)).await?; // TODO: can upgrade to ed25519? + Ok(()) + } + pub async fn remove_all(mut self) -> Result<(), Error> { + let mut errors = ErrorCollection::new(); + if let Some(ctrl) = Weak::upgrade(&self.controller) { + for ((_, external), (key, rcs)) in std::mem::take(&mut self.lan) { + errors.handle(ctrl.remove_lan(&key, external, rcs).await); + } + for ((_, external), (key, rcs)) in std::mem::take(&mut self.tor) { + errors.handle(ctrl.remove_tor(&key, external, rcs).await); + } + std::mem::take(&mut self.dns); + errors.handle(ctrl.dns.gc(Some(self.id.clone()), self.ip).await); + errors.into_result() + } else { + Err(Error::new( + eyre!("NetController is shutdown"), + crate::ErrorKind::Network, + )) } - Ok(GeneratedCertificateMountPoint(())) } +} - pub async fn export_root_ca(&self) -> Result<(PKey, X509), Error> { - self.ssl.export_root_ca().await +impl Drop for NetService { + fn drop(&mut self) { + let svc = std::mem::replace( + self, + NetService { + id: Default::default(), + ip: [0, 0, 0, 0].into(), + dns: Default::default(), + controller: Default::default(), + tor: Default::default(), + lan: Default::default(), + }, + ); + tokio::spawn(async move { svc.remove_all().await.unwrap() }); } } diff --git a/backend/src/net/net_utils.rs b/backend/src/net/net_utils.rs index 81a5850711..1736912b2b 100644 --- a/backend/src/net/net_utils.rs +++ b/backend/src/net/net_utils.rs @@ -1,14 +1,12 @@ -use std::fmt; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::convert::Infallible; +use std::net::{Ipv4Addr, Ipv6Addr}; use std::path::Path; -use std::str::FromStr; use async_stream::try_stream; use color_eyre::eyre::eyre; use futures::stream::BoxStream; use futures::{StreamExt, TryStreamExt}; -use http::{Request, Uri}; -use hyper::Body; +use ipnet::{Ipv4Net, Ipv6Net}; use tokio::process::Command; use crate::util::Invoke; @@ -19,11 +17,7 @@ fn parse_iface_ip(output: &str) -> Result, Error> { if output.is_empty() { return Ok(None); } - if let Some(ip) = output - .split_ascii_whitespace() - .nth(3) - .and_then(|range| range.split("/").next()) - { + if let Some(ip) = output.split_ascii_whitespace().nth(3) { Ok(Some(ip)) } else { Err(Error::new( @@ -33,7 +27,7 @@ fn parse_iface_ip(output: &str) -> Result, Error> { } } -pub async fn get_iface_ipv4_addr(iface: &str) -> Result, Error> { +pub async fn get_iface_ipv4_addr(iface: &str) -> Result, Error> { Ok(parse_iface_ip(&String::from_utf8( Command::new("ip") .arg("-4") @@ -44,11 +38,11 @@ pub async fn get_iface_ipv4_addr(iface: &str) -> Result, Error> .invoke(crate::ErrorKind::Network) .await?, )?)? - .map(|s| s.parse()) + .map(|s| Ok::<_, Error>((s.split("/").next().unwrap().parse()?, s.parse()?))) .transpose()?) } -pub async fn get_iface_ipv6_addr(iface: &str) -> Result, Error> { +pub async fn get_iface_ipv6_addr(iface: &str) -> Result, Error> { Ok(parse_iface_ip(&String::from_utf8( Command::new("ip") .arg("-6") @@ -59,7 +53,7 @@ pub async fn get_iface_ipv6_addr(iface: &str) -> Result, Error> .invoke(crate::ErrorKind::Network) .await?, )?)? - .map(|s| s.parse()) + .map(|s| Ok::<_, Error>((s.split("/").next().unwrap().parse()?, s.parse()?))) .transpose()?) } @@ -110,132 +104,20 @@ pub async fn find_eth_iface() -> Result { )) } -pub fn host_addr_fqdn(req: &Request) -> Result { - let host = req.headers().get(http::header::HOST); - - match host { - Some(host) => { - let host_str = host - .to_str() - .map_err(|e| Error::new(eyre!("{}", e), crate::ErrorKind::Ascii))? - .to_string(); - - let host_uri: ResourceFqdn = host_str.split(':').next().unwrap().parse()?; - - Ok(host_uri) - } - - None => Err(Error::new( - eyre!("No Host header"), - crate::ErrorKind::MissingHeader, - )), - } -} - -#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone)] -pub enum ResourceFqdn { - IpAddr, - Uri { - full_uri: String, - root: String, - tld: Tld, - }, - LocalHost, -} - -impl fmt::Display for ResourceFqdn { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ResourceFqdn::Uri { - full_uri, - root: _, - tld: _, - } => { - write!(f, "{}", full_uri) - } - ResourceFqdn::LocalHost => write!(f, "localhost"), - ResourceFqdn::IpAddr => write!(f, "ip-address"), - } - } -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] -pub enum Tld { - Local, - Onion, - Embassy, -} - -impl fmt::Display for Tld { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Tld::Local => write!(f, ".local"), - Tld::Onion => write!(f, ".onion"), - Tld::Embassy => write!(f, ".embassy"), - } +#[pin_project::pin_project] +pub struct SingleAccept(Option); +impl SingleAccept { + pub fn new(conn: T) -> Self { + Self(Some(conn)) } } - -impl FromStr for ResourceFqdn { - type Err = Error; - - fn from_str(input: &str) -> Result { - if input.parse::().is_ok() { - return Ok(ResourceFqdn::IpAddr); - } - - if input == "localhost" { - return Ok(ResourceFqdn::LocalHost); - } - - let hostname_split: Vec<&str> = input.split('.').collect(); - - if hostname_split.len() != 2 { - return Err(Error::new( - eyre!("invalid url tld number: add support for tldextract to parse complex urls like blah.domain.co.uk and etc?"), - crate::ErrorKind::ParseUrl, - )); - } - - match hostname_split[1] { - "local" => Ok(ResourceFqdn::Uri { - full_uri: input.to_owned(), - root: hostname_split[0].to_owned(), - tld: Tld::Local, - }), - "embassy" => Ok(ResourceFqdn::Uri { - full_uri: input.to_owned(), - root: hostname_split[0].to_owned(), - tld: Tld::Embassy, - }), - "onion" => Ok(ResourceFqdn::Uri { - full_uri: input.to_owned(), - root: hostname_split[0].to_owned(), - tld: Tld::Onion, - }), - _ => Err(Error::new( - eyre!("Unknown TLD for enum"), - crate::ErrorKind::ParseUrl, - )), - } - } -} - -impl TryFrom for ResourceFqdn { - type Error = Error; - - fn try_from(value: Uri) -> Result { - Self::from_str(&value.to_string()) +impl hyper::server::accept::Accept for SingleAccept { + type Conn = T; + type Error = Infallible; + fn poll_accept( + self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll>> { + std::task::Poll::Ready(self.project().0.take().map(Ok)) } } - -pub fn is_upgrade_req(req: &Request) -> bool { - req.headers() - .get("connection") - .and_then(|c| c.to_str().ok()) - .map(|c| { - c.split(",") - .any(|c| c.trim().eq_ignore_ascii_case("upgrade")) - }) - .unwrap_or(false) -} diff --git a/backend/src/net/proxy_controller.rs b/backend/src/net/proxy_controller.rs deleted file mode 100644 index b708f87417..0000000000 --- a/backend/src/net/proxy_controller.rs +++ /dev/null @@ -1,334 +0,0 @@ -use std::collections::BTreeMap; -use std::net::{Ipv4Addr, SocketAddr}; -use std::str::FromStr; -use std::sync::Arc; - -use color_eyre::eyre::eyre; -use futures::FutureExt; -use http::uri::{Authority, Scheme}; -use http::{Request, Response, Uri}; -use hyper::{Body, Error as HyperError}; -use models::{InterfaceId, PackageId}; -use openssl::pkey::{PKey, Private}; -use openssl::x509::X509; -use tokio::sync::Mutex; -use tracing::{error, instrument}; - -use crate::net::net_utils::{is_upgrade_req, ResourceFqdn}; -use crate::net::ssl::SslManager; -use crate::net::vhost_controller::VHOSTController; -use crate::net::{HttpHandler, InterfaceMetadata, PackageNetInfo}; -use crate::{Error, ResultExt}; - -pub struct ProxyController { - inner: Mutex, -} - -impl ProxyController { - pub async fn init( - embassyd_socket_addr: SocketAddr, - embassy_fqdn: ResourceFqdn, - ssl_manager: SslManager, - ) -> Result { - Ok(ProxyController { - inner: Mutex::new( - ProxyControllerInner::init(embassyd_socket_addr, embassy_fqdn, ssl_manager).await?, - ), - }) - } - - pub async fn add_docker_service>( - &self, - package: PackageId, - ipv4: Ipv4Addr, - interfaces: I, - ) -> Result<(), Error> { - self.inner - .lock() - .await - .add_docker_service(package, ipv4, interfaces) - .await - } - - pub async fn remove_docker_service(&self, package: &PackageId) -> Result<(), Error> { - self.inner.lock().await.remove_docker_service(package).await - } - - pub async fn add_certificate_to_resolver( - &self, - fqdn: ResourceFqdn, - cert_data: (PKey, Vec), - ) -> Result<(), Error> { - self.inner - .lock() - .await - .add_certificate_to_resolver(fqdn, cert_data) - .await - } - - pub async fn add_handle( - &self, - ext_port: u16, - fqdn: ResourceFqdn, - handler: HttpHandler, - is_ssl: bool, - ) -> Result<(), Error> { - self.inner - .lock() - .await - .add_handle(ext_port, fqdn, handler, is_ssl) - .await - } - - pub async fn get_hostname(&self) -> String { - self.inner.lock().await.get_embassy_hostname() - } - - async fn proxy( - client: &hyper::Client, - mut req: Request, - addr: SocketAddr, - ) -> Result, HyperError> { - let mut uri = std::mem::take(req.uri_mut()).into_parts(); - - uri.scheme = Some(Scheme::HTTP); - uri.authority = Authority::from_str(&addr.to_string()).ok(); - match Uri::from_parts(uri) { - Ok(uri) => *req.uri_mut() = uri, - Err(e) => error!("Error rewriting uri: {}", e), - } - let addr = req.uri().to_string(); - - if is_upgrade_req(&req) { - let upgraded_req = hyper::upgrade::on(&mut req); - let mut res = client.request(req).await?; - let upgraded_res = hyper::upgrade::on(&mut res); - tokio::spawn(async move { - if let Err(e) = async { - let mut req = upgraded_req.await?; - let mut res = upgraded_res.await?; - tokio::io::copy_bidirectional(&mut req, &mut res).await?; - - Ok::<_, color_eyre::eyre::Report>(()) - } - .await - { - error!("error binding together tcp streams for {}: {}", addr, e); - } - }); - Ok(res) - } else { - client.request(req).await - } - } -} -struct ProxyControllerInner { - ssl_manager: SslManager, - vhosts: VHOSTController, - embassyd_fqdn: ResourceFqdn, - docker_interfaces: BTreeMap, - docker_iface_lookups: BTreeMap<(PackageId, InterfaceId), ResourceFqdn>, -} - -impl ProxyControllerInner { - #[instrument] - async fn init( - embassyd_socket_addr: SocketAddr, - embassyd_fqdn: ResourceFqdn, - ssl_manager: SslManager, - ) -> Result { - let inner = ProxyControllerInner { - vhosts: VHOSTController::init(embassyd_socket_addr), - ssl_manager, - embassyd_fqdn, - docker_interfaces: BTreeMap::new(), - docker_iface_lookups: BTreeMap::new(), - }; - - Ok(inner) - } - - async fn add_certificate_to_resolver( - &mut self, - hostname: ResourceFqdn, - cert_data: (PKey, Vec), - ) -> Result<(), Error> { - self.vhosts - .cert_resolver - .add_certificate_to_resolver(hostname, cert_data) - .await - .map_err(|err| { - Error::new( - eyre!("Unable to add ssl cert to the resolver: {}", err), - crate::ErrorKind::Network, - ) - })?; - - Ok(()) - } - - async fn add_package_certificate_to_resolver( - &mut self, - resource_fqdn: ResourceFqdn, - pkg_id: PackageId, - ) -> Result<(), Error> { - let package_cert = match resource_fqdn.clone() { - ResourceFqdn::IpAddr => { - return Err(Error::new( - eyre!("ssl not supported for ip addresses"), - crate::ErrorKind::Network, - )) - } - ResourceFqdn::Uri { - full_uri: _, - root, - tld: _, - } => self.ssl_manager.certificate_for(&root, &pkg_id).await?, - ResourceFqdn::LocalHost => { - return Err(Error::new( - eyre!("ssl not supported for localhost"), - crate::ErrorKind::Network, - )) - } - }; - - self.vhosts - .cert_resolver - .add_certificate_to_resolver(resource_fqdn, package_cert) - .await - .map_err(|err| { - Error::new( - eyre!("Unable to add ssl cert to the resolver: {}", err), - crate::ErrorKind::Network, - ) - })?; - - Ok(()) - } - - pub async fn add_handle( - &mut self, - external_svc_port: u16, - fqdn: ResourceFqdn, - svc_handler: HttpHandler, - is_ssl: bool, - ) -> Result<(), Error> { - self.vhosts - .add_server_or_handle(external_svc_port, fqdn, svc_handler, is_ssl) - .await - } - - #[instrument(skip(self, interfaces))] - pub async fn add_docker_service>( - &mut self, - package: PackageId, - docker_ipv4: Ipv4Addr, - interfaces: I, - ) -> Result<(), Error> { - let mut interface_map = interfaces - .into_iter() - .filter(|(_, meta)| { - // don't add stuff for anything we can't connect to over some flavor of http - (meta.protocols.contains("http") || meta.protocols.contains("https")) - // also don't add anything unless it has at least one exposed port - && !meta.lan_config.is_empty() - }) - .collect::>(); - - for (id, meta) in interface_map.iter() { - for (external_svc_port, lan_port_config) in meta.lan_config.iter() { - let full_fqdn = ResourceFqdn::from_str(&meta.fqdn).unwrap(); - - self.docker_iface_lookups - .insert((package.clone(), id.clone()), full_fqdn.clone()); - - self.add_package_certificate_to_resolver(full_fqdn.clone(), package.clone()) - .await?; - - let svc_handler = - Self::create_docker_handle((docker_ipv4, lan_port_config.internal).into()) - .await; - - self.add_handle( - external_svc_port.0, - full_fqdn.clone(), - svc_handler, - lan_port_config.ssl, - ) - .await?; - } - } - - let docker_interface = self.docker_interfaces.entry(package.clone()).or_default(); - docker_interface.interfaces.append(&mut interface_map); - - Ok(()) - } - - async fn create_docker_handle(internal_addr: SocketAddr) -> HttpHandler { - let svc_handler: HttpHandler = Arc::new(move |req| { - let client = hyper::client::Client::builder() - .set_host(false) - .build_http(); - async move { ProxyController::proxy(&client, req, internal_addr).await }.boxed() - }); - - svc_handler - } - - #[instrument(skip(self))] - pub async fn remove_docker_service(&mut self, package: &PackageId) -> Result<(), Error> { - let mut server_removals: Vec<(u16, InterfaceId)> = Default::default(); - - let net_info = match self.docker_interfaces.get(package) { - Some(a) => a, - None => return Ok(()), - }; - - for (id, meta) in &net_info.interfaces { - for (service_ext_port, _lan_port_config) in meta.lan_config.iter() { - if let Some(server) = self.vhosts.service_servers.get_mut(&service_ext_port.0) { - if let Some(fqdn) = self - .docker_iface_lookups - .get(&(package.clone(), id.clone())) - { - server.remove_svc_handler_mapping(fqdn.to_owned()).await?; - self.vhosts - .cert_resolver - .remove_cert(fqdn.to_owned()) - .await?; - - let mapping = server.svc_mapping.read().await; - - if mapping.is_empty() { - server_removals.push((service_ext_port.0, id.to_owned())); - } - } - } - } - } - - for (port, interface_id) in server_removals { - if let Some(removed_server) = self.vhosts.service_servers.remove(&port) { - removed_server.shutdown.send(()).map_err(|_| { - Error::new( - eyre!("Hyper server did not quit properly"), - crate::ErrorKind::Unknown, - ) - })?; - removed_server - .handle - .await - .with_kind(crate::ErrorKind::Unknown)?; - self.docker_interfaces.remove(&package.clone()); - self.docker_iface_lookups - .remove(&(package.clone(), interface_id)); - } - } - Ok(()) - } - - pub fn get_embassy_hostname(&self) -> String { - self.embassyd_fqdn.to_string() - } -} diff --git a/backend/src/net/ssl.rs b/backend/src/net/ssl.rs index 358301aec4..258adec1bc 100644 --- a/backend/src/net/ssl.rs +++ b/backend/src/net/ssl.rs @@ -1,7 +1,8 @@ use std::cmp::Ordering; +use std::collections::{BTreeMap, BTreeSet}; +use std::net::IpAddr; use std::path::Path; -use color_eyre::eyre::eyre; use futures::FutureExt; use openssl::asn1::{Asn1Integer, Asn1Time}; use openssl::bn::{BigNum, MsbOption}; @@ -11,300 +12,125 @@ use openssl::nid::Nid; use openssl::pkey::{PKey, Private}; use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509}; use openssl::*; -use patch_db::DbHandle; -use sqlx::PgPool; -use tokio::process::Command; -use tokio::sync::Mutex; +use tokio::sync::{Mutex, RwLock}; use tracing::instrument; -use crate::s9pk::manifest::PackageId; -use crate::util::Invoke; +use crate::account::AccountInfo; +use crate::hostname::Hostname; +use crate::net::dhcp::ips; +use crate::net::keys::{Key, KeyInfo}; use crate::{Error, ErrorKind, ResultExt}; static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you. -pub const ROOT_CA_STATIC_PATH: &str = "/var/lib/embassy/ssl/root-ca.crt"; -#[derive(Debug, Clone)] -pub struct SslManager { - store: SslStore, - root_cert: X509, - int_key: PKey, - int_cert: X509, -} - -#[derive(Debug, Clone)] -struct SslStore { - secret_store: PgPool, +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct CertPair { + pub ed25519: X509, + pub nistp256: X509, } -impl SslStore { - fn new(db: PgPool) -> Result { - Ok(SslStore { secret_store: db }) - } - #[instrument(skip(self))] - async fn save_root_certificate(&self, key: &PKey, cert: &X509) -> Result<(), Error> { - let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?; - let cert_str = String::from_utf8(cert.to_pem()?)?; - let _n = sqlx::query!("INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (0, $1, $2, NULL, now(), now())", key_str, cert_str).execute(&self.secret_store).await?; - Ok(()) - } - #[instrument(skip(self))] - async fn load_root_certificate(&self) -> Result, X509)>, Error> { - let m_row = - sqlx::query!("SELECT priv_key_pem, certificate_pem FROM certificates WHERE id = 0;") - .fetch_optional(&self.secret_store) - .await?; - match m_row { - None => Ok(None), - Some(row) => { - let priv_key = PKey::private_key_from_pem(&row.priv_key_pem.into_bytes())?; - let certificate = X509::from_pem(&row.certificate_pem.into_bytes())?; - Ok(Some((priv_key, certificate))) - } - } - } - #[instrument(skip(self))] - async fn save_intermediate_certificate( - &self, - key: &PKey, - cert: &X509, - ) -> Result<(), Error> { - let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?; - let cert_str = String::from_utf8(cert.to_pem()?)?; - let _n = sqlx::query!("INSERT INTO certificates (id, priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES (1, $1, $2, NULL, now(), now())", key_str, cert_str).execute(&self.secret_store).await?; - Ok(()) - } - async fn load_intermediate_certificate(&self) -> Result, X509)>, Error> { - let m_row = - sqlx::query!("SELECT priv_key_pem, certificate_pem FROM certificates WHERE id = 1;") - .fetch_optional(&self.secret_store) - .await?; - match m_row { - None => Ok(None), - Some(row) => { - let priv_key = PKey::private_key_from_pem(&row.priv_key_pem.into_bytes())?; - let certificate = X509::from_pem(&row.certificate_pem.into_bytes())?; - Ok(Some((priv_key, certificate))) - } - } - } - #[instrument(skip(self))] - async fn import_root_certificate( - &self, - root_key: &PKey, - root_cert: &X509, - ) -> Result<(), Error> { - // remove records for both root and intermediate CA - sqlx::query!("DELETE FROM certificates WHERE id = 0 OR id = 1;") - .execute(&self.secret_store) - .await?; - self.save_root_certificate(root_key, root_cert).await?; - Ok(()) - } - #[instrument(skip(self))] - async fn save_certificate( - &self, - key: &PKey, - cert: &X509, - lookup_string: &str, - ) -> Result<(), Error> { - let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?; - let cert_str = String::from_utf8(cert.to_pem()?)?; - let _n = sqlx::query!("INSERT INTO certificates (priv_key_pem, certificate_pem, lookup_string, created_at, updated_at) VALUES ($1, $2, $3, now(), now())", key_str, cert_str, lookup_string).execute(&self.secret_store).await?; - Ok(()) - } - async fn load_certificate( - &self, - lookup_string: &str, - ) -> Result, X509)>, Error> { - let m_row = sqlx::query!( - "SELECT priv_key_pem, certificate_pem FROM certificates WHERE lookup_string = $1", - lookup_string - ) - .fetch_optional(&self.secret_store) - .await?; - match m_row { - None => Ok(None), - Some(row) => { - let priv_key = PKey::private_key_from_pem(&row.priv_key_pem.into_bytes())?; - let certificate = X509::from_pem(&row.certificate_pem.into_bytes())?; - Ok(Some((priv_key, certificate))) +impl CertPair { + fn updated( + pair: Option<&Self>, + hostname: &Hostname, + signer: (&PKey, &X509), + applicant: &Key, + ip: BTreeSet, + ) -> Result<(Self, bool), Error> { + let mut updated = false; + let mut updated_cert = |cert: Option<&X509>, osk: PKey| -> Result { + let mut ips = BTreeSet::new(); + if let Some(cert) = cert { + ips.extend( + cert.subject_alt_names() + .iter() + .flatten() + .filter_map(|a| a.ipaddress()) + .filter_map(|a| match a.len() { + 4 => Some::(<[u8; 4]>::try_from(a).unwrap().into()), + 16 => Some::(<[u8; 16]>::try_from(a).unwrap().into()), + _ => None, + }), + ); + if cert + .not_after() + .compare(Asn1Time::days_from_now(30)?.as_ref())? + == Ordering::Greater + && ips.is_superset(&ip) + { + return Ok(cert.clone()); + } } - } - } - #[instrument(skip(self))] - async fn update_certificate( - &self, - key: &PKey, - cert: &X509, - lookup_string: &str, - ) -> Result<(), Error> { - let key_str = String::from_utf8(key.private_key_to_pem_pkcs8()?)?; - let cert_str = String::from_utf8(cert.to_pem()?)?; - let n = sqlx::query!("UPDATE certificates SET priv_key_pem = $1, certificate_pem = $2, updated_at = now() WHERE lookup_string = $3", key_str, cert_str, lookup_string) - .execute(&self.secret_store).await?; - if n.rows_affected() == 0 { - return Err(Error::new( - eyre!( - "Attempted to update non-existent certificate: {}", - lookup_string - ), - ErrorKind::OpenSsl, - )); - } - Ok(()) + ips.extend(ip.iter().copied()); + updated = true; + make_leaf_cert(signer, (&osk, &SANInfo::new(&applicant, hostname, ips))) + }; + Ok(( + Self { + ed25519: updated_cert(pair.map(|c| &c.ed25519), applicant.openssl_key_ed25519())?, + nistp256: updated_cert( + pair.map(|c| &c.nistp256), + applicant.openssl_key_nistp256(), + )?, + }, + updated, + )) } } -const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1; -lazy_static::lazy_static! { - static ref EC_GROUP: EcGroup = EcGroup::from_curve_name(EC_CURVE_NAME).unwrap(); - static ref SSL_MUTEX: Mutex<()> = Mutex::new(()); // TODO: make thread safe +#[derive(Debug)] +pub struct SslManager { + hostname: Hostname, + root_cert: X509, + int_key: PKey, + int_cert: X509, + cert_cache: RwLock>, } - impl SslManager { - #[instrument(skip(db, handle))] - pub async fn init(db: PgPool, handle: &mut Db) -> Result { - let store = SslStore::new(db)?; - let receipts = crate::hostname::HostNameReceipt::new(handle).await?; - let id = crate::hostname::get_id(handle, &receipts).await?; - let (root_key, root_cert) = match store.load_root_certificate().await? { - None => { - let root_key = generate_key()?; - let server_id = id; - let root_cert = make_root_cert(&root_key, &server_id)?; - store.save_root_certificate(&root_key, &root_cert).await?; - Ok::<_, Error>((root_key, root_cert)) - } - Some((key, cert)) => Ok((key, cert)), - }?; - // generate static file for download, this will gte blown up on embassy restart so it's good to write it on - // every ssl manager init - tokio::fs::create_dir_all( - Path::new(ROOT_CA_STATIC_PATH) - .parent() - .unwrap_or(Path::new("/")), - ) - .await?; - tokio::fs::write(ROOT_CA_STATIC_PATH, root_cert.to_pem()?).await?; - - // write to ca cert store - tokio::fs::write( - "/usr/local/share/ca-certificates/embassy-root-ca.crt", - root_cert.to_pem()?, - ) - .await?; - Command::new("update-ca-certificates") - .invoke(crate::ErrorKind::OpenSsl) - .await?; - - let (int_key, int_cert) = match store.load_intermediate_certificate().await? { - None => { - let int_key = generate_key()?; - let int_cert = make_int_cert((&root_key, &root_cert), &int_key)?; - store - .save_intermediate_certificate(&int_key, &int_cert) - .await?; - Ok::<_, Error>((int_key, int_cert)) - } - Some((key, cert)) => Ok((key, cert)), - }?; - - sqlx::query!("SELECT setval('certificates_id_seq', GREATEST(MAX(id) + 1, nextval('certificates_id_seq') - 1)) FROM certificates") - .fetch_one(&store.secret_store).await?; - - Ok(SslManager { - store, - root_cert, - int_key, - int_cert, - }) - } - - // TODO: currently the burden of proof is on the caller to ensure that all of the arguments to this function are - // consistent. The following properties are assumed and not verified: - // 1. `root_cert` is self-signed and contains the public key that matches the private key `root_key` - // 2. certificate is not past its expiration date - // Warning: If this function ever fails, you must either call it again or regenerate your certificates from scratch - // since it is possible for it to fail after successfully saving the root certificate but before successfully saving - // the intermediate certificate - #[instrument(skip(db))] - pub async fn import_root_ca( - db: PgPool, - root_key: PKey, - root_cert: X509, - ) -> Result { - let store = SslStore::new(db)?; - store.import_root_certificate(&root_key, &root_cert).await?; + pub fn new(account: &AccountInfo) -> Result { let int_key = generate_key()?; - let int_cert = make_int_cert((&root_key, &root_cert), &int_key)?; - store - .save_intermediate_certificate(&int_key, &int_cert) - .await?; - Ok(SslManager { - store, - root_cert, + let int_cert = make_int_cert((&account.root_ca_key, &account.root_ca_cert), &int_key)?; + Ok(Self { + hostname: account.hostname.clone(), + root_cert: account.root_ca_cert.clone(), int_key, int_cert, + cert_cache: RwLock::new(BTreeMap::new()), }) } - - #[instrument(skip(self))] - pub async fn export_root_ca(&self) -> Result<(PKey, X509), Error> { - match self.store.load_root_certificate().await? { - None => Err(Error::new( - eyre!("Failed to export root certificate: root certificate has not been generated"), - ErrorKind::OpenSsl, - )), - Some(a) => Ok(a), + pub async fn with_certs(&self, key: Key, ip: IpAddr) -> Result { + let mut ips = ips().await?; + ips.insert(ip); + let (pair, updated) = CertPair::updated( + self.cert_cache.read().await.get(&key), + &self.hostname, + (&self.int_key, &self.int_cert), + &key, + ips, + )?; + if updated { + self.cert_cache + .write() + .await + .insert(key.clone(), pair.clone()); } - } - #[instrument(skip(self))] - pub async fn certificate_for( - &self, - dns_base: &str, - package_id: &PackageId, - ) -> Result<(PKey, Vec), Error> { - let (key, cert) = match self.store.load_certificate(dns_base).await? { - None => { - let key = generate_key()?; - let cert = make_leaf_cert( - (&self.int_key, &self.int_cert), - (&key, dns_base, package_id), - )?; - self.store.save_certificate(&key, &cert, dns_base).await?; - Ok::<_, Error>((key, cert)) - } - Some((key, cert)) => { - let window_end = Asn1Time::days_from_now(30)?; - let expiration = cert.not_after(); - if expiration.compare(&window_end)? == Ordering::Less { - let key = generate_key()?; - let cert = make_leaf_cert( - (&self.int_key, &self.int_cert), - (&key, dns_base, package_id), - )?; - self.store.update_certificate(&key, &cert, dns_base).await?; - Ok((key, cert)) - } else { - Ok((key, cert)) - } - } - }?; - Ok(( - key, - vec![cert, self.int_cert.clone(), self.root_cert.clone()], - )) + Ok(key.with_certs(pair, self.int_cert.clone(), self.root_cert.clone())) } } +const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1; +lazy_static::lazy_static! { + static ref EC_GROUP: EcGroup = EcGroup::from_curve_name(EC_CURVE_NAME).unwrap(); + static ref SSL_MUTEX: Mutex<()> = Mutex::new(()); // TODO: make thread safe +} + pub async fn export_key(key: &PKey, target: &Path) -> Result<(), Error> { tokio::fs::write(target, key.private_key_to_pem_pkcs8()?) .map(|res| res.with_ctx(|_| (ErrorKind::Filesystem, target.display().to_string()))) .await?; Ok(()) } -pub async fn export_cert(chain: &Vec, target: &Path) -> Result<(), Error> { +pub async fn export_cert(chain: &[&X509], target: &Path) -> Result<(), Error> { tokio::fs::write( target, chain @@ -315,6 +141,7 @@ pub async fn export_cert(chain: &Vec, target: &Path) -> Result<(), Error> .await?; Ok(()) } + #[instrument] fn rand_serial() -> Result { let mut bn = BigNum::new()?; @@ -323,13 +150,14 @@ fn rand_serial() -> Result { Ok(asn1) } #[instrument] -fn generate_key() -> Result, Error> { +pub fn generate_key() -> Result, Error> { let new_key = EcKey::generate(EC_GROUP.as_ref())?; let key = PKey::from_ec_key(new_key)?; Ok(key) } + #[instrument] -fn make_root_cert(root_key: &PKey, server_id: &str) -> Result { +pub fn make_root_cert(root_key: &PKey, hostname: &Hostname) -> Result { let mut builder = X509Builder::new()?; builder.set_version(CERTIFICATE_VERSION)?; @@ -342,8 +170,7 @@ fn make_root_cert(root_key: &PKey, server_id: &str) -> Result, server_id: &str) -> Result, &X509), applicant: &PKey, ) -> Result { @@ -442,10 +269,52 @@ fn make_int_cert( Ok(cert) } +#[derive(Debug)] +pub struct SANInfo { + pub dns: BTreeSet, + pub ips: BTreeSet, +} +impl SANInfo { + pub fn new(key: &Key, hostname: &Hostname, ips: BTreeSet) -> Self { + let mut dns = BTreeSet::new(); + if let Some((id, _)) = key.interface() { + dns.insert(format!("{id}.embassy")); + dns.insert(key.local_address().to_string()); + } else { + dns.insert("embassy".to_owned()); + dns.insert(hostname.local_domain_name()); + dns.insert(hostname.no_dot_host_name()); + dns.insert("localhost".to_owned()); + } + dns.insert(key.tor_address().to_string()); + Self { dns, ips } + } +} +impl std::fmt::Display for SANInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut written = false; + for dns in &self.dns { + if written { + write!(f, ",")?; + } + written = true; + write!(f, "DNS:{dns},DNS:*.{dns}")?; + } + for ip in &self.ips { + if written { + write!(f, ",")?; + } + written = true; + write!(f, "IP:{ip}")?; + } + Ok(()) + } +} + #[instrument] -fn make_leaf_cert( +pub fn make_leaf_cert( signer: (&PKey, &X509), - applicant: (&PKey, &str, &PackageId), + applicant: (&PKey, &SANInfo), ) -> Result { let mut builder = X509Builder::new()?; builder.set_version(CERTIFICATE_VERSION)?; @@ -461,7 +330,15 @@ fn make_leaf_cert( builder.set_serial_number(&*rand_serial()?)?; let mut subject_name_builder = X509NameBuilder::new()?; - subject_name_builder.append_entry_by_text("CN", &format!("{}.local", &applicant.1))?; + subject_name_builder.append_entry_by_text( + "CN", + applicant + .1 + .dns + .first() + .map(String::as_str) + .unwrap_or("localhost"), + )?; subject_name_builder.append_entry_by_text("O", "Start9")?; subject_name_builder.append_entry_by_text("OU", "Embassy")?; let subject_name = subject_name_builder.build(); @@ -493,15 +370,9 @@ fn make_leaf_cert( "critical,digitalSignature,keyEncipherment", )?; - let subject_alt_name = X509Extension::new_nid( - Some(&cfg), - Some(&ctx), - Nid::SUBJECT_ALT_NAME, - &format!( - "DNS:{}.local,DNS:*.{}.local,DNS:{}.onion,DNS:*.{}.onion,DNS:{}.embassy,DNS:*.{}.embassy", - &applicant.1, &applicant.1, &applicant.1, &applicant.1, &applicant.2, &applicant.2, - ), - )?; + let san_string = applicant.1.to_string(); + let subject_alt_name = + X509Extension::new_nid(Some(&cfg), Some(&ctx), Nid::SUBJECT_ALT_NAME, &san_string)?; builder.append_extension(subject_key_identifier)?; builder.append_extension(authority_key_identifier)?; builder.append_extension(subject_alt_name)?; diff --git a/backend/src/net/static_server.rs b/backend/src/net/static_server.rs index 3db1d5dce0..e86c8a27a5 100644 --- a/backend/src/net/static_server.rs +++ b/backend/src/net/static_server.rs @@ -1,5 +1,5 @@ use std::fs::Metadata; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::sync::Arc; use std::time::UNIX_EPOCH; @@ -9,8 +9,11 @@ use color_eyre::eyre::eyre; use digest::Digest; use futures::FutureExt; use http::header::ACCEPT_ENCODING; +use http::header::CONTENT_ENCODING; use http::response::Builder; use hyper::{Body, Method, Request, Response, StatusCode}; +use openssl::hash::MessageDigest; +use openssl::x509::X509; use rpc_toolkit::rpc_handler; use tokio::fs::File; use tokio::io::BufReader; @@ -249,7 +252,12 @@ async fn alt_ui(req: Request, ui_mode: UiMode) -> Result, E let full_path = Path::new(selected_root_dir).join(uri_path); file_send( - if tokio::fs::metadata(&full_path).await.is_ok() { + if tokio::fs::metadata(&full_path) + .await + .ok() + .map(|f| f.is_file()) + .unwrap_or(false) + { full_path } else { Path::new(selected_root_dir).join("index.html") @@ -296,10 +304,7 @@ async fn main_embassy_ui(req: Request, ctx: RpcContext) -> Result { - file_send(crate::net::ssl::ROOT_CA_STATIC_PATH, &accept_encoding) - .await - } + Some("local.crt") => cert_send(&ctx.account.read().await.root_ca_cert), None => Ok(bad_request()), _ => Ok(not_found()), } @@ -312,13 +317,7 @@ async fn main_embassy_ui(req: Request, ctx: RpcContext) -> Result { match HasValidSession::from_request_parts(&request_parts, &ctx).await { - Ok(_) => { - file_send( - PathBuf::from(crate::net::ssl::ROOT_CA_STATIC_PATH), - &accept_encoding, - ) - .await - } + Ok(_) => cert_send(&ctx.account.read().await.root_ca_cert), Err(e) => un_authorized(e, "eos/local.crt"), } } @@ -331,7 +330,12 @@ async fn main_embassy_ui(req: Request, ctx: RpcContext) -> Result Response { .unwrap() } +fn cert_send(cert: &X509) -> Result, Error> { + let pem = cert.to_pem()?; + Response::builder() + .status(StatusCode::OK) + .header( + http::header::ETAG, + base32::encode( + base32::Alphabet::RFC4648 { padding: false }, + &*cert.digest(MessageDigest::sha256())?, + ) + .to_lowercase(), + ) + .header(http::header::CONTENT_TYPE, "application/x-pem-file") + .header(http::header::CONTENT_LENGTH, pem.len()) + .body(Body::from(pem)) + .with_kind(ErrorKind::Network) +} + async fn file_send( path: impl AsRef, accept_encoding: &[&str], @@ -407,12 +429,14 @@ async fn file_send( let mut builder = Response::builder().status(StatusCode::OK); builder = with_e_tag(path, &metadata, builder)?; builder = with_content_type(path, builder); - builder = with_content_length(&metadata, builder); - let body = if accept_encoding.contains(&"br") { + let body = if accept_encoding.contains(&"br") && metadata.len() > u16::MAX as u64 { + builder = builder.header(CONTENT_ENCODING, "br"); Body::wrap_stream(ReaderStream::new(BrotliEncoder::new(BufReader::new(file)))) - } else if accept_encoding.contains(&"gzip") { + } else if accept_encoding.contains(&"gzip") && metadata.len() > u16::MAX as u64 { + builder = builder.header(CONTENT_ENCODING, "gzip"); Body::wrap_stream(ReaderStream::new(GzipEncoder::new(BufReader::new(file)))) } else { + builder = with_content_length(&metadata, builder); Body::wrap_stream(ReaderStream::new(file)) }; builder.body(body).with_kind(ErrorKind::Network) @@ -446,7 +470,7 @@ fn with_e_tag(path: &Path, metadata: &Metadata, builder: Builder) -> Result Builder { }, None => "text/plain", }; - builder.header("Content-Type", content_type) + builder.header(http::header::CONTENT_TYPE, content_type) } fn with_content_length(metadata: &Metadata, builder: Builder) -> Builder { diff --git a/backend/src/net/tor.rs b/backend/src/net/tor.rs index 2f03dc9f00..526c14c6fa 100644 --- a/backend/src/net/tor.rs +++ b/backend/src/net/tor.rs @@ -1,27 +1,25 @@ use std::collections::BTreeMap; -use std::net::{Ipv4Addr, SocketAddr}; +use std::net::SocketAddr; +use std::sync::{Arc, Weak}; use clap::ArgMatches; use color_eyre::eyre::eyre; use futures::future::BoxFuture; use futures::FutureExt; use rpc_toolkit::command; -use sqlx::{Executor, Postgres}; use tokio::net::TcpStream; use tokio::sync::Mutex; use torut::control::{AsyncEvent, AuthenticatedConn, ConnError}; use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use tracing::instrument; -use super::interface::{InterfaceId, TorConfig}; use crate::context::RpcContext; -use crate::s9pk::manifest::PackageId; use crate::util::serde::{display_serializable, IoFormat}; use crate::{Error, ErrorKind, ResultExt as _}; #[test] fn random_key() { - println!("x'{}'", hex::encode(TorSecretKeyV3::generate().as_bytes())); + println!("x'{}'", hex::encode(rand::random::<[u8; 32]>())); } #[command(subcommands(list_services))] @@ -54,64 +52,29 @@ pub async fn list_services( ctx.net_controller.tor.list_services().await } -#[instrument(skip(secrets))] -pub async fn os_key(secrets: &mut Ex) -> Result -where - for<'a> &'a mut Ex: Executor<'a, Database = Postgres>, -{ - let key = sqlx::query!("SELECT tor_key FROM account") - .fetch_one(secrets) - .await? - .tor_key; - - let mut buf = [0; 64]; - buf.clone_from_slice( - key.get(0..64).ok_or_else(|| { - Error::new(eyre!("Invalid Tor Key Length"), crate::ErrorKind::Database) - })?, - ); - Ok(buf.into()) -} - fn event_handler(_event: AsyncEvent<'static>) -> BoxFuture<'static, Result<(), ConnError>> { async move { Ok(()) }.boxed() } pub struct TorController(Mutex); impl TorController { - pub async fn init( - embassyd_addr: SocketAddr, - embassyd_tor_key: TorSecretKeyV3, - tor_control: SocketAddr, - ) -> Result { + pub async fn init(tor_control: SocketAddr) -> Result { Ok(TorController(Mutex::new( - TorControllerInner::init(embassyd_addr, embassyd_tor_key, tor_control).await?, + TorControllerInner::init(tor_control).await?, ))) } - pub async fn add + Clone>( - &self, - pkg_id: &PackageId, - ip: Ipv4Addr, - interfaces: I, - ) -> Result<(), Error> { - self.0.lock().await.add(pkg_id, ip, interfaces).await - } - - pub async fn remove + Clone>( + pub async fn add( &self, - pkg_id: &PackageId, - interfaces: I, - ) -> Result<(), Error> { - self.0.lock().await.remove(pkg_id, interfaces).await + key: &TorSecretKeyV3, + external: u16, + target: SocketAddr, + ) -> Result, Error> { + self.0.lock().await.add(key, external, target).await } - pub async fn embassyd_tor_key(&self) -> TorSecretKeyV3 { - self.0.lock().await.embassyd_tor_key.clone() - } - - pub async fn embassyd_onion(&self) -> OnionAddressV3 { - self.0.lock().await.embassyd_onion() + pub async fn gc(&self, key: &TorSecretKeyV3, external: u16) -> Result<(), Error> { + self.0.lock().await.gc(key, external).await } pub async fn list_services(&self) -> Result, Error> { @@ -124,92 +87,95 @@ type AuthenticatedConnection = AuthenticatedConn< fn(AsyncEvent<'static>) -> BoxFuture<'static, Result<(), ConnError>>, >; -#[derive(Clone, Debug, PartialEq, Eq)] -struct HiddenServiceConfig { - ip: Ipv4Addr, - cfg: TorConfig, -} - pub struct TorControllerInner { - embassyd_addr: SocketAddr, - embassyd_tor_key: TorSecretKeyV3, control_addr: SocketAddr, - connection: Option, - services: BTreeMap<(PackageId, InterfaceId), (TorSecretKeyV3, TorConfig, Ipv4Addr)>, + connection: AuthenticatedConnection, + services: BTreeMap>>>, } impl TorControllerInner { - #[instrument(skip(self, interfaces))] - async fn add<'a, I: IntoIterator>( + #[instrument(skip(self))] + async fn add( &mut self, - pkg_id: &PackageId, - ip: Ipv4Addr, - interfaces: I, - ) -> Result<(), Error> { - for (interface_id, tor_cfg, key) in interfaces { - let id = (pkg_id.clone(), interface_id); - match self.services.get(&id) { - Some(k) if k.0 != key => { - self.remove(pkg_id, std::iter::once(id.1.clone())).await?; - } - Some(_) => continue, - None => (), - } - self.connection - .as_mut() - .ok_or_else(|| { - Error::new(eyre!("Missing Tor Control Connection"), ErrorKind::Unknown) - })? - .add_onion_v3( - &key, - false, - false, - false, - None, - &mut tor_cfg - .port_mapping - .iter() - .map(|(external, internal)| { - (external.0, SocketAddr::from((ip, internal.0))) - }) - .collect::>() - .iter(), - ) - .await?; - self.services.insert(id, (key, tor_cfg, ip)); - } - Ok(()) + key: &TorSecretKeyV3, + external: u16, + target: SocketAddr, + ) -> Result, Error> { + let mut rm_res = Ok(()); + let onion_base = key + .public() + .get_onion_address() + .get_address_without_dot_onion(); + let mut service = if let Some(service) = self.services.remove(&onion_base) { + rm_res = self.connection.del_onion(&onion_base).await; + service + } else { + BTreeMap::new() + }; + let mut binding = service.remove(&external).unwrap_or_default(); + let rc = if let Some(rc) = Weak::upgrade(&binding.remove(&target).unwrap_or_default()) { + rc + } else { + Arc::new(()) + }; + binding.insert(target, Arc::downgrade(&rc)); + service.insert(external, binding); + let bindings = service + .iter() + .flat_map(|(ext, int)| { + int.iter() + .find(|(_, rc)| rc.strong_count() > 0) + .map(|(addr, _)| (*ext, SocketAddr::from(*addr))) + }) + .collect::>(); + self.services.insert(onion_base, service); + rm_res?; + self.connection + .add_onion_v3(key, false, false, false, None, &mut bindings.iter()) + .await?; + Ok(rc) } - #[instrument(skip(self, interfaces))] - async fn remove>( - &mut self, - pkg_id: &PackageId, - interfaces: I, - ) -> Result<(), Error> { - for interface_id in interfaces { - if let Some((key, _cfg, _ip)) = self.services.remove(&(pkg_id.clone(), interface_id)) { + #[instrument(skip(self))] + async fn gc(&mut self, key: &TorSecretKeyV3, external: u16) -> Result<(), Error> { + let onion_base = key + .public() + .get_onion_address() + .get_address_without_dot_onion(); + if let Some(mut service) = self.services.remove(&onion_base) { + if let Some(mut binding) = service.remove(&external) { + binding = binding + .into_iter() + .filter(|(_, rc)| rc.strong_count() > 0) + .collect(); + if !binding.is_empty() { + service.insert(external, binding); + } + } + let rm_res = self.connection.del_onion(&onion_base).await; + if !service.is_empty() { + let bindings = service + .iter() + .flat_map(|(ext, int)| { + int.iter() + .find(|(_, rc)| rc.strong_count() > 0) + .map(|(addr, _)| (*ext, SocketAddr::from(*addr))) + }) + .collect::>(); + self.services.insert(onion_base, service); + rm_res?; self.connection - .as_mut() - .ok_or_else(|| { - Error::new(eyre!("Missing Tor Control Connection"), ErrorKind::Tor) - })? - .del_onion( - &key.public() - .get_onion_address() - .get_address_without_dot_onion(), - ) + .add_onion_v3(&key, false, false, false, None, &mut bindings.iter()) .await?; + } else { + rm_res?; } } + Ok(()) } #[instrument] - async fn init( - embassyd_addr: SocketAddr, - embassyd_tor_key: TorSecretKeyV3, - tor_control: SocketAddr, - ) -> Result { + async fn init(tor_control: SocketAddr) -> Result { let mut conn = torut::control::UnauthenticatedConn::new( TcpStream::connect(tor_control).await?, // TODO ); @@ -223,51 +189,16 @@ impl TorControllerInner { let mut connection: AuthenticatedConnection = conn.into_authenticated().await; connection.set_async_event_handler(Some(event_handler)); - let mut controller = TorControllerInner { - embassyd_addr, - embassyd_tor_key, + Ok(Self { control_addr: tor_control, - connection: Some(connection), + connection, services: BTreeMap::new(), - }; - controller.add_embassyd_onion().await?; - Ok(controller) - } - - #[instrument(skip(self))] - async fn add_embassyd_onion(&mut self) -> Result<(), Error> { - tracing::info!( - "Registering Main Tor Service: {}", - self.embassyd_tor_key.public().get_onion_address() - ); - self.connection - .as_mut() - .ok_or_else(|| Error::new(eyre!("Missing Tor Control Connection"), ErrorKind::Tor))? - .add_onion_v3( - &self.embassyd_tor_key, - false, - false, - false, - None, - &mut std::iter::once(&(self.embassyd_addr.port(), self.embassyd_addr)), - ) - .await?; - tracing::info!( - "Registered Main Tor Service: {}", - self.embassyd_tor_key.public().get_onion_address() - ); - Ok(()) - } - - fn embassyd_onion(&self) -> OnionAddressV3 { - self.embassyd_tor_key.public().get_onion_address() + }) } #[instrument(skip(self))] async fn list_services(&mut self) -> Result, Error> { self.connection - .as_mut() - .ok_or_else(|| Error::new(eyre!("Missing Tor Control Connection"), ErrorKind::Tor))? .get_info("onions/current") .await? .lines() @@ -312,6 +243,15 @@ async fn test() { ) .await .unwrap(); + connection + .del_onion( + &tor_key + .public() + .get_onion_address() + .get_address_without_dot_onion(), + ) + .await + .unwrap(); connection .add_onion_v3( &tor_key, diff --git a/backend/src/net/vhost_controller.rs b/backend/src/net/vhost_controller.rs index 56fdc61d2c..c8b35b307e 100644 --- a/backend/src/net/vhost_controller.rs +++ b/backend/src/net/vhost_controller.rs @@ -1,81 +1,322 @@ use std::collections::BTreeMap; -use std::net::SocketAddr; -use std::sync::Arc; +use std::convert::Infallible; +use std::net::{IpAddr, SocketAddr}; +use std::sync::{Arc, Weak}; -use tokio_rustls::rustls::ServerConfig; +use color_eyre::eyre::eyre; +use helpers::NonDetachingJoinHandle; +use http::Response; +use hyper::service::{make_service_fn, service_fn}; +use hyper::Body; +use models::ResultExt; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::{Mutex, RwLock}; +use tokio_rustls::rustls::server::Acceptor; +use tokio_rustls::rustls::{RootCertStore, ServerConfig}; +use tokio_rustls::{LazyConfigAcceptor, TlsConnector}; -use crate::net::cert_resolver::EmbassyCertResolver; -use crate::net::embassy_service_http_server::EmbassyServiceHTTPServer; -use crate::net::net_utils::ResourceFqdn; -use crate::net::HttpHandler; +use crate::net::keys::Key; +use crate::net::net_utils::SingleAccept; +use crate::net::ssl::SslManager; +use crate::util::io::BackTrackingReader; use crate::Error; -pub struct VHOSTController { - pub service_servers: BTreeMap, - pub cert_resolver: EmbassyCertResolver, - embassyd_addr: SocketAddr, -} +// not allowed: <=1024, >=32768, 5355, 5432, 9050, 6010, 9051, 5353 -impl VHOSTController { - pub fn init(embassyd_addr: SocketAddr) -> Self { +pub struct VHostController { + ssl: Arc, + servers: Mutex>, +} +impl VHostController { + pub fn new(ssl: Arc) -> Self { Self { - embassyd_addr, - service_servers: BTreeMap::new(), - cert_resolver: EmbassyCertResolver::new(), + ssl, + servers: Mutex::new(BTreeMap::new()), + } + } + pub async fn add( + &self, + key: Key, + hostname: Option, + external: u16, + target: SocketAddr, + connect_ssl: bool, + ) -> Result, Error> { + let mut writable = self.servers.lock().await; + let server = if let Some(server) = writable.remove(&external) { + server + } else { + VHostServer::new(external, self.ssl.clone()).await? + }; + let rc = server + .add( + hostname, + TargetInfo { + addr: target, + connect_ssl, + key, + }, + ) + .await; + writable.insert(external, server); + Ok(rc?) + } + pub async fn gc(&self, hostname: Option, external: u16) -> Result<(), Error> { + let mut writable = self.servers.lock().await; + if let Some(server) = writable.remove(&external) { + server.gc(hostname).await?; + if !server.is_empty().await? { + writable.insert(external, server); + } } + Ok(()) } +} - pub fn build_ssl_svr_cfg(&self) -> Result, Error> { - let ssl_cfg = ServerConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_safe_default_protocol_versions() - .unwrap() - .with_no_client_auth() - .with_cert_resolver(Arc::new(self.cert_resolver.clone())); +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +struct TargetInfo { + addr: SocketAddr, + connect_ssl: bool, + key: Key, +} - Ok(Arc::new(ssl_cfg)) +struct VHostServer { + mapping: Weak, BTreeMap>>>>, + _thread: NonDetachingJoinHandle<()>, +} +impl VHostServer { + async fn new(port: u16, ssl: Arc) -> Result { + // check if port allowed + let listener = TcpListener::bind(SocketAddr::new([0, 0, 0, 0].into(), port)) + .await + .with_kind(crate::ErrorKind::Network)?; + let mapping = Arc::new(RwLock::new(BTreeMap::new())); + Ok(Self { + mapping: Arc::downgrade(&mapping), + _thread: tokio::spawn(async move { + loop { + match listener.accept().await { + Ok((stream, _)) => { + let mut stream = BackTrackingReader::new(stream); + stream.start_buffering(); + let mapping = mapping.clone(); + let ssl = ssl.clone(); + tokio::spawn(async move { + if let Err(e) = async { + let mid = match LazyConfigAcceptor::new( + Acceptor::default(), + &mut stream, + ) + .await + { + Ok(a) => a, + Err(e) => { + stream.rewind(); + return hyper::server::Server::builder( + SingleAccept::new(stream), + ) + .serve(make_service_fn(|_| async { + Ok::<_, Infallible>(service_fn(|req| async move { + Response::builder() + .status( + http::StatusCode::TEMPORARY_REDIRECT, + ) + .header( + http::header::LOCATION, + req.headers() + .get(http::header::HOST) + .and_then(|host| host.to_str().ok()) + .map(|host| { + format!("https://{host}") + }) + .unwrap_or_default(), + ) + .body(Body::default()) + })) + })) + .await + .with_kind(crate::ErrorKind::Network); + } + }; + let target_name = + mid.client_hello().server_name().map(|s| s.to_owned()); + let target = { + let mapping = mapping.read().await; + mapping + .get(&target_name) + .into_iter() + .flatten() + .find(|(_, rc)| rc.strong_count() > 0) + .or_else(|| { + if target_name + .map(|s| s.parse::().is_ok()) + .unwrap_or(true) + { + mapping + .get(&None) + .into_iter() + .flatten() + .find(|(_, rc)| rc.strong_count() > 0) + } else { + None + } + }) + .map(|(target, _)| target.clone()) + }; + if let Some(target) = target { + let mut tcp_stream = + TcpStream::connect(target.addr).await?; + let key = + ssl.with_certs(target.key, target.addr.ip()).await?; + let cfg = ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth(); + let cfg = + if mid.client_hello().signature_schemes().contains( + &tokio_rustls::rustls::SignatureScheme::ED25519, + ) { + cfg.with_single_cert( + key.fullchain_ed25519() + .into_iter() + .map(|c| { + Ok(tokio_rustls::rustls::Certificate( + c.to_der()?, + )) + }) + .collect::>()?, + tokio_rustls::rustls::PrivateKey( + key.key() + .openssl_key_ed25519() + .private_key_to_der()?, + ), + ) + } else { + cfg.with_single_cert( + key.fullchain_nistp256() + .into_iter() + .map(|c| { + Ok(tokio_rustls::rustls::Certificate( + c.to_der()?, + )) + }) + .collect::>()?, + tokio_rustls::rustls::PrivateKey( + key.key() + .openssl_key_nistp256() + .private_key_to_der()?, + ), + ) + }; + let mut tls_stream = mid + .into_stream(Arc::new( + cfg.with_kind(crate::ErrorKind::OpenSsl)?, + )) + .await?; + tls_stream.get_mut().0.stop_buffering(); + if target.connect_ssl { + tokio::io::copy_bidirectional( + &mut tls_stream, + &mut TlsConnector::from(Arc::new( + tokio_rustls::rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates({ + let mut store = RootCertStore::empty(); + store.add( + &tokio_rustls::rustls::Certificate( + key.root_ca().to_der()?, + ), + ).with_kind(crate::ErrorKind::OpenSsl)?; + store + }) + .with_no_client_auth(), + )) + .connect( + key.key() + .internal_address() + .as_str() + .try_into() + .with_kind(crate::ErrorKind::OpenSsl)?, + tcp_stream, + ) + .await + .with_kind(crate::ErrorKind::OpenSsl)?, + ) + .await?; + } else { + tokio::io::copy_bidirectional( + &mut tls_stream, + &mut tcp_stream, + ) + .await?; + } + } else { + // 503 + } + Ok::<_, Error>(()) + } + .await + { + tracing::error!("Error in VHostController on port {port}: {e}"); + tracing::debug!("{e:?}") + } + }); + } + Err(e) => { + tracing::error!("Error in VHostController on port {port}: {e}"); + tracing::debug!("{e:?}"); + } + } + } + }) + .into(), + }) } - - pub async fn add_server_or_handle( - &mut self, - external_svc_port: u16, - fqdn: ResourceFqdn, - svc_handler: HttpHandler, - is_ssl: bool, - ) -> Result<(), Error> { - if let Some(server) = self.service_servers.get_mut(&external_svc_port) { - server.add_svc_handler_mapping(fqdn, svc_handler).await?; + async fn add(&self, hostname: Option, target: TargetInfo) -> Result, Error> { + if let Some(mapping) = Weak::upgrade(&self.mapping) { + let mut writable = mapping.write().await; + let mut targets = writable.remove(&hostname).unwrap_or_default(); + let rc = if let Some(rc) = Weak::upgrade(&targets.remove(&target).unwrap_or_default()) { + rc + } else { + Arc::new(()) + }; + targets.insert(target, Arc::downgrade(&rc)); + writable.insert(hostname, targets); + Ok(rc) } else { - self.add_server(is_ssl, external_svc_port, fqdn, svc_handler) - .await?; + Err(Error::new( + eyre!("VHost Service Thread has exited"), + crate::ErrorKind::Network, + )) } - - Ok(()) } - - async fn add_server( - &mut self, - is_ssl: bool, - external_svc_port: u16, - fqdn: ResourceFqdn, - svc_handler: HttpHandler, - ) -> Result<(), Error> { - let ssl_cfg = if is_ssl { - Some(self.build_ssl_svr_cfg()?) + async fn gc(&self, hostname: Option) -> Result<(), Error> { + if let Some(mapping) = Weak::upgrade(&self.mapping) { + let mut writable = mapping.write().await; + let mut targets = writable.remove(&hostname).unwrap_or_default(); + targets = targets + .into_iter() + .filter(|(_, rc)| rc.strong_count() > 0) + .collect(); + if !targets.is_empty() { + writable.insert(hostname, targets); + } + Ok(()) } else { - None - }; - - let mut new_service_server = - EmbassyServiceHTTPServer::new(self.embassyd_addr.ip(), external_svc_port, ssl_cfg) - .await?; - new_service_server - .add_svc_handler_mapping(fqdn.clone(), svc_handler) - .await?; - self.service_servers - .insert(external_svc_port, new_service_server); - - Ok(()) + Err(Error::new( + eyre!("VHost Service Thread has exited"), + crate::ErrorKind::Network, + )) + } + } + async fn is_empty(&self) -> Result { + if let Some(mapping) = Weak::upgrade(&self.mapping) { + Ok(mapping.read().await.is_empty()) + } else { + Err(Error::new( + eyre!("VHost Service Thread has exited"), + crate::ErrorKind::Network, + )) + } } } diff --git a/backend/src/net/web_server.rs b/backend/src/net/web_server.rs new file mode 100644 index 0000000000..c2e25a4134 --- /dev/null +++ b/backend/src/net/web_server.rs @@ -0,0 +1,61 @@ +use std::convert::Infallible; +use std::net::SocketAddr; + +use futures::future::ready; +use futures::FutureExt; +use helpers::NonDetachingJoinHandle; +use hyper::service::{make_service_fn, service_fn}; +use hyper::Server; +use tokio::sync::oneshot; + +use crate::context::{DiagnosticContext, InstallContext, RpcContext, SetupContext}; +use crate::net::static_server::{ + diag_ui_file_router, install_ui_file_router, main_ui_server_router, setup_ui_file_router, +}; +use crate::net::HttpHandler; +use crate::Error; + +pub struct WebServer { + shutdown: oneshot::Sender<()>, + thread: NonDetachingJoinHandle<()>, +} +impl WebServer { + pub fn new(bind: SocketAddr, router: HttpHandler) -> Self { + let (shutdown, shutdown_recv) = oneshot::channel(); + let thread = NonDetachingJoinHandle::from(tokio::spawn(async move { + let server = Server::bind(&bind) + .http1_preserve_header_case(true) + .http1_title_case_headers(true) + .serve(make_service_fn(move |_| { + let router = router.clone(); + ready(Ok::<_, Infallible>(service_fn(move |req| router(req)))) + })) + .with_graceful_shutdown(shutdown_recv.map(|_| ())); + if let Err(e) = server.await { + tracing::error!("Spawning hyper server error: {}", e); + } + })); + Self { shutdown, thread } + } + + pub async fn shutdown(self) { + self.shutdown.send(()).unwrap_or_default(); + self.thread.await.unwrap() + } + + pub async fn main(bind: SocketAddr, ctx: RpcContext) -> Result { + Ok(Self::new(bind, main_ui_server_router(ctx).await?)) + } + + pub async fn setup(bind: SocketAddr, ctx: SetupContext) -> Result { + Ok(Self::new(bind, setup_ui_file_router(ctx).await?)) + } + + pub async fn diagnostic(bind: SocketAddr, ctx: DiagnosticContext) -> Result { + Ok(Self::new(bind, diag_ui_file_router(ctx).await?)) + } + + pub async fn install(bind: SocketAddr, ctx: InstallContext) -> Result { + Ok(Self::new(bind, install_ui_file_router(ctx).await?)) + } +} diff --git a/backend/src/notifications.rs b/backend/src/notifications.rs index 69cb20340e..7f7a08149f 100644 --- a/backend/src/notifications.rs +++ b/backend/src/notifications.rs @@ -254,7 +254,7 @@ impl NotificationManager { .unread_notification_count() .get_mut(db) .await?; - let sql_package_id = package_id.map::(|p| p.into()); + let sql_package_id = package_id.as_ref().map(|p| &**p); let sql_code = T::CODE; let sql_level = format!("{}", level); let sql_data = diff --git a/backend/src/procedure/docker.rs b/backend/src/procedure/docker.rs index 01fc61518a..345b6bc2f0 100644 --- a/backend/src/procedure/docker.rs +++ b/backend/src/procedure/docker.rs @@ -12,6 +12,7 @@ use color_eyre::Report; use futures::future::Either as EitherFuture; use futures::TryStreamExt; use helpers::{NonDetachingJoinHandle, UnixRpcClient}; +use models::{Id, ImageId}; use nix::sys::signal; use nix::unistd::Pid; use serde::de::DeserializeOwned; @@ -25,7 +26,6 @@ use tracing::instrument; use super::ProcedureName; use crate::context::RpcContext; -use crate::id::{Id, ImageId}; use crate::s9pk::manifest::{PackageId, SYSTEM_PACKAGE_ID}; use crate::util::serde::{Duration as SerdeDuration, IoFormat}; use crate::util::Version; @@ -668,7 +668,7 @@ impl DockerProcedure { } } - pub fn uncontainer_name(name: &str) -> Option<(PackageId<&str>, Option<&str>)> { + pub fn uncontainer_name(name: &str) -> Option<(PackageId, Option<&str>)> { let (pre_tld, _) = name.split_once('.')?; if pre_tld.contains('_') { let (pkg, name) = name.split_once('_')?; @@ -716,7 +716,7 @@ impl DockerProcedure { res.push(OsStr::new("--entrypoint").into()); res.push(OsStr::new(&self.entrypoint).into()); if self.system { - res.push(OsString::from(self.image.for_package(SYSTEM_PACKAGE_ID, None)).into()); + res.push(OsString::from(self.image.for_package(&*SYSTEM_PACKAGE_ID, None)).into()); } else { res.push(OsString::from(self.image.for_package(pkg_id, Some(pkg_version))).into()); } @@ -804,7 +804,7 @@ impl LongRunning { .arg("'{{.Architecture}}'"); if docker.system { - cmd.arg(docker.image.for_package(SYSTEM_PACKAGE_ID, None)); + cmd.arg(docker.image.for_package(&*SYSTEM_PACKAGE_ID, None)); } else { cmd.arg(docker.image.for_package(pkg_id, Some(pkg_version))); } @@ -856,7 +856,7 @@ impl LongRunning { } cmd.arg("--log-driver=journald"); if docker.system { - cmd.arg(docker.image.for_package(SYSTEM_PACKAGE_ID, None)); + cmd.arg(docker.image.for_package(&*SYSTEM_PACKAGE_ID, None)); } else { cmd.arg(docker.image.for_package(pkg_id, Some(pkg_version))); } diff --git a/backend/src/procedure/mod.rs b/backend/src/procedure/mod.rs index 2924c0c54c..e2ab47aeb7 100644 --- a/backend/src/procedure/mod.rs +++ b/backend/src/procedure/mod.rs @@ -2,6 +2,7 @@ use std::collections::BTreeSet; use std::time::Duration; use color_eyre::eyre::eyre; +use models::ImageId; use patch_db::HasModel; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -9,7 +10,6 @@ use tracing::instrument; use self::docker::{DockerContainers, DockerProcedure}; use crate::context::RpcContext; -use crate::id::ImageId; use crate::s9pk::manifest::PackageId; use crate::util::Version; use crate::volume::Volumes; diff --git a/backend/src/s9pk/reader.rs b/backend/src/s9pk/reader.rs index 3d707bd939..f3912ec8fd 100644 --- a/backend/src/s9pk/reader.rs +++ b/backend/src/s9pk/reader.rs @@ -10,6 +10,7 @@ use color_eyre::eyre::eyre; use digest_old::Output; use ed25519_dalek::PublicKey; use futures::TryStreamExt; +use models::ImageId; use sha2_old::{Digest, Sha512}; use tokio::fs::File; use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, ReadBuf}; @@ -18,7 +19,6 @@ use tracing::instrument; use super::header::{FileSection, Header, TableOfContents}; use super::manifest::{Manifest, PackageId}; use super::SIG_CONTEXT; -use crate::id::ImageId; use crate::install::progress::InstallProgressTracker; use crate::s9pk::docker::DockerReader; use crate::util::Version; diff --git a/backend/src/setup.rs b/backend/src/setup.rs index 558fb901e2..238d0dafa0 100644 --- a/backend/src/setup.rs +++ b/backend/src/setup.rs @@ -7,17 +7,16 @@ use helpers::{Rsync, RsyncOptions}; use josekit::jwk::Jwk; use openssl::x509::X509; use patch_db::DbHandle; -use rand::random; use rpc_toolkit::command; use rpc_toolkit::yajrc::RpcError; use serde::{Deserialize, Serialize}; -use sqlx::{Connection, Executor, Postgres}; -use ssh_key::private::Ed25519PrivateKey; +use sqlx::Connection; use tokio::fs::File; use tokio::io::AsyncWriteExt; -use torut::onion::{OnionAddressV3, TorSecretKeyV3}; +use torut::onion::OnionAddressV3; use tracing::instrument; +use crate::account::AccountInfo; use crate::backup::restore::recover_full_embassy; use crate::backup::target::BackupTargetFS; use crate::context::rpc::RpcContextConfig; @@ -30,25 +29,11 @@ use crate::disk::mount::filesystem::ReadWrite; use crate::disk::mount::guard::TmpMountGuard; use crate::disk::util::{pvscan, recovery_info, DiskInfo, EmbassyOsRecoveryInfo}; use crate::disk::REPAIR_DISK_PATH; -use crate::hostname::{get_hostname, HostNameReceipt, Hostname}; +use crate::hostname::Hostname; use crate::init::{init, InitResult}; use crate::middleware::encrypt::EncryptedWire; -use crate::net::ssl::SslManager; use crate::{Error, ErrorKind, ResultExt}; -#[instrument(skip(secrets))] -pub async fn password_hash(secrets: &mut Ex) -> Result -where - for<'a> &'a mut Ex: Executor<'a, Database = Postgres>, -{ - let password = sqlx::query!("SELECT password FROM account") - .fetch_one(secrets) - .await? - .password; - - Ok(password) -} - #[command(subcommands(status, disk, attach, execute, cifs, complete, get_pubkey, exit))] pub fn setup() -> Result<(), Error> { Ok(()) @@ -75,30 +60,26 @@ async fn setup_init( let mut secrets_tx = secrets_handle.begin().await?; let mut db_tx = db_handle.begin().await?; + let mut account = AccountInfo::load(&mut secrets_tx).await?; + if let Some(password) = password { - let set_password_receipt = crate::auth::SetPasswordReceipt::new(&mut db_tx).await?; - crate::auth::set_password( - &mut db_tx, - &set_password_receipt, - &mut secrets_tx, - &password, - ) - .await?; + account.set_password(&password)?; + account.save(&mut secrets_tx).await?; + crate::db::DatabaseModel::new() + .server_info() + .password_hash() + .put(&mut db_tx, &account.password) + .await?; } - let tor_key = crate::net::tor::os_key(&mut secrets_tx).await?; - db_tx.commit().await?; secrets_tx.commit().await?; - let hostname_receipts = HostNameReceipt::new(&mut db_handle).await?; - let hostname = get_hostname(&mut db_handle, &hostname_receipts).await?; - - let (_, root_ca) = SslManager::init(secret_store, &mut db_handle) - .await? - .export_root_ca() - .await?; - Ok((hostname, tor_key.public().get_onion_address(), root_ca)) + Ok(( + account.hostname, + account.key.tor_address(), + account.root_ca_cert, + )) } #[command(rpc_only)] @@ -385,38 +366,18 @@ async fn fresh_setup( ctx: &SetupContext, embassy_password: &str, ) -> Result<(Hostname, OnionAddressV3, X509), Error> { - let password = argon2::hash_encoded( - embassy_password.as_bytes(), - &rand::random::<[u8; 16]>()[..], - &argon2::Config::default(), - ) - .with_kind(crate::ErrorKind::PasswordHashGeneration)?; - let tor_key = TorSecretKeyV3::generate(); - let tor_key_bytes = tor_key.as_bytes().to_vec(); - let ssh_key = Ed25519PrivateKey::from_bytes(&random()); - let ssh_key_bytes = ssh_key.to_bytes().to_vec(); + let account = AccountInfo::new(embassy_password)?; let sqlite_pool = ctx.secret_store().await?; - sqlx::query!( - "INSERT INTO account (id, password, tor_key, ssh_key) VALUES ($1, $2, $3, $4) ON CONFLICT (id) DO UPDATE SET password = $2, tor_key = $3, ssh_key = $4", - 0, - password, - tor_key_bytes, - ssh_key_bytes, - ) - .execute(&mut sqlite_pool.acquire().await?) - .await?; + account.save(&sqlite_pool).await?; sqlite_pool.close().await; - let InitResult { secret_store, db } = + let InitResult { secret_store, .. } = init(&RpcContextConfig::load(ctx.config_path.clone()).await?).await?; - let mut handle = db.handle(); - let receipts = crate::hostname::HostNameReceipt::new(&mut handle).await?; - let hostname = get_hostname(&mut handle, &receipts).await?; - let (_, root_ca) = SslManager::init(secret_store.clone(), &mut handle) - .await? - .export_root_ca() - .await?; secret_store.close().await; - Ok((hostname, tor_key.public().get_onion_address(), root_ca)) + Ok(( + account.hostname.clone(), + account.key.tor_address(), + account.root_ca_cert.clone(), + )) } #[instrument(skip(ctx, embassy_password, recovery_password))] diff --git a/backend/src/ssh.rs b/backend/src/ssh.rs index 21211fd336..ab907e3b35 100644 --- a/backend/src/ssh.rs +++ b/backend/src/ssh.rs @@ -15,25 +15,6 @@ use crate::{Error, ErrorKind}; static SSH_AUTHORIZED_KEYS_FILE: &str = "/home/start9/.ssh/authorized_keys"; -#[instrument(skip(secrets))] -pub async fn os_key(secrets: &mut Ex) -> Result -where - for<'a> &'a mut Ex: Executor<'a, Database = Postgres>, -{ - let key = sqlx::query!("SELECT ssh_key FROM account") - .fetch_one(secrets) - .await? - .ssh_key; - - let mut buf = [0; 32]; - buf.clone_from_slice( - key.get(0..64).ok_or_else(|| { - Error::new(eyre!("Invalid Ssh Key Length"), crate::ErrorKind::Database) - })?, - ); - Ok(Ed25519PrivateKey::from_bytes(&buf)) -} - #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct PubKey( #[serde(serialize_with = "crate::util::serde::serialize_display")] diff --git a/backend/src/status/health_check.rs b/backend/src/status/health_check.rs index d94debf9f0..7710c00b3d 100644 --- a/backend/src/status/health_check.rs +++ b/backend/src/status/health_check.rs @@ -2,11 +2,11 @@ use std::collections::{BTreeMap, BTreeSet}; use chrono::{DateTime, Utc}; pub use models::HealthCheckId; +use models::ImageId; use serde::{Deserialize, Serialize}; use tracing::instrument; use crate::context::RpcContext; -use crate::id::ImageId; use crate::procedure::docker::DockerContainers; use crate::procedure::{NoOutput, PackageProcedure, ProcedureName}; use crate::s9pk::manifest::PackageId; diff --git a/backend/src/util/io.rs b/backend/src/util/io.rs index af3b5b7da1..827c6ce7e1 100644 --- a/backend/src/util/io.rs +++ b/backend/src/util/io.rs @@ -1,4 +1,5 @@ use std::future::Future; +use std::io::Cursor; use std::path::Path; use std::task::Poll; @@ -295,3 +296,111 @@ impl AsyncRead for BufferedWriteReader { } } } + +pub trait CursorExt { + fn pure_read(&mut self, buf: &mut ReadBuf<'_>); +} + +impl> CursorExt for Cursor { + fn pure_read(&mut self, buf: &mut ReadBuf<'_>) { + let end = self.position() as usize + + std::cmp::max( + buf.remaining(), + self.get_ref().as_ref().len() - self.position() as usize, + ); + buf.put_slice(&self.get_ref().as_ref()[self.position() as usize..end]); + self.set_position(end as u64); + } +} + +#[pin_project::pin_project] +pub struct BackTrackingReader { + #[pin] + reader: T, + buffer: Cursor>, + buffering: bool, +} +impl BackTrackingReader { + pub fn new(reader: T) -> Self { + Self { + reader, + buffer: Cursor::new(Vec::new()), + buffering: false, + } + } + pub fn start_buffering(&mut self) { + self.buffer.set_position(0); + self.buffer.get_mut().truncate(0); + self.buffering = true; + } + pub fn stop_buffering(&mut self) { + self.buffer.set_position(0); + self.buffer.get_mut().truncate(0); + self.buffering = false; + } + pub fn rewind(&mut self) { + self.buffering = false; + } + pub fn unwrap(self) -> T { + self.reader + } +} + +impl AsyncRead for BackTrackingReader { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let this = self.project(); + if *this.buffering { + let filled = buf.filled().len(); + let res = this.reader.poll_read(cx, buf); + this.buffer + .get_mut() + .extend_from_slice(&buf.filled()[filled..]); + res + } else { + if (this.buffer.position() as usize) < this.buffer.get_ref().len() { + this.buffer.pure_read(buf); + } + if buf.remaining() > 0 { + this.reader.poll_read(cx, buf) + } else { + Poll::Ready(Ok(())) + } + } + } +} + +impl AsyncWrite for BackTrackingReader { + fn is_write_vectored(&self) -> bool { + self.reader.is_write_vectored() + } + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + self.project().reader.poll_flush(cx) + } + fn poll_shutdown( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + self.project().reader.poll_shutdown(cx) + } + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> Poll> { + self.project().reader.poll_write(cx, buf) + } + fn poll_write_vectored( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + bufs: &[std::io::IoSlice<'_>], + ) -> Poll> { + self.project().reader.poll_write_vectored(cx, bufs) + } +} diff --git a/backend/src/util/mod.rs b/backend/src/util/mod.rs index ebfb14192c..5ddd8b545f 100644 --- a/backend/src/util/mod.rs +++ b/backend/src/util/mod.rs @@ -330,3 +330,7 @@ impl FileLock { Ok(()) } } + +pub fn assure_send(x: T) -> T { + x +} diff --git a/backend/src/util/serde.rs b/backend/src/util/serde.rs index 2a6e9bdd69..75d8cc172c 100644 --- a/backend/src/util/serde.rs +++ b/backend/src/util/serde.rs @@ -739,3 +739,57 @@ impl<'de, K: Deserialize<'de>, V: Deserialize<'de>> Deserialize<'de> for KeyVal< deserializer.deserialize_map(Visitor(PhantomData)) } } + +pub struct Base32(pub T); +impl<'de, T: TryFrom>> Deserialize<'de> for Base32 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + base32::decode(base32::Alphabet::RFC4648 { padding: true }, &s) + .ok_or_else(|| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"a valid base32 string", + ) + })? + .try_into() + .map_err(|_| serde::de::Error::custom("invalid length")) + .map(Self) + } +} +impl> Serialize for Base32 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&base32::encode( + base32::Alphabet::RFC4648 { padding: true }, + self.0.as_ref(), + )) + } +} + +pub struct Base64(pub T); +impl<'de, T: TryFrom>> Deserialize<'de> for Base64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + base64::decode(&s) + .map_err(serde::de::Error::custom)? + .try_into() + .map_err(|_| serde::de::Error::custom("invalid length")) + .map(Self) + } +} +impl> Serialize for Base64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&base64::encode(self.0.as_ref())) + } +} diff --git a/backend/src/version/mod.rs b/backend/src/version/mod.rs index 3af28f24f2..48f914ee22 100644 --- a/backend/src/version/mod.rs +++ b/backend/src/version/mod.rs @@ -4,6 +4,7 @@ use async_trait::async_trait; use color_eyre::eyre::eyre; use patch_db::DbHandle; use rpc_toolkit::command; +use sqlx::PgPool; use crate::init::InitReceipts; use crate::Error; @@ -76,8 +77,8 @@ where fn new() -> Self; fn semver(&self) -> emver::Version; fn compat(&self) -> &'static emver::VersionRange; - async fn up(&self, db: &mut Db) -> Result<(), Error>; - async fn down(&self, db: &mut Db) -> Result<(), Error>; + async fn up(&self, db: &mut Db, secrets: &PgPool) -> Result<(), Error>; + async fn down(&self, db: &mut Db, secrets: &PgPool) -> Result<(), Error>; async fn commit( &self, db: &mut Db, @@ -98,11 +99,19 @@ where &self, version: &V, db: &mut Db, + secrets: &PgPool, receipts: &InitReceipts, ) -> Result<(), Error> { match self.semver().cmp(&version.semver()) { - Ordering::Greater => self.rollback_to_unchecked(version, db, receipts).await, - Ordering::Less => version.migrate_from_unchecked(self, db, receipts).await, + Ordering::Greater => { + self.rollback_to_unchecked(version, db, secrets, receipts) + .await + } + Ordering::Less => { + version + .migrate_from_unchecked(self, db, secrets, receipts) + .await + } Ordering::Equal => Ok(()), } } @@ -110,12 +119,13 @@ where &self, version: &V, db: &mut Db, + secrets: &PgPool, receipts: &InitReceipts, ) -> Result<(), Error> { let previous = Self::Previous::new(); if version.semver() < previous.semver() { previous - .migrate_from_unchecked(version, db, receipts) + .migrate_from_unchecked(version, db, secrets, receipts) .await?; } else if version.semver() > previous.semver() { return Err(Error::new( @@ -127,7 +137,7 @@ where )); } tracing::info!("{} -> {}", previous.semver(), self.semver(),); - self.up(db).await?; + self.up(db, secrets).await?; self.commit(db, receipts).await?; Ok(()) } @@ -135,15 +145,16 @@ where &self, version: &V, db: &mut Db, + secrets: &PgPool, receipts: &InitReceipts, ) -> Result<(), Error> { let previous = Self::Previous::new(); tracing::info!("{} -> {}", self.semver(), previous.semver(),); - self.down(db).await?; + self.down(db, secrets).await?; previous.commit(db, receipts).await?; if version.semver() < previous.semver() { previous - .rollback_to_unchecked(version, db, receipts) + .rollback_to_unchecked(version, db, secrets, receipts) .await?; } else if version.semver() > previous.semver() { return Err(Error::new( @@ -184,21 +195,55 @@ where pub async fn init( db: &mut Db, + secrets: &PgPool, receipts: &crate::init::InitReceipts, ) -> Result<(), Error> { let version = Version::from_util_version(receipts.server_version.get(db).await?); match version { - Version::V0_3_0(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_0_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_0_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_0_3(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_1_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_1_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_2_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_3(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, - Version::V0_3_4(v) => v.0.migrate_to(&Current::new(), db, receipts).await?, + Version::V0_3_0(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_0_1(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_0_2(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_0_3(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_1(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_1_1(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_1_2(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_2(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_2_1(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_3(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } + Version::V0_3_4(v) => { + v.0.migrate_to(&Current::new(), db, secrets, receipts) + .await? + } Version::Other(_) => { return Err(Error::new( eyre!("Cannot downgrade"), diff --git a/backend/src/version/v0_3_0.rs b/backend/src/version/v0_3_0.rs index 087cbcceff..ccaf59fdd8 100644 --- a/backend/src/version/v0_3_0.rs +++ b/backend/src/version/v0_3_0.rs @@ -28,10 +28,10 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_0_1.rs b/backend/src/version/v0_3_0_1.rs index 9326782814..e42593e6b5 100644 --- a/backend/src/version/v0_3_0_1.rs +++ b/backend/src/version/v0_3_0_1.rs @@ -18,10 +18,10 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*v0_3_0::V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_0_2.rs b/backend/src/version/v0_3_0_2.rs index 29d6720aee..0b56a9e668 100644 --- a/backend/src/version/v0_3_0_2.rs +++ b/backend/src/version/v0_3_0_2.rs @@ -18,10 +18,10 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*v0_3_0::V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_0_3.rs b/backend/src/version/v0_3_0_3.rs index 0042f925e6..041f0bf138 100644 --- a/backend/src/version/v0_3_0_3.rs +++ b/backend/src/version/v0_3_0_3.rs @@ -18,10 +18,10 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*v0_3_0::V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_1.rs b/backend/src/version/v0_3_1.rs index 315ee5e0de..dcb738dda0 100644 --- a/backend/src/version/v0_3_1.rs +++ b/backend/src/version/v0_3_1.rs @@ -19,10 +19,10 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_1_1.rs b/backend/src/version/v0_3_1_1.rs index 48d8b8bee3..f7b70e4dec 100644 --- a/backend/src/version/v0_3_1_1.rs +++ b/backend/src/version/v0_3_1_1.rs @@ -19,10 +19,10 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_1_2.rs b/backend/src/version/v0_3_1_2.rs index a3a672e047..310b7ee87c 100644 --- a/backend/src/version/v0_3_1_2.rs +++ b/backend/src/version/v0_3_1_2.rs @@ -19,10 +19,10 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_2.rs b/backend/src/version/v0_3_2.rs index d0f6548c15..6df2784c2b 100644 --- a/backend/src/version/v0_3_2.rs +++ b/backend/src/version/v0_3_2.rs @@ -2,8 +2,6 @@ use emver::VersionRange; use super::v0_3_0::V0_3_0_COMPAT; use super::*; -use crate::config::util::MergeWith; -use crate::hostname::{generate_id, sync_hostname}; const V0_3_2: emver::Version = emver::Version::new(0, 3, 2, 0); @@ -42,19 +40,117 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, db: &mut Db) -> Result<(), Error> { - let receipts = crate::hostname::HostNameReceipt::new(db).await?; - crate::hostname::ensure_hostname_is_set(db, &receipts).await?; - receipts.id.set(db, generate_id()).await?; + async fn up(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { + let hostname = legacy::hostname::get_hostname(db).await?; + crate::db::DatabaseModel::new() + .server_info() + .hostname() + .put(db, &Some(hostname.0)) + .await?; + crate::db::DatabaseModel::new() + .server_info() + .id() + .put(db, &legacy::hostname::generate_id()) + .await?; - let mut ui = crate::db::DatabaseModel::new().ui().get(db).await?.clone(); - ui.merge_with(&DEFAULT_UI); - crate::db::DatabaseModel::new().ui().put(db, &ui).await?; - - sync_hostname(db, &receipts).await?; + legacy::hostname::sync_hostname(db).await?; Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } + +mod legacy { + pub mod hostname { + use patch_db::DbHandle; + use rand::{thread_rng, Rng}; + use tokio::process::Command; + use tracing::instrument; + + use crate::util::Invoke; + use crate::{Error, ErrorKind}; + #[derive(Clone, serde::Deserialize, serde::Serialize, Debug)] + pub struct Hostname(pub String); + + lazy_static::lazy_static! { + static ref ADJECTIVES: Vec = include_str!("../assets/adjectives.txt").lines().map(|x| x.to_string()).collect(); + static ref NOUNS: Vec = include_str!("../assets/nouns.txt").lines().map(|x| x.to_string()).collect(); + } + impl AsRef for Hostname { + fn as_ref(&self) -> &str { + &self.0 + } + } + + pub fn generate_hostname() -> Hostname { + let mut rng = thread_rng(); + let adjective = &ADJECTIVES[rng.gen_range(0..ADJECTIVES.len())]; + let noun = &NOUNS[rng.gen_range(0..NOUNS.len())]; + Hostname(format!("embassy-{adjective}-{noun}")) + } + + pub fn generate_id() -> String { + let id = uuid::Uuid::new_v4(); + id.to_string() + } + + #[instrument] + pub async fn get_current_hostname() -> Result { + let out = Command::new("hostname") + .invoke(ErrorKind::ParseSysInfo) + .await?; + let out_string = String::from_utf8(out)?; + Ok(Hostname(out_string.trim().to_owned())) + } + + #[instrument] + pub async fn set_hostname(hostname: &Hostname) -> Result<(), Error> { + let hostname: &String = &hostname.0; + let _out = Command::new("hostnamectl") + .arg("set-hostname") + .arg(hostname) + .invoke(ErrorKind::ParseSysInfo) + .await?; + Ok(()) + } + + #[instrument(skip(handle))] + pub async fn get_id(handle: &mut Db) -> Result { + let id = crate::db::DatabaseModel::new() + .server_info() + .id() + .get(handle) + .await?; + Ok(id.to_string()) + } + + pub async fn get_hostname(handle: &mut Db) -> Result { + if let Ok(hostname) = crate::db::DatabaseModel::new() + .server_info() + .hostname() + .get(handle) + .await + { + if let Some(hostname) = hostname.to_owned() { + return Ok(Hostname(hostname)); + } + } + let id = get_id(handle).await?; + if id.len() != 8 { + return Ok(generate_hostname()); + } + return Ok(Hostname(format!("embassy-{}", id))); + } + #[instrument(skip(handle))] + pub async fn sync_hostname(handle: &mut Db) -> Result<(), Error> { + set_hostname(&get_hostname(handle).await?).await?; + Command::new("systemctl") + .arg("restart") + .arg("avahi-daemon") + .invoke(crate::ErrorKind::Network) + .await?; + Ok(()) + } + } +} diff --git a/backend/src/version/v0_3_2_1.rs b/backend/src/version/v0_3_2_1.rs index d82d7f973b..62f36f623f 100644 --- a/backend/src/version/v0_3_2_1.rs +++ b/backend/src/version/v0_3_2_1.rs @@ -17,10 +17,10 @@ impl VersionT for Version { fn compat(&self) -> &'static emver::VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, _db: &mut Db) -> Result<(), Error> { + async fn up(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } - async fn down(&self, _db: &mut Db) -> Result<(), Error> { + async fn down(&self, _db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { Ok(()) } } diff --git a/backend/src/version/v0_3_3.rs b/backend/src/version/v0_3_3.rs index 194898d061..b168aaa56b 100644 --- a/backend/src/version/v0_3_3.rs +++ b/backend/src/version/v0_3_3.rs @@ -24,7 +24,7 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, db: &mut Db) -> Result<(), Error> { + async fn up(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?; if let Some(Value::String(selected_url)) = @@ -65,7 +65,7 @@ impl VersionT for Version { Ok(()) } - async fn down(&self, db: &mut Db) -> Result<(), Error> { + async fn down(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?; let selected_url = ui["marketplace"]["selected-url"] .as_str() diff --git a/backend/src/version/v0_3_4.rs b/backend/src/version/v0_3_4.rs index 03cf3d4f83..78faab3e60 100644 --- a/backend/src/version/v0_3_4.rs +++ b/backend/src/version/v0_3_4.rs @@ -1,6 +1,12 @@ use async_trait::async_trait; use emver::VersionRange; +use itertools::Itertools; +use openssl::hash::MessageDigest; use serde_json::{json, Value}; +use ssh_key::public::Ed25519PublicKey; + +use crate::account::AccountInfo; +use crate::hostname::{generate_hostname, sync_hostname, Hostname}; use super::v0_3_0::V0_3_0_COMPAT; use super::*; @@ -37,7 +43,44 @@ impl VersionT for Version { fn compat(&self) -> &'static VersionRange { &*V0_3_0_COMPAT } - async fn up(&self, db: &mut Db) -> Result<(), Error> { + async fn up(&self, db: &mut Db, secrets: &PgPool) -> Result<(), Error> { + let mut account = AccountInfo::load(secrets).await?; + crate::db::DatabaseModel::new() + .server_info() + .pubkey() + .put( + db, + &ssh_key::PublicKey::from(Ed25519PublicKey::from(&account.key.ssh_key())) + .to_openssh()?, + ) + .await?; + crate::db::DatabaseModel::new() + .server_info() + .ca_fingerprint() + .put( + db, + &account + .root_ca_cert + .digest(MessageDigest::sha256()) + .unwrap() + .iter() + .map(|x| format!("{x:X}")) + .join(":"), + ) + .await?; + let server_info = crate::db::DatabaseModel::new() + .server_info() + .get(db) + .await? + .into_owned(); + account.hostname = server_info + .hostname + .map(Hostname) + .unwrap_or_else(generate_hostname); + account.server_id = server_info.id; + account.save(secrets).await?; + sync_hostname(&account).await?; + let parsed_url = Some(COMMUNITY_URL.parse().unwrap()); let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?; ui["marketplace"]["known-hosts"][COMMUNITY_URL] = json!({}); @@ -66,7 +109,7 @@ impl VersionT for Version { ui.save(db).await?; Ok(()) } - async fn down(&self, db: &mut Db) -> Result<(), Error> { + async fn down(&self, db: &mut Db, _secrets: &PgPool) -> Result<(), Error> { let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?; let parsed_url = Some(MAIN_REGISTRY.parse().unwrap()); for package_id in crate::db::DatabaseModel::new() diff --git a/backend/src/volume.rs b/backend/src/volume.rs index c180104393..4c0eb4d083 100644 --- a/backend/src/volume.rs +++ b/backend/src/volume.rs @@ -10,7 +10,7 @@ use tracing::instrument; use crate::context::RpcContext; use crate::net::interface::{InterfaceId, Interfaces}; -use crate::net::net_controller::NetController; +use crate::net::PACKAGE_CERT_PATH; use crate::s9pk::manifest::PackageId; use crate::util::Version; use crate::{Error, ResultExt}; @@ -113,6 +113,10 @@ pub fn backup_dir(pkg_id: &PackageId) -> PathBuf { Path::new(BACKUP_DIR).join(pkg_id).join("data") } +pub fn cert_dir(pkg_id: &PackageId, interface_id: &InterfaceId) -> PathBuf { + Path::new(PACKAGE_CERT_PATH).join(pkg_id).join(interface_id) +} + #[derive(Clone, Debug, Deserialize, Serialize, HasModel)] #[serde(tag = "type")] #[serde(rename_all = "kebab-case")] @@ -185,7 +189,7 @@ impl Volume { } else { path.as_ref() }), - Volume::Certificate { interface_id: _ } => NetController::ssl_directory_for(pkg_id), + Volume::Certificate { interface_id } => cert_dir(pkg_id, &interface_id), Volume::Backup { .. } => backup_dir(pkg_id), } } diff --git a/frontend/patchdb-ui-seed.json b/frontend/patchdb-ui-seed.json index 99ca601f54..e32725588b 100644 --- a/frontend/patchdb-ui-seed.json +++ b/frontend/patchdb-ui-seed.json @@ -1,6 +1,6 @@ { "name": null, - "ack-welcome": "0.3.3.1", + "ack-welcome": "0.3.4", "marketplace": { "selected-url": "https://registry.start9.com/", "known-hosts": { diff --git a/libs/Cargo.lock b/libs/Cargo.lock index 180f1865ab..0b6c47c9f4 100644 --- a/libs/Cargo.lock +++ b/libs/Cargo.lock @@ -1560,6 +1560,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "internment" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a798d7677f07d6f1e77be484ea8626ddb1566194de399f1206306820c406371" +dependencies = [ + "ahash", + "dashmap", + "hashbrown", + "once_cell", + "parking_lot 0.12.1", + "serde", +] + [[package]] name = "io-lifetimes" version = "1.0.4" @@ -1923,10 +1937,14 @@ dependencies = [ "color-eyre", "ed25519-dalek", "emver", + "internment", + "ipnet", + "lazy_static", "mbrman", "openssl", "patch-db", "rand 0.8.5", + "regex", "rpc-toolkit", "serde", "serde_json", diff --git a/libs/models/Cargo.toml b/libs/models/Cargo.toml index 7f973c7799..6362919f64 100644 --- a/libs/models/Cargo.toml +++ b/libs/models/Cargo.toml @@ -9,15 +9,19 @@ edition = "2021" bollard = "0.13.0" color-eyre = "0.6.1" ed25519-dalek = { version = "1.0.1", features = ["serde"] } +lazy_static = "1.4" mbrman = "0.5.0" emver = { version = "0.1", git = "https://github.com/Start9Labs/emver-rs.git", features = [ "serde", ] } +internment = { version = "0.7.0", features = ["arc", "serde"] } +ipnet = "2.7.1" openssl = { version = "0.10.41", features = ["vendored"] } patch-db = { version = "*", path = "../../patch-db/patch-db", features = [ "trace", ] } rand = "0.8" +regex = "1.7.1" rpc-toolkit = "0.2.1" serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0.82" diff --git a/libs/models/src/action_id.rs b/libs/models/src/action_id.rs index 8ce73ded33..9b814a98af 100644 --- a/libs/models/src/action_id.rs +++ b/libs/models/src/action_id.rs @@ -6,43 +6,34 @@ use serde::{Deserialize, Serialize}; use crate::{Id, InvalidId}; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -pub struct ActionId = String>(Id); +pub struct ActionId(Id); impl FromStr for ActionId { type Err = InvalidId; fn from_str(s: &str) -> Result { Ok(ActionId(Id::try_from(s.to_owned())?)) } } -impl From for String { - fn from(value: ActionId) -> Self { - value.0.into() - } -} -impl> AsRef> for ActionId { - fn as_ref(&self) -> &ActionId { +impl AsRef for ActionId { + fn as_ref(&self) -> &ActionId { self } } -impl> std::fmt::Display for ActionId { +impl std::fmt::Display for ActionId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", &self.0) } } -impl> AsRef for ActionId { +impl AsRef for ActionId { fn as_ref(&self) -> &str { self.0.as_ref() } } -impl> AsRef for ActionId { +impl AsRef for ActionId { fn as_ref(&self) -> &Path { self.0.as_ref().as_ref() } } -impl<'de, S> Deserialize<'de> for ActionId -where - S: AsRef, - Id: Deserialize<'de>, -{ +impl<'de> Deserialize<'de> for ActionId { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, diff --git a/libs/models/src/errors.rs b/libs/models/src/errors.rs index 328cb10f12..245409416a 100644 --- a/libs/models/src/errors.rs +++ b/libs/models/src/errors.rs @@ -246,6 +246,11 @@ impl From for Error { Error::new(e, ErrorKind::ParseNetAddress) } } +impl From for Error { + fn from(e: ipnet::AddrParseError) -> Self { + Error::new(e, ErrorKind::ParseNetAddress) + } +} impl From for Error { fn from(e: openssl::error::ErrorStack) -> Self { Error::new(eyre!("{}", e), ErrorKind::OpenSsl) diff --git a/libs/models/src/health_check_id.rs b/libs/models/src/health_check_id.rs index a2c757d36c..dc643c9127 100644 --- a/libs/models/src/health_check_id.rs +++ b/libs/models/src/health_check_id.rs @@ -4,23 +4,19 @@ use serde::{Deserialize, Deserializer, Serialize}; use crate::Id; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -pub struct HealthCheckId = String>(Id); -impl> std::fmt::Display for HealthCheckId { +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct HealthCheckId(Id); +impl std::fmt::Display for HealthCheckId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", &self.0) } } -impl> AsRef for HealthCheckId { +impl AsRef for HealthCheckId { fn as_ref(&self) -> &str { self.0.as_ref() } } -impl<'de, S> Deserialize<'de> for HealthCheckId -where - S: AsRef, - Id: Deserialize<'de>, -{ +impl<'de> Deserialize<'de> for HealthCheckId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -28,7 +24,7 @@ where Ok(HealthCheckId(Deserialize::deserialize(deserializer)?)) } } -impl> AsRef for HealthCheckId { +impl AsRef for HealthCheckId { fn as_ref(&self) -> &Path { self.0.as_ref().as_ref() } diff --git a/libs/models/src/id.rs b/libs/models/src/id.rs index d8b9ae1634..30a1818ec9 100644 --- a/libs/models/src/id.rs +++ b/libs/models/src/id.rs @@ -1,72 +1,79 @@ use std::borrow::Borrow; +use internment::ArcIntern; +use regex::Regex; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::id_unchecked::IdUnchecked; use crate::invalid_id::InvalidId; -pub const SYSTEM_ID: Id<&'static str> = Id("x_system"); +lazy_static::lazy_static! { + static ref ID_REGEX: Regex = Regex::new("^[a-z]+(-[a-z]+)*$").unwrap(); + pub static ref SYSTEM_ID: Id = Id(ArcIntern::from_ref("x_system")); +} -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -pub struct Id = String>(S); -impl> Id { - pub fn try_from(value: S) -> Result { - if value - .as_ref() - .chars() - .all(|c| c.is_ascii_lowercase() || c == '-') - { +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct Id(ArcIntern); +impl TryFrom> for Id { + type Error = InvalidId; + fn try_from(value: ArcIntern) -> Result { + if ID_REGEX.is_match(&*value) { Ok(Id(value)) } else { Err(InvalidId) } } } -impl<'a> Id<&'a str> { - pub fn owned(&self) -> Id { - Id(self.0.to_owned()) +impl TryFrom for Id { + type Error = InvalidId; + fn try_from(value: String) -> Result { + if ID_REGEX.is_match(&value) { + Ok(Id(ArcIntern::new(value))) + } else { + Err(InvalidId) + } } } -impl From for String { - fn from(value: Id) -> Self { - value.0 +impl TryFrom<&str> for Id { + type Error = InvalidId; + fn try_from(value: &str) -> Result { + if ID_REGEX.is_match(&value) { + Ok(Id(ArcIntern::from_ref(value))) + } else { + Err(InvalidId) + } } } -impl> std::ops::Deref for Id { - type Target = S; +impl std::ops::Deref for Id { + type Target = String; fn deref(&self) -> &Self::Target { - &self.0 + &*self.0 } } -impl> std::fmt::Display for Id { +impl std::fmt::Display for Id { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0.as_ref()) + write!(f, "{}", &*self.0) } } -impl> AsRef for Id { +impl AsRef for Id { fn as_ref(&self) -> &str { - self.0.as_ref() + &*self.0 } } -impl> Borrow for Id { +impl Borrow for Id { fn borrow(&self) -> &str { self.0.as_ref() } } -impl<'de, S> Deserialize<'de> for Id -where - S: AsRef, - IdUnchecked: Deserialize<'de>, -{ +impl<'de> Deserialize<'de> for Id { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let unchecked: IdUnchecked = Deserialize::deserialize(deserializer)?; - Id::try_from(unchecked.0).map_err(serde::de::Error::custom) + let unchecked: String = Deserialize::deserialize(deserializer)?; + Id::try_from(unchecked).map_err(serde::de::Error::custom) } } -impl> Serialize for Id { +impl Serialize for Id { fn serialize(&self, serializer: Ser) -> Result where Ser: Serializer, diff --git a/libs/models/src/id_unchecked.rs b/libs/models/src/id_unchecked.rs deleted file mode 100644 index a112085068..0000000000 --- a/libs/models/src/id_unchecked.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::borrow::Cow; - -use serde::{Deserialize, Deserializer}; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct IdUnchecked>(pub S); -impl<'de> Deserialize<'de> for IdUnchecked> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = IdUnchecked>; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "a valid ID") - } - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(IdUnchecked(Cow::Owned(v.to_owned()))) - } - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - Ok(IdUnchecked(Cow::Owned(v))) - } - fn visit_borrowed_str(self, v: &'de str) -> Result - where - E: serde::de::Error, - { - Ok(IdUnchecked(Cow::Borrowed(v))) - } - } - deserializer.deserialize_any(Visitor) - } -} -impl<'de> Deserialize<'de> for IdUnchecked { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(IdUnchecked(String::deserialize(deserializer)?)) - } -} -impl<'de> Deserialize<'de> for IdUnchecked<&'de str> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(IdUnchecked(<&'de str>::deserialize(deserializer)?)) - } -} diff --git a/backend/src/id.rs b/libs/models/src/image_id.rs similarity index 53% rename from backend/src/id.rs rename to libs/models/src/image_id.rs index a2dc950524..10ef0451d7 100644 --- a/backend/src/id.rs +++ b/libs/models/src/image_id.rs @@ -1,27 +1,22 @@ use std::fmt::Debug; use std::str::FromStr; -pub use models::{Id, IdUnchecked, InvalidId, SYSTEM_ID}; use serde::{Deserialize, Deserializer, Serialize}; -use crate::util::Version; +use crate::{Id, InvalidId, PackageId, Version}; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -pub struct ImageId = String>(Id); -impl> std::fmt::Display for ImageId { +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct ImageId(Id); +impl std::fmt::Display for ImageId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", &self.0) } } -impl> ImageId { - pub fn for_package>, S0: AsRef>( - &self, - pkg_id: PkgId, - pkg_version: Option<&Version>, - ) -> String { +impl ImageId { + pub fn for_package(&self, pkg_id: &PackageId, pkg_version: Option<&Version>) -> String { format!( "start9/{}/{}:{}", - pkg_id.as_ref(), + pkg_id, self.0, pkg_version.map(|v| { v.as_str() }).unwrap_or("latest") ) @@ -33,11 +28,7 @@ impl FromStr for ImageId { Ok(ImageId(Id::try_from(s.to_owned())?)) } } -impl<'de, S> Deserialize<'de> for ImageId -where - S: AsRef, - Id: Deserialize<'de>, -{ +impl<'de> Deserialize<'de> for ImageId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, diff --git a/libs/models/src/interface_id.rs b/libs/models/src/interface_id.rs index 168756bac4..b2f82f83c5 100644 --- a/libs/models/src/interface_id.rs +++ b/libs/models/src/interface_id.rs @@ -4,34 +4,30 @@ use serde::{Deserialize, Deserializer, Serialize}; use crate::Id; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Default)] -pub struct InterfaceId = String>(Id); -impl> From> for InterfaceId { - fn from(id: Id) -> Self { +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct InterfaceId(Id); +impl From for InterfaceId { + fn from(id: Id) -> Self { Self(id) } } -impl> std::fmt::Display for InterfaceId { +impl std::fmt::Display for InterfaceId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", &self.0) } } -impl> std::ops::Deref for InterfaceId { - type Target = S; +impl std::ops::Deref for InterfaceId { + type Target = String; fn deref(&self) -> &Self::Target { &*self.0 } } -impl> AsRef for InterfaceId { +impl AsRef for InterfaceId { fn as_ref(&self) -> &str { self.0.as_ref() } } -impl<'de, S> Deserialize<'de> for InterfaceId -where - S: AsRef, - Id: Deserialize<'de>, -{ +impl<'de> Deserialize<'de> for InterfaceId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -39,7 +35,7 @@ where Ok(InterfaceId(Deserialize::deserialize(deserializer)?)) } } -impl> AsRef for InterfaceId { +impl AsRef for InterfaceId { fn as_ref(&self) -> &Path { self.0.as_ref().as_ref() } diff --git a/libs/models/src/lib.rs b/libs/models/src/lib.rs index 1cb9dca9cf..01edd65e88 100644 --- a/libs/models/src/lib.rs +++ b/libs/models/src/lib.rs @@ -2,7 +2,7 @@ mod action_id; mod errors; mod health_check_id; mod id; -mod id_unchecked; +mod image_id; mod interface_id; mod invalid_id; mod package_id; @@ -14,7 +14,7 @@ pub use action_id::*; pub use errors::*; pub use health_check_id::*; pub use id::*; -pub use id_unchecked::*; +pub use image_id::*; pub use interface_id::*; pub use invalid_id::*; pub use package_id::*; diff --git a/libs/models/src/package_id.rs b/libs/models/src/package_id.rs index 3f4d948345..9b534a4f5a 100644 --- a/libs/models/src/package_id.rs +++ b/libs/models/src/package_id.rs @@ -6,66 +6,54 @@ use serde::{Deserialize, Serialize, Serializer}; use crate::{Id, InvalidId, SYSTEM_ID}; -pub const SYSTEM_PACKAGE_ID: PackageId<&'static str> = PackageId(SYSTEM_ID); -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PackageId = String>(Id); -impl<'a> PackageId<&'a str> { - pub fn owned(&self) -> PackageId { - PackageId(self.0.owned()) - } +lazy_static::lazy_static! { + pub static ref SYSTEM_PACKAGE_ID: PackageId = PackageId(SYSTEM_ID.clone()); } +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PackageId(Id); impl FromStr for PackageId { type Err = InvalidId; fn from_str(s: &str) -> Result { Ok(PackageId(Id::try_from(s.to_owned())?)) } } -impl From for String { - fn from(value: PackageId) -> Self { - value.0.into() - } -} -impl> From> for PackageId { - fn from(id: Id) -> Self { +impl From for PackageId { + fn from(id: Id) -> Self { PackageId(id) } } -impl> std::ops::Deref for PackageId { - type Target = S; +impl std::ops::Deref for PackageId { + type Target = String; fn deref(&self) -> &Self::Target { &*self.0 } } -impl> AsRef> for PackageId { - fn as_ref(&self) -> &PackageId { +impl AsRef for PackageId { + fn as_ref(&self) -> &PackageId { self } } -impl> std::fmt::Display for PackageId { +impl std::fmt::Display for PackageId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", &self.0) } } -impl> AsRef for PackageId { +impl AsRef for PackageId { fn as_ref(&self) -> &str { self.0.as_ref() } } -impl> Borrow for PackageId { +impl Borrow for PackageId { fn borrow(&self) -> &str { self.0.as_ref() } } -impl> AsRef for PackageId { +impl AsRef for PackageId { fn as_ref(&self) -> &Path { self.0.as_ref().as_ref() } } -impl<'de, S> Deserialize<'de> for PackageId -where - S: AsRef, - Id: Deserialize<'de>, -{ +impl<'de> Deserialize<'de> for PackageId { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, @@ -73,10 +61,7 @@ where Ok(PackageId(Deserialize::deserialize(deserializer)?)) } } -impl Serialize for PackageId -where - S: AsRef, -{ +impl Serialize for PackageId { fn serialize(&self, serializer: Ser) -> Result where Ser: Serializer, diff --git a/libs/models/src/volume_id.rs b/libs/models/src/volume_id.rs index c5215123ed..16821a3cfd 100644 --- a/libs/models/src/volume_id.rs +++ b/libs/models/src/volume_id.rs @@ -3,14 +3,14 @@ use std::path::Path; use serde::{Deserialize, Deserializer, Serialize}; -use crate::{Id, IdUnchecked}; +use crate::Id; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum VolumeId = String> { +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum VolumeId { Backup, - Custom(Id), + Custom(Id), } -impl> std::fmt::Display for VolumeId { +impl std::fmt::Display for VolumeId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { VolumeId::Backup => write!(f, "BACKUP"), @@ -18,7 +18,7 @@ impl> std::fmt::Display for VolumeId { } } } -impl> AsRef for VolumeId { +impl AsRef for VolumeId { fn as_ref(&self) -> &str { match self { VolumeId::Backup => "BACKUP", @@ -26,33 +26,29 @@ impl> AsRef for VolumeId { } } } -impl> Borrow for VolumeId { +impl Borrow for VolumeId { fn borrow(&self) -> &str { self.as_ref() } } -impl> AsRef for VolumeId { +impl AsRef for VolumeId { fn as_ref(&self) -> &Path { AsRef::::as_ref(self).as_ref() } } -impl<'de, S> Deserialize<'de> for VolumeId -where - S: AsRef, - IdUnchecked: Deserialize<'de>, -{ +impl<'de> Deserialize<'de> for VolumeId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let unchecked: IdUnchecked = Deserialize::deserialize(deserializer)?; - Ok(match unchecked.0.as_ref() { + let unchecked: String = Deserialize::deserialize(deserializer)?; + Ok(match unchecked.as_ref() { "BACKUP" => VolumeId::Backup, - _ => VolumeId::Custom(Id::try_from(unchecked.0).map_err(serde::de::Error::custom)?), + _ => VolumeId::Custom(Id::try_from(unchecked).map_err(serde::de::Error::custom)?), }) } } -impl> Serialize for VolumeId { +impl Serialize for VolumeId { fn serialize(&self, serializer: Ser) -> Result where Ser: serde::Serializer, diff --git a/system-images/compat/Cargo.lock b/system-images/compat/Cargo.lock index 2cc68866c0..7b6fa8c282 100644 --- a/system-images/compat/Cargo.lock +++ b/system-images/compat/Cargo.lock @@ -50,6 +50,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -109,6 +124,20 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-stream" version = "0.3.3" @@ -364,6 +393,27 @@ dependencies = [ "serde_with 1.14.0", ] +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "0.2.17" @@ -378,9 +428,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -1033,7 +1083,19 @@ dependencies = [ "der", "elliptic-curve", "rfc6979", - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature 2.0.0", ] [[package]] @@ -1044,7 +1106,7 @@ checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "pkcs8", "serde", - "signature", + "signature 1.6.4", ] [[package]] @@ -1084,6 +1146,8 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", + "pkcs8", "rand_core 0.6.4", "sec1", "subtle", @@ -1092,9 +1156,10 @@ dependencies = [ [[package]] name = "embassy-os" -version = "0.3.3" +version = "0.3.4" dependencies = [ "aes", + "async-compression", "async-stream", "async-trait", "base32", @@ -1128,6 +1193,8 @@ dependencies = [ "hyper-ws-listener", "imbl 2.0.0", "indexmap", + "ipnet", + "iprange", "isocountry", "itertools 0.10.5", "josekit", @@ -1143,6 +1210,7 @@ dependencies = [ "num_enum", "openssh-keys", "openssl", + "p256 0.12.0", "patch-db", "pbkdf2", "pin-project", @@ -1187,6 +1255,7 @@ dependencies = [ "typed-builder", "url", "uuid", + "zeroize", ] [[package]] @@ -1373,7 +1442,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall 0.2.16", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1704,6 +1773,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -1958,6 +2033,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "internment" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a798d7677f07d6f1e77be484ea8626ddb1566194de399f1206306820c406371" +dependencies = [ + "ahash", + "dashmap", + "hashbrown", + "once_cell", + "parking_lot 0.12.1", + "serde", +] + [[package]] name = "io-lifetimes" version = "1.0.4" @@ -1965,7 +2054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1973,6 +2062,31 @@ name = "ipnet" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +dependencies = [ + "serde", +] + +[[package]] +name = "iprange" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37209be0ad225457e63814401415e748e2453a5297f9b637338f5fb8afa4ec00" +dependencies = [ + "ipnet", + "serde", +] + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] [[package]] name = "isocountry" @@ -2295,7 +2409,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -2306,10 +2420,14 @@ dependencies = [ "color-eyre", "ed25519-dalek", "emver", + "internment", + "ipnet", + "lazy_static", "mbrman", "openssl", "patch-db", "rand 0.8.5", + "regex", "rpc-toolkit", "serde", "serde_json", @@ -2651,8 +2769,20 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "ecdsa", + "ecdsa 0.14.8", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "p256" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49c124b3cbce43bcbac68c58ec181d98ed6cc7e6d0aa7c3ba97b2563410b0e55" +dependencies = [ + "ecdsa 0.15.1", "elliptic-curve", + "primeorder", "sha2 0.10.6", ] @@ -2662,7 +2792,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "ecdsa", + "ecdsa 0.14.8", "elliptic-curve", "sha2 0.10.6", ] @@ -2712,7 +2842,7 @@ dependencies = [ "libc", "redox_syscall 0.2.16", "smallvec", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -2943,18 +3073,27 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettytable-rs" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f375cb74c23b51d23937ffdeb48b1fbf5b6409d4b9979c1418c1de58bc8f801" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" dependencies = [ - "atty", "csv", "encode_unicode", + "is-terminal", "lazy_static", "term", "unicode-width", ] +[[package]] +name = "primeorder" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b54f7131b3dba65a2f414cf5bd25b66d4682e4608610668eae785750ba4c5b2" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" version = "1.2.1" @@ -3399,7 +3538,7 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core 0.6.4", - "signature", + "signature 1.6.4", "smallvec", "subtle", "zeroize", @@ -3453,7 +3592,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -3507,7 +3646,7 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -3825,6 +3964,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + [[package]] name = "simple-logging" version = "2.0.2" @@ -4019,13 +4168,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "288d8f5562af5a3be4bda308dd374b2c807b940ac370b5efa1c99311da91d9a1" dependencies = [ "ed25519-dalek", - "p256", + "p256 0.11.1", "p384", "rand_core 0.6.4", "rsa", "sec1", "sha2 0.10.6", - "signature", + "signature 1.6.4", "ssh-encoding", "zeroize", ] @@ -4303,7 +4452,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -4969,6 +5118,30 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1"