From 59b2ba07dd824b19d25791174079f8774b67d8cd Mon Sep 17 00:00:00 2001 From: ealui-statsig Date: Thu, 17 Oct 2024 17:35:53 -0700 Subject: [PATCH] Sync to 1.1.8 --- Cargo.lock | 389 ++++++++---------- Cargo.toml | 7 +- Dockerfile | 2 +- README.md | 1 + Rocket.toml | 6 +- src/client.rs | 26 +- src/datastore/caching/disabled_cache.rs | 10 +- src/datastore/caching/in_memory_cache.rs | 10 +- src/datastore/caching/redis_cache.rs | 104 ++++- src/datastore/config_spec_store.rs | 22 +- .../background_data_provider.rs | 93 ++++- .../data_providers/http_data_provider.rs | 57 ++- src/datastore/data_providers/mod.rs | 4 +- .../data_providers/request_builder.rs | 15 +- src/datastore/get_id_list_store.rs | 10 +- src/datastore/log_event_store.rs | 89 ++-- src/datastore/sdk_key_store.rs | 37 +- src/datatypes/log_event.rs | 1 + src/loggers/stats_logger.rs | 139 ++++--- src/observers/http_data_provider_observer.rs | 4 +- src/observers/mod.rs | 42 +- src/server.rs | 88 +++- src/servers/authorized_request_context.rs | 52 ++- src/servers/grpc_server.rs | 148 ++++++- src/servers/http_server.rs | 123 +++++- src/servers/streaming_channel.rs | 16 +- x509_test_certs/gen_certs.sh | 114 +++++ x509_test_certs/intermediate/certs/client.crt | 29 ++ .../intermediate/certs/client_full_chain.crt | 96 +++++ .../intermediate/certs/intermediate_ca.crt | 35 ++ .../intermediate/certs/intermediate_ca.srl | 1 + x509_test_certs/intermediate/certs/server.crt | 30 ++ .../intermediate/certs/server_chain.crt | 96 +++++ .../intermediate/certs/server_full_chain.crt | 97 +++++ x509_test_certs/intermediate/client.cnf | 19 + x509_test_certs/intermediate/csr/client.csr | 17 + .../intermediate/csr/intermediate_ca.csr | 28 ++ x509_test_certs/intermediate/csr/server.csr | 17 + .../intermediate/intermediate_ca.cnf | 20 + .../intermediate/private/client.key | 28 ++ .../intermediate/private/intermediate_ca.key | 52 +++ .../intermediate/private/server.key | 28 ++ x509_test_certs/intermediate/server.cnf | 21 + x509_test_certs/root/certs/root_ca.crt | 32 ++ x509_test_certs/root/certs/root_ca.srl | 1 + x509_test_certs/root/private/root_ca.key | 52 +++ 46 files changed, 1799 insertions(+), 509 deletions(-) create mode 100755 x509_test_certs/gen_certs.sh create mode 100644 x509_test_certs/intermediate/certs/client.crt create mode 100644 x509_test_certs/intermediate/certs/client_full_chain.crt create mode 100644 x509_test_certs/intermediate/certs/intermediate_ca.crt create mode 100644 x509_test_certs/intermediate/certs/intermediate_ca.srl create mode 100644 x509_test_certs/intermediate/certs/server.crt create mode 100644 x509_test_certs/intermediate/certs/server_chain.crt create mode 100644 x509_test_certs/intermediate/certs/server_full_chain.crt create mode 100644 x509_test_certs/intermediate/client.cnf create mode 100644 x509_test_certs/intermediate/csr/client.csr create mode 100644 x509_test_certs/intermediate/csr/intermediate_ca.csr create mode 100644 x509_test_certs/intermediate/csr/server.csr create mode 100644 x509_test_certs/intermediate/intermediate_ca.cnf create mode 100644 x509_test_certs/intermediate/private/client.key create mode 100644 x509_test_certs/intermediate/private/intermediate_ca.key create mode 100644 x509_test_certs/intermediate/private/server.key create mode 100644 x509_test_certs/intermediate/server.cnf create mode 100644 x509_test_certs/root/certs/root_ca.crt create mode 100644 x509_test_certs/root/certs/root_ca.srl create mode 100644 x509_test_certs/root/private/root_ca.key diff --git a/Cargo.lock b/Cargo.lock index b1764cf..a6f340f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -122,9 +122,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -176,15 +176,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -202,16 +202,16 @@ dependencies = [ "rustversion", "serde", "sync_wrapper 1.0.1", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -222,7 +222,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", ] @@ -366,9 +366,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.1.21" +version = "1.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" dependencies = [ "shlex", ] @@ -396,9 +396,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", "clap_derive", @@ -406,9 +406,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ "anstream", "anstyle", @@ -418,9 +418,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -780,9 +780,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", @@ -954,9 +954,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -998,7 +998,7 @@ dependencies = [ "google-cloud-token", "home", "jsonwebtoken", - "reqwest 0.12.7", + "reqwest", "serde", "serde_json", "thiserror", @@ -1014,7 +1014,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f945a208886a13d07636f38fb978da371d0abc3e34bad338124b9f8c135a8f" dependencies = [ - "reqwest 0.12.7", + "reqwest", "thiserror", "tokio", ] @@ -1060,7 +1060,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -1079,7 +1079,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -1102,6 +1102,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heck" version = "0.5.0" @@ -1193,9 +1199,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1291,7 +1297,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "hyper-util", - "rustls 0.23.13", + "rustls 0.23.14", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1311,19 +1317,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.30", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -1342,9 +1335,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", @@ -1355,7 +1348,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -1423,12 +1415,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "serde", ] @@ -1440,9 +1432,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-terminal" @@ -1517,9 +1509,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libflate" @@ -1772,18 +1764,21 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "openssl" @@ -1910,7 +1905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.6.0", ] [[package]] @@ -1947,9 +1942,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "powerfmt" @@ -2032,12 +2033,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", - "prost-derive 0.13.2", + "prost-derive 0.13.3", ] [[package]] @@ -2076,9 +2077,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", "itertools 0.13.0", @@ -2104,9 +2105,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "protobuf" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bcc343da15609eaecd65f8aa76df8dc4209d325131d8219358c0aaaebab0bf6" +checksum = "3018844a02746180074f621e847703737d27d89d7f0721a7a4da317f88b16385" dependencies = [ "once_cell", "protobuf-support", @@ -2134,9 +2135,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0766e3675a627c327e4b3964582594b0e8741305d628a98a5de75a1d15f99b9" +checksum = "faf96d872914fcda2b66d66ea3fff2be7c66865d31c7bb2790cff32c0e714880" dependencies = [ "thiserror", ] @@ -2195,7 +2196,7 @@ dependencies = [ "pin-project-lite", "rustls 0.22.4", "rustls-native-certs 0.7.3", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "ryu", "sha1_smol", @@ -2208,9 +2209,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -2237,14 +2238,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -2258,13 +2259,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -2275,55 +2276,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-tls 0.5.0", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding 2.3.1", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", - "tokio", - "tokio-native-tls", - "tower-service", - "url 2.5.2", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "reqwest" -version = "0.12.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ "base64 0.22.1", "bytes", @@ -2336,7 +2297,7 @@ dependencies = [ "http-body-util", "hyper 1.4.1", "hyper-rustls 0.27.3", - "hyper-tls 0.6.0", + "hyper-tls", "hyper-util", "ipnet", "js-sys", @@ -2346,12 +2307,12 @@ dependencies = [ "once_cell", "percent-encoding 2.3.1", "pin-project-lite", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.1", - "system-configuration 0.6.1", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -2421,7 +2382,7 @@ dependencies = [ "either", "figment", "futures", - "indexmap 2.5.0", + "indexmap 2.6.0", "log", "memchr", "multer", @@ -2453,7 +2414,7 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" dependencies = [ "devise", "glob", - "indexmap 2.5.0", + "indexmap 2.6.0", "proc-macro2", "quote", "rocket_http", @@ -2473,7 +2434,7 @@ dependencies = [ "futures", "http 0.2.12", "hyper 0.14.30", - "indexmap 2.5.0", + "indexmap 2.6.0", "log", "memchr", "pear", @@ -2545,11 +2506,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ + "log", "once_cell", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2575,7 +2538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -2592,19 +2555,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" @@ -2681,9 +2643,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -2729,9 +2691,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2750,15 +2712,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +checksum = "9720086b3357bcb44fce40117d769a4d068c70ecfa190850a980a71755f66fcc" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_derive", "serde_json", @@ -2768,9 +2730,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +checksum = "5f1abbfe725f27678f4663bcacb75a83e829fd464c25d78dd038a3a29e307cec" dependencies = [ "darling", "proc-macro2", @@ -2784,7 +2746,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "ryu", "serde", @@ -2907,17 +2869,17 @@ dependencies = [ [[package]] name = "statsig" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf9ac9aaffb4fc18b2f52583b1462d555e6f0073659d5a65370d21f1f45626e" +checksum = "92e3768b95b63839ec2e83def25c58cea117363d27720540e13aeffe187d5c29" dependencies = [ "async-trait", "base64 0.21.7", "chrono", - "http 0.2.12", + "http 1.1.0", "lazy_static", "regex", - "reqwest 0.11.27", + "reqwest", "serde", "serde_json", "sha2", @@ -2933,6 +2895,7 @@ dependencies = [ "async-trait", "bb8", "bb8-redis", + "bytes", "cached", "chrono", "clap", @@ -2940,6 +2903,7 @@ dependencies = [ "dashmap", "dogstatsd", "envy", + "flate2", "futures", "fxhash", "lazy_static", @@ -2948,10 +2912,10 @@ dependencies = [ "memchr", "once_cell", "parking_lot", - "prost 0.13.2", - "protobuf 3.5.1", + "prost 0.13.3", + "protobuf 3.6.0", "redis", - "reqwest 0.12.7", + "reqwest", "rocket", "serde", "serde_json", @@ -2961,6 +2925,7 @@ dependencies = [ "statsig", "tokio", "tokio-stream", + "tokio-util", "tonic", "tonic-build", "uuid", @@ -2980,9 +2945,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "symbolic-common" -version = "12.11.1" +version = "12.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fdf97c441f18a4f92425b896a4ec7a27e03631a0b1047ec4e34e9916a9a167e" +checksum = "366f1b4c6baf6cfefc234bbd4899535fca0b06c74443039a73f6dfb2fad88d77" dependencies = [ "debugid", "memmap2", @@ -2992,9 +2957,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.11.1" +version = "12.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc8ece6b129e97e53d1fbb3f61d33a6a9e5369b11d01228c068094d6d134eaea" +checksum = "aba05ba5b9962ea5617baf556293720a8b2d0a282aa14ee4bf10e22efc7da8c8" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -3003,9 +2968,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -3027,17 +2992,6 @@ dependencies = [ "futures-core", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys 0.5.0", -] - [[package]] name = "system-configuration" version = "0.6.1" @@ -3046,17 +3000,7 @@ checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.6.0", "core-foundation", - "system-configuration-sys 0.6.0", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", + "system-configuration-sys", ] [[package]] @@ -3071,9 +3015,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -3084,18 +3028,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -3227,7 +3171,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.13", + "rustls 0.23.14", "rustls-pki-types", "tokio", ] @@ -3279,11 +3223,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -3292,9 +3236,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", @@ -3310,11 +3254,13 @@ dependencies = [ "hyper-util", "percent-encoding 2.3.1", "pin-project", - "prost 0.13.2", + "prost 0.13.3", + "rustls-pemfile 2.2.0", "socket2", "tokio", + "tokio-rustls 0.26.0", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -3353,6 +3299,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -3473,9 +3433,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -3769,15 +3729,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -3919,23 +3870,13 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 5c44c2b..35a25a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,12 @@ rust-version = "1.75.0" [dependencies] clap = { version = "4.5.17", features = ["derive"] } -tonic = "0.12.2" +tonic = { version = "0.12.3", features = ["tls"] } protobuf = "3.5.1" prost = "0.13.2" rocket = { version = "0.5.1", features = ["json"] } tokio = { version = "1.40.0", features = ["full"] } -reqwest = { version = "0.12.7" } +reqwest = { version = "0.12.8" } futures = "0.3.30" envy = "0.4.2" serde = { version = "1.0.210", features = ["derive"] } @@ -40,6 +40,9 @@ dashmap = "6.1.0" memchr = "2.7.4" smallvec = "1.13.2" fxhash = "0.2.1" +tokio-util = "0.7.12" +bytes = "1.7.2" +flate2 = "1.0.34" [build-dependencies] diff --git a/Dockerfile b/Dockerfile index e3ae376..ea2b809 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,5 +33,5 @@ WORKDIR /app COPY ./.cargo ./.cargo COPY ./Rocket.toml ./Rocket.toml COPY --from=builder /statsig_forward_proxy/target/release/server /usr/local/bin/statsig_forward_proxy - +ENV ROCKET_ENV=prod ENTRYPOINT [ "statsig_forward_proxy" ] diff --git a/README.md b/README.md index a4701be..97612ae 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,4 @@ curl -X GET 'http://0.0.0.0:8000/v1/download_config_specs/.json Besides using the flag force_gcp_profiling_enabled, if you provide your statsig SDK key as an environment variable (STATSIG_SERVER_SDK_KEY), you can also configure a feature gate called enable_gcp_profiler_for_sfp to dynamically enable and disable gcp profiling. + diff --git a/Rocket.toml b/Rocket.toml index 3d1b287..2b7f0b3 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -1,3 +1,7 @@ [global] address = "0.0.0.0" -limits = { json = "5 MiB", string = "5 MiB" } \ No newline at end of file +limits = { json = "5 MiB", string = "5 MiB" } +log_level = "critical" +workers = 128 +keep_alive=15 +ident="statsig-forward-proxy" \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index 2c6542a..346a9bd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1,11 @@ +use std::fs; + use chrono::Local; use statsig_forward_proxy::statsig_forward_proxy_client::StatsigForwardProxyClient; use statsig_forward_proxy::ConfigSpecRequest; +use tonic::transport::{Certificate, Channel, ClientTlsConfig}; + pub mod statsig_forward_proxy { tonic::include_proto!("statsig_forward_proxy"); } @@ -15,16 +19,26 @@ fn last_500_char(spec: &String) -> String { #[tokio::main] async fn main() -> Result<(), Box> { - let mut client = StatsigForwardProxyClient::connect("http://0.0.0.0:50051") - .await? - // 16mb -- default is 4mb - .max_decoding_message_size(16777216); + let pem = std::fs::read_to_string("./x509_test_certs/root/certs/root_ca.crt")?; + let cert = fs::read_to_string("./x509_test_certs/intermediate/certs/client.crt")?; + let key = fs::read_to_string("./x509_test_certs/intermediate/private/client.key")?; + let client_tls_id = tonic::transport::Identity::from_pem(cert.as_bytes(), key.as_bytes()); + let ca = Certificate::from_pem(pem); + let tls = ClientTlsConfig::new() + .ca_certificate(ca) + .identity(client_tls_id); + let channel = Channel::from_static("http://0.0.0.0:50051") + .tls_config(tls)? + .connect() + .await?; + + let mut client = StatsigForwardProxyClient::new(channel).max_decoding_message_size(20870203); // Non-Streaming for version in 0..3 { let request = tonic::Request::new(ConfigSpecRequest { since_time: Some(1234), - sdk_key: "1234".into(), + sdk_key: "secret-1234".into(), version: Some(version), }); let response: tonic::Response = @@ -42,7 +56,7 @@ async fn main() -> Result<(), Box> { // Streaming let request = tonic::Request::new(ConfigSpecRequest { since_time: Some(1234), - sdk_key: "1234".into(), + sdk_key: "secret-1234".into(), version: Some(2), }); let response = client.stream_config_spec(request).await?; diff --git a/src/datastore/caching/disabled_cache.rs b/src/datastore/caching/disabled_cache.rs index 3be45d6..d125b59 100644 --- a/src/datastore/caching/disabled_cache.rs +++ b/src/datastore/caching/disabled_cache.rs @@ -1,5 +1,6 @@ use crate::{ - datastore::data_providers::DataProviderRequestResult, observers::HttpDataProviderObserverTrait, + datastore::data_providers::{http_data_provider::ResponsePayload, DataProviderRequestResult}, + observers::HttpDataProviderObserverTrait, servers::authorized_request_context::AuthorizedRequestContext, }; @@ -20,12 +21,15 @@ impl HttpDataProviderObserverTrait for DisabledCache { _result: &DataProviderRequestResult, _request_context: &Arc, _lcut: u64, - _data: &Arc, + _data: &Arc, ) { /* noop */ } - async fn get(&self, _request_context: &Arc) -> Option> { + async fn get( + &self, + _request_context: &Arc, + ) -> Option> { return None; } } diff --git a/src/datastore/caching/in_memory_cache.rs b/src/datastore/caching/in_memory_cache.rs index 20e5557..bd3bd09 100644 --- a/src/datastore/caching/in_memory_cache.rs +++ b/src/datastore/caching/in_memory_cache.rs @@ -1,6 +1,7 @@ use crate::{ datastore::{ - config_spec_store::ConfigSpecForCompany, data_providers::DataProviderRequestResult, + config_spec_store::ConfigSpecForCompany, + data_providers::{http_data_provider::ResponsePayload, DataProviderRequestResult}, }, observers::{ proxy_event_observer::ProxyEventObserver, EventStat, HttpDataProviderObserverTrait, @@ -39,7 +40,7 @@ impl HttpDataProviderObserverTrait for InMemoryCache { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - data: &Arc, + data: &Arc, ) { let storage_key = request_context.to_string(); if result == &DataProviderRequestResult::DataAvailable { @@ -95,7 +96,10 @@ impl HttpDataProviderObserverTrait for InMemoryCache { } } - async fn get(&self, request_context: &Arc) -> Option> { + async fn get( + &self, + request_context: &Arc, + ) -> Option> { ProxyEventObserver::publish_event( ProxyEvent::new_with_rc(ProxyEventType::InMemoryCacheReadSucceed, request_context) .with_stat(EventStat { diff --git a/src/datastore/caching/redis_cache.rs b/src/datastore/caching/redis_cache.rs index 2864746..039f249 100644 --- a/src/datastore/caching/redis_cache.rs +++ b/src/datastore/caching/redis_cache.rs @@ -1,11 +1,15 @@ -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::HashMap, + io::{Cursor, Read, Write}, + sync::Arc, +}; use bb8_redis::{redis::AsyncCommands, RedisConnectionManager}; use parking_lot::RwLock; use redis::aio::MultiplexedConnection; use crate::{ - datastore::data_providers::DataProviderRequestResult, + datastore::data_providers::{http_data_provider::ResponsePayload, DataProviderRequestResult}, observers::{ proxy_event_observer::ProxyEventObserver, HttpDataProviderObserverTrait, ProxyEvent, ProxyEventType, @@ -17,6 +21,8 @@ use crate::observers::EventStat; use crate::observers::OperationType; use bb8_redis::redis::RedisError; +use bytes::Bytes; +use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use serde::Deserialize; use sha2::{Digest, Sha256}; @@ -122,20 +128,21 @@ impl HttpDataProviderObserverTrait for RedisCache { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - data: &Arc, + data: &Arc, ) { + // TODO: This will be a problem if we start using DCS v2 with the forward + // proxy because the redis data adapter currently has no way + // to differentiate between the DCS v1 and DCS v2. + // + // So for now, to keep functionality, continue using just + // the sdk key. + let redis_key = self.hash_key(&request_context.sdk_key).await; + if result == &DataProviderRequestResult::DataAvailable { let connection: Result< bb8::PooledConnection, bb8::RunError, > = self.connection.get().await; - // TODO: This will be a problem if we start using DCS v2 with the forward - // proxy because the redis data adapter currently has no way - // to differentiate between the DCS v1 and DCS v2. - // - // So for now, to keep functionality, continue using just - // the sdk key. - let redis_key = self.hash_key(&request_context.sdk_key).await; match connection { Ok(mut conn) => { let mut pipe = redis::pipe(); @@ -178,9 +185,38 @@ impl HttpDataProviderObserverTrait for RedisCache { }; if should_update { + let data_to_write = match request_context.use_gzip + && data.encoding.as_deref() == Some("gzip") + { + true => { + let mut decoder = GzDecoder::new(Cursor::new(&**data.data)); + let mut decompressed = Vec::new(); + match decoder.read_to_end(&mut decompressed) { + Ok(_) => decompressed, + Err(e) => { + ProxyEventObserver::publish_event( + ProxyEvent::new_with_rc( + ProxyEventType::RedisCacheWriteFailed, + request_context, + ) + .with_stat(EventStat { + operation_type: OperationType::IncrByValue, + value: 1, + }), + ); + eprintln!("Failed to decode gzipped data before writing to redis: {:?}", e); + return; + } + } + } + false => data.data.to_vec(), + }; + + // We currently only support writing data to redis as plain_text match pipe + .hset(&redis_key, "encoding", "plain_text") .hset(&redis_key, "lcut", lcut) - .hset(&redis_key, "config", data.to_string()) + .hset(&redis_key, "config", data_to_write) .expire(&redis_key, self.redis_cache_ttl_in_s) .expire(REDIS_LEADER_KEY, self.leader_key_ttl) .query_async::(&mut *conn) @@ -246,7 +282,6 @@ impl HttpDataProviderObserverTrait for RedisCache { && self.clear_external_datastore_on_unauthorized { let connection = self.connection.get().await; - let redis_key = self.hash_key(&request_context.to_string()).await; match connection { Ok(mut conn) => match conn.del(&redis_key).await { Ok(()) => { @@ -295,16 +330,24 @@ impl HttpDataProviderObserverTrait for RedisCache { } } - async fn get(&self, request_context: &Arc) -> Option> { + async fn get( + &self, + request_context: &Arc, + ) -> Option> { let connection = self.connection.get().await; + let redis_key = self.hash_key(&request_context.sdk_key).await; match connection { Ok(mut conn) => { - let res: Result, RedisError> = conn - .hget(self.hash_key(&request_context.to_string()).await, "config") + let mut pipe = redis::pipe(); + let res = pipe + .hget(redis_key.clone(), "encoding") + .hget(redis_key, "config") + .query_async::, Vec)>(&mut *conn) .await; + match res { - Ok(data) => { - if data.is_empty() { + Ok(payload) => { + if payload.1.is_empty() { ProxyEventObserver::publish_event( ProxyEvent::new_with_rc( ProxyEventType::RedisCacheReadMiss, @@ -327,7 +370,32 @@ impl HttpDataProviderObserverTrait for RedisCache { value: 1, }), ); - Some(Arc::from(data.first().expect("Must have data").to_owned())) + + match request_context.use_gzip + && payload.0.is_some_and(|encoding| encoding == "plain_text") + { + true => { + let mut compressed = Vec::new(); + let mut encoder = + GzEncoder::new(&mut compressed, Compression::best()); + if let Err(e) = encoder.write_all(&payload.1) { + eprintln!("Failed to gzip data from redis: {:?}", e); + return None; + } + if let Err(e) = encoder.finish() { + eprintln!("Failed to gzip data from redis: {:?}", e); + return None; + } + Some(Arc::new(ResponsePayload { + encoding: Arc::new(Some("gzip".to_string())), + data: Arc::new(Bytes::from(compressed)), + })) + } + false => Some(Arc::new(ResponsePayload { + encoding: Arc::new(None), + data: Arc::new(Bytes::from(payload.1)), + })), + } } } Err(e) => { diff --git a/src/datastore/config_spec_store.rs b/src/datastore/config_spec_store.rs index 66b0f0f..be5285a 100644 --- a/src/datastore/config_spec_store.rs +++ b/src/datastore/config_spec_store.rs @@ -1,4 +1,5 @@ use super::data_providers::background_data_provider::{foreground_fetch, BackgroundDataProvider}; +use super::data_providers::http_data_provider::ResponsePayload; use super::data_providers::DataProviderRequestResult; use super::sdk_key_store::SdkKeyStore; @@ -7,15 +8,18 @@ use crate::observers::{ EventStat, HttpDataProviderObserverTrait, OperationType, ProxyEvent, ProxyEventType, }; use crate::servers::authorized_request_context::AuthorizedRequestContext; -use dashmap::DashMap; +use bytes::Bytes; use chrono::Utc; + use std::sync::Arc; +use dashmap::DashMap; + #[derive(Clone, Debug)] pub struct ConfigSpecForCompany { pub lcut: u64, - pub config: Arc, + pub config: Arc, } pub struct ConfigSpecStore { @@ -37,7 +41,7 @@ impl HttpDataProviderObserverTrait for ConfigSpecStore { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - data: &Arc, + data: &Arc, ) { let should_insert = result == &DataProviderRequestResult::Error || (result == &DataProviderRequestResult::DataAvailable @@ -81,14 +85,17 @@ impl HttpDataProviderObserverTrait for ConfigSpecStore { }), ); }) - .or_insert_with(|| new_data); + .or_insert(new_data); } } else if result == &DataProviderRequestResult::Unauthorized { self.store.remove(request_context); } } - async fn get(&self, request_context: &Arc) -> Option> { + async fn get( + &self, + request_context: &Arc, + ) -> Option> { self.store .get(request_context) .map(|record| record.config.clone()) @@ -106,7 +113,10 @@ impl ConfigSpecStore { background_data_provider, no_update_config_spec: Arc::new(ConfigSpecForCompany { lcut: 0, - config: Arc::from("{\"has_updates\":false}".to_string()), + config: Arc::new(ResponsePayload { + encoding: Arc::new(None), + data: Arc::new(Bytes::from("{\"has_updates\":false}".to_string())), + }), }), } } diff --git a/src/datastore/data_providers/background_data_provider.rs b/src/datastore/data_providers/background_data_provider.rs index bed6b0f..2870b03 100644 --- a/src/datastore/data_providers/background_data_provider.rs +++ b/src/datastore/data_providers/background_data_provider.rs @@ -1,13 +1,17 @@ use crate::datastore::sdk_key_store::SdkKeyStore; use crate::servers::authorized_request_context::AuthorizedRequestContext; +use crate::GRACEFUL_SHUTDOWN_TOKEN; +use super::http_data_provider::ResponsePayload; use super::request_builder::{CachedRequestBuilders, RequestBuilderTrait}; use super::{http_data_provider::HttpDataProvider, DataProviderRequestResult, DataProviderTrait}; use std::collections::hash_map::Entry; use std::{collections::HashMap, sync::Arc}; +use bytes::Bytes; use tokio::runtime::Handle; use tokio::sync::RwLock; +use tokio::time::timeout; use tokio::time::{Duration, Instant}; pub struct BackgroundDataProvider { @@ -63,7 +67,6 @@ fn should_perform_fetch(per_key_lock: &Option) -> bool { match *per_key_lock { Some(init_time) => { let duration = Instant::now().duration_since(init_time); - println!("Duration since last fetch: {:?}", duration); duration >= Duration::from_secs(30) } None => true, @@ -91,6 +94,7 @@ impl BackgroundDataProvider { let batch_size = self.update_batch_size; let polling_interval_in_s = self.polling_interval_in_s; let sdk_key_store = Arc::clone(&self.sdk_key_store); + let graceful_shutdown_token = GRACEFUL_SHUTDOWN_TOKEN.clone(); rocket::tokio::task::spawn_blocking(move || { Handle::current().block_on(async move { loop { @@ -100,7 +104,15 @@ impl BackgroundDataProvider { batch_size, ) .await; - rocket::tokio::time::sleep(Duration::from_secs(polling_interval_in_s)).await; + + if tokio::select! { + _ = tokio::time::sleep(Duration::from_secs(polling_interval_in_s)) => { false }, + _ = graceful_shutdown_token.cancelled() => { + true + }, + } { + break; + } } }); }); @@ -111,25 +123,64 @@ impl BackgroundDataProvider { data_provider: &Arc, update_batch_size: u64, ) { - let mut join_handles = Vec::with_capacity(update_batch_size as usize); + match reqwest::Client::builder() + .timeout(Duration::from_secs(30)) + .read_timeout(Duration::from_secs(10)) + .connect_timeout(Duration::from_secs(10)) + .pool_max_idle_per_host(0) + .build() + { + Ok(http_client) => { + let mut join_handles = Vec::with_capacity(update_batch_size as usize); - for (request_context, lcut) in store_iter { - let request_builder = CachedRequestBuilders::get_request_builder(&request_context.path); - if !request_builder.should_make_request(&request_context).await { - continue; - } + for (request_context, lcut) in store_iter { + let request_builder = + CachedRequestBuilders::get_request_builder(&request_context.path); + if !request_builder.should_make_request(&request_context).await { + continue; + } - let data_provider = data_provider.clone(); - let join_handle = tokio::task::spawn(async move { - Self::process_request(data_provider, request_builder, &request_context, lcut).await; - }); + let data_provider = data_provider.clone(); + let client_clone = http_client.clone(); + let join_handle = tokio::task::spawn(async move { + match timeout( + Duration::from_secs(60), + Self::process_request( + data_provider, + request_builder, + &request_context, + lcut, + &client_clone, + ), + ) + .await + { + Ok(_) => {} + Err(_) => { + let mut key = request_context.sdk_key.clone(); + key.truncate(20); + eprintln!( + "Error: process_request timed out after 60 seconds for request_context.. skipping update..({}): {}", + key, + request_context.path) + } + } + }); - join_handles.push(join_handle); - if join_handles.len() >= (update_batch_size as usize) { - futures::future::join_all(join_handles.drain(..)).await; + join_handles.push(join_handle); + if join_handles.len() >= (update_batch_size as usize) { + futures::future::join_all(join_handles.drain(..)).await; + } + } + futures::future::join_all(join_handles).await; + } + Err(e) => { + eprintln!( + "Failed to build http client.. skipping background update...: {}", + e + ); } } - futures::future::join_all(join_handles).await; } async fn process_request( @@ -137,9 +188,10 @@ impl BackgroundDataProvider { request_builder: Arc, request_context: &Arc, lcut: u64, + http_client: &reqwest::Client, ) { let dp_result = data_provider - .get(&request_builder, request_context, lcut) + .get(http_client, &request_builder, request_context, lcut) .await; match dp_result.result { @@ -179,7 +231,10 @@ impl BackgroundDataProvider { &dp_result.result, request_context, lcut, - &Arc::from(String::new()), + &(Arc::new(ResponsePayload { + encoding: Arc::new(None), + data: Arc::new(Bytes::new()), + })), ) .await; } @@ -192,7 +247,7 @@ impl BackgroundDataProvider { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - data: &Arc, + data: &Arc, ) { request_builder .get_observers() diff --git a/src/datastore/data_providers/http_data_provider.rs b/src/datastore/data_providers/http_data_provider.rs index e0a7a16..15ded65 100644 --- a/src/datastore/data_providers/http_data_provider.rs +++ b/src/datastore/data_providers/http_data_provider.rs @@ -6,21 +6,20 @@ use crate::observers::EventStat; use crate::observers::OperationType; use crate::observers::{proxy_event_observer::ProxyEventObserver, ProxyEvent, ProxyEventType}; use crate::servers::authorized_request_context::AuthorizedRequestContext; +use bytes::Bytes; use reqwest::header::HeaderMap; -pub trait DataProviderObserver { - fn update(&self, key: &str, data: &str); +#[derive(Debug)] +pub struct ResponsePayload { + pub encoding: Arc>, + pub data: Arc, } -pub struct HttpDataProvider { - http_client: reqwest::Client, +pub trait DataProviderObserver { + fn update(&self, key: &str, data: &str); } -impl HttpDataProvider { - pub fn new(http_client: reqwest::Client) -> Self { - HttpDataProvider { http_client } - } -} +pub struct HttpDataProvider {} use async_trait::async_trait; @@ -29,6 +28,7 @@ use tokio::time::Instant; impl DataProviderTrait for HttpDataProvider { async fn get( &self, + http_client: &reqwest::Client, request_builder: &Arc, request_context: &Arc, lcut: u64, @@ -36,7 +36,7 @@ impl DataProviderTrait for HttpDataProvider { let start_time = Instant::now(); let response = match request_builder - .make_request(&self.http_client, request_context, lcut) + .make_request(http_client, request_context, lcut) .await { Ok(response) => response, @@ -50,8 +50,8 @@ impl DataProviderTrait for HttpDataProvider { let status = response.status(); let headers = response.headers().clone(); - let body = match response.bytes().await { - Ok(bytes) => String::from_utf8_lossy(&bytes).into_owned(), + let (body, bytes) = match response.bytes().await { + Ok(bytes) => (String::from_utf8_lossy(&bytes).into_owned(), bytes), Err(err) => { return self .handle_error(err.to_string(), start_time, request_context, lcut) @@ -69,16 +69,26 @@ impl DataProviderTrait for HttpDataProvider { .is_an_update(&body, &request_context.sdk_key) .await { - return self - .handle_no_data(body, lcut, start_time, request_context) - .await; + return self.handle_no_data(lcut, start_time, request_context).await; } let since_time = self .parse_since_time(&headers, lcut, start_time, request_context) .await; - self.handle_success(body, since_time, start_time, request_context) - .await + let content_encoding = + headers + .get("content-encoding") + .and_then(|value| match value.to_str() { + Ok(encoding) => Some(encoding.to_string()), + Err(_e) => None, + }); + self.handle_success( + (content_encoding, bytes), + since_time, + start_time, + request_context, + ) + .await } } @@ -115,7 +125,6 @@ impl HttpDataProvider { async fn handle_no_data( &self, - body: String, lcut: u64, start_time: Instant, request_context: &Arc, @@ -134,7 +143,7 @@ impl HttpDataProvider { DataProviderResult { result: DataProviderRequestResult::NoDataAvailable, - data: Some((Arc::from(body), lcut)), + data: None, } } @@ -171,7 +180,7 @@ impl HttpDataProvider { async fn handle_success( &self, - body: String, + result: (Option, Bytes), since_time: u64, start_time: Instant, request_context: &Arc, @@ -190,7 +199,13 @@ impl HttpDataProvider { DataProviderResult { result: DataProviderRequestResult::DataAvailable, - data: Some((Arc::from(body), since_time)), + data: Some(( + Arc::new(ResponsePayload { + encoding: Arc::from(result.0), + data: Arc::from(result.1), + }), + since_time, + )), } } } diff --git a/src/datastore/data_providers/mod.rs b/src/datastore/data_providers/mod.rs index d7b3b84..84e7985 100644 --- a/src/datastore/data_providers/mod.rs +++ b/src/datastore/data_providers/mod.rs @@ -4,6 +4,7 @@ pub mod request_builder; use std::sync::Arc; use async_trait::async_trait; +use http_data_provider::ResponsePayload; use crate::servers::authorized_request_context::AuthorizedRequestContext; @@ -12,6 +13,7 @@ use self::request_builder::RequestBuilderTrait; pub trait DataProviderTrait { async fn get( &self, + http_client: &reqwest::Client, request_builder: &Arc, request_context: &Arc, lcut: u64, @@ -29,5 +31,5 @@ pub enum DataProviderRequestResult { #[derive(Debug)] pub struct DataProviderResult { result: DataProviderRequestResult, - data: Option<(Arc, u64)>, + data: Option<(Arc, u64)>, } diff --git a/src/datastore/data_providers/request_builder.rs b/src/datastore/data_providers/request_builder.rs index 3191ca3..0508313 100644 --- a/src/datastore/data_providers/request_builder.rs +++ b/src/datastore/data_providers/request_builder.rs @@ -118,7 +118,20 @@ impl RequestBuilderTrait for DcsRequestBuilder { ), }; - http_client.get(url).send().await + if request_context.use_gzip { + http_client + .get(url) + .header(reqwest::header::ACCEPT_ENCODING, "gzip") + .timeout(Duration::from_secs(30)) + .send() + .await + } else { + http_client + .get(url) + .timeout(Duration::from_secs(30)) + .send() + .await + } } async fn is_an_update(&self, body: &str, _sdk_key: &str) -> bool { diff --git a/src/datastore/get_id_list_store.rs b/src/datastore/get_id_list_store.rs index 527c4c9..ad1540e 100644 --- a/src/datastore/get_id_list_store.rs +++ b/src/datastore/get_id_list_store.rs @@ -1,4 +1,5 @@ use super::data_providers::background_data_provider::{foreground_fetch, BackgroundDataProvider}; +use super::data_providers::http_data_provider::ResponsePayload; use super::data_providers::DataProviderRequestResult; use super::sdk_key_store::SdkKeyStore; use crate::observers::HttpDataProviderObserverTrait; @@ -9,7 +10,7 @@ use std::sync::Arc; #[derive(Clone, Debug)] pub struct IdlistForCompany { - pub idlists: Arc, + pub idlists: Arc, } pub struct GetIdListStore { @@ -30,7 +31,7 @@ impl HttpDataProviderObserverTrait for GetIdListStore { result: &DataProviderRequestResult, request_context: &Arc, _lcut: u64, - data: &Arc, + data: &Arc, ) { if result == &DataProviderRequestResult::Error || result == &DataProviderRequestResult::DataAvailable @@ -46,7 +47,10 @@ impl HttpDataProviderObserverTrait for GetIdListStore { } } - async fn get(&self, request_context: &Arc) -> Option> { + async fn get( + &self, + request_context: &Arc, + ) -> Option> { self.store .get(request_context) .map(|record| record.idlists.clone()) diff --git a/src/datastore/log_event_store.rs b/src/datastore/log_event_store.rs index df51229..dd32253 100644 --- a/src/datastore/log_event_store.rs +++ b/src/datastore/log_event_store.rs @@ -5,25 +5,25 @@ use crate::observers::proxy_event_observer::ProxyEventObserver; use crate::observers::{EventStat, OperationType, ProxyEvent, ProxyEventType}; use crate::servers::authorized_request_context::AuthorizedRequestContext; use chrono::{DateTime, Timelike, Utc}; -use futures::future::join_all; +use dashmap::DashMap; + +use std::collections::hash_map::RandomState; use std::hash::BuildHasher; -use std::{ - collections::{hash_map::RandomState, HashSet}, - sync::Arc, -}; + +use std::sync::Arc; use reqwest::StatusCode; use rocket::{ http::Status, serde::json::{to_string, Value}, }; -use tokio::sync::RwLock; +use std::fmt::Write; pub struct LogEventStore { url: String, http_client: reqwest::Client, random_state: RandomState, - dedupe_cache: Arc>>, + dedupe_cache: Arc>, dedupe_cache_limit: usize, } @@ -33,7 +33,7 @@ impl LogEventStore { url: format!("{}/v1/log_event", base_url), http_client, random_state: RandomState::new(), - dedupe_cache: Arc::new(RwLock::new(HashSet::new())), + dedupe_cache: Arc::new(DashMap::new()), dedupe_cache_limit, } } @@ -44,15 +44,11 @@ impl LogEventStore { request_context: &Arc, ) -> Result { let batch_in_size = data.events.len(); - data.events = join_all(data.events.into_iter().map(|e| { - self.process_event(&request_context.sdk_key, data.statsig_metadata.clone(), e) - })) - .await - .into_iter() - .flatten() - .collect(); + data.events + .retain(|e| self.process_event(&request_context.sdk_key, &data.statsig_metadata, e)); let batch_out_size = data.events.len(); let data_string = to_string(&data).map_err(|_| Status::new(500))?; + // todo: ungzip + deduplicate + batching + regzip let response = self .http_client @@ -62,11 +58,12 @@ impl LogEventStore { .body(data_string) .send() .await; + let deduped_count = (batch_in_size - batch_out_size) as i64; ProxyEventObserver::publish_event( ProxyEvent::new_with_rc(ProxyEventType::LogEventStoreDeduped, request_context) .with_stat(EventStat { operation_type: OperationType::IncrByValue, - value: (batch_in_size - batch_out_size) as i64, + value: deduped_count, }), ); match response { @@ -82,10 +79,9 @@ impl LogEventStore { } // returns true if this is a new event - async fn check_key(&self, event: String) -> bool { + fn check_key(&self, event: &String) -> bool { let key = self.random_state.hash_one(event); - let mut lock = self.dedupe_cache.write().await; - if lock.len() >= self.dedupe_cache_limit { + if self.dedupe_cache.len() >= self.dedupe_cache_limit { ProxyEventObserver::publish_event( ProxyEvent::new(ProxyEventType::LogEventStoreDedupeCacheCleared).with_stat( EventStat { @@ -94,38 +90,38 @@ impl LogEventStore { }, ), ); - lock.clear(); + self.dedupe_cache.clear(); } - lock.insert(key) + self.dedupe_cache.insert(key, ()).is_none() } - async fn process_event( + fn process_event( &self, sdk_key: &str, - global_metadata: Option, - mut event: PossiblyLogEvent, - ) -> Option { + global_metadata: &Option, + event: &PossiblyLogEvent, + ) -> bool { match event { - PossiblyLogEvent::ValidLogEvent(ref mut e) => { - if e.statsig_metadata.is_none() { - e.statsig_metadata = global_metadata; - } - match Self::compute_key(sdk_key, e) { - Some(s) if self.check_key(s.clone()).await => Some(event), - None => Some(event), - _ => None, + PossiblyLogEvent::ValidLogEvent(e) => { + match Self::compute_key(sdk_key, e, global_metadata) { + Some(ref s) => self.check_key(s), + None => true, } } - PossiblyLogEvent::InvalidLogEvent(_) => Some(event), + PossiblyLogEvent::InvalidLogEvent(_) => false, } } - pub fn compute_key(sdk_key: &str, event: &LogEvent) -> Option { + pub fn compute_key( + sdk_key: &str, + event: &LogEvent, + global_metadata: &Option, + ) -> Option { if let EventName::Other { .. } = event.event_name { return None; } // TODO: add global statsig_metadata and user - let mut user_key = format!( + let mut exposure_key = format!( "k:{};u:{};s:{};", sdk_key, event @@ -137,28 +133,33 @@ impl LogEventStore { .statsig_metadata .as_ref() .and_then(|metadata| metadata.stable_id.as_ref()) + .or_else(|| global_metadata + .as_ref() + .and_then(|metadata| metadata.stable_id.as_ref())) .map_or("", |s| s.as_str()) ); if let Some(user) = &event.user { if let Some(Value::Object(map)) = &user.custom_ids { for (k, v) in map.iter() { - user_key.push_str(&format!("{}:{};", k, v)); + write!(&mut exposure_key, "{}:{};", k, v).expect("Writing should never fail"); } } } - let mut exposure_key = match event.event_name { + match event.event_name { EventName::ConfigExposure => { let config_name = event.get_metadata("config"); let rule_id = event.get_metadata("ruleID"); - format!("n:${config_name};u:{user_key}r:{rule_id}") + write!(&mut exposure_key, "n:${config_name};r:{rule_id}") + .expect("Writing should never fail") } EventName::GateExposure => { let gate_name = event.get_metadata("gate"); let rule_id = event.get_metadata("ruleID"); let value = event.get_metadata("gateValue"); - format!("n:{gate_name};u:{user_key}r:{rule_id};v:{value}") + write!(&mut exposure_key, "n:{gate_name};r:{rule_id};v:{value}") + .expect("Writing should never fail") } _ => return None, }; @@ -171,10 +172,14 @@ impl LogEventStore { let time_millis = round_to_minute(time) .and_then(|dt| dt.timestamp_millis().try_into().ok()) .or(event.time); + match time_millis { - Some(time) => exposure_key.push_str(&format!(";t{}", time)), - None => return None, // shouldn't ever happen, but just say unique if it does + Some(time) => { + write!(&mut exposure_key, ";t{}", time).expect("Writing should never fail") + } + None => {} } + Some(exposure_key) } } diff --git a/src/datastore/sdk_key_store.rs b/src/datastore/sdk_key_store.rs index cfaba2e..a698755 100644 --- a/src/datastore/sdk_key_store.rs +++ b/src/datastore/sdk_key_store.rs @@ -1,10 +1,12 @@ +use super::data_providers::http_data_provider::ResponsePayload; use super::data_providers::DataProviderRequestResult; use crate::observers::HttpDataProviderObserverTrait; use crate::servers::authorized_request_context::AuthorizedRequestContext; use async_trait::async_trait; -use dashmap::DashMap; - +use parking_lot::RwLock; +use std::collections::HashMap; use std::sync::Arc; + #[async_trait] impl HttpDataProviderObserverTrait for SdkKeyStore { fn force_notifier_to_wait_for_update(&self) -> bool { @@ -16,33 +18,39 @@ impl HttpDataProviderObserverTrait for SdkKeyStore { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - _data: &Arc, + _data: &Arc, ) { - let write_lock = self.keystore.clone(); match result { DataProviderRequestResult::DataAvailable => { if !request_context.use_lcut - || write_lock.get(request_context).map_or(true, |v| *v < lcut) + || self + .keystore + .read() + .get(request_context) + .map_or(true, |&v| v < lcut) { - write_lock.insert(Arc::clone(request_context), lcut); + self.keystore + .write() + .insert(Arc::clone(request_context), lcut); } } DataProviderRequestResult::Unauthorized => { - if write_lock.contains_key(request_context) { - write_lock.remove(request_context); - } + self.keystore.write().remove(request_context); } _ => {} } } - async fn get(&self, _request_context: &Arc) -> Option> { + async fn get( + &self, + _request_context: &Arc, + ) -> Option> { None } } pub struct SdkKeyStore { - keystore: Arc, u64>>, + keystore: Arc, u64>>>, } impl Default for SdkKeyStore { @@ -54,18 +62,19 @@ impl Default for SdkKeyStore { impl SdkKeyStore { pub fn new() -> SdkKeyStore { SdkKeyStore { - keystore: Arc::new(DashMap::new()), + keystore: Arc::new(RwLock::new(HashMap::new())), } } pub fn has_key(&self, request_context: &Arc) -> bool { - self.keystore.contains_key(request_context) + self.keystore.read().contains_key(request_context) } pub async fn get_registered_store(&self) -> Vec<(Arc, u64)> { self.keystore + .read() .iter() - .map(|entry| (entry.key().clone(), *entry.value())) + .map(|(key, &value)| (key.clone(), value)) .collect() } } diff --git a/src/datatypes/log_event.rs b/src/datatypes/log_event.rs index 44e08fa..aa5859f 100644 --- a/src/datatypes/log_event.rs +++ b/src/datatypes/log_event.rs @@ -84,4 +84,5 @@ pub struct LogEventRequest { #[derive(Serialize, Deserialize)] pub struct LogEventResponse { pub success: bool, + pub message: Option, } diff --git a/src/loggers/stats_logger.rs b/src/loggers/stats_logger.rs index 03df783..5676340 100644 --- a/src/loggers/stats_logger.rs +++ b/src/loggers/stats_logger.rs @@ -1,4 +1,5 @@ use crate::observers::{ProxyEvent, ProxyEventObserverTrait}; +use crate::GRACEFUL_SHUTDOWN_TOKEN; use async_trait::async_trait; use dogstatsd::{BatchingOptions, Client, OptionsBuilder}; @@ -93,10 +94,20 @@ impl StatsLogger { .await; }); }); + + let graceful_shutdown_token = GRACEFUL_SHUTDOWN_TOKEN.clone(); rocket::tokio::task::spawn_blocking(move || { Handle::current().block_on(async move { loop { - rocket::tokio::time::sleep(Duration::from_secs(60)).await; + if tokio::select! { + _ = tokio::time::sleep(Duration::from_secs(60)) => { false }, + _ = graceful_shutdown_token.cancelled() => { + true + }, + } { + break; + } + TAG_CACHE .lock() .retain(|_, (_, last_used)| last_used.elapsed() > Duration::from_secs(60)); @@ -227,6 +238,7 @@ mod batching { use std::sync::Arc; + use cached::proc_macro::once; use dogstatsd::Options; use statsig::StatsigEvent; @@ -234,11 +246,15 @@ mod batching { use super::*; - lazy_static! { - static ref STATSD_CLIENT: Arc = Arc::new( - Client::new(initialize_datadog_client_options()) - .expect("Need datadog client if enabled") - ); + #[once(option = true)] + fn get_or_initialize_client() -> Option> { + match Client::new(initialize_datadog_client_options()) { + Ok(client) => Some(Arc::new(client)), + Err(e) => { + eprintln!("Failed to initialize statsd client: {}", e); + None + } + } } fn initialize_datadog_client_options() -> Options { @@ -331,51 +347,60 @@ mod batching { } fn send_timing(event: &Operation) -> bool { - match STATSD_CLIENT.timing(&event.counter_name, event.value, event.tags.as_ref()) { - Ok(()) => true, - Err(err) => { - eprintln!("Failed to update timing for counter: {}", err); - false + if let Some(statsd_client) = get_or_initialize_client() { + match statsd_client.timing(&event.counter_name, event.value, event.tags.as_ref()) { + Ok(()) => return true, + Err(err) => { + eprintln!("Failed to update timing for counter: {}", err); + } } } + false } fn send_distribution(event: &Operation) -> bool { - match STATSD_CLIENT.distribution( - &event.counter_name, - event.value.to_string(), - event.tags.as_ref(), - ) { - Ok(()) => true, - Err(err) => { - eprintln!("Failed to update latency for counter: {}", err); - false + if let Some(statsd_client) = get_or_initialize_client() { + match statsd_client.distribution( + &event.counter_name, + event.value.to_string(), + event.tags.as_ref(), + ) { + Ok(()) => return true, + Err(err) => { + eprintln!("Failed to update latency for counter: {}", err); + } } } + false } fn send_gauge(event: &Operation) -> bool { - match STATSD_CLIENT.gauge( - &event.counter_name, - event.value.to_string(), - event.tags.as_ref(), - ) { - Ok(()) => true, - Err(err) => { - eprintln!("Failed to update gauge for counter: {}", err); - false + if let Some(statsd_client) = get_or_initialize_client() { + match statsd_client.gauge( + &event.counter_name, + event.value.to_string(), + event.tags.as_ref(), + ) { + Ok(()) => return true, + Err(err) => { + eprintln!("Failed to update gauge for counter: {}", err); + } } } + false } fn send_incr_by_value(event: &Operation) -> bool { - match STATSD_CLIENT.incr_by_value(&event.counter_name, event.value, event.tags.as_ref()) { - Ok(()) => true, - Err(err) => { - eprintln!("Failed to update increment for counter: {}", err); - false + if let Some(statsd_client) = get_or_initialize_client() { + match statsd_client.incr_by_value(&event.counter_name, event.value, event.tags.as_ref()) + { + Ok(()) => return true, + Err(err) => { + eprintln!("Failed to update increment for counter: {}", err); + } } } + false } fn parse_key_value(s: &str) -> Option<(String, serde_json::Value)> { @@ -426,31 +451,35 @@ mod batching { let max_batch_time = Duration::from_millis(CONFIG.datadog_max_batch_time_ms.unwrap_or(10000)); let max_batch_size = CONFIG.datadog_max_batch_event_count.unwrap_or(3000); + let graceful_shutdown_token = GRACEFUL_SHUTDOWN_TOKEN.clone(); loop { - if let Some(operation) = rx.recv().await { - match operation.operation_type { - OperationType::IncrByValue => { - *incr_op_map - .entry((operation.counter_name.clone(), operation.tags.clone())) - .or_insert(0) += operation.value; + tokio::select! { + Some(operation) = rx.recv() => { + match operation.operation_type { + OperationType::IncrByValue => { + *incr_op_map + .entry((operation.counter_name.clone(), operation.tags.clone())) + .or_insert(0) += operation.value; + } + _ => batch.push(operation), } - _ => batch.push(operation), - } - if batch.len() + incr_op_map.len() >= max_batch_size - || last_updated.elapsed() >= max_batch_time - { - let final_batch = std::mem::take(&mut batch); - let final_incr_op_map = std::mem::take(&mut incr_op_map); - tokio::spawn(flush( - final_batch, - final_incr_op_map, - write_to_statsd, - write_to_statsig, - )); - last_updated = Instant::now(); - } + if batch.len() + incr_op_map.len() >= max_batch_size + || last_updated.elapsed() >= max_batch_time + { + let final_batch = std::mem::take(&mut batch); + let final_incr_op_map = std::mem::take(&mut incr_op_map); + tokio::spawn(flush( + final_batch, + final_incr_op_map, + write_to_statsd, + write_to_statsig, + )); + last_updated = Instant::now(); + } + }, + _ = graceful_shutdown_token.cancelled() => break, } } } diff --git a/src/observers/http_data_provider_observer.rs b/src/observers/http_data_provider_observer.rs index 5292374..19ee4d2 100644 --- a/src/observers/http_data_provider_observer.rs +++ b/src/observers/http_data_provider_observer.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use tokio::sync::RwLock; use crate::{ - datastore::data_providers::DataProviderRequestResult, + datastore::data_providers::{http_data_provider::ResponsePayload, DataProviderRequestResult}, servers::authorized_request_context::AuthorizedRequestContext, }; @@ -38,7 +38,7 @@ impl HttpDataProviderObserver { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - data: &Arc, + data: &Arc, ) { let (async_observers, sync_observers): (Vec<_>, Vec<_>) = { let observers = self.observers.read().await; diff --git a/src/observers/mod.rs b/src/observers/mod.rs index 271b15e..2fc9305 100644 --- a/src/observers/mod.rs +++ b/src/observers/mod.rs @@ -1,14 +1,14 @@ pub mod http_data_provider_observer; pub mod proxy_event_observer; -use dashmap::DashMap; -use std::sync::Arc; +use parking_lot::RwLock; +use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use once_cell::sync::Lazy; use crate::{ - datastore::data_providers::DataProviderRequestResult, + datastore::data_providers::{http_data_provider::ResponsePayload, DataProviderRequestResult}, servers::authorized_request_context::AuthorizedRequestContext, }; @@ -21,9 +21,12 @@ pub trait HttpDataProviderObserverTrait { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - data: &Arc, + data: &Arc, ); - async fn get(&self, request_context: &Arc) -> Option>; + async fn get( + &self, + request_context: &Arc, + ) -> Option>; } #[derive(Debug, Copy, Clone, PartialEq)] @@ -154,18 +157,26 @@ impl ProxyEvent { pub fn get_sdk_key(&self) -> Option> { self.request_context.as_ref().map(|rc| { - if let Some(cached_key) = SDK_KEY_CACHE.get(&rc.sdk_key) { + let cache = SDK_KEY_CACHE.read(); + if let Some(cached_key) = cache.get(&rc.sdk_key) { cached_key.clone() } else { - let new_key: Arc = if rc.sdk_key.len() > 20 { - let mut truncated = rc.sdk_key[..20].to_string(); - truncated.push_str("***"); - Arc::from(truncated) + drop(cache); // Release the read lock + let mut cache = SDK_KEY_CACHE.write(); + // Check again in case another thread inserted the key + if let Some(cached_key) = cache.get(&rc.sdk_key) { + cached_key.clone() } else { - Arc::from(rc.sdk_key.as_str()) - }; - SDK_KEY_CACHE.insert(rc.sdk_key.to_string(), new_key.clone()); - new_key + let new_key: Arc = if rc.sdk_key.len() > 20 { + let mut truncated = rc.sdk_key[..20].to_string(); + truncated.push_str("***"); + Arc::from(truncated) + } else { + Arc::from(rc.sdk_key.as_str()) + }; + cache.insert(rc.sdk_key.to_string(), new_key.clone()); + new_key + } } }) } @@ -190,7 +201,8 @@ impl ProxyEvent { } } -static SDK_KEY_CACHE: Lazy>> = Lazy::new(DashMap::new); +static SDK_KEY_CACHE: Lazy>>> = + Lazy::new(|| RwLock::new(HashMap::new())); #[async_trait] pub trait ProxyEventObserverTrait { diff --git a/src/server.rs b/src/server.rs index d7b81e6..41cd941 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,4 @@ +use clap::ArgAction; use clap::Parser; use clap::ValueEnum; @@ -18,6 +19,7 @@ use futures::join; use loggers::debug_logger; use loggers::stats_logger; use observers::http_data_provider_observer::HttpDataProviderObserver; +use tokio_util::sync::CancellationToken; use servers::authorized_request_context::AuthorizedRequestContextCache; use statsig::{Statsig, StatsigOptions}; @@ -25,8 +27,12 @@ use statsig::{Statsig, StatsigOptions}; use observers::proxy_event_observer::ProxyEventObserver; use observers::HttpDataProviderObserverTrait; use std::sync::Arc; +use std::time::Duration; + use uuid::Uuid; +use lazy_static::lazy_static; + pub mod datastore; pub mod datatypes; pub mod loggers; @@ -35,9 +41,13 @@ pub mod servers; pub mod utils; use serde::Deserialize; -#[derive(Parser)] +lazy_static! { + pub static ref GRACEFUL_SHUTDOWN_TOKEN: CancellationToken = CancellationToken::new(); +} + +#[derive(Parser, Clone)] #[command(version, about, long_about = None)] -struct Cli { +pub struct Cli { #[arg(value_enum)] mode: TransportMode, #[arg(value_enum)] @@ -61,6 +71,8 @@ struct Cli { redis_leader_key_ttl: i64, #[clap(long, default_value = "86400")] redis_cache_ttl_in_s: i64, + #[clap(long, default_value = "20000")] + log_event_process_queue_size: usize, #[clap(long, action)] force_gcp_profiling_enabled: bool, #[clap(short, long, default_value = "500")] @@ -71,6 +83,18 @@ struct Cli { // inavailability of the config. #[clap(long, action)] clear_external_datastore_on_unauthorized: bool, + + // Authorization and TLS Configuration: + #[clap(long, default_value = None)] + x509_server_cert_path: Option, + #[clap(long, default_value = None)] + x509_server_key_path: Option, + #[clap(long, default_value = None)] + x509_client_cert_path: Option, + #[clap(long, action = ArgAction::Set, default_value = "true")] + enforce_tls: bool, + #[clap(long, default_value = "false")] + enforce_mtls: bool, } #[derive(Deserialize, Debug)] @@ -158,6 +182,7 @@ async fn try_initialize_statsig_sdk_and_profiling(cli: &Cli, config: &Configurat } async fn create_config_spec_store( + _cli: &Cli, overrides: &ConfigurationAndOverrides, background_data_provider: Arc, config_spec_observer: Arc, @@ -221,14 +246,16 @@ async fn create_log_event_store( http_client: reqwest::Client, config: &ConfigurationAndOverrides, ) -> Arc { - Arc::new(LogEventStore::new( + let store = Arc::new(LogEventStore::new( config .log_event_statsig_endpoint .as_ref() .map_or("https://statsigapi.net", |s| s.as_str()), http_client, config.log_event_dedupe_cache_limit.unwrap_or(2_000_000), - )) + )); + + store.clone() } #[rocket::main] @@ -252,13 +279,7 @@ async fn main() -> Result<(), Box> { let debug_logger = Arc::new(debug_logger::DebugLogger::new()); ProxyEventObserver::add_observer(debug_logger).await; } - let http_client = reqwest::Client::builder() - .pool_idle_timeout(None) - .build() - .expect("We must have an http client"); - let shared_http_data_provider = Arc::new(http_data_provider::HttpDataProvider::new( - http_client.clone(), - )); + let shared_http_data_provider = Arc::new(http_data_provider::HttpDataProvider {}); let sdk_key_store = Arc::new(sdk_key_store::SdkKeyStore::new()); let background_data_provider = Arc::new(background_data_provider::BackgroundDataProvider::new( shared_http_data_provider, @@ -267,7 +288,7 @@ async fn main() -> Result<(), Box> { Arc::clone(&sdk_key_store), )); let cache_uuid = Uuid::new_v4().to_string(); - let shared_cache: Arc = match cli.cache { + let config_spec_cache: Arc = match cli.cache { CacheMode::Local => Arc::new(in_memory_cache::InMemoryCache::new( cli.maximum_concurrent_sdk_keys, )), @@ -284,13 +305,31 @@ async fn main() -> Result<(), Box> { ), CacheMode::Disabled => Arc::new(disabled_cache::DisabledCache::default()), }; + let id_list_cache: Arc = match cli.cache { + CacheMode::Local => Arc::new(in_memory_cache::InMemoryCache::new( + cli.maximum_concurrent_sdk_keys, + )), + CacheMode::Redis => Arc::new( + redis_cache::RedisCache::new( + "statsig_id_list".to_string(), + cli.redis_leader_key_ttl, + &cache_uuid, + false, /* check lcut */ + cli.clear_external_datastore_on_unauthorized, + cli.redis_cache_ttl_in_s, + ) + .await, + ), + CacheMode::Disabled => Arc::new(disabled_cache::DisabledCache::default()), + }; let config_spec_observer = Arc::new(HttpDataProviderObserver::new()); let config_spec_store = create_config_spec_store( + &cli, &overrides, Arc::clone(&background_data_provider), Arc::clone(&config_spec_observer), - &shared_cache, + &config_spec_cache, &sdk_key_store, ) .await; @@ -299,18 +338,25 @@ async fn main() -> Result<(), Box> { &overrides, Arc::clone(&background_data_provider), Arc::clone(&idlist_observer), - &shared_cache, + &id_list_cache, &sdk_key_store, ) .await; - let log_event_store = create_log_event_store(http_client.clone(), &overrides).await; background_data_provider.start_background_thread().await; let rc_cache = Arc::new(AuthorizedRequestContextCache::new()); - + // Default buffer size is 20000 messages + let http_client = reqwest::Client::builder() + .timeout(Duration::from_secs(30)) + .read_timeout(Duration::from_secs(10)) + .connect_timeout(Duration::from_secs(10)) + .pool_max_idle_per_host(0) + .build() + .expect("We must have an http client"); + let log_event_store = create_log_event_store(http_client.clone(), &overrides).await; match cli.mode { TransportMode::Grpc => { servers::grpc_server::GrpcServer::start_server( - cli.grpc_max_concurrent_streams, + &cli, config_spec_store, config_spec_observer, rc_cache, @@ -319,6 +365,7 @@ async fn main() -> Result<(), Box> { } TransportMode::Http => { servers::http_server::HttpServer::start_server( + &cli, config_spec_store, id_list_store, log_event_store, @@ -328,12 +375,13 @@ async fn main() -> Result<(), Box> { } TransportMode::GrpcAndHttp => { let grpc_server = servers::grpc_server::GrpcServer::start_server( - cli.grpc_max_concurrent_streams, + &cli, config_spec_store.clone(), config_spec_observer.clone(), rc_cache.clone(), ); let http_server = servers::http_server::HttpServer::start_server( + &cli, config_spec_store, id_list_store, log_event_store, @@ -344,5 +392,9 @@ async fn main() -> Result<(), Box> { },); } } + + // Terminate all actively running loops in other threads + GRACEFUL_SHUTDOWN_TOKEN.cancel(); + Ok(()) } diff --git a/src/servers/authorized_request_context.rs b/src/servers/authorized_request_context.rs index 570665b..be0b602 100644 --- a/src/servers/authorized_request_context.rs +++ b/src/servers/authorized_request_context.rs @@ -1,13 +1,14 @@ -use dashmap::DashMap; +use parking_lot::RwLock; use rocket::http::Status; use rocket::request::{self, FromRequest, Outcome, Request}; +use std::collections::HashMap; use std::sync::Arc; #[derive(Debug)] pub struct AuthError; pub struct AuthorizedRequestContextCache( - Arc>>, + Arc>>>, ); impl Default for AuthorizedRequestContextCache { @@ -18,13 +19,27 @@ impl Default for AuthorizedRequestContextCache { impl AuthorizedRequestContextCache { pub fn new() -> Self { - Self(Arc::new(DashMap::new())) + Self(Arc::new(RwLock::new(HashMap::new()))) } - pub fn get_or_insert(&self, sdk_key: String, path: String) -> Arc { - self.0 - .entry((sdk_key.clone(), path.clone())) - .or_insert_with(|| Arc::new(AuthorizedRequestContext::new(sdk_key, path))) + pub fn get_or_insert( + &self, + sdk_key: String, + path: String, + use_gzip: bool, + ) -> Arc { + let key = (sdk_key.clone(), path.clone(), use_gzip); + { + let read_lock = self.0.read(); + if let Some(context) = read_lock.get(&key) { + return context.clone(); + } + } + + let mut write_lock = self.0.write(); + write_lock + .entry(key) + .or_insert_with(|| Arc::new(AuthorizedRequestContext::new(sdk_key, path, use_gzip))) .clone() } } @@ -48,10 +63,18 @@ impl<'r> FromRequest<'r> for AuthorizedRequestContextWrapper { .state::>() .unwrap(); + let use_gzip = headers + .get("Accept-Encoding") + .any(|v| v.to_lowercase().contains("gzip")); + match headers.get_one("statsig-api-key") { - Some(sdk_key) => Outcome::Success(AuthorizedRequestContextWrapper( - cache.get_or_insert(sdk_key.to_string(), request.uri().path().to_string()), - )), + Some(sdk_key) => { + Outcome::Success(AuthorizedRequestContextWrapper(cache.get_or_insert( + sdk_key.to_string(), + request.uri().path().to_string(), + use_gzip, + ))) + } None => Outcome::Error((Status::BadRequest, AuthError)), } } @@ -62,10 +85,11 @@ pub struct AuthorizedRequestContext { pub sdk_key: String, pub path: String, pub use_lcut: bool, + pub use_gzip: bool, } impl AuthorizedRequestContext { - pub fn new(sdk_key: String, path: String) -> Self { + pub fn new(sdk_key: String, path: String, use_gzip: bool) -> Self { let mut normalized_path = path; if normalized_path.ends_with(".json") || normalized_path.ends_with(".js") { if let Some(pos) = normalized_path.rfind('/') { @@ -82,19 +106,20 @@ impl AuthorizedRequestContext { sdk_key, path: normalized_path, use_lcut, + use_gzip, } } } impl std::fmt::Display for AuthorizedRequestContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}|{}", self.sdk_key, self.path) + write!(f, "{}|{}|{}", self.sdk_key, self.path, self.use_gzip) } } impl PartialEq for AuthorizedRequestContext { fn eq(&self, other: &Self) -> bool { - self.sdk_key == other.sdk_key && self.path == other.path + self.sdk_key == other.sdk_key && self.path == other.path && self.use_gzip == other.use_gzip } } @@ -104,5 +129,6 @@ impl std::hash::Hash for AuthorizedRequestContext { fn hash(&self, state: &mut H) { self.sdk_key.hash(state); self.path.hash(state); + self.use_gzip.hash(state); } } diff --git a/src/servers/grpc_server.rs b/src/servers/grpc_server.rs index a183cd1..29975a9 100644 --- a/src/servers/grpc_server.rs +++ b/src/servers/grpc_server.rs @@ -1,6 +1,8 @@ +use std::fs; use tokio::sync::{mpsc, RwLock}; use tokio_stream::wrappers::ReceiverStream; + use tonic::{transport::Server, Request, Response, Status}; use crate::datastore::config_spec_store::ConfigSpecStore; @@ -12,6 +14,7 @@ use crate::observers::OperationType; use crate::observers::{EventStat, HttpDataProviderObserverTrait}; use crate::observers::{ProxyEvent, ProxyEventType}; use crate::servers::streaming_channel::StreamingChannel; +use crate::Cli; use statsig_forward_proxy::statsig_forward_proxy_server::{ StatsigForwardProxy, StatsigForwardProxyServer, }; @@ -30,6 +33,7 @@ pub mod statsig_forward_proxy { tonic::include_proto!("statsig_forward_proxy"); } +#[derive(Clone)] pub struct StatsigForwardProxyServerImpl { config_spec_store: Arc, dcs_observer: Arc, @@ -64,6 +68,14 @@ impl StatsigForwardProxyServerImpl { version_number } + + fn add_common_metadata(&self, response: &mut Response) { + response.metadata_mut().insert( + "x-sfp-hostname", + MetadataValue::from_str(&env::var("HOSTNAME").unwrap_or("no_hostname_set".to_string())) + .unwrap(), + ); + } } #[tonic::async_trait] @@ -81,6 +93,7 @@ impl StatsigForwardProxy for StatsigForwardProxyServerImpl { "/v{}/download_config_specs/", StatsigForwardProxyServerImpl::get_api_version_from_request(&request) ), + false, /* use_gzip */ ), request.get_ref().since_time.unwrap_or(0), ) @@ -92,10 +105,13 @@ impl StatsigForwardProxy for StatsigForwardProxyServerImpl { } }; let reply = ConfigSpecResponse { - spec: data.config.to_string(), + spec: unsafe { String::from_utf8_unchecked(data.config.data.to_vec()) }, last_updated: data.lcut, }; - Ok(Response::new(reply)) + + let mut response = Response::new(reply); + self.add_common_metadata(&mut response); + Ok(response) } type StreamConfigSpecStream = ReceiverStream>; @@ -105,9 +121,11 @@ impl StatsigForwardProxy for StatsigForwardProxyServerImpl { ) -> Result, tonic::Status> { let sdk_key = request.get_ref().sdk_key.to_string(); let api_version = StatsigForwardProxyServerImpl::get_api_version_from_request(&request); - let request_context = self - .rc_cache - .get_or_insert(sdk_key, format!("/v{}/download_config_specs/", api_version)); + let request_context = self.rc_cache.get_or_insert( + sdk_key, + format!("/v{}/download_config_specs/", api_version,), + false, /* use_gzip */ + ); let (tx, rx) = mpsc::channel(1); // Get initial DCS on first request @@ -164,7 +182,13 @@ impl StatsigForwardProxy for StatsigForwardProxyServerImpl { loop { match rc.recv().await { Ok(maybe_csr) => match maybe_csr { - Some(csr) => match tx.send(Ok(csr)).await { + Some((data, lcut)) => match tx + .send(Ok(ConfigSpecResponse { + spec: unsafe { String::from_utf8_unchecked(data.data.to_vec()) }, + last_updated: lcut, + })) + .await + { Ok(_) => { ProxyEventObserver::publish_event( ProxyEvent::new_with_rc( @@ -213,11 +237,7 @@ impl StatsigForwardProxy for StatsigForwardProxyServerImpl { }); let mut response = Response::new(ReceiverStream::new(rx)); - response.metadata_mut().insert( - "x-sfp-hostname", - MetadataValue::from_str(&env::var("HOSTNAME").unwrap_or("no_hostname_set".to_string())) - .unwrap(), - ); + self.add_common_metadata(&mut response); Ok(response) } } @@ -254,12 +274,13 @@ impl GrpcServer { } pub async fn start_server( - max_concurrent_streams: u32, + cli: &Cli, config_spec_store: Arc, shared_dcs_observer: Arc, rc_cache: Arc, ) -> Result<(), Box> { - let addr = "0.0.0.0:50051".parse().unwrap(); + let http_addr = "0.0.0.0:50051".parse().unwrap(); + let https_addr = "0.0.0.0:50052".parse().unwrap(); let update_broadcast_cache = Arc::new(RwLock::new(HashMap::new())); let sfp_server = StatsigForwardProxyServerImpl::new( config_spec_store, @@ -268,13 +289,106 @@ impl GrpcServer { rc_cache, ); - println!("GrpcServer listening on {}", addr); - Server::builder() - .max_concurrent_streams(max_concurrent_streams) - .add_service(StatsigForwardProxyServer::new(sfp_server)) + match ( + cli.x509_server_cert_path.is_some() && cli.x509_server_key_path.is_some(), + cli.enforce_tls, + ) { + (false, _) => { + println!("GrpcServer listening on {} (HTTP)", http_addr); + Self::create_http_server(sfp_server, http_addr, update_broadcast_cache.clone()) + .await? + } + (true, true) => { + println!("GrpcServer listening on {} (HTTPS)", http_addr); + Self::create_https_server( + cli, + sfp_server.clone(), + https_addr, + update_broadcast_cache.clone(), + ) + .await? + } + (true, false) => { + let https_server = Self::create_https_server( + cli, + sfp_server.clone(), + https_addr, + update_broadcast_cache.clone(), + ); + let http_server = + Self::create_http_server(sfp_server, http_addr, update_broadcast_cache.clone()); + println!( + "GrpcServer listening on {} (HTTPS) and {} (HTTP)", + https_addr, http_addr + ); + + // Use select! to run both servers concurrently + tokio::select! { + https_result = https_server => { + if let Err(e) = https_result { + eprintln!("HTTPS server error: {:?}", e); + } + } + http_result = http_server => { + if let Err(e) = http_result { + eprintln!("HTTP server error: {:?}", e); + } + } + } + } + } + + Ok(()) + } + + async fn create_https_server( + cli: &Cli, + sfp_server: StatsigForwardProxyServerImpl, // Remove & here + addr: std::net::SocketAddr, + update_broadcast_cache: Arc< + RwLock, Arc>>, + >, + ) -> Result<(), Box> { + let mut builder = Server::builder(); + if let (Some(cert_path), Some(key_path)) = ( + cli.x509_server_cert_path.clone(), + cli.x509_server_key_path.clone(), + ) { + let cert = fs::read_to_string(cert_path)?; + let key = fs::read_to_string(key_path)?; + let server_tls_id = + tonic::transport::Identity::from_pem(cert.as_bytes(), key.as_bytes()); + let mut tls = tonic::transport::ServerTlsConfig::new().identity(server_tls_id); + tls = tls.client_auth_optional(!cli.enforce_mtls); + + if let Some(client_cert_path) = cli.x509_client_cert_path.clone() { + let encoded_client_cert = fs::read_to_string(client_cert_path)?; + let client_cert = + tonic::transport::Certificate::from_pem(encoded_client_cert.as_bytes()); + tls = tls.client_ca_root(client_cert); + } + + builder = builder.tls_config(tls)?; + } + builder + .max_concurrent_streams(cli.grpc_max_concurrent_streams) + .add_service(StatsigForwardProxyServer::new(sfp_server)) // Remove .clone() here .serve_with_shutdown(addr, GrpcServer::shutdown_signal(update_broadcast_cache)) .await?; + Ok(()) + } + async fn create_http_server( + sfp_server: StatsigForwardProxyServerImpl, // Remove & here + addr: std::net::SocketAddr, + update_broadcast_cache: Arc< + RwLock, Arc>>, + >, + ) -> Result<(), Box> { + Server::builder() + .add_service(StatsigForwardProxyServer::new(sfp_server)) // Remove .clone() here + .serve_with_shutdown(addr, GrpcServer::shutdown_signal(update_broadcast_cache)) + .await?; Ok(()) } } diff --git a/src/servers/http_server.rs b/src/servers/http_server.rs index c416bfd..fa01aa1 100644 --- a/src/servers/http_server.rs +++ b/src/servers/http_server.rs @@ -1,8 +1,12 @@ use std::collections::HashMap; +use std::io::Cursor; use std::sync::Arc; +use crate::datastore::data_providers::http_data_provider::ResponsePayload; use crate::datastore::get_id_list_store::GetIdListStore; + use crate::datastore::log_event_store::LogEventStore; + use crate::datatypes::gzip_data::LoggedBodyJSON; use crate::datatypes::log_event::LogEventRequest; use crate::datatypes::log_event::LogEventResponse; @@ -10,26 +14,41 @@ use crate::observers::EventStat; use crate::observers::OperationType; use crate::observers::{ProxyEvent, ProxyEventType}; use crate::servers::http_apis; +use crate::Cli; +use bytes::Bytes; + use rocket::fairing::AdHoc; +use rocket::http::ContentType; use rocket::http::StatusClass; use rocket::http::{Header, Status}; use rocket::post; use rocket::response::status::Custom; +use rocket::response::Responder; use rocket::routes; use rocket::serde::json::Json; use rocket::Request; +use rocket::Response; use rocket::State; use rocket::{catch, catchers, get}; use serde::{Deserialize, Serialize}; + use tokio::runtime::Handle; use tokio::sync::RwLock; use tokio::time::Instant; use crate::datastore::config_spec_store::ConfigSpecStore; use crate::observers::proxy_event_observer::ProxyEventObserver; +use lazy_static::lazy_static; + +lazy_static! { + static ref UNAUTHORIZED_RESPONSE: Arc = Arc::new(ResponsePayload { + encoding: Arc::new(None), + data: Arc::from(Bytes::from("Unauthorized")) + }); +} // Import the new module use crate::servers::authorized_request_context::{ @@ -55,19 +74,69 @@ fn default_catcher(status: Status, _: &Request<'_>) -> Json { }) } +#[repr(transparent)] +struct DerefRef(T); + +impl AsRef<[u8]> for DerefRef +where + T::Target: AsRef<[u8]>, +{ + fn as_ref(&self) -> &[u8] { + self.0.deref().as_ref() + } +} + +enum RequestPayloads { + Gzipped(Arc), + Plain(Arc), + Unauthorized(), +} + +impl<'r> Responder<'r, 'static> for RequestPayloads { + fn respond_to(self, _req: &'r Request) -> Result, Status> { + match self { + RequestPayloads::Gzipped(data) => Response::build() + .status(Status::Ok) + .header(ContentType::JSON) + .header(rocket::http::Header::new("Content-Encoding", "gzip")) + .sized_body(data.len(), Cursor::new(DerefRef(data))) + .ok(), + RequestPayloads::Plain(data) => Response::build() + .status(Status::Ok) + .header(ContentType::JSON) + .sized_body(data.len(), Cursor::new(DerefRef(data))) + .ok(), + RequestPayloads::Unauthorized() => Response::build() + .status(Status::Unauthorized) + .header(ContentType::Plain) + .sized_body( + UNAUTHORIZED_RESPONSE.data.len(), + Cursor::new(&*UNAUTHORIZED_RESPONSE.data), + ) + .ok(), + } + } +} + #[get("/download_config_specs/?")] async fn get_download_config_specs( config_spec_store: &State>, #[allow(unused_variables)] sdk_key_file: &str, #[allow(non_snake_case)] sinceTime: Option, authorized_rc: AuthorizedRequestContextWrapper, -) -> Result, Status> { +) -> RequestPayloads { match config_spec_store .get_config_spec(&authorized_rc.inner(), sinceTime.unwrap_or(0)) .await { - Some(data) => Ok(Arc::clone(&data.config)), - None => Err(Status::Unauthorized), + Some(data) => { + if data.config.encoding.as_deref() == Some("gzip") { + RequestPayloads::Gzipped(Arc::clone(&data.config.data)) + } else { + RequestPayloads::Plain(Arc::clone(&data.config.data)) + } + } + None => RequestPayloads::Unauthorized(), } } @@ -82,14 +151,20 @@ async fn post_download_config_specs( dcs_request_json: Json, config_spec_store: &State>, authorized_rc: AuthorizedRequestContextWrapper, -) -> Result, Status> { +) -> RequestPayloads { let dcs_request = dcs_request_json.into_inner(); match config_spec_store .get_config_spec(&authorized_rc.inner(), dcs_request.since_time.unwrap_or(0)) .await { - Some(data) => Ok(Arc::clone(&data.config)), - None => Err(Status::Unauthorized), + Some(data) => { + if data.config.encoding.as_deref() == Some("gzip") { + RequestPayloads::Gzipped(Arc::clone(&data.config.data)) + } else { + RequestPayloads::Plain(Arc::clone(&data.config.data)) + } + } + None => RequestPayloads::Unauthorized(), } } @@ -97,25 +172,35 @@ async fn post_download_config_specs( async fn post_get_id_lists( get_id_list_store: &State>, authorized_rc: AuthorizedRequestContextWrapper, -) -> Result, Status> { +) -> RequestPayloads { match get_id_list_store.get_id_lists(&authorized_rc.inner()).await { - Some(data) => Ok(Arc::clone(&data.idlists)), - None => Err(Status::Unauthorized), + Some(data) => RequestPayloads::Plain(Arc::clone(&data.idlists.data)), + None => RequestPayloads::Unauthorized(), } } #[post("/log_event", data = "")] -fn post_log_event( +async fn post_log_event( log_event_store: &State>, request_body: LoggedBodyJSON, auth_header: AuthorizedRequestContextWrapper, ) -> Custom> { - let store = log_event_store.inner().clone(); - rocket::tokio::task::spawn_blocking(move || { - let body = request_body.into_inner(); - Handle::current().block_on(store.log_event(body, &auth_header.inner())) + let store_copy = log_event_store.inner().clone(); + tokio::task::spawn_blocking(move || { + Handle::current().block_on(async move { + let _ = store_copy + .log_event(request_body.into_inner(), &auth_header.inner()) + .await; + }); }); - Custom(Status::Accepted, Json(LogEventResponse { success: true })) + + Custom( + Status::Accepted, + Json(LogEventResponse { + success: true, + message: None, + }), + ) } pub struct HttpServer {} @@ -124,6 +209,7 @@ pub struct SdkKeyCache(pub RwLock>); impl HttpServer { pub async fn start_server( + cli: &Cli, config_spec_store: Arc, id_list_store: Arc, log_event_store: Arc, @@ -153,6 +239,7 @@ impl HttpServer { .manage(log_event_store) .manage(rc_cache) .manage(sdk_key_cache) + .manage(cli.clone()) .attach(AdHoc::on_request("Normalize SDK Key", |req, _| { Box::pin(async move { req.local_cache(|| TimerStart(Some(Instant::now()))); @@ -205,13 +292,17 @@ impl HttpServer { .get_one("statsig-api-key") .unwrap_or("no-key-provided") .to_string(); + let use_gzip = req + .headers() + .get("Accept-Encoding") + .any(|v| v.to_lowercase().contains("gzip")); let path = req.uri().path().to_string(); let status_code = resp.status().code; let status_class = resp.status().class(); // Spawn a new task to handle logging tokio::spawn(async move { - let request_context = cache.get_or_insert(sdk_key, path); + let request_context = cache.get_or_insert(sdk_key, path, use_gzip); let event = ProxyEvent::new_with_rc( if status_class == StatusClass::Success { diff --git a/src/servers/streaming_channel.rs b/src/servers/streaming_channel.rs index 66973f3..693a8a2 100644 --- a/src/servers/streaming_channel.rs +++ b/src/servers/streaming_channel.rs @@ -1,3 +1,4 @@ +use crate::datastore::data_providers::http_data_provider::ResponsePayload; use crate::datastore::data_providers::DataProviderRequestResult; use crate::observers::HttpDataProviderObserverTrait; use async_trait::async_trait; @@ -6,7 +7,6 @@ use tokio::sync::broadcast; use tokio::sync::broadcast::Sender; use tokio::sync::RwLock; -use super::grpc_server::statsig_forward_proxy::ConfigSpecResponse; use crate::observers::proxy_event_observer::ProxyEventObserver; use crate::observers::EventStat; use crate::observers::OperationType; @@ -16,7 +16,7 @@ use crate::servers::authorized_request_context::AuthorizedRequestContext; pub struct StreamingChannel { request_context: Arc, last_updated: Arc>, - pub sender: Arc>>>, + pub sender: Arc, u64)>>>>, } impl StreamingChannel { @@ -41,7 +41,7 @@ impl HttpDataProviderObserverTrait for StreamingChannel { result: &DataProviderRequestResult, request_context: &Arc, lcut: u64, - data: &Arc, + data: &Arc, ) { let mut wlock = self.last_updated.write().await; let is_newer_lcut = lcut > *wlock; @@ -63,10 +63,7 @@ impl HttpDataProviderObserverTrait for StreamingChannel { .sender .write() .await - .send(Some(ConfigSpecResponse { - spec: data.to_string(), - last_updated: lcut, - })) + .send(Some((Arc::clone(data), lcut))) .is_err() { // TODO: Optimize code, no receivers are listening @@ -78,7 +75,10 @@ impl HttpDataProviderObserverTrait for StreamingChannel { } } - async fn get(&self, _request_context: &Arc) -> Option> { + async fn get( + &self, + _request_context: &Arc, + ) -> Option> { unimplemented!("Not Used") } } diff --git a/x509_test_certs/gen_certs.sh b/x509_test_certs/gen_certs.sh new file mode 100755 index 0000000..06a4c27 --- /dev/null +++ b/x509_test_certs/gen_certs.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Set variables +ROOT_CA_NAME="root_ca" +INTERMEDIATE_CA_NAME="intermediate_ca" +VALIDITY_DAYS=3650 # 10 years + +# Check if root and intermediate CAs exist +if [ ! -f "root/certs/${ROOT_CA_NAME}.crt" ] || [ ! -f "intermediate/certs/${INTERMEDIATE_CA_NAME}.crt" ]; then + echo "Error: Root or Intermediate CA certificates not found. Please run the full script to generate them first." + exit 1 +fi + +echo "Using existing Root and Intermediate CAs..." + +# Generate Server Certificate +echo "Generating Server Certificate..." +SERVER_NAME="server" +openssl genrsa -out intermediate/private/${SERVER_NAME}.key 2048 +chmod 400 intermediate/private/${SERVER_NAME}.key + +openssl req -new -sha256 -key intermediate/private/${SERVER_NAME}.key -out intermediate/csr/${SERVER_NAME}.csr -subj "/C=US/ST=State/L=City/O=Organization/OU=Server/CN=${SERVER_NAME}" + +# Create a config file for the server certificate with SANs +cat > intermediate/${SERVER_NAME}.cnf < intermediate/certs/${SERVER_NAME}_full_chain.crt +echo "Server certificate full chain: intermediate/certs/${SERVER_NAME}_full_chain.crt" + +# Verify the full chain +echo "Verifying the full certificate chain..." +openssl verify -CAfile root/certs/${ROOT_CA_NAME}.crt -untrusted intermediate/certs/${INTERMEDIATE_CA_NAME}.crt intermediate/certs/${SERVER_NAME}.crt + +# Generate Client Certificate +echo "Generating Client Certificate..." +CLIENT_NAME="client" +openssl genrsa -out intermediate/private/${CLIENT_NAME}.key 2048 +chmod 400 intermediate/private/${CLIENT_NAME}.key + +openssl req -new -sha256 -key intermediate/private/${CLIENT_NAME}.key -out intermediate/csr/${CLIENT_NAME}.csr -subj "/C=US/ST=State/L=City/O=Organization/OU=Client/CN=${CLIENT_NAME}" + +# Create a config file for the client certificate with SANs +cat > intermediate/${CLIENT_NAME}.cnf < intermediate/certs/${CLIENT_NAME}_full_chain.crt +echo "Client certificate full chain: intermediate/certs/${CLIENT_NAME}_full_chain.crt" + +# Verify the full client chain +echo "Verifying the full client certificate chain..." +openssl verify -CAfile root/certs/${ROOT_CA_NAME}.crt -untrusted intermediate/certs/${INTERMEDIATE_CA_NAME}.crt intermediate/certs/${CLIENT_NAME}.crt \ No newline at end of file diff --git a/x509_test_certs/intermediate/certs/client.crt b/x509_test_certs/intermediate/certs/client.crt new file mode 100644 index 0000000..de2b106 --- /dev/null +++ b/x509_test_certs/intermediate/certs/client.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE8DCCAtigAwIBAgIUEV3th0jZ+9ES/h0cXgfAsp1J2gowDQYJKoZIhvcNAQEL +BQAwbTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xKDAmBgNVBAsMH0ludGVybWVkaWF0ZSBD +Tj1pbnRlcm1lZGlhdGVfY2EwHhcNMjQxMDA3MDU1NjA4WhcNMzQxMDA1MDU1NjA4 +WjBlMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkx +FTATBgNVBAoMDE9yZ2FuaXphdGlvbjEPMA0GA1UECwwGQ2xpZW50MQ8wDQYDVQQD +DAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbuXgyIHjg +04Pg+njFcMEXaaabMBCLKPy/hlRYo2wG4GoXjEEZSXqrXygwzmZboTA1LPZ4RpvK ++Bf/jnqg60U3odU86+8d1EPbWuRmYeaAgD1QoZMjfuJ7A3IKyF9Xvd0rodpfBiKx +gzeBcWhNh0ropaGVAWFbFVW951/f+fM9MwZ483BO9Tt85j6I8xD+pgInl5K8Xdm6 +5UWM6SL6lCfC3vFJw+98PlAdDATb1DNgIyhzD1+buUc2xe8XGzZ4dA5TPVMq/RKY +jCFNAHII4UedqEtwjKYALJ9hYUr+6kxQc5R4xo9+OM8bs6c0N8EyPlUXo2MkeK95 +wxii7DRE50/9AgMBAAGjgY8wgYwwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAw +EwYDVR0lBAwwCgYIKwYBBQUHAwIwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAAB +MB0GA1UdDgQWBBRepp6/BDIXJqKIB+pawHL9qhDpUDAfBgNVHSMEGDAWgBStAmmb +H4+dUmP3KvBiSxZD0dlYkDANBgkqhkiG9w0BAQsFAAOCAgEAhpDwj6Xng4RLIpZd +2FCNfAi7Bz99R2K/Ar1iyk5yA7OCLpQdRSIp0RdPQhcu0s5GidxTqN9Bhjg+wMGE +eW2dX4yFd3pBiTgosllkMffniZKJTgkY5OxFJr4lxvLOrlnc64T2agX7xkRSkliZ +0Rkb6oi6QooOGDqKrpJ7730ToqVzUjoUeZdojz73cUIH2h3sFqndF19hLMaMicjz +tMlfg1fvu+1mdsoYGgBpZEKePgnN22ikvPE7DBMfDRMs/v4DsPd4zzxocyyJRp/N +msYY4KZyHfImg+YZm1yq5b3fNprS1iIx4+BdDmWtm0D9WVyOxMBN5VRg0yFg04QT +e6EG8cI92/q9J5/oMkv73epCZrVDwAoxHJM+5ZLJ1E1HPgD/+RVMzpyA06LTkbp6 +EYaZMo7CHt1U7c9NdtlLuo7F43oW6yZw1yv6BgXUu14NO4HqwwzcFYcKFXVTQMkq +dm+Hs20YnOlSE4ftgecuMKN1fg3drBZj4hhChU/yOUpfPtKKFbioWuPaqox1liil +kAQALskMKHJdQ1GVhVGqONYJ6Q+vrMyKW8Z5HNz2enSVMcmkPpTsMpun3hC6o9FD +arSIbLMe5bBhz/HuRRp2y4UWdxekxATwDBBIjJm83vQR9LkDkk13ZPsn2tWSjGr2 +WyfJN3DZY6oOyu2g5c3TiwW8UAY= +-----END CERTIFICATE----- diff --git a/x509_test_certs/intermediate/certs/client_full_chain.crt b/x509_test_certs/intermediate/certs/client_full_chain.crt new file mode 100644 index 0000000..8dfe83a --- /dev/null +++ b/x509_test_certs/intermediate/certs/client_full_chain.crt @@ -0,0 +1,96 @@ +-----BEGIN CERTIFICATE----- +MIIE8DCCAtigAwIBAgIUEV3th0jZ+9ES/h0cXgfAsp1J2gowDQYJKoZIhvcNAQEL +BQAwbTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xKDAmBgNVBAsMH0ludGVybWVkaWF0ZSBD +Tj1pbnRlcm1lZGlhdGVfY2EwHhcNMjQxMDA3MDU1NjA4WhcNMzQxMDA1MDU1NjA4 +WjBlMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkx +FTATBgNVBAoMDE9yZ2FuaXphdGlvbjEPMA0GA1UECwwGQ2xpZW50MQ8wDQYDVQQD +DAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbuXgyIHjg +04Pg+njFcMEXaaabMBCLKPy/hlRYo2wG4GoXjEEZSXqrXygwzmZboTA1LPZ4RpvK ++Bf/jnqg60U3odU86+8d1EPbWuRmYeaAgD1QoZMjfuJ7A3IKyF9Xvd0rodpfBiKx +gzeBcWhNh0ropaGVAWFbFVW951/f+fM9MwZ483BO9Tt85j6I8xD+pgInl5K8Xdm6 +5UWM6SL6lCfC3vFJw+98PlAdDATb1DNgIyhzD1+buUc2xe8XGzZ4dA5TPVMq/RKY +jCFNAHII4UedqEtwjKYALJ9hYUr+6kxQc5R4xo9+OM8bs6c0N8EyPlUXo2MkeK95 +wxii7DRE50/9AgMBAAGjgY8wgYwwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAw +EwYDVR0lBAwwCgYIKwYBBQUHAwIwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAAB +MB0GA1UdDgQWBBRepp6/BDIXJqKIB+pawHL9qhDpUDAfBgNVHSMEGDAWgBStAmmb +H4+dUmP3KvBiSxZD0dlYkDANBgkqhkiG9w0BAQsFAAOCAgEAhpDwj6Xng4RLIpZd +2FCNfAi7Bz99R2K/Ar1iyk5yA7OCLpQdRSIp0RdPQhcu0s5GidxTqN9Bhjg+wMGE +eW2dX4yFd3pBiTgosllkMffniZKJTgkY5OxFJr4lxvLOrlnc64T2agX7xkRSkliZ +0Rkb6oi6QooOGDqKrpJ7730ToqVzUjoUeZdojz73cUIH2h3sFqndF19hLMaMicjz +tMlfg1fvu+1mdsoYGgBpZEKePgnN22ikvPE7DBMfDRMs/v4DsPd4zzxocyyJRp/N +msYY4KZyHfImg+YZm1yq5b3fNprS1iIx4+BdDmWtm0D9WVyOxMBN5VRg0yFg04QT +e6EG8cI92/q9J5/oMkv73epCZrVDwAoxHJM+5ZLJ1E1HPgD/+RVMzpyA06LTkbp6 +EYaZMo7CHt1U7c9NdtlLuo7F43oW6yZw1yv6BgXUu14NO4HqwwzcFYcKFXVTQMkq +dm+Hs20YnOlSE4ftgecuMKN1fg3drBZj4hhChU/yOUpfPtKKFbioWuPaqox1liil +kAQALskMKHJdQ1GVhVGqONYJ6Q+vrMyKW8Z5HNz2enSVMcmkPpTsMpun3hC6o9FD +arSIbLMe5bBhz/HuRRp2y4UWdxekxATwDBBIjJm83vQR9LkDkk13ZPsn2tWSjGr2 +WyfJN3DZY6oOyu2g5c3TiwW8UAY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGATCCA+mgAwIBAgIUXOZaiUcbnqv+GLKy+6SlLThCOtMwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMTIxMzhaFw0zNDA5MjUyMTIxMzhaMG0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMSgwJgYDVQQLDB9JbnRlcm1lZGlhdGUgQ049aW50ZXJtZWRpYXRlX2Nh +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA592XixtZoUQnUXnX76Ye +R+WsKyTtYcCtQteiZ5e7lH8d6hvSE5JXNiDxfEpwunr/2SOGBxU/8hyLQuKkXd+C +nDtiVYk68GknsgiIC+2y6QrF/OFZsVrni63BYYeMSqLd+K8FNufNSvlkUPRBZ+aZ +Iy6/nQHT0B0zOVmTBZ/3tnwsvvnfM9TUl9ajz2S/mCSj1/OEq7l0y2QkEegrGN1o +YjNkr3wAl6EKr5EtchVlIVRAD+2vzCEo2fwQZhSDyyzO+twsQxWVbV3BTDR3QMhu +2r0NoGpLg+3gPu5WJ53RRZ5uPSNxl1HTVtZOireD1utyL0rv6JawUi1MRdHdburm +1HAbEEAUGES1N5iycA9M1igNBYjteS2y3HniZJ/KioIWapYw6xLEHETTCB+o/kWO +dAwAoOwNbitj3jM32TKnUmzmyNziIB6GVcnJW331U1sjZEJnR3332aAIbUj3u1bN +tGmY59xkD8AKdivZ2bB7Xu0lOFzKDMFiMMVpVL3f+21SyNMOkPsisFkQV9LiuYiR ++y+tPejGb/H6/iDPzf26Dhx2qUz28JMuObEuLvRlxkXE4nroR4w8bkubua64kIDX +55g622WjiTg0ZkpHAPKN8eN9qT8d+uqYMYAQosymameYzcGtEAs6z3ksUivcOB7J +/sT1Svn0CFKSAC4mREppGlECAwEAAaOBqDCBpTASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +IAYDVR0RBBkwF4IJbG9jYWxob3N0hwR/AAABhwQAAAAAMB0GA1UdDgQWBBStAmmb +H4+dUmP3KvBiSxZD0dlYkDAfBgNVHSMEGDAWgBT9/KhrWT9ey/WTI2rYN1+kL0q9 +TjANBgkqhkiG9w0BAQsFAAOCAgEAHl9yzoNhJACIEtlQYb3HvAWQBiIM9A/W4/je +4ENsZp2EIFEW+pXvTtXxkbdX0LFIeJ8Ut2Fjici17AuJe4CPZ4ccbbkwvkZGwopp +T2k6XqHKZnDJQEL4ypj34o6nFnlGqkFM+KtCiMR0EQDDB45jjj54ayWrLeNECeE9 +Q3oufjVqd+07+VbSKAmLqTPTgH0r/B1aX5frq0UCk9J88N/ykYLWboDXLL7Ivm4N +cfdVnayo8d1Wk5yPbriKkV1pDNEofg7pcKNFepBibkIvkWr7ir6usDtBqt/FAM4L +FrMKSD0W49e56VSAZSHlyFiQyQyGt+rZ97mRMxGE8GdDVBBTBDXI2yxBck7d+U+T +HEC/LyDvYToiUsG8chkUi/B0ntctEZwen2SA26m/A9Q5BE6v+L09U/MoboSFnhul +DgJosoAAbixICxuzrnJbC++0dgg1ING1fp7XcCK/GNmhddHN2w+m0SoGy+2h3/YP +euzFUQblVIOSiA6uG6QZa++tLHKNm4KovaUHvgfY6zjYLWNJNrQxYC0AfbDOBaVi +GIOowoUOMXSowbcPNroC2YJ/UJQusB1nMnXp7xz2YrFBN3+wQOdJRaiVfGpW0Qrq +sDalsoltC7UTpjR1ue6lvyiYMQ7mXkl4Xa/1gPgjJMP744jmI7fBPVht643L/bbm +/GHStM4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFmzCCA4OgAwIBAgIUQEjtec3bO/kDXxGz6WHeSHdc6GkwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMTIxMzhaFw0zNDA5MjUyMTIxMzhaMF0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMRgwFgYDVQQLDA9Sb290IENOPXJvb3RfY2EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDAyvwNu/yBG+xQY3tsUTLIXOnZmrldbPC+3hmjb5ZH +ovjQK8/en2YCzv6YDRduSXmw87LNgPaj4/R9OrnX7e8bdAJN8yzKR2UxxBVnLYxt +yzLxiBL9yFEvAVmLt72IfwZ1pR85BH3s/O+5enWfM7O1xpS8UsekBJsWXvkvUrfl +c6D1YnpBVgVIB3267PKSkXbT9N8rW05uBTohIdT7gTrx1C3nUVzwxQylhCWCCD9J +DHNIbbsWxKFw8StNOU6zZ/Zrsk7y6TB6pYS9WggNVPEqOa0vl79On5MtWpaazkH8 +4Hqjjl8qcoY4yKOPrh0kUhKWsuQPmkIqsQX0fJjYsthoUjvofWcFwUWWGe64Dgu6 +faU9HWs8kqZH4wIcBr+6Il4VAw5yuQlD8lzwhvMg1TNW9QPR4JmeAIiDG9u6Tpot +s2LuUYna/DzsxHuKNFn/1nwJDm7CyZ+rGzjLhKlVH7F1Mnmo7mMfKJGgh09hxoz5 +a7ZFL+ybFe/j4bOZQiMBMdVk06tmQtu7iA/RUSVD1w0Lzz9hr0hwqYlCMZHaeyZw +v0Xa73ypzu/UCVz5yxgQGZxGHMBkiFm78tTmQeqtAfOi8EzgNq5idSXp3dTn2z3z +UTlnsv7B1kBFBKLVDT99PsEXqrCrWDfJkoD3788uA8gI+WjA20gSdVyRqBRSOHrp +JQIDAQABo1MwUTAdBgNVHQ4EFgQU/fyoa1k/Xsv1kyNq2DdfpC9KvU4wHwYDVR0j +BBgwFoAU/fyoa1k/Xsv1kyNq2DdfpC9KvU4wDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEAfIf1eK0Nd53170VAwm4qc9GKNbK0C/RH1iysnlGm3KGn +IimUr4Nl3M9rR5WWfNftctxeZj/g2ALBfF+598VO6ezp6PYn1inA8ZOa5lBuxXPi +z4JY1BOEIathR77v+nA4W2YFIn65B8O76+SyTkDRsCprSyIVpjE/FdHyoEqciK5f +5QDbaELG3n3UTgfwJTKoug/xDQmxu/vc5Y0Y9z2j6PsOYIg4eC+5X75CLjuH4KGG +7Newrsu8jQi1QmlSTsWN5iQa4QfvILbq6BznlOPB4jnnD1/lSn2opPLAZ622OrSC +41BrpTNsDdG3yrzxxyxo3u7flOd2fYoq4Opvdr60ZQuuee1FeONOJmK72q7DvqxZ +YmT7+kgL4NyvmXGx4diBsMygdL9M2k6U3O7HaitdiY/a28AuOWKRuseX+Pwh3Rta +9dJZo+YzSiQ1DXf+ddbxWjji2diW4CpyVpkwgTq8KOUbbDWm1xeZHQAicsTS3O5F +LvNXrJ54NrTsGMA6wh/AOfjDfcwly2CBW3envSmQHr3Ywb5E/dIRtkNcp09w6G9f +kO5eMAn1RKm7tjqFEoCMwkF2PsX8wXqIi5TiFKoHApFodFZckpsJg0bRQH/B6de7 +31Nqyc4RFh4axbp6RUH7+oFv0y/Pi8dufYvGaWxGwLO2OnVUg2mWDu47Z2wI1MI= +-----END CERTIFICATE----- diff --git a/x509_test_certs/intermediate/certs/intermediate_ca.crt b/x509_test_certs/intermediate/certs/intermediate_ca.crt new file mode 100644 index 0000000..e98edcf --- /dev/null +++ b/x509_test_certs/intermediate/certs/intermediate_ca.crt @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGATCCA+mgAwIBAgIUXOZaiUcbnqv+GLKy+6SlLThCOtMwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMTIxMzhaFw0zNDA5MjUyMTIxMzhaMG0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMSgwJgYDVQQLDB9JbnRlcm1lZGlhdGUgQ049aW50ZXJtZWRpYXRlX2Nh +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA592XixtZoUQnUXnX76Ye +R+WsKyTtYcCtQteiZ5e7lH8d6hvSE5JXNiDxfEpwunr/2SOGBxU/8hyLQuKkXd+C +nDtiVYk68GknsgiIC+2y6QrF/OFZsVrni63BYYeMSqLd+K8FNufNSvlkUPRBZ+aZ +Iy6/nQHT0B0zOVmTBZ/3tnwsvvnfM9TUl9ajz2S/mCSj1/OEq7l0y2QkEegrGN1o +YjNkr3wAl6EKr5EtchVlIVRAD+2vzCEo2fwQZhSDyyzO+twsQxWVbV3BTDR3QMhu +2r0NoGpLg+3gPu5WJ53RRZ5uPSNxl1HTVtZOireD1utyL0rv6JawUi1MRdHdburm +1HAbEEAUGES1N5iycA9M1igNBYjteS2y3HniZJ/KioIWapYw6xLEHETTCB+o/kWO +dAwAoOwNbitj3jM32TKnUmzmyNziIB6GVcnJW331U1sjZEJnR3332aAIbUj3u1bN +tGmY59xkD8AKdivZ2bB7Xu0lOFzKDMFiMMVpVL3f+21SyNMOkPsisFkQV9LiuYiR ++y+tPejGb/H6/iDPzf26Dhx2qUz28JMuObEuLvRlxkXE4nroR4w8bkubua64kIDX +55g622WjiTg0ZkpHAPKN8eN9qT8d+uqYMYAQosymameYzcGtEAs6z3ksUivcOB7J +/sT1Svn0CFKSAC4mREppGlECAwEAAaOBqDCBpTASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +IAYDVR0RBBkwF4IJbG9jYWxob3N0hwR/AAABhwQAAAAAMB0GA1UdDgQWBBStAmmb +H4+dUmP3KvBiSxZD0dlYkDAfBgNVHSMEGDAWgBT9/KhrWT9ey/WTI2rYN1+kL0q9 +TjANBgkqhkiG9w0BAQsFAAOCAgEAHl9yzoNhJACIEtlQYb3HvAWQBiIM9A/W4/je +4ENsZp2EIFEW+pXvTtXxkbdX0LFIeJ8Ut2Fjici17AuJe4CPZ4ccbbkwvkZGwopp +T2k6XqHKZnDJQEL4ypj34o6nFnlGqkFM+KtCiMR0EQDDB45jjj54ayWrLeNECeE9 +Q3oufjVqd+07+VbSKAmLqTPTgH0r/B1aX5frq0UCk9J88N/ykYLWboDXLL7Ivm4N +cfdVnayo8d1Wk5yPbriKkV1pDNEofg7pcKNFepBibkIvkWr7ir6usDtBqt/FAM4L +FrMKSD0W49e56VSAZSHlyFiQyQyGt+rZ97mRMxGE8GdDVBBTBDXI2yxBck7d+U+T +HEC/LyDvYToiUsG8chkUi/B0ntctEZwen2SA26m/A9Q5BE6v+L09U/MoboSFnhul +DgJosoAAbixICxuzrnJbC++0dgg1ING1fp7XcCK/GNmhddHN2w+m0SoGy+2h3/YP +euzFUQblVIOSiA6uG6QZa++tLHKNm4KovaUHvgfY6zjYLWNJNrQxYC0AfbDOBaVi +GIOowoUOMXSowbcPNroC2YJ/UJQusB1nMnXp7xz2YrFBN3+wQOdJRaiVfGpW0Qrq +sDalsoltC7UTpjR1ue6lvyiYMQ7mXkl4Xa/1gPgjJMP744jmI7fBPVht643L/bbm +/GHStM4= +-----END CERTIFICATE----- diff --git a/x509_test_certs/intermediate/certs/intermediate_ca.srl b/x509_test_certs/intermediate/certs/intermediate_ca.srl new file mode 100644 index 0000000..b831dbf --- /dev/null +++ b/x509_test_certs/intermediate/certs/intermediate_ca.srl @@ -0,0 +1 @@ +115DED8748D9FBD112FE1D1C5E07C0B29D49DA0A diff --git a/x509_test_certs/intermediate/certs/server.crt b/x509_test_certs/intermediate/certs/server.crt new file mode 100644 index 0000000..f3e42de --- /dev/null +++ b/x509_test_certs/intermediate/certs/server.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFMTCCAxmgAwIBAgIUEV3th0jZ+9ES/h0cXgfAsp1J2gkwDQYJKoZIhvcNAQEL +BQAwbTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xKDAmBgNVBAsMH0ludGVybWVkaWF0ZSBD +Tj1pbnRlcm1lZGlhdGVfY2EwHhcNMjQxMDA3MDU1NjA4WhcNMzQxMDA1MDU1NjA4 +WjBlMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkx +FTATBgNVBAoMDE9yZ2FuaXphdGlvbjEPMA0GA1UECwwGU2VydmVyMQ8wDQYDVQQD +DAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz75g4xbUA +hgHJXESHGFNwde7TDB5AZ0pt/fipfRzYSPfUWDO+4msmk6oeYTAeBcxthGjwcz2K +HYklbY/9nWIvGiG1c99atQ9a1Fu/XXRU7Y+qbZrV9gxhUyzsGNXibHVfVJz6T//P +oCkh6fhPCEMKADd+F+eoRxmyAxCPdMd3fXHvUN1lo3n/as74ZsW6nVB0VajAVgDm +OUfPGKx5kwmWd8UZ/+FZm0WWqXn4XyYr7DIiTLVJdFMAqtveUQbWgDDNLY3JcqAO +j7bZ7XcCCmv1uFEbBXNIai3rICVyUpRQwee6uk1R48N+OPZfVVWquCjuOLLozsHe +kQ6Zype7WogxAgMBAAGjgdAwgc0wCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAw +HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMFEGA1UdEQRKMEiCCWxvY2Fs +aG9zdIIvc3RhdHNpZy1mb3J3YXJkLXByb3h5LmRlZmF1bHQuc3ZjLmNsdXN0ZXIu +bG9jYWyHBH8AAAGHBAAAAAAwHQYDVR0OBBYEFPd4Y74prW6/PmLqrBVhCGNyZrPp +MB8GA1UdIwQYMBaAFK0CaZsfj51SY/cq8GJLFkPR2ViQMA0GCSqGSIb3DQEBCwUA +A4ICAQAgUXV2cKqAj1H342js5nPYMXW5VCJ5rcOY9xdslTDKkdVBIkjmd7GHENNE +7JbRcxhfqe62edM0jZwr6PZwHKTR2MHQ2s5MH6Jd+X4PXNvbT58pW2U2mXwUFcMn +CY98YMpuvvmcuxFuyR0j3c4qCwaDs5yjwLw6hvx1/F/b71n4fl92Dw43xrNsbmgl +ZWVHq+l/50d7zyt82Cs8E7ifJe27rdT19zWofSKTATBjty83E6FPP/bZvq/y7q2K +Xx+MP+ROoIfvw9I7h1sN2AiBFM/x8zPVzY5hOaDiG7K32CAWPxpbFdfTFT3xRxVh +3zjP4Ie/85MZ46gaaDP2Hz1bBFg5mhjhCxtOXk2nbK5jKOTMr3MRAWDkm9R/VyR8 +9spC60iRW2naRLbIxcjFwuzFGrgQFj1dzY2H8GtyLMd0fQADqsLMTU0++cFnlFjM +PPBv6CGNZYitXEWlqb5VEfso0l0Jfs5cMCAIhfCZfnUosmhl4d2tTVPpjdF1ap5R +ZJQSG8w/3LurSNSjZPkJLzn8UdBE90PkGh9xGKhh/ug7hK2vPLSWKSD/+IA1SpYh +pYH9/aMVxeZcTkgbFIVKO0UBdlW+SsMHR9mJjbr2NbHizAzBaw8akb9XDhqDaXpV +QvEoEXYHBJOm8VIMz/Dfws7MmyXqhoX7r4yFv7rJ84oynqwnLA== +-----END CERTIFICATE----- diff --git a/x509_test_certs/intermediate/certs/server_chain.crt b/x509_test_certs/intermediate/certs/server_chain.crt new file mode 100644 index 0000000..e691f74 --- /dev/null +++ b/x509_test_certs/intermediate/certs/server_chain.crt @@ -0,0 +1,96 @@ +-----BEGIN CERTIFICATE----- +MIIFADCCAuigAwIBAgIUEV3th0jZ+9ES/h0cXgfAsp1J2gMwDQYJKoZIhvcNAQEL +BQAwbTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xKDAmBgNVBAsMH0ludGVybWVkaWF0ZSBD +Tj1pbnRlcm1lZGlhdGVfY2EwHhcNMjQwOTI3MjA0MzE2WhcNMzQwOTI1MjA0MzE2 +WjBlMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkx +FTATBgNVBAoMDE9yZ2FuaXphdGlvbjEPMA0GA1UECwwGU2VydmVyMQ8wDQYDVQQD +DAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCe9aM1L8jg +ZjJ6+mPgKBj/O49TwtaQEkuV0uZPNosKuoFMqxi/A94CRsDDuhNUXDcxWEVHDh0B +WqNBecXUOKnBi03Vb8lLLNZ54aFFQn6Muo3nWKYLnr2ZaMFPXnHCWAWR/E7cAstT +rXuISynN2uORJu4erd4aEB84YtHQj0NSbNwdSCLtpuXOa9qO9yTNilqY16aGdvVm +WGsIaHJSd07xgxNpkrvPDaQpmOP9aK0JDibw9AksI26nDDNkmTIEXvTStaD5Ic4W +td63J7EYFzEvfjhMQ2lih/DKfS+CVEATqcRHViLiBDsKReC1fI6R2830K0eKRwEM +dXHJvChz4SOLAgMBAAGjgZ8wgZwwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAw +HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCAGA1UdEQQZMBeCCWxvY2Fs +aG9zdIcEfwAAAYcEAAAAADAdBgNVHQ4EFgQUTtYgxKoBXTSRfZq4/AfB84GyZwgw +HwYDVR0jBBgwFoAULTu6CHUb2leOp4rcaHN6ulIHcOAwDQYJKoZIhvcNAQELBQAD +ggIBAG3gjiPkoTQFVs/JJiNC3utdeGjlpfV83EOW/QCliR9E66th0x62vlosjZV6 +x1HRbPf5Tza/TQr3sOa6IIybSRHH7KDrTIhuKv3S7Zm+RtvLdWmgSGxt56mQv3rH +kkNrHTZXAaxG960W0qZsLifkR2atNLB6c6fTpMYq1w4qSZ0Lkr2QPl4Rs3C6Tt2i +LJMwTUU14azSH7P3Br6qUGf99CrgvqHJN1kFXhv9CGSp1tdCUEEwVPvkzb/e+GBG +/4fLYLy01zK1p7yUGScpTt0IYvkmibimfXcs2C6wXCS2hYUS4OGo+RftD88xqhgX +NrislQkRsJJBPaqCizuxbcQdOR7uO+Y86ge7gxirOuwIVkVRxHmSjX8NJ7znK/jZ +6wuHG2jVEmARRPxdicfjbT35o434cbm0+dHWqGJKePRiiuqgvqxaYNh+siUbtins +TRWR4N7rM3lYK44O2cDU/c9eqsK2bmmbh63CvD/+Vh1keqKbgwXeo3L+euHq//2U +INeOHucMORP1LigXDK95eTm7Qy2Z1PL3CvFzUw8midlp806uqT5sTIQjAKRj+Co+ +fxDHAtuBFq9mFawlnFnNDcTLSOryK5vfAj+myJkCU3ktVOoiWcWrF+ykvEkkfUb5 +OCr9WksCgK8qMebPXiVs207fehebq6py4woUW+rDgyWwXupC +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGATCCA+mgAwIBAgIUXOZaiUcbnqv+GLKy+6SlLThCOtAwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMDQzMTZaFw0zNDA5MjUyMDQzMTZaMG0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMSgwJgYDVQQLDB9JbnRlcm1lZGlhdGUgQ049aW50ZXJtZWRpYXRlX2Nh +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoRTt5pjqzZ1NZ1HSHZas +XH0aSRvNy8yB/IbQZPKePytK251ZDdBYQzicdtJObDj7fZHjG+0vQO5FpBQVmI77 ++izyceysx9fPnJ6pmpaP9LXQ2aYcJue4VSNGiK8ulFMnKHCZareNDmTW6NaQx4n6 +vRlAkdh1yzYdK+vCWnngVfdQAUpFMfMa9D2bjGdkOiLWFf7Aez7Eue8exdF3hFzq +5VPCF8cXNzYa4BWsS54xGnfgrW6pFwAiMU+mLH8k5eQ7b9N+Jcx4s9aqE/WZwlFC +octHRTCXBTUcrMC5GAlJOLwIp6msHjNZ4CTNhurSh2EKU4s+lxxdCUUt0ZLePAwf +U1BxkM9cps2zgUaI74mNlLr/VSdPpz6nhsNZIgAaII7KSQAK0isXxqA2NN1yOI7X +r6weE+4/NpBjxsalZXE9DtBhhkh0uUy4ngOzOoh3J1+8YFHHdwQ3rDJWhXWvwMzN +j12WGcnAz+nXoFIV0Yqe0ZvC6w05itDHLIH+WEcRf99aX1rxc0Z0ynPLr7a+Dxyr +WRX3ukdb/gyB4cc2w3mbrpdOxQvIr9nSjaRqLSlKdfqrK5RJKkIeEOMR3XYqd782 +irCNOwDkvJEOba9+M2CWQpOHpjNyb0KjjF2MuQfVAuA57OFrpY0OCmqjuQs4ltTh +lOIVwjLyDZmWqKmyQjWmmScCAwEAAaOBqDCBpTASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +IAYDVR0RBBkwF4IJbG9jYWxob3N0hwR/AAABhwQAAAAAMB0GA1UdDgQWBBQtO7oI +dRvaV46nitxoc3q6Ugdw4DAfBgNVHSMEGDAWgBSYnRAKtBf2Hjy9JWBy5OgT/5f0 +ezANBgkqhkiG9w0BAQsFAAOCAgEABCQvf7K6oRv0+zGxNO4wDc7s8x+kdOmYXDqF +hpIEK2ObRP/o/TIGgLtR0T4RCAab9QY5f4jBh798iQ07UdIt6Fso+tCdt4na12nD +9IFHQnC+fFl2tfSGTvLO7ierYZ57OvZTvIW0Q3KbEXbBjogPl1+Dwr/nunf75Y7C +mdGwrnw0/sZju0cKJQos4yZFVew+Jp+mnDdMgB+nTbSkSI8sVYm4rMIzYcHpJhRw +nYS1t5fSg+lc4Ol2l+y/odHZY72sji7HXIv8IUtU0SaOD8xCQ6KltnHiGxspLwJf +3E8GdLF0JcfQcRRqnmr88JJm7f1rNZ6b4rjy+Iy2TNvusAEFGyCXL8TFrU0rCsyI +THzVhmgi76NzX0PbpRmVapNx728ZVQOF0GYSgZ/Ayc9wsMIl/cEDFhFbeokecO4m +2PthBHi9+cCeou80OErpdsvWbeDP6PRPxNWHyA9f2HSRBDnqje+kw+jj06iYOzr+ +z2GUGJGVFW6Eqc4gt6gkypGIw9YLdf9ZQW8SGBVKZXS+r+uroA3/aDGJI2IUjAVJ +ta9iTM6j/27X3bKnOr3sazIs2SUKAkxZM1ACbENj6TSFgSrGG8VAa4B8JSthN8Wt +NjRB2wG5THfbtc7g8nDIbuCmCnNqM9XsI7e2c8Iym6aKdSxISqO3WrGQK3Zdh1sa +nVXYOIY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFmzCCA4OgAwIBAgIUIxuw1633tmmQpV8g5S6zT4EteIwwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMDQzMTZaFw0zNDA5MjUyMDQzMTZaMF0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMRgwFgYDVQQLDA9Sb290IENOPXJvb3RfY2EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCqJA5c58aQ3JwzE8jrLyJgR7x1O0TOFboI21lM67bO +0w16y6GYvT2mBxxy96WCf9uqq/CCgaOoeEwovdWBo432vV6YkrDPOcRhja02Kr9t +mobwIdRmFbpiHlTWxqT/oP5XsxGVU7t+wqR+b70xPcdTJbB8FwmsnvXF8D2Powjg +oE/6uai5WFXraBY5/dwl1ZBiVCpaK1kbQTX/F4c3lMgJnU7mD8Mlqo0mmkqEpX6D +5TH71gl0R8Ij2dl1ziGI0azsJJw51RRm/u11gr5LTZFMIRFMo21eSCrU9Qrm5w4d +8AGDP0X5JwX5oN8iNuIaN1LAuWuHQdZK3bbf1wrsag2gqfUbZ+zBdqlLJRiz7xuw +mh8VEePsn9SkvWhSSNg10GL2K6Zv2SLDdOZ5JOLySLNl4bHRuwKqzBK8WVJJtwFf +H9e9KbxP7TuFNaPT275lq73O1KnMWvX6/aGqyHzXcy9fewKun3ELmuDUjY4qePyk +w8hwIhDCaCfFzDwYX3FHphKcfT5m1uJDeH+DO2gehc3Q+hgXO3DUYKM5EwwjVFjV +yCcu8YsTov8/Rh7jCj8Rtb4BYP3TwQtAhSEblAUS/DnYPNzten7ebr9tO+hyMXrj +C5Lb+59Fgm4a8eWEi7p1HjzHpclsEfhGw8ZJCRm01bmbdahYdYxUCm70grgg3F/M +yQIDAQABo1MwUTAdBgNVHQ4EFgQUmJ0QCrQX9h48vSVgcuToE/+X9HswHwYDVR0j +BBgwFoAUmJ0QCrQX9h48vSVgcuToE/+X9HswDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEABJVTACCQNuh/glRfov50PmlEHCPFV6relxUFf1T6ulX5 +m/LfowcvT20V6FEY2l5orOXIZO4kRFt9JIV1RwVaCCA+zgKibF0nV4mdWisSJ7FC +aiDKP12WB3beTKSe8WPcRTbHe8OcFMN78Z1VHLB1fLsVvcQXpZr1IqV7sDhxZhM7 +WstmgJXkprZC1PvsJ1bjRi9Py7dMgS7dkbrGXpVrBHAOMceWuABwzNOh8NTgU9X0 +lndjxvqpUbrksEHNrTSfINnwH+EBoeNdScEWSVGTmFg934i7uOXaYSZ7xbNFORBA +0WYPgl7aIkmrMPsAU2p3zmmiAwQkFnE7TaMFKeZQkTU1jQKtZ6SOseEE0UgFSixd +RF7u0BKcATBAFwgIo3LLYrxRVY7ex9xnm+GVH+YLpX0GwFHDOuAdj153jft1uXD6 +Fbl/z+8mHgQTxJtY7De6CDH6Pd7FU9I8oVfxRGSXZfCPCAoqHmSprNnW1bCm2/t9 +KrHtHK6wCnQ5FgrrtTTlWHsJxyRKRJwJeAp2PihJ7MGUHn5Qz1TsdFbt81XOpm+J +mBwFWi5CVGiI+SodkT9G9kdiHreSccMPizZDZDN3QevPSBWN01YCia8SBa+xfWbr +/OKEiv8k63CZixHLFfCI61TOwxAXoEQylvQFSbrf9p4uVcQSW/uMI/V9bURqBPQ= +-----END CERTIFICATE----- diff --git a/x509_test_certs/intermediate/certs/server_full_chain.crt b/x509_test_certs/intermediate/certs/server_full_chain.crt new file mode 100644 index 0000000..0b762c9 --- /dev/null +++ b/x509_test_certs/intermediate/certs/server_full_chain.crt @@ -0,0 +1,97 @@ +-----BEGIN CERTIFICATE----- +MIIFMTCCAxmgAwIBAgIUEV3th0jZ+9ES/h0cXgfAsp1J2gkwDQYJKoZIhvcNAQEL +BQAwbTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xKDAmBgNVBAsMH0ludGVybWVkaWF0ZSBD +Tj1pbnRlcm1lZGlhdGVfY2EwHhcNMjQxMDA3MDU1NjA4WhcNMzQxMDA1MDU1NjA4 +WjBlMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkx +FTATBgNVBAoMDE9yZ2FuaXphdGlvbjEPMA0GA1UECwwGU2VydmVyMQ8wDQYDVQQD +DAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz75g4xbUA +hgHJXESHGFNwde7TDB5AZ0pt/fipfRzYSPfUWDO+4msmk6oeYTAeBcxthGjwcz2K +HYklbY/9nWIvGiG1c99atQ9a1Fu/XXRU7Y+qbZrV9gxhUyzsGNXibHVfVJz6T//P +oCkh6fhPCEMKADd+F+eoRxmyAxCPdMd3fXHvUN1lo3n/as74ZsW6nVB0VajAVgDm +OUfPGKx5kwmWd8UZ/+FZm0WWqXn4XyYr7DIiTLVJdFMAqtveUQbWgDDNLY3JcqAO +j7bZ7XcCCmv1uFEbBXNIai3rICVyUpRQwee6uk1R48N+OPZfVVWquCjuOLLozsHe +kQ6Zype7WogxAgMBAAGjgdAwgc0wCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAw +HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMFEGA1UdEQRKMEiCCWxvY2Fs +aG9zdIIvc3RhdHNpZy1mb3J3YXJkLXByb3h5LmRlZmF1bHQuc3ZjLmNsdXN0ZXIu +bG9jYWyHBH8AAAGHBAAAAAAwHQYDVR0OBBYEFPd4Y74prW6/PmLqrBVhCGNyZrPp +MB8GA1UdIwQYMBaAFK0CaZsfj51SY/cq8GJLFkPR2ViQMA0GCSqGSIb3DQEBCwUA +A4ICAQAgUXV2cKqAj1H342js5nPYMXW5VCJ5rcOY9xdslTDKkdVBIkjmd7GHENNE +7JbRcxhfqe62edM0jZwr6PZwHKTR2MHQ2s5MH6Jd+X4PXNvbT58pW2U2mXwUFcMn +CY98YMpuvvmcuxFuyR0j3c4qCwaDs5yjwLw6hvx1/F/b71n4fl92Dw43xrNsbmgl +ZWVHq+l/50d7zyt82Cs8E7ifJe27rdT19zWofSKTATBjty83E6FPP/bZvq/y7q2K +Xx+MP+ROoIfvw9I7h1sN2AiBFM/x8zPVzY5hOaDiG7K32CAWPxpbFdfTFT3xRxVh +3zjP4Ie/85MZ46gaaDP2Hz1bBFg5mhjhCxtOXk2nbK5jKOTMr3MRAWDkm9R/VyR8 +9spC60iRW2naRLbIxcjFwuzFGrgQFj1dzY2H8GtyLMd0fQADqsLMTU0++cFnlFjM +PPBv6CGNZYitXEWlqb5VEfso0l0Jfs5cMCAIhfCZfnUosmhl4d2tTVPpjdF1ap5R +ZJQSG8w/3LurSNSjZPkJLzn8UdBE90PkGh9xGKhh/ug7hK2vPLSWKSD/+IA1SpYh +pYH9/aMVxeZcTkgbFIVKO0UBdlW+SsMHR9mJjbr2NbHizAzBaw8akb9XDhqDaXpV +QvEoEXYHBJOm8VIMz/Dfws7MmyXqhoX7r4yFv7rJ84oynqwnLA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGATCCA+mgAwIBAgIUXOZaiUcbnqv+GLKy+6SlLThCOtMwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMTIxMzhaFw0zNDA5MjUyMTIxMzhaMG0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMSgwJgYDVQQLDB9JbnRlcm1lZGlhdGUgQ049aW50ZXJtZWRpYXRlX2Nh +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA592XixtZoUQnUXnX76Ye +R+WsKyTtYcCtQteiZ5e7lH8d6hvSE5JXNiDxfEpwunr/2SOGBxU/8hyLQuKkXd+C +nDtiVYk68GknsgiIC+2y6QrF/OFZsVrni63BYYeMSqLd+K8FNufNSvlkUPRBZ+aZ +Iy6/nQHT0B0zOVmTBZ/3tnwsvvnfM9TUl9ajz2S/mCSj1/OEq7l0y2QkEegrGN1o +YjNkr3wAl6EKr5EtchVlIVRAD+2vzCEo2fwQZhSDyyzO+twsQxWVbV3BTDR3QMhu +2r0NoGpLg+3gPu5WJ53RRZ5uPSNxl1HTVtZOireD1utyL0rv6JawUi1MRdHdburm +1HAbEEAUGES1N5iycA9M1igNBYjteS2y3HniZJ/KioIWapYw6xLEHETTCB+o/kWO +dAwAoOwNbitj3jM32TKnUmzmyNziIB6GVcnJW331U1sjZEJnR3332aAIbUj3u1bN +tGmY59xkD8AKdivZ2bB7Xu0lOFzKDMFiMMVpVL3f+21SyNMOkPsisFkQV9LiuYiR ++y+tPejGb/H6/iDPzf26Dhx2qUz28JMuObEuLvRlxkXE4nroR4w8bkubua64kIDX +55g622WjiTg0ZkpHAPKN8eN9qT8d+uqYMYAQosymameYzcGtEAs6z3ksUivcOB7J +/sT1Svn0CFKSAC4mREppGlECAwEAAaOBqDCBpTASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +IAYDVR0RBBkwF4IJbG9jYWxob3N0hwR/AAABhwQAAAAAMB0GA1UdDgQWBBStAmmb +H4+dUmP3KvBiSxZD0dlYkDAfBgNVHSMEGDAWgBT9/KhrWT9ey/WTI2rYN1+kL0q9 +TjANBgkqhkiG9w0BAQsFAAOCAgEAHl9yzoNhJACIEtlQYb3HvAWQBiIM9A/W4/je +4ENsZp2EIFEW+pXvTtXxkbdX0LFIeJ8Ut2Fjici17AuJe4CPZ4ccbbkwvkZGwopp +T2k6XqHKZnDJQEL4ypj34o6nFnlGqkFM+KtCiMR0EQDDB45jjj54ayWrLeNECeE9 +Q3oufjVqd+07+VbSKAmLqTPTgH0r/B1aX5frq0UCk9J88N/ykYLWboDXLL7Ivm4N +cfdVnayo8d1Wk5yPbriKkV1pDNEofg7pcKNFepBibkIvkWr7ir6usDtBqt/FAM4L +FrMKSD0W49e56VSAZSHlyFiQyQyGt+rZ97mRMxGE8GdDVBBTBDXI2yxBck7d+U+T +HEC/LyDvYToiUsG8chkUi/B0ntctEZwen2SA26m/A9Q5BE6v+L09U/MoboSFnhul +DgJosoAAbixICxuzrnJbC++0dgg1ING1fp7XcCK/GNmhddHN2w+m0SoGy+2h3/YP +euzFUQblVIOSiA6uG6QZa++tLHKNm4KovaUHvgfY6zjYLWNJNrQxYC0AfbDOBaVi +GIOowoUOMXSowbcPNroC2YJ/UJQusB1nMnXp7xz2YrFBN3+wQOdJRaiVfGpW0Qrq +sDalsoltC7UTpjR1ue6lvyiYMQ7mXkl4Xa/1gPgjJMP744jmI7fBPVht643L/bbm +/GHStM4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFmzCCA4OgAwIBAgIUQEjtec3bO/kDXxGz6WHeSHdc6GkwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMTIxMzhaFw0zNDA5MjUyMTIxMzhaMF0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMRgwFgYDVQQLDA9Sb290IENOPXJvb3RfY2EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDAyvwNu/yBG+xQY3tsUTLIXOnZmrldbPC+3hmjb5ZH +ovjQK8/en2YCzv6YDRduSXmw87LNgPaj4/R9OrnX7e8bdAJN8yzKR2UxxBVnLYxt +yzLxiBL9yFEvAVmLt72IfwZ1pR85BH3s/O+5enWfM7O1xpS8UsekBJsWXvkvUrfl +c6D1YnpBVgVIB3267PKSkXbT9N8rW05uBTohIdT7gTrx1C3nUVzwxQylhCWCCD9J +DHNIbbsWxKFw8StNOU6zZ/Zrsk7y6TB6pYS9WggNVPEqOa0vl79On5MtWpaazkH8 +4Hqjjl8qcoY4yKOPrh0kUhKWsuQPmkIqsQX0fJjYsthoUjvofWcFwUWWGe64Dgu6 +faU9HWs8kqZH4wIcBr+6Il4VAw5yuQlD8lzwhvMg1TNW9QPR4JmeAIiDG9u6Tpot +s2LuUYna/DzsxHuKNFn/1nwJDm7CyZ+rGzjLhKlVH7F1Mnmo7mMfKJGgh09hxoz5 +a7ZFL+ybFe/j4bOZQiMBMdVk06tmQtu7iA/RUSVD1w0Lzz9hr0hwqYlCMZHaeyZw +v0Xa73ypzu/UCVz5yxgQGZxGHMBkiFm78tTmQeqtAfOi8EzgNq5idSXp3dTn2z3z +UTlnsv7B1kBFBKLVDT99PsEXqrCrWDfJkoD3788uA8gI+WjA20gSdVyRqBRSOHrp +JQIDAQABo1MwUTAdBgNVHQ4EFgQU/fyoa1k/Xsv1kyNq2DdfpC9KvU4wHwYDVR0j +BBgwFoAU/fyoa1k/Xsv1kyNq2DdfpC9KvU4wDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEAfIf1eK0Nd53170VAwm4qc9GKNbK0C/RH1iysnlGm3KGn +IimUr4Nl3M9rR5WWfNftctxeZj/g2ALBfF+598VO6ezp6PYn1inA8ZOa5lBuxXPi +z4JY1BOEIathR77v+nA4W2YFIn65B8O76+SyTkDRsCprSyIVpjE/FdHyoEqciK5f +5QDbaELG3n3UTgfwJTKoug/xDQmxu/vc5Y0Y9z2j6PsOYIg4eC+5X75CLjuH4KGG +7Newrsu8jQi1QmlSTsWN5iQa4QfvILbq6BznlOPB4jnnD1/lSn2opPLAZ622OrSC +41BrpTNsDdG3yrzxxyxo3u7flOd2fYoq4Opvdr60ZQuuee1FeONOJmK72q7DvqxZ +YmT7+kgL4NyvmXGx4diBsMygdL9M2k6U3O7HaitdiY/a28AuOWKRuseX+Pwh3Rta +9dJZo+YzSiQ1DXf+ddbxWjji2diW4CpyVpkwgTq8KOUbbDWm1xeZHQAicsTS3O5F +LvNXrJ54NrTsGMA6wh/AOfjDfcwly2CBW3envSmQHr3Ywb5E/dIRtkNcp09w6G9f +kO5eMAn1RKm7tjqFEoCMwkF2PsX8wXqIi5TiFKoHApFodFZckpsJg0bRQH/B6de7 +31Nqyc4RFh4axbp6RUH7+oFv0y/Pi8dufYvGaWxGwLO2OnVUg2mWDu47Z2wI1MI= +-----END CERTIFICATE----- diff --git a/x509_test_certs/intermediate/client.cnf b/x509_test_certs/intermediate/client.cnf new file mode 100644 index 0000000..40d0571 --- /dev/null +++ b/x509_test_certs/intermediate/client.cnf @@ -0,0 +1,19 @@ +[req] +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no +[req_distinguished_name] +C = US +ST = State +L = City +O = Organization +OU = Client +CN = client +[v3_req] +basicConstraints = CA:FALSE +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth +subjectAltName = @alt_names +[alt_names] +DNS.1 = localhost +IP.1 = 127.0.0.1 diff --git a/x509_test_certs/intermediate/csr/client.csr b/x509_test_certs/intermediate/csr/client.csr new file mode 100644 index 0000000..620205d --- /dev/null +++ b/x509_test_certs/intermediate/csr/client.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICqjCCAZICAQAwZTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYD +VQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xDzANBgNVBAsMBkNsaWVu +dDEPMA0GA1UEAwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAm7l4MiB44NOD4Pp4xXDBF2mmmzAQiyj8v4ZUWKNsBuBqF4xBGUl6q18oMM5m +W6EwNSz2eEabyvgX/456oOtFN6HVPOvvHdRD21rkZmHmgIA9UKGTI37iewNyCshf +V73dK6HaXwYisYM3gXFoTYdK6KWhlQFhWxVVvedf3/nzPTMGePNwTvU7fOY+iPMQ +/qYCJ5eSvF3ZuuVFjOki+pQnwt7xScPvfD5QHQwE29QzYCMocw9fm7lHNsXvFxs2 +eHQOUz1TKv0SmIwhTQByCOFHnahLcIymACyfYWFK/upMUHOUeMaPfjjPG7OnNDfB +Mj5VF6NjJHivecMYouw0ROdP/QIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAFf1 +1rde6uuuOChhOdDR6/zxl2M58jk9c8fMBPoPQTaUj2TJILy74polewiX99vjXOyD +jmL6YWcF8PLOkIaR7WMdOsoaY161Tx10G2WnPALRP7sSlLElhnJeqUd1B2OQ2y8T +LuBJ0dQqtk6pQK8sDlltrn5ipAZ0dAsm8GC+t58hZ0gyOtqVTI7khZaNxov563eo +evE2Xmn7vRv1jhUn6oZGDUqOSzpnvWB2qsGGeeXSiCud2QFg/EBt4hkWbY9HTqrm +U0KnbcChoXJT5X9TyRhkfh897qE2o6hHo+T+M8yLXT44r2Rnc6sJwLc6wQb0PjE4 +ZaR5jk1pHZzs7JaLSNo= +-----END CERTIFICATE REQUEST----- diff --git a/x509_test_certs/intermediate/csr/intermediate_ca.csr b/x509_test_certs/intermediate/csr/intermediate_ca.csr new file mode 100644 index 0000000..6852a20 --- /dev/null +++ b/x509_test_certs/intermediate/csr/intermediate_ca.csr @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEsjCCApoCAQAwbTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYD +VQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xKDAmBgNVBAsMH0ludGVy +bWVkaWF0ZSBDTj1pbnRlcm1lZGlhdGVfY2EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDn3ZeLG1mhRCdRedfvph5H5awrJO1hwK1C16Jnl7uUfx3qG9IT +klc2IPF8SnC6ev/ZI4YHFT/yHItC4qRd34KcO2JViTrwaSeyCIgL7bLpCsX84Vmx +WueLrcFhh4xKot34rwU2581K+WRQ9EFn5pkjLr+dAdPQHTM5WZMFn/e2fCy++d8z +1NSX1qPPZL+YJKPX84SruXTLZCQR6CsY3WhiM2SvfACXoQqvkS1yFWUhVEAP7a/M +ISjZ/BBmFIPLLM763CxDFZVtXcFMNHdAyG7avQ2gakuD7eA+7lYnndFFnm49I3GX +UdNW1k6Kt4PW63IvSu/olrBSLUxF0d1u6ubUcBsQQBQYRLU3mLJwD0zWKA0FiO15 +LbLceeJkn8qKghZqljDrEsQcRNMIH6j+RY50DACg7A1uK2PeMzfZMqdSbObI3OIg +HoZVyclbffVTWyNkQmdHfffZoAhtSPe7Vs20aZjn3GQPwAp2K9nZsHte7SU4XMoM +wWIwxWlUvd/7bVLI0w6Q+yKwWRBX0uK5iJH7L6096MZv8fr+IM/N/boOHHapTPbw +ky45sS4u9GXGRcTieuhHjDxuS5u5rriQgNfnmDrbZaOJODRmSkcA8o3x432pPx36 +6pgxgBCizKZqZ5jNwa0QCzrPeSxSK9w4Hsn+xPVK+fQIUpIALiZESmkaUQIDAQAB +oAAwDQYJKoZIhvcNAQELBQADggIBAGHm0V4S3MInTXMHPUVZZxEyPVzO+7R0Q4L4 +wbgpgEsoHKe4fa6YQk3xsmlkWB4ooqDcgN3Kw9KMX0Ztrj4/8OE908g4mUx2q4P8 +PuaWCIMi2ayzqP30uvHER4mDUrY9KDUVGo5ZR1HejgIsMHwuPSPb7eQAMeFdWH6R +WTdqn7pQMQS17mS3GfLB7cW8LOJ11E6v1dtdfNnM1rhHiaowKD3iIL8cmgxBtwQS +mzqFsfDeHQCr3oTXQkdkr5eB/qCXmDKPEnFOf7CbVUoEegCzL9NldyxXhiR/zsQk +Dm2X3uZC7nJewBDM2QxhVjvYPAyKKqaLuAfOA7v+3Zi4h9A0MaVU5asP9iG8jYnB +Di0GU4uodzcBhd3790LJr/56t7GERISfV/AWU5oXYT2WQS8erTcOikrJYcS66fae +5Qpm6OinsKY7QemUQGhfu/pZRgaDjVw6SHW+S4Xy0KMO73hsxwvG5VROeEbGJ9/E +ZWss4dj11y+p7/ceIFjUbfUJDbWtivToR3hBZnFLZCUwmSfkmOOuK/lPlGguM838 +v+BSrBGsyMtBt/EyUObHf5VYupSKNDcQgsmf8MuEDbFdWduQL7y/p7bMRBdC5QnQ +1xkrfdF8u2eGDmGi/r/I5WeBCedvyaoON4ycgZXpZ0cBuOpJBvzPjI1WRRonWOkT +MdgPY33r +-----END CERTIFICATE REQUEST----- diff --git a/x509_test_certs/intermediate/csr/server.csr b/x509_test_certs/intermediate/csr/server.csr new file mode 100644 index 0000000..9c206e5 --- /dev/null +++ b/x509_test_certs/intermediate/csr/server.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICqjCCAZICAQAwZTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYD +VQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xDzANBgNVBAsMBlNlcnZl +cjEPMA0GA1UEAwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAs++YOMW1AIYByVxEhxhTcHXu0wweQGdKbf34qX0c2Ej31FgzvuJrJpOqHmEw +HgXMbYRo8HM9ih2JJW2P/Z1iLxohtXPfWrUPWtRbv110VO2Pqm2a1fYMYVMs7BjV +4mx1X1Sc+k//z6ApIen4TwhDCgA3fhfnqEcZsgMQj3THd31x71DdZaN5/2rO+GbF +up1QdFWowFYA5jlHzxiseZMJlnfFGf/hWZtFlql5+F8mK+wyIky1SXRTAKrb3lEG +1oAwzS2NyXKgDo+22e13Agpr9bhRGwVzSGot6yAlclKUUMHnurpNUePDfjj2X1VV +qrgo7jiy6M7B3pEOmcqXu1qIMQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAI48 +bAk3Me4IcSbjYE+ub/c2yMi0uv3w9bvfS7hyZCpDHfSKuV2BxmEaxhbG8IxUGv3f +kU7q41kNsr//7d8sWcxWDx5ZomWsWB4YTU7MGUOFiH9ajexxt3tHUOoknOrd22ng +fAXG6vmAVT3ltbVTJzzb3WGww2Cz0+oAvvex2wE1qMXBKHBqTwh9gSUBDn5WRx3x +XE0EFlqTbCv+J1EBvlsJnd1bavvagSnF148wk5YUD0yJHvZPnN6Sik0LPNc2/5sv +XPfQz2l7rAxEHzT191HSJPbWbw4RNIY/8Kdn3jsn/b02SXWKlq4uvwHKMHwrk94b +GCH57eifaDqSmmkZISU= +-----END CERTIFICATE REQUEST----- diff --git a/x509_test_certs/intermediate/intermediate_ca.cnf b/x509_test_certs/intermediate/intermediate_ca.cnf new file mode 100644 index 0000000..346c547 --- /dev/null +++ b/x509_test_certs/intermediate/intermediate_ca.cnf @@ -0,0 +1,20 @@ +[req] +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no +[req_distinguished_name] +C = US +ST = State +L = City +O = Organization +OU = Intermediate CA +CN = intermediate_ca +[v3_req] +basicConstraints = critical,CA:TRUE,pathlen:0 +keyUsage = critical,digitalSignature,cRLSign,keyCertSign +extendedKeyUsage = serverAuth,clientAuth +subjectAltName = @alt_names +[alt_names] +DNS.1 = localhost +IP.1 = 127.0.0.1 +IP.2 = 0.0.0.0 diff --git a/x509_test_certs/intermediate/private/client.key b/x509_test_certs/intermediate/private/client.key new file mode 100644 index 0000000..5962487 --- /dev/null +++ b/x509_test_certs/intermediate/private/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCbuXgyIHjg04Pg ++njFcMEXaaabMBCLKPy/hlRYo2wG4GoXjEEZSXqrXygwzmZboTA1LPZ4RpvK+Bf/ +jnqg60U3odU86+8d1EPbWuRmYeaAgD1QoZMjfuJ7A3IKyF9Xvd0rodpfBiKxgzeB +cWhNh0ropaGVAWFbFVW951/f+fM9MwZ483BO9Tt85j6I8xD+pgInl5K8Xdm65UWM +6SL6lCfC3vFJw+98PlAdDATb1DNgIyhzD1+buUc2xe8XGzZ4dA5TPVMq/RKYjCFN +AHII4UedqEtwjKYALJ9hYUr+6kxQc5R4xo9+OM8bs6c0N8EyPlUXo2MkeK95wxii +7DRE50/9AgMBAAECggEAAdp3P8F7LtHVo/vXLCJnaevr+7TavY1DVNQ4dMWdJ4fM +O3OD4tUpDJkZVVtfhH+qnzdlx5Cvkf7Z6kS72OZz8HGQpCT6t5QrKpUuWrmXkLf2 +AtupcKa181jbJp2z5Mtxsm8+thrQluRE6Nl8sE+H6LTv82IM0pSybIwwa+4BoxzP +mRMcpO5/Zo/cuP/WlsToW9cra16LQi3R74mnaApP4JGdB82NSRWA23FmHiGNyXJX +qdAcCPFvQ8EmF4RQIA/sj7MATKTenNDOwEvoRdLFwWNOoSbmvc+1RuD+XRt1AZAR +yqCdD6NTkAGlCWkdAc4ZsNrc2xqlD/UkQBsiKLxNgQKBgQDQe0Po+BjYxpF0kAqo +MQSoQX6c+5130E1payttvd6wf7htuHwUBu6cv5yOgFA0ilyTqB89OGvNbOWhrnkT +g/kikUQr1A23GusU0V1S1t6jObnWquhn5GmAR/7uquul6DCVDFqHyD0emYGMSXap +VvFYRrYOpS+SJIemLlumc4kq9QKBgQC/N9+J814+6xYCJAv9m+iHmhvUGWD/N7IU +IzBFF2LJBkV/Kgh8Ee86RmCCU78Za2URda3jBuxCQoUcnatxmeEdxL4Tw4PzOY1/ +phZ2lhkZLcHQhM+ZxZ/5fbnpbFc6a9jfB9u42+2Cx2EGONFdyobqx44+2LimUzIw +Z6OAu4376QKBgAdr6+32LwRAhVsN6aM1I4JN2pECEAf83VScYQ5mDPBhi5I0WA5q +dMIVvCACh1hneIY1HO+T+5pzfvUzfdHpB8xoOXIJ+XcEOUwja9wQFBrhajEvIljY +c70CUxvx77OXQWt85hghU7OsKUVyNAwxMSRAWCjLTpc4P6/1xIN3Z3GlAoGARlKg +DTXJhU4jq+nh1wUNgUE11sz2lSXAghNm3yH4nIGH8Xpd4HMgDchwGb3+27RZWRB0 +QLf9D2kzVxDNSlwTJEWEufP/hOW3mihUvov48v+W9b7CMoUxjimkw6mqcrvS6EMV +lWiccqosjoM6zEl9UL7PG2HMlq6mGJMk2GUhlHECgYAFBnQy7vfX/XoBMYpl4Qwg +O45BizYGAKiBri2BeW70J3mf7D1TTaoR/8aRfLD33JK3i2cU2xzLA0lnLT9VS4cL +6t3iSb9oWamgxUFZggHNfN4iEmj6cujMBxANgXsWQlBObBQHnslJpE2dynZd9OYR +q/Palb8sux4zt24t9kOZgA== +-----END PRIVATE KEY----- diff --git a/x509_test_certs/intermediate/private/intermediate_ca.key b/x509_test_certs/intermediate/private/intermediate_ca.key new file mode 100644 index 0000000..55af64b --- /dev/null +++ b/x509_test_certs/intermediate/private/intermediate_ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +8E3gFiRvwCqWl+fsNjb6Tu0Tyj/rrC7j +-----END PRIVATE KEY----- diff --git a/x509_test_certs/intermediate/private/server.key b/x509_test_certs/intermediate/private/server.key new file mode 100644 index 0000000..e303f70 --- /dev/null +++ b/x509_test_certs/intermediate/private/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCz75g4xbUAhgHJ +XESHGFNwde7TDB5AZ0pt/fipfRzYSPfUWDO+4msmk6oeYTAeBcxthGjwcz2KHYkl +bY/9nWIvGiG1c99atQ9a1Fu/XXRU7Y+qbZrV9gxhUyzsGNXibHVfVJz6T//PoCkh +6fhPCEMKADd+F+eoRxmyAxCPdMd3fXHvUN1lo3n/as74ZsW6nVB0VajAVgDmOUfP +GKx5kwmWd8UZ/+FZm0WWqXn4XyYr7DIiTLVJdFMAqtveUQbWgDDNLY3JcqAOj7bZ +7XcCCmv1uFEbBXNIai3rICVyUpRQwee6uk1R48N+OPZfVVWquCjuOLLozsHekQ6Z +ype7WogxAgMBAAECggEABCSfJ6d9vG5Zei5ysX2g5jUA88ESrT7zmsa/Q//Kezxq +4GvLfTivaOr7fsmUFwzhISBsXiT5JMX7U25LY3aNPTJn1kq9uNP0b432MtjPBemW +ZkEx0H1i2ZbVsPX9bsI6jaBgIO9Yn4o7iDlf60nwaTmKDZA1WE2SzON/LR5uPVUP +JCal1kbg/FAbIWbipXz7gt5ESbU5H159uZgl5NUadPVUd6jDMS1CRQjDvpG//XJ9 +j5R3b3Ijx7hi8hekta6rfVt6010e3geSvWz8cbntCHD2lVe9uZZ69aL9ONbitgec +9junpbHlyv9tug7gyhAeGKO6ESIdgjrrFHE+Hq+ldQKBgQD0PH9i89o7yO7olGim +kqN8q0Ika6alj5NFkskwXpoIpQAyxYiaf1sViZZYt4mBuYkKFaKhy6hltobzHqfn +q4zXtVxUrWuPfjRV1uHCV7MpwTleK+V7KZVL7UGbTe/UglTOhq9+1RZI8C8pOcV8 +gWlHdrc/INTHWCHQBgS4mUULfwKBgQC8mkFHZJoJP/gB9Qool9Ze7DswMHaNn6m5 +nIXb9dHvdX1v5KDdrjw1xPfan2oo6Lh9CBRGD8RBHq6Rozq2x01m/+boNqvskqhE +2bKphHJGC3EImONko80n3fvprvh0qpNFVovCjYFksBmgG3M0GmUT5Mocmv0X0NaC +CUjqLCIETwKBgQC51dZvqnAhEVCHc3T1WZo/3+dh4u9YBhje1UN0JGc7sKCoARlh +xQm/J4NYWb6tEEkvfSrTxSyKyAmCr46+fg+aigqI+7Nd6X9U2T3KLATM/pLyDqB7 +yDYrIYnL689SeSCiTGT4MpbURLz9t1GG3MkIcVIZHJgKXgrMR+gLtcONbQKBgDbt +uhtf2ljdT47KXgYKir2kkWxsgOqoWJcdVgME6fqSvlCrRoqppxGF5yW+Df4SIUEQ +7E6nYuIQXIk//+ahzxIzb32sBpBk+irrOFSUpW9u/6GgXYG/Dw4QYcDiW6wmzbOB +DD1CVzAK9buiov/GvC96D78bCKE6Cm9e4uSIq/OTAoGAG/7OWO8oXMTOfGSWmgZV +nMYzDJqWJHp1Eb0p+Wcvr4HTl9pMwIm/pgG3OghMIM1hJXTFBCADE4DNfu+LHShD +XQ6idoP+A2y0ChFUjceoS9Mqthrlfp+yj83v/SE9ARydJSPw8IqWgRU1RErHYOcg +2osUBStuFwBO6CmzbKpMsN0= +-----END PRIVATE KEY----- diff --git a/x509_test_certs/intermediate/server.cnf b/x509_test_certs/intermediate/server.cnf new file mode 100644 index 0000000..4e1002e --- /dev/null +++ b/x509_test_certs/intermediate/server.cnf @@ -0,0 +1,21 @@ +[req] +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no +[req_distinguished_name] +C = US +ST = State +L = City +O = Organization +OU = Server +CN = server +[v3_req] +basicConstraints = CA:FALSE +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth +subjectAltName = @alt_names +[alt_names] +DNS.1 = localhost +DNS.2 = statsig-forward-proxy.default.svc.cluster.local +IP.1 = 127.0.0.1 +IP.2 = 0.0.0.0 diff --git a/x509_test_certs/root/certs/root_ca.crt b/x509_test_certs/root/certs/root_ca.crt new file mode 100644 index 0000000..b1db133 --- /dev/null +++ b/x509_test_certs/root/certs/root_ca.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmzCCA4OgAwIBAgIUQEjtec3bO/kDXxGz6WHeSHdc6GkwDQYJKoZIhvcNAQEL +BQAwXTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xGDAWBgNVBAsMD1Jvb3QgQ049cm9vdF9j +YTAeFw0yNDA5MjcyMTIxMzhaFw0zNDA5MjUyMTIxMzhaMF0xCzAJBgNVBAYTAlVT +MQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5p +emF0aW9uMRgwFgYDVQQLDA9Sb290IENOPXJvb3RfY2EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDAyvwNu/yBG+xQY3tsUTLIXOnZmrldbPC+3hmjb5ZH +ovjQK8/en2YCzv6YDRduSXmw87LNgPaj4/R9OrnX7e8bdAJN8yzKR2UxxBVnLYxt +yzLxiBL9yFEvAVmLt72IfwZ1pR85BH3s/O+5enWfM7O1xpS8UsekBJsWXvkvUrfl +c6D1YnpBVgVIB3267PKSkXbT9N8rW05uBTohIdT7gTrx1C3nUVzwxQylhCWCCD9J +DHNIbbsWxKFw8StNOU6zZ/Zrsk7y6TB6pYS9WggNVPEqOa0vl79On5MtWpaazkH8 +4Hqjjl8qcoY4yKOPrh0kUhKWsuQPmkIqsQX0fJjYsthoUjvofWcFwUWWGe64Dgu6 +faU9HWs8kqZH4wIcBr+6Il4VAw5yuQlD8lzwhvMg1TNW9QPR4JmeAIiDG9u6Tpot +s2LuUYna/DzsxHuKNFn/1nwJDm7CyZ+rGzjLhKlVH7F1Mnmo7mMfKJGgh09hxoz5 +a7ZFL+ybFe/j4bOZQiMBMdVk06tmQtu7iA/RUSVD1w0Lzz9hr0hwqYlCMZHaeyZw +v0Xa73ypzu/UCVz5yxgQGZxGHMBkiFm78tTmQeqtAfOi8EzgNq5idSXp3dTn2z3z +UTlnsv7B1kBFBKLVDT99PsEXqrCrWDfJkoD3788uA8gI+WjA20gSdVyRqBRSOHrp +JQIDAQABo1MwUTAdBgNVHQ4EFgQU/fyoa1k/Xsv1kyNq2DdfpC9KvU4wHwYDVR0j +BBgwFoAU/fyoa1k/Xsv1kyNq2DdfpC9KvU4wDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEAfIf1eK0Nd53170VAwm4qc9GKNbK0C/RH1iysnlGm3KGn +IimUr4Nl3M9rR5WWfNftctxeZj/g2ALBfF+598VO6ezp6PYn1inA8ZOa5lBuxXPi +z4JY1BOEIathR77v+nA4W2YFIn65B8O76+SyTkDRsCprSyIVpjE/FdHyoEqciK5f +5QDbaELG3n3UTgfwJTKoug/xDQmxu/vc5Y0Y9z2j6PsOYIg4eC+5X75CLjuH4KGG +7Newrsu8jQi1QmlSTsWN5iQa4QfvILbq6BznlOPB4jnnD1/lSn2opPLAZ622OrSC +41BrpTNsDdG3yrzxxyxo3u7flOd2fYoq4Opvdr60ZQuuee1FeONOJmK72q7DvqxZ +YmT7+kgL4NyvmXGx4diBsMygdL9M2k6U3O7HaitdiY/a28AuOWKRuseX+Pwh3Rta +9dJZo+YzSiQ1DXf+ddbxWjji2diW4CpyVpkwgTq8KOUbbDWm1xeZHQAicsTS3O5F +LvNXrJ54NrTsGMA6wh/AOfjDfcwly2CBW3envSmQHr3Ywb5E/dIRtkNcp09w6G9f +kO5eMAn1RKm7tjqFEoCMwkF2PsX8wXqIi5TiFKoHApFodFZckpsJg0bRQH/B6de7 +31Nqyc4RFh4axbp6RUH7+oFv0y/Pi8dufYvGaWxGwLO2OnVUg2mWDu47Z2wI1MI= +-----END CERTIFICATE----- diff --git a/x509_test_certs/root/certs/root_ca.srl b/x509_test_certs/root/certs/root_ca.srl new file mode 100644 index 0000000..4dbd80a --- /dev/null +++ b/x509_test_certs/root/certs/root_ca.srl @@ -0,0 +1 @@ +5CE65A89471B9EABFE18B2B2FBA4A52D38423AD3 diff --git a/x509_test_certs/root/private/root_ca.key b/x509_test_certs/root/private/root_ca.key new file mode 100644 index 0000000..35878f6 --- /dev/null +++ b/x509_test_certs/root/private/root_ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +Qds+VaIsSPz4VMIDohMSApUvK2t9EA== +-----END PRIVATE KEY-----