From 90a64d4e67ce24a243ada5a275e525d7b3538593 Mon Sep 17 00:00:00 2001 From: 1570005763 Date: Thu, 28 Dec 2023 17:30:14 +0800 Subject: [PATCH] cdh: 'get_secret' support EcsRamRole access for aliyun kms Signed-off-by: 1570005763 --- Cargo.lock | 267 +++---- .../docs/kms-providers/alibaba.md | 37 +- confidential-data-hub/kms/Cargo.toml | 4 +- .../kms/src/plugins/aliyun/annotations.rs | 2 +- .../kms/src/plugins/aliyun/client.rs | 686 ++++++++++++++---- .../kms/src/plugins/aliyun/config.rs | 101 +++ .../kms/src/plugins/aliyun/credential.rs | 126 +++- .../aliyun/example_credential/ecsRamRole.json | 4 + .../kms/src/plugins/aliyun/mod.rs | 1 + 9 files changed, 935 insertions(+), 293 deletions(-) create mode 100644 confidential-data-hub/kms/src/plugins/aliyun/config.rs create mode 100644 confidential-data-hub/kms/src/plugins/aliyun/example_credential/ecsRamRole.json diff --git a/Cargo.lock b/Cargo.lock index baab4771f..18fbacac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,9 +137,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" [[package]] name = "api-server-rest" @@ -213,13 +213,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -354,9 +354,9 @@ dependencies = [ [[package]] name = "az-cvm-vtpm" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6239da1e7629eabf1ee6bf5e7dd78b532c029e2fc477afe846db201c67325233" +checksum = "8810a74cfe3024bdfd6bf13e1829114a3ce5431b7d2ef4e4a718a78ffaf03f79" dependencies = [ "bincode", "jsonwebkey", @@ -375,9 +375,9 @@ dependencies = [ [[package]] name = "az-snp-vtpm" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26f68465245c4571f5f4a47c5b76bab5cb394f53a3eaa5827a9a794e6556e8d" +checksum = "b0703ff4c71faae6f5ab21ac8590104e771d17ff117f6941a48deb3db6f75769" dependencies = [ "az-cvm-vtpm", "bincode", @@ -819,7 +819,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1035,9 +1035,9 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" dependencies = [ "cfg-if", "crossbeam-channel", @@ -1049,9 +1049,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1070,21 +1070,20 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", ] [[package]] name = "crossbeam-queue" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153" +checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1092,9 +1091,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if", ] @@ -1233,7 +1232,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1305,7 +1304,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1338,7 +1337,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1395,7 +1394,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1643,9 +1642,9 @@ dependencies = [ [[package]] name = "ed25519-compact" -version = "2.0.4" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3d382e8464107391c8706b4c14b087808ecb909f6c15c34114bc42e53a9e4c" +checksum = "a667e6426df16c2ac478efa4a439d0e674cba769c5556e8cf221739251640c8c" dependencies = [ "ct-codecs", "getrandom", @@ -1747,7 +1746,7 @@ checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1909,7 +1908,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1966,9 +1965,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1981,9 +1980,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1991,15 +1990,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -2008,32 +2007,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -2043,9 +2042,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -2275,11 +2274,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2339,9 +2338,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -2354,7 +2353,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.5", "tokio", "tower-service", "tracing", @@ -2805,9 +2804,9 @@ dependencies = [ [[package]] name = "kbs-types" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40d90e19846fb37e6025740825dd10f65320d3e56f8e957b4bba021b85bc79d6" +checksum = "d1f4b0642769e12f56cfc646d8be13668ed48d3caed0e99efb161c407f3ec532" dependencies = [ "serde", "serde_json", @@ -2862,6 +2861,7 @@ dependencies = [ "openssl", "p12", "prost 0.11.9", + "rand 0.8.5", "reqwest", "resource_uri", "rstest", @@ -2875,6 +2875,7 @@ dependencies = [ "toml 0.8.8", "tonic", "tonic-build", + "url", "uuid", "yasna 0.5.2", "zeroize", @@ -3274,7 +3275,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -3448,9 +3449,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -3569,9 +3570,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -3590,7 +3591,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -3610,9 +3611,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -3880,7 +3881,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -3975,9 +3976,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "platforms" @@ -4071,9 +4072,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -4443,9 +4444,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "base64 0.21.5", "bytes", @@ -4728,9 +4729,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b" +checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" [[package]] name = "rustls-webpki" @@ -4785,11 +4786,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4995,9 +4996,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "8bb1879ea93538b78549031e2d54da3e901fd7e75f2e4dc758d760937b123d10" dependencies = [ "serde", ] @@ -5010,7 +5011,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -5042,14 +5043,14 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -5114,14 +5115,14 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "serde_yaml" -version = "0.9.27" +version = "0.9.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129" dependencies = [ "indexmap 2.1.0", "itoa", @@ -5177,7 +5178,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -5522,7 +5523,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -5544,9 +5545,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -5615,15 +5616,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5687,29 +5688,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] name = "time" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ "deranged", "itoa", @@ -5729,9 +5730,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" dependencies = [ "time-core", ] @@ -5760,11 +5761,32 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls_codec" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38a1d5fcfa859f0ec2b5e111dc903890bd7dac7f34713232bf9aa4fd7cad7b2" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e00e3e7a54e0f1c8834ce72ed49c8487fbd3f801d8cfe1a0ad0640382f8e15" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -5797,7 +5819,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -6035,7 +6057,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -6159,9 +6181,9 @@ dependencies = [ [[package]] name = "tzdb" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec758958f2fb5069cd7fae385be95cc8eceb8cdfd270c7d14de6034f0108d99e" +checksum = "c76c47aa2434148c4c709d5f5ac44a1de3137da224cb74eb7c0c425cb83f837e" dependencies = [ "iana-time-zone", "tz-rs", @@ -6301,7 +6323,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -6409,7 +6431,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "wasm-bindgen-shared", ] @@ -6443,7 +6465,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6752,9 +6774,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.28" +version = "0.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" dependencies = [ "memchr", ] @@ -6782,13 +6804,14 @@ dependencies = [ [[package]] name = "x509-cert" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25eefca1d99701da3a57feb07e5079fc62abba059fc139e98c13bbb250f3ef29" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", "der 0.7.8", "spki 0.7.3", + "tls_codec", ] [[package]] @@ -6804,9 +6827,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9828b178da53440fa9c766a3d2f73f7cf5d0ac1fe3980c1e5018d899fd19e07b" +checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61" [[package]] name = "yasna" @@ -6825,9 +6848,9 @@ checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "byteorder", "zerocopy-derive", @@ -6835,13 +6858,13 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -6861,7 +6884,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] diff --git a/confidential-data-hub/docs/kms-providers/alibaba.md b/confidential-data-hub/docs/kms-providers/alibaba.md index 5613a87f4..199e28ffa 100644 --- a/confidential-data-hub/docs/kms-providers/alibaba.md +++ b/confidential-data-hub/docs/kms-providers/alibaba.md @@ -14,34 +14,60 @@ The `provider_settings` and `annotations` defined in [Sealed Secret](../SEALED_S #### annotations +##### encryption/decryption + | Name | Usage | | ------------------ | -------------------------------------------------------------------- | | `iv` | The initialization vector used in an encryption/decryption operation | +##### get_secret + +| Name | Usage | +| ------------------ | -------------------------------------------------------------------- | +| `version_stage` | (Optional) If this parameter is specified, KMS returns the credentials value of the version that is marked as the specified status. | +| `version_id` | (Optional) If this parameter is specified, KMS returns the credentials value for the specified version number. | + #### provider_settings +`client_type` is used to specify the method of accessing KMS. Only 'client_key' is avaliable for encryption/decryption. While both 'client_key' and 'ecs_ram_role' are avaliable for get_secret. + +If `client_type` is set to 'client_key', provider_settings shall be as following: + | Name | Usage | | ------------------ | -------------------------------------------------------------------- | +| `client_type` | Used to specify the method of accessing KMS. ('client_key' is set here) | | `client_key_id` | The ID of the client key used to access the KMS instance | | `kms_instance_id` | The KMS instance ID to be connected | +Else if `client_type` is set to 'ecs_ram_role', provider_settings shall be as following: + +| Name | Usage | +| ------------------ | -------------------------------------------------------------------- | +| `client_type` | Used to specify the method of accessing KMS. ('ecs_ram_role' is set here) | + ### Credential files -To connect to a KMS instance, a client key is needed. A client key is actually +To connect to a KMS instance with `client_type` set to 'client_key', a client key is needed. A client key is actually [an json with encrypted inside](../../kms/src/plugins/aliyun/example_credential/clientKey_KAAP.f4c8____.json) private key. The name of the client key is always derived from the client key id. Suppose the client key ID is `xxx`, then the client key file has name `clientKey_xxx.json`. The key to encrypt the private key is derived from a password that is also saved in [a file](../../kms/src/plugins/aliyun/example_credential/password_KAAP.f4c8____.json). Suppose the client key ID is `xxx`, then the password file has name `password_xxx.json`. - -To connect to a KMS server, [a cert of the KMS server](../../kms/src/plugins/aliyun/example_credential/PrivateKmsCA_kst-shh64702cf2jvc_____.pem) +Besides, [a cert of the KMS server](../../kms/src/plugins/aliyun/example_credential/PrivateKmsCA_kst-shh64702cf2jvc_____.pem) is also needed. Suppose the kms instance id is `xxx`, then the cert of the KMS server has name `PrivateKmsCA_xxx.pem`. -For more details please see the [developer document for aliyun](https://www.alibabacloud.com/help/en/key-management-service/latest/api-overview?spm=a2c63.l28256.0.0.bc4f4c6fB82yGa). +For more details please see the [developer document for aliyun](https://www.alibabacloud.com/help/en/key-management-service/latest/api-overview). + +To connect to a KMS instance with `client_type` set to 'ecs_ram_role', a [ecsRamRole.json](../../kms/src/plugins/aliyun/example_credential/ecsRamRole.json) file is needed. +In the json file, `ecs_ram_role_name` and `region_id` is set in order to get access to Dedicated KMS. +Among them,`ecs_ram_role_name` refer to RAM role for ECS instances in a VPC network, where CDH runs. Can be set on Aliyun Console. +And `region_id` refers to region id of Dedicated KMS, to which more details can be refered [here](https://www.alibabacloud.com/help/en/kms/product-overview/supported-regions). + +More details about accessing via EcsRamRole can be seen at [Access KMS from an ECS instance in a secure manner](https://www.alibabacloud.com/help/en/kms/use-cases/access-kms-from-an-ecs-instance-in-a-secure-manner). ## Behavior -The client `AliyunKmsClient` supports both `Encrypter` and `Decrypter` api. When at the +The client `AliyunKmsClient` supports `Encrypter`, `Decrypter`, and `Getter` api. When at the user side, the credential files can be directly given by the user. When in Tee, the credential files is supposed to be placed under `/run/confidential-containers/cdh/kms-credential/aliyun` directory. @@ -124,6 +150,7 @@ And the output "iv": "Q/g...", "provider": "aliyun", "provider_settings": { + "client_type": "client_key", "client_key_id": "KAAP.e9...", "kms_instance_id": "kst-bj..." }, diff --git a/confidential-data-hub/kms/Cargo.toml b/confidential-data-hub/kms/Cargo.toml index 8e1d3fe22..1b1e4c229 100644 --- a/confidential-data-hub/kms/Cargo.toml +++ b/confidential-data-hub/kms/Cargo.toml @@ -21,17 +21,19 @@ log.workspace = true openssl = { workspace = true, optional = true } p12 = { version = "0.6.3", optional = true } prost = { workspace = true, optional = true } +rand.workspace = true +reqwest = { version = "0.11", optional = true } resource_uri = { path = "../../attestation-agent/deps/resource_uri" } sha2 = { workspace = true, optional = true } serde.workspace = true serde_json.workspace = true sev = { path = "../../attestation-agent/deps/sev", optional = true } strum.workspace = true -reqwest = { version = "0.11", optional = true } thiserror.workspace = true tokio = { workspace = true, features = ["fs"] } toml.workspace = true tonic = { workspace = true, optional = true } +url.workspace = true uuid = { workspace = true, features = ["serde", "v4"], optional = true } yasna = { version = "0.5.2", optional = true } zeroize = { workspace = true, optional = true } diff --git a/confidential-data-hub/kms/src/plugins/aliyun/annotations.rs b/confidential-data-hub/kms/src/plugins/aliyun/annotations.rs index 89d2de8e0..8f8bffd86 100644 --- a/confidential-data-hub/kms/src/plugins/aliyun/annotations.rs +++ b/confidential-data-hub/kms/src/plugins/aliyun/annotations.rs @@ -20,7 +20,7 @@ pub struct AliSecretAnnotations { /// Serialized [`crate::ProviderSettings`] #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct AliProviderSettings { +pub struct AliClientKeyProviderSettings { pub client_key_id: String, pub kms_instance_id: String, } diff --git a/confidential-data-hub/kms/src/plugins/aliyun/client.rs b/confidential-data-hub/kms/src/plugins/aliyun/client.rs index 82ed02827..b0cd7fbf8 100644 --- a/confidential-data-hub/kms/src/plugins/aliyun/client.rs +++ b/confidential-data-hub/kms/src/plugins/aliyun/client.rs @@ -3,27 +3,29 @@ // SPDX-License-Identifier: Apache-2.0 // -use std::collections::BTreeMap; +use std::{ + collections::{BTreeMap, HashMap}, + fmt::Write, +}; use async_trait::async_trait; -use base64::engine::general_purpose::STANDARD; -use base64::Engine; +use base64::{engine::general_purpose::STANDARD, Engine}; use chrono::Utc; use const_format::concatcp; use log::error; use prost::Message; -use reqwest::Certificate; -use reqwest::{header::HeaderMap, ClientBuilder}; -use serde_json::Value; +use rand::{distributions::Alphanumeric, Rng}; +use reqwest::{header::HeaderMap, Certificate, ClientBuilder}; +use serde_json::{json, Value}; use sha2::{Digest, Sha256}; use tokio::fs; -// use crate::plugins::aliyun::client::dkms_api; use crate::plugins::_IN_GUEST_DEFAULT_KEY_PATH; use crate::{Annotations, Decrypter, Encrypter, Getter, ProviderSettings}; use crate::{Error, Result}; -use super::annotations::{AliCryptAnnotations, AliProviderSettings, AliSecretAnnotations}; +use super::annotations::*; +use super::config::Config; use super::credential::Credential; pub mod dkms_api { @@ -31,10 +33,10 @@ pub mod dkms_api { } pub struct AliyunKmsClient { + client_type: String, http_client: reqwest::Client, credential: Credential, - kms_instance_id: String, - endpoint: String, + config: Config, } const ALIYUN_IN_GUEST_DEFAULT_KEY_PATH: &str = concatcp!(_IN_GUEST_DEFAULT_KEY_PATH, "/aliyun"); @@ -52,10 +54,17 @@ impl AliyunKmsClient { password: &str, cert_pem: &str, ) -> Result { - let credential = Credential::new(client_key, password).map_err(|e| { - Error::AliyunKmsError(format!("create credential of the kms instance failed: {e}")) - })?; - let endpoint = format!("{kms_instance_id}.cryptoservice.kms.aliyuncs.com"); + Self::new_client_key_client(client_key, kms_instance_id, password, cert_pem) + } + + pub fn new_client_key_client( + client_key: &str, + kms_instance_id: &str, + password: &str, + cert_pem: &str, + ) -> Result { + let client_type: String = String::from("client_key"); + let cert = Self::read_kms_instance_cert(cert_pem.as_bytes())?; let http_client = ClientBuilder::new() .use_rustls_tls() @@ -63,11 +72,56 @@ impl AliyunKmsClient { .build() .map_err(|e| Error::AliyunKmsError(format!("build http client failed: {e}")))?; + let credential = + Credential::new_credential_client_key(client_key, password).map_err(|e| { + Error::AliyunKmsError(format!( + "create client_key credential of the kms instance failed: {e}" + )) + })?; + + let endpoint = format!("{kms_instance_id}.cryptoservice.kms.aliyuncs.com"); + let config = Config::new_config_client_key(kms_instance_id, &endpoint).map_err(|e| { + Error::AliyunKmsError(format!( + "create client_key config of the kms instance failed: {e}" + )) + })?; + Ok(Self { + client_type, http_client, - kms_instance_id: kms_instance_id.to_string(), credential, - endpoint, + config, + }) + } + + pub fn new_ecs_ram_role_client(ecs_ram_role_name: &str, region_id: &str) -> Result { + let client_type: String = String::from("ecs_ram_role"); + + let http_client = ClientBuilder::new() + .use_rustls_tls() + .build() + .map_err(|e| Error::AliyunKmsError(format!("build http client failed: {e}")))?; + + let credential = + Credential::new_credential_ecs_ram_role(ecs_ram_role_name).map_err(|e| { + Error::AliyunKmsError(format!( + "create ecs_ram_role credential of the kms instance failed: {e}" + )) + })?; + + let endpoint = format!("kms.{region_id}.aliyuncs.com"); + let vpc = format!("kms-vpc.{region_id}.aliyuncs.com"); + let config = Config::new_config_ecs_ram_role(region_id, &endpoint, &vpc).map_err(|e| { + Error::AliyunKmsError(format!( + "create ecs_ram_role config of the kms instance failed: {e}" + )) + })?; + + Ok(Self { + client_type, + http_client, + credential, + config, }) } @@ -75,55 +129,121 @@ impl AliyunKmsClient { /// [`ALIYUN_IN_GUEST_DEFAULT_KEY_PATH`] which is the by default path where the credential /// to access kms is saved. pub async fn from_provider_settings(provider_settings: &ProviderSettings) -> Result { - let provider_settings: AliProviderSettings = - serde_json::from_value(Value::Object(provider_settings.clone())).map_err(|e| { - Error::AliyunKmsError(format!("parse provider setting failed: {e}")) + if provider_settings.contains_key("client_type") + && provider_settings["client_type"] == "client_key" + { + let provider_settings: AliClientKeyProviderSettings = + serde_json::from_value(Value::Object(provider_settings.clone())).map_err(|e| { + Error::AliyunKmsError(format!("parse client_key provider setting failed: {e}")) + })?; + + let cert_path = format!( + "{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/PrivateKmsCA_{}.pem", + provider_settings.kms_instance_id + ); + let pswd_path = format!( + "{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/password_{}.json", + provider_settings.client_key_id + ); + let client_key_path = format!( + "{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/clientKey_{}.json", + provider_settings.client_key_id + ); + let cert_pem = fs::read_to_string(cert_path).await.map_err(|e| { + Error::AliyunKmsError(format!("read kms instance pem cert failed: {e}")) + })?; + let pswd = fs::read_to_string(pswd_path).await.map_err(|e| { + Error::AliyunKmsError(format!("read password of the credential failed: {e}")) + })?; + let client_key = fs::read_to_string(client_key_path).await.map_err(|e| { + Error::AliyunKmsError(format!("read client key of the credential failed: {e}")) })?; - let cert_path = format!( - "{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/PrivateKmsCA_{}.pem", - provider_settings.kms_instance_id - ); - let pswd_path = format!( - "{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/password_{}.json", - provider_settings.client_key_id - ); - let client_key_path = format!( - "{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/clientKey_{}.json", - provider_settings.client_key_id - ); - let cert_pem = fs::read_to_string(cert_path).await.map_err(|e| { - Error::AliyunKmsError(format!("read kms instance pem cert failed: {e}")) - })?; - let pswd = fs::read_to_string(pswd_path).await.map_err(|e| { - Error::AliyunKmsError(format!("read password of the credential failed: {e}")) - })?; - let client_key = fs::read_to_string(client_key_path).await.map_err(|e| { - Error::AliyunKmsError(format!("read client key of the credential failed: {e}")) - })?; + Self::new_client_key_client( + &client_key, + &provider_settings.kms_instance_id, + &pswd, + &cert_pem, + ) + } else if provider_settings.contains_key("client_type") + && provider_settings["client_type"] == "ecs_ram_role" + { + let ecs_ram_role_path = format!("{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/ecsRamRole.json"); + + let ecs_ram_role_str = fs::read_to_string(ecs_ram_role_path).await.map_err(|e| { + Error::AliyunKmsError(format!( + "read ecs_ram_role with `fs::read_to_string()` failed: {e}" + )) + })?; - Self::new( - &client_key, - &provider_settings.kms_instance_id, - &pswd, - &cert_pem, - ) + let ecs_ram_role_json: serde_json::Value = serde_json::from_str(&ecs_ram_role_str) + .map_err(|e| { + Error::AliyunKmsError(format!( + "read ecs_ram_role with `serde_json::from_str()` failed: {e}" + )) + })?; + + let ecs_ram_role_name = + if let Some(ecs_ram_role_name) = ecs_ram_role_json["ecs_ram_role_name"].as_str() { + ecs_ram_role_name + } else { + return Err(Error::AliyunKmsError( + "get 'ecs_ram_role_name' from ecs_ram_role_json failed.".to_string(), + )); + }; + let region_id = if let Some(region_id) = ecs_ram_role_json["region_id"].as_str() { + region_id + } else { + return Err(Error::AliyunKmsError( + "get 'region_id' from ecs_ram_role_json failed.".to_string(), + )); + }; + + Self::new_ecs_ram_role_client(ecs_ram_role_name, region_id) + } else { + Err(Error::AliyunKmsError( + "client type invalid or not exist.".to_string(), + )) + } } /// Export the [`ProviderSettings`] of the current client. This function is to be used /// in the encryptor side. The [`ProviderSettings`] will be used to initial a client /// in the decryptor side. pub fn export_provider_settings(&self) -> Result { - let provider_settings = AliProviderSettings { - client_key_id: self.credential.client_key_id.clone(), - kms_instance_id: self.kms_instance_id.clone(), - }; + let mut provider_settings; + if self.client_type == "client_key" { + let client_key_provider_settings = AliClientKeyProviderSettings { + client_key_id: self + .credential + .get_client_key_id_of_client_key() + .ok_or(Error::AliyunKmsError( + "get_client_key_id_of_client_key() fail".to_string(), + ))? + .clone(), + kms_instance_id: self + .config + .get_kms_instance_id_of_client_key() + .ok_or(Error::AliyunKmsError( + "get_kms_instance_id_of_client_key() fail".to_string(), + ))? + .clone(), + }; + + provider_settings = serde_json::to_value(client_key_provider_settings) + .map_err(|e| { + Error::AliyunKmsError(format!("serialize ProviderSettings failed: {e}")) + })? + .as_object() + .expect("must be an object") + .to_owned(); + } else if self.client_type == "ecs_ram_role" { + provider_settings = serde_json::Map::::new(); + } else { + return Err(Error::AliyunKmsError("client type invalid.".to_string())); + } - let provider_settings = serde_json::to_value(provider_settings) - .map_err(|e| Error::AliyunKmsError(format!("serialize ProviderSettings failed: {e}")))? - .as_object() - .expect("must be an object") - .to_owned(); + provider_settings.insert(String::from("client_type"), json!(self.client_type)); Ok(provider_settings) } @@ -152,7 +272,7 @@ impl Encrypter for AliyunKmsClient { })?; let res = self - .do_request(body, headers) + .do_request(body, headers, HashMap::::new()) .await .map_err(|e| Error::AliyunKmsError(format!("do request to kms server failed: {e}")))?; @@ -210,7 +330,7 @@ impl Decrypter for AliyunKmsClient { })?; let res = self - .do_request(body, headers) + .do_request(body, headers, HashMap::::new()) .await .map_err(|e| Error::AliyunKmsError(format!("do request to kms server failed: {e}")))?; @@ -233,35 +353,94 @@ impl Getter for AliyunKmsClient { )) })?; - let get_secret_request = dkms_api::GetSecretValueRequest { - secret_name: name.into(), - version_stage: secret_settings.version_stage, - version_id: secret_settings.version_id, - fetch_extended_config: false, + let mut body = Vec::new(); + + let headers = if self.client_type == "client_key" { + let get_secret_request = dkms_api::GetSecretValueRequest { + secret_name: name.into(), + version_stage: secret_settings.version_stage.clone(), + version_id: secret_settings.version_id.clone(), + fetch_extended_config: true, + }; + get_secret_request.encode(&mut body).map_err(|e| { + Error::AliyunKmsError(format!( + "encode get_secret request using protobuf failed: {e}" + )) + })?; + self.build_headers("GetSecretValue", &body).map_err(|e| { + Error::AliyunKmsError(format!("build get_secret request http header failed: {e}")) + })? + } else if self.client_type == "ecs_ram_role" { + self.build_headers("GetSecretValue", &[]).map_err(|e| { + Error::AliyunKmsError(format!("build get_secret request http header failed: {e}")) + })? + } else { + return Err(Error::AliyunKmsError("client type invalid.".to_string())); }; - let mut body = Vec::new(); - get_secret_request.encode(&mut body).map_err(|e| { - Error::AliyunKmsError(format!( - "encode get_secret request body using protobuf failed: {e}" - )) - })?; - let headers = self.build_headers("GetSecretValue", &body).map_err(|e| { - Error::AliyunKmsError(format!("build get_secret request http header failed: {e}")) - })?; + let params = if self.client_type == "client_key" { + self.build_params(HashMap::::new()) + .await + .map_err(|e| { + Error::AliyunKmsError(format!( + "build get_secret request http param failed: {e}" + )) + })? + } else if self.client_type == "ecs_ram_role" { + let mut get_secret_request = HashMap::::new(); + get_secret_request.insert("SecretName".to_string(), name.to_string()); + get_secret_request.insert( + "VersionStage".to_string(), + secret_settings.version_stage.to_string(), + ); + get_secret_request.insert( + "VersionId".to_string(), + secret_settings.version_id.to_string(), + ); + get_secret_request.insert("FetchExtendedConfig".to_string(), "true".to_string()); + self.build_params(get_secret_request).await.map_err(|e| { + Error::AliyunKmsError(format!("build get_secret request http param failed: {e}")) + })? + } else { + return Err(Error::AliyunKmsError("client type invalid.".to_string())); + }; let res = self - .do_request(body, headers) + .do_request(body, headers, params) .await .map_err(|e| Error::AliyunKmsError(format!("do request to kms server failed: {e}")))?; - let get_secret_response = - dkms_api::GetSecretValueResponse::decode(&res[..]).map_err(|e| { + let secret_data = if self.client_type == "client_key" { + let get_secret_response = + dkms_api::GetSecretValueResponse::decode(&res[..]).map_err(|e| { + Error::AliyunKmsError(format!( + "decode get_secret response using protobuf failed: {e}" + )) + })?; + get_secret_response.secret_data.as_bytes().to_vec() + } else if self.client_type == "ecs_ram_role" { + let res_string: String = String::from_utf8(res).map_err(|e| { + Error::AliyunKmsError(format!("get_secret response using `from_utf8` failed: {e}")) + })?; + let get_secret_response: Value = serde_json::from_str(&res_string).map_err(|e| { Error::AliyunKmsError(format!( - "decrypt get_secret response using protobuf failed: {e}" + "get_secret response using `serde_json` failed: {e}" )) })?; - Ok(get_secret_response.secret_data.into()) + let secret_data = + if let Some(secret_data_str) = get_secret_response["SecretData"].as_str() { + secret_data_str.as_bytes().to_vec() + } else { + return Err(Error::AliyunKmsError( + "get 'SecretData' from get_secret response failed.".to_string(), + )); + }; + secret_data + } else { + return Err(Error::AliyunKmsError("client type invalid.".to_string())); + }; + + Ok(secret_data) } } @@ -271,84 +450,259 @@ impl AliyunKmsClient { const CONTENT_TYPE: &'static str = "application/x-protobuf"; fn build_headers(&self, api_name: &str, body: &[u8]) -> anyhow::Result { - let mut headers = HeaderMap::new(); - headers.insert("Accept", "application/x-protobuf".parse()?); - headers.insert("Host", self.endpoint.parse()?); - let date = Utc::now().format("%a, %d %b %Y %H:%M:%S GMT").to_string(); - headers.insert("date", date.parse()?); - headers.insert( - "user-agent", - Into::::into(concat!( - env!("CARGO_PKG_NAME"), - "/", - env!("CARGO_PKG_VERSION") - )) - .parse()?, - ); - headers.insert("x-kms-apiversion", Self::API_VERSION.to_string().parse()?); - headers.insert("x-kms-apiname", api_name.parse()?); - headers.insert("x-kms-signaturemethod", Self::SIGNATURE_METHOD.parse()?); - headers.insert("x-kms-acccesskeyid", self.credential.client_key_id.parse()?); - headers.insert("Content-Type", "application/x-protobuf".parse()?); - headers.insert("Content-Length", format!("{}", body.len()).parse()?); - let sha256 = { - let mut hasher = Sha256::new(); - hasher.update(body); - hex::encode_upper(hasher.finalize()) - }; - headers.insert("Content-Sha256", sha256.parse()?); - - let canonicalized_headers = headers - .iter() - .filter(|(k, _)| k.as_str().starts_with("x-kms-")) - .filter_map(|(k, v)| match v.to_str() { - std::result::Result::Ok(v) => Some((k.as_str(), v.trim())), - Err(_) => None, - }) - .collect::>() - .iter() - .map(|(k, v)| format!("{k}:{v}\n")) - .collect::>() - .join(""); - - let tbs = format!( - "POST\n{}\n{}\n{}\n{}/", - sha256, - Self::CONTENT_TYPE, - date, - canonicalized_headers - ); - let bear_auth = self.credential.generate_bear_auth(&tbs)?; - headers.insert("Authorization", bear_auth.parse()?); - Ok(headers) + if self.client_type == "client_key" { + let mut headers = HeaderMap::new(); + headers.insert("Accept", "application/x-protobuf".parse()?); + headers.insert( + "Host", + self.config + .get_endpoint_of_client_key() + .ok_or(anyhow::anyhow!("get_endpoint_of_client_key() fail"))? + .parse()?, + ); + let date = Utc::now().format("%a, %d %b %Y %H:%M:%S GMT").to_string(); + headers.insert("date", date.parse()?); + headers.insert( + "user-agent", + Into::::into(concat!( + env!("CARGO_PKG_NAME"), + "/", + env!("CARGO_PKG_VERSION") + )) + .parse()?, + ); + headers.insert("x-kms-apiversion", Self::API_VERSION.to_string().parse()?); + headers.insert("x-kms-apiname", api_name.parse()?); + headers.insert("x-kms-signaturemethod", Self::SIGNATURE_METHOD.parse()?); + headers.insert( + "x-kms-acccesskeyid", + self.credential + .get_client_key_id_of_client_key() + .ok_or(anyhow::anyhow!("get_client_key_id_of_client_key() fail"))? + .parse()?, + ); + headers.insert("Content-Type", "application/x-protobuf".parse()?); + headers.insert("Content-Length", format!("{}", body.len()).parse()?); + let sha256 = { + let mut hasher = Sha256::new(); + hasher.update(body); + hex::encode_upper(hasher.finalize()) + }; + headers.insert("Content-Sha256", sha256.parse()?); + + let canonicalized_headers = headers + .iter() + .filter(|(k, _)| k.as_str().starts_with("x-kms-")) + .filter_map(|(k, v)| match v.to_str() { + std::result::Result::Ok(v) => Some((k.as_str(), v.trim())), + Err(_) => None, + }) + .collect::>() + .iter() + .map(|(k, v)| format!("{k}:{v}\n")) + .collect::>() + .join(""); + + let string_to_sign = format!( + "POST\n{}\n{}\n{}\n{}/", + sha256, + Self::CONTENT_TYPE, + date, + canonicalized_headers + ); + let string_signed = self + .credential + .sign_with_credential_client_key(&string_to_sign)?; + headers.insert("Authorization", string_signed.parse()?); + + Ok(headers) + } else if self.client_type == "ecs_ram_role" { + let mut headers = HeaderMap::new(); + // headers.insert("Accept-Encoding", "identity".parse()?); + headers.insert( + "user-agent", + Into::::into(concat!( + env!("CARGO_PKG_NAME"), + "/", + env!("CARGO_PKG_VERSION") + )) + .parse()?, + ); + + headers.insert("x-acs-version", "2016-01-20".parse()?); + headers.insert("x-acs-action", "GetSecretValue".parse()?); + + headers.insert("x-sdk-invoke-type", "normal".parse()?); + headers.insert("x-sdk-client", "python/2.0.0".parse()?); + + Ok(headers) + } else { + return Err(anyhow::anyhow!(format!("client type invalid."))); + } } - async fn do_request(&self, body: Vec, headers: HeaderMap) -> anyhow::Result> { - let server_url = format!("https://{}", self.endpoint); - - let response = self - .http_client - .post(server_url) - .headers(headers) - .body(body) - .send() - .await?; - - if !response.status().is_success() { - error!("aliyun kms: do request fail!"); - let body_bytes = response.bytes().await?; - let error_msg = dkms_api::Error::decode(&*body_bytes)?; - let error_msg = format!( - "status code: {}, request id: {}, error code: {}, message: {}", - error_msg.status_code, - error_msg.request_id, - error_msg.error_code, - error_msg.error_message + async fn build_params( + &self, + mut params: HashMap, + ) -> anyhow::Result> { + if self.client_type == "client_key" { + Ok(HashMap::::new()) + } else if self.client_type == "ecs_ram_role" { + params.insert("Version".to_string(), "2016-01-20".to_string()); + params.insert("Action".to_string(), "GetSecretValue".to_string()); + params.insert("Format".to_string(), "json".to_string()); + params.insert( + "RegionId".to_string(), + self.config + .get_region_id_of_ecs_ram_role() + .ok_or(Error::AliyunKmsError( + "get_region_id_of_ecs_ram_role() fail".to_string(), + ))? + .to_string(), ); - anyhow::bail!(error_msg); + + let iso8601_date = Utc::now().format("%Y-%m-%dT%H:%M:%S%.fZ").to_string(); + params.insert("Timestamp".to_string(), iso8601_date.to_string()); + params.insert("SignatureMethod".to_string(), "HMAC-SHA1".to_string()); + params.insert("SignatureType".to_string(), "".to_string()); + params.insert("SignatureVersion".to_string(), "1.0".to_string()); + let bytes: Vec = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(16) + .collect(); + // let hex_nonce: String = bytes.iter().map(|b| format!("{:02x}", b)).collect(); + let hex_nonce: String = bytes.iter().fold(String::new(), |mut output, b| { + let _ = write!(output, "{b:02X}"); + output + }); + params.insert("SignatureNonce".to_string(), hex_nonce.to_string()); + + let (session_ak, session_sk, token) = self + .credential + .get_ecs_ram_role_session_credential() + .await + .map_err(|e| { + Error::AliyunKmsError(format!( + "get_ecs_ram_role_session_credential() failed: {e}" + )) + })?; + params.insert("AccessKeyId".to_string(), session_ak.to_string()); + params.insert("SecurityToken".to_string(), token.to_string()); + + let canonicalized_params = params + .iter() + .collect::>() + .iter() + .map(|(k, v)| { + format!( + "{}={}", + self.credential.urlencode_openapi(k), + self.credential.urlencode_openapi(v), + ) + }) + .collect::>() + .join("&"); + let urlencoded_canonicalized_params: String = + self.credential.urlencode_openapi(&canonicalized_params); + let string_to_sign = format!("POST&%2F&{}", urlencoded_canonicalized_params,); + let string_signed = self + .credential + .sign_with_credential_ecs_ram_role(&string_to_sign, &(session_sk + "&"))?; + params.insert("Signature".to_string(), string_signed.to_string()); + + Ok(params) + } else { + return Err(anyhow::anyhow!(format!("client type invalid."))); } + } - Ok(response.bytes().await?.to_vec()) + async fn do_request( + &self, + body: Vec, + headers: HeaderMap, + params: HashMap, + ) -> anyhow::Result> { + if self.client_type == "client_key" { + let server_url = format!( + "https://{}", + self.config + .get_endpoint_of_client_key() + .ok_or(Error::AliyunKmsError( + "get_endpoint_of_client_key() fail".to_string() + ))? + ); + + let response = self + .http_client + .post(server_url) + .headers(headers) + .body(body) + .send() + .await?; + + if !response.status().is_success() { + error!("aliyun kms: do request fail!"); + let body_bytes = response.bytes().await?; + let error_msg = dkms_api::Error::decode(&*body_bytes)?; + let error_msg = format!( + "status code: {}, request id: {}, error code: {}, message: {}", + error_msg.status_code, + error_msg.request_id, + error_msg.error_code, + error_msg.error_message + ); + anyhow::bail!(error_msg); + } + + Ok(response.bytes().await?.to_vec()) + } else if self.client_type == "ecs_ram_role" { + let url_params = params + .iter() + .map(|(k, v)| { + format!( + "{}={}", + self.credential.urlencode_openapi(k), + self.credential.urlencode_openapi(v), + ) + }) + .collect::>() + .join("&"); + let server_url = format!( + "https://{}/?{}", + self.config + .get_endpoint_of_ecs_ram_role() + .ok_or(Error::AliyunKmsError( + "get_endpoint_of_ecs_ram_role() fail".to_string() + ))?, + url_params + ); + + let response = self + .http_client + .post(server_url) + .headers(headers) + .send() + .await?; + + if !response.status().is_success() { + error!("aliyun kms: do request fail!"); + let content = response.text().await?; + let error_msg: Value = serde_json::from_str(&content)?; + let error_msg = format!( + "status code: {}, request id: {}, error code: {}, message: {}", + error_msg["HttpStatus"], + error_msg["RequestId"], + error_msg["Code"], + error_msg["Message"] + ); + anyhow::bail!(error_msg); + } + + let content = response.text().await?; + Ok(content.into_bytes()) + } else { + return Err(anyhow::anyhow!("client type invalid.")); + } } } @@ -368,8 +722,9 @@ mod tests { #[case(b"this is a another test plaintext")] #[tokio::test] async fn key_lifetime(#[case] plaintext: &[u8]) { - let kid = "yyy"; + let kid = "key_id"; let provider_settings = json!({ + "client_type": "client_key", "client_key_id": "KAAP.f4c8****", "kms_instance_id": "kst-shh6****", }); @@ -403,8 +758,9 @@ mod tests { async fn get_secret_with_client_key() { let secret_name = "test_secret"; let provider_settings = json!({ - "client_key_id": "KAAP.2bc431c0-416f-4ccb-8638-e71245fd60c1", - "kms_instance_id": "kst-bjj65794c36xm0v9aeujs", + "client_type": "client_key", + "client_key_id": "KAAP.f4c8****", + "kms_instance_id": "kst-shh6****", }); // init encrypter at user side let provider_settings = provider_settings.as_object().unwrap().to_owned(); @@ -424,4 +780,30 @@ mod tests { // We have set "test_secret_value" as secret on Aliyun KMS console. assert_eq!(String::from_utf8_lossy(&secret_value), "test_secret_value"); } + + #[ignore] + #[tokio::test] + async fn get_secret_with_ecs_ram_role() { + let secret_name = "test_secret"; + let provider_settings = json!({ + "client_type": "ecs_ram_role", + }); + // init encrypter at user side + let provider_settings = provider_settings.as_object().unwrap().to_owned(); + let mut getter = AliyunKmsClient::from_provider_settings(&provider_settings) + .await + .unwrap(); + + // do get + let mut annotations: Annotations = Map::::new(); + annotations.insert("version_stage".to_string(), Value::String("".to_string())); + annotations.insert("version_id".to_string(), Value::String("".to_string())); + let secret_value = getter + .get_secret(secret_name, &annotations) + .await + .expect("get_secret_with_ecs_ram_role"); + + // We have set "test_secret_value" as secret on Aliyun KMS console. + assert_eq!(String::from_utf8_lossy(&secret_value), "test_secret_value"); + } } diff --git a/confidential-data-hub/kms/src/plugins/aliyun/config.rs b/confidential-data-hub/kms/src/plugins/aliyun/config.rs new file mode 100644 index 000000000..75f06b2df --- /dev/null +++ b/confidential-data-hub/kms/src/plugins/aliyun/config.rs @@ -0,0 +1,101 @@ +// Copyright (c) 2023 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! Configs to access aliyun KMS + +use anyhow::*; +// use base64::{engine::general_purpose::STANDARD, Engine}; +// use log::debug; +// use openssl::{ +// pkey::{PKey, Private}, +// sign::Signer, +// }; +// use p12::{CertBag, ContentInfo, MacData, SafeBagKind, PFX}; +// use serde::Deserialize; +// use yasna::ASN1Result; + +#[derive(Clone, Debug)] +pub enum Config { + ConfigClientKey { + kms_instance_id: String, + endpoint: String, + }, + ConfigEcsRamRole { + region_id: String, + endpoint: String, + vpc: String, + }, +} + +// implement Config::ConfigClientKey related function +impl Config { + pub(crate) fn new_config_client_key(kms_instance_id: &str, endpoint: &str) -> Result { + let kms_instance_id = kms_instance_id.to_string(); + let endpoint = endpoint.to_string(); + + let config = Config::ConfigClientKey { + kms_instance_id, + endpoint, + }; + + Ok(config) + } + + pub(crate) fn get_kms_instance_id_of_client_key(&self) -> Option { + if let Config::ConfigClientKey { + kms_instance_id, .. + } = self + { + Some(kms_instance_id.clone()) + } else { + None + } + } + + pub(crate) fn get_endpoint_of_client_key(&self) -> Option { + if let Config::ConfigClientKey { endpoint, .. } = self { + Some(endpoint.clone()) + } else { + None + } + } +} + +// implement Config::ConfigEcsRamRole related function +impl Config { + pub(crate) fn new_config_ecs_ram_role( + region_id: &str, + endpoint: &str, + vpc: &str, + ) -> Result { + let region_id = region_id.to_string(); + let endpoint = endpoint.to_string(); + let vpc = vpc.to_string(); + + let config = Config::ConfigEcsRamRole { + region_id, + endpoint, + vpc, + }; + + Ok(config) + } + + pub(crate) fn get_region_id_of_ecs_ram_role(&self) -> Option { + if let Config::ConfigEcsRamRole { region_id, .. } = self { + Some(region_id.clone()) + } else { + None + } + } + + pub(crate) fn get_endpoint_of_ecs_ram_role(&self) -> Option { + if let Config::ConfigEcsRamRole { endpoint, .. } = self { + Some(endpoint.clone()) + } else { + None + } + } +} diff --git a/confidential-data-hub/kms/src/plugins/aliyun/credential.rs b/confidential-data-hub/kms/src/plugins/aliyun/credential.rs index 36dd90536..946eaa02d 100644 --- a/confidential-data-hub/kms/src/plugins/aliyun/credential.rs +++ b/confidential-data-hub/kms/src/plugins/aliyun/credential.rs @@ -9,17 +9,25 @@ use anyhow::*; use base64::{engine::general_purpose::STANDARD, Engine}; use log::debug; use openssl::{ + hash::MessageDigest, pkey::{PKey, Private}, sign::Signer, }; use p12::{CertBag, ContentInfo, MacData, SafeBagKind, PFX}; use serde::Deserialize; +use serde_json::Value; +use url::form_urlencoded::byte_serialize; use yasna::ASN1Result; #[derive(Clone, Debug)] -pub(crate) struct Credential { - pub(crate) client_key_id: String, - private_key: PKey, +pub enum Credential { + CredentialClientKey { + client_key_id: String, + private_key: PKey, + }, + CredentialEcsRamRole { + ecs_ram_role_name: String, + }, } #[derive(Deserialize)] @@ -35,17 +43,17 @@ struct Password { client_key_password: String, } +// implement Credential::CredentialClientKey related function impl Credential { - pub(crate) fn new(client_key: &str, pswd: &str) -> Result { + pub(crate) fn new_credential_client_key(client_key: &str, pswd: &str) -> Result { let ck: ClientKey = serde_json::from_str(client_key)?; let password: Password = serde_json::from_str(pswd)?; - let private_key = Self::parse_private_key(ck.private_key_data, password.client_key_password)?; - let private_key = PKey::private_key_from_der(&private_key)?; - let credential = Credential { + + let credential = Credential::CredentialClientKey { client_key_id: ck.key_id.clone(), private_key, }; @@ -53,12 +61,38 @@ impl Credential { Ok(credential) } - pub(crate) fn generate_bear_auth(&self, str_to_sign: &str) -> Result { - let mut signer = Signer::new(openssl::hash::MessageDigest::sha256(), &self.private_key)?; - signer.update(str_to_sign.as_bytes())?; - let signature = signer.sign_to_vec()?; + pub(crate) fn get_client_key_id_of_client_key(&self) -> Option { + if let Credential::CredentialClientKey { client_key_id, .. } = self { + Some(client_key_id.clone()) + } else { + None + } + } - Ok(format!("Bearer {}", STANDARD.encode(signature))) + fn get_private_key_of_client_key(&self) -> Option> { + if let Credential::CredentialClientKey { private_key, .. } = self { + Some(private_key.clone()) + } else { + None + } + } + + pub(crate) fn sign_with_credential_client_key(&self, str_to_sign: &str) -> Result { + match self { + Credential::CredentialClientKey { .. } => { + let pk = self + .get_private_key_of_client_key() + .ok_or(anyhow!("get_private_key_of_client_key() fail"))?; + let mut signer = Signer::new(openssl::hash::MessageDigest::sha256(), &pk)?; + signer.update(str_to_sign.as_bytes())?; + let signature = signer.sign_to_vec()?; + + Ok(format!("Bearer {}", STANDARD.encode(signature))) + } + _ => Err(anyhow!( + "Require 'Credential::CredentialClientKey' in 'sign_with_credential_client_key()'!" + )), + } } pub(crate) fn parse_private_key(private_key_data: String, password: String) -> Result> { @@ -113,3 +147,71 @@ impl Credential { bytes } } + +// implement Credential::CredentialEcsRamRole related function +impl Credential { + pub(crate) fn new_credential_ecs_ram_role(ecs_ram_role_name: &str) -> Result { + let credential = Credential::CredentialEcsRamRole { + ecs_ram_role_name: ecs_ram_role_name.to_string(), + }; + + Ok(credential) + } + + pub(crate) async fn get_ecs_ram_role_session_credential( + &self, + ) -> Result<(String, String, String)> { + match self { + Credential::CredentialEcsRamRole { ecs_ram_role_name } => { + let request_url = format!( + "http://100.100.100.200/latest/meta-data/ram/security-credentials/{}", + ecs_ram_role_name + ); + + let response = reqwest::get(&request_url).await?; + let body = if response.status().is_success() { + response.text().await? + } else { + return Err(anyhow!( + "Request session_credential failed with status: {}", + response.status() + )); + }; + let body_json: Value = serde_json::from_str(&body)?; + + let session_ak = body_json["AccessKeyId"].as_str().unwrap().to_string(); + let session_sk = body_json["AccessKeySecret"].as_str().unwrap().to_string(); + let token = body_json["SecurityToken"].as_str().unwrap().to_string(); + + Ok((session_ak, session_sk, token)) + } + _ => Err(anyhow!( + "Require 'CredentialEcsRamRole' in 'get_ecs_ram_role_session_credential()'!" + )), + } + } + + pub(crate) fn sign_with_credential_ecs_ram_role( + &self, + str_to_sign: &str, + secret: &str, + ) -> Result { + match self { + Credential::CredentialEcsRamRole { .. } => { + let pkey = PKey::hmac(secret.as_bytes())?; + let mut signer = Signer::new(MessageDigest::sha1(), &pkey)?; + signer.update(str_to_sign.as_bytes())?; + let signature = signer.sign_to_vec()?; + Ok(STANDARD.encode(signature)) + }, + _=> Err(anyhow!("Require 'Credential::CredentialClientKey' in 'sign_with_credential_ecs_ram_role()'!")) + } + } + + pub(crate) fn urlencode_openapi(&self, s: &String) -> String { + let s: String = byte_serialize(s.as_bytes()).collect(); + s.replace('+', "%20") + .replace('*', "%2A") + .replace("%7E", "~") + } +} diff --git a/confidential-data-hub/kms/src/plugins/aliyun/example_credential/ecsRamRole.json b/confidential-data-hub/kms/src/plugins/aliyun/example_credential/ecsRamRole.json new file mode 100644 index 000000000..752b30941 --- /dev/null +++ b/confidential-data-hub/kms/src/plugins/aliyun/example_credential/ecsRamRole.json @@ -0,0 +1,4 @@ +{ + "ecs_ram_role_name": "EcsRamRoleTest", + "region_id": "cn-beijing" +} \ No newline at end of file diff --git a/confidential-data-hub/kms/src/plugins/aliyun/mod.rs b/confidential-data-hub/kms/src/plugins/aliyun/mod.rs index 23eb7cf84..f08ccc74a 100644 --- a/confidential-data-hub/kms/src/plugins/aliyun/mod.rs +++ b/confidential-data-hub/kms/src/plugins/aliyun/mod.rs @@ -10,6 +10,7 @@ mod annotations; mod client; +mod config; mod credential; pub use client::AliyunKmsClient;