Feat (kv2): Support Sealing
This commit is contained in:
parent
1accd45648
commit
4d342e8b99
3 changed files with 56 additions and 16 deletions
|
|
@ -21,10 +21,12 @@ CREATE TABLE kv2_secret_version (
|
||||||
secret_path TEXT NOT NULL,
|
secret_path TEXT NOT NULL,
|
||||||
|
|
||||||
version_number INTEGER NOT NULL CHECK ( version_number > 0 ),
|
version_number INTEGER NOT NULL CHECK ( version_number > 0 ),
|
||||||
secret_data TEXT NOT NULL,
|
|
||||||
created_time DATETIME NOT NULL,
|
created_time DATETIME NOT NULL,
|
||||||
deletion_time DATETIME,
|
deletion_time DATETIME,
|
||||||
|
|
||||||
|
encrypted_data BLOB NOT NULL,
|
||||||
|
nonce BLOB NOT NULL CHECK ( length(nonce) = 12 ),
|
||||||
|
|
||||||
PRIMARY KEY (engine_path, secret_path, version_number),
|
PRIMARY KEY (engine_path, secret_path, version_number),
|
||||||
FOREIGN KEY (engine_path, secret_path) REFERENCES kv2_metadata(engine_path, secret_path)
|
FOREIGN KEY (engine_path, secret_path) REFERENCES kv2_metadata(engine_path, secret_path)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::structs::KvV2WriteRequest;
|
||||||
use crate::{
|
use crate::{
|
||||||
common::HttpError, engines::{
|
common::HttpError, engines::{
|
||||||
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath
|
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath
|
||||||
}, DbPool
|
}, storage::sealing::{seal, unseal}, DbPool
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
Extension, Json,
|
Extension, Json,
|
||||||
|
|
@ -10,9 +10,9 @@ use axum::{
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, NoContent, Response},
|
response::{IntoResponse, NoContent, Response},
|
||||||
};
|
};
|
||||||
use log::{error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use time::UtcDateTime;
|
use time::{OffsetDateTime, UtcDateTime};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GetDataQuery {
|
pub struct GetDataQuery {
|
||||||
|
|
@ -22,25 +22,55 @@ pub struct GetDataQuery {
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unluckily needed as `sqlx::query_as!()` does not support FromRow derivations
|
||||||
|
struct SecretDataInternal {
|
||||||
|
pub created_time: OffsetDateTime,
|
||||||
|
pub deletion_time: Option<OffsetDateTime>,
|
||||||
|
pub version_number: i64,
|
||||||
|
pub secret_path: String,
|
||||||
|
|
||||||
|
pub nonce: Vec<u8>,
|
||||||
|
pub encrypted_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecretDataInternal {
|
||||||
|
pub async fn into_external(self) -> KvSecretData {
|
||||||
|
let secret = unseal(self.nonce, &self.encrypted_data).await;
|
||||||
|
KvSecretData {
|
||||||
|
created_time: self.created_time,
|
||||||
|
deletion_time: self.deletion_time,
|
||||||
|
version_number: self.version_number,
|
||||||
|
secret_path: self.secret_path,
|
||||||
|
secret_data: secret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_data(
|
pub async fn get_data(
|
||||||
State(pool): State<DbPool>,
|
State(pool): State<DbPool>,
|
||||||
Query(params): Query<GetDataQuery>,
|
Query(params): Query<GetDataQuery>,
|
||||||
Path(path): Path<String>,
|
Path(path): Path<String>,
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
) -> Result<Response, ()> {
|
) -> Result<Response, ()> {
|
||||||
let res = if params.version == 0 {
|
debug!(
|
||||||
|
"Get request: Engine: {}, path: {}",
|
||||||
|
engine_path,
|
||||||
|
path,
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = if params.version != 0 {
|
||||||
// With specific version
|
// With specific version
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
KvSecretData,
|
SecretDataInternal,
|
||||||
r#"SELECT secret_data, created_time, deletion_time, version_number, secret_path
|
r#"SELECT nonce, encrypted_data, created_time, deletion_time, version_number, secret_path
|
||||||
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
||||||
AND version_number = $3"#,
|
AND version_number = $3"#,
|
||||||
engine_path, path, params.version).fetch_one(&pool).await
|
engine_path, path, params.version).fetch_one(&pool).await
|
||||||
} else {
|
} else {
|
||||||
// Without specific version
|
// Without specific version
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
KvSecretData,
|
SecretDataInternal,
|
||||||
r#"SELECT secret_data, created_time, deletion_time, version_number, secret_path
|
r#"SELECT nonce, encrypted_data, created_time, deletion_time, version_number, secret_path
|
||||||
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
||||||
ORDER BY version_number DESC LIMIT 1"#,
|
ORDER BY version_number DESC LIMIT 1"#,
|
||||||
engine_path, path).fetch_one(&pool).await
|
engine_path, path).fetch_one(&pool).await
|
||||||
|
|
@ -48,8 +78,10 @@ pub async fn get_data(
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(secret_content) => {
|
Ok(secret_content) => {
|
||||||
|
let secret_content = secret_content.into_external().await;
|
||||||
|
let inner = secret_content.secret_data;
|
||||||
let data = Wrapper {
|
let data = Wrapper {
|
||||||
data: serde_json::from_str(&secret_content.secret_data).unwrap(),
|
data: serde_json::from_str(&inner).unwrap(),
|
||||||
};
|
};
|
||||||
let return_secret = KvSecretRes {
|
let return_secret = KvSecretRes {
|
||||||
data,
|
data,
|
||||||
|
|
@ -69,7 +101,7 @@ pub async fn get_data(
|
||||||
"Secret not found within kv2 engine",
|
"Secret not found within kv2 engine",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => panic!("{e:?}"),
|
_ => panic!("Unhandled error: {e:?}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +123,11 @@ pub async fn post_data(
|
||||||
let created_time = time::UtcDateTime::now();
|
let created_time = time::UtcDateTime::now();
|
||||||
let ts = created_time.unix_timestamp();
|
let ts = created_time.unix_timestamp();
|
||||||
|
|
||||||
|
let content = serde_json::to_string(&secret.data).unwrap();
|
||||||
|
|
||||||
|
let (nonce, enc) = seal(&content).await;
|
||||||
|
let nonce = nonce.as_slice();
|
||||||
|
|
||||||
let mut tx = pool.begin().await.unwrap();
|
let mut tx = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let res_m = sqlx::query!("
|
let res_m = sqlx::query!("
|
||||||
|
|
@ -103,7 +140,7 @@ pub async fn post_data(
|
||||||
"src/engines/kv/post_secret.sql",
|
"src/engines/kv/post_secret.sql",
|
||||||
engine_path,
|
engine_path,
|
||||||
kv_path,
|
kv_path,
|
||||||
secret.data,
|
nonce, enc,
|
||||||
ts,
|
ts,
|
||||||
secret.version,
|
secret.version,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@ WITH latest_version AS (
|
||||||
FROM kv2_secret_version
|
FROM kv2_secret_version
|
||||||
WHERE engine_path = $1 AND secret_path = $2 -- engine_path AND secret_path
|
WHERE engine_path = $1 AND secret_path = $2 -- engine_path AND secret_path
|
||||||
)
|
)
|
||||||
INSERT INTO kv2_secret_version (engine_path, secret_path, secret_data, created_time, version_number)
|
INSERT INTO kv2_secret_version (engine_path, secret_path, nonce, encrypted_data, created_time, version_number)
|
||||||
VALUES (
|
VALUES (
|
||||||
$1, -- engine_path
|
$1, -- engine_path
|
||||||
$2, -- secret_path
|
$2, -- secret_path
|
||||||
$3, -- secret_data
|
$3, -- nonce
|
||||||
$4, -- created_time
|
$4, -- encrypted_data
|
||||||
|
$5, -- created_time
|
||||||
CASE -- Use provided version if given
|
CASE -- Use provided version if given
|
||||||
WHEN $5 IS NOT NULL THEN $5 -- version_number (optional)
|
WHEN $6 IS NOT NULL THEN $6 -- version_number (optional)
|
||||||
ELSE COALESCE((SELECT max_version FROM latest_version) + 1, 1) -- otherwise 1
|
ELSE COALESCE((SELECT max_version FROM latest_version) + 1, 1) -- otherwise 1
|
||||||
END -- version_number logic
|
END -- version_number logic
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue