Skip to content

Commit

Permalink
CDH/KMS/Aliyun: read credentials from env
Browse files Browse the repository at this point in the history
Related to confidential-containers#413. Before this commit, we will read credentials from local
filesystem for aliyun KMS plugin. This would make unexpected side
effects. We now read them from the env, which will not spread side
effects.

There are some envs that will be used.

If the `client_type` is `client_key`:
- `ALIYUN_CLIENT_KEY`: Aliyun KMS client key
- `ALIYUN_PASSWORD`: The password of Aliyun KMS client key
- `ALIYUN_KMS_INSTANCE_CERT`: The public key cert to KMS instance

if the `client_type` is `ecs_ram_role`
- `ALIYUN_ECS_RAM_ROLE`: The ECS RAM Role name on Aliyun
- `ALIYUN_REGION_ID`: ECS Instance region id

Signed-off-by: Xynnn007 <[email protected]>
  • Loading branch information
Xynnn007 committed Jan 22, 2024
1 parent 01c9452 commit 9370499
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 108 deletions.
32 changes: 20 additions & 12 deletions confidential-data-hub/docs/kms-providers/alibaba.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,29 @@ Else if `client_type` is set to 'ecs_ram_role', provider_settings shall be as fo

### Credential files

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/client/client_key_client/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/client/client_key_client/example_credential/password_KAAP.f4c8____.json).
Suppose the client key ID is `xxx`, then the password file has name `password_xxx.json`.
Besides, [a cert of the KMS server](../../kms/src/plugins/aliyun/client/client_key_client/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`.
There are two ways to connect to a KMS instance.

#### With Client Key

In this case, 'client_type' is set to `client_key`.

To leverage the client key way to connect to a KMS instance, the following files should be provided.
- A client key file, like [the example](./alibaba/clientKey_KAAP.f4c8____.json), which is actually
an json with encrypted inside private key.
- A password file, like [the example](./alibaba/password_KAAP.f4c8____.json). This will be derived
into a symmetric key to decrypt the client key.
- A cert of the KMS server, [like the example](./alibaba/PrivateKmsCA_kst-shh64702cf2jvc_____.pem). This is
the public key cert of the KMS server.

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/client/ecs_ram_role_client/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).
#### With ECS RAM Role

In this case, 'client_type' is set to `ecs_ram_role`.

To leverage the ecs ram role way to connect to a KMS instance, the following files should be provided.
- `eca_ram_role_name`: refer to RAM role for ECS instances in a VPC network, where CDH runs. Can be set on Aliyun Console.
- `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).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ F6TFaabuFbw/42mjB3IB5RtR/+fz5je5WykII/ST4xQdKCU3reU6zZ3jU9erhHVP
FwLb4ltDYHMNlN1HCj31Z8NxWUtnIH7Xv1c93FTCFoeOc9fssNDgsy5FFXy0XIkm
1xrT6gQcxRKoDCC4LwEmLwV3S1OfrNXhgJzx1R65pahAzjJR0vgWU8NbkmY8ZZS8
i6LVVFDar9z0K/8UN1n+nl6saJSUuVFdWzHBRX4wYuUxQI+P
-----END CERTIFICATE-----
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"KeyId": "KAAP.f4c8****",
"PrivateKeyData": "MIIJ****"
}
Empty file.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//

use std::collections::BTreeMap;
use std::{collections::BTreeMap, env};

use async_trait::async_trait;
use base64::{engine::general_purpose::STANDARD, Engine};
Expand All @@ -14,7 +14,6 @@ use reqwest::{header::HeaderMap, Certificate, ClientBuilder};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sha2::{Digest, Sha256};
use tokio::fs;

mod config;
mod credential;
Expand All @@ -23,7 +22,6 @@ use crate::{Annotations, Decrypter, Encrypter, Getter, ProviderSettings};
use crate::{Error, Result};

use super::super::annotations::*;
use super::ALIYUN_IN_GUEST_DEFAULT_KEY_PATH;
use config::*;
use credential::*;

Expand Down Expand Up @@ -82,34 +80,23 @@ impl ClientKeyClient {
}

/// This new function is used by a in-pod client. The side-effect is to read the
/// [`ALIYUN_IN_GUEST_DEFAULT_KEY_PATH`] which is the by default path where the credential
/// to access kms is saved.
/// environment variable to get parameters, s.t.
/// - `ALIYUN_CLIENT_KEY`: Aliyun KMS client key
/// - `ALIYUN_PASSWORD`: The password of Aliyun KMS client key
/// - `ALIYUN_KMS_INSTANCE_CERT`: The public key cert to KMS instance
pub async fn from_provider_settings(provider_settings: &ProviderSettings) -> Result<Self> {
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 = env::var("ALIYUN_CLIENT_KEY")
.map_err(|_| Error::AliyunKmsError("ALIYUN_CLIENT_KEY not set in env".to_string()))?;
let pswd = env::var("ALIYUN_PASSWORD").map_err(|_| {
Error::AliyunKmsError("ALIYUN_CLIENT_KEY_PASSWORD not set in env".to_string())
})?;
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_pem = env::var("ALIYUN_KMS_INSTANCE_CERT").map_err(|_| {
Error::AliyunKmsError("ALIYUN_KMS_INSTANCE_CERT not set in env".to_string())
})?;

Self::new(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use std::{
collections::{BTreeMap, HashMap},
env,
fmt::Write,
};

Expand All @@ -14,7 +15,6 @@ use log::error;
use rand::{distributions::Alphanumeric, Rng};
use reqwest::{header::HeaderMap, ClientBuilder};
use serde_json::Value;
use tokio::fs;

mod config;
mod credential;
Expand All @@ -23,7 +23,6 @@ use crate::{Annotations, Getter, ProviderSettings};
use crate::{Error, Result};

use super::super::annotations::*;
use super::ALIYUN_IN_GUEST_DEFAULT_KEY_PATH;
use config::*;
use credential::*;

Expand Down Expand Up @@ -55,54 +54,16 @@ impl EcsRamRoleClient {
}

/// This new function is used by a in-pod client. The side-effect is to read the
/// [`ALIYUN_IN_GUEST_DEFAULT_KEY_PATH`] which is the by default path where the credential
/// to access kms is saved.
/// environment variable to get parameters, s.t.
/// - `ALIYUN_ECS_RAM_ROLE`: The ECS RAM Role name on Aliyun
/// - `ALIYUN_REGION_ID`: ECS Instance region id
pub async fn from_provider_settings(_provider_settings: &ProviderSettings) -> Result<Self> {
let ecs_ram_role_path = format!("{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/ecsRamRole.json");
let ecs_ram_role_name = env::var("ALIYUN_ECS_RAM_ROLE")
.map_err(|_| Error::AliyunKmsError("ALIYUN_ECS_RAM_ROLE not set in env".to_string()))?;
let region_id = env::var("ALIYUN_REGION_ID")
.map_err(|_| Error::AliyunKmsError("ALIYUN_REGION_ID not set in env".to_string()))?;

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}"
))
})?;

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_value) = ecs_ram_role_json.get("ecs_ram_role_name") {
match ecs_ram_role_name_value.as_str() {
Some(ecs_ram_role_name) => ecs_ram_role_name,
None => {
return Err(Error::AliyunKmsError(
"ecs ram role name value is not str.".to_string(),
))
}
}
} else {
return Err(Error::AliyunKmsError(
"ecs ram role name not exist.".to_string(),
));
};

let region_id = if let Some(region_id_value) = ecs_ram_role_json.get("region_id") {
match region_id_value.as_str() {
Some(region_id) => region_id,
None => {
return Err(Error::AliyunKmsError(
"region id value is not str.".to_string(),
))
}
}
} else {
return Err(Error::AliyunKmsError("region id not exist.".to_string()));
};

Self::new(ecs_ram_role_name, region_id)
Self::new(&ecs_ram_role_name, &region_id)
}

/// Export the [`ProviderSettings`] of the current client. This function is to be used
Expand Down
29 changes: 17 additions & 12 deletions confidential-data-hub/kms/src/plugins/aliyun/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
//

use async_trait::async_trait;
use const_format::concatcp;
use serde_json::json;

mod client_key_client;
mod ecs_ram_role_client;

use crate::plugins::_IN_GUEST_DEFAULT_KEY_PATH;
use crate::{Annotations, Decrypter, Encrypter, Getter, ProviderSettings};
use crate::{Error, Result};

Expand All @@ -31,8 +29,6 @@ pub struct AliyunKmsClient {
inner_client: Client,
}

const ALIYUN_IN_GUEST_DEFAULT_KEY_PATH: &str = concatcp!(_IN_GUEST_DEFAULT_KEY_PATH, "/aliyun");

impl AliyunKmsClient {
pub fn new(
client_key: &str,
Expand Down Expand Up @@ -70,8 +66,15 @@ impl AliyunKmsClient {
}

/// This new function is used by a in-pod client. The side-effect is to read the
/// [`ALIYUN_IN_GUEST_DEFAULT_KEY_PATH`] which is the by default path where the credential
/// to access kms is saved.
/// environment variable to get parameters. If the `client_type` is `client_key`,
/// will read the following env keys
/// - `ALIYUN_CLIENT_KEY`: Aliyun KMS client key
/// - `ALIYUN_PASSWORD`: The password of Aliyun KMS client key
/// - `ALIYUN_KMS_INSTANCE_CERT`: The public key cert to KMS instance
///
/// if the `client_type` is `ecs_ram_role`, will read the following env keys
/// - `ALIYUN_ECS_RAM_ROLE`: The ECS RAM Role name on Aliyun
/// - `ALIYUN_REGION_ID`: ECS Instance region id
pub async fn from_provider_settings(provider_settings: &ProviderSettings) -> Result<Self> {
let client_type = if let Some(client_type_value) = provider_settings.get("client_type") {
match client_type_value.as_str() {
Expand Down Expand Up @@ -209,10 +212,11 @@ mod tests {
#[tokio::test]
async fn key_lifetime(#[case] plaintext: &[u8]) {
let kid = "alias/test_key_id";
std::env::set_var("ALIYUN_CLIENT_KEY", "KAAP.f4c8****");
std::env::set_var("ALIYUN_KMS_INSTANCE_CERT", "kst-shh6****");
std::env::set_var("ALIYUN_PASSWORD", "xxx");
let provider_settings = json!({
"client_type": "client_key",
"client_key_id": "KAAP.f4c8****",
"kms_instance_id": "kst-shh6****",
"client_type": "client_key"
});
// init encrypter at user side
let provider_settings = provider_settings.as_object().unwrap().to_owned();
Expand Down Expand Up @@ -247,10 +251,11 @@ mod tests {
#[tokio::test]
async fn get_secret(#[case] client_type: &str) {
let secret_name = "test_secret";
std::env::set_var("ALIYUN_CLIENT_KEY", "KAAP.f4c8****");
std::env::set_var("ALIYUN_KMS_INSTANCE_CERT", "kst-shh6****");
std::env::set_var("ALIYUN_PASSWORD", "xxx");
let provider_settings = json!({
"client_type": client_type,
"client_key_id": "KAAP.f4c8****",
"kms_instance_id": "kst-shh6****",
"client_type": client_type
});
// init getter at user side
let provider_settings = provider_settings.as_object().unwrap().to_owned();
Expand Down

0 comments on commit 9370499

Please sign in to comment.