TLS: oxy serve --tls-cert/--tls-key via rustls; remote.<name>.ca and OXY_CA for self-hosted roots 2b5ee371060e
- Author
- Andy McCall @andymccall <[email protected]>
- When
- · 2026-09-26 19:11 UTC
- Signature
- ✔ signed by Andy McCall
- Hash
2b5ee371060e4359f6bebd5436825c1de4e6fe3cac5cf16774ea6481cafb7c4b- Depends on
- 5b9c6ad45f6e 750251b21a96 982864068a59 e993a086c13f fce44ae9ad29 ff0af1ee299d
- Depended on by
- 0588de69a84d 569802ebae0e 642da2636de2 544d0541a7e5 c8d9059e4e70 c895bc52e4e0 68a924846379 cb8b8082fa73 42ccf504aa50 2cd40ea1c04c 8d80356371c5 9f9e72b93e4e ff1f62fee654 a07f60a37fb2 11c958021e1a 3e5772ccf9da e7ddd552b619 37c299cec358 75167672ebcd e02bf0cd0533 b3c5a59a875a 2b872d074ddb 2863456a0f6d 135212de5370 252e8cb23fbc ba160f60219e d02e545dc265 83e0677b07fe
- In bookmarks
- agents-in-readme bare-hashes carried-not-removed carry-identical doctor-trust identical-sides keyless-tags main merge-remote-bookmark only-help-103 paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state switch-says
- In tags
- v0.1.0 v0.10.0 v0.11.0 v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.4.1 v0.4.2 v0.4.3 v0.4.4 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
- Edits
- 87 atoms, 0 tree operations, 11 files
Browse the tree at this change
modified Cargo.lock +417 −5
| … | 73 unchanged lines |
||
| 74 | 74 | checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" |
|
| 75 | 75 | ||
| 76 | 76 | [[package]] |
|
| 77 | + | name = "arc-swap" |
|
| 78 | + | version = "1.9.2" |
|
| 79 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 80 | + | checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" |
|
| 81 | + | dependencies = [ |
|
| 82 | + | "rustversion", |
|
| 83 | + | ] |
|
| 84 | + | ||
| 85 | + | [[package]] |
|
| 77 | 86 | name = "arrayvec" |
|
| 78 | 87 | version = "0.7.8" |
|
| 79 | 88 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 80 | 89 | checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" |
|
| 81 | 90 | ||
| 82 | 91 | [[package]] |
|
| 92 | + | name = "asn1-rs" |
|
| 93 | + | version = "0.7.2" |
|
| 94 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 95 | + | checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" |
|
| 96 | + | dependencies = [ |
|
| 97 | + | "asn1-rs-derive", |
|
| 98 | + | "asn1-rs-impl", |
|
| 99 | + | "displaydoc", |
|
| 100 | + | "nom", |
|
| 101 | + | "num-traits", |
|
| 102 | + | "rusticata-macros", |
|
| 103 | + | "thiserror 2.0.21", |
|
| 104 | + | "time", |
|
| 105 | + | ] |
|
| 106 | + | ||
| 107 | + | [[package]] |
|
| 108 | + | name = "asn1-rs-derive" |
|
| 109 | + | version = "0.6.0" |
|
| 110 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 111 | + | checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" |
|
| 112 | + | dependencies = [ |
|
| 113 | + | "proc-macro2", |
|
| 114 | + | "quote", |
|
| 115 | + | "syn 2.0.119", |
|
| 116 | + | "synstructure 0.13.2", |
|
| 117 | + | ] |
|
| 118 | + | ||
| 119 | + | [[package]] |
|
| 120 | + | name = "asn1-rs-impl" |
|
| 121 | + | version = "0.2.0" |
|
| 122 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 123 | + | checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" |
|
| 124 | + | dependencies = [ |
|
| 125 | + | "proc-macro2", |
|
| 126 | + | "quote", |
|
| 127 | + | "syn 2.0.119", |
|
| 128 | + | ] |
|
| 129 | + | ||
| 130 | + | [[package]] |
|
| 83 | 131 | name = "atomic-waker" |
|
| 84 | 132 | version = "1.1.2" |
|
| 85 | 133 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 58 unchanged lines |
||
| 144 | 192 | ] |
|
| 145 | 193 | ||
| 146 | 194 | [[package]] |
|
| 195 | + | name = "axum-server" |
|
| 196 | + | version = "0.8.0" |
|
| 197 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 198 | + | checksum = "b1df331683d982a0b9492b38127151e6453639cd34926eb9c07d4cd8c6d22bfc" |
|
| 199 | + | dependencies = [ |
|
| 200 | + | "arc-swap", |
|
| 201 | + | "bytes", |
|
| 202 | + | "either", |
|
| 203 | + | "fs-err", |
|
| 204 | + | "http", |
|
| 205 | + | "http-body", |
|
| 206 | + | "hyper", |
|
| 207 | + | "hyper-util", |
|
| 208 | + | "pin-project-lite", |
|
| 209 | + | "rustls", |
|
| 210 | + | "rustls-pki-types", |
|
| 211 | + | "tokio", |
|
| 212 | + | "tokio-rustls", |
|
| 213 | + | "tower-service", |
|
| 214 | + | ] |
|
| 215 | + | ||
| 216 | + | [[package]] |
|
| 147 | 217 | name = "base64" |
|
| 148 | 218 | version = "0.22.1" |
|
| 149 | 219 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 150 | 220 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" |
|
| 151 | 221 | ||
| 152 | 222 | [[package]] |
|
| 223 | + | name = "base64" |
|
| 224 | + | version = "0.23.1" |
|
| 225 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 226 | + | checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" |
|
| 227 | + | ||
| 228 | + | [[package]] |
|
| 153 | 229 | name = "base64ct" |
|
| 154 | 230 | version = "1.8.3" |
|
| 155 | 231 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 156 | 232 | checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" |
|
| 157 | 233 | ||
| 158 | 234 | [[package]] |
|
| 235 | + | name = "bit-vec" |
|
| 236 | + | version = "0.9.1" |
|
| 237 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 238 | + | checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" |
|
| 239 | + | dependencies = [ |
|
| 240 | + | "serde", |
|
| 241 | + | ] |
|
| 242 | + | ||
| 243 | + | [[package]] |
|
| 159 | 244 | name = "bitflags" |
|
| 160 | 245 | version = "2.13.2" |
|
| 161 | 246 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 208 unchanged lines |
||
| 370 | 455 | ] |
|
| 371 | 456 | ||
| 372 | 457 | [[package]] |
|
| 458 | + | name = "data-encoding" |
|
| 459 | + | version = "2.11.1" |
|
| 460 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 461 | + | checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" |
|
| 462 | + | ||
| 463 | + | [[package]] |
|
| 373 | 464 | name = "der" |
|
| 374 | 465 | version = "0.8.2" |
|
| 375 | 466 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 4 unchanged lines |
||
| 380 | 471 | ] |
|
| 381 | 472 | ||
| 382 | 473 | [[package]] |
|
| 474 | + | name = "der-parser" |
|
| 475 | + | version = "10.0.0" |
|
| 476 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 477 | + | checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" |
|
| 478 | + | dependencies = [ |
|
| 479 | + | "asn1-rs", |
|
| 480 | + | "displaydoc", |
|
| 481 | + | "nom", |
|
| 482 | + | "num-bigint", |
|
| 483 | + | "num-traits", |
|
| 484 | + | "rusticata-macros", |
|
| 485 | + | ] |
|
| 486 | + | ||
| 487 | + | [[package]] |
|
| 488 | + | name = "deranged" |
|
| 489 | + | version = "0.5.8" |
|
| 490 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 491 | + | checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" |
|
| 492 | + | ||
| 493 | + | [[package]] |
|
| 383 | 494 | name = "digest" |
|
| 384 | 495 | version = "0.11.3" |
|
| 385 | 496 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 37 unchanged lines |
||
| 423 | 534 | ] |
|
| 424 | 535 | ||
| 425 | 536 | [[package]] |
|
| 537 | + | name = "either" |
|
| 538 | + | version = "1.18.0" |
|
| 539 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 540 | + | checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" |
|
| 541 | + | ||
| 542 | + | [[package]] |
|
| 543 | + | name = "equivalent" |
|
| 544 | + | version = "1.0.2" |
|
| 545 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 546 | + | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" |
|
| 547 | + | ||
| 548 | + | [[package]] |
|
| 426 | 549 | name = "errno" |
|
| 427 | 550 | version = "0.3.14" |
|
| 428 | 551 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 33 unchanged lines |
||
| 462 | 585 | ] |
|
| 463 | 586 | ||
| 464 | 587 | [[package]] |
|
| 588 | + | name = "fnv" |
|
| 589 | + | version = "1.0.7" |
|
| 590 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 591 | + | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" |
|
| 592 | + | ||
| 593 | + | [[package]] |
|
| 465 | 594 | name = "form_urlencoded" |
|
| 466 | 595 | version = "1.2.2" |
|
| 467 | 596 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 3 unchanged lines |
||
| 471 | 600 | ] |
|
| 472 | 601 | ||
| 473 | 602 | [[package]] |
|
| 603 | + | name = "fs-err" |
|
| 604 | + | version = "3.3.1" |
|
| 605 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 606 | + | checksum = "b91aa448ca50d7e79433bdf3ee8d99215430d2ec02ade5aefab2a073a1822e8a" |
|
| 607 | + | dependencies = [ |
|
| 608 | + | "autocfg", |
|
| 609 | + | "tokio", |
|
| 610 | + | ] |
|
| 611 | + | ||
| 612 | + | [[package]] |
|
| 474 | 613 | name = "futures-channel" |
|
| 475 | 614 | version = "0.3.34" |
|
| 476 | 615 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 9 unchanged lines |
||
| 486 | 625 | checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" |
|
| 487 | 626 | ||
| 488 | 627 | [[package]] |
|
| 628 | + | name = "futures-sink" |
|
| 629 | + | version = "0.3.34" |
|
| 630 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 631 | + | checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" |
|
| 632 | + | ||
| 633 | + | [[package]] |
|
| 489 | 634 | name = "futures-task" |
|
| 490 | 635 | version = "0.3.34" |
|
| 491 | 636 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 48 unchanged lines |
||
| 540 | 685 | ] |
|
| 541 | 686 | ||
| 542 | 687 | [[package]] |
|
| 688 | + | name = "h2" |
|
| 689 | + | version = "0.4.19" |
|
| 690 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 691 | + | checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" |
|
| 692 | + | dependencies = [ |
|
| 693 | + | "atomic-waker", |
|
| 694 | + | "bytes", |
|
| 695 | + | "fnv", |
|
| 696 | + | "futures-core", |
|
| 697 | + | "futures-sink", |
|
| 698 | + | "http", |
|
| 699 | + | "indexmap", |
|
| 700 | + | "slab", |
|
| 701 | + | "tokio", |
|
| 702 | + | "tokio-util", |
|
| 703 | + | "tracing", |
|
| 704 | + | ] |
|
| 705 | + | ||
| 706 | + | [[package]] |
|
| 707 | + | name = "hashbrown" |
|
| 708 | + | version = "0.17.1" |
|
| 709 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 710 | + | checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" |
|
| 711 | + | ||
| 712 | + | [[package]] |
|
| 543 | 713 | name = "heck" |
|
| 544 | 714 | version = "0.5.0" |
|
| 545 | 715 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 70 unchanged lines |
||
| 616 | 786 | "bytes", |
|
| 617 | 787 | "futures-channel", |
|
| 618 | 788 | "futures-core", |
|
| 789 | + | "h2", |
|
| 619 | 790 | "http", |
|
| 620 | 791 | "http-body", |
|
| 621 | 792 | "httparse", |
|
| … | 140 unchanged lines |
||
| 762 | 933 | ] |
|
| 763 | 934 | ||
| 764 | 935 | [[package]] |
|
| 936 | + | name = "indexmap" |
|
| 937 | + | version = "2.14.2" |
|
| 938 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 939 | + | checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" |
|
| 940 | + | dependencies = [ |
|
| 941 | + | "equivalent", |
|
| 942 | + | "hashbrown", |
|
| 943 | + | ] |
|
| 944 | + | ||
| 945 | + | [[package]] |
|
| 765 | 946 | name = "is_terminal_polyfill" |
|
| 766 | 947 | version = "1.70.2" |
|
| 767 | 948 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 16 unchanged lines |
||
| 784 | 965 | ] |
|
| 785 | 966 | ||
| 786 | 967 | [[package]] |
|
| 968 | + | name = "lazy_static" |
|
| 969 | + | version = "1.5.0" |
|
| 970 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 971 | + | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" |
|
| 972 | + | ||
| 973 | + | [[package]] |
|
| 787 | 974 | name = "libc" |
|
| 788 | 975 | version = "0.2.189" |
|
| 789 | 976 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 36 unchanged lines |
||
| 826 | 1013 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" |
|
| 827 | 1014 | ||
| 828 | 1015 | [[package]] |
|
| 1016 | + | name = "minimal-lexical" |
|
| 1017 | + | version = "0.2.1" |
|
| 1018 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1019 | + | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" |
|
| 1020 | + | ||
| 1021 | + | [[package]] |
|
| 829 | 1022 | name = "miniz_oxide" |
|
| 830 | 1023 | version = "0.9.1" |
|
| 831 | 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 42 unchanged lines |
||
| 874 | 1067 | ] |
|
| 875 | 1068 | ||
| 876 | 1069 | [[package]] |
|
| 1070 | + | name = "nom" |
|
| 1071 | + | version = "7.1.3" |
|
| 1072 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1073 | + | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" |
|
| 1074 | + | dependencies = [ |
|
| 1075 | + | "memchr", |
|
| 1076 | + | "minimal-lexical", |
|
| 1077 | + | ] |
|
| 1078 | + | ||
| 1079 | + | [[package]] |
|
| 1080 | + | name = "num-bigint" |
|
| 1081 | + | version = "0.4.8" |
|
| 1082 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1083 | + | checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" |
|
| 1084 | + | dependencies = [ |
|
| 1085 | + | "num-integer", |
|
| 1086 | + | "num-traits", |
|
| 1087 | + | ] |
|
| 1088 | + | ||
| 1089 | + | [[package]] |
|
| 1090 | + | name = "num-conv" |
|
| 1091 | + | version = "0.2.2" |
|
| 1092 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1093 | + | checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" |
|
| 1094 | + | ||
| 1095 | + | [[package]] |
|
| 1096 | + | name = "num-integer" |
|
| 1097 | + | version = "0.1.47" |
|
| 1098 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1099 | + | checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" |
|
| 1100 | + | dependencies = [ |
|
| 1101 | + | "num-traits", |
|
| 1102 | + | ] |
|
| 1103 | + | ||
| 1104 | + | [[package]] |
|
| 877 | 1105 | name = "num-traits" |
|
| 878 | 1106 | version = "0.2.19" |
|
| 879 | 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 3 unchanged lines |
||
| 883 | 1111 | ] |
|
| 884 | 1112 | ||
| 885 | 1113 | [[package]] |
|
| 1114 | + | name = "oid-registry" |
|
| 1115 | + | version = "0.8.1" |
|
| 1116 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1117 | + | checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" |
|
| 1118 | + | dependencies = [ |
|
| 1119 | + | "asn1-rs", |
|
| 1120 | + | ] |
|
| 1121 | + | ||
| 1122 | + | [[package]] |
|
| 886 | 1123 | name = "once_cell" |
|
| 887 | 1124 | version = "1.21.4" |
|
| 888 | 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 27 unchanged lines |
||
| 916 | 1153 | "ignore", |
|
| 917 | 1154 | "ml-dsa", |
|
| 918 | 1155 | "rand", |
|
| 1156 | + | "rustls", |
|
| 1157 | + | "rustls-pemfile", |
|
| 919 | 1158 | "similar", |
|
| 920 | 1159 | "tempfile", |
|
| 921 | - | "thiserror", |
|
| 1160 | + | "thiserror 1.0.69", |
|
| 922 | 1161 | "ureq", |
|
| 1162 | + | "webpki-roots 1.0.9", |
|
| 923 | 1163 | ] |
|
| 924 | 1164 | ||
| 925 | 1165 | [[package]] |
|
| … | 1 unchanged lines |
||
| 927 | 1167 | version = "0.1.0" |
|
| 928 | 1168 | dependencies = [ |
|
| 929 | 1169 | "axum", |
|
| 1170 | + | "axum-server", |
|
| 930 | 1171 | "oxy-core", |
|
| 1172 | + | "rcgen", |
|
| 1173 | + | "rustls", |
|
| 931 | 1174 | "tempfile", |
|
| 932 | 1175 | "tokio", |
|
| 933 | 1176 | ] |
|
| 934 | 1177 | ||
| 935 | 1178 | [[package]] |
|
| 1179 | + | name = "pem" |
|
| 1180 | + | version = "4.0.0" |
|
| 1181 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1182 | + | checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120" |
|
| 1183 | + | dependencies = [ |
|
| 1184 | + | "base64 0.23.1", |
|
| 1185 | + | "serde_core", |
|
| 1186 | + | ] |
|
| 1187 | + | ||
| 1188 | + | [[package]] |
|
| 936 | 1189 | name = "percent-encoding" |
|
| 937 | 1190 | version = "2.3.2" |
|
| 938 | 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 25 unchanged lines |
||
| 964 | 1217 | ] |
|
| 965 | 1218 | ||
| 966 | 1219 | [[package]] |
|
| 1220 | + | name = "powerfmt" |
|
| 1221 | + | version = "0.2.0" |
|
| 1222 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1223 | + | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" |
|
| 1224 | + | ||
| 1225 | + | [[package]] |
|
| 967 | 1226 | name = "ppv-lite86" |
|
| 968 | 1227 | version = "0.2.21" |
|
| 969 | 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 63 unchanged lines |
||
| 1033 | 1292 | checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" |
|
| 1034 | 1293 | ||
| 1035 | 1294 | [[package]] |
|
| 1295 | + | name = "rcgen" |
|
| 1296 | + | version = "0.14.10" |
|
| 1297 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1298 | + | checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8" |
|
| 1299 | + | dependencies = [ |
|
| 1300 | + | "pem", |
|
| 1301 | + | "ring", |
|
| 1302 | + | "rustls-pki-types", |
|
| 1303 | + | "time", |
|
| 1304 | + | "x509-parser", |
|
| 1305 | + | "yasna", |
|
| 1306 | + | ] |
|
| 1307 | + | ||
| 1308 | + | [[package]] |
|
| 1036 | 1309 | name = "regex-automata" |
|
| 1037 | 1310 | version = "0.4.18" |
|
| 1038 | 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 34 unchanged lines |
||
| 1073 | 1346 | ] |
|
| 1074 | 1347 | ||
| 1075 | 1348 | [[package]] |
|
| 1349 | + | name = "rusticata-macros" |
|
| 1350 | + | version = "4.1.0" |
|
| 1351 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1352 | + | checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" |
|
| 1353 | + | dependencies = [ |
|
| 1354 | + | "nom", |
|
| 1355 | + | ] |
|
| 1356 | + | ||
| 1357 | + | [[package]] |
|
| 1076 | 1358 | name = "rustix" |
|
| 1077 | 1359 | version = "1.1.5" |
|
| 1078 | 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 22 unchanged lines |
||
| 1101 | 1383 | ] |
|
| 1102 | 1384 | ||
| 1103 | 1385 | [[package]] |
|
| 1386 | + | name = "rustls-pemfile" |
|
| 1387 | + | version = "2.2.0" |
|
| 1388 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1389 | + | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" |
|
| 1390 | + | dependencies = [ |
|
| 1391 | + | "rustls-pki-types", |
|
| 1392 | + | ] |
|
| 1393 | + | ||
| 1394 | + | [[package]] |
|
| 1104 | 1395 | name = "rustls-pki-types" |
|
| 1105 | 1396 | version = "1.15.1" |
|
| 1106 | 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 14 unchanged lines |
||
| 1121 | 1412 | ] |
|
| 1122 | 1413 | ||
| 1123 | 1414 | [[package]] |
|
| 1415 | + | name = "rustversion" |
|
| 1416 | + | version = "1.0.23" |
|
| 1417 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1418 | + | checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" |
|
| 1419 | + | ||
| 1420 | + | [[package]] |
|
| 1124 | 1421 | name = "ryu" |
|
| 1125 | 1422 | version = "1.0.23" |
|
| 1126 | 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 21 unchanged lines |
||
| 1148 | 1445 | checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" |
|
| 1149 | 1446 | dependencies = [ |
|
| 1150 | 1447 | "serde_core", |
|
| 1448 | + | "serde_derive", |
|
| 1151 | 1449 | ] |
|
| 1152 | 1450 | ||
| 1153 | 1451 | [[package]] |
|
| … | 188 unchanged lines |
||
| 1342 | 1640 | ||
| 1343 | 1641 | [[package]] |
|
| 1344 | 1642 | name = "synstructure" |
|
| 1643 | + | version = "0.13.2" |
|
| 1644 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1645 | + | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" |
|
| 1646 | + | dependencies = [ |
|
| 1647 | + | "proc-macro2", |
|
| 1648 | + | "quote", |
|
| 1649 | + | "syn 2.0.119", |
|
| 1650 | + | ] |
|
| 1651 | + | ||
| 1652 | + | [[package]] |
|
| 1653 | + | name = "synstructure" |
|
| 1345 | 1654 | version = "0.14.0" |
|
| 1346 | 1655 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1347 | 1656 | checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" |
|
| … | 22 unchanged lines |
||
| 1370 | 1679 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1371 | 1680 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" |
|
| 1372 | 1681 | dependencies = [ |
|
| 1373 | - | "thiserror-impl", |
|
| 1682 | + | "thiserror-impl 1.0.69", |
|
| 1374 | 1683 | ] |
|
| 1375 | 1684 | ||
| 1376 | 1685 | [[package]] |
|
| 1686 | + | name = "thiserror" |
|
| 1687 | + | version = "2.0.21" |
|
| 1688 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1689 | + | checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" |
|
| 1690 | + | dependencies = [ |
|
| 1691 | + | "thiserror-impl 2.0.21", |
|
| 1692 | + | ] |
|
| 1693 | + | ||
| 1694 | + | [[package]] |
|
| 1377 | 1695 | name = "thiserror-impl" |
|
| 1378 | 1696 | version = "1.0.69" |
|
| 1379 | 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 5 unchanged lines |
||
| 1385 | 1703 | ] |
|
| 1386 | 1704 | ||
| 1387 | 1705 | [[package]] |
|
| 1706 | + | name = "thiserror-impl" |
|
| 1707 | + | version = "2.0.21" |
|
| 1708 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1709 | + | checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" |
|
| 1710 | + | dependencies = [ |
|
| 1711 | + | "proc-macro2", |
|
| 1712 | + | "quote", |
|
| 1713 | + | "syn 3.0.6", |
|
| 1714 | + | ] |
|
| 1715 | + | ||
| 1716 | + | [[package]] |
|
| 1717 | + | name = "time" |
|
| 1718 | + | version = "0.3.55" |
|
| 1719 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1720 | + | checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" |
|
| 1721 | + | dependencies = [ |
|
| 1722 | + | "deranged", |
|
| 1723 | + | "num-conv", |
|
| 1724 | + | "powerfmt", |
|
| 1725 | + | "serde_core", |
|
| 1726 | + | "time-core", |
|
| 1727 | + | "time-macros", |
|
| 1728 | + | ] |
|
| 1729 | + | ||
| 1730 | + | [[package]] |
|
| 1731 | + | name = "time-core" |
|
| 1732 | + | version = "0.1.9" |
|
| 1733 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1734 | + | checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" |
|
| 1735 | + | ||
| 1736 | + | [[package]] |
|
| 1737 | + | name = "time-macros" |
|
| 1738 | + | version = "0.2.32" |
|
| 1739 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1740 | + | checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" |
|
| 1741 | + | dependencies = [ |
|
| 1742 | + | "num-conv", |
|
| 1743 | + | "time-core", |
|
| 1744 | + | ] |
|
| 1745 | + | ||
| 1746 | + | [[package]] |
|
| 1388 | 1747 | name = "tinystr" |
|
| 1389 | 1748 | version = "0.8.4" |
|
| 1390 | 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 9 unchanged lines |
||
| 1400 | 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1401 | 1760 | checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" |
|
| 1402 | 1761 | dependencies = [ |
|
| 1762 | + | "bytes", |
|
| 1403 | 1763 | "libc", |
|
| 1404 | 1764 | "mio", |
|
| 1405 | 1765 | "pin-project-lite", |
|
| … | 14 unchanged lines |
||
| 1420 | 1780 | ] |
|
| 1421 | 1781 | ||
| 1422 | 1782 | [[package]] |
|
| 1783 | + | name = "tokio-rustls" |
|
| 1784 | + | version = "0.26.5" |
|
| 1785 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1786 | + | checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" |
|
| 1787 | + | dependencies = [ |
|
| 1788 | + | "rustls", |
|
| 1789 | + | "tokio", |
|
| 1790 | + | ] |
|
| 1791 | + | ||
| 1792 | + | [[package]] |
|
| 1793 | + | name = "tokio-util" |
|
| 1794 | + | version = "0.7.19" |
|
| 1795 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1796 | + | checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" |
|
| 1797 | + | dependencies = [ |
|
| 1798 | + | "bytes", |
|
| 1799 | + | "futures-core", |
|
| 1800 | + | "futures-sink", |
|
| 1801 | + | "libc", |
|
| 1802 | + | "pin-project-lite", |
|
| 1803 | + | "tokio", |
|
| 1804 | + | ] |
|
| 1805 | + | ||
| 1806 | + | [[package]] |
|
| 1423 | 1807 | name = "tower" |
|
| 1424 | 1808 | version = "0.5.3" |
|
| 1425 | 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 65 unchanged lines |
||
| 1491 | 1875 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 1492 | 1876 | checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" |
|
| 1493 | 1877 | dependencies = [ |
|
| 1494 | - | "base64", |
|
| 1878 | + | "base64 0.22.1", |
|
| 1495 | 1879 | "flate2", |
|
| 1496 | 1880 | "log", |
|
| 1497 | 1881 | "once_cell", |
|
| … | 165 unchanged lines |
||
| 1663 | 2047 | checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" |
|
| 1664 | 2048 | ||
| 1665 | 2049 | [[package]] |
|
| 2050 | + | name = "x509-parser" |
|
| 2051 | + | version = "0.18.1" |
|
| 2052 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 2053 | + | checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" |
|
| 2054 | + | dependencies = [ |
|
| 2055 | + | "asn1-rs", |
|
| 2056 | + | "data-encoding", |
|
| 2057 | + | "der-parser", |
|
| 2058 | + | "lazy_static", |
|
| 2059 | + | "nom", |
|
| 2060 | + | "oid-registry", |
|
| 2061 | + | "ring", |
|
| 2062 | + | "rusticata-macros", |
|
| 2063 | + | "thiserror 2.0.21", |
|
| 2064 | + | "time", |
|
| 2065 | + | ] |
|
| 2066 | + | ||
| 2067 | + | [[package]] |
|
| 2068 | + | name = "yasna" |
|
| 2069 | + | version = "0.6.0" |
|
| 2070 | + | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| 2071 | + | checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" |
|
| 2072 | + | dependencies = [ |
|
| 2073 | + | "bit-vec", |
|
| 2074 | + | "time", |
|
| 2075 | + | ] |
|
| 2076 | + | ||
| 2077 | + | [[package]] |
|
| 1666 | 2078 | name = "yoke" |
|
| 1667 | 2079 | version = "0.8.3" |
|
| 1668 | 2080 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
| … | 13 unchanged lines |
||
| 1682 | 2094 | "proc-macro2", |
|
| 1683 | 2095 | "quote", |
|
| 1684 | 2096 | "syn 3.0.6", |
|
| 1685 | - | "synstructure", |
|
| 2097 | + | "synstructure 0.14.0", |
|
| 1686 | 2098 | ] |
|
| 1687 | 2099 | ||
| 1688 | 2100 | [[package]] |
|
| … | 34 unchanged lines |
||
| 1723 | 2135 | "proc-macro2", |
|
| 1724 | 2136 | "quote", |
|
| 1725 | 2137 | "syn 3.0.6", |
|
| 1726 | - | "synstructure", |
|
| 2138 | + | "synstructure 0.14.0", |
|
| 1727 | 2139 | ] |
|
| 1728 | 2140 | ||
| 1729 | 2141 | [[package]] |
|
| … | 49 unchanged lines |
modified crates/oxy-core/Cargo.toml +3 −0
| … | 14 unchanged lines |
||
| 15 | 15 | ml-dsa = "0.1" |
|
| 16 | 16 | ed25519-dalek = "3" |
|
| 17 | 17 | ureq = "2" |
|
| 18 | + | rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } |
|
| 19 | + | rustls-pemfile = "2" |
|
| 20 | + | webpki-roots = "1" |
|
| 18 | 21 | ||
| 19 | 22 | [dev-dependencies] |
|
| 20 | 23 | tempfile = "3" |
modified crates/oxy-core/src/protocol.rs +41 −5
| … | 11 unchanged lines |
||
| 12 | 12 | use crate::identity::Signature; |
|
| 13 | 13 | use crate::objects::{self as o, Change, ObjectError}; |
|
| 14 | 14 | use crate::repo::{Repo, RepoError, Result}; |
|
| 15 | - | use std::path::Path; |
|
| 15 | + | use std::path::{Path, PathBuf}; |
|
| 16 | + | use std::sync::Arc; |
|
| 16 | 17 | ||
| 17 | 18 | pub const CONTENT_TYPE: &str = "application/oxy-cbor"; |
|
| 18 | 19 | pub const PROTOCOL_VERSION: u64 = 1; |
|
| … | 90 unchanged lines |
||
| 109 | 110 | ||
| 110 | 111 | // ---------------------------------------------------------------- http |
|
| 111 | 112 | ||
| 113 | + | /// How to reach a remote: credentials and trust. |
|
| 114 | + | #[derive(Clone, Debug, Default)] |
|
| 115 | + | pub struct RemoteOptions { |
|
| 116 | + | /// Bearer token for pushes. |
|
| 117 | + | pub token: Option<String>, |
|
| 118 | + | /// PEM file with extra root certificates to trust for `https://` remotes |
|
| 119 | + | /// (self-hosted servers); the system web roots are always trusted. |
|
| 120 | + | pub ca: Option<PathBuf>, |
|
| 121 | + | } |
|
| 122 | + | ||
| 112 | 123 | /// A remote served by `oxy-server` at `<base>/oxy/v1/...`. |
|
| 113 | 124 | pub struct HttpRemote { |
|
| 114 | 125 | base: String, |
|
| … | 1 unchanged lines |
||
| 116 | 127 | agent: ureq::Agent, |
|
| 117 | 128 | } |
|
| 118 | 129 | ||
| 130 | + | /// A TLS client configuration trusting the web roots plus `ca`, if given. |
|
| 131 | + | fn tls_client_config(ca: Option<&Path>) -> Result<Arc<rustls::ClientConfig>> { |
|
| 132 | + | let mut roots = rustls::RootCertStore::empty(); |
|
| 133 | + | roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); |
|
| 134 | + | if let Some(path) = ca { |
|
| 135 | + | let pem = std::fs::read(path).map_err(|e| RepoError::Msg(format!("reading CA file {}: {e}", path.display())))?; |
|
| 136 | + | let certs = rustls_pemfile::certs(&mut pem.as_slice()) |
|
| 137 | + | .collect::<std::result::Result<Vec<_>, _>>() |
|
| 138 | + | .map_err(|e| RepoError::Msg(format!("parsing CA file {}: {e}", path.display())))?; |
|
| 139 | + | if certs.is_empty() { |
|
| 140 | + | return Err(RepoError::Msg(format!("CA file {} holds no certificates", path.display()))); |
|
| 141 | + | } |
|
| 142 | + | for c in certs { |
|
| 143 | + | roots.add(c).map_err(|e| RepoError::Msg(format!("CA file {}: {e}", path.display())))?; |
|
| 144 | + | } |
|
| 145 | + | } |
|
| 146 | + | let cfg = rustls::ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider())) |
|
| 147 | + | .with_safe_default_protocol_versions() |
|
| 148 | + | .map_err(|e| RepoError::Msg(e.to_string()))? |
|
| 149 | + | .with_root_certificates(roots) |
|
| 150 | + | .with_no_client_auth(); |
|
| 151 | + | Ok(Arc::new(cfg)) |
|
| 152 | + | } |
|
| 153 | + | ||
| 119 | 154 | impl HttpRemote { |
|
| 120 | - | pub fn new(base: &str, token: Option<String>) -> HttpRemote { |
|
| 121 | - | HttpRemote { base: base.trim_end_matches('/').to_string(), token, agent: ureq::Agent::new() } |
|
| 155 | + | pub fn new(base: &str, options: &RemoteOptions) -> Result<HttpRemote> { |
|
| 156 | + | let agent = ureq::AgentBuilder::new().tls_config(tls_client_config(options.ca.as_deref())?).build(); |
|
| 157 | + | Ok(HttpRemote { base: base.trim_end_matches('/').to_string(), token: options.token.clone(), agent }) |
|
| 122 | 158 | } |
|
| 123 | 159 | ||
| 124 | 160 | fn call(&self, post: bool, endpoint: &str, body: Option<&[u8]>) -> Result<Vec<u8>> { |
|
| … | 41 unchanged lines |
||
| 166 | 202 | } |
|
| 167 | 203 | ||
| 168 | 204 | /// Open a remote by URL: a local path (optionally `file://`), or `http(s)://`. |
|
| 169 | - | pub fn open_remote(url: &str, token: Option<String>) -> Result<Box<dyn Remote>> { |
|
| 205 | + | pub fn open_remote(url: &str, options: &RemoteOptions) -> Result<Box<dyn Remote>> { |
|
| 170 | 206 | if url.starts_with("http://") || url.starts_with("https://") { |
|
| 171 | - | return Ok(Box::new(HttpRemote::new(url, token))); |
|
| 207 | + | return Ok(Box::new(HttpRemote::new(url, options)?)); |
|
| 172 | 208 | } |
|
| 173 | 209 | let path = url.strip_prefix("file://").unwrap_or(url); |
|
| 174 | 210 | if path.contains("://") { |
|
| … | 288 unchanged lines |
modified crates/oxy-core/src/repo/config.rs +15 −6
| … | 20 unchanged lines |
||
| 21 | 21 | pub remotes: BTreeMap<String, String>, |
|
| 22 | 22 | /// `remote.<name>.token`: bearer token for HTTP pushes. |
|
| 23 | 23 | pub remote_tokens: BTreeMap<String, String>, |
|
| 24 | + | /// `remote.<name>.ca`: PEM file of extra roots to trust for that remote. |
|
| 25 | + | pub remote_cas: BTreeMap<String, String>, |
|
| 24 | 26 | } |
|
| 25 | 27 | ||
| 26 | 28 | impl Config { |
|
| … | 8 unchanged lines |
||
| 35 | 37 | bookmark: "main".into(), |
|
| 36 | 38 | remotes: BTreeMap::new(), |
|
| 37 | 39 | remote_tokens: BTreeMap::new(), |
|
| 40 | + | remote_cas: BTreeMap::new(), |
|
| 38 | 41 | } |
|
| 39 | 42 | } |
|
| 40 | 43 | ||
| … | 43 unchanged lines |
||
| 84 | 87 | } |
|
| 85 | 88 | } |
|
| 86 | 89 | "workspace.bookmark" => self.bookmark = value.into(), |
|
| 87 | - | k if k.starts_with("remote.") && (k.ends_with(".url") || k.ends_with(".token")) => { |
|
| 88 | - | let (name, is_url) = match k.strip_suffix(".url") { |
|
| 89 | - | Some(n) => (&n["remote.".len()..], true), |
|
| 90 | - | None => (&k["remote.".len()..k.len() - ".token".len()], false), |
|
| 90 | + | k if k.starts_with("remote.") => { |
|
| 91 | + | let rest = &k["remote.".len()..]; |
|
| 92 | + | let Some((name, field)) = rest.rsplit_once('.') else { |
|
| 93 | + | return fail(format!("unknown configuration key {key:?}")); |
|
| 91 | 94 | }; |
|
| 92 | 95 | if name.is_empty() || name.contains('/') { |
|
| 93 | 96 | return fail(format!("invalid remote name in {key:?}")); |
|
| 94 | 97 | } |
|
| 95 | - | let map = if is_url { &mut self.remotes } else { &mut self.remote_tokens }; |
|
| 98 | + | let map = match field { |
|
| 99 | + | "url" => &mut self.remotes, |
|
| 100 | + | "token" => &mut self.remote_tokens, |
|
| 101 | + | "ca" => &mut self.remote_cas, |
|
| 102 | + | _ => return fail(format!("unknown configuration key {key:?}")), |
|
| 103 | + | }; |
|
| 96 | 104 | if value.is_empty() { |
|
| 97 | 105 | map.remove(name); |
|
| 98 | - | if is_url { |
|
| 106 | + | if field == "url" { |
|
| 99 | 107 | self.remote_tokens.remove(name); |
|
| 108 | + | self.remote_cas.remove(name); |
|
| 100 | 109 | } |
|
| 101 | 110 | } else { |
|
| 102 | 111 | map.insert(name.to_string(), value.into()); |
|
| … | 57 unchanged lines |
modified crates/oxy-core/src/repo/mod.rs +1 −1
| … | 7 unchanged lines |
||
| 8 | 8 | ||
| 9 | 9 | use crate::attest::{Attest, AttestBody}; |
|
| 10 | 10 | use crate::diff::{ranges, split_lines, text_edits}; |
|
| 11 | - | use crate::protocol::{open_remote, Bundle, RefResult, RefUpdate, RemoteRef, SignedAttest}; |
|
| 11 | + | use crate::protocol::{open_remote, Bundle, RefResult, RefUpdate, RemoteOptions, RemoteRef, SignedAttest}; |
|
| 12 | 12 | use crate::hash::{hash_object, ChangeHash, KeyId, ObjectRef, ObjectType}; |
|
| 13 | 13 | use crate::identity::{verify as verify_signature, Domain, Identity, SecretKey, Signature}; |
|
| 14 | 14 | use crate::ids::{ChangeId, NodeId, WorkspaceId}; |
|
| … | 543 unchanged lines |
modified crates/oxy-core/src/repo/sync.rs +13 −5
| … | 279 unchanged lines |
||
| 280 | 280 | self.config.remotes.get(name).cloned().ok_or_else(|| RepoError::Msg(format!("no remote named {name:?}; add one with `oxy remote add`"))) |
|
| 281 | 281 | } |
|
| 282 | 282 | ||
| 283 | - | /// Token for a remote: `remote.<name>.token`, else `$OXY_TOKEN`. |
|
| 284 | - | fn remote_token(&self, name: &str) -> Option<String> { |
|
| 285 | - | self.config.remote_tokens.get(name).cloned().or_else(|| std::env::var("OXY_TOKEN").ok()) |
|
| 283 | + | /// Credentials and trust for a remote: `remote.<name>.token` (else |
|
| 284 | + | /// `$OXY_TOKEN`) and `remote.<name>.ca` (else `$OXY_CA`). |
|
| 285 | + | fn remote_options(&self, name: &str) -> RemoteOptions { |
|
| 286 | + | RemoteOptions { |
|
| 287 | + | token: self.config.remote_tokens.get(name).cloned().or_else(|| std::env::var("OXY_TOKEN").ok()), |
|
| 288 | + | ca: self.config.remote_cas.get(name).map(PathBuf::from).or_else(|| std::env::var("OXY_CA").ok().map(PathBuf::from)), |
|
| 289 | + | } |
|
| 286 | 290 | } |
|
| 287 | 291 | ||
| 288 | 292 | fn open_named_remote(&self, name: &str) -> Result<Box<dyn crate::protocol::Remote>> { |
|
| 289 | - | open_remote(&self.remote_url(name)?, self.remote_token(name)) |
|
| 293 | + | open_remote(&self.remote_url(name)?, &self.remote_options(name)) |
|
| 290 | 294 | } |
|
| 291 | 295 | ||
| 292 | 296 | fn serial_path(&self, remote: &str, bookmark: &str) -> PathBuf { |
|
| … | 185 unchanged lines |
||
| 478 | 482 | /// Clone a remote into `dest`: same repository ID, `origin` remote, |
|
| 479 | 483 | /// bookmark set to the remote's, working copy checked out. |
|
| 480 | 484 | pub fn clone(url: &str, dest: &Path) -> Result<Repo> { |
|
| 481 | - | let remote = open_remote(url, std::env::var("OXY_TOKEN").ok())?; |
|
| 485 | + | let options = RemoteOptions { |
|
| 486 | + | token: std::env::var("OXY_TOKEN").ok(), |
|
| 487 | + | ca: std::env::var("OXY_CA").ok().map(PathBuf::from), |
|
| 488 | + | }; |
|
| 489 | + | let remote = open_remote(url, &options)?; |
|
| 482 | 490 | let id = remote.repo_id()?; |
|
| 483 | 491 | let mut repo = Repo::init_with_id(dest, id)?; |
|
| 484 | 492 | repo.config.set("remote.origin.url", url)?; |
|
| … | 14 unchanged lines |
modified crates/oxy-server/Cargo.toml +3 −0
| … | 8 unchanged lines |
||
| 9 | 9 | oxy-core = { path = "../oxy-core" } |
|
| 10 | 10 | axum = "0.8" |
|
| 11 | 11 | tokio = { version = "1", features = ["rt-multi-thread", "net", "macros", "sync"] } |
|
| 12 | + | axum-server = { version = "0.8", default-features = false, features = ["tls-rustls-no-provider"] } |
|
| 13 | + | rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } |
|
| 12 | 14 | ||
| 13 | 15 | [dev-dependencies] |
|
| 16 | + | rcgen = "0.14" |
|
| 14 | 17 | tempfile = "3" |
modified crates/oxy-server/src/lib.rs +26 −6
| 1 | 1 | //! Reference HTTP server for the Oxygen wire protocol (spec §15.1, §16). |
|
| 2 | 2 | //! |
|
| 3 | - | //! Serves every repository directory under a root at `/{repo}/oxy/v1/...`. |
|
| 4 | - | //! TLS is expected to be terminated in front of this server for now; the |
|
| 5 | - | //! protocol's integrity does not depend on the channel (spec §15). |
|
| 3 | + | //! Serves every repository directory under a root at `/{repo}/oxy/v1/...`, |
|
| 4 | + | //! over TLS when a certificate and key are given, else plain HTTP for use |
|
| 5 | + | //! behind a TLS-terminating proxy or on a loopback interface (spec §15.1). |
|
| 6 | 6 | //! |
|
| 7 | 7 | //! Authentication: when bearer tokens are configured, `push` requires one; |
|
| 8 | 8 | //! reads are open. With no tokens configured everything is open (development). |
|
| … | 16 unchanged lines |
||
| 25 | 25 | pub root: PathBuf, |
|
| 26 | 26 | /// Bearer tokens allowed to push; empty means no authentication. |
|
| 27 | 27 | pub tokens: Vec<String>, |
|
| 28 | + | /// Serve HTTPS with this certificate chain and private key (PEM files). |
|
| 29 | + | pub tls: Option<TlsFiles>, |
|
| 28 | 30 | } |
|
| 29 | 31 | ||
| 32 | + | #[derive(Clone, Debug)] |
|
| 33 | + | pub struct TlsFiles { |
|
| 34 | + | pub cert: PathBuf, |
|
| 35 | + | pub key: PathBuf, |
|
| 36 | + | } |
|
| 37 | + | ||
| 30 | 38 | struct App { |
|
| 31 | 39 | cfg: ServerConfig, |
|
| 32 | 40 | /// One lock per repository so concurrent pushes serialize. |
|
| … | 119 unchanged lines |
||
| 152 | 160 | .with_state(app) |
|
| 153 | 161 | } |
|
| 154 | 162 | ||
| 155 | - | /// Serve on an already-bound listener until the process ends. |
|
| 163 | + | /// Serve on an already-bound listener until the process ends; HTTPS when |
|
| 164 | + | /// `cfg.tls` is set. |
|
| 156 | 165 | pub async fn serve(listener: std::net::TcpListener, cfg: ServerConfig) -> std::io::Result<()> { |
|
| 157 | 166 | listener.set_nonblocking(true)?; |
|
| 158 | - | let listener = tokio::net::TcpListener::from_std(listener)?; |
|
| 159 | - | axum::serve(listener, router(cfg)).await |
|
| 167 | + | match cfg.tls.clone() { |
|
| 168 | + | Some(tls) => { |
|
| 169 | + | let _ = rustls::crypto::ring::default_provider().install_default(); |
|
| 170 | + | let rustls_cfg = axum_server::tls_rustls::RustlsConfig::from_pem_file(&tls.cert, &tls.key) |
|
| 171 | + | .await |
|
| 172 | + | .map_err(|e| std::io::Error::other(format!("loading TLS files: {e}")))?; |
|
| 173 | + | axum_server::from_tcp_rustls(listener, rustls_cfg)?.serve(router(cfg).into_make_service()).await |
|
| 174 | + | } |
|
| 175 | + | None => { |
|
| 176 | + | let listener = tokio::net::TcpListener::from_std(listener)?; |
|
| 177 | + | axum::serve(listener, router(cfg)).await |
|
| 178 | + | } |
|
| 179 | + | } |
|
| 160 | 180 | } |
|
| 161 | 181 | ||
| 162 | 182 | /// Build a runtime and serve; for the CLI and for tests on a background thread. |
|
| … | 4 unchanged lines |
modified crates/oxy-server/tests/http.rs +52 −2
| … | 23 unchanged lines |
||
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | fn start(root: &Path, tokens: Vec<String>) -> String { |
|
| 27 | + | start_with(root, tokens, None) |
|
| 28 | + | } |
|
| 29 | + | ||
| 30 | + | fn start_with(root: &Path, tokens: Vec<String>, tls: Option<oxy_server::TlsFiles>) -> String { |
|
| 27 | 31 | let listener = TcpListener::bind("127.0.0.1:0").unwrap(); |
|
| 28 | 32 | let port = listener.local_addr().unwrap().port(); |
|
| 29 | - | let cfg = oxy_server::ServerConfig { root: root.to_path_buf(), tokens }; |
|
| 33 | + | let scheme = if tls.is_some() { "https" } else { "http" }; |
|
| 34 | + | let cfg = oxy_server::ServerConfig { root: root.to_path_buf(), tokens, tls }; |
|
| 30 | 35 | std::thread::spawn(move || oxy_server::run_blocking(listener, cfg).unwrap()); |
|
| 31 | - | format!("http://127.0.0.1:{port}") |
|
| 36 | + | // Give the acceptor a moment; the first request otherwise races the bind. |
|
| 37 | + | std::thread::sleep(std::time::Duration::from_millis(50)); |
|
| 38 | + | format!("{scheme}://127.0.0.1:{port}") |
|
| 32 | 39 | } |
|
| 33 | 40 | ||
| 41 | + | /// A self-signed certificate for 127.0.0.1 written to `dir`: (cert, key). |
|
| 42 | + | fn self_signed(dir: &Path) -> (std::path::PathBuf, std::path::PathBuf) { |
|
| 43 | + | fs::create_dir_all(dir).unwrap(); |
|
| 44 | + | let ck = rcgen::generate_simple_self_signed(vec!["127.0.0.1".into(), "localhost".into()]).unwrap(); |
|
| 45 | + | let cert = dir.join("cert.pem"); |
|
| 46 | + | let key = dir.join("key.pem"); |
|
| 47 | + | fs::write(&cert, ck.cert.pem()).unwrap(); |
|
| 48 | + | fs::write(&key, ck.signing_key.serialize_pem()).unwrap(); |
|
| 49 | + | (cert, key) |
|
| 50 | + | } |
|
| 51 | + | ||
| 34 | 52 | #[test] |
|
| 35 | 53 | fn clone_push_pull_over_http() { |
|
| 36 | 54 | hermetic(); |
|
| … | 64 unchanged lines |
||
| 101 | 119 | let origin = Repo::open(&served.join("origin")).unwrap(); |
|
| 102 | 120 | assert_eq!(origin.bookmark_heads().unwrap().len(), 1); |
|
| 103 | 121 | } |
|
| 122 | + | ||
| 123 | + | #[test] |
|
| 124 | + | fn https_with_a_self_signed_certificate() { |
|
| 125 | + | hermetic(); |
|
| 126 | + | let dir = tempfile::tempdir().unwrap(); |
|
| 127 | + | let root = dir.path(); |
|
| 128 | + | let served = root.join("served"); |
|
| 129 | + | Repo::init(&served.join("origin")).unwrap(); |
|
| 130 | + | let (cert, key) = self_signed(root); |
|
| 131 | + | let base = start_with(&served, vec![], Some(oxy_server::TlsFiles { cert: cert.clone(), key })); |
|
| 132 | + | let url = format!("{base}/origin"); |
|
| 133 | + | ||
| 134 | + | // Without trusting the certificate the clone fails; with `OXY_CA` it works. |
|
| 135 | + | let err = Repo::clone(&url, &root.join("untrusted")).err().unwrap().to_string(); |
|
| 136 | + | assert!(err.contains("certificate") || err.contains("tls") || err.contains("TLS") || err.contains("handshake"), "{err}"); |
|
| 137 | + | std::env::set_var("OXY_CA", &cert); |
|
| 138 | + | let mut a = Repo::clone(&url, &root.join("a")).unwrap(); |
|
| 139 | + | std::env::remove_var("OXY_CA"); |
|
| 140 | + | // Persist the trust for this remote so later commands need no env var. |
|
| 141 | + | a.config.set("remote.origin.ca", cert.to_str().unwrap()).unwrap(); |
|
| 142 | + | a.save_config().unwrap(); |
|
| 143 | + | write(&root.join("a"), "f.txt", b"tls\n"); |
|
| 144 | + | let h = a.commit("over tls", &[]).unwrap(); |
|
| 145 | + | assert!(a.push("origin", false).unwrap()[0].ok); |
|
| 146 | + | let origin = Repo::open(&served.join("origin")).unwrap(); |
|
| 147 | + | assert_eq!(origin.bookmark_heads().unwrap(), vec![h]); |
|
| 148 | + | // A wrong CA is refused. |
|
| 149 | + | let (other_cert, _) = self_signed(&root.join("other")); |
|
| 150 | + | a.config.set("remote.origin.ca", other_cert.to_str().unwrap()).unwrap(); |
|
| 151 | + | a.save_config().unwrap(); |
|
| 152 | + | assert!(a.fetch("origin").is_err()); |
|
| 153 | + | } |
modified src/cli/mod.rs +9 −3
| … | 71 unchanged lines |
||
| 72 | 72 | #[arg(long)] |
|
| 73 | 73 | force: bool, |
|
| 74 | 74 | }, |
|
| 75 | - | /// Serve repositories under ROOT over HTTP (spec §16); TLS is terminated in front |
|
| 75 | + | /// Serve repositories under ROOT over HTTPS (with --tls-cert/--tls-key) or plain HTTP |
|
| 76 | 76 | Serve { |
|
| 77 | 77 | /// Directory with one repository per subdirectory |
|
| 78 | 78 | #[arg(long, default_value = ".")] |
|
| … | 3 unchanged lines |
||
| 82 | 82 | /// Bearer token allowed to push (repeatable); none means open |
|
| 83 | 83 | #[arg(long = "token")] |
|
| 84 | 84 | tokens: Vec<String>, |
|
| 85 | + | /// PEM certificate chain; enables HTTPS together with --tls-key |
|
| 86 | + | #[arg(long, requires = "tls_key")] |
|
| 87 | + | tls_cert: Option<PathBuf>, |
|
| 88 | + | /// PEM private key |
|
| 89 | + | #[arg(long, requires = "tls_cert")] |
|
| 90 | + | tls_key: Option<PathBuf>, |
|
| 85 | 91 | }, |
|
| 86 | 92 | /// Manage the signing identity |
|
| 87 | 93 | Identity { |
|
| … | 2 unchanged lines |
||
| 90 | 96 | }, |
|
| 91 | 97 | /// Verify the signatures on a change (default: every change in the log) |
|
| 92 | 98 | Verify { change: Option<String> }, |
|
| 93 | - | /// Set a configuration key (author.*, core.sign, workspace.bookmark, remote.<name>.url|token) |
|
| 99 | + | /// Set a configuration key (author.*, core.sign, workspace.bookmark, remote.<name>.url|token|ca) |
|
| 94 | 100 | Config { |
|
| 95 | 101 | key: String, |
|
| 96 | 102 | value: String, |
|
| … | 59 unchanged lines |
||
| 156 | 162 | Cmd::Fetch { remote } => sync::fetch(remote.as_deref()), |
|
| 157 | 163 | Cmd::Pull { remote } => sync::pull(remote.as_deref()), |
|
| 158 | 164 | Cmd::Push { remote, force } => sync::push(remote.as_deref(), force), |
|
| 159 | - | Cmd::Serve { root, bind, tokens } => sync::serve(root, &bind, tokens), |
|
| 165 | + | Cmd::Serve { root, bind, tokens, tls_cert, tls_key } => sync::serve(root, &bind, tokens, tls_cert.zip(tls_key)), |
|
| 160 | 166 | Cmd::Identity { cmd } => match cmd { |
|
| 161 | 167 | IdentityCmd::Create { name, email, local } => identity::create(name, email, local), |
|
| 162 | 168 | IdentityCmd::Show => identity::show(), |
|
| … | 5 unchanged lines |
modified src/cli/sync.rs +8 −3
| … | 96 unchanged lines |
||
| 97 | 97 | Ok(code) |
|
| 98 | 98 | } |
|
| 99 | 99 | ||
| 100 | - | pub fn serve(root: PathBuf, bind: &str, tokens: Vec<String>) -> Result<i32> { |
|
| 100 | + | pub fn serve(root: PathBuf, bind: &str, tokens: Vec<String>, tls: Option<(PathBuf, PathBuf)>) -> Result<i32> { |
|
| 101 | 101 | let listener = std::net::TcpListener::bind(bind).with_context(|| format!("binding {bind}"))?; |
|
| 102 | 102 | let root = root.canonicalize()?; |
|
| 103 | + | let tls = tls.map(|(cert, key)| oxy_server::TlsFiles { cert, key }); |
|
| 103 | 104 | println!( |
|
| 104 | - | "Serving repositories under {} on http://{bind}/<repo>/oxy/v1/ ({})", |
|
| 105 | + | "Serving repositories under {} on {}://{bind}/<repo>/oxy/v1/ ({})", |
|
| 105 | 106 | root.display(), |
|
| 107 | + | if tls.is_some() { "https" } else { "http" }, |
|
| 106 | 108 | if tokens.is_empty() { "open: no push tokens configured" } else { "push requires a token" } |
|
| 107 | 109 | ); |
|
| 108 | - | oxy_server::run_blocking(listener, oxy_server::ServerConfig { root, tokens })?; |
|
| 110 | + | if tls.is_none() && !bind.starts_with("127.") && !bind.starts_with("localhost") && !bind.starts_with("[::1]") { |
|
| 111 | + | eprintln!("warning: serving plain HTTP on a non-loopback address; put a TLS terminator in front or pass --tls-cert/--tls-key"); |
|
| 112 | + | } |
|
| 113 | + | oxy_server::run_blocking(listener, oxy_server::ServerConfig { root, tokens, tls })?; |
|
| 109 | 114 | Ok(0) |
|
| 110 | 115 | } |