Skip to content

Commit ebb51cd

Browse files
Support SSE-C for S3 object storage (#941)
This PR introduces new args/envs allowing users to use SSE-C for encrypting the objects in S3. Fixes #919 --------- Co-authored-by: Nikhil Sinha <[email protected]>
1 parent f3a1e4e commit ebb51cd

File tree

3 files changed

+99
-4
lines changed

3 files changed

+99
-4
lines changed

Cargo.lock

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
[workspace]
22
members = ["server"]
33
resolver = "2"
4+
5+
[patch.crates-io]
6+
# object_store added support for SSE-C headers in:
7+
# - https://github.com/apache/arrow-rs/pull/6230
8+
# - https://github.com/apache/arrow-rs/pull/6260
9+
# But a new version hasn't been published to crates.io for this yet. So, we are using this patch temporarily.
10+
object_store = { git = "https://github.com/apache/arrow-rs.git", rev = "23b6ff9f432e8e29c08d47a315ba0b7cb8758225" }

server/src/storage/s3.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ use object_store::{ClientOptions, ObjectStore, PutPayload};
3232
use relative_path::{RelativePath, RelativePathBuf};
3333

3434
use std::collections::BTreeMap;
35+
use std::fmt::Display;
3536
use std::iter::Iterator;
3637
use std::path::Path as StdPath;
38+
use std::str::FromStr;
3739
use std::sync::Arc;
3840
use std::time::{Duration, Instant};
3941

@@ -84,6 +86,16 @@ pub struct S3Config {
8486
#[arg(long, env = "P_S3_BUCKET", value_name = "bucket-name", required = true)]
8587
pub bucket_name: String,
8688

89+
/// Server side encryption to use for operations with objects.
90+
/// Currently, this only supports SSE-C. Value should be
91+
/// like SSE-C:AES256:<base64_encoded_encryption_key>.
92+
#[arg(
93+
long,
94+
env = "P_S3_SSEC_ENCRYPTION_KEY",
95+
value_name = "ssec-encryption-key"
96+
)]
97+
pub ssec_encryption_key: Option<SSECEncryptionKey>,
98+
8799
/// Set client to send checksum header on every put request
88100
#[arg(
89101
long,
@@ -130,6 +142,72 @@ pub struct S3Config {
130142
pub metadata_endpoint: Option<String>,
131143
}
132144

145+
/// This represents the server side encryption to be
146+
/// used when working with S3 objects.
147+
#[derive(Debug, Clone)]
148+
pub enum SSECEncryptionKey {
149+
/// https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerSideEncryptionCustomerKeys.html
150+
SseC {
151+
// algorithm unused but being tracked separately to maintain
152+
// consistent interface via CLI if AWS adds any new algorithms
153+
// in future.
154+
_algorithm: ObjectEncryptionAlgorithm,
155+
base64_encryption_key: String,
156+
},
157+
}
158+
159+
impl FromStr for SSECEncryptionKey {
160+
type Err = String;
161+
162+
fn from_str(s: &str) -> Result<Self, Self::Err> {
163+
let parts = s.split(':').collect::<Vec<_>>();
164+
if parts.len() == 3 {
165+
let sse_type = parts[0];
166+
if sse_type != "SSE-C" {
167+
return Err("Only SSE-C is supported for object encryption for now".into());
168+
}
169+
170+
let algorithm = parts[1];
171+
let encryption_key = parts[2];
172+
173+
let alg = ObjectEncryptionAlgorithm::from_str(algorithm)?;
174+
175+
Ok(match alg {
176+
ObjectEncryptionAlgorithm::Aes256 => SSECEncryptionKey::SseC {
177+
_algorithm: alg,
178+
base64_encryption_key: encryption_key.to_owned(),
179+
},
180+
})
181+
} else {
182+
Err("Expected SSE-C:AES256:<base64_encryption_key>".into())
183+
}
184+
}
185+
}
186+
187+
#[derive(Debug, Clone, Copy)]
188+
pub enum ObjectEncryptionAlgorithm {
189+
Aes256,
190+
}
191+
192+
impl FromStr for ObjectEncryptionAlgorithm {
193+
type Err = String;
194+
195+
fn from_str(s: &str) -> Result<Self, Self::Err> {
196+
match s {
197+
"AES256" => Ok(ObjectEncryptionAlgorithm::Aes256),
198+
_ => Err("Invalid SSE algorithm. Following are supported: AES256".into()),
199+
}
200+
}
201+
}
202+
203+
impl Display for ObjectEncryptionAlgorithm {
204+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205+
match self {
206+
ObjectEncryptionAlgorithm::Aes256 => write!(f, "AES256"),
207+
}
208+
}
209+
}
210+
133211
impl S3Config {
134212
fn get_default_builder(&self) -> AmazonS3Builder {
135213
let mut client_options = ClientOptions::default()
@@ -160,6 +238,17 @@ impl S3Config {
160238
.with_secret_access_key(secret_key);
161239
}
162240

241+
if let Some(ssec_encryption_key) = &self.ssec_encryption_key {
242+
match ssec_encryption_key {
243+
SSECEncryptionKey::SseC {
244+
_algorithm,
245+
base64_encryption_key,
246+
} => {
247+
builder = builder.with_ssec_encryption(base64_encryption_key);
248+
}
249+
}
250+
}
251+
163252
if let Ok(relative_uri) = std::env::var(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
164253
builder = builder.with_config(
165254
AmazonS3ConfigKey::ContainerCredentialsRelativeUri,

0 commit comments

Comments
 (0)