diff --git a/Cargo.lock b/Cargo.lock index 04a29fa..4991c9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,19 +10,19 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "actix-codec" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" dependencies = [ "bitflags", "bytes", "futures-core", "futures-sink", - "log", "memchr", "pin-project-lite", "tokio", "tokio-util", + "tracing", ] [[package]] @@ -71,7 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" dependencies = [ "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -184,7 +184,7 @@ dependencies = [ "serde_urlencoded", "smallvec", "socket2", - "time 0.3.17", + "time 0.3.21", "url", ] @@ -197,7 +197,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -259,16 +259,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.8", + "getrandom", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -303,6 +303,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "arrayvec" version = "0.7.2" @@ -316,7 +365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -332,9 +381,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ "async-lock", "async-task", @@ -374,7 +423,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.3", + "rustix", "slab", "socket2", "waker-fn", @@ -418,9 +467,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", @@ -429,13 +478,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", ] [[package]] @@ -452,7 +501,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] @@ -466,9 +515,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" @@ -497,7 +546,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -530,15 +579,15 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bigdecimal" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" dependencies = [ "num-bigint", "num-integer", @@ -552,19 +601,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "block-buffer" -version = "0.10.3" +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "blocking" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel", "async-lock", @@ -572,6 +633,7 @@ dependencies = [ "atomic-waker", "fastrand", "futures-lite", + "log", ] [[package]] @@ -581,7 +643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", - "hashbrown", + "hashbrown 0.13.2", ] [[package]] @@ -594,7 +656,7 @@ dependencies = [ "borsh-schema-derive-internal", "proc-macro-crate", "proc-macro2", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -605,7 +667,7 @@ checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -616,7 +678,7 @@ checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -642,15 +704,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" [[package]] name = "bytecheck" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" dependencies = [ "bytecheck_derive", "ptr_meta", @@ -659,13 +721,13 @@ dependencies = [ [[package]] name = "bytecheck_derive" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -732,12 +794,12 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags", - "clap_derive 3.2.18", + "clap_derive 3.2.25", "clap_lex 0.2.4", "indexmap", "once_cell", @@ -746,43 +808,51 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.10" +version = "4.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce38afc168d8665cfc75c7b1dd9672e50716a137f433f070991619744a67342a" +checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" dependencies = [ - "bitflags", - "clap_derive 4.1.9", - "clap_lex 0.3.3", - "is-terminal", + "clap_builder", + "clap_derive 4.2.0", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex 0.4.1", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "clap_derive" -version = "4.1.9" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck 0.4.1", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", ] [[package]] @@ -796,22 +866,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" @@ -826,9 +889,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] @@ -858,7 +921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "percent-encoding", - "time 0.3.17", + "time 0.3.21", "version_check", ] @@ -874,15 +937,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -933,7 +996,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -971,55 +1034,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cxx" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.107", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - [[package]] name = "der" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" +checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" dependencies = [ "const-oid", "zeroize", @@ -1035,7 +1054,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1060,11 +1079,11 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys 0.4.0", + "dirs-sys 0.4.1", ] [[package]] @@ -1080,18 +1099,21 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "dnapi-rs" version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "089055338c55024ba337072022e8d38b37dc018ea5c26e3a5522e61a5fd932f8" dependencies = [ "base64 0.21.0", "base64-serde", @@ -1102,7 +1124,24 @@ dependencies = [ "reqwest", "serde", "serde_json", - "trifid-pki 0.1.9", + "trifid-pki 0.1.10", + "url", +] + +[[package]] +name = "dnapi-rs" +version = "0.1.11" +dependencies = [ + "base64 0.21.0", + "base64-serde", + "chrono", + "log", + "openssl-sys", + "rand", + "reqwest", + "serde", + "serde_json", + "trifid-pki 0.1.11", "url", ] @@ -1154,24 +1193,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", -] - -[[package]] -name = "errno" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1192,9 +1220,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -1207,21 +1235,21 @@ checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.45.0", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", ] [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -1258,10 +1286,16 @@ dependencies = [ ] [[package]] -name = "futures" -version = "0.3.26" +name = "funty" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1274,9 +1308,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1284,15 +1318,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1312,15 +1346,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand", "futures-core", @@ -1333,32 +1367,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1374,9 +1408,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1384,9 +1418,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -1417,9 +1451,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -1443,13 +1477,22 @@ dependencies = [ "ahash 0.7.6", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + [[package]] name = "hashlink" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -1520,9 +1563,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -1554,9 +1597,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -1591,26 +1634,25 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -1625,12 +1667,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -1653,34 +1695,34 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd6da19f25979c7270e70fa95ab371ec3b701cd0eefc47667a09785b3c59155" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" dependencies = [ "serde", ] [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.36.10", - "windows-sys 0.45.0", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -1694,9 +1736,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" @@ -1709,9 +1751,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" dependencies = [ "wasm-bindgen", ] @@ -1739,9 +1781,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libm" @@ -1749,26 +1791,11 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "linux-raw-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "local-channel" @@ -1834,9 +1861,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" @@ -1846,23 +1873,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1956,9 +1983,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opaque-debug" @@ -1968,9 +1995,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if", @@ -1983,13 +2010,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", ] [[package]] @@ -2000,20 +2027,19 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.25.2+1.1.1t" +version = "111.25.3+1.1.1t" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320708a054ad9b3bf314688b5db87cf4d6683d64cfc835e2337924ae62bf4431" +checksum = "924757a6a226bf60da5f7dd0311a34d2b52283dd82ddeb103208ddc66362f80c" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.83" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -2022,10 +2048,16 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.4.1" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "ouroboros" @@ -2047,7 +2079,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2062,9 +2094,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -2096,7 +2128,7 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] @@ -2109,16 +2141,16 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] [[package]] name = "paste" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pem" @@ -2159,9 +2191,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" @@ -2171,9 +2203,9 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polling" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags", @@ -2182,7 +2214,7 @@ dependencies = [ "libc", "log", "pin-project-lite", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2221,7 +2253,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "version_check", ] @@ -2238,9 +2270,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.52" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -2262,7 +2294,7 @@ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2276,13 +2308,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -2322,6 +2360,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -2329,19 +2376,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -2350,14 +2397,20 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "rend" @@ -2370,9 +2423,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" dependencies = [ "base64 0.21.0", "bytes", @@ -2422,27 +2475,30 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.41" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ + "bitvec", "bytecheck", - "hashbrown", + "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", + "tinyvec", + "uuid", ] [[package]] name = "rkyv_derive" -version = "0.7.41" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2474,30 +2530,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.10" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe885c3a125aa45213b68cc1472a49880cb5923dc23f522ad2791b882228778" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", - "errno 0.2.8", + "errno", "io-lifetimes", "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustix" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" -dependencies = [ - "bitflags", - "errno 0.3.0", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.1", - "windows-sys 0.45.0", + "linux-raw-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2529,9 +2571,9 @@ checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" @@ -2548,12 +2590,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - [[package]] name = "sct" version = "0.7.0" @@ -2566,9 +2602,9 @@ dependencies = [ [[package]] name = "sea-orm" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d875e2fcd965320e50066028ac0b4877ff07edbb734a6ddfeff48a87dbab38" +checksum = "fade86e8d41fd1a4721f84cb834f4ca2783f973cc30e6212b7fafc134f169214" dependencies = [ "async-stream", "async-trait", @@ -2586,7 +2622,7 @@ dependencies = [ "serde_json", "sqlx", "thiserror", - "time 0.3.17", + "time 0.3.21", "tracing", "url", "uuid", @@ -2594,12 +2630,12 @@ dependencies = [ [[package]] name = "sea-orm-cli" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ead9f7dac975f10447f17d08edbb2046daa087b5e0b50bbf8211f303459078c" +checksum = "efbf34a2caf70c2e3be9bb1e674e9540f6dfd7c8f40f6f05daf3b9740e476005" dependencies = [ "chrono", - "clap 3.2.23", + "clap 3.2.25", "dotenvy", "regex", "sea-schema", @@ -2610,25 +2646,25 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b593e9c0cdbb18cafd4da7b92e67a9c2d9892934f3a2d8bbac73d5ba4a98a1" +checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" dependencies = [ "bae", "heck 0.3.3", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sea-orm-migration" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7a6123c1035b0530deb713820688f0234431ab6c1893b14dce493ade76af" +checksum = "278d3adfd0832b6ffc17d3cfbc574d3695a5c1b38814e0bc8ac238d33f3d87cf" dependencies = [ "async-trait", - "clap 3.2.23", + "clap 3.2.25", "dotenvy", "futures", "sea-orm", @@ -2640,24 +2676,24 @@ dependencies = [ [[package]] name = "sea-query" -version = "0.28.3" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fbe015dbdaa7d8829d71c1e14fb6289e928ac256b93dfda543c85cd89d6f03" +checksum = "bbab99b8cd878ab7786157b7eb8df96333a6807cc6e45e8888c85b51534b401a" dependencies = [ "bigdecimal", "chrono", "rust_decimal", "sea-query-derive", "serde_json", - "time 0.3.17", + "time 0.3.21", "uuid", ] [[package]] name = "sea-query-binder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03548c63aec07afd4fd190923e0160d2f2fc92def27470b54154cf232da6203b" +checksum = "4cea85029985b40dfbf18318d85fe985c04db7c1b4e5e8e0a0a0cdff5f1e30f9" dependencies = [ "bigdecimal", "chrono", @@ -2665,7 +2701,7 @@ dependencies = [ "sea-query", "serde_json", "sqlx", - "time 0.3.17", + "time 0.3.21", "uuid", ] @@ -2678,7 +2714,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "thiserror", ] @@ -2702,7 +2738,7 @@ dependencies = [ "heck 0.3.3", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2724,7 +2760,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2735,9 +2771,9 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1" dependencies = [ "bitflags", "core-foundation", @@ -2748,9 +2784,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -2764,29 +2800,29 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.159" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -2816,9 +2852,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.19" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" +checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" dependencies = [ "indexmap", "itoa", @@ -2860,18 +2896,18 @@ dependencies = [ [[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", ] [[package]] name = "signature" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" [[package]] name = "simdutf8" @@ -2888,15 +2924,15 @@ dependencies = [ "atty", "colored", "log", - "time 0.3.17", + "time 0.3.21", "windows-sys 0.42.0", ] [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -2909,9 +2945,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -2925,9 +2961,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spki" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", "der", @@ -2946,9 +2982,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" +checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" dependencies = [ "sqlx-core", "sqlx-macros", @@ -3004,7 +3040,7 @@ dependencies = [ "sqlx-rt", "stringprep", "thiserror", - "time 0.3.17", + "time 0.3.21", "tokio-stream", "url", "uuid", @@ -3014,9 +3050,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" +checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" dependencies = [ "dotenvy", "either", @@ -3027,7 +3063,7 @@ dependencies = [ "serde_json", "sqlx-core", "sqlx-rt", - "syn 1.0.107", + "syn 1.0.109", "url", ] @@ -3072,9 +3108,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -3083,9 +3119,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.11" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", @@ -3093,16 +3129,10 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "unicode-xid", -] +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" @@ -3117,24 +3147,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", - "rustix 0.36.10", - "windows-sys 0.42.0", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -3145,15 +3166,15 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "tfclient" -version = "0.1.7" +version = "0.1.8" dependencies = [ "base64 0.21.0", "base64-serde", "chrono", - "clap 4.1.10", + "clap 4.2.7", "ctrlc", - "dirs 5.0.0", - "dnapi-rs", + "dirs 5.0.1", + "dnapi-rs 0.1.11", "flate2", "hex", "ipnet", @@ -3168,28 +3189,28 @@ dependencies = [ "tar", "tempfile", "toml 0.7.3", - "trifid-pki 0.1.9", + "trifid-pki 0.1.11", "url", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", ] [[package]] @@ -3215,9 +3236,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" dependencies = [ "itoa", "libc", @@ -3229,15 +3250,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" dependencies = [ "time-core", ] @@ -3259,28 +3280,27 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[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", @@ -3299,9 +3319,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -3310,9 +3330,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -3354,9 +3374,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.7" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", "serde", @@ -3402,29 +3422,29 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "once_cell", @@ -3444,17 +3464,21 @@ dependencies = [ "aes-gcm", "base64 0.21.0", "chrono", + "dnapi-rs 0.1.9", + "ed25519-dalek", "hex", + "ipnet", "log", "once_cell", "rand", "sea-orm", "serde", "serde_json", + "serde_yaml", "simple_logger", "toml 0.7.3", "totp-rs", - "trifid-pki 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "trifid-pki 0.1.10", "trifid_api_entities", "trifid_api_migration", ] @@ -3462,6 +3486,8 @@ dependencies = [ [[package]] name = "trifid-pki" version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8cbfb2f93137355fb818e39e8c4881eae777098dd57fe3094b13b29b4dd96b6" dependencies = [ "ed25519-dalek", "hex", @@ -3477,9 +3503,7 @@ dependencies = [ [[package]] name = "trifid-pki" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20f0b2d11a5219f67b42a0af7f07b10f1c6c1ea57f8cb9f1115d394176b64c6b" +version = "0.1.11" dependencies = [ "ed25519-dalek", "hex", @@ -3487,7 +3511,8 @@ dependencies = [ "pem", "quick-protobuf", "rand", - "rand_core 0.6.4", + "rand_core", + "serde", "sha2", "x25519-dalek", ] @@ -3522,15 +3547,15 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -3547,18 +3572,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "unicode_categories" version = "0.1.1" @@ -3577,9 +3590,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" +checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" [[package]] name = "untrusted" @@ -3605,15 +3618,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" [[package]] -name = "uuid" -version = "1.3.0" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" dependencies = [ - "getrandom 0.2.8", + "getrandom", "serde", - "rand", - "uuid-macro-internal", ] [[package]] @@ -3668,9 +3685,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3678,24 +3695,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" dependencies = [ "cfg-if", "js-sys", @@ -3705,9 +3722,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3715,28 +3732,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.16", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" dependencies = [ "js-sys", "wasm-bindgen", @@ -3787,34 +3804,34 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 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", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -3823,71 +3840,137 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 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", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.3.6" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] @@ -3901,6 +3984,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x25519-dalek" version = "2.0.0-rc.2" @@ -3924,23 +4016,22 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", - "synstructure", + "syn 2.0.16", ] [[package]] @@ -3954,9 +4045,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.4+zstd.1.5.4" +version = "6.0.5+zstd.1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7afb4b54b8910cf5447638cb54bf4e8a65cbedd783af98b98c62ffe91f185543" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" dependencies = [ "libc", "zstd-sys", @@ -3964,9 +4055,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", diff --git a/dnapi-rs/Cargo.toml b/dnapi-rs/Cargo.toml index c70fae4..f6408ed 100644 --- a/dnapi-rs/Cargo.toml +++ b/dnapi-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dnapi-rs" -version = "0.1.9" +version = "0.1.11" edition = "2021" description = "A rust client for the Defined Networking API" license = "AGPL-3.0-or-later" diff --git a/dnapi-rs/src/client_async.rs b/dnapi-rs/src/client_async.rs index 3badec2..a6ff422 100644 --- a/dnapi-rs/src/client_async.rs +++ b/dnapi-rs/src/client_async.rs @@ -1,17 +1,21 @@ //! Client structs to handle communication with the Defined Networking API. This is the async client API - if you want blocking instead, enable the blocking (or default) feature instead. -use std::error::Error; +use crate::credentials::{ed25519_public_keys_from_pem, Credentials}; +use crate::crypto::{new_keys, nonce}; +use crate::message::{ + CheckForUpdateResponseWrapper, DoUpdateRequest, DoUpdateResponse, EnrollRequest, + EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper, CHECK_FOR_UPDATE, DO_UPDATE, + ENDPOINT_V1, ENROLL_ENDPOINT, +}; +use base64::Engine; use chrono::Local; use log::{debug, error}; use reqwest::StatusCode; -use url::Url; +use serde::{Deserialize, Serialize}; +use std::error::Error; use trifid_pki::cert::serialize_ed25519_public; use trifid_pki::ed25519_dalek::{Signature, Signer, SigningKey, Verifier}; -use crate::credentials::{Credentials, ed25519_public_keys_from_pem}; -use crate::crypto::{new_keys, nonce}; -use crate::message::{CHECK_FOR_UPDATE, CheckForUpdateResponseWrapper, DO_UPDATE, DoUpdateRequest, DoUpdateResponse, ENDPOINT_V1, ENROLL_ENDPOINT, EnrollRequest, EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper}; -use serde::{Serialize, Deserialize}; -use base64::Engine; +use url::Url; /// A type alias to abstract return types pub type NebulaConfig = Vec; @@ -22,7 +26,7 @@ pub type DHPrivateKeyPEM = Vec; /// A combination of persistent data and HTTP client used for communicating with the API. pub struct Client { http_client: reqwest::Client, - server_url: Url + server_url: Url, } #[derive(Serialize, Deserialize, Clone)] @@ -31,7 +35,7 @@ pub struct EnrollMeta { /// The server organization ID this node is now a member of pub organization_id: String, /// The server organization name this node is now a member of - pub organization_name: String + pub organization_name: String, } impl Client { @@ -42,7 +46,7 @@ impl Client { let client = reqwest::Client::builder().user_agent(user_agent).build()?; Ok(Self { http_client: client, - server_url: api_base + server_url: api_base, }) } @@ -59,8 +63,14 @@ impl Client { /// - the server returns an error /// - the server returns invalid JSON /// - the `trusted_keys` field is invalid - pub async fn enroll(&self, code: &str) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials, EnrollMeta), Box> { - debug!("making enrollment request to API {{server: {}, code: {}}}", self.server_url, code); + pub async fn enroll( + &self, + code: &str, + ) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials, EnrollMeta), Box> { + debug!( + "making enrollment request to API {{server: {}, code: {}}}", + self.server_url, code + ); let (dh_pubkey_pem, dh_privkey_pem, ed_pubkey, ed_privkey) = new_keys(); @@ -71,9 +81,19 @@ impl Client { timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(), })?; - let resp = self.http_client.post(self.server_url.join(ENROLL_ENDPOINT)?).body(req_json).send().await?; + let resp = self + .http_client + .post(self.server_url.join(ENROLL_ENDPOINT)?) + .body(req_json) + .header("Content-Type", "application/json") + .send() + .await?; - let req_id = resp.headers().get("X-Request-ID").ok_or("Response missing X-Request-ID")?.to_str()?; + let req_id = resp + .headers() + .get("X-Request-ID") + .ok_or("Response missing X-Request-ID")? + .to_str()?; debug!("enrollment request complete {{req_id: {}}}", req_id); let resp: EnrollResponse = resp.json().await?; @@ -107,7 +127,15 @@ impl Client { /// # Errors /// This function returns an error if the dnclient request fails, or the server returns invalid data. pub async fn check_for_update(&self, creds: &Credentials) -> Result> { - let body = self.post_dnclient(CHECK_FOR_UPDATE, &[], &creds.host_id, creds.counter, &creds.ed_privkey).await?; + let body = self + .post_dnclient( + CHECK_FOR_UPDATE, + &[], + &creds.host_id, + creds.counter, + &creds.ed_privkey, + ) + .await?; let result: CheckForUpdateResponseWrapper = serde_json::from_slice(&body)?; @@ -125,7 +153,10 @@ impl Client { /// - if the response could not be deserialized /// - if the signature is invalid /// - if the keys are invalid - pub async fn do_update(&self, creds: &Credentials) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials), Box> { + pub async fn do_update( + &self, + creds: &Credentials, + ) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials), Box> { let (dh_pubkey_pem, dh_privkey_pem, ed_pubkey, ed_privkey) = new_keys(); let update_keys = DoUpdateRequest { @@ -136,28 +167,45 @@ impl Client { let update_keys_blob = serde_json::to_vec(&update_keys)?; - let resp = self.post_dnclient(DO_UPDATE, &update_keys_blob, &creds.host_id, creds.counter, &creds.ed_privkey).await?; + let resp = self + .post_dnclient( + DO_UPDATE, + &update_keys_blob, + &creds.host_id, + creds.counter, + &creds.ed_privkey, + ) + .await?; let result_wrapper: SignedResponseWrapper = serde_json::from_slice(&resp)?; let mut valid = false; for ca_pubkey in &creds.trusted_keys { - if ca_pubkey.verify(&result_wrapper.data.message, &Signature::from_slice(&result_wrapper.data.signature)?).is_ok() { + if ca_pubkey + .verify( + &result_wrapper.data.message, + &Signature::from_slice(&result_wrapper.data.signature)?, + ) + .is_ok() + { valid = true; break; } } if !valid { - return Err("Failed to verify signed API result".into()) + return Err("Failed to verify signed API result".into()); } let result: DoUpdateResponse = serde_json::from_slice(&result_wrapper.data.message)?; if result.nonce != update_keys.nonce { - error!("nonce mismatch between request {:x?} and response {:x?}", result.nonce, update_keys.nonce); - return Err("nonce mismatch between request and response".into()) + error!( + "nonce mismatch between request {:x?} and response {:x?}", + result.nonce, update_keys.nonce + ); + return Err("nonce mismatch between request and response".into()); } let trusted_keys = ed25519_public_keys_from_pem(&result.trusted_keys)?; @@ -179,7 +227,14 @@ impl Client { /// - serialization in any step fails /// - if the `server_url` is invalid /// - if the request could not be sent - pub async fn post_dnclient(&self, req_type: &str, value: &[u8], host_id: &str, counter: u32, ed_privkey: &SigningKey) -> Result, Box> { + pub async fn post_dnclient( + &self, + req_type: &str, + value: &[u8], + host_id: &str, + counter: u32, + ed_privkey: &SigningKey, + ) -> Result, Box> { let encoded_msg = serde_json::to_string(&RequestWrapper { message_type: req_type.to_string(), value: value.to_vec(), @@ -203,19 +258,23 @@ impl Client { let post_body = serde_json::to_string(&body)?; - let resp = self.http_client.post(self.server_url.join(ENDPOINT_V1)?).body(post_body).send().await?; + let resp = self + .http_client + .post(self.server_url.join(ENDPOINT_V1)?) + .body(post_body) + .send() + .await?; match resp.status() { - StatusCode::OK => { - Ok(resp.bytes().await?.to_vec()) - }, - StatusCode::FORBIDDEN => { - Err("Forbidden".into()) - }, + StatusCode::OK => Ok(resp.bytes().await?.to_vec()), + StatusCode::FORBIDDEN => Err("Forbidden".into()), _ => { - error!("dnclient endpoint returned bad status code {}", resp.status()); + error!( + "dnclient endpoint returned bad status code {}", + resp.status() + ); Err("dnclient endpoint returned error".into()) } } } -} \ No newline at end of file +} diff --git a/dnapi-rs/src/client_blocking.rs b/dnapi-rs/src/client_blocking.rs index bd932e9..a823dec 100644 --- a/dnapi-rs/src/client_blocking.rs +++ b/dnapi-rs/src/client_blocking.rs @@ -1,17 +1,21 @@ //! Client structs to handle communication with the Defined Networking API. This is the blocking client API - if you want async instead, set no-default-features and enable the async feature instead. -use std::error::Error; +use crate::credentials::{ed25519_public_keys_from_pem, Credentials}; +use crate::crypto::{new_keys, nonce}; +use crate::message::{ + CheckForUpdateResponseWrapper, DoUpdateRequest, DoUpdateResponse, EnrollRequest, + EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper, CHECK_FOR_UPDATE, DO_UPDATE, + ENDPOINT_V1, ENROLL_ENDPOINT, +}; use base64::Engine; use chrono::Local; use log::{debug, error, trace}; use reqwest::StatusCode; -use url::Url; +use serde::{Deserialize, Serialize}; +use std::error::Error; use trifid_pki::cert::serialize_ed25519_public; use trifid_pki::ed25519_dalek::{Signature, Signer, SigningKey, Verifier}; -use crate::credentials::{Credentials, ed25519_public_keys_from_pem}; -use crate::crypto::{new_keys, nonce}; -use crate::message::{CHECK_FOR_UPDATE, CheckForUpdateResponseWrapper, DO_UPDATE, DoUpdateRequest, DoUpdateResponse, ENDPOINT_V1, ENROLL_ENDPOINT, EnrollRequest, EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper}; -use serde::{Serialize, Deserialize}; +use url::Url; /// A type alias to abstract return types pub type NebulaConfig = Vec; @@ -22,7 +26,7 @@ pub type DHPrivateKeyPEM = Vec; /// A combination of persistent data and HTTP client used for communicating with the API. pub struct Client { http_client: reqwest::blocking::Client, - server_url: Url + server_url: Url, } #[derive(Serialize, Deserialize, Clone)] @@ -31,7 +35,7 @@ pub struct EnrollMeta { /// The server organization ID this node is now a member of pub organization_id: String, /// The server organization name this node is now a member of - pub organization_name: String + pub organization_name: String, } impl Client { @@ -39,10 +43,12 @@ impl Client { /// # Errors /// This function will return an error if the reqwest Client could not be created. pub fn new(user_agent: String, api_base: Url) -> Result> { - let client = reqwest::blocking::Client::builder().user_agent(user_agent).build()?; + let client = reqwest::blocking::Client::builder() + .user_agent(user_agent) + .build()?; Ok(Self { http_client: client, - server_url: api_base + server_url: api_base, }) } @@ -59,8 +65,14 @@ impl Client { /// - the server returns an error /// - the server returns invalid JSON /// - the `trusted_keys` field is invalid - pub fn enroll(&self, code: &str) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials, EnrollMeta), Box> { - debug!("making enrollment request to API {{server: {}, code: {}}}", self.server_url, code); + pub fn enroll( + &self, + code: &str, + ) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials, EnrollMeta), Box> { + debug!( + "making enrollment request to API {{server: {}, code: {}}}", + self.server_url, code + ); let (dh_pubkey_pem, dh_privkey_pem, ed_pubkey, ed_privkey) = new_keys(); @@ -71,9 +83,18 @@ impl Client { timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(), })?; - let resp = self.http_client.post(self.server_url.join(ENROLL_ENDPOINT)?).body(req_json).send()?; + let resp = self + .http_client + .post(self.server_url.join(ENROLL_ENDPOINT)?) + .header("Content-Type", "application/json") + .body(req_json) + .send()?; - let req_id = resp.headers().get("X-Request-ID").ok_or("Response missing X-Request-ID")?.to_str()?; + let req_id = resp + .headers() + .get("X-Request-ID") + .ok_or("Response missing X-Request-ID")? + .to_str()?; debug!("enrollment request complete {{req_id: {}}}", req_id); let resp: EnrollResponse = resp.json()?; @@ -93,7 +114,6 @@ impl Client { debug!("parsing public keys"); - let trusted_keys = ed25519_public_keys_from_pem(&r.trusted_keys)?; let creds = Credentials { @@ -110,7 +130,13 @@ impl Client { /// # Errors /// This function returns an error if the dnclient request fails, or the server returns invalid data. pub fn check_for_update(&self, creds: &Credentials) -> Result> { - let body = self.post_dnclient(CHECK_FOR_UPDATE, &[], &creds.host_id, creds.counter, &creds.ed_privkey)?; + let body = self.post_dnclient( + CHECK_FOR_UPDATE, + &[], + &creds.host_id, + creds.counter, + &creds.ed_privkey, + )?; let result: CheckForUpdateResponseWrapper = serde_json::from_slice(&body)?; @@ -128,7 +154,10 @@ impl Client { /// - if the response could not be deserialized /// - if the signature is invalid /// - if the keys are invalid - pub fn do_update(&self, creds: &Credentials) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials), Box> { + pub fn do_update( + &self, + creds: &Credentials, + ) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials), Box> { let (dh_pubkey_pem, dh_privkey_pem, ed_pubkey, ed_privkey) = new_keys(); let update_keys = DoUpdateRequest { @@ -139,33 +168,51 @@ impl Client { let update_keys_blob = serde_json::to_vec(&update_keys)?; - let resp = self.post_dnclient(DO_UPDATE, &update_keys_blob, &creds.host_id, creds.counter, &creds.ed_privkey)?; + let resp = self.post_dnclient( + DO_UPDATE, + &update_keys_blob, + &creds.host_id, + creds.counter, + &creds.ed_privkey, + )?; let result_wrapper: SignedResponseWrapper = serde_json::from_slice(&resp)?; let mut valid = false; for ca_pubkey in &creds.trusted_keys { - if ca_pubkey.verify(&result_wrapper.data.message, &Signature::from_slice(&result_wrapper.data.signature)?).is_ok() { + if ca_pubkey + .verify( + &result_wrapper.data.message, + &Signature::from_slice(&result_wrapper.data.signature)?, + ) + .is_ok() + { valid = true; break; } } if !valid { - return Err("Failed to verify signed API result".into()) + return Err("Failed to verify signed API result".into()); } let result: DoUpdateResponse = serde_json::from_slice(&result_wrapper.data.message)?; if result.nonce != update_keys.nonce { - error!("nonce mismatch between request {:x?} and response {:x?}", result.nonce, update_keys.nonce); - return Err("nonce mismatch between request and response".into()) + error!( + "nonce mismatch between request {:x?} and response {:x?}", + result.nonce, update_keys.nonce + ); + return Err("nonce mismatch between request and response".into()); } if result.counter <= creds.counter { - error!("counter in request {} should be less than counter in response {}", creds.counter, result.counter); - return Err("received older config than what we already had".into()) + error!( + "counter in request {} should be less than counter in response {}", + creds.counter, result.counter + ); + return Err("received older config than what we already had".into()); } let trusted_keys = ed25519_public_keys_from_pem(&result.trusted_keys)?; @@ -187,7 +234,14 @@ impl Client { /// - serialization in any step fails /// - if the `server_url` is invalid /// - if the request could not be sent - pub fn post_dnclient(&self, req_type: &str, value: &[u8], host_id: &str, counter: u32, ed_privkey: &SigningKey) -> Result, Box> { + pub fn post_dnclient( + &self, + req_type: &str, + value: &[u8], + host_id: &str, + counter: u32, + ed_privkey: &SigningKey, + ) -> Result, Box> { let encoded_msg = serde_json::to_string(&RequestWrapper { message_type: req_type.to_string(), value: value.to_vec(), @@ -213,19 +267,22 @@ impl Client { trace!("sending dnclient request {}", post_body); - let resp = self.http_client.post(self.server_url.join(ENDPOINT_V1)?).body(post_body).send()?; + let resp = self + .http_client + .post(self.server_url.join(ENDPOINT_V1)?) + .body(post_body) + .send()?; match resp.status() { - StatusCode::OK => { - Ok(resp.bytes()?.to_vec()) - }, - StatusCode::FORBIDDEN => { - Err("Forbidden".into()) - }, + StatusCode::OK => Ok(resp.bytes()?.to_vec()), + StatusCode::FORBIDDEN => Err("Forbidden".into()), _ => { - error!("dnclient endpoint returned bad status code {}", resp.status()); + error!( + "dnclient endpoint returned bad status code {}", + resp.status() + ); Err("dnclient endpoint returned error".into()) } } } -} \ No newline at end of file +} diff --git a/dnapi-rs/src/credentials.rs b/dnapi-rs/src/credentials.rs index 01a0b3d..daa412b 100644 --- a/dnapi-rs/src/credentials.rs +++ b/dnapi-rs/src/credentials.rs @@ -1,9 +1,9 @@ //! Contains the `Credentials` struct, which contains all keys, IDs, organizations and other identity-related and security-related data that is persistent in a `Client` +use serde::{Deserialize, Serialize}; use std::error::Error; use trifid_pki::cert::{deserialize_ed25519_public_many, serialize_ed25519_public}; use trifid_pki::ed25519_dalek::{SigningKey, VerifyingKey}; -use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Clone)] /// Contains information necessary to make requests against the `DNClient` API. @@ -15,7 +15,7 @@ pub struct Credentials { /// The counter used in the other API requests. It is unknown what the purpose of this is, but the original client persists it and it is needed for API calls. pub counter: u32, /// The set of trusted ed25519 keys that may be used by the API to sign API responses. - pub trusted_keys: Vec + pub trusted_keys: Vec, } /// Converts an array of `VerifyingKey`s to a singular bundle of PEM-encoded keys @@ -38,8 +38,10 @@ pub fn ed25519_public_keys_from_pem(pem: &[u8]) -> Result, Box #[allow(clippy::unwrap_used)] for pem in pems { - keys.push(VerifyingKey::from_bytes(&pem.try_into().unwrap_or_else(|_| unreachable!()))?); + keys.push(VerifyingKey::from_bytes( + &pem.try_into().unwrap_or_else(|_| unreachable!()), + )?); } Ok(keys) -} \ No newline at end of file +} diff --git a/dnapi-rs/src/crypto.rs b/dnapi-rs/src/crypto.rs index 26e47c8..498cd40 100644 --- a/dnapi-rs/src/crypto.rs +++ b/dnapi-rs/src/crypto.rs @@ -1,7 +1,7 @@ //! Functions for generating keys and nonces for use in API calls -use rand::Rng; use rand::rngs::OsRng; +use rand::Rng; use trifid_pki::cert::{serialize_x25519_private, serialize_x25519_public}; use trifid_pki::ed25519_dalek::{SigningKey, VerifyingKey}; use trifid_pki::x25519_dalek::{PublicKey, StaticSecret}; @@ -38,4 +38,4 @@ pub fn new_ed25519_keypair() -> (VerifyingKey, SigningKey) { /// Generates a 16-byte random nonce for use in API calls pub fn nonce() -> [u8; 16] { rand::thread_rng().gen() -} \ No newline at end of file +} diff --git a/dnapi-rs/src/lib.rs b/dnapi-rs/src/lib.rs index b9b63d8..a32301e 100644 --- a/dnapi-rs/src/lib.rs +++ b/dnapi-rs/src/lib.rs @@ -17,8 +17,8 @@ pub mod message; -pub mod client_blocking; pub mod client_async; +pub mod client_blocking; pub mod credentials; -pub mod crypto; \ No newline at end of file +pub mod crypto; diff --git a/dnapi-rs/src/message.rs b/dnapi-rs/src/message.rs index d1360ed..ccaabd1 100644 --- a/dnapi-rs/src/message.rs +++ b/dnapi-rs/src/message.rs @@ -1,7 +1,7 @@ //! Models for interacting with the Defined Networking API. use base64_serde::base64_serde_type; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; /// The version 1 `DNClient` API endpoint pub const ENDPOINT_V1: &str = "/v1/dnclient"; @@ -27,7 +27,7 @@ pub struct RequestV1 { pub message: String, #[serde(with = "Base64Standard")] /// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key - pub signature: Vec + pub signature: Vec, } #[derive(Serialize, Deserialize)] @@ -45,14 +45,14 @@ pub struct RequestWrapper { /// For example: /// `2023-03-29T09:56:42.380006369-04:00` /// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4` - pub timestamp: String + pub timestamp: String, } #[derive(Serialize, Deserialize)] /// `SignedResponseWrapper` contains a response message and a signature to validate inside `data`. pub struct SignedResponseWrapper { /// The response data contained in this message - pub data: SignedResponse + pub data: SignedResponse, } #[derive(Serialize, Deserialize)] @@ -65,14 +65,14 @@ pub struct SignedResponse { pub message: Vec, #[serde(with = "Base64Standard")] /// The ed25519 signature over the `message` - pub signature: Vec + pub signature: Vec, } #[derive(Serialize, Deserialize)] /// `CheckForUpdateResponseWrapper` contains a response to `CheckForUpdate` inside "data." pub struct CheckForUpdateResponseWrapper { /// The response data contained in this message - pub data: CheckForUpdateResponse + pub data: CheckForUpdateResponse, } #[derive(Serialize, Deserialize)] @@ -80,7 +80,7 @@ pub struct CheckForUpdateResponseWrapper { pub struct CheckForUpdateResponse { #[serde(rename = "updateAvailable")] /// Set to true if a config update is available - pub update_available: bool + pub update_available: bool, } #[derive(Serialize, Deserialize)] @@ -97,7 +97,7 @@ pub struct DoUpdateRequest { #[serde(with = "Base64Standard")] /// A randomized value used to uniquely identify this request. /// The original client uses a randomized, 16-byte value here, which dnapi-rs replicates - pub nonce: Vec + pub nonce: Vec, } #[derive(Serialize, Deserialize)] @@ -114,13 +114,13 @@ pub struct DoUpdateResponse { #[serde(rename = "trustedKeys")] #[serde(with = "Base64Standard")] /// A new set of trusted ed25519 keys that can be used by the server to sign messages. - pub trusted_keys: Vec + pub trusted_keys: Vec, } /// The REST enrollment endpoint pub const ENROLL_ENDPOINT: &str = "/v2/enroll"; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] /// `EnrollRequest` is issued to the `ENROLL_ENDPOINT` to enroll this `dnclient` with a dnapi organization pub struct EnrollRequest { /// The enrollment code given by the API server. @@ -138,10 +138,9 @@ pub struct EnrollRequest { /// For example: /// `2023-03-29T09:56:42.380006369-04:00` /// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4` - pub timestamp: String + pub timestamp: String, } - #[derive(Serialize, Deserialize)] #[serde(untagged)] /// The response to an `EnrollRequest` @@ -149,13 +148,13 @@ pub enum EnrollResponse { /// A successful enrollment, with a `data` field pointing to an `EnrollResponseData` Success { /// The response data from this response - data: EnrollResponseData + data: EnrollResponseData, }, /// An unsuccessful enrollment, with an `errors` field pointing to an array of `APIError`s. Error { /// A list of `APIError`s that happened while trying to enroll. `APIErrors` is a type alias to `Vec` - errors: APIErrors - } + errors: APIErrors, + }, } #[derive(Serialize, Deserialize)] @@ -174,7 +173,7 @@ pub struct EnrollResponseData { /// A new set of trusted ed25519 keys that can be used by the server to sign messages. pub trusted_keys: Vec, /// The organization data that this node is now a part of - pub organization: EnrollResponseDataOrg + pub organization: EnrollResponseDataOrg, } #[derive(Serialize, Deserialize)] @@ -183,7 +182,7 @@ pub struct EnrollResponseDataOrg { /// The organization ID that this node is now a part of pub id: String, /// The name of the organization that this node is now a part of - pub name: String + pub name: String, } #[derive(Serialize, Deserialize)] @@ -194,8 +193,8 @@ pub struct APIError { /// The human-readable error message pub message: String, /// An optional path to where the error occured - pub path: Option + pub path: Option, } /// A type alias to a array of `APIErrors`. Just for parity with dnapi. -pub type APIErrors = Vec; \ No newline at end of file +pub type APIErrors = Vec; diff --git a/tfclient/Cargo.toml b/tfclient/Cargo.toml index ee5171c..6cbb715 100644 --- a/tfclient/Cargo.toml +++ b/tfclient/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tfclient" -version = "0.1.7" +version = "0.1.8" edition = "2021" description = "An open-source reimplementation of a Defined Networking-compatible client" license = "GPL-3.0-or-later" diff --git a/tfclient/build.rs b/tfclient/build.rs index 78f87ed..b10ea90 100644 --- a/tfclient/build.rs +++ b/tfclient/build.rs @@ -1,19 +1,18 @@ +use flate2::read::GzDecoder; +use reqwest::blocking::Response; +use reqwest::header::HeaderMap; use std::fs; -use std::fs::{File, remove_file}; +use std::fs::{remove_file, File}; use std::io::{Read, Write}; use std::os::unix::fs::PermissionsExt; use std::path::Path; use std::process::{Command, Output}; -use flate2::read::GzDecoder; -use reqwest::blocking::Response; -use reqwest::header::HeaderMap; use tar::Archive; - #[derive(serde::Deserialize, Debug)] struct GithubRelease { name: String, - assets: Vec + assets: Vec, } #[derive(serde::Deserialize, Debug)] @@ -23,11 +22,18 @@ struct GithubUser {} struct GithubReleaseAsset { browser_download_url: String, name: String, - size: i64 + size: i64, } fn main() { - if Path::new(&format!("{}/{}", std::env::var("OUT_DIR").unwrap(), "noredownload")).exists() && std::env::var("TFBUILD_FORCE_REDOWNLOAD").is_err() { + if Path::new(&format!( + "{}/{}", + std::env::var("OUT_DIR").unwrap(), + "noredownload" + )) + .exists() + && std::env::var("TFBUILD_FORCE_REDOWNLOAD").is_err() + { println!("noredownload exists and TFBUILD_FORCE_REDOWNLOAD is not set. Not redoing build process."); return; } @@ -38,15 +44,32 @@ fn main() { let mut has_api_key = false; if let Ok(api_key) = std::env::var("GH_API_KEY") { - headers.insert("Authorization", format!("Bearer {}", api_key).parse().unwrap()); + headers.insert( + "Authorization", + format!("Bearer {}", api_key).parse().unwrap(), + ); has_api_key = true; } - let client = reqwest::blocking::Client::builder().user_agent("curl/7.57.1").default_headers(headers).build().unwrap(); + let client = reqwest::blocking::Client::builder() + .user_agent("curl/7.57.1") + .default_headers(headers) + .build() + .unwrap(); - let resp: Response = client.get("https://api.github.com/repos/slackhq/nebula/releases/latest").send().unwrap(); + let resp: Response = client + .get("https://api.github.com/repos/slackhq/nebula/releases/latest") + .send() + .unwrap(); - if resp.headers().get("X-Ratelimit-Remaining").unwrap().to_str().unwrap() == "0" { + if resp + .headers() + .get("X-Ratelimit-Remaining") + .unwrap() + .to_str() + .unwrap() + == "0" + { println!("You've been ratelimited from the GitHub API. Wait a while (1 hour)"); if !has_api_key { println!("You can also set a GitHub API key with the environment variable GH_API_KEY, which will increase your ratelimit ( a lot )"); @@ -54,7 +77,6 @@ fn main() { panic!("Ratelimited"); } - let release: GithubRelease = resp.json().unwrap(); println!("[*] Fetching target triplet..."); @@ -84,9 +106,16 @@ fn main() { println!("[*] Embedding {} {}", target_file, release.name); - let download = release.assets.iter().find(|r| r.name == format!("{}.tar.gz", target_file)).expect("That architecture isn't avaliable :("); + let download = release + .assets + .iter() + .find(|r| r.name == format!("{}.tar.gz", target_file)) + .expect("That architecture isn't avaliable :("); - println!("[*] Downloading {}.tar.gz ({}, {} bytes) from {}", target_file, target, download.size, download.browser_download_url); + println!( + "[*] Downloading {}.tar.gz ({}, {} bytes) from {}", + target_file, target, download.size, download.browser_download_url + ); let response = reqwest::blocking::get(&download.browser_download_url).unwrap(); let content = response.bytes().unwrap().to_vec(); @@ -102,10 +131,14 @@ fn main() { for entry in entries { let mut entry = entry.unwrap(); - if entry.path().unwrap() == Path::new("nebula") || entry.path().unwrap() == Path::new("nebula.exe") { + if entry.path().unwrap() == Path::new("nebula") + || entry.path().unwrap() == Path::new("nebula.exe") + { nebula_bin.reserve(entry.size() as usize); entry.read_to_end(&mut nebula_bin).unwrap(); - } else if entry.path().unwrap() == Path::new("nebula-cert") || entry.path().unwrap() == Path::new("nebula-cert.exe") { + } else if entry.path().unwrap() == Path::new("nebula-cert") + || entry.path().unwrap() == Path::new("nebula-cert.exe") + { nebula_cert_bin.reserve(entry.size() as usize); entry.read_to_end(&mut nebula_cert_bin).unwrap(); } else if entry.path().unwrap() == Path::new("SHASUM256.txt") { @@ -121,18 +154,28 @@ fn main() { panic!("[x] Release did not contain nebula_cert binary"); } - let mut nebula_file = File::create(format!("{}/nebula.bin", std::env::var("OUT_DIR").unwrap())).unwrap(); + let mut nebula_file = + File::create(format!("{}/nebula.bin", std::env::var("OUT_DIR").unwrap())).unwrap(); nebula_file.write_all(&nebula_bin).unwrap(); codegen_version(&nebula_bin, "nebula.bin", "NEBULA"); - let mut nebula_cert_file = File::create(format!("{}/nebula_cert.bin", std::env::var("OUT_DIR").unwrap())).unwrap(); + let mut nebula_cert_file = File::create(format!( + "{}/nebula_cert.bin", + std::env::var("OUT_DIR").unwrap() + )) + .unwrap(); nebula_cert_file.write_all(&nebula_cert_bin).unwrap(); codegen_version(&nebula_cert_bin, "nebula_cert.bin", "NEBULA_CERT"); // Indicate to cargo and ourselves that we have already downloaded and codegenned - File::create(format!("{}/{}", std::env::var("OUT_DIR").unwrap(), "noredownload")).unwrap(); + File::create(format!( + "{}/{}", + std::env::var("OUT_DIR").unwrap(), + "noredownload" + )) + .unwrap(); println!("cargo:rerun-if-changed=build.rs"); } @@ -149,7 +192,8 @@ fn codegen_version(bin: &[u8], fp: &str, name: &str) { let code = format!("// This code was automatically @generated by build.rs. It should not be modified.\npub const {}_BIN: &[u8] = include_bytes!(concat!(env!(\"OUT_DIR\"), \"/{}\"));\npub const {}_VERSION: &str = \"{}\";", name, fp, name, version); - let mut file = File::create(format!("{}/{}.rs", std::env::var("OUT_DIR").unwrap(), fp)).unwrap(); + let mut file = + File::create(format!("{}/{}.rs", std::env::var("OUT_DIR").unwrap(), fp)).unwrap(); file.write_all(code.as_bytes()).unwrap(); } diff --git a/tfclient/src/apiworker.rs b/tfclient/src/apiworker.rs index 4102499..ae5ef8d 100644 --- a/tfclient/src/apiworker.rs +++ b/tfclient/src/apiworker.rs @@ -1,13 +1,9 @@ use std::fs; -use std::sync::mpsc::{Receiver, RecvError, TryRecvError}; - +use std::sync::mpsc::Receiver; +use dnapi_rs::client_blocking::Client; use log::{error, info, warn}; use url::Url; -use dnapi_rs::client_blocking::Client; - - - use crate::config::{load_cdata, save_cdata, TFClientConfig}; use crate::daemon::ThreadMessageSender; @@ -18,10 +14,16 @@ pub enum APIWorkerMessage { Shutdown, Enroll { code: String }, Update, - Timer + Timer, } -pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx: ThreadMessageSender, rx: Receiver) { +pub fn apiworker_main( + _config: TFClientConfig, + instance: String, + url: String, + tx: ThreadMessageSender, + rx: Receiver, +) { let server = Url::parse(&url).unwrap(); let client = Client::new(format!("tfclient/{}", env!("CARGO_PKG_VERSION")), server).unwrap(); @@ -33,7 +35,7 @@ pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx APIWorkerMessage::Shutdown => { info!("recv on command socket: shutdown, stopping"); break; - }, + } APIWorkerMessage::Timer | APIWorkerMessage::Update => { info!("updating config"); let mut cdata = match load_cdata(&instance) { @@ -108,9 +110,13 @@ pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx }; cdata.creds = Some(creds); - cdata.dh_privkey = Some(dh_privkey.try_into().expect("32 != 32")); + cdata.dh_privkey = Some(dh_privkey); - match fs::write(get_nebulaconfig_file(&instance).expect("Unable to determine nebula config file location"), config) { + match fs::write( + get_nebulaconfig_file(&instance) + .expect("Unable to determine nebula config file location"), + config, + ) { Ok(_) => (), Err(e) => { error!("unable to save nebula config: {}", e); @@ -146,7 +152,7 @@ pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx return; } } - }, + } APIWorkerMessage::Enroll { code } => { info!("recv on command socket: enroll {}", code); let mut cdata = match load_cdata(&instance) { @@ -170,7 +176,11 @@ pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx } }; - match fs::write(get_nebulaconfig_file(&instance).expect("Unable to determine nebula config file location"), config) { + match fs::write( + get_nebulaconfig_file(&instance) + .expect("Unable to determine nebula config file location"), + config, + ) { Ok(_) => (), Err(e) => { error!("unable to save nebula config: {}", e); @@ -179,7 +189,7 @@ pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx } cdata.creds = Some(creds); - cdata.dh_privkey = Some(dh_privkey.try_into().expect("32 != 32")); + cdata.dh_privkey = Some(dh_privkey); cdata.meta = Some(meta); // Save vardata @@ -204,11 +214,11 @@ pub fn apiworker_main(_config: TFClientConfig, instance: String, url: String, tx } } } - }, + } Err(e) => { error!("error on command socket: {}", e); return; } } } -} \ No newline at end of file +} diff --git a/tfclient/src/config.rs b/tfclient/src/config.rs index 9fd2927..4f69c75 100644 --- a/tfclient/src/config.rs +++ b/tfclient/src/config.rs @@ -1,33 +1,34 @@ +use ipnet::{IpNet, Ipv4Net}; use std::collections::HashMap; use std::error::Error; use std::fs; use std::net::{Ipv4Addr, SocketAddrV4}; -use ipnet::{IpNet, Ipv4Net}; - -use log::{debug, info}; -use serde::{Deserialize, Serialize}; use dnapi_rs::client_blocking::EnrollMeta; use dnapi_rs::credentials::Credentials; +use log::{debug, info}; +use serde::{Deserialize, Serialize}; use crate::dirs::{get_cdata_dir, get_cdata_file, get_config_dir, get_config_file}; pub const DEFAULT_PORT: u16 = 8157; -fn default_port() -> u16 { DEFAULT_PORT } +fn default_port() -> u16 { + DEFAULT_PORT +} #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TFClientConfig { #[serde(default = "default_port")] pub listen_port: u16, #[serde(default = "bool_false")] - pub disable_automatic_config_updates: bool + pub disable_automatic_config_updates: bool, } #[derive(Serialize, Deserialize, Clone)] pub struct TFClientData { pub dh_privkey: Option>, pub creds: Option, - pub meta: Option + pub meta: Option, } pub fn create_config(instance: &str) -> Result<(), Box> { @@ -39,7 +40,10 @@ pub fn create_config(instance: &str) -> Result<(), Box> { disable_automatic_config_updates: false, }; let config_str = toml::to_string(&config)?; - fs::write(get_config_file(instance).ok_or("Unable to load config dir")?, config_str)?; + fs::write( + get_config_file(instance).ok_or("Unable to load config dir")?, + config_str, + )?; Ok(()) } @@ -63,9 +67,16 @@ pub fn create_cdata(instance: &str) -> Result<(), Box> { info!("Creating data directory..."); fs::create_dir_all(get_cdata_dir(instance).ok_or("Unable to load data dir")?)?; info!("Copying default data file to config directory..."); - let config = TFClientData { dh_privkey: None, creds: None, meta: None }; + let config = TFClientData { + dh_privkey: None, + creds: None, + meta: None, + }; let config_str = toml::to_string(&config)?; - fs::write(get_cdata_file(instance).ok_or("Unable to load data dir")?, config_str)?; + fs::write( + get_cdata_file(instance).ok_or("Unable to load data dir")?, + config_str, + )?; Ok(()) } @@ -141,7 +152,7 @@ pub struct NebulaConfig { #[serde(default = "none")] #[serde(skip_serializing_if = "is_none")] - pub local_range: Option + pub local_range: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -156,7 +167,7 @@ pub struct NebulaConfigPki { pub blocklist: Vec, #[serde(default = "bool_false")] #[serde(skip_serializing_if = "is_bool_false")] - pub disconnect_invalid: bool + pub disconnect_invalid: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -190,7 +201,7 @@ pub struct NebulaConfigLighthouseDns { pub host: String, #[serde(default = "u16_53")] #[serde(skip_serializing_if = "is_u16_53")] - pub port: u16 + pub port: u16, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -207,7 +218,7 @@ pub struct NebulaConfigListen { #[serde(skip_serializing_if = "is_none")] pub read_buffer: Option, #[serde(skip_serializing_if = "is_none")] - pub write_buffer: Option + pub write_buffer: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -220,7 +231,7 @@ pub struct NebulaConfigPunchy { pub respond: bool, #[serde(default = "string_1s")] #[serde(skip_serializing_if = "is_string_1s")] - pub delay: String + pub delay: String, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -228,7 +239,7 @@ pub enum NebulaConfigCipher { #[serde(rename = "aes")] Aes, #[serde(rename = "chachapoly")] - ChaChaPoly + ChaChaPoly, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -241,7 +252,7 @@ pub struct NebulaConfigRelay { pub am_relay: bool, #[serde(default = "bool_true")] #[serde(skip_serializing_if = "is_bool_true")] - pub use_relays: bool + pub use_relays: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -268,13 +279,13 @@ pub struct NebulaConfigTun { pub routes: Vec, #[serde(default = "empty_vec")] #[serde(skip_serializing_if = "is_empty_vec")] - pub unsafe_routes: Vec + pub unsafe_routes: Vec, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NebulaConfigTunRouteOverride { pub mtu: u64, - pub route: Ipv4Net + pub route: Ipv4Net, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -286,7 +297,7 @@ pub struct NebulaConfigTunUnsafeRoute { pub mtu: u64, #[serde(default = "i64_100")] #[serde(skip_serializing_if = "is_i64_100")] - pub metric: i64 + pub metric: i64, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -302,7 +313,7 @@ pub struct NebulaConfigLogging { pub disable_timestamp: bool, #[serde(default = "timestamp")] #[serde(skip_serializing_if = "is_timestamp")] - pub timestamp_format: String + pub timestamp_format: String, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -318,7 +329,7 @@ pub enum NebulaConfigLoggingLevel { #[serde(rename = "info")] Info, #[serde(rename = "debug")] - Debug + Debug, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -326,7 +337,7 @@ pub enum NebulaConfigLoggingFormat { #[serde(rename = "json")] Json, #[serde(rename = "text")] - Text + Text, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -338,7 +349,7 @@ pub struct NebulaConfigSshd { pub host_key: String, #[serde(default = "empty_vec")] #[serde(skip_serializing_if = "is_empty_vec")] - pub authorized_users: Vec + pub authorized_users: Vec, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -346,7 +357,7 @@ pub struct NebulaConfigSshdAuthorizedUser { pub user: String, #[serde(default = "empty_vec")] #[serde(skip_serializing_if = "is_empty_vec")] - pub keys: Vec + pub keys: Vec, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -355,7 +366,7 @@ pub enum NebulaConfigStats { #[serde(rename = "graphite")] Graphite(NebulaConfigStatsGraphite), #[serde(rename = "prometheus")] - Prometheus(NebulaConfigStatsPrometheus) + Prometheus(NebulaConfigStatsPrometheus), } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -373,7 +384,7 @@ pub struct NebulaConfigStatsGraphite { pub message_metrics: bool, #[serde(default = "bool_false")] #[serde(skip_serializing_if = "is_bool_false")] - pub lighthouse_metrics: bool + pub lighthouse_metrics: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -381,7 +392,7 @@ pub enum NebulaConfigStatsGraphiteProtocol { #[serde(rename = "tcp")] Tcp, #[serde(rename = "udp")] - Udp + Udp, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -400,7 +411,7 @@ pub struct NebulaConfigStatsPrometheus { pub message_metrics: bool, #[serde(default = "bool_false")] #[serde(skip_serializing_if = "is_bool_false")] - pub lighthouse_metrics: bool + pub lighthouse_metrics: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -428,7 +439,7 @@ pub struct NebulaConfigFirewallConntrack { pub udp_timeout: String, #[serde(default = "string_10m")] #[serde(skip_serializing_if = "is_string_10m")] - pub default_timeout: String + pub default_timeout: String, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -456,82 +467,175 @@ pub struct NebulaConfigFirewallRule { pub groups: Option>, #[serde(default = "none")] #[serde(skip_serializing_if = "is_none")] - pub cidr: Option + pub cidr: Option, } // Default values for serde -fn string_12m() -> String { "12m".to_string() } -fn is_string_12m(s: &str) -> bool { s == "12m" } +fn string_12m() -> String { + "12m".to_string() +} +fn is_string_12m(s: &str) -> bool { + s == "12m" +} -fn string_3m() -> String { "3m".to_string() } -fn is_string_3m(s: &str) -> bool { s == "3m" } +fn string_3m() -> String { + "3m".to_string() +} +fn is_string_3m(s: &str) -> bool { + s == "3m" +} -fn string_10m() -> String { "10m".to_string() } -fn is_string_10m(s: &str) -> bool { s == "10m" } +fn string_10m() -> String { + "10m".to_string() +} +fn is_string_10m(s: &str) -> bool { + s == "10m" +} -fn empty_vec() -> Vec { vec![] } -fn is_empty_vec(v: &Vec) -> bool { v.is_empty() } +fn empty_vec() -> Vec { + vec![] +} +fn is_empty_vec(v: &Vec) -> bool { + v.is_empty() +} -fn empty_hashmap() -> HashMap { HashMap::new() } -fn is_empty_hashmap(h: &HashMap) -> bool { h.is_empty() } +fn empty_hashmap() -> HashMap { + HashMap::new() +} +fn is_empty_hashmap(h: &HashMap) -> bool { + h.is_empty() +} -fn bool_false() -> bool { false } -fn is_bool_false(b: &bool) -> bool { !*b } +fn bool_false() -> bool { + false +} +fn is_bool_false(b: &bool) -> bool { + !*b +} -fn bool_true() -> bool { true } -fn is_bool_true(b: &bool) -> bool { *b } +fn bool_true() -> bool { + true +} +fn is_bool_true(b: &bool) -> bool { + *b +} -fn u16_53() -> u16 { 53 } -fn is_u16_53(u: &u16) -> bool { *u == 53 } +fn u16_53() -> u16 { + 53 +} +fn is_u16_53(u: &u16) -> bool { + *u == 53 +} -fn u32_10() -> u32 { 10 } -fn is_u32_10(u: &u32) -> bool { *u == 10 } +fn u32_10() -> u32 { + 10 +} +fn is_u32_10(u: &u32) -> bool { + *u == 10 +} -fn ipv4_0000() -> Ipv4Addr { Ipv4Addr::new(0, 0, 0, 0) } -fn is_ipv4_0000(i: &Ipv4Addr) -> bool { *i == ipv4_0000() } +fn u16_0() -> u16 { + 0 +} +fn is_u16_0(u: &u16) -> bool { + *u == 0 +} -fn u16_0() -> u16 { 0 } -fn is_u16_0(u: &u16) -> bool { *u == 0 } +fn u32_64() -> u32 { + 64 +} +fn is_u32_64(u: &u32) -> bool { + *u == 64 +} -fn u32_64() -> u32 { 64 } -fn is_u32_64(u: &u32) -> bool { *u == 64 } +fn string_1s() -> String { + "1s".to_string() +} +fn is_string_1s(s: &str) -> bool { + s == "1s" +} -fn string_1s() -> String { "1s".to_string() } -fn is_string_1s(s: &str) -> bool { s == "1s" } +fn cipher_aes() -> NebulaConfigCipher { + NebulaConfigCipher::Aes +} +fn is_cipher_aes(c: &NebulaConfigCipher) -> bool { + matches!(c, NebulaConfigCipher::Aes) +} -fn cipher_aes() -> NebulaConfigCipher { NebulaConfigCipher::Aes } -fn is_cipher_aes(c: &NebulaConfigCipher) -> bool { matches!(c, NebulaConfigCipher::Aes) } +fn u64_500() -> u64 { + 500 +} +fn is_u64_500(u: &u64) -> bool { + *u == 500 +} -fn u64_500() -> u64 { 500 } -fn is_u64_500(u: &u64) -> bool { *u == 500 } +fn u64_1300() -> u64 { + 1300 +} +fn is_u64_1300(u: &u64) -> bool { + *u == 1300 +} -fn u64_1300() -> u64 { 1300 } -fn is_u64_1300(u: &u64) -> bool { *u == 1300 } +fn i64_100() -> i64 { + 100 +} +fn is_i64_100(i: &i64) -> bool { + *i == 100 +} -fn i64_100() -> i64 { 100 } -fn is_i64_100(i: &i64) -> bool { *i == 100 } +fn loglevel_info() -> NebulaConfigLoggingLevel { + NebulaConfigLoggingLevel::Info +} +fn is_loglevel_info(l: &NebulaConfigLoggingLevel) -> bool { + matches!(l, NebulaConfigLoggingLevel::Info) +} -fn loglevel_info() -> NebulaConfigLoggingLevel { NebulaConfigLoggingLevel::Info } -fn is_loglevel_info(l: &NebulaConfigLoggingLevel) -> bool { matches!(l, NebulaConfigLoggingLevel::Info) } +fn format_text() -> NebulaConfigLoggingFormat { + NebulaConfigLoggingFormat::Text +} +fn is_format_text(f: &NebulaConfigLoggingFormat) -> bool { + matches!(f, NebulaConfigLoggingFormat::Text) +} -fn format_text() -> NebulaConfigLoggingFormat { NebulaConfigLoggingFormat::Text } -fn is_format_text(f: &NebulaConfigLoggingFormat) -> bool { matches!(f, NebulaConfigLoggingFormat::Text) } +fn timestamp() -> String { + "2006-01-02T15:04:05Z07:00".to_string() +} +fn is_timestamp(s: &str) -> bool { + s == "2006-01-02T15:04:05Z07:00" +} -fn timestamp() -> String { "2006-01-02T15:04:05Z07:00".to_string() } -fn is_timestamp(s: &str) -> bool { s == "2006-01-02T15:04:05Z07:00" } +fn u64_1() -> u64 { + 1 +} +fn is_u64_1(u: &u64) -> bool { + *u == 1 +} -fn u64_1() -> u64 { 1 } -fn is_u64_1(u: &u64) -> bool { *u == 1 } +fn string_nebula() -> String { + "nebula".to_string() +} +fn is_string_nebula(s: &str) -> bool { + s == "nebula" +} -fn string_nebula() -> String { "nebula".to_string() } -fn is_string_nebula(s: &str) -> bool { s == "nebula" } +fn string_empty() -> String { + String::new() +} +fn is_string_empty(s: &str) -> bool { + s.is_empty() +} -fn string_empty() -> String { String::new() } -fn is_string_empty(s: &str) -> bool { s == "" } +fn protocol_tcp() -> NebulaConfigStatsGraphiteProtocol { + NebulaConfigStatsGraphiteProtocol::Tcp +} +fn is_protocol_tcp(p: &NebulaConfigStatsGraphiteProtocol) -> bool { + matches!(p, NebulaConfigStatsGraphiteProtocol::Tcp) +} -fn protocol_tcp() -> NebulaConfigStatsGraphiteProtocol { NebulaConfigStatsGraphiteProtocol::Tcp } -fn is_protocol_tcp(p: &NebulaConfigStatsGraphiteProtocol) -> bool { matches!(p, NebulaConfigStatsGraphiteProtocol::Tcp) } - -fn none() -> Option { None } -fn is_none(o: &Option) -> bool { o.is_none() } \ No newline at end of file +fn none() -> Option { + None +} +fn is_none(o: &Option) -> bool { + o.is_none() +} diff --git a/tfclient/src/daemon.rs b/tfclient/src/daemon.rs index 6c9e2c2..b39aed8 100644 --- a/tfclient/src/daemon.rs +++ b/tfclient/src/daemon.rs @@ -1,7 +1,7 @@ +use log::{error, info}; use std::sync::mpsc; use std::sync::mpsc::Sender; use std::thread; -use log::{error, info}; use crate::apiworker::{apiworker_main, APIWorkerMessage}; use crate::config::load_config; @@ -44,28 +44,49 @@ pub fn daemon_main(name: String, server: String) { match ctrlc::set_handler(move || { info!("Ctrl-C detected. Stopping threads..."); - match mainthread_transmitter.nebula_thread.send(NebulaWorkerMessage::Shutdown) { + match mainthread_transmitter + .nebula_thread + .send(NebulaWorkerMessage::Shutdown) + { Ok(_) => (), Err(e) => { - error!("Error sending shutdown message to nebula worker thread: {}", e); + error!( + "Error sending shutdown message to nebula worker thread: {}", + e + ); } } - match mainthread_transmitter.api_thread.send(APIWorkerMessage::Shutdown) { + match mainthread_transmitter + .api_thread + .send(APIWorkerMessage::Shutdown) + { Ok(_) => (), Err(e) => { error!("Error sending shutdown message to api worker thread: {}", e); } } - match mainthread_transmitter.socket_thread.send(SocketWorkerMessage::Shutdown) { + match mainthread_transmitter + .socket_thread + .send(SocketWorkerMessage::Shutdown) + { Ok(_) => (), Err(e) => { - error!("Error sending shutdown message to socket worker thread: {}", e); + error!( + "Error sending shutdown message to socket worker thread: {}", + e + ); } } - match mainthread_transmitter.timer_thread.send(TimerWorkerMessage::Shutdown) { + match mainthread_transmitter + .timer_thread + .send(TimerWorkerMessage::Shutdown) + { Ok(_) => (), Err(e) => { - error!("Error sending shutdown message to timer worker thread: {}", e); + error!( + "Error sending shutdown message to timer worker thread: {}", + e + ); } } }) { @@ -81,21 +102,21 @@ pub fn daemon_main(name: String, server: String) { let config_api = config.clone(); let transmitter_api = transmitter.clone(); let name_api = name.clone(); - let server_api = server.clone(); + let server_api = server; let api_thread = thread::spawn(move || { - apiworker_main(config_api, name_api, server_api,transmitter_api, rx_api); + apiworker_main(config_api, name_api, server_api, transmitter_api, rx_api); }); info!("Starting Nebula thread..."); let config_nebula = config.clone(); let transmitter_nebula = transmitter.clone(); let name_nebula = name.clone(); - let nebula_thread = thread::spawn(move || { - nebulaworker_main(config_nebula, name_nebula, transmitter_nebula, rx_nebula); - }); + //let nebula_thread = thread::spawn(move || { + // nebulaworker_main(config_nebula, name_nebula, transmitter_nebula, rx_nebula); + //}); info!("Starting socket worker thread..."); - let name_socket = name.clone(); + let name_socket = name; let config_socket = config.clone(); let tx_socket = transmitter.clone(); let socket_thread = thread::spawn(move || { @@ -104,7 +125,7 @@ pub fn daemon_main(name: String, server: String) { info!("Starting timer thread..."); if !config.disable_automatic_config_updates { - let timer_transmitter = transmitter.clone(); + let timer_transmitter = transmitter; let timer_thread = thread::spawn(move || { timer_main(timer_transmitter, rx_timer); }); @@ -142,13 +163,13 @@ pub fn daemon_main(name: String, server: String) { info!("API thread exited"); info!("Waiting for Nebula thread to exit..."); - match nebula_thread.join() { - Ok(_) => (), - Err(_) => { - error!("Error waiting for nebula thread to exit."); - std::process::exit(1); - } - } + //match nebula_thread.join() { + // Ok(_) => (), + // Err(_) => { + // error!("Error waiting for nebula thread to exit."); + // std::process::exit(1); + // } + //} info!("Nebula thread exited"); info!("All threads exited"); @@ -159,5 +180,5 @@ pub struct ThreadMessageSender { pub socket_thread: Sender, pub api_thread: Sender, pub nebula_thread: Sender, - pub timer_thread: Sender -} \ No newline at end of file + pub timer_thread: Sender, +} diff --git a/tfclient/src/dirs.rs b/tfclient/src/dirs.rs index 0010aa1..07e206d 100644 --- a/tfclient/src/dirs.rs +++ b/tfclient/src/dirs.rs @@ -22,4 +22,4 @@ pub fn get_cdata_file(instance: &str) -> Option { pub fn get_nebulaconfig_file(instance: &str) -> Option { get_cdata_dir(instance).map(|f| f.join("nebula.sk_embedded.yml")) -} \ No newline at end of file +} diff --git a/tfclient/src/embedded_nebula.rs b/tfclient/src/embedded_nebula.rs index 76b6ffc..d48c4aa 100644 --- a/tfclient/src/embedded_nebula.rs +++ b/tfclient/src/embedded_nebula.rs @@ -1,3 +1,6 @@ +use crate::dirs::get_data_dir; +use crate::util::sha256; +use log::debug; use std::error::Error; use std::fs; use std::fs::File; @@ -5,9 +8,6 @@ use std::io::Write; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; use std::process::{Child, Command}; -use log::debug; -use crate::dirs::get_data_dir; -use crate::util::sha256; pub fn extract_embedded_nebula() -> Result> { let data_dir = get_data_dir().ok_or("Unable to get platform-specific data dir")?; @@ -25,7 +25,11 @@ pub fn extract_embedded_nebula() -> Result> { } let executable_postfix = if cfg!(windows) { ".exe" } else { "" }; - let executable_name = format!("nebula-{}{}", crate::nebula_bin::NEBULA_VERSION, executable_postfix); + let executable_name = format!( + "nebula-{}{}", + crate::nebula_bin::NEBULA_VERSION, + executable_postfix + ); let file_path = hash_dir.join(executable_name); @@ -49,7 +53,10 @@ pub fn extract_embedded_nebula_cert() -> Result> { } let bin_dir = data_dir.join("cache/"); - let hash_dir = bin_dir.join(format!("{}/", sha256(crate::nebula_cert_bin::NEBULA_CERT_BIN))); + let hash_dir = bin_dir.join(format!( + "{}/", + sha256(crate::nebula_cert_bin::NEBULA_CERT_BIN) + )); if !hash_dir.exists() { fs::create_dir_all(&hash_dir)?; @@ -57,7 +64,11 @@ pub fn extract_embedded_nebula_cert() -> Result> { } let executable_postfix = if cfg!(windows) { ".exe" } else { "" }; - let executable_name = format!("nebula-cert-{}{}", crate::nebula_cert_bin::NEBULA_CERT_VERSION, executable_postfix); + let executable_name = format!( + "nebula-cert-{}{}", + crate::nebula_cert_bin::NEBULA_CERT_VERSION, + executable_postfix + ); let file_path = hash_dir.join(executable_name); @@ -101,4 +112,4 @@ pub fn run_embedded_nebula_cert(args: &[String]) -> Result debug!("Running {} with args {:?}", path.as_path().display(), args); _setup_permissions(&path)?; Ok(Command::new(path).args(args).spawn()?) -} \ No newline at end of file +} diff --git a/tfclient/src/main.rs b/tfclient/src/main.rs index b5c06de..4d1bac6 100644 --- a/tfclient/src/main.rs +++ b/tfclient/src/main.rs @@ -14,16 +14,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub mod embedded_nebula; -pub mod dirs; -pub mod util; -pub mod nebulaworker; -pub mod daemon; -pub mod config; pub mod apiworker; -pub mod socketworker; +pub mod config; +pub mod daemon; +pub mod dirs; +pub mod embedded_nebula; +pub mod nebulaworker; pub mod socketclient; +pub mod socketworker; pub mod timerworker; +pub mod util; pub mod nebula_bin { include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs")); @@ -32,15 +32,14 @@ pub mod nebula_cert_bin { include!(concat!(env!("OUT_DIR"), "/nebula_cert.bin.rs")); } - use std::fs; -use clap::{Parser, ArgAction, Subcommand}; -use log::{error, info}; -use simple_logger::SimpleLogger; use crate::config::load_config; use crate::dirs::get_data_dir; use crate::embedded_nebula::{run_embedded_nebula, run_embedded_nebula_cert}; +use clap::{ArgAction, Parser, Subcommand}; +use log::{error, info}; +use simple_logger::SimpleLogger; #[derive(Parser)] #[command(author = "c0repwn3r", version, about, long_about = None)] @@ -52,7 +51,7 @@ struct Cli { version: bool, #[command(subcommand)] - subcommand: Commands + subcommand: Commands, } #[derive(Subcommand)] @@ -60,14 +59,14 @@ enum Commands { /// Run the `nebula` binary. This is useful if you want to do debugging with tfclient's internal nebula. RunNebula { /// Arguments to pass to the `nebula` binary - #[clap(trailing_var_arg=true, allow_hyphen_values=true)] - args: Vec + #[clap(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, }, /// Run the `nebula-cert` binary. This is useful if you want to mess with certificates. Note: tfclient does not actually use nebula-cert for certificate operations, and instead uses trifid-pki internally RunNebulaCert { /// Arguments to pass to the `nebula-cert` binary - #[clap(trailing_var_arg=true, allow_hyphen_values=true)] - args: Vec + #[clap(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, }, /// Clear any cached data that tfclient may have added ClearCache {}, @@ -79,7 +78,7 @@ enum Commands { name: String, #[clap(short, long)] /// Server to use for API calls. - server: String + server: String, }, /// Enroll this host using a trifid-api enrollment code @@ -97,7 +96,7 @@ enum Commands { #[clap(short, long, default_value = "tfclient")] /// Service name specified on install name: String, - } + }, } fn main() { @@ -110,34 +109,28 @@ fn main() { } match args.subcommand { - Commands::RunNebula { args } => { - match run_embedded_nebula(&args) { - Ok(mut c) => { - match c.wait() { - Ok(stat) => { - match stat.code() { - Some(code) => { - if code != 0 { - error!("Nebula process exited with nonzero status code {}", code); - } - std::process::exit(code); - }, - None => { - info!("Nebula process terminated by signal"); - std::process::exit(0); - } - } - }, - Err(e) => { - error!("Unable to wait for child to exit: {}", e); - std::process::exit(1); + Commands::RunNebula { args } => match run_embedded_nebula(&args) { + Ok(mut c) => match c.wait() { + Ok(stat) => match stat.code() { + Some(code) => { + if code != 0 { + error!("Nebula process exited with nonzero status code {}", code); } + std::process::exit(code); + } + None => { + info!("Nebula process terminated by signal"); + std::process::exit(0); } }, Err(e) => { - error!("Unable to start nebula binary: {}", e); + error!("Unable to wait for child to exit: {}", e); std::process::exit(1); } + }, + Err(e) => { + error!("Unable to start nebula binary: {}", e); + std::process::exit(1); } }, Commands::ClearCache { .. } => { @@ -159,37 +152,34 @@ fn main() { info!("Removed all cached data."); std::process::exit(0); - }, - Commands::RunNebulaCert { args } => { - match run_embedded_nebula_cert(&args) { - Ok(mut c) => { - match c.wait() { - Ok(stat) => { - match stat.code() { - Some(code) => { - if code != 0 { - error!("nebula-cert process exited with nonzero status code {}", code); - } - std::process::exit(code); - }, - None => { - info!("nebula-cert process terminated by signal"); - std::process::exit(0); - } - } - }, - Err(e) => { - error!("Unable to wait for child to exit: {}", e); - std::process::exit(1); + } + Commands::RunNebulaCert { args } => match run_embedded_nebula_cert(&args) { + Ok(mut c) => match c.wait() { + Ok(stat) => match stat.code() { + Some(code) => { + if code != 0 { + error!( + "nebula-cert process exited with nonzero status code {}", + code + ); } + std::process::exit(code); + } + None => { + info!("nebula-cert process terminated by signal"); + std::process::exit(0); } }, Err(e) => { - error!("Unable to start nebula-cert binary: {}", e); + error!("Unable to wait for child to exit: {}", e); std::process::exit(1); } + }, + Err(e) => { + error!("Unable to start nebula-cert binary: {}", e); + std::process::exit(1); } - } + }, Commands::Run { name, server } => { daemon::daemon_main(name, server); } @@ -209,7 +199,7 @@ fn main() { std::process::exit(1); } }; - }, + } Commands::Update { name } => { info!("Loading config..."); let config = match load_config(&name) { @@ -231,5 +221,11 @@ fn main() { } fn print_version() { - println!("tfclient v{} linked to trifid-pki v{}, embedding nebula v{} and nebula-cert v{}", env!("CARGO_PKG_VERSION"), trifid_pki::TRIFID_PKI_VERSION, crate::nebula_bin::NEBULA_VERSION, crate::nebula_cert_bin::NEBULA_CERT_VERSION); -} \ No newline at end of file + println!( + "tfclient v{} linked to trifid-pki v{}, embedding nebula v{} and nebula-cert v{}", + env!("CARGO_PKG_VERSION"), + trifid_pki::TRIFID_PKI_VERSION, + crate::nebula_bin::NEBULA_VERSION, + crate::nebula_cert_bin::NEBULA_CERT_VERSION + ); +} diff --git a/tfclient/src/nebulaworker.rs b/tfclient/src/nebulaworker.rs index f9dcc7f..ead052c 100644 --- a/tfclient/src/nebulaworker.rs +++ b/tfclient/src/nebulaworker.rs @@ -1,29 +1,34 @@ // Code to handle the nebula worker -use std::error::Error; -use std::fs; -use std::sync::mpsc::{Receiver, TryRecvError}; -use std::time::{Duration, SystemTime}; -use log::{debug, error, info}; use crate::config::{load_cdata, NebulaConfig, TFClientConfig}; use crate::daemon::ThreadMessageSender; use crate::dirs::get_nebulaconfig_file; use crate::embedded_nebula::run_embedded_nebula; +use log::{debug, error, info}; +use std::error::Error; +use std::fs; +use std::sync::mpsc::Receiver; +use std::time::{Duration, SystemTime}; pub enum NebulaWorkerMessage { Shutdown, ConfigUpdated, - WakeUp + WakeUp, } fn insert_private_key(instance: &str) -> Result<(), Box> { - if !get_nebulaconfig_file(instance).ok_or("Could not get config file location")?.exists() { + if !get_nebulaconfig_file(instance) + .ok_or("Could not get config file location")? + .exists() + { return Ok(()); // cant insert private key into a file that does not exist - BUT. we can gracefully handle nebula crashing - we cannot gracefully handle this fn failing } let cdata = load_cdata(instance)?; let key = cdata.dh_privkey.ok_or("Missing private key")?; - let config_str = fs::read_to_string(get_nebulaconfig_file(instance).ok_or("Could not get config file location")?)?; + let config_str = fs::read_to_string( + get_nebulaconfig_file(instance).ok_or("Could not get config file location")?, + )?; let mut config: NebulaConfig = serde_yaml::from_str(&config_str)?; config.pki.key = Some(String::from_utf8(key)?); @@ -31,12 +36,20 @@ fn insert_private_key(instance: &str) -> Result<(), Box> { debug!("inserted private key into config: {:?}", config); let config_str = serde_yaml::to_string(&config)?; - fs::write(get_nebulaconfig_file(instance).ok_or("Could not get config file location")?, config_str)?; + fs::write( + get_nebulaconfig_file(instance).ok_or("Could not get config file location")?, + config_str, + )?; Ok(()) } -pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter: ThreadMessageSender, rx: Receiver) { +pub fn nebulaworker_main( + _config: TFClientConfig, + instance: String, + _transmitter: ThreadMessageSender, + rx: Receiver, +) { let _cdata = match load_cdata(&instance) { Ok(data) => data, Err(e) => { @@ -50,7 +63,7 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter match insert_private_key(&instance) { Ok(_) => { info!("config fixed (private-key embedded)"); - }, + } Err(e) => { error!("unable to fix config: {}", e); error!("nebula thread exiting with error"); @@ -58,7 +71,14 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter } } info!("starting nebula child..."); - let mut child = match run_embedded_nebula(&["-config".to_string(), get_nebulaconfig_file(&instance).unwrap().to_str().unwrap().to_string()]) { + let mut child = match run_embedded_nebula(&[ + "-config".to_string(), + get_nebulaconfig_file(&instance) + .unwrap() + .to_str() + .unwrap() + .to_string(), + ]) { Ok(c) => c, Err(e) => { error!("unable to start embedded nebula binary: {}", e); @@ -75,7 +95,14 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter if let Ok(e) = child.try_wait() { if e.is_some() && SystemTime::now() > last_restart_time + Duration::from_secs(5) { info!("nebula process has exited, restarting"); - child = match run_embedded_nebula(&["-config".to_string(), get_nebulaconfig_file(&instance).unwrap().to_str().unwrap().to_string()]) { + child = match run_embedded_nebula(&[ + "-config".to_string(), + get_nebulaconfig_file(&instance) + .unwrap() + .to_str() + .unwrap() + .to_string(), + ]) { Ok(c) => c, Err(e) => { error!("unable to start embedded nebula binary: {}", e); @@ -88,59 +115,64 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter } } match rx.recv() { - Ok(msg) => { - match msg { - NebulaWorkerMessage::WakeUp => { - continue; - }, - NebulaWorkerMessage::Shutdown => { - info!("recv on command socket: shutdown, stopping"); - info!("shutting down nebula binary"); - match child.kill() { - Ok(_) => { - debug!("nebula process exited"); - }, - Err(e) => { - error!("nebula process already exited: {}", e); - } + Ok(msg) => match msg { + NebulaWorkerMessage::WakeUp => { + continue; + } + NebulaWorkerMessage::Shutdown => { + info!("recv on command socket: shutdown, stopping"); + info!("shutting down nebula binary"); + match child.kill() { + Ok(_) => { + debug!("nebula process exited"); } - info!("nebula shut down"); - break; - }, - NebulaWorkerMessage::ConfigUpdated => { - info!("our configuration has been updated - restarting"); - debug!("killing existing process"); - match child.kill() { - Ok(_) => { - debug!("nebula process exited"); - }, - Err(e) => { - error!("nebula process already exited: {}", e); - } + Err(e) => { + error!("nebula process already exited: {}", e); } - debug!("fixing config..."); - match insert_private_key(&instance) { - Ok(_) => { - debug!("config fixed (private-key embedded)"); - }, - Err(e) => { - error!("unable to fix config: {}", e); - error!("nebula thread exiting with error"); - return; - } - } - debug!("restarting nebula process"); - child = match run_embedded_nebula(&["-config".to_string(), get_nebulaconfig_file(&instance).unwrap().to_str().unwrap().to_string()]) { - Ok(c) => c, - Err(e) => { - error!("unable to start embedded nebula binary: {}", e); - error!("nebula thread exiting with error"); - return; - } - }; - last_restart_time = SystemTime::now(); - debug!("nebula process restarted"); } + info!("nebula shut down"); + break; + } + NebulaWorkerMessage::ConfigUpdated => { + info!("our configuration has been updated - restarting"); + debug!("killing existing process"); + match child.kill() { + Ok(_) => { + debug!("nebula process exited"); + } + Err(e) => { + error!("nebula process already exited: {}", e); + } + } + debug!("fixing config..."); + match insert_private_key(&instance) { + Ok(_) => { + debug!("config fixed (private-key embedded)"); + } + Err(e) => { + error!("unable to fix config: {}", e); + error!("nebula thread exiting with error"); + return; + } + } + debug!("restarting nebula process"); + child = match run_embedded_nebula(&[ + "-config".to_string(), + get_nebulaconfig_file(&instance) + .unwrap() + .to_str() + .unwrap() + .to_string(), + ]) { + Ok(c) => c, + Err(e) => { + error!("unable to start embedded nebula binary: {}", e); + error!("nebula thread exiting with error"); + return; + } + }; + last_restart_time = SystemTime::now(); + debug!("nebula process restarted"); } }, Err(e) => { @@ -149,4 +181,4 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter } } } -} \ No newline at end of file +} diff --git a/tfclient/src/socketclient.rs b/tfclient/src/socketclient.rs index addb9ff..796bc67 100644 --- a/tfclient/src/socketclient.rs +++ b/tfclient/src/socketclient.rs @@ -1,13 +1,16 @@ +use crate::config::TFClientConfig; +use crate::socketworker::{ctob, DisconnectReason, JsonMessage, JSON_API_VERSION}; +use log::{error, info}; use std::error::Error; use std::io::{BufRead, BufReader, Write}; use std::net::{IpAddr, SocketAddr, TcpStream}; -use log::{error, info}; -use crate::config::TFClientConfig; -use crate::socketworker::{ctob, DisconnectReason, JSON_API_VERSION, JsonMessage}; pub fn enroll(code: &str, config: &TFClientConfig) -> Result<(), Box> { info!("Connecting to local command socket..."); - let mut stream = TcpStream::connect(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), config.listen_port))?; + let mut stream = TcpStream::connect(SocketAddr::new( + IpAddr::from([127, 0, 0, 1]), + config.listen_port, + ))?; let stream2 = stream.try_clone()?; let mut reader = BufReader::new(&stream2); @@ -52,7 +55,10 @@ pub fn enroll(code: &str, config: &TFClientConfig) -> Result<(), Box> pub fn update(config: &TFClientConfig) -> Result<(), Box> { info!("Connecting to local command socket..."); - let mut stream = TcpStream::connect(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), config.listen_port))?; + let mut stream = TcpStream::connect(SocketAddr::new( + IpAddr::from([127, 0, 0, 1]), + config.listen_port, + ))?; let stream2 = stream.try_clone()?; let mut reader = BufReader::new(&stream2); @@ -98,4 +104,4 @@ fn read_msg(reader: &mut BufReader<&TcpStream>) -> Result) { +pub fn socketworker_main( + config: TFClientConfig, + instance: String, + transmitter: ThreadMessageSender, + rx: Receiver, +) { info!("socketworker_main called, entering realmain"); match _main(config, instance, transmitter, rx) { Ok(_) => (), @@ -29,8 +34,16 @@ pub fn socketworker_main(config: TFClientConfig, instance: String, transmitter: }; } -fn _main(config: TFClientConfig, instance: String, transmitter: ThreadMessageSender, rx: Receiver) -> Result<(), Box> { - let listener = TcpListener::bind(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), config.listen_port))?; +fn _main( + config: TFClientConfig, + instance: String, + transmitter: ThreadMessageSender, + rx: Receiver, +) -> Result<(), Box> { + let listener = TcpListener::bind(SocketAddr::new( + IpAddr::from([127, 0, 0, 1]), + config.listen_port, + ))?; listener.set_nonblocking(true)?; loop { @@ -47,21 +60,21 @@ fn _main(config: TFClientConfig, instance: String, transmitter: ThreadMessageSen } } }); - }, + } Err(e) if e.kind() == io::ErrorKind::WouldBlock => (), - Err(e) => { Err(e)?; } + Err(e) => { + Err(e)?; + } } match rx.recv() { - Ok(msg) => { - match msg { - SocketWorkerMessage::Shutdown => { - info!("recv on command socket: shutdown, stopping"); - break; - }, - SocketWorkerMessage::WakeUp => { - continue; - } + Ok(msg) => match msg { + SocketWorkerMessage::Shutdown => { + info!("recv on command socket: shutdown, stopping"); + break; + } + SocketWorkerMessage::WakeUp => { + continue; } }, Err(e) => { @@ -74,22 +87,27 @@ fn _main(config: TFClientConfig, instance: String, transmitter: ThreadMessageSen Ok(()) } -fn handle_stream(stream: (TcpStream, SocketAddr), transmitter: ThreadMessageSender, config: TFClientConfig, instance: String) -> Result<(), io::Error> { +fn handle_stream( + stream: (TcpStream, SocketAddr), + transmitter: ThreadMessageSender, + config: TFClientConfig, + instance: String, +) -> Result<(), io::Error> { info!("Incoming client"); match handle_client(stream.0, transmitter, config, instance) { Ok(()) => (), Err(e) if e.kind() == io::ErrorKind::TimedOut => { warn!("Client timed out, connection aborted"); - }, + } Err(e) if e.kind() == io::ErrorKind::NotConnected => { warn!("Client connection severed"); - }, + } Err(e) if e.kind() == io::ErrorKind::BrokenPipe => { warn!("Client connection returned error: broken pipe"); - }, + } Err(e) if e.kind() == io::ErrorKind::ConnectionAborted => { warn!("Client aborted connection"); - }, + } Err(e) => { error!("Error in client handler: {}", e); return Err(e); @@ -98,15 +116,18 @@ fn handle_stream(stream: (TcpStream, SocketAddr), transmitter: ThreadMessageSend Ok(()) } -fn handle_client(stream: TcpStream, transmitter: ThreadMessageSender, config: TFClientConfig, instance: String) -> Result<(), io::Error> { +fn handle_client( + stream: TcpStream, + transmitter: ThreadMessageSender, + _config: TFClientConfig, + instance: String, +) -> Result<(), io::Error> { info!("Handling connection from {}", stream.peer_addr()?); let mut client = Client { state: ClientState::WaitHello, reader: BufReader::new(&stream), - writer: BufWriter::new(&stream), stream: &stream, - config, instance, }; @@ -118,18 +139,14 @@ fn handle_client(stream: TcpStream, transmitter: ThreadMessageSender, config: TF trace!("recv {:?} from {}", command, client.stream.peer_addr()?); - let should_disconnect; + let should_disconnect = match client.state { + ClientState::WaitHello => waithello_handle(&mut client, &transmitter, command)?, + ClientState::SentHello => senthello_handle(&mut client, &transmitter, command)?, + }; - match client.state { - ClientState::WaitHello => { - should_disconnect = waithello_handle(&mut client, &transmitter, command)?; - } - ClientState::SentHello => { - should_disconnect = senthello_handle(&mut client, &transmitter, command)?; - } + if should_disconnect { + break; } - - if should_disconnect { break; } } // Gracefully close the connection @@ -141,13 +158,15 @@ fn handle_client(stream: TcpStream, transmitter: ThreadMessageSender, config: TF struct Client<'a> { state: ClientState, reader: BufReader<&'a TcpStream>, - writer: BufWriter<&'a TcpStream>, stream: &'a TcpStream, - config: TFClientConfig, - instance: String + instance: String, } -fn waithello_handle(client: &mut Client, _transmitter: &ThreadMessageSender, command: JsonMessage) -> Result { +fn waithello_handle( + client: &mut Client, + _transmitter: &ThreadMessageSender, + command: JsonMessage, +) -> Result { trace!("state: WaitHello, handing with waithello_handle"); let mut should_disconnect = false; @@ -158,20 +177,20 @@ fn waithello_handle(client: &mut Client, _transmitter: &ThreadMessageSender, com client.stream.write_all(&ctob(JsonMessage::Goodbye { reason: DisconnectReason::UnsupportedVersion { expected: JSON_API_VERSION, - got: version - } + got: version, + }, }))?; } client.stream.write_all(&ctob(JsonMessage::Hello { - version: JSON_API_VERSION + version: JSON_API_VERSION, }))?; client.state = ClientState::SentHello; trace!("setting state to SentHello"); - }, + } JsonMessage::Goodbye { reason } => { info!("Client sent disconnect: {:?}", reason); should_disconnect = true; - }, + } _ => { debug!("message type unexpected in WaitHello state"); should_disconnect = true; @@ -184,7 +203,11 @@ fn waithello_handle(client: &mut Client, _transmitter: &ThreadMessageSender, com Ok(should_disconnect) } -fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, command: JsonMessage) -> Result { +fn senthello_handle( + client: &mut Client, + transmitter: &ThreadMessageSender, + command: JsonMessage, +) -> Result { trace!("state: SentHello, handing with senthello_handle"); let mut should_disconnect = false; @@ -192,14 +215,20 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm JsonMessage::Goodbye { reason } => { info!("Client sent disconnect: {:?}", reason); should_disconnect = true; - }, + } JsonMessage::Shutdown {} => { info!("Requested to shutdown by local control socket. Sending shutdown message to threads"); - match transmitter.nebula_thread.send(NebulaWorkerMessage::Shutdown) { + match transmitter + .nebula_thread + .send(NebulaWorkerMessage::Shutdown) + { Ok(_) => (), Err(e) => { - error!("Error sending shutdown message to nebula worker thread: {}", e); + error!( + "Error sending shutdown message to nebula worker thread: {}", + e + ); } } match transmitter.api_thread.send(APIWorkerMessage::Shutdown) { @@ -208,19 +237,28 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm error!("Error sending shutdown message to api worker thread: {}", e); } } - match transmitter.socket_thread.send(SocketWorkerMessage::Shutdown) { + match transmitter + .socket_thread + .send(SocketWorkerMessage::Shutdown) + { Ok(_) => (), Err(e) => { - error!("Error sending shutdown message to socket worker thread: {}", e); + error!( + "Error sending shutdown message to socket worker thread: {}", + e + ); } } match transmitter.timer_thread.send(TimerWorkerMessage::Shutdown) { Ok(_) => (), Err(e) => { - error!("Error sending shutdown message to timer worker thread: {}", e); + error!( + "Error sending shutdown message to timer worker thread: {}", + e + ); } } - }, + } JsonMessage::GetHostID {} => { let data = match load_cdata(&client.instance) { @@ -232,20 +270,26 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm }; client.stream.write_all(&ctob(JsonMessage::HostID { has_id: data.creds.is_some(), - id: data.creds.map(|c| c.host_id) + id: data.creds.map(|c| c.host_id), }))?; - }, + } JsonMessage::Enroll { code } => { info!("Client sent enroll with code {}", code); info!("Sending enroll request to apiworker"); - transmitter.api_thread.send(APIWorkerMessage::Enroll { code }).unwrap(); - }, + transmitter + .api_thread + .send(APIWorkerMessage::Enroll { code }) + .unwrap(); + } JsonMessage::Update {} => { info!("Client sent update request."); info!("Telling apiworker to update configuration"); - transmitter.api_thread.send(APIWorkerMessage::Update).unwrap(); + transmitter + .api_thread + .send(APIWorkerMessage::Update) + .unwrap(); } _ => { @@ -267,7 +311,7 @@ pub fn ctob(command: JsonMessage) -> Vec { enum ClientState { WaitHello, - SentHello + SentHello, } pub const JSON_API_VERSION: i32 = 1; @@ -276,28 +320,19 @@ pub const JSON_API_VERSION: i32 = 1; #[serde(tag = "method")] pub enum JsonMessage { #[serde(rename = "hello")] - Hello { - version: i32 - }, + Hello { version: i32 }, #[serde(rename = "goodbye")] - Goodbye { - reason: DisconnectReason - }, + Goodbye { reason: DisconnectReason }, #[serde(rename = "shutdown")] Shutdown {}, #[serde(rename = "get_host_id")] GetHostID {}, #[serde(rename = "host_id")] - HostID { - has_id: bool, - id: Option - }, + HostID { has_id: bool, id: Option }, #[serde(rename = "enroll")] - Enroll { - code: String - }, + Enroll { code: String }, #[serde(rename = "update")] - Update {} + Update {}, } #[derive(Serialize, Deserialize, Debug)] @@ -308,5 +343,5 @@ pub enum DisconnectReason { #[serde(rename = "unexpected_message_type")] UnexpectedMessageType, #[serde(rename = "done")] - Done -} \ No newline at end of file + Done, +} diff --git a/tfclient/src/timerworker.rs b/tfclient/src/timerworker.rs index f1d5386..a747b1f 100644 --- a/tfclient/src/timerworker.rs +++ b/tfclient/src/timerworker.rs @@ -1,15 +1,15 @@ -use std::ops::Add; -use std::sync::mpsc::{Receiver, TryRecvError}; -use std::thread; -use std::time::{Duration, SystemTime}; -use log::{error, info}; use crate::apiworker::APIWorkerMessage; use crate::daemon::ThreadMessageSender; use crate::nebulaworker::NebulaWorkerMessage; use crate::socketworker::SocketWorkerMessage; +use log::{error, info}; +use std::ops::Add; +use std::sync::mpsc::{Receiver, TryRecvError}; +use std::thread; +use std::time::{Duration, SystemTime}; pub enum TimerWorkerMessage { - Shutdown + Shutdown, } pub fn timer_main(tx: ThreadMessageSender, rx: Receiver) { @@ -19,23 +19,19 @@ pub fn timer_main(tx: ThreadMessageSender, rx: Receiver) { thread::sleep(Duration::from_secs(10)); match rx.try_recv() { - Ok(msg) => { - match msg { - TimerWorkerMessage::Shutdown => { - info!("recv on command socket: shutdown, stopping"); - break; - } + Ok(msg) => match msg { + TimerWorkerMessage::Shutdown => { + info!("recv on command socket: shutdown, stopping"); + break; } }, - Err(e) => { - match e { - TryRecvError::Empty => {} - TryRecvError::Disconnected => { - error!("timerworker command socket disconnected, shutting down to prevent orphaning"); - break; - } + Err(e) => match e { + TryRecvError::Empty => {} + TryRecvError::Disconnected => { + error!("timerworker command socket disconnected, shutting down to prevent orphaning"); + break; } - } + }, } if SystemTime::now().gt(&api_reload_timer) { @@ -52,15 +48,21 @@ pub fn timer_main(tx: ThreadMessageSender, rx: Receiver) { match tx.nebula_thread.send(NebulaWorkerMessage::WakeUp) { Ok(_) => (), Err(e) => { - error!("Error sending wakeup message to nebula worker thread: {}", e); + error!( + "Error sending wakeup message to nebula worker thread: {}", + e + ); } } match tx.socket_thread.send(SocketWorkerMessage::WakeUp) { Ok(_) => (), Err(e) => { - error!("Error sending wakeup message to socket worker thread: {}", e); + error!( + "Error sending wakeup message to socket worker thread: {}", + e + ); } } } -} \ No newline at end of file +} diff --git a/tfclient/src/util.rs b/tfclient/src/util.rs index eeecaca..3176447 100644 --- a/tfclient/src/util.rs +++ b/tfclient/src/util.rs @@ -1,6 +1,6 @@ use log::{error, warn}; -use sha2::Sha256; use sha2::Digest; +use sha2::Sha256; use url::Url; pub fn sha256(bytes: &[u8]) -> String { @@ -11,7 +11,7 @@ pub fn sha256(bytes: &[u8]) -> String { } pub fn check_server_url(server: &str) { - let api_base = match Url::parse(&server) { + let api_base = match Url::parse(server) { Ok(u) => u, Err(e) => { error!("Invalid server url `{}`: {}", server, e); @@ -19,11 +19,16 @@ pub fn check_server_url(server: &str) { } }; match api_base.scheme() { - "http" => { warn!("HTTP api urls are not reccomended. Please switch to HTTPS if possible.") }, + "http" => { + warn!("HTTP api urls are not reccomended. Please switch to HTTPS if possible.") + } "https" => (), _ => { - error!("Unsupported protocol `{}` (expected one of http, https)", api_base.scheme()); + error!( + "Unsupported protocol `{}` (expected one of http, https)", + api_base.scheme() + ); std::process::exit(1); } } -} \ No newline at end of file +} diff --git a/trifid-api/Cargo.toml b/trifid-api/Cargo.toml index f3467c4..007aecb 100644 --- a/trifid-api/Cargo.toml +++ b/trifid-api/Cargo.toml @@ -12,8 +12,9 @@ actix-request-identifier = "4" # Web framework serde = { version = "1", features = ["derive"] } # Serialization and deserialization serde_json = "1.0.95" # Serialization and deserialization (cursors) -once_cell = "1" # Config -toml = "0.7" # Config / Serialization and deserialization +once_cell = "1" # Config +toml = "0.7" # Config / Serialization and deserialization +serde_yaml = "0.9.21" # Config / Serialization and deserialization log = "0.4" # Logging simple_logger = "4" # Logging @@ -28,5 +29,9 @@ totp-rs = { version = "5.0.1", features = ["gen_secret", "otpauth"] } # Misc. base64 = "0.21.0" # Misc. chrono = "0.4.24" # Misc. -trifid-pki = { version = "0.1.9" } # Cryptography -aes-gcm = "0.10.1" # Cryptography \ No newline at end of file +trifid-pki = { version = "0.1.9", features = ["serde_derive"] } # Cryptography +aes-gcm = "0.10.1" # Cryptography +ed25519-dalek = "2.0.0-rc.2" # Cryptography + +dnapi-rs = "0.1.9" # API message types +ipnet = "2.7.2" # API message types \ No newline at end of file diff --git a/trifid-api/config.example.toml b/trifid-api/config.example.toml index 975ec0f..8073dd1 100644 --- a/trifid-api/config.example.toml +++ b/trifid-api/config.example.toml @@ -120,4 +120,10 @@ url = "your-database-url-here" # ------- WARNING ------- # Do not change this value in a production instance. It will make existing data inaccessible until changed back. # ------- WARNING ------- -data-key = "edd600bcebea461381ea23791b6967c8667e12827ac8b94dc022f189a5dc59a2" \ No newline at end of file +data-key = "edd600bcebea461381ea23791b6967c8667e12827ac8b94dc022f189a5dc59a2" + +# The data directory used for storing keys, configuration, signing keys, etc. Must be writable by this instance. +# This directory will be used to store very sensitive data - protect it like a password! It should be writable by +# this instance and ONLY this instance. +# Do not modify any files in this directory manually unless directed to do so by trifid. +local_keystore_directory = "./trifid_data" \ No newline at end of file diff --git a/trifid-api/src/codegen/mod.rs b/trifid-api/src/codegen/mod.rs new file mode 100644 index 0000000..a9594f5 --- /dev/null +++ b/trifid-api/src/codegen/mod.rs @@ -0,0 +1,338 @@ +use std::collections::HashMap; +use std::error::Error; +use std::net::{Ipv4Addr, SocketAddrV4}; +use std::str::FromStr; +use std::time::{Duration, SystemTime}; + +use actix_web::web::Data; + +use crate::config::{NebulaConfig, NebulaConfigCipher, NebulaConfigLighthouse, NebulaConfigListen, NebulaConfigPki, NebulaConfigPunchy, NebulaConfigRelay, NebulaConfigTun, CONFIG, NebulaConfigFirewall, NebulaConfigFirewallRule}; +use crate::crypto::{decrypt_with_nonce, encrypt_with_nonce, get_cipher_from_config}; +use crate::AppState; +use ed25519_dalek::SigningKey; +use ipnet::Ipv4Net; +use log::{debug, error}; +use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +use trifid_api_entities::entity::{firewall_rule, host, host_config_override, host_static_address, network, organization, signing_ca}; +use trifid_pki::cert::{ + deserialize_ed25519_private, deserialize_nebula_certificate_from_pem, NebulaCertificate, + NebulaCertificateDetails, +}; + +pub struct CodegenRequiredInfo { + pub host: host::Model, + pub host_static_addresses: HashMap>, + pub host_config_overrides: Vec, + pub network: network::Model, + pub organization: organization::Model, + pub dh_pubkey: Vec, + pub ca: signing_ca::Model, + pub other_cas: Vec, + pub relay_ips: Vec, + pub lighthouse_ips: Vec, + pub blocked_hosts: Vec, + pub firewall_rules: Vec +} + +pub async fn generate_config( + data: &Data, + info: &CodegenRequiredInfo, +) -> Result<(NebulaConfig, NebulaCertificate), Box> { + debug!("chk: deserialize CA cert {:x?}", hex::decode(&info.ca.cert)?); + // decode the CA data + let ca_cert = deserialize_nebula_certificate_from_pem(&hex::decode(&info.ca.cert)?)?; + + // generate the client's new cert + let mut cert = NebulaCertificate { + details: NebulaCertificateDetails { + name: info.host.name.clone(), + ips: vec![Ipv4Net::new( + Ipv4Addr::from_str(&info.host.ip).unwrap(), + Ipv4Net::from_str(&info.network.cidr).unwrap().prefix_len(), + ) + .unwrap()], + subnets: vec![], + groups: vec![ + format!("role:{}", info.host.role) + ], + not_before: SystemTime::now(), + not_after: SystemTime::now() + Duration::from_secs(CONFIG.crypto.certs_expiry_time), + public_key: info.dh_pubkey.clone().try_into().unwrap(), + is_ca: false, + issuer: ca_cert.sha256sum()?, + }, + signature: vec![], + }; + + // decrypt the private key + let private_pem = decrypt_with_nonce( + &hex::decode(&info.ca.key)?, + hex::decode(&info.ca.nonce)?.try_into().unwrap(), + &get_cipher_from_config(&CONFIG)?, + ) + .map_err(|_| "Encryption error")?; + + let private_key = deserialize_ed25519_private(&private_pem)?; + let signing_key = SigningKey::from_keypair_bytes(&private_key.try_into().unwrap()).unwrap(); + + cert.sign(&signing_key)?; + + // cas + let mut cas = String::new(); + for ca in &info.other_cas { + cas += &String::from_utf8(hex::decode(&ca.cert)?)?; + } + + // blocked hosts + let mut blocked_hosts_fingerprints = vec![]; + for host in &info.blocked_hosts { + if let Some(host) = data.keystore.hosts.iter().find(|u| &u.id == host) { + for cert in &host.certs { + blocked_hosts_fingerprints.push(cert.cert.sha256sum()?); + } + } + } + + let nebula_config = NebulaConfig { + pki: NebulaConfigPki { + ca: cas, + cert: String::from_utf8(cert.serialize_to_pem()?)?, + key: None, + blocklist: blocked_hosts_fingerprints, + disconnect_invalid: true, + }, + static_host_map: info + .host_static_addresses + .iter() + .map(|(u, addrs)| (Ipv4Addr::from_str(u).unwrap(), addrs.clone())) + .collect(), + lighthouse: match info.host.is_lighthouse { + true => Some(NebulaConfigLighthouse { + am_lighthouse: true, + serve_dns: false, + dns: None, + interval: 60, + hosts: vec![], + remote_allow_list: HashMap::new(), + local_allow_list: HashMap::new(), + }), + false => Some(NebulaConfigLighthouse { + am_lighthouse: false, + serve_dns: false, + dns: None, + interval: 60, + hosts: info.lighthouse_ips.to_vec(), + remote_allow_list: HashMap::new(), + local_allow_list: HashMap::new(), + }), + }, + listen: match info.host.is_lighthouse || info.host.is_relay { + true => Some(NebulaConfigListen { + host: "[::]".to_string(), + port: info.host.listen_port as u16, + batch: 64, + read_buffer: Some(10485760), + write_buffer: Some(10485760), + }), + false => Some(NebulaConfigListen { + host: "[::]".to_string(), + port: 0u16, + batch: 64, + read_buffer: Some(10485760), + write_buffer: Some(10485760), + }), + }, + punchy: Some(NebulaConfigPunchy { + punch: true, + respond: true, + delay: "".to_string(), + }), + cipher: NebulaConfigCipher::Aes, + preferred_ranges: vec![], + relay: Some(NebulaConfigRelay { + relays: info.relay_ips.to_vec(), + am_relay: info.host.is_relay, + use_relays: true, + }), + tun: Some(NebulaConfigTun { + disabled: false, + dev: Some("trifid1".to_string()), + drop_local_broadcast: true, + drop_multicast: true, + tx_queue: 500, + mtu: 1300, + routes: vec![], + unsafe_routes: vec![], + }), + logging: None, + sshd: None, + firewall: Some(NebulaConfigFirewall { + conntrack: None, + inbound: Some(info.firewall_rules.clone()), + outbound: Some(vec![ + NebulaConfigFirewallRule { + port: Some("any".to_string()), + proto: Some("any".to_string()), + ca_name: None, + ca_sha: None, + host: Some("any".to_string()), + group: None, + groups: None, + cidr: None, + } + ]), + }), + routines: 0, + stats: None, + local_range: None, + }; + + Ok((nebula_config, cert)) +} + +pub async fn collect_info<'a>( + db: &'a Data, + host: &'a str, + dh_pubkey: &'a [u8], +) -> Result> { + // load host info + let host = trifid_api_entities::entity::host::Entity::find() + .filter(host::Column::Id.eq(host)) + .one(&db.conn) + .await?; + let host = match host { + Some(host) => host, + None => return Err("Host does not exist".into()), + }; + + let host_config_overrides = trifid_api_entities::entity::host_config_override::Entity::find() + .filter(host_config_override::Column::Id.eq(&host.id)) + .all(&db.conn) + .await?; + + let _host_static_addresses = trifid_api_entities::entity::host_static_address::Entity::find() + .filter(host_static_address::Column::Id.eq(&host.id)) + .all(&db.conn) + .await?; + + // load network info + let network = trifid_api_entities::entity::network::Entity::find() + .filter(network::Column::Id.eq(&host.network)) + .one(&db.conn) + .await?; + + let network = match network { + Some(network) => network, + None => { + return Err("Network does not exist".into()); + } + }; + + // get all lighthouses and relays and get all of their static addresses, and get internal addresses of relays + let mut host_x_static_addresses = HashMap::new(); + let mut relays = vec![]; + let mut lighthouses = vec![]; + let mut blocked_hosts = vec![]; + + let hosts = trifid_api_entities::entity::host::Entity::find() + .filter(host::Column::Network.eq(&network.id)) + .filter(host::Column::IsRelay.eq(true)) + .filter(host::Column::IsLighthouse.eq(true)) + .all(&db.conn) + .await?; + + for host in hosts { + if host.is_relay { + relays.push(Ipv4Addr::from_str(&host.ip).unwrap()); + } else if host.is_lighthouse { + lighthouses.push(Ipv4Addr::from_str(&host.ip).unwrap()); + } + + if host.is_blocked { + blocked_hosts.push(host.id.clone()); + } + + let static_addresses = trifid_api_entities::entity::host_static_address::Entity::find() + .filter(host_static_address::Column::Host.eq(host.id)) + .all(&db.conn) + .await?; + let static_addresses: Vec = static_addresses + .iter() + .map(|u| SocketAddrV4::from_str(&u.address).unwrap()) + .collect(); + + host_x_static_addresses.insert(host.ip.clone(), static_addresses); + } + + // load org info + let org = trifid_api_entities::entity::organization::Entity::find() + .filter(organization::Column::Id.eq(&network.organization)) + .one(&db.conn) + .await?; + let org = match org { + Some(org) => org, + None => { + return Err("Organization does not exist".into()); + } + }; + + // get the CA that is closest to expiry, but *not* expired + let available_cas = trifid_api_entities::entity::signing_ca::Entity::find() + .filter(signing_ca::Column::Organization.eq(&org.id)) + .all(&db.conn) + .await?; + + let mut best_ca: Option = None; + let mut all_cas = vec![]; + + for ca in available_cas { + if let Some(existing_best) = &best_ca { + if ca.expires < existing_best.expires { + best_ca = Some(ca.clone()); + } + } else { + best_ca = Some(ca.clone()); + } + all_cas.push(ca); + } + + if best_ca.is_none() { + error!( + "!!! NO AVAILABLE CAS !!! while trying to sign cert for {}", + org.id + ); + return Err("No signing CAs available".into()); + } + + let best_ca = best_ca.unwrap(); + + // pull our role's firewall rules + let firewall_rules = trifid_api_entities::entity::firewall_rule::Entity::find().filter(firewall_rule::Column::Role.eq(&host.id)).all(&db.conn).await?; + let firewall_rules = firewall_rules.iter().map(|u| { + NebulaConfigFirewallRule { + port: Some(if u.port_range_from == 0 && u.port_range_to == 65535 { "any".to_string() } else { format!("{}-{}", u.port_range_from, u.port_range_to) }), + proto: Some(u.protocol.clone()), + ca_name: None, + ca_sha: None, + host: if u.allowed_role_id.is_some() { None } else { Some("any".to_string()) }, + groups: if u.allowed_role_id.is_some() { Some(vec![format!("role:{}", u.allowed_role_id.clone().unwrap())])} else { None }, + group: None, + cidr: None, + } + }).collect(); + + Ok(CodegenRequiredInfo { + host, + host_static_addresses: host_x_static_addresses, + host_config_overrides, + network, + organization: org, + dh_pubkey: dh_pubkey.to_vec(), + ca: best_ca, + other_cas: all_cas, + relay_ips: relays, + lighthouse_ips: lighthouses, + blocked_hosts, + firewall_rules + }) +} diff --git a/trifid-api/src/config.rs b/trifid-api/src/config.rs index 9d06334..1b8445f 100644 --- a/trifid-api/src/config.rs +++ b/trifid-api/src/config.rs @@ -14,11 +14,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use ipnet::{IpNet, Ipv4Net}; use log::error; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::fs; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::path::PathBuf; pub static CONFIG: Lazy = Lazy::new(|| { let config_str = match fs::read_to_string("/etc/trifid/config.toml") { @@ -88,6 +91,9 @@ pub struct TrifidConfigTokens { #[derive(Serialize, Deserialize, Debug)] pub struct TrifidConfigCryptography { pub data_encryption_key: String, + pub local_keystore_directory: PathBuf, + #[serde(default = "certs_expiry_time")] + pub certs_expiry_time: u64, } fn max_connections_default() -> u32 { @@ -120,3 +126,534 @@ fn mfa_tokens_expiry_time() -> u64 { fn enrollment_tokens_expiry_time() -> u64 { 600 } // 10 minutes +fn certs_expiry_time() -> u64 { + 3600 * 24 * 31 * 12 // 1 year +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfig { + pub pki: NebulaConfigPki, + #[serde(default = "empty_hashmap")] + #[serde(skip_serializing_if = "is_empty_hashmap")] + pub static_host_map: HashMap>, + #[serde(skip_serializing_if = "is_none")] + pub lighthouse: Option, + #[serde(skip_serializing_if = "is_none")] + pub listen: Option, + #[serde(skip_serializing_if = "is_none")] + pub punchy: Option, + #[serde(default = "cipher_aes")] + #[serde(skip_serializing_if = "is_cipher_aes")] + pub cipher: NebulaConfigCipher, + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub preferred_ranges: Vec, + #[serde(skip_serializing_if = "is_none")] + pub relay: Option, + #[serde(skip_serializing_if = "is_none")] + pub tun: Option, + #[serde(skip_serializing_if = "is_none")] + pub logging: Option, + #[serde(skip_serializing_if = "is_none")] + pub sshd: Option, + + #[serde(skip_serializing_if = "is_none")] + pub firewall: Option, + + #[serde(default = "u64_1")] + #[serde(skip_serializing_if = "is_u64_1")] + pub routines: u64, + + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub stats: Option, + + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub local_range: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigPki { + pub ca: String, + pub cert: String, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub key: Option, + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub blocklist: Vec, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub disconnect_invalid: bool, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigLighthouse { + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub am_lighthouse: bool, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub serve_dns: bool, + #[serde(skip_serializing_if = "is_none")] + pub dns: Option, + #[serde(default = "u32_10")] + #[serde(skip_serializing_if = "is_u32_10")] + pub interval: u32, + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub hosts: Vec, + #[serde(default = "empty_hashmap")] + #[serde(skip_serializing_if = "is_empty_hashmap")] + pub remote_allow_list: HashMap, + #[serde(default = "empty_hashmap")] + #[serde(skip_serializing_if = "is_empty_hashmap")] + pub local_allow_list: HashMap, // `interfaces` is not supported +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigLighthouseDns { + #[serde(default = "string_empty")] + #[serde(skip_serializing_if = "is_string_empty")] + pub host: String, + #[serde(default = "u16_53")] + #[serde(skip_serializing_if = "is_u16_53")] + pub port: u16, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigListen { + #[serde(default = "string_empty")] + #[serde(skip_serializing_if = "is_string_empty")] + pub host: String, + #[serde(default = "u16_0")] + #[serde(skip_serializing_if = "is_u16_0")] + pub port: u16, + #[serde(default = "u32_64")] + #[serde(skip_serializing_if = "is_u32_64")] + pub batch: u32, + #[serde(skip_serializing_if = "is_none")] + pub read_buffer: Option, + #[serde(skip_serializing_if = "is_none")] + pub write_buffer: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigPunchy { + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub punch: bool, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub respond: bool, + #[serde(default = "string_1s")] + #[serde(skip_serializing_if = "is_string_1s")] + pub delay: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum NebulaConfigCipher { + #[serde(rename = "aes")] + Aes, + #[serde(rename = "chachapoly")] + ChaChaPoly, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigRelay { + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub relays: Vec, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub am_relay: bool, + #[serde(default = "bool_true")] + #[serde(skip_serializing_if = "is_bool_true")] + pub use_relays: bool, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigTun { + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub disabled: bool, + #[serde(skip_serializing_if = "is_none")] + pub dev: Option, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub drop_local_broadcast: bool, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub drop_multicast: bool, + #[serde(default = "u64_500")] + #[serde(skip_serializing_if = "is_u64_500")] + pub tx_queue: u64, + #[serde(default = "u64_1300")] + #[serde(skip_serializing_if = "is_u64_1300")] + pub mtu: u64, + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub routes: Vec, + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub unsafe_routes: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigTunRouteOverride { + pub mtu: u64, + pub route: Ipv4Net, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigTunUnsafeRoute { + pub route: Ipv4Net, + pub via: Ipv4Addr, + #[serde(default = "u64_1300")] + #[serde(skip_serializing_if = "is_u64_1300")] + pub mtu: u64, + #[serde(default = "i64_100")] + #[serde(skip_serializing_if = "is_i64_100")] + pub metric: i64, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigLogging { + #[serde(default = "loglevel_info")] + #[serde(skip_serializing_if = "is_loglevel_info")] + pub level: NebulaConfigLoggingLevel, + #[serde(default = "format_text")] + #[serde(skip_serializing_if = "is_format_text")] + pub format: NebulaConfigLoggingFormat, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub disable_timestamp: bool, + #[serde(default = "timestamp")] + #[serde(skip_serializing_if = "is_timestamp")] + pub timestamp_format: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum NebulaConfigLoggingLevel { + #[serde(rename = "panic")] + Panic, + #[serde(rename = "fatal")] + Fatal, + #[serde(rename = "error")] + Error, + #[serde(rename = "warning")] + Warning, + #[serde(rename = "info")] + Info, + #[serde(rename = "debug")] + Debug, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum NebulaConfigLoggingFormat { + #[serde(rename = "json")] + Json, + #[serde(rename = "text")] + Text, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigSshd { + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub enabled: bool, + pub listen: SocketAddrV4, + pub host_key: String, + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub authorized_users: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigSshdAuthorizedUser { + pub user: String, + #[serde(default = "empty_vec")] + #[serde(skip_serializing_if = "is_empty_vec")] + pub keys: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum NebulaConfigStats { + #[serde(rename = "graphite")] + Graphite(NebulaConfigStatsGraphite), + #[serde(rename = "prometheus")] + Prometheus(NebulaConfigStatsPrometheus), +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigStatsGraphite { + #[serde(default = "string_nebula")] + #[serde(skip_serializing_if = "is_string_nebula")] + pub prefix: String, + #[serde(default = "protocol_tcp")] + #[serde(skip_serializing_if = "is_protocol_tcp")] + pub protocol: NebulaConfigStatsGraphiteProtocol, + pub host: SocketAddrV4, + pub interval: String, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub message_metrics: bool, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub lighthouse_metrics: bool, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum NebulaConfigStatsGraphiteProtocol { + #[serde(rename = "tcp")] + Tcp, + #[serde(rename = "udp")] + Udp, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigStatsPrometheus { + pub listen: String, + pub path: String, + #[serde(default = "string_nebula")] + #[serde(skip_serializing_if = "is_string_nebula")] + pub namespace: String, + #[serde(default = "string_nebula")] + #[serde(skip_serializing_if = "is_string_nebula")] + pub subsystem: String, + pub interval: String, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub message_metrics: bool, + #[serde(default = "bool_false")] + #[serde(skip_serializing_if = "is_bool_false")] + pub lighthouse_metrics: bool, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigFirewall { + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub conntrack: Option, + + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub inbound: Option>, + + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub outbound: Option>, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigFirewallConntrack { + #[serde(default = "string_12m")] + #[serde(skip_serializing_if = "is_string_12m")] + pub tcp_timeout: String, + #[serde(default = "string_3m")] + #[serde(skip_serializing_if = "is_string_3m")] + pub udp_timeout: String, + #[serde(default = "string_10m")] + #[serde(skip_serializing_if = "is_string_10m")] + pub default_timeout: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct NebulaConfigFirewallRule { + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub port: Option, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub proto: Option, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub ca_name: Option, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub ca_sha: Option, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub host: Option, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub group: Option, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub groups: Option>, + #[serde(default = "none")] + #[serde(skip_serializing_if = "is_none")] + pub cidr: Option, +} + +// Default values for serde + +fn string_12m() -> String { + "12m".to_string() +} +fn is_string_12m(s: &str) -> bool { + s == "12m" +} + +fn string_3m() -> String { + "3m".to_string() +} +fn is_string_3m(s: &str) -> bool { + s == "3m" +} + +fn string_10m() -> String { + "10m".to_string() +} +fn is_string_10m(s: &str) -> bool { + s == "10m" +} + +fn empty_vec() -> Vec { + vec![] +} +fn is_empty_vec(v: &Vec) -> bool { + v.is_empty() +} + +fn empty_hashmap() -> HashMap { + HashMap::new() +} +fn is_empty_hashmap(h: &HashMap) -> bool { + h.is_empty() +} + +fn bool_false() -> bool { + false +} +fn is_bool_false(b: &bool) -> bool { + !*b +} + +fn bool_true() -> bool { + true +} +fn is_bool_true(b: &bool) -> bool { + *b +} + +fn u16_53() -> u16 { + 53 +} +fn is_u16_53(u: &u16) -> bool { + *u == 53 +} + +fn u32_10() -> u32 { + 10 +} +fn is_u32_10(u: &u32) -> bool { + *u == 10 +} + +fn u16_0() -> u16 { + 0 +} +fn is_u16_0(u: &u16) -> bool { + *u == 0 +} + +fn u32_64() -> u32 { + 64 +} +fn is_u32_64(u: &u32) -> bool { + *u == 64 +} + +fn string_1s() -> String { + "1s".to_string() +} +fn is_string_1s(s: &str) -> bool { + s == "1s" +} + +fn cipher_aes() -> NebulaConfigCipher { + NebulaConfigCipher::Aes +} +fn is_cipher_aes(c: &NebulaConfigCipher) -> bool { + matches!(c, NebulaConfigCipher::Aes) +} + +fn u64_500() -> u64 { + 500 +} +fn is_u64_500(u: &u64) -> bool { + *u == 500 +} + +fn u64_1300() -> u64 { + 1300 +} +fn is_u64_1300(u: &u64) -> bool { + *u == 1300 +} + +fn i64_100() -> i64 { + 100 +} +fn is_i64_100(i: &i64) -> bool { + *i == 100 +} + +fn loglevel_info() -> NebulaConfigLoggingLevel { + NebulaConfigLoggingLevel::Info +} +fn is_loglevel_info(l: &NebulaConfigLoggingLevel) -> bool { + matches!(l, NebulaConfigLoggingLevel::Info) +} + +fn format_text() -> NebulaConfigLoggingFormat { + NebulaConfigLoggingFormat::Text +} +fn is_format_text(f: &NebulaConfigLoggingFormat) -> bool { + matches!(f, NebulaConfigLoggingFormat::Text) +} + +fn timestamp() -> String { + "2006-01-02T15:04:05Z07:00".to_string() +} +fn is_timestamp(s: &str) -> bool { + s == "2006-01-02T15:04:05Z07:00" +} + +fn u64_1() -> u64 { + 1 +} +fn is_u64_1(u: &u64) -> bool { + *u == 1 +} + +fn string_nebula() -> String { + "nebula".to_string() +} +fn is_string_nebula(s: &str) -> bool { + s == "nebula" +} + +fn string_empty() -> String { + String::new() +} +fn is_string_empty(s: &str) -> bool { + s.is_empty() +} + +fn protocol_tcp() -> NebulaConfigStatsGraphiteProtocol { + NebulaConfigStatsGraphiteProtocol::Tcp +} +fn is_protocol_tcp(p: &NebulaConfigStatsGraphiteProtocol) -> bool { + matches!(p, NebulaConfigStatsGraphiteProtocol::Tcp) +} + +fn none() -> Option { + None +} +fn is_none(o: &Option) -> bool { + o.is_none() +} diff --git a/trifid-api/src/keystore.rs b/trifid-api/src/keystore.rs new file mode 100644 index 0000000..c9b6a9b --- /dev/null +++ b/trifid-api/src/keystore.rs @@ -0,0 +1,83 @@ +use crate::config::{NebulaConfig, CONFIG}; +use ed25519_dalek::{SigningKey, VerifyingKey}; +use serde::{Deserialize, Serialize}; +use std::error::Error; +use std::fs; +use trifid_pki::cert::NebulaCertificate; +use trifid_pki::x25519_dalek::PublicKey; + +#[derive(Serialize, Deserialize)] +pub struct Keystore { + #[serde(default = "default_vec")] + pub hosts: Vec, +} + +fn default_vec() -> Vec { + vec![] +} + +pub fn keystore_init() -> Result> { + let mut ks_fp = CONFIG.crypto.local_keystore_directory.clone(); + ks_fp.push("/tfks.toml"); + + if !ks_fp.exists() { + return Ok(Keystore { + hosts: vec![] + }) + } + + let f_str = fs::read_to_string(ks_fp)?; + let keystore: Keystore = toml::from_str(&f_str)?; + + Ok(keystore) +} + +pub fn keystore_flush(ks: &Keystore) -> Result<(), Box> { + let mut ks_fp = CONFIG.crypto.local_keystore_directory.clone(); + ks_fp.push("/tfks.toml"); + + fs::write(ks_fp, toml::to_string(ks)?)?; + + Ok(()) +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct KeystoreHostInformation { + pub id: String, + + pub current_signing_key: u64, + pub current_client_key: u64, + + pub current_config: u64, + pub current_cert: u64, + + pub certs: Vec, + pub config: Vec, + pub signing_keys: Vec, + pub client_keys: Vec, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct KSCert { + pub id: u64, + pub cert: NebulaCertificate, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct KSConfig { + pub id: u64, + pub config: NebulaConfig, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct KSSigningKey { + pub id: u64, + pub key: SigningKey, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct KSClientKey { + pub id: u64, + pub dh_pub: PublicKey, + pub ed_pub: VerifyingKey, +} diff --git a/trifid-api/src/main.rs b/trifid-api/src/main.rs index 9e588f6..eaa6534 100644 --- a/trifid-api/src/main.rs +++ b/trifid-api/src/main.rs @@ -26,14 +26,17 @@ use std::time::Duration; use crate::config::CONFIG; use crate::error::{APIError, APIErrorsResponse}; +use crate::keystore::{keystore_init, Keystore}; use crate::tokens::random_id_no_id; use trifid_api_migration::{Migrator, MigratorTrait}; pub mod auth_tokens; +pub mod codegen; pub mod config; pub mod crypto; pub mod cursor; pub mod error; +pub mod keystore; pub mod magic_link; pub mod routes; pub mod timers; @@ -41,12 +44,17 @@ pub mod tokens; pub struct AppState { pub conn: DatabaseConnection, + pub keystore: Keystore, } #[actix_web::main] async fn main() -> Result<(), Box> { simple_logger::init_with_level(Level::Debug).unwrap(); + info!("Creating keystore..."); + + let keystore = keystore_init()?; + info!("Connecting to database at {}...", CONFIG.database.url); let mut opt = ConnectOptions::new(CONFIG.database.url.clone()); @@ -64,7 +72,7 @@ async fn main() -> Result<(), Box> { info!("Performing database migration..."); Migrator::up(&db, None).await?; - let data = Data::new(AppState { conn: db }); + let data = Data::new(AppState { conn: db, keystore }); HttpServer::new(move || { App::new() @@ -103,6 +111,7 @@ async fn main() -> Result<(), Box> { .service(routes::v1::hosts::block_host) .service(routes::v1::hosts::enroll_host) .service(routes::v1::hosts::create_host_and_enrollment_code) + .service(routes::v2::enroll::enroll) }) .bind(CONFIG.server.bind)? .run() diff --git a/trifid-api/src/routes/mod.rs b/trifid-api/src/routes/mod.rs index a3a6d96..ae6adc7 100644 --- a/trifid-api/src/routes/mod.rs +++ b/trifid-api/src/routes/mod.rs @@ -1 +1,2 @@ pub mod v1; +pub mod v2; diff --git a/trifid-api/src/routes/v1/organization.rs b/trifid-api/src/routes/v1/organization.rs index bd11080..e4c51ec 100644 --- a/trifid-api/src/routes/v1/organization.rs +++ b/trifid-api/src/routes/v1/organization.rs @@ -33,7 +33,7 @@ use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, Query use serde::{Deserialize, Serialize}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use trifid_api_entities::entity::{network, organization, signing_ca}; -use trifid_pki::cert::{serialize_x25519_private, NebulaCertificate, NebulaCertificateDetails}; +use trifid_pki::cert::{serialize_x25519_private, NebulaCertificate, NebulaCertificateDetails, serialize_ed25519_private}; use trifid_pki::ed25519_dalek::SigningKey; use trifid_pki::rand_core::OsRng; @@ -146,7 +146,7 @@ pub async fn create_org_request( } // PEM-encode the CA key - let ca_key_pem = serialize_x25519_private(&private_key.to_keypair_bytes()); + let ca_key_pem = serialize_ed25519_private(&private_key.to_keypair_bytes()); // PEM-encode the CA cert let ca_cert_pem = match cert.serialize_to_pem() { Ok(pem) => pem, @@ -204,8 +204,8 @@ pub async fn create_org_request( let signing_ca = signing_ca::Model { id: random_id("ca"), organization: org.id.clone(), - cert: ca_key_encrypted, - key: ca_crt, + cert: ca_crt, + key: ca_key_encrypted, expires: cert .details .not_after diff --git a/trifid-api/src/routes/v2/enroll.rs b/trifid-api/src/routes/v2/enroll.rs new file mode 100644 index 0000000..13894d6 --- /dev/null +++ b/trifid-api/src/routes/v2/enroll.rs @@ -0,0 +1,233 @@ +use actix_web::web::{Data, Json}; +use actix_web::{post, HttpRequest, HttpResponse}; +use dnapi_rs::message::{ + APIError, EnrollRequest, EnrollResponse, EnrollResponseData, EnrollResponseDataOrg, +}; +use ed25519_dalek::{SigningKey, VerifyingKey}; +use log::{debug, error, trace}; +use rand::rngs::OsRng; +use sea_orm::{ColumnTrait, EntityTrait, ModelTrait, QueryFilter}; + +use crate::codegen::{collect_info, generate_config}; +use crate::keystore::{KSCert, KSClientKey, KSConfig, KSSigningKey, KeystoreHostInformation}; +use crate::AppState; +use trifid_api_entities::entity::host_enrollment_code; +use trifid_pki::cert::{ + deserialize_ed25519_public, deserialize_x25519_public, serialize_ed25519_public, +}; +use trifid_pki::x25519_dalek::PublicKey; + +use crate::timers::expired; + +#[post("/v2/enroll")] +pub async fn enroll( + req: Json, + _req_info: HttpRequest, + db: Data, +) -> HttpResponse { + debug!("{:x?} {:x?}", req.dh_pubkey, req.ed_pubkey); + + // pull enroll information from the db + let code_info = match host_enrollment_code::Entity::find() + .filter(host_enrollment_code::Column::Id.eq(&req.code)) + .one(&db.conn) + .await + { + Ok(ci) => ci, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: + "There was an error with the database request. Please try again later." + .to_string(), + path: None, + }], + }); + } + }; + + let enroll_info = match code_info { + Some(ei) => ei, + None => { + return HttpResponse::Unauthorized().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "That code is invalid or has expired.".to_string(), + path: None, + }], + }) + } + }; + + if expired(enroll_info.expires_on as u64) { + return HttpResponse::Unauthorized().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "That code is invalid or has expired.".to_string(), + path: None, + }], + }); + } + + // deserialize + let dh_pubkey = match deserialize_x25519_public(&req.dh_pubkey) { + Ok(k) => k, + Err(e) => { + error!("public key deserialization error: {}", e); + return HttpResponse::BadRequest().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_BAD_DH_PUB".to_string(), + message: "Unable to deserialize the DH public key.".to_string(), + path: None, + }], + }); + } + }; + let ed_pubkey = match deserialize_ed25519_public(&req.ed_pubkey) { + Ok(k) => k, + Err(e) => { + error!("public key deserialization error: {}", e); + return HttpResponse::BadRequest().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_BAD_ED_PUB".to_string(), + message: "Unable to deserialize the ED25519 public key.".to_string(), + path: None, + }], + }); + } + }; + + // destroy the enrollment code before doing anything else + + match enroll_info.clone().delete(&db.conn).await { + Ok(_) => (), + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: + "There was an error with the database request. Please try again later." + .to_string(), + path: None, + }], + }); + } + } + + let info = match collect_info(&db, &enroll_info.host, &dh_pubkey).await { + Ok(i) => i, + Err(e) => { + return HttpResponse::InternalServerError().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_CFG_GENERATION_ERROR".to_string(), + message: e.to_string(), + path: None, + }], + }); + } + }; + + // codegen: handoff to dedicated codegen module, we have collected all information + let (cfg, cert) = match generate_config(&db, &info).await { + Ok(cfg) => cfg, + Err(e) => { + error!("error generating configuration: {}", e); + return HttpResponse::InternalServerError().json(EnrollResponse::Error { + errors: vec![APIError { + code: "ERR_CFG_GENERATION_ERROR".to_string(), + message: "There was an error generating the host configuration.".to_string(), + path: None, + }], + }); + } + }; + + let host_in_ks = db.keystore.hosts.iter().find(|u| u.id == enroll_info.id); + + let host_in_ks = match host_in_ks { + Some(ksinfo) => { + let mut ks = ksinfo.clone(); + + ks.certs.push(KSCert { + id: ks.current_cert + 1, + cert, + }); + ks.current_cert += 1; + + ks.config.push(KSConfig { + id: ks.current_config + 1, + config: cfg.clone(), + }); + ks.current_config += 1; + + ks.signing_keys.push(KSSigningKey { + id: ks.current_signing_key, + key: SigningKey::generate(&mut OsRng), + }); + ks.current_signing_key += 1; + + let dh_pubkey_typed: [u8; 32] = dh_pubkey.clone().try_into().unwrap(); + + ks.client_keys.push(KSClientKey { + id: ks.current_client_key + 1, + dh_pub: PublicKey::from(dh_pubkey_typed), + ed_pub: VerifyingKey::from_bytes(&ed_pubkey.try_into().unwrap()).unwrap(), + }); + ks.current_client_key += 1; + + ks + } + None => { + let dh_pubkey_typed: [u8; 32] = dh_pubkey.clone().try_into().unwrap(); + + KeystoreHostInformation { + id: enroll_info.id.clone(), + current_signing_key: 1, + current_client_key: 1, + current_config: 1, + current_cert: 1, + certs: vec![KSCert { id: 1, cert }], + config: vec![KSConfig { + id: 1, + config: cfg.clone(), + }], + signing_keys: vec![KSSigningKey { + id: 1, + key: SigningKey::generate(&mut OsRng), + }], + client_keys: vec![KSClientKey { + id: 1, + dh_pub: PublicKey::from(dh_pubkey_typed), + ed_pub: VerifyingKey::from_bytes(&ed_pubkey.try_into().unwrap()).unwrap(), + }], + } + } + }; + + HttpResponse::Ok().json(EnrollResponse::Success { + data: EnrollResponseData { + config: match serde_yaml::to_string(&cfg) { + Ok(cfg) => cfg.as_bytes().to_vec(), + Err(e) => { + error!("serialization error: {}", e); + return HttpResponse::InternalServerError().json(EnrollResponse::Error { + errors: vec![ + APIError { + code: "ERR_CFG_SERIALIZATION_ERROR".to_string(), + message: "There was an error serializing the node's configuration. Please try again later.".to_string(), + path: None, + } + ], + }); + } + }, + host_id: enroll_info.host.clone(), + counter: host_in_ks.current_config as u32, + trusted_keys: serialize_ed25519_public(host_in_ks.signing_keys.iter().find(|u| u.id == host_in_ks.current_signing_key).unwrap().key.verifying_key().as_bytes().as_slice()).to_vec(), + organization: EnrollResponseDataOrg { id: info.organization.id.clone(), name: info.organization.name.clone() }, + }, + }) +} diff --git a/trifid-api/src/routes/v2/mod.rs b/trifid-api/src/routes/v2/mod.rs new file mode 100644 index 0000000..fa6b687 --- /dev/null +++ b/trifid-api/src/routes/v2/mod.rs @@ -0,0 +1 @@ +pub mod enroll; diff --git a/trifid-pki/Cargo.toml b/trifid-pki/Cargo.toml index 4910beb..44351b8 100644 --- a/trifid-pki/Cargo.toml +++ b/trifid-pki/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trifid-pki" -version = "0.1.10" +version = "0.1.11" edition = "2021" description = "A rust implementation of the Nebula PKI system" license = "AGPL-3.0-or-later" diff --git a/trifid-pki/src/ca.rs b/trifid-pki/src/ca.rs index d5b3aaf..da7903a 100644 --- a/trifid-pki/src/ca.rs +++ b/trifid-pki/src/ca.rs @@ -1,14 +1,14 @@ //! Structs to represent a pool of CA's and blacklisted certificates +use crate::cert::{deserialize_nebula_certificate_from_pem, NebulaCertificate}; +use ed25519_dalek::VerifyingKey; use std::collections::HashMap; use std::error::Error; use std::fmt::{Display, Formatter}; use std::time::SystemTime; -use ed25519_dalek::VerifyingKey; -use crate::cert::{deserialize_nebula_certificate_from_pem, NebulaCertificate}; #[cfg(feature = "serde_derive")] -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; /// A pool of trusted CA certificates, and certificates that should be blocked. /// This is equivalent to the `pki` section in a typical Nebula config.yml. @@ -20,7 +20,7 @@ pub struct NebulaCAPool { /// The list of blocklisted certificate fingerprints pub cert_blocklist: Vec, /// True if any of the member CAs certificates are expired. Must be handled. - pub expired: bool + pub expired: bool, } impl NebulaCAPool { @@ -41,8 +41,12 @@ impl NebulaCAPool { for cert in pems { match pool.add_ca_certificate(pem::encode(&cert).as_bytes()) { - Ok(did_expire) => if did_expire { pool.expired = true }, - Err(e) => return Err(e) + Ok(did_expire) => { + if did_expire { + pool.expired = true + } + } + Err(e) => return Err(e), } } @@ -56,21 +60,23 @@ impl NebulaCAPool { let cert = deserialize_nebula_certificate_from_pem(bytes)?; if !cert.details.is_ca { - return Err(CaPoolError::NotACA.into()) + return Err(CaPoolError::NotACA.into()); } if !cert.check_signature(&VerifyingKey::from_bytes(&cert.details.public_key)?)? { - return Err(CaPoolError::NotSelfSigned.into()) + return Err(CaPoolError::NotSelfSigned.into()); } let fingerprint = cert.sha256sum()?; let expired = cert.expired(SystemTime::now()); - if expired { self.expired = true } + if expired { + self.expired = true + } self.cas.insert(fingerprint, cert); - Ok(expired) + Ok(expired) } /// Blocklist the given certificate in the CA pool @@ -92,9 +98,12 @@ impl NebulaCAPool { /// Gets the CA certificate used to sign the given certificate /// # Errors /// This function will return an error if the certificate does not have an issuer attached (it is self-signed) - pub fn get_ca_for_cert(&self, cert: &NebulaCertificate) -> Result, Box> { + pub fn get_ca_for_cert( + &self, + cert: &NebulaCertificate, + ) -> Result, Box> { if cert.details.issuer == String::new() { - return Err(CaPoolError::NoIssuer.into()) + return Err(CaPoolError::NoIssuer.into()); } Ok(self.cas.get(&cert.details.issuer)) @@ -115,7 +124,7 @@ pub enum CaPoolError { /// Tried to add a non-self-signed cert to the CA pool (all CAs must be root certificates) NotSelfSigned, /// Tried to look up a certificate that does not have an issuer field - NoIssuer + NoIssuer, } impl Error for CaPoolError {} #[cfg(not(tarpaulin_include))] @@ -127,4 +136,4 @@ impl Display for CaPoolError { Self::NoIssuer => write!(f, "Tried to look up a certificate with a null issuer field") } } -} \ No newline at end of file +} diff --git a/trifid-pki/src/cert.rs b/trifid-pki/src/cert.rs index 60b43fd..1ed09d4 100644 --- a/trifid-pki/src/cert.rs +++ b/trifid-pki/src/cert.rs @@ -1,22 +1,22 @@ //! Manage Nebula PKI Certificates //! This is pretty much a direct port of nebula/cert/cert.go +use crate::ca::NebulaCAPool; +use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails}; +use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; +use ipnet::Ipv4Net; +use pem::Pem; +use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; +use sha2::Digest; +use sha2::Sha256; use std::error::Error; use std::fmt::{Display, Formatter}; use std::net::Ipv4Addr; use std::ops::Add; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; -use ipnet::{Ipv4Net}; -use pem::Pem; -use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; -use sha2::Sha256; -use crate::ca::NebulaCAPool; -use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails}; -use sha2::Digest; #[cfg(feature = "serde_derive")] -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; /// The length, in bytes, of public keys pub const PUBLIC_KEY_LENGTH: i32 = 32; @@ -39,7 +39,7 @@ pub struct NebulaCertificate { /// The signed data of this certificate pub details: NebulaCertificateDetails, /// The Ed25519 signature of this certificate - pub signature: Vec + pub signature: Vec, } /// The signed details contained in a Nebula PKI certificate @@ -63,7 +63,7 @@ pub struct NebulaCertificateDetails { /// Is this node a CA? pub is_ca: bool, /// SHA256 of issuer certificate. If blank, this cert is self-signed. - pub issuer: String + pub issuer: String, } /// A list of errors that can occur parsing certificates @@ -87,7 +87,7 @@ pub enum CertificateError { /// This certificate either is not yet valid or has already expired Expired, /// The public key does not match the expected value - KeyMismatch + KeyMismatch, } #[cfg(not(tarpaulin_include))] impl Display for CertificateError { @@ -95,13 +95,29 @@ impl Display for CertificateError { match self { Self::EmptyByteArray => write!(f, "Certificate bytearray is empty"), Self::NilDetails => write!(f, "The encoded Details field is null"), - Self::IpsNotPairs => write!(f, "encoded IPs should be in pairs, an odd number was found"), - Self::SubnetsNotPairs => write!(f, "encoded subnets should be in pairs, an odd number was found"), - Self::WrongSigLength => write!(f, "Signature should be 64 bytes but is a different size"), - Self::WrongKeyLength => write!(f, "Public keys are expected to be 32 bytes but the public key on this cert is not"), - Self::WrongPemTag => write!(f, "Certificates should have the PEM tag `NEBULA CERTIFICATE`, but this block did not"), - Self::Expired => write!(f, "This certificate either is not yet valid or has already expired"), - Self::KeyMismatch => write!(f, "Key does not match expected value") + Self::IpsNotPairs => { + write!(f, "encoded IPs should be in pairs, an odd number was found") + } + Self::SubnetsNotPairs => write!( + f, + "encoded subnets should be in pairs, an odd number was found" + ), + Self::WrongSigLength => { + write!(f, "Signature should be 64 bytes but is a different size") + } + Self::WrongKeyLength => write!( + f, + "Public keys are expected to be 32 bytes but the public key on this cert is not" + ), + Self::WrongPemTag => write!( + f, + "Certificates should have the PEM tag `NEBULA CERTIFICATE`, but this block did not" + ), + Self::Expired => write!( + f, + "This certificate either is not yet valid or has already expired" + ), + Self::KeyMismatch => write!(f, "Key does not match expected value"), } } } @@ -110,7 +126,10 @@ impl Error for CertificateError {} fn map_cidr_pairs(pairs: &[u32]) -> Result, Box> { let mut res_vec = vec![]; for pair in pairs.chunks(2) { - res_vec.push(Ipv4Net::with_netmask(Ipv4Addr::from(pair[0]), Ipv4Addr::from(pair[1]))?); + res_vec.push(Ipv4Net::with_netmask( + Ipv4Addr::from(pair[0]), + Ipv4Addr::from(pair[1]), + )?); } Ok(res_vec) } @@ -129,7 +148,11 @@ impl Display for NebulaCertificate { writeln!(f, " Not after: {:?}", self.details.not_after)?; writeln!(f, " Is CA: {}", self.details.is_ca)?; writeln!(f, " Issuer: {}", self.details.issuer)?; - writeln!(f, " Public key: {}", hex::encode(self.details.public_key))?; + writeln!( + f, + " Public key: {}", + hex::encode(self.details.public_key) + )?; writeln!(f, " }}")?; writeln!(f, " Fingerprint: {}", self.sha256sum().unwrap())?; writeln!(f, " Signature: {}", hex::encode(self.signature.clone()))?; @@ -143,7 +166,7 @@ impl Display for NebulaCertificate { /// # Panics pub fn deserialize_nebula_certificate(bytes: &[u8]) -> Result> { if bytes.is_empty() { - return Err(CertificateError::EmptyByteArray.into()) + return Err(CertificateError::EmptyByteArray.into()); } let mut reader = BytesReader::from_bytes(bytes); @@ -153,11 +176,11 @@ pub fn deserialize_nebula_certificate(bytes: &[u8]) -> Result Result Result) -> std::fmt::Result { match self { - Self::WrongPemTag => write!(f, "Keys should have their associated PEM tags but this had the wrong one"), + Self::WrongPemTag => write!( + f, + "Keys should have their associated PEM tags but this had the wrong one" + ), Self::Not64Bytes => write!(f, "Ed25519 private keys are 64 bytes"), - Self::Not32Bytes => write!(f, "X25519 private keys are 32 bytes") + Self::Not32Bytes => write!(f, "X25519 private keys are 32 bytes"), } } } impl Error for KeyError {} - /// Deserialize the first PEM block in the given byte array into a `NebulaCertificate` /// # Errors /// This function will return an error if the PEM data is invalid, or if there is an error parsing the certificate (see `deserialize_nebula_certificate`) -pub fn deserialize_nebula_certificate_from_pem(bytes: &[u8]) -> Result> { +pub fn deserialize_nebula_certificate_from_pem( + bytes: &[u8], +) -> Result> { let pem = pem::parse(bytes)?; if pem.tag != CERT_BANNER { - return Err(CertificateError::WrongPemTag.into()) + return Err(CertificateError::WrongPemTag.into()); } deserialize_nebula_certificate(&pem.contents) } @@ -230,7 +265,9 @@ pub fn serialize_x25519_private(bytes: &[u8]) -> Vec { pem::encode(&Pem { tag: X25519_PRIVATE_KEY_BANNER.to_string(), contents: bytes.to_vec(), - }).as_bytes().to_vec() + }) + .as_bytes() + .to_vec() } /// Simple helper to PEM encode an X25519 public key @@ -238,7 +275,9 @@ pub fn serialize_x25519_public(bytes: &[u8]) -> Vec { pem::encode(&Pem { tag: X25519_PUBLIC_KEY_BANNER.to_string(), contents: bytes.to_vec(), - }).as_bytes().to_vec() + }) + .as_bytes() + .to_vec() } /// Attempt to deserialize a PEM encoded X25519 private key @@ -247,10 +286,10 @@ pub fn serialize_x25519_public(bytes: &[u8]) -> Vec { pub fn deserialize_x25519_private(bytes: &[u8]) -> Result, Box> { let pem = pem::parse(bytes)?; if pem.tag != X25519_PRIVATE_KEY_BANNER { - return Err(KeyError::WrongPemTag.into()) + return Err(KeyError::WrongPemTag.into()); } if pem.contents.len() != 32 { - return Err(KeyError::Not32Bytes.into()) + return Err(KeyError::Not32Bytes.into()); } Ok(pem.contents) } @@ -261,10 +300,10 @@ pub fn deserialize_x25519_private(bytes: &[u8]) -> Result, Box Result, Box> { let pem = pem::parse(bytes)?; if pem.tag != X25519_PUBLIC_KEY_BANNER { - return Err(KeyError::WrongPemTag.into()) + return Err(KeyError::WrongPemTag.into()); } if pem.contents.len() != 32 { - return Err(KeyError::Not32Bytes.into()) + return Err(KeyError::Not32Bytes.into()); } Ok(pem.contents) } @@ -274,7 +313,9 @@ pub fn serialize_ed25519_private(bytes: &[u8]) -> Vec { pem::encode(&Pem { tag: ED25519_PRIVATE_KEY_BANNER.to_string(), contents: bytes.to_vec(), - }).as_bytes().to_vec() + }) + .as_bytes() + .to_vec() } /// Simple helper to PEM encode an Ed25519 public key @@ -282,7 +323,9 @@ pub fn serialize_ed25519_public(bytes: &[u8]) -> Vec { pem::encode(&Pem { tag: ED25519_PUBLIC_KEY_BANNER.to_string(), contents: bytes.to_vec(), - }).as_bytes().to_vec() + }) + .as_bytes() + .to_vec() } /// Attempt to deserialize a PEM encoded Ed25519 private key @@ -291,10 +334,10 @@ pub fn serialize_ed25519_public(bytes: &[u8]) -> Vec { pub fn deserialize_ed25519_private(bytes: &[u8]) -> Result, Box> { let pem = pem::parse(bytes)?; if pem.tag != ED25519_PRIVATE_KEY_BANNER { - return Err(KeyError::WrongPemTag.into()) + return Err(KeyError::WrongPemTag.into()); } if pem.contents.len() != 64 { - return Err(KeyError::Not64Bytes.into()) + return Err(KeyError::Not64Bytes.into()); } Ok(pem.contents) } @@ -305,10 +348,10 @@ pub fn deserialize_ed25519_private(bytes: &[u8]) -> Result, Box Result, Box> { let pem = pem::parse(bytes)?; if pem.tag != ED25519_PUBLIC_KEY_BANNER { - return Err(KeyError::WrongPemTag.into()) + return Err(KeyError::WrongPemTag.into()); } if pem.contents.len() != 32 { - return Err(KeyError::Not32Bytes.into()) + return Err(KeyError::Not32Bytes.into()); } Ok(pem.contents) } @@ -322,10 +365,10 @@ pub fn deserialize_ed25519_public_many(bytes: &[u8]) -> Result>, Box for pem in pems { if pem.tag != ED25519_PUBLIC_KEY_BANNER { - return Err(KeyError::WrongPemTag.into()) + return Err(KeyError::WrongPemTag.into()); } if pem.contents.len() != 32 { - return Err(KeyError::Not32Bytes.into()) + return Err(KeyError::Not32Bytes.into()); } keys.push(pem.contents); } @@ -367,7 +410,11 @@ impl NebulaCertificate { /// Verify will ensure a certificate is good in all respects (expiry, group membership, signature, cert blocklist, etc) /// # Errors /// This function will return an error if there is an error parsing the cert or the CA pool. - pub fn verify(&self, time: SystemTime, ca_pool: &NebulaCAPool) -> Result> { + pub fn verify( + &self, + time: SystemTime, + ca_pool: &NebulaCAPool, + ) -> Result> { if ca_pool.is_blocklisted(self) { return Ok(CertificateValidity::Blocklisted); } @@ -375,15 +422,15 @@ impl NebulaCertificate { let Some(signer) = ca_pool.get_ca_for_cert(self)? else { return Ok(CertificateValidity::NotSignedByThisCAPool) }; if signer.expired(time) { - return Ok(CertificateValidity::RootCertExpired) + return Ok(CertificateValidity::RootCertExpired); } if self.expired(time) { - return Ok(CertificateValidity::CertExpired) + return Ok(CertificateValidity::CertExpired); } if !self.check_signature(&VerifyingKey::from_bytes(&signer.details.public_key)?)? { - return Ok(CertificateValidity::BadSignature) + return Ok(CertificateValidity::BadSignature); } Ok(self.check_root_constraints(signer)) @@ -392,7 +439,10 @@ impl NebulaCertificate { /// Make sure that this certificate does not break any of the constraints set by the signing certificate pub fn check_root_constraints(&self, signer: &Self) -> CertificateValidity { // Make sure this cert doesn't expire after the signer - println!("{:?} {:?}", signer.details.not_before, self.details.not_before); + println!( + "{:?} {:?}", + signer.details.not_before, self.details.not_before + ); if signer.details.not_before < self.details.not_before { return CertificateValidity::CertExpiresAfterSigner; } @@ -404,7 +454,10 @@ impl NebulaCertificate { // If the signer contains a limited set of groups, make sure this cert only has a subset of them if !signer.details.groups.is_empty() { - println!("root groups: {:?}, child groups: {:?}", signer.details.groups, self.details.groups); + println!( + "root groups: {:?}, child groups: {:?}", + signer.details.groups, self.details.groups + ); for group in &self.details.groups { if !signer.details.groups.contains(group) { return CertificateValidity::GroupNotPresentOnSigner; @@ -443,10 +496,9 @@ impl NebulaCertificate { if self.details.is_ca { // convert the keys if key.len() != 64 { - return Err("key not 64-bytes long".into()) + return Err("key not 64-bytes long".into()); } - let secret = SigningKey::from_keypair_bytes(key.try_into().unwrap())?; let pub_key = secret.verifying_key().to_bytes(); if pub_key != self.details.public_key { @@ -457,13 +509,17 @@ impl NebulaCertificate { } if key.len() != 32 { - return Err("key not 32-bytes long".into()) + return Err("key not 32-bytes long".into()); } let pubkey_raw = SigningKey::from_bytes(key.try_into()?).verifying_key(); let pubkey = pubkey_raw.as_bytes(); - println!("{} {}", hex::encode(pubkey), hex::encode(self.details.public_key)); + println!( + "{} {}", + hex::encode(pubkey), + hex::encode(self.details.public_key) + ); if *pubkey != self.details.public_key { return Err(CertificateError::KeyMismatch.into()); } @@ -471,21 +527,34 @@ impl NebulaCertificate { Ok(()) } - /// Get a protobuf-ready raw struct, ready for serialization #[allow(clippy::expect_used)] #[allow(clippy::cast_possible_wrap)] /// # Panics /// This function will panic if time went backwards, or if the certificate contains extremely invalid data. pub fn get_raw_details(&self) -> RawNebulaCertificateDetails { - let mut raw = RawNebulaCertificateDetails { Name: self.details.name.clone(), Ips: vec![], Subnets: vec![], - Groups: self.details.groups.iter().map(std::convert::Into::into).collect(), - NotBefore: self.details.not_before.duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64, - NotAfter: self.details.not_after.duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64, + Groups: self + .details + .groups + .iter() + .map(std::convert::Into::into) + .collect(), + NotBefore: self + .details + .not_before + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs() as i64, + NotAfter: self + .details + .not_after + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs() as i64, PublicKey: self.details.public_key.into(), IsCA: self.details.is_ca, Issuer: hex::decode(&self.details.issuer).expect("Issuer was not a hex-encoded value"), @@ -529,7 +598,9 @@ impl NebulaCertificate { Ok(pem::encode(&Pem { tag: CERT_BANNER.to_string(), contents: pbuf_bytes, - }).as_bytes().to_vec()) + }) + .as_bytes() + .to_vec()) } /// Get the fingerprint of this certificate @@ -570,7 +641,7 @@ pub enum CertificateValidity { /// An IP present on this certificate is not present on the signer's certificate IPNotPresentOnSigner, /// A subnet on this certificate is not present on the signer's certificate - SubnetNotPresentOnSigner + SubnetNotPresentOnSigner, } fn net_match(cert_ip: Ipv4Net, root_ips: &Vec) -> bool { @@ -580,4 +651,4 @@ fn net_match(cert_ip: Ipv4Net, root_ips: &Vec) -> bool { } } false -} \ No newline at end of file +} diff --git a/trifid-pki/src/lib.rs b/trifid-pki/src/lib.rs index 4c67fe0..da5610d 100644 --- a/trifid-pki/src/lib.rs +++ b/trifid-pki/src/lib.rs @@ -32,7 +32,6 @@ //! // } //! ``` - #![warn(clippy::pedantic)] #![warn(clippy::nursery)] #![deny(clippy::unwrap_used)] @@ -46,8 +45,8 @@ #![allow(clippy::module_name_repetitions)] pub use ed25519_dalek; -pub use x25519_dalek; pub use rand_core; +pub use x25519_dalek; extern crate core; @@ -60,4 +59,4 @@ pub(crate) mod cert_codec; pub mod test; /// Get the compiled version of trifid-pki. -pub const TRIFID_PKI_VERSION: &str = env!("CARGO_PKG_VERSION"); \ No newline at end of file +pub const TRIFID_PKI_VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/trifid-pki/src/test.rs b/trifid-pki/src/test.rs index 2a409c5..a74bd1c 100644 --- a/trifid-pki/src/test.rs +++ b/trifid-pki/src/test.rs @@ -1,18 +1,24 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::expect_used)] +use crate::ca::NebulaCAPool; +use crate::cert::{ + deserialize_ed25519_private, deserialize_ed25519_public, deserialize_ed25519_public_many, + deserialize_nebula_certificate, deserialize_nebula_certificate_from_pem, + deserialize_x25519_private, deserialize_x25519_public, serialize_ed25519_private, + serialize_ed25519_public, serialize_x25519_private, serialize_x25519_public, + CertificateValidity, NebulaCertificate, NebulaCertificateDetails, +}; +use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails}; use crate::netmask; -use std::net::Ipv4Addr; -use std::ops::{Add, Sub}; -use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH}; -use ipnet::Ipv4Net; -use crate::cert::{CertificateValidity, deserialize_ed25519_private, deserialize_ed25519_public, deserialize_ed25519_public_many, deserialize_nebula_certificate, deserialize_nebula_certificate_from_pem, deserialize_x25519_private, deserialize_x25519_public, NebulaCertificate, NebulaCertificateDetails, serialize_ed25519_private, serialize_ed25519_public, serialize_x25519_private, serialize_x25519_public}; -use std::str::FromStr; use ed25519_dalek::{SigningKey, VerifyingKey}; +use ipnet::Ipv4Net; use quick_protobuf::{MessageWrite, Writer}; use rand::rngs::OsRng; -use crate::ca::{NebulaCAPool}; -use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails}; +use std::net::Ipv4Addr; +use std::ops::{Add, Sub}; +use std::str::FromStr; +use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH}; /// This is a cert that we (e3team) actually use in production, and it's a known-good certificate. pub const KNOWN_GOOD_CERT: &[u8; 258] = b"-----BEGIN NEBULA CERTIFICATE-----\nCkkKF2UzdGVhbSBJbnRlcm5hbCBOZXR3b3JrKJWev5wGMJWFxKsGOiCvpwoHyKY5\n8Q5+2XxDjtoCf/zlNY/EUdB8bwXQSwEo50ABEkB0Dx76lkMqc3IyH5+ml2dKjTyv\nB4Jiw6x3abf5YZcf8rDuVEgQpvFdJmo3xJyIb3C9vKZ6kXsUxjw6s1JdWgkA\n-----END NEBULA CERTIFICATE-----"; @@ -29,14 +35,18 @@ fn certificate_serialization() { ips: vec![ netmask!("10.1.1.1", "255.255.255.0"), netmask!("10.1.1.2", "255.255.0.0"), - netmask!("10.1.1.3", "255.0.0.0") + netmask!("10.1.1.3", "255.0.0.0"), ], subnets: vec![ netmask!("9.1.1.1", "255.255.255.128"), netmask!("9.1.1.2", "255.255.255.0"), - netmask!("9.1.1.3", "255.255.0.0") + netmask!("9.1.1.3", "255.255.0.0"), + ], + groups: vec![ + "test-group1".to_string(), + "test-group2".to_string(), + "test-group3".to_string(), ], - groups: vec!["test-group1".to_string(), "test-group2".to_string(), "test-group3".to_string()], not_before: before, not_after: after, public_key: *pub_key, @@ -59,17 +69,29 @@ fn certificate_serialization() { assert_eq!(cert.details.ips.len(), deserialized.details.ips.len()); for item in &cert.details.ips { - assert!(deserialized.details.ips.contains(item), "deserialized does not contain from source"); + assert!( + deserialized.details.ips.contains(item), + "deserialized does not contain from source" + ); } - assert_eq!(cert.details.subnets.len(), deserialized.details.subnets.len()); + assert_eq!( + cert.details.subnets.len(), + deserialized.details.subnets.len() + ); for item in &cert.details.subnets { - assert!(deserialized.details.subnets.contains(item), "deserialized does not contain from source"); + assert!( + deserialized.details.subnets.contains(item), + "deserialized does not contain from source" + ); } assert_eq!(cert.details.groups.len(), deserialized.details.groups.len()); for item in &cert.details.groups { - assert!(deserialized.details.groups.contains(item), "deserialized does not contain from source"); + assert!( + deserialized.details.groups.contains(item), + "deserialized does not contain from source" + ); } } @@ -85,14 +107,18 @@ fn certificate_serialization_pem() { ips: vec![ netmask!("10.1.1.1", "255.255.255.0"), netmask!("10.1.1.2", "255.255.0.0"), - netmask!("10.1.1.3", "255.0.0.0") + netmask!("10.1.1.3", "255.0.0.0"), ], subnets: vec![ netmask!("9.1.1.1", "255.255.255.128"), netmask!("9.1.1.2", "255.255.255.0"), - netmask!("9.1.1.3", "255.255.0.0") + netmask!("9.1.1.3", "255.255.0.0"), + ], + groups: vec![ + "test-group1".to_string(), + "test-group2".to_string(), + "test-group3".to_string(), ], - groups: vec!["test-group1".to_string(), "test-group2".to_string(), "test-group3".to_string()], not_before: before, not_after: after, public_key: *pub_key, @@ -115,17 +141,29 @@ fn certificate_serialization_pem() { assert_eq!(cert.details.ips.len(), deserialized.details.ips.len()); for item in &cert.details.ips { - assert!(deserialized.details.ips.contains(item), "deserialized does not contain from source"); + assert!( + deserialized.details.ips.contains(item), + "deserialized does not contain from source" + ); } - assert_eq!(cert.details.subnets.len(), deserialized.details.subnets.len()); + assert_eq!( + cert.details.subnets.len(), + deserialized.details.subnets.len() + ); for item in &cert.details.subnets { - assert!(deserialized.details.subnets.contains(item), "deserialized does not contain from source"); + assert!( + deserialized.details.subnets.contains(item), + "deserialized does not contain from source" + ); } assert_eq!(cert.details.groups.len(), deserialized.details.groups.len()); for item in &cert.details.groups { - assert!(deserialized.details.groups.contains(item), "deserialized does not contain from source"); + assert!( + deserialized.details.groups.contains(item), + "deserialized does not contain from source" + ); } } @@ -141,14 +179,18 @@ fn cert_signing() { ips: vec![ netmask!("10.1.1.1", "255.255.255.0"), netmask!("10.1.1.2", "255.255.0.0"), - netmask!("10.1.1.3", "255.0.0.0") + netmask!("10.1.1.3", "255.0.0.0"), ], subnets: vec![ netmask!("9.1.1.1", "255.255.255.128"), netmask!("9.1.1.2", "255.255.255.0"), - netmask!("9.1.1.3", "255.255.0.0") + netmask!("9.1.1.3", "255.255.0.0"), + ], + groups: vec![ + "test-group1".to_string(), + "test-group2".to_string(), + "test-group3".to_string(), ], - groups: vec!["test-group1".to_string(), "test-group2".to_string(), "test-group3".to_string()], not_before: before, not_after: after, public_key: *pub_key, @@ -287,9 +329,15 @@ fn cert_deserialize_wrong_pubkey_len() { #[test] fn x25519_serialization() { let bytes = [0u8; 32]; - assert_eq!(deserialize_x25519_private(&serialize_x25519_private(&bytes)).unwrap(), bytes); + assert_eq!( + deserialize_x25519_private(&serialize_x25519_private(&bytes)).unwrap(), + bytes + ); assert!(deserialize_x25519_private(&[0u8; 32]).is_err()); - assert_eq!(deserialize_x25519_public(&serialize_x25519_public(&bytes)).unwrap(), bytes); + assert_eq!( + deserialize_x25519_public(&serialize_x25519_public(&bytes)).unwrap(), + bytes + ); assert!(deserialize_x25519_public(&[0u8; 32]).is_err()); } @@ -297,9 +345,15 @@ fn x25519_serialization() { fn ed25519_serialization() { let bytes = [0u8; 64]; let bytes2 = [0u8; 32]; - assert_eq!(deserialize_ed25519_private(&serialize_ed25519_private(&bytes)).unwrap(), bytes); + assert_eq!( + deserialize_ed25519_private(&serialize_ed25519_private(&bytes)).unwrap(), + bytes + ); assert!(deserialize_ed25519_private(&[0u8; 32]).is_err()); - assert_eq!(deserialize_ed25519_public(&serialize_ed25519_public(&bytes2)).unwrap(), bytes2); + assert_eq!( + deserialize_ed25519_public(&serialize_ed25519_public(&bytes2)).unwrap(), + bytes2 + ); assert!(deserialize_ed25519_public(&[0u8; 64]).is_err()); let mut bytes = vec![]; @@ -315,29 +369,87 @@ fn ed25519_serialization() { #[test] fn cert_verify() { - let (ca_cert, ca_key, _ca_pub) = test_ca_cert(round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(), vec![], vec![], vec!["groupa".to_string()]); + let (ca_cert, ca_key, _ca_pub) = test_ca_cert( + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(), + vec![], + vec![], + vec!["groupa".to_string()], + ); - let (cert, _, _) = test_cert(&ca_cert, &ca_key, SystemTime::now(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![], vec![], vec![]); + let (cert, _, _) = test_cert( + &ca_cert, + &ca_key, + SystemTime::now(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![], + vec![], + ); let mut ca_pool = NebulaCAPool::new(); - ca_pool.add_ca_certificate(&ca_cert.serialize_to_pem().unwrap()).unwrap(); + ca_pool + .add_ca_certificate(&ca_cert.serialize_to_pem().unwrap()) + .unwrap(); let fingerprint = cert.sha256sum().unwrap(); ca_pool.blocklist_fingerprint(&fingerprint); - assert!(matches!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Blocklisted)); + assert!(matches!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Blocklisted + )); ca_pool.reset_blocklist(); - assert!(matches!(cert.verify(SystemTime::now() + Duration::from_secs(60 * 60 * 60), &ca_pool).unwrap(), CertificateValidity::RootCertExpired)); - assert!(matches!(cert.verify(SystemTime::now() + Duration::from_secs(60 * 60 * 6), &ca_pool).unwrap(), CertificateValidity::CertExpired)); + assert!(matches!( + cert.verify( + SystemTime::now() + Duration::from_secs(60 * 60 * 60), + &ca_pool + ) + .unwrap(), + CertificateValidity::RootCertExpired + )); + assert!(matches!( + cert.verify( + SystemTime::now() + Duration::from_secs(60 * 60 * 6), + &ca_pool + ) + .unwrap(), + CertificateValidity::CertExpired + )); - let (cert_with_bad_group, _, _) = test_cert(&ca_cert, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), in_a_minute(), vec![], vec![], vec!["group-not-present on parent".to_string()]); - assert_eq!(cert_with_bad_group.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::GroupNotPresentOnSigner); - - let (cert_with_good_group, _, _) = test_cert(&ca_cert, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), in_a_minute(), vec![], vec![], vec!["groupa".to_string()]); - assert_eq!(cert_with_good_group.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + let (cert_with_bad_group, _, _) = test_cert( + &ca_cert, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + in_a_minute(), + vec![], + vec![], + vec!["group-not-present on parent".to_string()], + ); + assert_eq!( + cert_with_bad_group + .verify(SystemTime::now(), &ca_pool) + .unwrap(), + CertificateValidity::GroupNotPresentOnSigner + ); + let (cert_with_good_group, _, _) = test_cert( + &ca_cert, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + in_a_minute(), + vec![], + vec![], + vec!["groupa".to_string()], + ); + assert_eq!( + cert_with_good_group + .verify(SystemTime::now(), &ca_pool) + .unwrap(), + CertificateValidity::Ok + ); } #[test] @@ -345,7 +457,13 @@ fn cert_verify_ip() { let ca_ip_1 = Ipv4Net::from_str("10.0.0.0/16").unwrap(); let ca_ip_2 = Ipv4Net::from_str("192.168.0.0/24").unwrap(); - let (ca, ca_key, _ca_pub) = test_ca_cert(round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(), vec![ca_ip_1, ca_ip_2], vec![], vec![]); + let (ca, ca_key, _ca_pub) = test_ca_cert( + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(), + vec![ca_ip_1, ca_ip_2], + vec![], + vec![], + ); let ca_pem = ca.serialize_to_pem().unwrap(); @@ -355,49 +473,137 @@ fn cert_verify_ip() { // ip is outside the network let cip1 = netmask!("10.1.0.0", "255.255.255.0"); let cip2 = netmask!("192.198.0.1", "255.255.0.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![cip1, cip2], vec![], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![cip1, cip2], + vec![], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::IPNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::IPNotPresentOnSigner + ); // ip is outside the network - reversed order from above let cip1 = netmask!("192.198.0.1", "255.255.255.0"); let cip2 = netmask!("10.1.0.0", "255.255.255.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![cip1, cip2], vec![], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![cip1, cip2], + vec![], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::IPNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::IPNotPresentOnSigner + ); // ip is within the network but mask is outside let cip1 = netmask!("10.0.1.0", "255.254.0.0"); let cip2 = netmask!("192.168.0.1", "255.255.255.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![cip1, cip2], vec![], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![cip1, cip2], + vec![], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::IPNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::IPNotPresentOnSigner + ); // ip is within the network but mask is outside - reversed order from above let cip1 = netmask!("192.168.0.1", "255.255.255.0"); let cip2 = netmask!("10.0.1.0", "255.254.0.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![cip1, cip2], vec![], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![cip1, cip2], + vec![], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::IPNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::IPNotPresentOnSigner + ); // ip and mask are within the network let cip1 = netmask!("10.0.1.0", "255.255.0.0"); let cip2 = netmask!("192.168.0.1", "255.255.255.128"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![cip1, cip2], vec![], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![cip1, cip2], + vec![], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); // Exact matches - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![ca_ip_1, ca_ip_2], vec![], vec![]); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![ca_ip_1, ca_ip_2], + vec![], + vec![], + ); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); // Exact matches reversed - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![ca_ip_2, ca_ip_1], vec![], vec![]); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![ca_ip_2, ca_ip_1], + vec![], + vec![], + ); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); // Exact matches reversed with just one - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![ca_ip_2], vec![], vec![]); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![ca_ip_2], + vec![], + vec![], + ); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); } #[test] @@ -405,7 +611,13 @@ fn cert_verify_subnet() { let ca_ip_1 = Ipv4Net::from_str("10.0.0.0/16").unwrap(); let ca_ip_2 = Ipv4Net::from_str("192.168.0.0/24").unwrap(); - let (ca, ca_key, _ca_pub) = test_ca_cert(round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(), vec![],vec![ca_ip_1, ca_ip_2], vec![]); + let (ca, ca_key, _ca_pub) = test_ca_cert( + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(), + vec![], + vec![ca_ip_1, ca_ip_2], + vec![], + ); let ca_pem = ca.serialize_to_pem().unwrap(); @@ -415,63 +627,170 @@ fn cert_verify_subnet() { // ip is outside the network let cip1 = netmask!("10.1.0.0", "255.255.255.0"); let cip2 = netmask!("192.198.0.1", "255.255.0.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![],vec![cip1, cip2], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![cip1, cip2], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::SubnetNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::SubnetNotPresentOnSigner + ); // ip is outside the network - reversed order from above let cip1 = netmask!("192.198.0.1", "255.255.255.0"); let cip2 = netmask!("10.1.0.0", "255.255.255.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![],vec![cip1, cip2], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![cip1, cip2], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::SubnetNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::SubnetNotPresentOnSigner + ); // ip is within the network but mask is outside let cip1 = netmask!("10.0.1.0", "255.254.0.0"); let cip2 = netmask!("192.168.0.1", "255.255.255.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![],vec![cip1, cip2], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![cip1, cip2], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::SubnetNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::SubnetNotPresentOnSigner + ); // ip is within the network but mask is outside - reversed order from above let cip1 = netmask!("192.168.0.1", "255.255.255.0"); let cip2 = netmask!("10.0.1.0", "255.254.0.0"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![cip1, cip2], vec![], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![cip1, cip2], + vec![], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::SubnetNotPresentOnSigner); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::SubnetNotPresentOnSigner + ); // ip and mask are within the network let cip1 = netmask!("10.0.1.0", "255.255.0.0"); let cip2 = netmask!("192.168.0.1", "255.255.255.128"); - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![], vec![cip1, cip2], vec![]); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![cip1, cip2], + vec![], + ); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); // Exact matches - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![],vec![ca_ip_1, ca_ip_2], vec![]); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![ca_ip_1, ca_ip_2], + vec![], + ); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); // Exact matches reversed - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![], vec![ca_ip_2, ca_ip_1], vec![]); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![ca_ip_2, ca_ip_1], + vec![], + ); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); // Exact matches reversed with just one - let (cert, _, _) = test_cert(&ca, &ca_key, round_systime_to_secs(SystemTime::now()).unwrap(), round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), vec![], vec![ca_ip_2], vec![]); - assert_eq!(cert.verify(SystemTime::now(), &ca_pool).unwrap(), CertificateValidity::Ok); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + round_systime_to_secs(SystemTime::now()).unwrap(), + round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(), + vec![], + vec![ca_ip_2], + vec![], + ); + assert_eq!( + cert.verify(SystemTime::now(), &ca_pool).unwrap(), + CertificateValidity::Ok + ); } #[test] fn cert_private_key() { - let (ca, ca_key, _) = test_ca_cert(SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]); + let (ca, ca_key, _) = + test_ca_cert(SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]); ca.verify_private_key(&ca_key.to_keypair_bytes()).unwrap(); - let (_, ca_key2, _) = test_ca_cert(SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]); - ca.verify_private_key(&ca_key2.to_keypair_bytes()).unwrap_err(); + let (_, ca_key2, _) = + test_ca_cert(SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]); + ca.verify_private_key(&ca_key2.to_keypair_bytes()) + .unwrap_err(); - let (cert, priv_key, _) = test_cert(&ca, &ca_key, SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]); + let (cert, priv_key, _) = test_cert( + &ca, + &ca_key, + SystemTime::now(), + SystemTime::now(), + vec![], + vec![], + vec![], + ); cert.verify_private_key(&priv_key.to_bytes()).unwrap(); - let (cert2, _, _) = test_cert(&ca, &ca_key, SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]); + let (cert2, _, _) = test_cert( + &ca, + &ca_key, + SystemTime::now(), + SystemTime::now(), + vec![], + vec![], + vec![], + ); cert2.verify_private_key(&priv_key.to_bytes()).unwrap_err(); } @@ -489,7 +808,8 @@ CkMKEW5lYnVsYSByb290IGNhIDAxKJL2u9EFMJL86+cGOiDPXMH4oU6HZTk/CqTG BVG+oJpAoqokUBbI4U0N8CSfpUABEkB/Pm5A2xyH/nc8mg/wvGUWG3pZ7nHzaDMf 8/phAUt+FLzqTECzQKisYswKvE3pl9mbEYKbOdIHrxdIp95mo4sF -----END NEBULA CERTIFICATE-----"; - let with_newlines = b"# Current provisional, Remove once everything moves over to the real root. + let with_newlines = + b"# Current provisional, Remove once everything moves over to the real root. -----BEGIN NEBULA CERTIFICATE----- CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv @@ -511,24 +831,64 @@ WH1M9n4O7cFtGlM6sJJOS+rCVVEJ3ABS7+MPdQs= -----END NEBULA CERTIFICATE-----"; let pool_a = NebulaCAPool::new_from_pem(no_newlines).unwrap(); - assert_eq!(pool_a.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"].details.name, "nebula root ca".to_string()); - assert_eq!(pool_a.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"].details.name, "nebula root ca 01".to_string()); + assert_eq!( + pool_a.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"] + .details + .name, + "nebula root ca".to_string() + ); + assert_eq!( + pool_a.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"] + .details + .name, + "nebula root ca 01".to_string() + ); assert!(!pool_a.expired); let pool_b = NebulaCAPool::new_from_pem(with_newlines).unwrap(); - assert_eq!(pool_b.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"].details.name, "nebula root ca".to_string()); - assert_eq!(pool_b.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"].details.name, "nebula root ca 01".to_string()); + assert_eq!( + pool_b.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"] + .details + .name, + "nebula root ca".to_string() + ); + assert_eq!( + pool_b.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"] + .details + .name, + "nebula root ca 01".to_string() + ); assert!(!pool_b.expired); let pool_c = NebulaCAPool::new_from_pem(expired).unwrap(); assert!(pool_c.expired); - assert_eq!(pool_c.cas["152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0"].details.name, "expired"); + assert_eq!( + pool_c.cas["152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0"] + .details + .name, + "expired" + ); let mut pool_d = NebulaCAPool::new_from_pem(with_newlines).unwrap(); pool_d.add_ca_certificate(expired).unwrap(); - assert_eq!(pool_d.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"].details.name, "nebula root ca".to_string()); - assert_eq!(pool_d.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"].details.name, "nebula root ca 01".to_string()); - assert_eq!(pool_d.cas["152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0"].details.name, "expired"); + assert_eq!( + pool_d.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"] + .details + .name, + "nebula root ca".to_string() + ); + assert_eq!( + pool_d.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"] + .details + .name, + "nebula root ca 01".to_string() + ); + assert_eq!( + pool_d.cas["152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0"] + .details + .name, + "expired" + ); assert!(pool_d.expired); assert_eq!(pool_d.get_fingerprints().len(), 3); } @@ -536,7 +896,11 @@ WH1M9n4O7cFtGlM6sJJOS+rCVVEJ3ABS7+MPdQs= #[macro_export] macro_rules! netmask { ($ip:expr,$mask:expr) => { - Ipv4Net::with_netmask(Ipv4Addr::from_str($ip).unwrap(), Ipv4Addr::from_str($mask).unwrap()).unwrap() + Ipv4Net::with_netmask( + Ipv4Addr::from_str($ip).unwrap(), + Ipv4Addr::from_str($mask).unwrap(), + ) + .unwrap() }; } @@ -545,7 +909,13 @@ fn round_systime_to_secs(time: SystemTime) -> Result, subnets: Vec, groups: Vec) -> (NebulaCertificate, SigningKey, VerifyingKey) { +fn test_ca_cert( + before: SystemTime, + after: SystemTime, + ips: Vec, + subnets: Vec, + groups: Vec, +) -> (NebulaCertificate, SigningKey, VerifyingKey) { let mut csprng = OsRng; let key = SigningKey::generate(&mut csprng); let pub_key = key.verifying_key(); @@ -710,17 +1080,45 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= fn ca_pool_add_non_ca() { let mut ca_pool = NebulaCAPool::new(); - let (ca, ca_key, _) = test_ca_cert(SystemTime::now(), SystemTime::now() + Duration::from_secs(3600), vec![], vec![], vec![]); - let (cert, _, _) = test_cert(&ca, &ca_key, SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]); + let (ca, ca_key, _) = test_ca_cert( + SystemTime::now(), + SystemTime::now() + Duration::from_secs(3600), + vec![], + vec![], + vec![], + ); + let (cert, _, _) = test_cert( + &ca, + &ca_key, + SystemTime::now(), + SystemTime::now(), + vec![], + vec![], + vec![], + ); - ca_pool.add_ca_certificate(&cert.serialize_to_pem().unwrap()).unwrap_err(); + ca_pool + .add_ca_certificate(&cert.serialize_to_pem().unwrap()) + .unwrap_err(); } -fn test_cert(ca: &NebulaCertificate, key: &SigningKey, before: SystemTime, after: SystemTime, ips: Vec, subnets: Vec, groups: Vec) -> (NebulaCertificate, SigningKey, VerifyingKey) { +fn test_cert( + ca: &NebulaCertificate, + key: &SigningKey, + before: SystemTime, + after: SystemTime, + ips: Vec, + subnets: Vec, + groups: Vec, +) -> (NebulaCertificate, SigningKey, VerifyingKey) { let issuer = ca.sha256sum().unwrap(); let real_groups = if groups.is_empty() { - vec!["test-group1".to_string(), "test-group2".to_string(), "test-group3".to_string()] + vec![ + "test-group1".to_string(), + "test-group2".to_string(), + "test-group3".to_string(), + ] } else { groups }; @@ -729,7 +1127,7 @@ fn test_cert(ca: &NebulaCertificate, key: &SigningKey, before: SystemTime, after vec![ netmask!("10.1.1.1", "255.255.255.0"), netmask!("10.1.1.2", "255.255.0.0"), - netmask!("10.1.1.3", "255.0.0.0") + netmask!("10.1.1.3", "255.0.0.0"), ] } else { ips @@ -739,7 +1137,7 @@ fn test_cert(ca: &NebulaCertificate, key: &SigningKey, before: SystemTime, after vec![ netmask!("9.1.1.1", "255.255.255.128"), netmask!("9.1.1.2", "255.255.255.0"), - netmask!("9.1.1.3", "255.255.0.0") + netmask!("9.1.1.3", "255.255.0.0"), ] } else { subnets @@ -774,4 +1172,4 @@ fn in_a_minute() -> SystemTime { #[allow(dead_code)] fn a_minute_ago() -> SystemTime { round_systime_to_secs(SystemTime::now().sub(Duration::from_secs(60))).unwrap() -} \ No newline at end of file +}