diff --git a/docs/Configuration.md b/docs/Configuration.md index 78eb38e26..2bbf685e5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -123,7 +123,12 @@ bucket = "name" endpoint = "s3-us-east-1.amazonaws.com" use_ssl = true key_prefix = "s3prefix" +# SSE-S3 with an S3-managed key (AES256). server_side_encryption = false +# SSE-KMS with the AWS-managed KMS key (aws/s3). +server_side_encryption_aws_kms = false +# SSE-KMS with a customer-managed KMS key (takes precedence when set). +# server_side_encryption_kms_key_id = "arn:aws:kms:us-east-1:111:key/abc" [cache.webdav] endpoint = "http://192.168.10.42:80/some/webdav.php" diff --git a/docs/S3.md b/docs/S3.md index 8d91e5956..22addedbc 100644 --- a/docs/S3.md +++ b/docs/S3.md @@ -9,7 +9,12 @@ If you want to use S3 storage for the sccache cache, you need to set the followi If your endpoint requires HTTPS/TLS, set `SCCACHE_S3_USE_SSL=true`. If you don't need a secure network layer, HTTP (`SCCACHE_S3_USE_SSL=false`) might be better for performance. -Enable server-side encryption with s3 managed key (SSE-S3), set `SCCACHE_S3_SERVER_SIDE_ENCRYPTION=true`. +Server-side encryption can be enabled in one of three modes. They are resolved in the following order of precedence: + +- **SSE-KMS with a customer-managed KMS key:** set `SCCACHE_S3_SERVER_SIDE_ENCRYPTION_KMS_KEY_ID` to the KMS key ID/ARN. This implies `aws:kms` and takes precedence over the other settings. +- **SSE-KMS with the AWS-managed KMS key (`aws/s3`):** set `SCCACHE_S3_SERVER_SIDE_ENCRYPTION_AWS_KMS=true`. +- **SSE-S3 with an S3-managed key (AES256):** set `SCCACHE_S3_SERVER_SIDE_ENCRYPTION=true`. + More details about encryption [here](https://opendal.apache.org/docs/services/s3/#server-side-encryption) and documentation [here](https://docs.rs/opendal/latest/opendal/services/struct.S3.html#method.server_side_encryption_with_s3_key). You can also define a prefix that will be prepended to the keys of all cache objects created and read within the S3 bucket, effectively creating a scope. To do that use the `SCCACHE_S3_KEY_PREFIX` environment variable. This can be useful when sharing a bucket with another application. diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 24bc4379f..c8a4f2754 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -528,6 +528,8 @@ pub fn build_single_cache( .with_endpoint(c.endpoint.clone()) .with_use_ssl(c.use_ssl) .with_server_side_encryption(c.server_side_encryption) + .with_server_side_encryption_aws_kms(c.server_side_encryption_aws_kms) + .with_server_side_encryption_kms_key_id(c.server_side_encryption_kms_key_id.clone()) .with_enable_virtual_host_style(c.enable_virtual_host_style) .build() .map_err(|err| anyhow!("create s3 cache failed: {err:?}"))?; diff --git a/src/cache/s3.rs b/src/cache/s3.rs index 5af641f08..08992d40c 100644 --- a/src/cache/s3.rs +++ b/src/cache/s3.rs @@ -26,6 +26,8 @@ pub struct S3Cache { endpoint: Option, use_ssl: Option, server_side_encryption: Option, + server_side_encryption_aws_kms: Option, + server_side_encryption_kms_key_id: Option, enable_virtual_host_style: Option, } @@ -39,6 +41,8 @@ impl S3Cache { endpoint: None, use_ssl: None, server_side_encryption: None, + server_side_encryption_aws_kms: None, + server_side_encryption_kms_key_id: None, enable_virtual_host_style: None, } } @@ -58,6 +62,20 @@ impl S3Cache { self.server_side_encryption = server_side_encryption; self } + pub fn with_server_side_encryption_aws_kms( + mut self, + server_side_encryption_aws_kms: Option, + ) -> Self { + self.server_side_encryption_aws_kms = server_side_encryption_aws_kms; + self + } + pub fn with_server_side_encryption_kms_key_id( + mut self, + server_side_encryption_kms_key_id: Option, + ) -> Self { + self.server_side_encryption_kms_key_id = server_side_encryption_kms_key_id; + self + } pub fn with_enable_virtual_host_style( mut self, enable_virtual_host_style: Option, @@ -93,7 +111,14 @@ impl S3Cache { builder = builder.endpoint(&endpoint_resolver(endpoint, self.use_ssl)?); } - if self.server_side_encryption.unwrap_or_default() { + if let Some(kms_key_id) = &self.server_side_encryption_kms_key_id { + // SSE-KMS with a customer-managed KMS key. + builder = builder.server_side_encryption_with_customer_managed_kms_key(kms_key_id); + } else if self.server_side_encryption_aws_kms.unwrap_or_default() { + // SSE-KMS with the AWS-managed KMS key (aws/s3). + builder = builder.server_side_encryption_with_aws_managed_kms_key(); + } else if self.server_side_encryption.unwrap_or_default() { + // SSE-S3 with an S3-managed key (AES256). builder = builder.server_side_encryption_with_s3_key(); } diff --git a/src/config.rs b/src/config.rs index f6c3660ef..48e61c811 100644 --- a/src/config.rs +++ b/src/config.rs @@ -464,6 +464,8 @@ pub struct S3CacheConfig { pub endpoint: Option, pub use_ssl: Option, pub server_side_encryption: Option, + pub server_side_encryption_aws_kms: Option, + pub server_side_encryption_kms_key_id: Option, pub enable_virtual_host_style: Option, #[serde(default)] pub rw_mode: CacheModeConfig, @@ -886,6 +888,10 @@ fn config_from_env() -> Result { let no_credentials = bool_from_env_var("SCCACHE_S3_NO_CREDENTIALS")?.unwrap_or(false); let use_ssl = bool_from_env_var("SCCACHE_S3_USE_SSL")?; let server_side_encryption = bool_from_env_var("SCCACHE_S3_SERVER_SIDE_ENCRYPTION")?; + let server_side_encryption_aws_kms = + bool_from_env_var("SCCACHE_S3_SERVER_SIDE_ENCRYPTION_AWS_KMS")?; + let server_side_encryption_kms_key_id = + string_from_env_var("SCCACHE_S3_SERVER_SIDE_ENCRYPTION_KMS_KEY_ID"); let endpoint = string_from_env_var("SCCACHE_ENDPOINT"); let key_prefix = key_prefix_from_env_var("SCCACHE_S3_KEY_PREFIX"); let enable_virtual_host_style = bool_from_env_var("SCCACHE_S3_ENABLE_VIRTUAL_HOST_STYLE")?; @@ -900,6 +906,8 @@ fn config_from_env() -> Result { endpoint, use_ssl, server_side_encryption, + server_side_encryption_aws_kms, + server_side_encryption_kms_key_id, enable_virtual_host_style, rw_mode, }) @@ -2353,6 +2361,44 @@ fn test_s3_no_credentials_valid_false() { } } +#[test] +#[serial(config_from_env)] +#[cfg(feature = "s3")] +fn test_s3_sse_kms_from_env() { + unsafe { + env::set_var("SCCACHE_BUCKET", "my-bucket"); + env::set_var("SCCACHE_S3_SERVER_SIDE_ENCRYPTION_AWS_KMS", "true"); + env::set_var( + "SCCACHE_S3_SERVER_SIDE_ENCRYPTION_KMS_KEY_ID", + "arn:aws:kms:us-east-1:111:key/abc", + ); + } + + let cfg = config_from_env(); + + unsafe { + env::remove_var("SCCACHE_BUCKET"); + env::remove_var("SCCACHE_S3_SERVER_SIDE_ENCRYPTION_AWS_KMS"); + env::remove_var("SCCACHE_S3_SERVER_SIDE_ENCRYPTION_KMS_KEY_ID"); + } + + let env_cfg = cfg.unwrap(); + match env_cfg.cache.s3 { + Some(S3CacheConfig { + server_side_encryption_aws_kms, + ref server_side_encryption_kms_key_id, + .. + }) => { + assert_eq!(server_side_encryption_aws_kms, Some(true)); + assert_eq!( + server_side_encryption_kms_key_id.as_deref(), + Some("arn:aws:kms:us-east-1:111:key/abc") + ); + } + None => unreachable!(), + } +} + #[test] #[serial(config_from_env)] #[cfg(feature = "gcs")] @@ -2528,6 +2574,8 @@ key_prefix = "cosprefix" key_prefix: "s3prefix".into(), no_credentials: true, server_side_encryption: Some(false), + server_side_encryption_aws_kms: None, + server_side_encryption_kms_key_id: None, enable_virtual_host_style: None, rw_mode: CacheModeConfig::ReadWrite, }), @@ -3025,6 +3073,8 @@ fn test_get_cache_levels_single_cache() { endpoint: None, use_ssl: None, server_side_encryption: None, + server_side_encryption_aws_kms: None, + server_side_encryption_kms_key_id: None, enable_virtual_host_style: None, rw_mode: CacheModeConfig::ReadWrite, }),