Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KBS | Refactoring the codebase / update config file format / bring in plugin mechanism #514

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
4 changes: 3 additions & 1 deletion attestation-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use thiserror::Error;
const AS_WORK_DIR: &str = "AS_WORK_DIR";
const DEFAULT_WORK_DIR: &str = "/opt/confidential-containers/attestation-service";

#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct Config {
/// The location for Attestation Service to store data.
pub work_dir: PathBuf,
Expand All @@ -19,6 +19,7 @@ pub struct Config {
pub policy_engine: String,

/// Configurations for RVPS.
#[serde(default)]
pub rvps_config: RvpsConfig,

/// The Attestation Result Token Broker type.
Expand All @@ -28,6 +29,7 @@ pub struct Config {
pub attestation_token_broker: AttestationTokenBrokerType,

/// The Attestation Result Token Broker Config
#[serde(default)]
pub attestation_token_config: AttestationTokenConfig,
}

Expand Down
6 changes: 3 additions & 3 deletions attestation-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

pub mod config;
pub mod policy_engine;
mod rvps;
mod token;
mod utils;
pub mod rvps;
pub mod token;
pub mod utils;

use crate::token::AttestationTokenBroker;

Expand Down
6 changes: 4 additions & 2 deletions attestation-service/src/rvps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

use anyhow::Result;
use log::{info, warn};
use reference_value_provider_service::config::{Config as RvpsCrateConfig, DEFAULT_STORAGE_TYPE};
pub use reference_value_provider_service::config::{
Config as RvpsCrateConfig, DEFAULT_STORAGE_TYPE,
};
use serde::Deserialize;
use serde_json::{json, Value};
use thiserror::Error;
Expand Down Expand Up @@ -38,7 +40,7 @@ fn default_store_config() -> Value {
json!({})
}

#[derive(Deserialize, Clone, Debug)]
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub struct RvpsConfig {
/// Address of remote RVPS. If this field is given, a remote RVPS will be connected to.
/// If this field is not given, a built-in RVPS will be used.
Expand Down
15 changes: 10 additions & 5 deletions attestation-service/src/token/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
use anyhow::*;
use serde::Deserialize;
use serde_json::Value;
use simple::COCO_AS_ISSUER_NAME;
use strum::{Display, EnumString};

mod simple;

const DEFAULT_TOKEN_TIMEOUT: i64 = 5;
pub const COCO_AS_ISSUER_NAME: &str = "CoCo-Attestation-Service";
pub const DEFAULT_TOKEN_TIMEOUT: i64 = 5;

pub trait AttestationTokenBroker {
/// Issue an signed attestation token with custom claims.
Expand All @@ -23,7 +23,7 @@ pub trait AttestationTokenBroker {
fn pubkey_jwks(&self) -> Result<String>;
}

#[derive(Deserialize, Debug, Clone, EnumString, Display)]
#[derive(Deserialize, Debug, Clone, EnumString, Display, PartialEq)]
pub enum AttestationTokenBrokerType {
Simple,
}
Expand All @@ -42,9 +42,10 @@ impl AttestationTokenBrokerType {
}
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct AttestationTokenConfig {
/// The Attestation Result Token duration time(in minute)
#[serde(default = "default_duration_min")]
pub duration_min: i64,

#[serde(default = "default_issuer_name")]
Expand All @@ -53,11 +54,15 @@ pub struct AttestationTokenConfig {
pub signer: Option<TokenSignerConfig>,
}

fn default_duration_min() -> i64 {
DEFAULT_TOKEN_TIMEOUT
}

fn default_issuer_name() -> String {
COCO_AS_ISSUER_NAME.to_string()
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct TokenSignerConfig {
pub key_path: String,
pub cert_url: Option<String>,
Expand Down
3 changes: 1 addition & 2 deletions attestation-service/src/token/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use serde_json::{json, Value};

use crate::token::{AttestationTokenBroker, AttestationTokenConfig};

pub const COCO_AS_ISSUER_NAME: &str = "CoCo-Attestation-Service";
const RSA_KEY_BITS: u32 = 2048;
const SIMPLE_TOKEN_ALG: &str = "RS384";

Expand Down Expand Up @@ -92,6 +91,7 @@ impl AttestationTokenBroker for SimpleAttestationTokenBroker {
let header_value = json!({
"typ": "JWT",
"alg": SIMPLE_TOKEN_ALG,
"jwk": serde_json::from_str::<Value>(&self.pubkey_jwks()?)?["keys"][0].clone(),
});
let header_string = serde_json::to_string(&header_value)?;
let header_b64 = URL_SAFE_NO_PAD.encode(header_string.as_bytes());
Expand All @@ -109,7 +109,6 @@ impl AttestationTokenBroker for SimpleAttestationTokenBroker {
"iss": self.config.issuer_name.clone(),
"iat": now.unix_timestamp(),
"jti": id,
"jwk": serde_json::from_str::<Value>(&self.pubkey_jwks()?)?["keys"][0].clone(),
"nbf": now.unix_timestamp(),
"exp": exp.unix_timestamp(),
})
Expand Down
20 changes: 3 additions & 17 deletions kbs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,14 @@ edition.workspace = true
default = ["coco-as-builtin", "resource", "opa", "rustls"]

# Feature that allows to access resources from KBS
resource = ["rsa", "dep:openssl", "reqwest", "aes-gcm", "jsonwebtoken"]
resource = ["rsa", "reqwest", "aes-gcm", "jsonwebtoken"]

# Support a backend attestation service for KBS
as = []

# Use CoCo-AS as backend attestation service
coco-as = ["as"]

# Support resource policy for KBS
policy = []

# Use OPA/Rego as resource policy for KBS
opa = ["policy"]

# Use built-in CoCo-AS as backend attestation service
coco-as-builtin = ["coco-as", "attestation-service/default"]

Expand All @@ -36,17 +30,11 @@ coco-as-grpc = ["coco-as", "mobc", "tonic", "tonic-build", "prost"]
# Use Intel TA as backend attestation service
intel-trust-authority-as = ["as", "reqwest", "resource", "az-cvm-vtpm"]

# Use pure rust crypto stack for KBS
rustls = ["actix-web/rustls", "dep:rustls", "dep:rustls-pemfile"]

# Use openssl crypto stack for KBS
openssl = ["actix-web/openssl", "dep:openssl"]

# Use aliyun KMS as KBS backend
aliyun = ["kms/aliyun"]

[dependencies]
actix-web.workspace = true
actix-web = { workspace = true, features = ["openssl"] }
actix-web-httpauth.workspace = true
aes-gcm = { version = "0.10.1", optional = true }
anyhow.workspace = true
Expand All @@ -69,8 +57,6 @@ rand = "0.8.5"
regorus.workspace = true
reqwest = { workspace = true, features = ["json"], optional = true }
rsa = { version = "0.9.2", optional = true, features = ["sha2"] }
rustls = { version = "0.20.8", optional = true }
rustls-pemfile = { version = "1.0.4", optional = true }
scc = "2"
semver = "1.0.16"
serde = { workspace = true, features = ["derive"] }
Expand All @@ -81,7 +67,7 @@ time = { version = "0.3.23", features = ["std"] }
tokio.workspace = true
tonic = { workspace = true, optional = true }
uuid = { version = "1.2.2", features = ["serde", "v4"] }
openssl = { version = "0.10.46", optional = true }
openssl = "0.10.55"
az-cvm-vtpm = { version = "0.7.0", default-features = false, optional = true }

[dev-dependencies]
Expand Down
30 changes: 30 additions & 0 deletions kbs/src/admin/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2024 by Alibaba.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use std::path::PathBuf;

use serde::Deserialize;

pub const DEFAULT_INSECURE_API: bool = false;

#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct AdminConfig {
/// Public key used to authenticate the resource registration endpoint token (JWT).
/// Only JWTs signed with the corresponding private keys are authenticated.
pub auth_public_key: Option<PathBuf>,

/// Insecure HTTP APIs.
/// WARNING: Using this option enables KBS insecure APIs such as Resource Registration without
/// verifying the JWK.
pub insecure_api: bool,
}

impl Default for AdminConfig {
fn default() -> Self {
Self {
auth_public_key: None,
insecure_api: DEFAULT_INSECURE_API,
}
}
}
30 changes: 30 additions & 0 deletions kbs/src/admin/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2024 by Alibaba.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use log::error;
use strum::AsRefStr;
use thiserror::Error;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Error, AsRefStr, Debug)]
pub enum Error {
#[error("Admin Token verification failed")]
JwtVerificaitonFailed {
#[source]
source: jwt_simple::Error,
},

#[error("`auth_public_key` is not set in the config file")]
NoPublicKeyGiven,

#[error("Failed to parse admin public key")]
ParsePublicKey(#[from] jwt_simple::Error),

#[error("Failed to parse HTTP Auth Bearer header")]
ParseAuthHeaderFailed(#[from] actix_web::error::ParseError),

#[error("Read admin public key failed")]
ReadPublicKey(#[from] std::io::Error),
}
58 changes: 58 additions & 0 deletions kbs/src/admin/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) 2024 by Alibaba.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use config::AdminConfig;
use jwt_simple::{
claims::NoCustomClaims,
common::VerificationOptions,
prelude::{Ed25519PublicKey, EdDSAPublicKeyLike},
};

pub mod config;
pub mod error;
pub use error::*;
use log::warn;

#[derive(Default, Clone)]
pub struct Admin {
public_key: Option<Ed25519PublicKey>,
}

impl TryFrom<AdminConfig> for Admin {
type Error = Error;

fn try_from(value: AdminConfig) -> Result<Self> {
if value.insecure_api {
warn!("insecure admin APIs are enabled");
return Ok(Admin::default());
}

let key_path = value.auth_public_key.ok_or(Error::NoPublicKeyGiven)?;
let user_public_key_pem = std::fs::read_to_string(key_path)?;
let key = Ed25519PublicKey::from_pem(&user_public_key_pem)?;
Ok(Self {
public_key: Some(key),
})
}
}

impl Admin {
pub(crate) fn validate_auth(&self, request: &HttpRequest) -> Result<()> {
let Some(public_key) = &self.public_key else {
return Ok(());
};

let bearer = Authorization::<Bearer>::parse(request)?.into_scheme();

let token = bearer.token();

let _claims = public_key
.verify_token::<NoCustomClaims>(token, Some(VerificationOptions::default()))
.map_err(|e| Error::JwtVerificaitonFailed { source: e })?;

Ok(())
}
}
Loading