├── .gitattributes ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── flake.lock ├── flake.nix ├── src ├── cli │ ├── gen │ │ ├── cert.rs │ │ ├── completion.rs │ │ ├── man.rs │ │ └── mod.rs │ ├── mod.rs │ ├── run.rs │ ├── serve.rs │ └── status.rs ├── dashboard │ ├── handlers.rs │ └── mod.rs ├── lib.rs ├── main.rs ├── proxy.rs ├── proxy │ ├── tls_terminating.rs │ └── transparent.rs ├── registry.rs └── service.rs └── templates ├── hello.html └── layout.html /.gitattributes: -------------------------------------------------------------------------------- 1 | Cargo.lock merge=binary 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | dolores.crt 3 | dolores.key 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "askama" 22 | version = "0.11.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139" 25 | dependencies = [ 26 | "askama_derive", 27 | "askama_escape", 28 | "askama_shared", 29 | ] 30 | 31 | [[package]] 32 | name = "askama_derive" 33 | version = "0.11.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71" 36 | dependencies = [ 37 | "askama_shared", 38 | "proc-macro2", 39 | "syn", 40 | ] 41 | 42 | [[package]] 43 | name = "askama_escape" 44 | version = "0.10.3" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" 47 | 48 | [[package]] 49 | name = "askama_shared" 50 | version = "0.12.2" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0" 53 | dependencies = [ 54 | "askama_escape", 55 | "humansize", 56 | "mime", 57 | "mime_guess", 58 | "nom", 59 | "num-traits", 60 | "percent-encoding", 61 | "proc-macro2", 62 | "quote", 63 | "serde", 64 | "syn", 65 | "toml", 66 | ] 67 | 68 | [[package]] 69 | name = "asn1-rs" 70 | version = "0.5.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" 73 | dependencies = [ 74 | "asn1-rs-derive", 75 | "asn1-rs-impl", 76 | "displaydoc", 77 | "nom", 78 | "num-traits", 79 | "rusticata-macros", 80 | "thiserror", 81 | "time", 82 | ] 83 | 84 | [[package]] 85 | name = "asn1-rs-derive" 86 | version = "0.4.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "syn", 93 | "synstructure", 94 | ] 95 | 96 | [[package]] 97 | name = "asn1-rs-impl" 98 | version = "0.1.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" 101 | dependencies = [ 102 | "proc-macro2", 103 | "quote", 104 | "syn", 105 | ] 106 | 107 | [[package]] 108 | name = "async-stream" 109 | version = "0.3.3" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" 112 | dependencies = [ 113 | "async-stream-impl", 114 | "futures-core", 115 | ] 116 | 117 | [[package]] 118 | name = "async-stream-impl" 119 | version = "0.3.3" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" 122 | dependencies = [ 123 | "proc-macro2", 124 | "quote", 125 | "syn", 126 | ] 127 | 128 | [[package]] 129 | name = "async-trait" 130 | version = "0.1.57" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" 133 | dependencies = [ 134 | "proc-macro2", 135 | "quote", 136 | "syn", 137 | ] 138 | 139 | [[package]] 140 | name = "atty" 141 | version = "0.2.14" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 144 | dependencies = [ 145 | "hermit-abi", 146 | "libc", 147 | "winapi", 148 | ] 149 | 150 | [[package]] 151 | name = "autocfg" 152 | version = "1.1.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 155 | 156 | [[package]] 157 | name = "backtrace" 158 | version = "0.3.66" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" 161 | dependencies = [ 162 | "addr2line", 163 | "cc", 164 | "cfg-if", 165 | "libc", 166 | "miniz_oxide", 167 | "object", 168 | "rustc-demangle", 169 | ] 170 | 171 | [[package]] 172 | name = "base64" 173 | version = "0.13.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 176 | 177 | [[package]] 178 | name = "bincode" 179 | version = "1.3.3" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 182 | dependencies = [ 183 | "serde", 184 | ] 185 | 186 | [[package]] 187 | name = "bitflags" 188 | version = "1.3.2" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 191 | 192 | [[package]] 193 | name = "bumpalo" 194 | version = "3.11.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" 197 | 198 | [[package]] 199 | name = "bytes" 200 | version = "1.2.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 203 | 204 | [[package]] 205 | name = "cc" 206 | version = "1.0.73" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 209 | 210 | [[package]] 211 | name = "cfg-if" 212 | version = "1.0.0" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 215 | 216 | [[package]] 217 | name = "clap" 218 | version = "4.0.11" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "4ed45cc2c62a3eff523e718d8576ba762c83a3146151093283ac62ae11933a73" 221 | dependencies = [ 222 | "atty", 223 | "bitflags", 224 | "clap_derive", 225 | "clap_lex", 226 | "once_cell", 227 | "strsim", 228 | "termcolor", 229 | ] 230 | 231 | [[package]] 232 | name = "clap_complete" 233 | version = "4.0.2" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "11cba7abac9b56dfe2f035098cdb3a43946f276e6db83b72c4e692343f9aab9a" 236 | dependencies = [ 237 | "clap", 238 | ] 239 | 240 | [[package]] 241 | name = "clap_derive" 242 | version = "4.0.10" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce" 245 | dependencies = [ 246 | "heck", 247 | "proc-macro-error", 248 | "proc-macro2", 249 | "quote", 250 | "syn", 251 | ] 252 | 253 | [[package]] 254 | name = "clap_lex" 255 | version = "0.3.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 258 | dependencies = [ 259 | "os_str_bytes", 260 | ] 261 | 262 | [[package]] 263 | name = "clap_mangen" 264 | version = "0.2.2" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "d5d5cd261a1d5601621a7ee4870f6e7f3f1ba3fc901d867f5201b36691e7efbe" 267 | dependencies = [ 268 | "clap", 269 | "roff", 270 | ] 271 | 272 | [[package]] 273 | name = "color-eyre" 274 | version = "0.6.2" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" 277 | dependencies = [ 278 | "backtrace", 279 | "color-spantrace", 280 | "eyre", 281 | "indenter", 282 | "once_cell", 283 | "owo-colors", 284 | "tracing-error", 285 | ] 286 | 287 | [[package]] 288 | name = "color-spantrace" 289 | version = "0.2.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" 292 | dependencies = [ 293 | "once_cell", 294 | "owo-colors", 295 | "tracing-core", 296 | "tracing-error", 297 | ] 298 | 299 | [[package]] 300 | name = "data-encoding" 301 | version = "2.3.2" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" 304 | 305 | [[package]] 306 | name = "der-parser" 307 | version = "8.1.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" 310 | dependencies = [ 311 | "asn1-rs", 312 | "displaydoc", 313 | "nom", 314 | "num-bigint", 315 | "num-traits", 316 | "rusticata-macros", 317 | ] 318 | 319 | [[package]] 320 | name = "displaydoc" 321 | version = "0.2.3" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" 324 | dependencies = [ 325 | "proc-macro2", 326 | "quote", 327 | "syn", 328 | ] 329 | 330 | [[package]] 331 | name = "dolores" 332 | version = "0.1.0" 333 | dependencies = [ 334 | "askama", 335 | "async-stream", 336 | "async-trait", 337 | "bincode", 338 | "clap", 339 | "clap_complete", 340 | "clap_mangen", 341 | "color-eyre", 342 | "http", 343 | "hyper", 344 | "indoc", 345 | "matchit", 346 | "nix", 347 | "once_cell", 348 | "rand", 349 | "rcgen", 350 | "rustls", 351 | "serde", 352 | "tokio", 353 | "tokio-rustls", 354 | "tracing", 355 | "tracing-subscriber", 356 | ] 357 | 358 | [[package]] 359 | name = "eyre" 360 | version = "0.6.8" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" 363 | dependencies = [ 364 | "indenter", 365 | "once_cell", 366 | ] 367 | 368 | [[package]] 369 | name = "fnv" 370 | version = "1.0.7" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 373 | 374 | [[package]] 375 | name = "futures-channel" 376 | version = "0.3.24" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" 379 | dependencies = [ 380 | "futures-core", 381 | ] 382 | 383 | [[package]] 384 | name = "futures-core" 385 | version = "0.3.24" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" 388 | 389 | [[package]] 390 | name = "futures-sink" 391 | version = "0.3.24" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" 394 | 395 | [[package]] 396 | name = "futures-task" 397 | version = "0.3.24" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" 400 | 401 | [[package]] 402 | name = "futures-util" 403 | version = "0.3.24" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" 406 | dependencies = [ 407 | "futures-core", 408 | "futures-task", 409 | "pin-project-lite", 410 | "pin-utils", 411 | ] 412 | 413 | [[package]] 414 | name = "getrandom" 415 | version = "0.2.7" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 418 | dependencies = [ 419 | "cfg-if", 420 | "libc", 421 | "wasi", 422 | ] 423 | 424 | [[package]] 425 | name = "gimli" 426 | version = "0.26.2" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" 429 | 430 | [[package]] 431 | name = "h2" 432 | version = "0.3.14" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" 435 | dependencies = [ 436 | "bytes", 437 | "fnv", 438 | "futures-core", 439 | "futures-sink", 440 | "futures-util", 441 | "http", 442 | "indexmap", 443 | "slab", 444 | "tokio", 445 | "tokio-util", 446 | "tracing", 447 | ] 448 | 449 | [[package]] 450 | name = "hashbrown" 451 | version = "0.12.3" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 454 | 455 | [[package]] 456 | name = "heck" 457 | version = "0.4.0" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 460 | 461 | [[package]] 462 | name = "hermit-abi" 463 | version = "0.1.19" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 466 | dependencies = [ 467 | "libc", 468 | ] 469 | 470 | [[package]] 471 | name = "http" 472 | version = "0.2.8" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 475 | dependencies = [ 476 | "bytes", 477 | "fnv", 478 | "itoa", 479 | ] 480 | 481 | [[package]] 482 | name = "http-body" 483 | version = "0.4.5" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 486 | dependencies = [ 487 | "bytes", 488 | "http", 489 | "pin-project-lite", 490 | ] 491 | 492 | [[package]] 493 | name = "httparse" 494 | version = "1.8.0" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 497 | 498 | [[package]] 499 | name = "httpdate" 500 | version = "1.0.2" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 503 | 504 | [[package]] 505 | name = "humansize" 506 | version = "1.1.1" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" 509 | 510 | [[package]] 511 | name = "hyper" 512 | version = "0.14.20" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" 515 | dependencies = [ 516 | "bytes", 517 | "futures-channel", 518 | "futures-core", 519 | "futures-util", 520 | "h2", 521 | "http", 522 | "http-body", 523 | "httparse", 524 | "httpdate", 525 | "itoa", 526 | "pin-project-lite", 527 | "socket2", 528 | "tokio", 529 | "tower-service", 530 | "tracing", 531 | "want", 532 | ] 533 | 534 | [[package]] 535 | name = "indenter" 536 | version = "0.3.3" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 539 | 540 | [[package]] 541 | name = "indexmap" 542 | version = "1.9.1" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 545 | dependencies = [ 546 | "autocfg", 547 | "hashbrown", 548 | ] 549 | 550 | [[package]] 551 | name = "indoc" 552 | version = "1.0.7" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" 555 | 556 | [[package]] 557 | name = "itoa" 558 | version = "1.0.4" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 561 | 562 | [[package]] 563 | name = "js-sys" 564 | version = "0.3.60" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" 567 | dependencies = [ 568 | "wasm-bindgen", 569 | ] 570 | 571 | [[package]] 572 | name = "lazy_static" 573 | version = "1.4.0" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 576 | 577 | [[package]] 578 | name = "libc" 579 | version = "0.2.134" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" 582 | 583 | [[package]] 584 | name = "lock_api" 585 | version = "0.4.9" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 588 | dependencies = [ 589 | "autocfg", 590 | "scopeguard", 591 | ] 592 | 593 | [[package]] 594 | name = "log" 595 | version = "0.4.17" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 598 | dependencies = [ 599 | "cfg-if", 600 | ] 601 | 602 | [[package]] 603 | name = "matchit" 604 | version = "0.6.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "3dfc802da7b1cf80aefffa0c7b2f77247c8b32206cc83c270b61264f5b360a80" 607 | 608 | [[package]] 609 | name = "memchr" 610 | version = "2.5.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 613 | 614 | [[package]] 615 | name = "memoffset" 616 | version = "0.6.5" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 619 | dependencies = [ 620 | "autocfg", 621 | ] 622 | 623 | [[package]] 624 | name = "mime" 625 | version = "0.3.16" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 628 | 629 | [[package]] 630 | name = "mime_guess" 631 | version = "2.0.4" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" 634 | dependencies = [ 635 | "mime", 636 | "unicase", 637 | ] 638 | 639 | [[package]] 640 | name = "minimal-lexical" 641 | version = "0.2.1" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 644 | 645 | [[package]] 646 | name = "miniz_oxide" 647 | version = "0.5.4" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" 650 | dependencies = [ 651 | "adler", 652 | ] 653 | 654 | [[package]] 655 | name = "mio" 656 | version = "0.8.4" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 659 | dependencies = [ 660 | "libc", 661 | "log", 662 | "wasi", 663 | "windows-sys", 664 | ] 665 | 666 | [[package]] 667 | name = "nix" 668 | version = "0.25.0" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" 671 | dependencies = [ 672 | "autocfg", 673 | "bitflags", 674 | "cfg-if", 675 | "libc", 676 | "memoffset", 677 | "pin-utils", 678 | ] 679 | 680 | [[package]] 681 | name = "nom" 682 | version = "7.1.1" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 685 | dependencies = [ 686 | "memchr", 687 | "minimal-lexical", 688 | ] 689 | 690 | [[package]] 691 | name = "nu-ansi-term" 692 | version = "0.46.0" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 695 | dependencies = [ 696 | "overload", 697 | "winapi", 698 | ] 699 | 700 | [[package]] 701 | name = "num-bigint" 702 | version = "0.4.3" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 705 | dependencies = [ 706 | "autocfg", 707 | "num-integer", 708 | "num-traits", 709 | ] 710 | 711 | [[package]] 712 | name = "num-integer" 713 | version = "0.1.45" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 716 | dependencies = [ 717 | "autocfg", 718 | "num-traits", 719 | ] 720 | 721 | [[package]] 722 | name = "num-traits" 723 | version = "0.2.15" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 726 | dependencies = [ 727 | "autocfg", 728 | ] 729 | 730 | [[package]] 731 | name = "num_cpus" 732 | version = "1.13.1" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 735 | dependencies = [ 736 | "hermit-abi", 737 | "libc", 738 | ] 739 | 740 | [[package]] 741 | name = "num_threads" 742 | version = "0.1.6" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" 745 | dependencies = [ 746 | "libc", 747 | ] 748 | 749 | [[package]] 750 | name = "object" 751 | version = "0.29.0" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" 754 | dependencies = [ 755 | "memchr", 756 | ] 757 | 758 | [[package]] 759 | name = "oid-registry" 760 | version = "0.6.0" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "7d4bda43fd1b844cbc6e6e54b5444e2b1bc7838bce59ad205902cccbb26d6761" 763 | dependencies = [ 764 | "asn1-rs", 765 | ] 766 | 767 | [[package]] 768 | name = "once_cell" 769 | version = "1.15.0" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 772 | 773 | [[package]] 774 | name = "os_str_bytes" 775 | version = "6.3.0" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" 778 | 779 | [[package]] 780 | name = "overload" 781 | version = "0.1.1" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 784 | 785 | [[package]] 786 | name = "owo-colors" 787 | version = "3.5.0" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" 790 | 791 | [[package]] 792 | name = "parking_lot" 793 | version = "0.12.1" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 796 | dependencies = [ 797 | "lock_api", 798 | "parking_lot_core", 799 | ] 800 | 801 | [[package]] 802 | name = "parking_lot_core" 803 | version = "0.9.3" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 806 | dependencies = [ 807 | "cfg-if", 808 | "libc", 809 | "redox_syscall", 810 | "smallvec", 811 | "windows-sys", 812 | ] 813 | 814 | [[package]] 815 | name = "pem" 816 | version = "1.1.0" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" 819 | dependencies = [ 820 | "base64", 821 | ] 822 | 823 | [[package]] 824 | name = "percent-encoding" 825 | version = "2.2.0" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 828 | 829 | [[package]] 830 | name = "pin-project-lite" 831 | version = "0.2.9" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 834 | 835 | [[package]] 836 | name = "pin-utils" 837 | version = "0.1.0" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 840 | 841 | [[package]] 842 | name = "ppv-lite86" 843 | version = "0.2.16" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 846 | 847 | [[package]] 848 | name = "proc-macro-error" 849 | version = "1.0.4" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 852 | dependencies = [ 853 | "proc-macro-error-attr", 854 | "proc-macro2", 855 | "quote", 856 | "syn", 857 | "version_check", 858 | ] 859 | 860 | [[package]] 861 | name = "proc-macro-error-attr" 862 | version = "1.0.4" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 865 | dependencies = [ 866 | "proc-macro2", 867 | "quote", 868 | "version_check", 869 | ] 870 | 871 | [[package]] 872 | name = "proc-macro2" 873 | version = "1.0.46" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" 876 | dependencies = [ 877 | "unicode-ident", 878 | ] 879 | 880 | [[package]] 881 | name = "quote" 882 | version = "1.0.21" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 885 | dependencies = [ 886 | "proc-macro2", 887 | ] 888 | 889 | [[package]] 890 | name = "rand" 891 | version = "0.8.5" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 894 | dependencies = [ 895 | "libc", 896 | "rand_chacha", 897 | "rand_core", 898 | ] 899 | 900 | [[package]] 901 | name = "rand_chacha" 902 | version = "0.3.1" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 905 | dependencies = [ 906 | "ppv-lite86", 907 | "rand_core", 908 | ] 909 | 910 | [[package]] 911 | name = "rand_core" 912 | version = "0.6.4" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 915 | dependencies = [ 916 | "getrandom", 917 | ] 918 | 919 | [[package]] 920 | name = "rcgen" 921 | version = "0.10.0" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" 924 | dependencies = [ 925 | "pem", 926 | "ring", 927 | "time", 928 | "x509-parser", 929 | "yasna", 930 | ] 931 | 932 | [[package]] 933 | name = "redox_syscall" 934 | version = "0.2.16" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 937 | dependencies = [ 938 | "bitflags", 939 | ] 940 | 941 | [[package]] 942 | name = "ring" 943 | version = "0.16.20" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 946 | dependencies = [ 947 | "cc", 948 | "libc", 949 | "once_cell", 950 | "spin", 951 | "untrusted", 952 | "web-sys", 953 | "winapi", 954 | ] 955 | 956 | [[package]] 957 | name = "roff" 958 | version = "0.2.1" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" 961 | 962 | [[package]] 963 | name = "rustc-demangle" 964 | version = "0.1.21" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 967 | 968 | [[package]] 969 | name = "rusticata-macros" 970 | version = "4.1.0" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" 973 | dependencies = [ 974 | "nom", 975 | ] 976 | 977 | [[package]] 978 | name = "rustls" 979 | version = "0.20.6" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" 982 | dependencies = [ 983 | "log", 984 | "ring", 985 | "sct", 986 | "webpki", 987 | ] 988 | 989 | [[package]] 990 | name = "scopeguard" 991 | version = "1.1.0" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 994 | 995 | [[package]] 996 | name = "sct" 997 | version = "0.7.0" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 1000 | dependencies = [ 1001 | "ring", 1002 | "untrusted", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "serde" 1007 | version = "1.0.145" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 1010 | dependencies = [ 1011 | "serde_derive", 1012 | ] 1013 | 1014 | [[package]] 1015 | name = "serde_derive" 1016 | version = "1.0.145" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 1019 | dependencies = [ 1020 | "proc-macro2", 1021 | "quote", 1022 | "syn", 1023 | ] 1024 | 1025 | [[package]] 1026 | name = "sharded-slab" 1027 | version = "0.1.4" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 1030 | dependencies = [ 1031 | "lazy_static", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "signal-hook-registry" 1036 | version = "1.4.0" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1039 | dependencies = [ 1040 | "libc", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "slab" 1045 | version = "0.4.7" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 1048 | dependencies = [ 1049 | "autocfg", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "smallvec" 1054 | version = "1.10.0" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1057 | 1058 | [[package]] 1059 | name = "socket2" 1060 | version = "0.4.7" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 1063 | dependencies = [ 1064 | "libc", 1065 | "winapi", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "spin" 1070 | version = "0.5.2" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1073 | 1074 | [[package]] 1075 | name = "strsim" 1076 | version = "0.10.0" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1079 | 1080 | [[package]] 1081 | name = "syn" 1082 | version = "1.0.102" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" 1085 | dependencies = [ 1086 | "proc-macro2", 1087 | "quote", 1088 | "unicode-ident", 1089 | ] 1090 | 1091 | [[package]] 1092 | name = "synstructure" 1093 | version = "0.12.6" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 1096 | dependencies = [ 1097 | "proc-macro2", 1098 | "quote", 1099 | "syn", 1100 | "unicode-xid", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "termcolor" 1105 | version = "1.1.3" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1108 | dependencies = [ 1109 | "winapi-util", 1110 | ] 1111 | 1112 | [[package]] 1113 | name = "thiserror" 1114 | version = "1.0.37" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 1117 | dependencies = [ 1118 | "thiserror-impl", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "thiserror-impl" 1123 | version = "1.0.37" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 1126 | dependencies = [ 1127 | "proc-macro2", 1128 | "quote", 1129 | "syn", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "thread_local" 1134 | version = "1.1.4" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" 1137 | dependencies = [ 1138 | "once_cell", 1139 | ] 1140 | 1141 | [[package]] 1142 | name = "time" 1143 | version = "0.3.15" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" 1146 | dependencies = [ 1147 | "itoa", 1148 | "libc", 1149 | "num_threads", 1150 | "time-macros", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "time-macros" 1155 | version = "0.2.4" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" 1158 | 1159 | [[package]] 1160 | name = "tokio" 1161 | version = "1.21.2" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" 1164 | dependencies = [ 1165 | "autocfg", 1166 | "bytes", 1167 | "libc", 1168 | "memchr", 1169 | "mio", 1170 | "num_cpus", 1171 | "parking_lot", 1172 | "pin-project-lite", 1173 | "signal-hook-registry", 1174 | "socket2", 1175 | "tokio-macros", 1176 | "winapi", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "tokio-macros" 1181 | version = "1.8.0" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 1184 | dependencies = [ 1185 | "proc-macro2", 1186 | "quote", 1187 | "syn", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "tokio-rustls" 1192 | version = "0.23.4" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 1195 | dependencies = [ 1196 | "rustls", 1197 | "tokio", 1198 | "webpki", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "tokio-util" 1203 | version = "0.7.4" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 1206 | dependencies = [ 1207 | "bytes", 1208 | "futures-core", 1209 | "futures-sink", 1210 | "pin-project-lite", 1211 | "tokio", 1212 | "tracing", 1213 | ] 1214 | 1215 | [[package]] 1216 | name = "toml" 1217 | version = "0.5.9" 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" 1219 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1220 | dependencies = [ 1221 | "serde", 1222 | ] 1223 | 1224 | [[package]] 1225 | name = "tower-service" 1226 | version = "0.3.2" 1227 | source = "registry+https://github.com/rust-lang/crates.io-index" 1228 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1229 | 1230 | [[package]] 1231 | name = "tracing" 1232 | version = "0.1.37" 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" 1234 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 1235 | dependencies = [ 1236 | "cfg-if", 1237 | "pin-project-lite", 1238 | "tracing-attributes", 1239 | "tracing-core", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "tracing-attributes" 1244 | version = "0.1.23" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 1247 | dependencies = [ 1248 | "proc-macro2", 1249 | "quote", 1250 | "syn", 1251 | ] 1252 | 1253 | [[package]] 1254 | name = "tracing-core" 1255 | version = "0.1.30" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 1258 | dependencies = [ 1259 | "once_cell", 1260 | "valuable", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "tracing-error" 1265 | version = "0.2.0" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" 1268 | dependencies = [ 1269 | "tracing", 1270 | "tracing-subscriber", 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "tracing-log" 1275 | version = "0.1.3" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" 1278 | dependencies = [ 1279 | "lazy_static", 1280 | "log", 1281 | "tracing-core", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "tracing-subscriber" 1286 | version = "0.3.16" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" 1289 | dependencies = [ 1290 | "nu-ansi-term", 1291 | "sharded-slab", 1292 | "smallvec", 1293 | "thread_local", 1294 | "tracing-core", 1295 | "tracing-log", 1296 | ] 1297 | 1298 | [[package]] 1299 | name = "try-lock" 1300 | version = "0.2.3" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1303 | 1304 | [[package]] 1305 | name = "unicase" 1306 | version = "2.6.0" 1307 | source = "registry+https://github.com/rust-lang/crates.io-index" 1308 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1309 | dependencies = [ 1310 | "version_check", 1311 | ] 1312 | 1313 | [[package]] 1314 | name = "unicode-ident" 1315 | version = "1.0.5" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 1318 | 1319 | [[package]] 1320 | name = "unicode-xid" 1321 | version = "0.2.4" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 1324 | 1325 | [[package]] 1326 | name = "untrusted" 1327 | version = "0.7.1" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1330 | 1331 | [[package]] 1332 | name = "valuable" 1333 | version = "0.1.0" 1334 | source = "registry+https://github.com/rust-lang/crates.io-index" 1335 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1336 | 1337 | [[package]] 1338 | name = "version_check" 1339 | version = "0.9.4" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1342 | 1343 | [[package]] 1344 | name = "want" 1345 | version = "0.3.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1348 | dependencies = [ 1349 | "log", 1350 | "try-lock", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "wasi" 1355 | version = "0.11.0+wasi-snapshot-preview1" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1358 | 1359 | [[package]] 1360 | name = "wasm-bindgen" 1361 | version = "0.2.83" 1362 | source = "registry+https://github.com/rust-lang/crates.io-index" 1363 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" 1364 | dependencies = [ 1365 | "cfg-if", 1366 | "wasm-bindgen-macro", 1367 | ] 1368 | 1369 | [[package]] 1370 | name = "wasm-bindgen-backend" 1371 | version = "0.2.83" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" 1374 | dependencies = [ 1375 | "bumpalo", 1376 | "log", 1377 | "once_cell", 1378 | "proc-macro2", 1379 | "quote", 1380 | "syn", 1381 | "wasm-bindgen-shared", 1382 | ] 1383 | 1384 | [[package]] 1385 | name = "wasm-bindgen-macro" 1386 | version = "0.2.83" 1387 | source = "registry+https://github.com/rust-lang/crates.io-index" 1388 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" 1389 | dependencies = [ 1390 | "quote", 1391 | "wasm-bindgen-macro-support", 1392 | ] 1393 | 1394 | [[package]] 1395 | name = "wasm-bindgen-macro-support" 1396 | version = "0.2.83" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" 1399 | dependencies = [ 1400 | "proc-macro2", 1401 | "quote", 1402 | "syn", 1403 | "wasm-bindgen-backend", 1404 | "wasm-bindgen-shared", 1405 | ] 1406 | 1407 | [[package]] 1408 | name = "wasm-bindgen-shared" 1409 | version = "0.2.83" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 1412 | 1413 | [[package]] 1414 | name = "web-sys" 1415 | version = "0.3.60" 1416 | source = "registry+https://github.com/rust-lang/crates.io-index" 1417 | checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" 1418 | dependencies = [ 1419 | "js-sys", 1420 | "wasm-bindgen", 1421 | ] 1422 | 1423 | [[package]] 1424 | name = "webpki" 1425 | version = "0.22.0" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" 1428 | dependencies = [ 1429 | "ring", 1430 | "untrusted", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "winapi" 1435 | version = "0.3.9" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1438 | dependencies = [ 1439 | "winapi-i686-pc-windows-gnu", 1440 | "winapi-x86_64-pc-windows-gnu", 1441 | ] 1442 | 1443 | [[package]] 1444 | name = "winapi-i686-pc-windows-gnu" 1445 | version = "0.4.0" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1448 | 1449 | [[package]] 1450 | name = "winapi-util" 1451 | version = "0.1.5" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1454 | dependencies = [ 1455 | "winapi", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "winapi-x86_64-pc-windows-gnu" 1460 | version = "0.4.0" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1463 | 1464 | [[package]] 1465 | name = "windows-sys" 1466 | version = "0.36.1" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 1469 | dependencies = [ 1470 | "windows_aarch64_msvc", 1471 | "windows_i686_gnu", 1472 | "windows_i686_msvc", 1473 | "windows_x86_64_gnu", 1474 | "windows_x86_64_msvc", 1475 | ] 1476 | 1477 | [[package]] 1478 | name = "windows_aarch64_msvc" 1479 | version = "0.36.1" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 1482 | 1483 | [[package]] 1484 | name = "windows_i686_gnu" 1485 | version = "0.36.1" 1486 | source = "registry+https://github.com/rust-lang/crates.io-index" 1487 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 1488 | 1489 | [[package]] 1490 | name = "windows_i686_msvc" 1491 | version = "0.36.1" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 1494 | 1495 | [[package]] 1496 | name = "windows_x86_64_gnu" 1497 | version = "0.36.1" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 1500 | 1501 | [[package]] 1502 | name = "windows_x86_64_msvc" 1503 | version = "0.36.1" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 1506 | 1507 | [[package]] 1508 | name = "x509-parser" 1509 | version = "0.14.0" 1510 | source = "registry+https://github.com/rust-lang/crates.io-index" 1511 | checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" 1512 | dependencies = [ 1513 | "asn1-rs", 1514 | "base64", 1515 | "data-encoding", 1516 | "der-parser", 1517 | "lazy_static", 1518 | "nom", 1519 | "oid-registry", 1520 | "ring", 1521 | "rusticata-macros", 1522 | "thiserror", 1523 | "time", 1524 | ] 1525 | 1526 | [[package]] 1527 | name = "yasna" 1528 | version = "0.5.0" 1529 | source = "registry+https://github.com/rust-lang/crates.io-index" 1530 | checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" 1531 | dependencies = [ 1532 | "time", 1533 | ] 1534 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dolores" 3 | version = "0.1.0" 4 | authors = ["Łukasz Niemier "] 5 | edition = "2021" 6 | resolver = "2" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [profile.dev] 11 | split-debuginfo = "unpacked" 12 | 13 | [profile.release] 14 | lto = "thin" 15 | 16 | [dependencies] 17 | askama = "0.11.1" 18 | async-stream = "0.3" 19 | async-trait = "0.1" 20 | bincode = "1" 21 | clap = { version = "4", features = ["derive", "env"] } 22 | clap_complete = "4" 23 | clap_mangen = "0.2" 24 | color-eyre = "0.6" 25 | hyper = { version = "0.14", features = ["full"] } 26 | matchit = "0.6" 27 | nix = "0.25" 28 | indoc = "1" 29 | once_cell = "1" 30 | rand = "0.8" 31 | rcgen = { version = "0.10", features = ["pem", "x509-parser"] } 32 | rustls = "0.20" 33 | serde = { version = "1", features = ["derive"] } 34 | tokio = { version = "1", features = ["full"] } 35 | tokio-rustls = "0.23" 36 | tracing = "0.1" 37 | tracing-subscriber = "0.3" 38 | http = "0.2" 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Łukasz Niemier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dolores 2 | 3 | **WIP: Works, but there is still a lot rough edges** 4 | 5 | Local development HTTPS proxy server meant to simplify working with multi-domain 6 | applications by serving each application on separate domain under `.localhost` 7 | TLD. 8 | 9 | ## Usage 10 | 11 | First we need to run our master server that will proxy all requests to the 12 | separate applications: 13 | 14 | ```sh 15 | sudo dolores serve 16 | ``` 17 | 18 | Now, as **unprivileged user** we can run: 19 | 20 | ```sh 21 | dolores run --name my-app 22 | ``` 23 | 24 | For example 25 | 26 | ```sh 27 | dolores run --name foo mix phx.server 28 | ``` 29 | 30 | And your application will receive socket to listen on (passed as FD3). It is 31 | provided to your application in the same way that systemd socket activation 32 | works. In short there are 3 environment variables: 33 | 34 | - `LISTEN_FDS` - amount of file descriptors passed in. These are FD 3 up to 35 | FD 3 + `LISTEN_FDS` 36 | - `LISTEN_FDNAMES` - `:` (colon) separated list of FD names used to differentiate 37 | between them 38 | - `LISTEN_PID` - PID of the process that the FD are meant for 39 | 40 | With current PoC implementation you can assume that there will be only one 41 | FD passes and it will be FD 3. 42 | 43 | Now you should be able to visit your application on . 44 | 45 | ## Goals 46 | 47 | - [x] Listen on HTTPS requests and dispatch requests to given application 48 | - [x] Passthrough proxy 49 | - [x] TLS terminating proxy 50 | - [ ] Socket activation on macOS and systemd-enabled Linux distributions 51 | - [ ] On-the-fly generation of TLS certificates (partially supported, only 52 | self-signed certs are supported for now) 53 | - [ ] Registration of external ports 54 | - [ ] Built-in ACME server for passthrough services 55 | - [ ] Create page presenting all registered applications 56 | - [ ] Provide Prometheus metrics for the proxy server 57 | - [ ] Collect Prometheus metrics for all running applications 58 | 59 | ## Non-goals 60 | 61 | - Performance - this tool is meant to be a development utility, performance 62 | improvements that could hurt usability are no go. 63 | - Production-grade load balancing - for the same reason as above. Securing 64 | everything, performance tuning, etc. 65 | 66 | ## License 67 | 68 | MIT 69 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1659877975, 6 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "flake-utils_2": { 19 | "locked": { 20 | "lastModified": 1659877975, 21 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", 22 | "owner": "numtide", 23 | "repo": "flake-utils", 24 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "numtide", 29 | "repo": "flake-utils", 30 | "type": "github" 31 | } 32 | }, 33 | "nixpkgs": { 34 | "locked": { 35 | "lastModified": 1666753130, 36 | "narHash": "sha256-Wff1dGPFSneXJLI2c0kkdWTgxnQ416KE6X4KnFkgPYQ=", 37 | "owner": "NixOS", 38 | "repo": "nixpkgs", 39 | "rev": "f540aeda6f677354f1e7144ab04352f61aaa0118", 40 | "type": "github" 41 | }, 42 | "original": { 43 | "id": "nixpkgs", 44 | "type": "indirect" 45 | } 46 | }, 47 | "nixpkgs_2": { 48 | "locked": { 49 | "lastModified": 1665296151, 50 | "narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=", 51 | "owner": "NixOS", 52 | "repo": "nixpkgs", 53 | "rev": "14ccaaedd95a488dd7ae142757884d8e125b3363", 54 | "type": "github" 55 | }, 56 | "original": { 57 | "owner": "NixOS", 58 | "ref": "nixpkgs-unstable", 59 | "repo": "nixpkgs", 60 | "type": "github" 61 | } 62 | }, 63 | "root": { 64 | "inputs": { 65 | "flake-utils": "flake-utils", 66 | "nixpkgs": "nixpkgs", 67 | "rust-overlay": "rust-overlay" 68 | } 69 | }, 70 | "rust-overlay": { 71 | "inputs": { 72 | "flake-utils": "flake-utils_2", 73 | "nixpkgs": "nixpkgs_2" 74 | }, 75 | "locked": { 76 | "lastModified": 1666925689, 77 | "narHash": "sha256-X2n+i2efiXcniDPDbvMsJVrPd7epu3+6pS/Pwu3OkHg=", 78 | "owner": "oxalica", 79 | "repo": "rust-overlay", 80 | "rev": "4b1fcd5766db910c07c871d1454b1fe296e4547c", 81 | "type": "github" 82 | }, 83 | "original": { 84 | "owner": "oxalica", 85 | "repo": "rust-overlay", 86 | "type": "github" 87 | } 88 | } 89 | }, 90 | "root": "root", 91 | "version": 7 92 | } 93 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A very basic flake"; 3 | 4 | inputs.nixpkgs.url = "flake:nixpkgs"; 5 | inputs.flake-utils.url = "github:numtide/flake-utils"; 6 | inputs.rust-overlay.url = "github:oxalica/rust-overlay"; 7 | 8 | outputs = { self, nixpkgs, rust-overlay, flake-utils }: flake-utils.lib.eachDefaultSystem (system: 9 | let 10 | pkgs = import nixpkgs { 11 | inherit system; 12 | overlays = [ 13 | rust-overlay.overlay 14 | (self: super: { 15 | # Because rust-overlay bundles multiple rust packages into one 16 | # derivation, specify that mega-bundle here, so that crate2nix 17 | # will use them automatically. 18 | rustc = self.rust-bin.stable.latest.default; 19 | cargo = self.rust-bin.stable.latest.default; 20 | }) 21 | ]; 22 | }; 23 | 24 | in 25 | { 26 | inherit self nixpkgs; 27 | 28 | devShell = pkgs.mkShell { 29 | buildInputs = with pkgs; [ 30 | cargo 31 | cargo-bloat 32 | cargo-outdated 33 | clippy 34 | rustc 35 | rust-analyzer 36 | libiconv 37 | rustfmt 38 | socat 39 | openssl 40 | ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ 41 | darwin.apple_sdk.frameworks.CoreFoundation 42 | darwin.apple_sdk.frameworks.CoreServices 43 | darwin.apple_sdk.frameworks.IOKit 44 | ]; 45 | }; 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /src/cli/gen/cert.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::Result; 2 | 3 | use std::fs::File; 4 | use std::io::prelude::*; 5 | use std::path::PathBuf; 6 | 7 | use rcgen::{BasicConstraints::*, GeneralSubtree::DnsName, *}; 8 | 9 | #[derive(clap::Args, Debug)] 10 | /// Generate CA certificate and gey for usage with the server. 11 | pub(crate) struct Command { 12 | #[arg(long, default_value = "dolores.crt")] 13 | /// Filename for certificate file 14 | cert: PathBuf, 15 | #[arg(long, default_value = "dolores.key")] 16 | /// Filename for key file 17 | key: PathBuf, 18 | #[arg(long = "domain", default_value = "localhost")] 19 | /// Domains that will be supported by given certificate 20 | domains: Vec, 21 | } 22 | 23 | impl Command { 24 | pub(crate) fn run(self) -> Result<()> { 25 | let mut distinguished_name = DistinguishedName::new(); 26 | distinguished_name.push(DnType::CommonName, "Dolores localhost certificate"); 27 | let subtrees = self 28 | .domains 29 | .iter() 30 | .map(|domain| format!(".{}", domain)) 31 | .map(DnsName) 32 | .collect(); 33 | let name_constraints = NameConstraints { 34 | permitted_subtrees: subtrees, 35 | excluded_subtrees: vec![], 36 | }; 37 | 38 | let mut params = CertificateParams::new(self.domains); 39 | params.key_usages = vec![KeyUsagePurpose::KeyCertSign]; 40 | params.is_ca = IsCa::Ca(Constrained(0)); 41 | params.distinguished_name = distinguished_name; 42 | params.name_constraints = Some(name_constraints); 43 | 44 | let cert = Certificate::from_params(params)?; 45 | let cert_pem = cert.serialize_pem()?; 46 | let key_pem = cert.serialize_private_key_pem(); 47 | 48 | File::create(self.cert)?.write_all(cert_pem.as_bytes())?; 49 | File::create(self.key)?.write_all(key_pem.as_bytes())?; 50 | 51 | Ok(()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/cli/gen/completion.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use std::str::FromStr; 3 | 4 | use color_eyre::eyre::Result; 5 | 6 | use clap::CommandFactory; 7 | use clap_complete::{generate, Shell}; 8 | 9 | use indoc::printdoc; 10 | 11 | /// Generate shell completion 12 | #[derive(clap::Args, Debug)] 13 | pub(crate) struct Command { 14 | /// Name of the shell for which the completion should be generated. 15 | /// 16 | /// By default will try to detect the shell using `SHELL` environment variable. 17 | shell: Option, 18 | } 19 | 20 | impl Command { 21 | pub fn run(self) -> Result<()> { 22 | let mut cmd = crate::cli::App::command(); 23 | let name = cmd.get_name().to_string(); 24 | 25 | let shell = self.shell.or_else(Self::default_shell); 26 | 27 | // println!("{shell:?}"); 28 | 29 | match shell { 30 | Some(shell) => generate(shell, &mut cmd, name, &mut std::io::stdout()), 31 | None => { 32 | printdoc! {" 33 | Couldn't detect shell. 34 | Provide shell as an argument to the command, ex. 35 | 36 | dolores gen completion bash 37 | "}; 38 | }, 39 | } 40 | 41 | Ok(()) 42 | } 43 | 44 | fn default_shell() -> Option { 45 | let shell_env = std::env::var("SHELL").ok()?; 46 | let shell_path = Path::new(&shell_env); 47 | 48 | shell_path 49 | .file_stem()? 50 | .to_str() 51 | .and_then(|x| FromStr::from_str(x).ok()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/cli/gen/man.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | use std::path::Path; 4 | 5 | use color_eyre::eyre::Result; 6 | 7 | use clap::CommandFactory; 8 | use clap_mangen::Man; 9 | 10 | /// Generate manpages for Dolores 11 | #[derive(clap::Args, Debug)] 12 | pub(crate) struct Command { 13 | /// Directory where manpages will be written to. 14 | output_dir: std::path::PathBuf, 15 | } 16 | 17 | impl Command { 18 | pub fn run(self) -> Result<()> { 19 | let mut cmd = crate::cli::App::command(); 20 | cmd.build(); 21 | 22 | print_manpages(&self.output_dir, &cmd)?; 23 | 24 | Ok(()) 25 | } 26 | } 27 | 28 | fn print_manpages(dir: &Path, app: &clap::Command) -> Result<()> { 29 | fn print(dir: &Path, app: &clap::Command) -> Result<()> { 30 | // `get_display_name()` is `Some` for all instances, except the root. 31 | let name = app.get_display_name().unwrap_or_else(|| app.get_name()); 32 | 33 | { 34 | let mut out = File::create(dir.join(format!("{name}.1")))?; 35 | 36 | Man::new(app.clone()).render(&mut out)?; 37 | out.flush()?; 38 | } 39 | 40 | for sub in app.get_subcommands() { 41 | print(dir, sub)?; 42 | } 43 | 44 | Ok(()) 45 | } 46 | 47 | print(dir, &app) 48 | } 49 | -------------------------------------------------------------------------------- /src/cli/gen/mod.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::Result; 2 | 3 | mod cert; 4 | mod completion; 5 | mod man; 6 | 7 | /// Utilities for generating multiple files useful for working with Dolores 8 | #[derive(clap::Args, Debug)] 9 | pub(crate) struct Command { 10 | #[command(subcommand)] 11 | command: Generator 12 | } 13 | 14 | #[derive(clap::Subcommand, Debug)] 15 | enum Generator { 16 | Cert(cert::Command), 17 | Completion(completion::Command), 18 | Man(man::Command), 19 | } 20 | 21 | impl Command { 22 | pub(crate) fn run(self) -> Result<()> { 23 | match self.command { 24 | Generator::Cert(cmd) => cmd.run(), 25 | Generator::Completion(cmd) => cmd.run(), 26 | Generator::Man(cmd) => cmd.run(), 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/cli/mod.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::Result; 2 | 3 | mod run; 4 | mod serve; 5 | mod status; 6 | mod gen; 7 | 8 | #[derive(clap::Parser, Debug)] 9 | #[command(version, author, about)] 10 | pub struct App { 11 | /// Enable debugging logs. 12 | #[arg(short, long)] 13 | pub debug: bool, 14 | 15 | #[command(subcommand)] 16 | command: Command, 17 | 18 | /// Path for UNIX socket used for communicating with Dolores server 19 | #[arg( 20 | long = "socket", 21 | env = "DOLORES_SOCKET", 22 | default_value = "/var/run/dolores.sock" 23 | )] 24 | socket_path: std::path::PathBuf, 25 | } 26 | 27 | impl App { 28 | pub fn new() -> Self { clap::Parser::parse() } 29 | 30 | pub fn run(self) -> Result<()> { 31 | tracing::debug!(?self); 32 | 33 | self.command.run(&self.socket_path) 34 | } 35 | } 36 | 37 | #[derive(clap::Subcommand, Debug)] 38 | enum Command { 39 | Run(run::Command), 40 | Serve(serve::Command), 41 | Status(status::Command), 42 | Gen(gen::Command), 43 | } 44 | 45 | impl Command { 46 | fn run(self, path: &std::path::Path) -> Result<()> { 47 | match self { 48 | Command::Run(cmd) => cmd.run(path), 49 | Command::Serve(cmd) => cmd.run(path), 50 | Command::Status(cmd) => cmd.run(path), 51 | Command::Gen(cmd) => cmd.run(), 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/cli/run.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::net; 3 | use std::os::unix::process::CommandExt; 4 | use std::process; 5 | 6 | use nix::sys::socket::{self, socket}; 7 | use nix::unistd::{dup2, fork, ForkResult, Pid}; 8 | use color_eyre::eyre::Result; 9 | 10 | /// Run given command and pass sockets to listen on incoming connections 11 | #[derive(clap::Args, Debug)] 12 | pub(crate) struct Command { 13 | /// Name of the service to register in the daemon. 14 | /// It will also be used as a domain to access the application. 15 | #[arg(short, long)] 16 | name: Option, 17 | 18 | #[arg(long, default_value = "terminating")] 19 | proxy: crate::proxy::Type, 20 | 21 | #[arg(name = "PROG")] 22 | prog_name: String, 23 | 24 | #[arg(name = "ARGS")] 25 | prog_args: Vec, 26 | } 27 | 28 | const FD_START: i32 = 3; 29 | 30 | // TODO: Support more socket types and allow using other socket types, not only TCP 31 | fn open_socket() -> io::Result { 32 | let addr: socket::SockaddrIn6 = net::SocketAddrV6::new(net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 0, 0, 0).into(); 33 | 34 | let fd = socket( 35 | socket::AddressFamily::Inet6, 36 | socket::SockType::Stream, 37 | socket::SockFlag::empty(), 38 | None, 39 | )?; 40 | 41 | socket::bind(fd, &addr)?; 42 | socket::listen(fd, 10)?; 43 | 44 | dup2(fd, FD_START as i32)?; 45 | 46 | match socket::getsockname(fd)? { 47 | socket::SockAddr::Inet(addr) => Ok(addr.to_std()), 48 | _ => unreachable!(), 49 | } 50 | } 51 | 52 | impl Command { 53 | pub(crate) fn run(self, path: &std::path::Path) -> Result<()> { 54 | let name = self.name.as_ref().unwrap_or(&self.prog_name); 55 | let span = tracing::span!(tracing::Level::DEBUG, "run"); 56 | let _guard = span.enter(); 57 | 58 | tracing::debug!("Starting"); 59 | 60 | let addr = open_socket()?; 61 | 62 | match unsafe { fork() }? { 63 | ForkResult::Child => { 64 | let error = process::Command::new(&self.prog_name) 65 | .args(&self.prog_args) 66 | // Use systemd-like interface to pass the sockets to the new process 67 | .env("LISTEN_FDS", "1") 68 | .env("LISTEN_PID", Pid::this().to_string()) 69 | .env("LISTEN_FDNAMES", "http") 70 | .exec(); 71 | 72 | // If we reach that, then `exec` above failed, so we just return error directly 73 | Err(error.into()) 74 | } 75 | ForkResult::Parent { child, .. } => { 76 | let runtime = tokio::runtime::Builder::new_current_thread() 77 | .enable_all() 78 | .build()?; 79 | let span = tracing::span!(tracing::Level::DEBUG, "run", child = ?child.as_raw()); 80 | let _guard = span.enter(); 81 | 82 | runtime 83 | .block_on(async { 84 | use crate::registry; 85 | let client = registry::Client::open(path)?; 86 | let mut watcher = 87 | tokio::signal::unix::signal(tokio::signal::unix::SignalKind::child())?; 88 | 89 | client 90 | .send(registry::Command::Register { 91 | name: name.into(), 92 | addr, 93 | proxy: self.proxy, 94 | }) 95 | .await?; 96 | 97 | tracing::debug!(?addr, "Registered"); 98 | loop { 99 | tokio::select! { 100 | _ = tokio::signal::ctrl_c() => 101 | nix::sys::signal::kill(child, nix::sys::signal::SIGINT)?, 102 | _ = watcher.recv() => break, 103 | } 104 | } 105 | tracing::debug!("Shutting down"); 106 | 107 | client 108 | .send(registry::Command::Deregister { name: name.into() }) 109 | .await?; 110 | 111 | Ok(()) 112 | }) 113 | .or_else(|err| { 114 | nix::sys::signal::kill(child, nix::sys::signal::SIGTERM)?; 115 | Err(err) 116 | }) 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/cli/serve.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use color_eyre::eyre::Result; 4 | use tokio::net::{TcpListener, TcpStream}; 5 | 6 | /// Start master process listening for connections 7 | #[derive(clap::Args, Debug)] 8 | pub(crate) struct Command { 9 | /// TLD that will be used for handling the applications 10 | #[arg(short, long, default_value = "localhost")] 11 | domain: String, 12 | 13 | /// Address which Dolores should listen at 14 | #[arg(short, long, default_value = "0.0.0.0:443")] 15 | listen: std::net::SocketAddr, 16 | 17 | /// Path to the PEM encoded Certificate Authority key 18 | #[arg(long, requires("ca_key"))] 19 | ca_cert: Option, 20 | 21 | /// Path to the PEM encoded Certificate Authority private certificate 22 | #[arg(long, requires("ca_cert"))] 23 | ca_key: Option, 24 | } 25 | 26 | impl Command { 27 | pub(crate) fn run(self, path: &std::path::Path) -> Result<()> { 28 | let runtime = tokio::runtime::Runtime::new()?; 29 | 30 | let span = tracing::span!(tracing::Level::DEBUG, "serve"); 31 | let _guard = span.enter(); 32 | 33 | let result = runtime.block_on(self.serve(path)); 34 | 35 | tracing::info!("Shutting down"); 36 | 37 | result 38 | } 39 | 40 | async fn serve(&self, path: &std::path::Path) -> Result<()> { 41 | let listener = TcpListener::bind(self.listen).await?; 42 | let registry = crate::registry::Registry::open(path, &self.domain)?; 43 | 44 | // Use self signed certificate to make the `rustls` happy (it is not really used right 45 | // now). In future it may be used for https://localhost or other pages to show list of the 46 | // currently registered apps, metrics, etc. 47 | let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); 48 | let certs = vec![rustls::Certificate(cert.serialize_der().unwrap())]; 49 | let priv_key = rustls::PrivateKey(cert.serialize_private_key_der()); 50 | 51 | let config = rustls::ServerConfig::builder() 52 | .with_safe_defaults() 53 | .with_no_client_auth() 54 | .with_single_cert(certs, priv_key) 55 | .expect("Bad certificate/key"); 56 | 57 | let config = Arc::new(config); 58 | 59 | let dashboard = Arc::new(crate::dashboard::Server::new( 60 | registry.services.clone(), 61 | config.clone(), 62 | )); 63 | 64 | tracing::info!(%self.listen, "TCP request"); 65 | tracing::info!(?path, "Controller"); 66 | 67 | loop { 68 | tokio::select! { 69 | // Control socket 70 | _ = registry.handle() => (), 71 | // Frontend socket 72 | Ok((stream, addr)) = listener.accept() => { 73 | let span = tracing::span!(tracing::Level::DEBUG, "Connection", addr = %addr); 74 | let _guard = span.enter(); 75 | 76 | let connection = rustls::ServerConnection::new(config.clone())?; 77 | let services = registry.services.clone(); 78 | 79 | let handler = handle_request(services, stream, connection, dashboard.clone()); 80 | 81 | tokio::spawn(handler); 82 | } 83 | _ = tokio::signal::ctrl_c() => return Ok(()), 84 | } 85 | } 86 | } 87 | } 88 | 89 | async fn handle_request( 90 | services: crate::registry::RegistryStore, 91 | up: TcpStream, 92 | mut connection: rustls::ServerConnection, 93 | dashboard: Arc, 94 | ) { 95 | let mut buf = [0; 1024]; 96 | // Peek into the 1 MiB of the data and try to check if there is SNI information 97 | let len = up.peek(&mut buf).await.unwrap(); 98 | if let Some(sni) = crate::service::parse_handshake(&mut connection, &buf[..len]) { 99 | let span = tracing::span!(tracing::Level::DEBUG, "Request", sni = %sni); 100 | let _guard = span.enter(); 101 | 102 | tracing::info!("Request"); 103 | 104 | let service = match services.read().await.get(&*sni) { 105 | Some(service) => service.clone(), 106 | None => { 107 | // TODO: Redirect to page for service selection 108 | tracing::warn!("Unknown service"); 109 | return; 110 | } 111 | }; 112 | 113 | tracing::debug!(%service.addr); 114 | 115 | let down = TcpStream::connect(service.addr).await.unwrap(); 116 | 117 | let proxy = service.proxy.clone(); 118 | proxy.run(up, down).await.unwrap(); 119 | } else { 120 | tracing::info!("Dashboard"); 121 | if let Err(err) = dashboard.handle(up).await { 122 | tracing::error!(%err); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/cli/status.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::Result; 2 | 3 | /// Return status of the registered services 4 | #[derive(clap::Args, Debug)] 5 | pub(crate) struct Command { 6 | /// Name of service for which status should be checked 7 | name: Option, 8 | } 9 | 10 | impl Command { 11 | pub(crate) fn run(self, path: &std::path::Path) -> Result<()> { 12 | let runtime = tokio::runtime::Builder::new_current_thread() 13 | .enable_all() 14 | .build()?; 15 | 16 | let span = tracing::span!(tracing::Level::DEBUG, "status"); 17 | let _guard = span.enter(); 18 | 19 | runtime.block_on(async { 20 | tracing::debug!("Query status"); 21 | let client = crate::registry::Client::open(path)?; 22 | tracing::debug!("Client started"); 23 | let resp = client 24 | .call(crate::registry::Command::Status { name: self.name }) 25 | .await?; 26 | tracing::info!(?resp); 27 | Ok(()) 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/dashboard/handlers.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::Result; 2 | use hyper::{Body, Request, Response, StatusCode}; 3 | use askama::Template; 4 | 5 | use std::sync::Arc; 6 | use std::collections::HashMap; 7 | 8 | #[derive(Clone, Copy)] 9 | pub struct Home; 10 | 11 | #[derive(Template)] 12 | #[allow(dead_code)] 13 | #[template(path = "hello.html")] 14 | struct HomeTemplate<'a> { 15 | req: Request, 16 | registry: &'a HashMap 17 | } 18 | 19 | #[async_trait] 20 | impl super::Handler for Home { 21 | async fn handle( 22 | self: Arc, 23 | req: Request, 24 | ctx: super::Context, 25 | ) -> Result> { 26 | let registry = ctx.registry.read().await; 27 | 28 | let view = HomeTemplate { req, registry: &*registry }; 29 | 30 | Ok(Response::builder() 31 | .header("content-type", "text/html") 32 | .body(Body::from(view.render()?))?) 33 | } 34 | } 35 | 36 | pub struct Health; 37 | 38 | #[async_trait] 39 | impl super::Handler for Health { 40 | async fn handle( 41 | self: Arc, 42 | _req: Request, 43 | _ctx: super::Context, 44 | ) -> Result> { 45 | Ok(Response::new(Body::from("Ok\n"))) 46 | } 47 | } 48 | 49 | mod filters { 50 | #![allow(dead_code)] 51 | 52 | use hyper::Request; 53 | 54 | pub fn debug(val: impl std::fmt::Debug) -> askama::Result { 55 | Ok(format!("{val:?}")) 56 | } 57 | 58 | pub fn domain_url(domain: &str, req: &Request) -> askama::Result { 59 | let port = match req.uri().port_u16() { 60 | Some(p) if p != 443 => format!(":{p}"), 61 | _ => "".into() 62 | }; 63 | Ok(format!("https://{domain}{port}")) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/dashboard/mod.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::Result; 2 | use hyper::server::conn::Http; 3 | use hyper::service::service_fn; 4 | use hyper::{Body, Request, Response, StatusCode}; 5 | 6 | use std::sync::Arc; 7 | use std::collections::HashMap; 8 | 9 | use crate::registry::RegistryStore; 10 | 11 | mod handlers; 12 | 13 | #[async_trait] 14 | trait Handler: Send + Sync { 15 | async fn handle(self: Arc, req: Request, ctx: Context) -> Result>; 16 | } 17 | 18 | #[derive(Debug, Clone, serde::Serialize)] 19 | pub struct Data { 20 | registry: HashMap 21 | } 22 | 23 | #[derive(Clone)] 24 | pub struct Context { 25 | registry: RegistryStore, 26 | } 27 | 28 | pub struct Server { 29 | acceptor: tokio_rustls::TlsAcceptor, 30 | router: Arc>>, 31 | registry: RegistryStore, 32 | } 33 | 34 | impl Server { 35 | pub fn new(registry: RegistryStore, tls_config: Arc) -> Self { 36 | let acceptor = tokio_rustls::TlsAcceptor::from(tls_config); 37 | let mut router = matchit::Router::>::new(); 38 | 39 | router.insert("/", Arc::new(handlers::Home)).unwrap(); 40 | router.insert("/health", Arc::new(handlers::Health)).unwrap(); 41 | 42 | Server { acceptor, registry, router: Arc::new(router) } 43 | } 44 | 45 | pub async fn handle(&self, stream: tokio::net::TcpStream) -> std::io::Result<()> { 46 | let tls_stream = self.acceptor.accept(stream).await?; 47 | 48 | let service_fn = service_fn(move |req| { 49 | let req = add_host(req); 50 | tracing::info!(?req); 51 | let ctx = Context { 52 | registry: self.registry.clone(), 53 | }; 54 | let router = self.router.clone(); 55 | let route = router.at(req.uri().path()).unwrap(); 56 | Handler::handle(route.value.clone(), req, ctx) 57 | }); 58 | 59 | if let Err(http_err) = Http::new() 60 | .serve_connection(tls_stream, service_fn) 61 | .await 62 | { 63 | tracing::error!("Error while serving HTTP connection: {}", http_err); 64 | } 65 | 66 | Ok(()) 67 | } 68 | } 69 | 70 | /// Add details to URI from `Host` header 71 | fn add_host(mut req: Request) -> Request { 72 | let host = req.headers().get("host").cloned(); 73 | 74 | tracing::info!(?host); 75 | 76 | let uri = req.uri_mut(); 77 | let mut parts = uri.clone().into_parts(); 78 | // We know that we are handling HTTPS connection 79 | parts.scheme = Some(http::uri::Scheme::HTTPS); 80 | parts.authority = host.and_then(|host| http::uri::Authority::from_maybe_shared(host).ok()); 81 | 82 | *uri = hyper::Uri::from_parts(parts).unwrap(); 83 | 84 | req 85 | } 86 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate async_trait; 3 | 4 | pub mod cli; 5 | pub mod proxy; 6 | pub mod registry; 7 | pub mod service; 8 | 9 | mod dashboard; 10 | 11 | pub use registry::Client; 12 | pub use service::Service; 13 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre; 2 | use tracing_subscriber::filter::LevelFilter; 3 | 4 | fn main() -> eyre::Result<()> { 5 | color_eyre::install()?; 6 | 7 | let app = dolores::cli::App::new(); 8 | 9 | tracing_subscriber::fmt::fmt() 10 | .with_max_level(if app.debug { LevelFilter::DEBUG } else { LevelFilter::INFO }) 11 | .init(); 12 | 13 | app.run() 14 | } 15 | -------------------------------------------------------------------------------- /src/proxy.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io; 3 | use std::sync::Arc; 4 | 5 | mod tls_terminating; 6 | mod transparent; 7 | 8 | pub use tls_terminating::TlsTerminating; 9 | pub use transparent::Transparent; 10 | 11 | #[derive( 12 | Debug, Clone, Copy, PartialEq, Hash, serde::Serialize, serde::Deserialize, clap::ValueEnum 13 | )] 14 | pub enum Type { 15 | Passthrough, 16 | Terminating, 17 | } 18 | 19 | impl Type { 20 | pub fn build<'a>(self, domain: impl Into>) -> Arc { 21 | match self { 22 | Type::Passthrough => Arc::new(Transparent), 23 | Type::Terminating => Arc::new(TlsTerminating::self_signed(domain.into())), 24 | } 25 | } 26 | } 27 | 28 | pub struct Domain<'a>(Cow<'a, str>); 29 | 30 | impl<'a, S> From for Domain<'a> 31 | where 32 | S: Into>, 33 | { 34 | fn from(input: S) -> Self { 35 | Domain(input.into()) 36 | } 37 | } 38 | 39 | impl From> for Vec { 40 | fn from(Domain(ref domain): Domain) -> Vec { 41 | vec![domain.to_string(), format!("*.{}", domain)] 42 | } 43 | } 44 | 45 | #[async_trait] 46 | pub trait Proxy: Send + Sync + core::fmt::Debug { 47 | type Up; 48 | type Down; 49 | 50 | async fn run(&self, up: Self::Up, down: Self::Down) -> io::Result<()>; 51 | } 52 | 53 | pub type TcpProxy = dyn Proxy; 54 | -------------------------------------------------------------------------------- /src/proxy/tls_terminating.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::sync::Arc; 3 | 4 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 5 | use tokio_rustls::TlsAcceptor; 6 | 7 | /// TLS terminating proxy 8 | /// 9 | /// This proxy will terminate TLS on the boundary and will pass raw TCP communication downstream. 10 | /// It supports: 11 | /// 12 | /// - Self-signed certificates generated on demand 13 | /// - Generated certificates that are signed by the given CA (WIP) 14 | /// - Passed certificate (TODO) 15 | #[derive(Clone)] 16 | pub struct TlsTerminating { 17 | acceptor: TlsAcceptor, 18 | } 19 | 20 | impl TlsTerminating { 21 | pub fn self_signed(domain: super::Domain) -> Self { 22 | let cert = rcgen::generate_simple_self_signed(domain).unwrap(); 23 | let certs = vec![rustls::Certificate(cert.serialize_der().unwrap())]; 24 | let priv_key = rustls::PrivateKey(cert.serialize_private_key_der()); 25 | 26 | Self::build(certs, priv_key) 27 | } 28 | 29 | pub fn from_ca(domain: super::Domain, ca_cert: &rcgen::Certificate) -> Self { 30 | let cert = rcgen::generate_simple_self_signed(domain).unwrap(); 31 | let certs = vec![rustls::Certificate( 32 | cert.serialize_der_with_signer(ca_cert).unwrap(), 33 | )]; 34 | let priv_key = rustls::PrivateKey(cert.serialize_private_key_der()); 35 | 36 | Self::build(certs, priv_key) 37 | } 38 | 39 | fn build(certs: Vec, priv_key: rustls::PrivateKey) -> Self { 40 | let config = rustls::ServerConfig::builder() 41 | .with_safe_defaults() 42 | .with_no_client_auth() 43 | .with_single_cert(certs, priv_key) 44 | .expect("Bad certificate/key"); 45 | 46 | let acceptor = TlsAcceptor::from(Arc::new(config)); 47 | 48 | TlsTerminating { acceptor } 49 | } 50 | } 51 | 52 | #[async_trait] 53 | impl super::Proxy for TlsTerminating { 54 | type Up = tokio::net::TcpStream; 55 | type Down = tokio::net::TcpStream; 56 | 57 | async fn run(&self, up: Self::Up, mut down: Self::Down) -> io::Result<()> { 58 | tracing::debug!("Proxy started"); 59 | let up_addr = up.local_addr().unwrap(); 60 | let down_addr = down.peer_addr().unwrap(); 61 | let mut up_buf = [0; 4 * 1024]; 62 | let mut down_buf = [0; 4 * 1024]; 63 | let mut up = self.acceptor.accept(up).await?; 64 | 65 | loop { 66 | // Read from any connection and write to the another one 67 | let finished = tokio::select! { 68 | result = up.read(&mut up_buf) => { 69 | tracing::trace!("{} -> {}", up_addr, down_addr); 70 | copy(result, &up_buf, &mut down).await? 71 | } 72 | result = down.read(&mut down_buf) => { 73 | tracing::trace!("{} <- {}", up_addr, down_addr); 74 | copy(result, &down_buf, &mut up).await? 75 | } 76 | }; 77 | 78 | if finished { 79 | return Ok(()); 80 | } 81 | } 82 | } 83 | } 84 | 85 | async fn copy( 86 | result: io::Result, 87 | buf: &[u8], 88 | out: &mut (impl AsyncWriteExt + Unpin), 89 | ) -> io::Result { 90 | match result { 91 | Ok(0) => { 92 | tracing::trace!("EOF"); 93 | Ok(true) 94 | } 95 | Ok(len) => { 96 | let data = std::str::from_utf8(&buf[..len]); 97 | tracing::trace!(?data, "Received"); 98 | out.write(&buf[..len]).await?; 99 | 100 | Ok(false) 101 | } 102 | Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { 103 | tracing::trace!("Would block"); 104 | Ok(false) 105 | } 106 | Err(err) => { 107 | tracing::error!(?err, "Error"); 108 | Err(err) 109 | } 110 | } 111 | } 112 | 113 | impl std::fmt::Debug for TlsTerminating { 114 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { 115 | f.write_str("TlsTerminating") 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/proxy/transparent.rs: -------------------------------------------------------------------------------- 1 | use tokio::io; 2 | use tokio::io::AsyncWriteExt; 3 | 4 | /// Transparent proxy 5 | /// 6 | /// This proxy will forward all data **as is** so it is the downstream responsibility to handle TLS 7 | /// termination 8 | #[derive(Clone, Debug)] 9 | pub struct Transparent; 10 | 11 | #[async_trait] 12 | impl super::Proxy for Transparent { 13 | type Up = tokio::net::TcpStream; 14 | type Down = tokio::net::TcpStream; 15 | 16 | async fn run(&self, mut up: Self::Up, mut down: Self::Down) -> io::Result<()> { 17 | tracing::debug!("Proxy started"); 18 | 19 | let (mut ru, mut wu) = up.split(); 20 | let (mut rd, mut wd) = down.split(); 21 | 22 | loop { 23 | let up_down = async { 24 | io::copy(&mut ru, &mut wd).await?; 25 | wd.shutdown().await 26 | }; 27 | let down_up = async { 28 | io::copy(&mut rd, &mut wu).await?; 29 | wu.shutdown().await 30 | }; 31 | 32 | // XXX: Check how to handle this result and whether it makes sense to report anything 33 | // meaningful there 34 | let _ = tokio::try_join!(up_down, down_up); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/registry.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs::Permissions; 3 | use std::os::unix::fs::PermissionsExt; 4 | use std::path::Path; 5 | use std::sync::Arc; 6 | 7 | use std::borrow::Cow; 8 | 9 | use tokio::io; 10 | use tokio::net::UnixDatagram; 11 | use tokio::sync::RwLock; 12 | 13 | #[derive(Debug, serde::Serialize, serde::Deserialize)] 14 | pub enum Command<'a> { 15 | Register { 16 | name: Cow<'a, str>, 17 | addr: std::net::SocketAddr, 18 | proxy: crate::proxy::Type, 19 | }, 20 | Deregister { 21 | name: Cow<'a, str>, 22 | }, 23 | Status { 24 | name: Option, 25 | }, 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct Client { 30 | socket: UnixDatagram, 31 | } 32 | 33 | impl Client { 34 | pub fn open>(path: P) -> io::Result { 35 | let id = rand::random::(); 36 | let mut rx = std::env::temp_dir(); 37 | rx.push(format!("dolores-{:x}-client.sock", id)); 38 | let socket = UnixDatagram::bind(rx.as_path())?; 39 | let perms = Permissions::from_mode(0o777); 40 | std::fs::set_permissions(&rx, perms)?; 41 | match socket.connect(path) { 42 | Ok(_) => Ok(Client { socket }), 43 | Err(err) => { 44 | std::fs::remove_file(rx.as_path())?; 45 | Err(err) 46 | } 47 | } 48 | } 49 | 50 | /// Send message and ignore any response 51 | pub async fn send(&self, cmd: Command<'_>) -> io::Result<()> { 52 | self.socket 53 | .send(&bincode::serialize(&cmd).unwrap()) 54 | .await 55 | .map(|_| ()) 56 | } 57 | 58 | /// Send message and await for response 59 | pub async fn call(&self, cmd: Command<'_>) -> io::Result { 60 | self.send(cmd).await?; 61 | let mut buf = [0; 1024]; 62 | let len = tokio::time::timeout( 63 | std::time::Duration::from_secs(5), 64 | self.socket.recv(&mut buf), 65 | ) 66 | .await??; 67 | String::from_utf8(buf[..len].into()) 68 | .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) 69 | } 70 | } 71 | 72 | impl std::str::FromStr for Client { 73 | type Err = io::Error; 74 | 75 | fn from_str(s: &str) -> Result { 76 | Client::open(s) 77 | } 78 | } 79 | 80 | impl Drop for Client { 81 | fn drop(&mut self) { 82 | let addr = self.socket.local_addr().unwrap(); 83 | let path = addr.as_pathname().unwrap(); 84 | 85 | std::fs::remove_file(path).unwrap(); 86 | } 87 | } 88 | 89 | pub type RegistryStore = Arc>>; 90 | 91 | #[derive(Debug)] 92 | pub struct Registry { 93 | domain: String, 94 | socket: UnixDatagram, 95 | pub services: RegistryStore, 96 | } 97 | 98 | impl Registry { 99 | pub fn open>(path: P, domain: &str) -> io::Result { 100 | let socket = UnixDatagram::bind(&path)?; 101 | let perms = Permissions::from_mode(0o777); 102 | std::fs::set_permissions(&path, perms)?; 103 | 104 | Ok(Registry { 105 | domain: domain.into(), 106 | socket, 107 | services: Arc::new(Default::default()), 108 | }) 109 | } 110 | 111 | pub async fn handle(&self) -> io::Result<()> { 112 | let mut buf = [0; 1024]; 113 | let (len, from) = self.socket.recv_from(&mut buf).await?; 114 | let cmd = bincode::deserialize::(&buf[..len]).unwrap(); 115 | tracing::debug!(?cmd); 116 | Self::handle_command( 117 | self.services.clone(), 118 | cmd, 119 | &self.socket, 120 | from.as_pathname().unwrap(), 121 | &self.domain, 122 | ) 123 | .await 124 | } 125 | 126 | async fn handle_command( 127 | services: RegistryStore, 128 | command: Command<'_>, 129 | sock: &UnixDatagram, 130 | to: &std::path::Path, 131 | domain: &str, 132 | ) -> std::io::Result<()> { 133 | use Command::*; 134 | 135 | match command { 136 | Status { name, .. } => { 137 | tracing::info!(name = %name.as_deref().unwrap_or("(all)"), "Status"); 138 | match name { 139 | Some(ref name) => { 140 | let services = services.read().await; 141 | let service = services.get(&*name); 142 | sock.send_to( 143 | format!("ok {:?}", service.map(|s| &s.domain)).as_bytes(), 144 | to, 145 | ) 146 | .await?; 147 | } 148 | None => { 149 | let mut out = String::new(); 150 | for (name, service) in &*services.read().await { 151 | out.push_str(&format!("{} -> {}", name, service.addr)); 152 | } 153 | 154 | sock.send_to(out.as_bytes(), to).await?; 155 | } 156 | } 157 | } 158 | Register { name, addr, proxy } => { 159 | let domain = format!("{}.{}", name, domain); 160 | tracing::info!(%name, %domain, "Register"); 161 | services 162 | .write() 163 | .await 164 | .insert(domain, crate::service::Service::new(&name, addr, proxy)); 165 | } 166 | Deregister { name, .. } => { 167 | let domain = format!("{}.{}", name, domain); 168 | let mut services = services.write().await; 169 | services.remove(&*domain).unwrap(); 170 | tracing::info!(%name, %domain, "Deregistered"); 171 | } 172 | }; 173 | 174 | Ok(()) 175 | } 176 | } 177 | 178 | impl Drop for Registry { 179 | fn drop(&mut self) { 180 | let addr = self.socket.local_addr().unwrap(); 181 | let path = addr.as_pathname().unwrap(); 182 | 183 | std::fs::remove_file(path).unwrap(); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/service.rs: -------------------------------------------------------------------------------- 1 | use std::net; 2 | use std::sync::Arc; 3 | 4 | #[derive(Clone, Debug, serde::Serialize)] 5 | pub struct Service { 6 | pub domain: String, 7 | pub addr: net::SocketAddr, 8 | #[serde(skip_serializing)] 9 | pub proxy: Arc, 10 | } 11 | 12 | impl Service { 13 | pub fn new(domain: &str, addr: net::SocketAddr, proxy: crate::proxy::Type) -> Self { 14 | Service { 15 | domain: domain.into(), 16 | addr, 17 | proxy: proxy.build(domain), 18 | } 19 | } 20 | } 21 | 22 | pub fn parse_handshake(connection: &mut rustls::ServerConnection, mut data: &[u8]) -> Option { 23 | connection.read_tls(&mut data).ok()?; 24 | let _ = connection.process_new_packets(); 25 | connection.sni_hostname().and_then(|sni| { 26 | let mut parts = sni.split('.'); 27 | let tld = parts.nth_back(0)?; 28 | let name = parts.nth_back(0)?; 29 | Some(format!("{}.{}", name, tld)) 30 | }) 31 | } 32 | 33 | impl net::ToSocketAddrs for Service { 34 | type Iter = std::option::IntoIter; 35 | 36 | fn to_socket_addrs(&self) -> std::io::Result { 37 | Ok(Some(self.addr).into_iter()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /templates/hello.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block content %} 4 |

Hello world!

5 | 6 |
    7 | {% for (domain, _) in registry %} 8 |
  • {{ domain }}
  • 9 | {% endfor %} 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dolores 5 | 6 | 7 | {% block content %}{% endblock %} 8 | 9 | 10 | --------------------------------------------------------------------------------