Record SSH-signed keychain init ops
This commit is contained in:
parent
91c65e367d
commit
48a83c5a26
12 changed files with 283 additions and 18 deletions
|
|
@ -14,6 +14,7 @@ tokio.workspace = true
|
|||
tracing.workspace = true
|
||||
geth-auth = { path = "../geth-auth" }
|
||||
geth-cas = { path = "../geth-cas" }
|
||||
geth-codec = { path = "../geth-codec" }
|
||||
geth-config = { path = "../geth-config" }
|
||||
geth-control = { path = "../geth-control" }
|
||||
geth-crypto = { path = "../geth-crypto" }
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use geth_discovery::{
|
|||
};
|
||||
use geth_document::{DocumentResource, DocumentState};
|
||||
use geth_iroh::{EndpointStatus, GethIrohConfig, GethIrohEndpoint, GethRelayMode};
|
||||
use geth_keychain::{KeychainOp, KeychainOpKind};
|
||||
use geth_keychain::{KeychainOp, KeychainOpKind, KeychainOpSignature};
|
||||
use geth_kv::{KvEntry, KvResource, KvSyncEntry};
|
||||
use geth_pipe::{PipeConnection, PipeListener};
|
||||
use geth_pubsub::PubsubMessage;
|
||||
|
|
@ -34,8 +34,8 @@ use geth_ssh_identity::{
|
|||
use geth_ssh_proxy::SshProxyConnection;
|
||||
use geth_store::{
|
||||
Store, StoredAuthOp, StoredDbResource, StoredDocumentResource, StoredFileConflict,
|
||||
StoredFileRoot, StoredKeychainOp, StoredKvEntry, StoredKvStore, StoredModuleState,
|
||||
StoredPeerCard, StoredResource, StoredResourceSecret, StoredSshCertRequest,
|
||||
StoredFileRoot, StoredKeychainOp, StoredKeychainSignature, StoredKvEntry, StoredKvStore,
|
||||
StoredModuleState, StoredPeerCard, StoredResource, StoredResourceSecret, StoredSshCertRequest,
|
||||
StoredSshCertificate, StoredSshRevocation,
|
||||
};
|
||||
use geth_types::{
|
||||
|
|
@ -63,6 +63,8 @@ pub enum NodeError {
|
|||
Db(#[from] geth_db::DbError),
|
||||
#[error("control error: {0}")]
|
||||
Control(#[from] geth_control::ControlError),
|
||||
#[error("codec error: {0}")]
|
||||
Codec(#[from] geth_codec::CodecError),
|
||||
#[error("json error: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
#[error("io error: {0}")]
|
||||
|
|
@ -2860,7 +2862,10 @@ pub fn handle_request(
|
|||
conflict: file_conflict_from_stored(conflict)?,
|
||||
})
|
||||
}
|
||||
ControlRequest::KeychainInit { admin_key_path } => {
|
||||
ControlRequest::KeychainInit {
|
||||
admin_key_path,
|
||||
signing_key_path,
|
||||
} => {
|
||||
let mut ops = Vec::new();
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let init = KeychainOp {
|
||||
|
|
@ -2871,7 +2876,7 @@ pub fn handle_request(
|
|||
store_keychain_op(&store, &init)?;
|
||||
ops.push(init);
|
||||
|
||||
if let Some(admin_key_path) = admin_key_path {
|
||||
if let Some(admin_key_path) = admin_key_path.as_ref() {
|
||||
let public_key = std::fs::read_to_string(admin_key_path)?;
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let admin_key = KeyId::new(ssh_public_key_fingerprint(&public_key));
|
||||
|
|
@ -2884,7 +2889,25 @@ pub fn handle_request(
|
|||
ops.push(op);
|
||||
}
|
||||
|
||||
Ok(ControlResponse::KeychainInitialized { ops })
|
||||
let signatures = if let Some(signing_key_path) = signing_key_path {
|
||||
let signer =
|
||||
keychain_signer_from_paths(&signing_key_path, admin_key_path.as_deref())?;
|
||||
let mut signatures = Vec::new();
|
||||
for op in &ops {
|
||||
signatures.push(sign_keychain_op_with_ssh(
|
||||
&store,
|
||||
node,
|
||||
op,
|
||||
&signing_key_path,
|
||||
&signer,
|
||||
)?);
|
||||
}
|
||||
signatures
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Ok(ControlResponse::KeychainInitialized { ops, signatures })
|
||||
}
|
||||
ControlRequest::KeychainStatus => {
|
||||
let view = geth_keychain::reduce_keychain_ops(&load_keychain_ops(&store)?);
|
||||
|
|
@ -3994,6 +4017,64 @@ fn store_keychain_op(store: &Store, op: &KeychainOp) -> Result<(), NodeError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn keychain_signer_from_paths(
|
||||
signing_key_path: &Path,
|
||||
admin_key_path: Option<&Path>,
|
||||
) -> Result<KeyId, NodeError> {
|
||||
let public_key_path = admin_key_path
|
||||
.map(Path::to_path_buf)
|
||||
.unwrap_or_else(|| Path::new(&format!("{}.pub", signing_key_path.display())).to_path_buf());
|
||||
let public_key = std::fs::read_to_string(public_key_path)?;
|
||||
Ok(KeyId::new(ssh_public_key_fingerprint(&public_key)))
|
||||
}
|
||||
|
||||
fn sign_keychain_op_with_ssh(
|
||||
store: &Store,
|
||||
node: &LocalNode,
|
||||
op: &KeychainOp,
|
||||
signing_key_path: &Path,
|
||||
signer: &KeyId,
|
||||
) -> Result<KeychainOpSignature, NodeError> {
|
||||
geth_ssh_identity::ensure_ssh_keygen_available()?;
|
||||
let signature_dir = node.paths.home().join("keychain-signatures");
|
||||
std::fs::create_dir_all(&signature_dir)?;
|
||||
let payload_path = signature_dir.join(format!(
|
||||
"{}.payload",
|
||||
geth_crypto::blake3_hex(op.id.as_str().as_bytes())
|
||||
));
|
||||
std::fs::write(&payload_path, geth_keychain::keychain_signing_payload(op)?)?;
|
||||
let output = geth_ssh_identity::sign_command(
|
||||
signing_key_path,
|
||||
geth_keychain::KEYCHAIN_SIGNATURE_NAMESPACE,
|
||||
&payload_path,
|
||||
)
|
||||
.output()?;
|
||||
if !output.status.success() {
|
||||
return Err(geth_ssh_identity::SshIdentityError::SshKeygenFailed(
|
||||
String::from_utf8_lossy(&output.stderr).trim().to_owned(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
let signature_path = Path::new(&format!("{}.sig", payload_path.display())).to_path_buf();
|
||||
let signature_bytes = std::fs::read(signature_path)?;
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let signature = KeychainOpSignature {
|
||||
op_id: op.id.clone(),
|
||||
signer: signer.clone(),
|
||||
namespace: geth_keychain::KEYCHAIN_SIGNATURE_NAMESPACE.to_owned(),
|
||||
signature: signature_bytes,
|
||||
created_at,
|
||||
};
|
||||
store.insert_keychain_signature(&StoredKeychainSignature {
|
||||
op_id: signature.op_id.to_string(),
|
||||
signer: signature.signer.to_string(),
|
||||
namespace: signature.namespace.clone(),
|
||||
signature: signature.signature.clone(),
|
||||
created_at_ms: signature.created_at.0,
|
||||
})?;
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
fn load_keychain_ops(store: &Store) -> Result<Vec<KeychainOp>, NodeError> {
|
||||
store
|
||||
.list_keychain_ops()?
|
||||
|
|
|
|||
Loading…
Reference in a new issue