Skip to content

Commit

Permalink
AS: Optimize policy management mechanism
Browse files Browse the repository at this point in the history
1. Store the content of the policy file in memory instead of on disk to avoid potential security issues

2. Added API for listing and deleting policies

Signed-off-by: Jiale Zhang <[email protected]>
  • Loading branch information
jialez0 committed Mar 19, 2024
1 parent 9b8ef6c commit b382e40
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 64 deletions.
9 changes: 7 additions & 2 deletions attestation-service/attestation-service/src/bin/restful-as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use openssl::{
use strum::{AsRefStr, EnumString};
use tokio::sync::RwLock;

use crate::restful::{attestation, set_policy};
use crate::restful::{attestation, list_policy, remove_policy, set_policy};

mod restful;

Expand Down Expand Up @@ -73,7 +73,12 @@ async fn main() -> Result<()> {
let server = HttpServer::new(move || {
App::new()
.service(web::resource(WebApi::Attestation.as_ref()).route(web::post().to(attestation)))
.service(web::resource(WebApi::Policy.as_ref()).route(web::post().to(set_policy)))
.service(
web::resource(WebApi::Policy.as_ref())
.route(web::post().to(set_policy))
.route(web::get().to(list_policy))
.route(web::delete().to(remove_policy)),
)
.app_data(web::Data::clone(&attestation_service))
});

Expand Down
45 changes: 45 additions & 0 deletions attestation-service/attestation-service/src/bin/restful/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,48 @@ pub async fn set_policy(

Ok(HttpResponse::Ok().body(""))
}

pub async fn list_policy(
cocoas: web::Data<Arc<RwLock<AttestationService>>>,
) -> Result<HttpResponse> {
info!("get policy.");

let policy_list = cocoas
.read()
.await
.list_policy()
.await
.context("get policys")?;

let body = serde_json::to_string(&serde_json::json!({
"policys": policy_list,
}))
.context("serialize response body")?;

Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body))
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RemovePolicyRequest {
pub policy_ids: Vec<String>,
}

pub async fn remove_policy(
input: web::Json<RemovePolicyRequest>,
cocoas: web::Data<Arc<RwLock<AttestationService>>>,
) -> Result<HttpResponse> {
info!("set policy.");

for id in input.into_inner().policy_ids {
cocoas
.write()
.await
.remove_policy(id)
.await
.context("remove policy")?;
}

Ok(HttpResponse::Ok().body(""))
}
18 changes: 17 additions & 1 deletion attestation-service/attestation-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl AttestationService {

let policy_engine = PolicyEngineType::from_str(&config.policy_engine)
.map_err(|_| anyhow!("Policy Engine {} is not supported", &config.policy_engine))?
.to_policy_engine(config.work_dir.as_path())?;
.to_policy_engine()?;

let rvps = rvps::initialize_rvps_client(&config.rvps_config)
.await
Expand All @@ -121,6 +121,22 @@ impl AttestationService {
.map_err(|e| anyhow!("Cannot Set Policy: {:?}", e))
}

/// Remove Attestation Verification Policy.
pub async fn remove_policy(&mut self, policy_id: String) -> Result<()> {
self.policy_engine
.remove_policy(policy_id)
.await
.map_err(|e| anyhow!("Cannot Remove Policy: {:?}", e))
}

/// Get all Attestation Verification Policy.
pub async fn list_policy(&self) -> Result<Vec<crate::policy_engine::PolicyListEntry>> {
self.policy_engine
.list_policy()
.await
.map_err(|e| anyhow!("Cannot List Policy: {:?}", e))
}

/// Evaluate Attestation Evidence.
/// Issue an attestation results token which contain TCB status and TEE public key. Input parameters:
/// - `evidence`: TEE evidence bytes. This might not be the raw hardware evidence bytes. Definitions
Expand Down
20 changes: 16 additions & 4 deletions attestation-service/attestation-service/src/policy_engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use strum::EnumString;

pub mod opa;
Expand All @@ -14,6 +13,13 @@ pub struct SetPolicyInput {
pub policy: String,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct PolicyListEntry {
pub id: String,
pub digest: String,
pub content: String,
}

#[derive(Debug, EnumString, Deserialize)]
#[strum(ascii_case_insensitive)]
pub enum PolicyEngineType {
Expand All @@ -27,10 +33,12 @@ pub enum PolicyType {
}

impl PolicyEngineType {
pub fn to_policy_engine(&self, work_dir: &Path) -> Result<Box<dyn PolicyEngine + Send + Sync>> {
#[allow(dead_code)]
pub fn to_policy_engine(&self) -> Result<Box<dyn PolicyEngine + Send + Sync>> {
match self {
PolicyEngineType::OPA => Ok(Box::new(opa::OPA::new(work_dir.to_path_buf())?)
as Box<dyn PolicyEngine + Send + Sync>),
PolicyEngineType::OPA => {
Ok(Box::new(opa::OPA::new()?) as Box<dyn PolicyEngine + Send + Sync>)
}
}
}
}
Expand All @@ -53,4 +61,8 @@ pub trait PolicyEngine {
) -> Result<HashMap<String, (PolicyDigest, EvaluationResult)>>;

async fn set_policy(&mut self, input: SetPolicyInput) -> Result<()>;

async fn remove_policy(&mut self, policy_id: String) -> Result<()>;

async fn list_policy(&self) -> Result<Vec<PolicyListEntry>>;
}
102 changes: 49 additions & 53 deletions attestation-service/attestation-service/src/policy_engine/opa/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use crate::policy_engine::{PolicyEngine, PolicyType};
use crate::policy_engine::{PolicyEngine, PolicyListEntry, PolicyType};
use anyhow::{anyhow, bail, Context, Result};
use async_trait::async_trait;
use base64::Engine;
use log::debug;
use serde_json::Value;
use sha2::{Digest, Sha384};
use std::collections::HashMap;
use std::ffi::CStr;
use std::fs;
use std::os::raw::c_char;
use std::path::PathBuf;
use std::str::FromStr;

use super::{EvaluationResult, PolicyDigest, SetPolicyInput};

type PolicyMap = HashMap<String, String>;

// Link import cgo function
#[link(name = "cgo")]
extern "C" {
Expand All @@ -29,31 +30,16 @@ pub struct GoString {

#[derive(Debug)]
pub struct OPA {
policy_dir_path: PathBuf,
policy_map: PolicyMap,
}

impl OPA {
pub fn new(work_dir: PathBuf) -> Result<Self> {
let mut policy_dir_path = work_dir;

policy_dir_path.push("opa");
if !policy_dir_path.as_path().exists() {
fs::create_dir_all(&policy_dir_path)
.map_err(|e| anyhow!("Create policy dir failed: {:?}", e))?;
}

let mut default_policy_path = PathBuf::from(
&policy_dir_path
.to_str()
.ok_or_else(|| anyhow!("Policy DirPath to string failed"))?,
);
default_policy_path.push("default.rego");
if !default_policy_path.as_path().exists() {
let policy = std::include_str!("default_policy.rego").to_string();
fs::write(&default_policy_path, policy)?;
}
pub fn new() -> Result<Self> {
let default_policy = std::include_str!("default_policy.rego");
let mut policy_map = HashMap::new();
policy_map.insert("default".to_string(), default_policy.to_string());

Ok(Self { policy_dir_path })
Ok(Self { policy_map })
}
}

Expand All @@ -67,22 +53,16 @@ impl PolicyEngine for OPA {
) -> Result<HashMap<String, (PolicyDigest, EvaluationResult)>> {
let mut res = HashMap::new();

let policy_dir_path = self
.policy_dir_path
.to_str()
.ok_or_else(|| anyhow!("Miss Policy DirPath"))?;

for policy_id in policy_ids {
let policy_file_path = format!("{policy_dir_path}/{policy_id}.rego");

let policy = tokio::fs::read_to_string(policy_file_path)
.await
.map_err(|e| anyhow!("Read OPA policy file failed: {:?}", e))?;
let policy = self
.policy_map
.get(&policy_id)
.ok_or_else(|| anyhow!("Invalid Policy ID"))?;

let policy_hash = {
use sha2::Digest;
let mut hasher = sha2::Sha384::new();
hasher.update(&policy);
hasher.update(policy);
let hex = hasher.finalize().to_vec();
hex::encode(hex)
};
Expand Down Expand Up @@ -146,20 +126,37 @@ impl PolicyEngine for OPA {
bail!("OPA Policy Engine only support .rego policy");
}

let policy_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD
let policy_bytes = base64::engine::general_purpose::STANDARD
.decode(input.policy)
.map_err(|e| anyhow!("Base64 decode OPA policy string failed: {:?}", e))?;
let mut policy_file_path = PathBuf::from(
&self
.policy_dir_path
.to_str()
.ok_or_else(|| anyhow!("Policy DirPath to string failed"))?,
self.policy_map.insert(
input.policy_id,
String::from_utf8(policy_bytes)
.map_err(|_| anyhow!("Illegal policy content string"))?,
);
policy_file_path.push(format!("{}.rego", input.policy_id));
Ok(())
}

tokio::fs::write(&policy_file_path, policy_bytes)
.await
.map_err(|e| anyhow!("Write OPA policy to file failed: {:?}", e))
async fn remove_policy(&mut self, policy_id: String) -> Result<()> {
self.policy_map.remove(&policy_id);
Ok(())
}

async fn list_policy(&self) -> Result<Vec<PolicyListEntry>> {
let mut policy_list = Vec::new();

for (id, policy) in &self.policy_map {
let mut hasher = Sha384::new();
hasher.update(policy);
let digest = hasher.finalize().to_vec();
policy_list.push(PolicyListEntry {
id: id.to_string(),
digest: base64::engine::general_purpose::STANDARD.encode(digest),
content: policy.clone(),
});
}

Ok(policy_list)
}
}

Expand Down Expand Up @@ -187,10 +184,9 @@ mod tests {

#[tokio::test]
async fn test_evaluate() {
let opa = OPA {
policy_dir_path: PathBuf::from("./src/policy_engine/opa"),
};
let default_policy_id = "default_policy".to_string();
let default_policy_id = "default".to_string();

let opa = OPA::new().unwrap();

let reference_data: HashMap<String, Vec<String>> =
serde_json::from_str(&dummy_reference(5)).unwrap();
Expand All @@ -205,8 +201,8 @@ mod tests {
let res = res.expect("OPA execution should succeed");
// this expected value is calculated by `sha384sum`
let expected_digest = "c0e7929671fb6780387f54760d84d65d2ce96093dfb33efda21f5eb05afcda77bba444c02cd177b23a5d350716726157";
assert_eq!(expected_digest, res["default_policy"].0);
assert_json_eq!(json!({"allow":true}), res["default_policy"].1);
assert_eq!(expected_digest, res["default"].0);
assert_json_eq!(json!({"allow":true}), res["default"].1);

let res = opa
.evaluate(reference_data, dummy_input(0, 0), vec![default_policy_id])
Expand All @@ -217,15 +213,15 @@ mod tests {

#[tokio::test]
async fn test_set_policy() {
let mut opa = OPA::new(PathBuf::from("../test_data")).unwrap();
let mut opa = OPA::new().unwrap();
let policy = "package policy
default allow = true"
.to_string();

let input = SetPolicyInput {
r#type: "rego".to_string(),
policy_id: "test".to_string(),
policy: base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(policy),
policy: base64::engine::general_purpose::STANDARD.encode(policy),
};

assert!(opa.set_policy(input).await.is_ok());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Object {"platform": Object {"cca-platform-challenge": String("AQICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"), "cca-platform-sw-components": Array [Object {"measurement-type": String("BL"), "measurement-value": String("AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM="), "signer-id": String("BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ="), "version": String("3.4.2")}]}, "realm": Object {"cca-realm-extensible-measurements": Array [String("Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw=="), String("Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw=="), String("Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw=="), String("Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==")], "cca-realm-hash-algo-id": String("sha-256"), "cca-realm-initial-measurement": String("Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw=="), "cca-realm-personalization-value": String("QURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBRA=="), "cca-realm-public-key-hash-algo-id": String("sha-512")}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Object {"ccel": Object {"kernel": String("5b7aa6572f649714ff00b6a2b9170516a068fd1a0ba72aa8de27574131d454e6396d3bfa1727d9baf421618a942977fa"), "kernel_parameters": Object {"console": String("hvc0"), "root": String("/dev/vda1"), "rw": Null}}, "init_data": String("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), "quote": Object {"body": Object {"mr_config_id": String("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), "mr_owner": String("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), "mr_owner_config": String("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), "mr_seam": String("2fd279c16164a93dd5bf373d834328d46008c2b693af9ebb865b08b2ced320c9a89b4869a9fab60fbe9d0c5a5363c656"), "mr_td": String("705ee9381b8633a9fbe532b52345e8433343d2868959f57889d84ca377c395b689cac1599ccea1b7d420483a9ce5f031"), "mrsigner_seam": String("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), "report_data": String("7c71fe2c86eff65a7cf8dbc22b3275689fd0464a267baced1bf94fc1324656aeb755da3d44d098c0c87382f3a5f85b45c8a28fee1d3bdb38342bf96671501429"), "rtmr_0": String("e940da7c2712d2790e2961e00484f4fa8e6f9eed71361655ae22699476b14f9e63867eb41edd4b480fef0c59f496b288"), "rtmr_1": String("559cfcf42716ed6c40a48a73d5acb7da255435012f0a9f00fbe8c1c57612ede486a5684c4c9ff3ddf52315fcdca3a596"), "rtmr_2": String("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), "rtmr_3": String("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), "seam_attributes": String("0000000000000000"), "tcb_svn": String("03000500000000000000000000000000"), "td_attributes": String("0100001000000000"), "xfam": String("e742060000000000")}, "header": Object {"att_key_type": String("0200"), "reserved": String("00000000"), "tee_type": String("81000000"), "user_data": String("d099bfec0a477aa85a605dceabf2b10800000000"), "vendor_id": String("939a7233f79c4ca9940a0db3957f0607"), "version": String("0400")}}, "report_data": String("7c71fe2c86eff65a7cf8dbc22b3275689fd0464a267baced1bf94fc1324656aeb755da3d44d098c0c87382f3a5f85b45c8a28fee1d3bdb38342bf96671501429")}
Loading

0 comments on commit b382e40

Please sign in to comment.