-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
195 additions
and
106 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,105 +1,63 @@ | ||
use anyhow::{Context, Result}; | ||
use hex; | ||
use movement_signer::{ | ||
cryptography::{secp256k1::Secp256k1, ed25519::Ed25519}, | ||
Signing, | ||
cryptography::{secp256k1::Secp256k1, ed25519::Ed25519}, | ||
Signing, | ||
}; | ||
use signing_admin::{ | ||
application::{Application, HttpApplication}, | ||
backend::{aws::AwsBackend, vault::VaultBackend, Backend}, | ||
key_manager::KeyManager, | ||
application::{Application, HttpApplication}, | ||
backend::{aws::AwsBackend, vault::VaultBackend, Backend}, | ||
key_manager::KeyManager, | ||
}; | ||
use movement_signer_aws_kms::hsm::AwsKms; | ||
use movement_signer_hashicorp_vault::hsm::HashiCorpVault; | ||
use vaultrs::client::{VaultClient, VaultClientSettingsBuilder}; | ||
|
||
/// Enum to encapsulate different signers | ||
enum SignerBackend { | ||
Vault(HashiCorpVault<Ed25519>), | ||
Aws(AwsKms<Secp256k1>), | ||
} | ||
|
||
impl SignerBackend { | ||
/// Retrieve the public key from the signer | ||
async fn public_key(&self) -> Result<Vec<u8>> { | ||
match self { | ||
SignerBackend::Vault(signer) => { | ||
let public_key = signer.public_key().await?; | ||
Ok(public_key.as_bytes().to_vec()) | ||
} | ||
SignerBackend::Aws(signer) => { | ||
let public_key = signer.public_key().await?; | ||
Ok(public_key.as_bytes().to_vec()) | ||
} | ||
} | ||
} | ||
} | ||
use crate::cli::wal::{append_to_wal, update_wal_entry, update_wal_status, WalEntry}; | ||
|
||
pub async fn rotate_key( | ||
canonical_string: String, | ||
application_url: String, | ||
backend_name: String, | ||
canonical_string: String, | ||
application_url: String, | ||
backend_name: String, | ||
) -> Result<()> { | ||
let application = HttpApplication::new(application_url); | ||
|
||
let backend = match backend_name.as_str() { | ||
"vault" => Backend::Vault(VaultBackend::new()), | ||
"aws" => Backend::Aws(AwsBackend::new()), | ||
_ => return Err(anyhow::anyhow!("Unsupported backend: {}", backend_name)), | ||
}; | ||
|
||
let signer = match backend_name.as_str() { | ||
"vault" => { | ||
let vault_url = std::env::var("VAULT_URL") | ||
.context("Missing VAULT_URL environment variable")?; | ||
let vault_token = std::env::var("VAULT_TOKEN") | ||
.context("Missing VAULT_TOKEN environment variable")?; | ||
|
||
let client = VaultClient::new( | ||
VaultClientSettingsBuilder::default() | ||
.address(vault_url) | ||
.token(vault_token) | ||
.namespace(Some("admin".to_string())) | ||
.build() | ||
.context("Failed to build Vault client settings")?, | ||
) | ||
.context("Failed to create Vault client")?; | ||
|
||
SignerBackend::Vault(HashiCorpVault::<Ed25519>::new( | ||
client, | ||
canonical_string.clone(), | ||
"transit".to_string(), | ||
)) | ||
} | ||
"aws" => { | ||
let aws_config = aws_config::load_from_env().await; | ||
let client = aws_sdk_kms::Client::new(&aws_config); | ||
|
||
SignerBackend::Aws(AwsKms::<Secp256k1>::new( | ||
client, | ||
canonical_string.clone(), | ||
)) | ||
} | ||
_ => return Err(anyhow::anyhow!("Unsupported signer backend: {}", backend_name)), | ||
}; | ||
|
||
let key_manager = KeyManager::new(application, backend); | ||
|
||
key_manager | ||
.rotate_key(&canonical_string) | ||
.await | ||
.context("Failed to rotate the key")?; | ||
|
||
let public_key = signer | ||
.public_key() | ||
.await | ||
.context("Failed to fetch the public key from signer")?; | ||
|
||
key_manager | ||
.application | ||
.notify_public_key(public_key) | ||
.await | ||
.context("Failed to notify the application with the public key")?; | ||
|
||
println!("Key rotation and notification completed successfully."); | ||
Ok(()) | ||
// Initialize the application | ||
let application = HttpApplication::new(application_url); | ||
|
||
// Initialize the backend | ||
let backend = match backend_name.as_str() { | ||
"vault" => Backend::Vault(VaultBackend::new()), | ||
"aws" => Backend::Aws(AwsBackend::new()), | ||
_ => return Err(anyhow::anyhow!("Unsupported backend: {}", backend_name)), | ||
}; | ||
|
||
// Create the key manager | ||
let key_manager = KeyManager::new(application, backend); | ||
|
||
// Phase 1: Create New Key | ||
let new_key_id = key_manager.create_key(&canonical_string).await?; | ||
append_to_wal(WalEntry { | ||
operation: "rotate_key".to_string(), | ||
canonical_string: canonical_string.clone(), | ||
status: "key_created".to_string(), | ||
public_key: None, | ||
key_id: Some(new_key_id.clone()), | ||
})?; | ||
update_wal_status(&canonical_string, "key_created")?; | ||
|
||
// Fetch the public key from the new key | ||
let public_key = new_key_id.as_bytes().to_vec(); | ||
key_manager | ||
.notify_application(public_key.clone()) | ||
.await | ||
.context("Failed to notify application with the public key")?; | ||
update_wal_entry(&canonical_string, |entry| { | ||
entry.public_key = Some(hex::encode(&public_key)); | ||
})?; | ||
|
||
// Phase 2: Rotate Key | ||
update_wal_status(&canonical_string, "commit")?; | ||
key_manager | ||
.rotate_key(&new_key_id) | ||
.await | ||
.context("Failed to rotate key to the new ID")?; | ||
update_wal_status(&canonical_string, "completed")?; | ||
|
||
println!("Key rotation completed successfully."); | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use anyhow::Result; | ||
use serde::{Deserialize, Serialize}; | ||
use std::fs::{self, OpenOptions}; | ||
use std::io::{BufReader, BufWriter}; | ||
use std::path::Path; | ||
|
||
const WAL_FILE: &str = "wal.json"; | ||
|
||
#[derive(Serialize, Deserialize, Debug, Clone)] | ||
pub struct WalEntry { | ||
pub operation: String, | ||
pub canonical_string: String, | ||
pub status: String, | ||
pub public_key: Option<String>, // Store the public key in base64 or hex | ||
pub key_id: Option<String>, // Store the AWS or Vault Key ID | ||
} | ||
|
||
pub fn read_wal() -> Result<Vec<WalEntry>> { | ||
if !Path::new(WAL_FILE).exists() { | ||
return Ok(vec![]); | ||
} | ||
let file = fs::File::open(WAL_FILE)?; | ||
let reader = BufReader::new(file); | ||
let entries: Vec<WalEntry> = serde_json::from_reader(reader)?; | ||
Ok(entries) | ||
} | ||
|
||
pub fn write_wal(entries: &[WalEntry]) -> Result<()> { | ||
let file = OpenOptions::new() | ||
.create(true) | ||
.write(true) | ||
.truncate(true) | ||
.open(WAL_FILE)?; | ||
let writer = BufWriter::new(file); | ||
serde_json::to_writer(writer, entries)?; | ||
Ok(()) | ||
} | ||
|
||
pub fn append_to_wal(entry: WalEntry) -> Result<()> { | ||
let mut entries = read_wal()?; | ||
entries.push(entry); | ||
write_wal(&entries) | ||
} | ||
|
||
pub fn update_wal_status(canonical_string: &str, new_status: &str) -> Result<()> { | ||
let mut entries = read_wal()?; | ||
for entry in &mut entries { | ||
if entry.canonical_string == canonical_string { | ||
entry.status = new_status.to_string(); | ||
} | ||
} | ||
write_wal(&entries) | ||
} | ||
|
||
pub fn update_wal_entry<F>(canonical_string: &str, update_fn: F) -> Result<()> | ||
where | ||
F: Fn(&mut WalEntry), | ||
{ | ||
let mut entries = read_wal()?; | ||
for entry in &mut entries { | ||
if entry.canonical_string == canonical_string { | ||
update_fn(entry); | ||
} | ||
} | ||
write_wal(&entries) | ||
} |
Oops, something went wrong.