Harden signed node authorization flow
This commit is contained in:
parent
27a79768e4
commit
1336fa38e8
9 changed files with 382 additions and 55 deletions
|
|
@ -5662,16 +5662,35 @@ pub fn handle_request(
|
|||
resource,
|
||||
capability,
|
||||
grant_id,
|
||||
signing_key_path,
|
||||
admin_key_path,
|
||||
} => {
|
||||
let target = resolve_keychain_node(&store, &target)?;
|
||||
let op = record_node_grant(&store, target.as_str(), &resource, &capability, grant_id)?;
|
||||
let op = node_grant_op(target.as_str(), &resource, &capability, grant_id);
|
||||
let signatures = if let Some(signing_key_path) = signing_key_path {
|
||||
store_and_sign_auth_ops(
|
||||
&store,
|
||||
node,
|
||||
std::slice::from_ref(&op),
|
||||
&signing_key_path,
|
||||
admin_key_path.as_deref(),
|
||||
)?
|
||||
} else {
|
||||
store_auth_op(&store, &op)?;
|
||||
Vec::new()
|
||||
};
|
||||
Ok(ControlResponse::NodeGrantUpdated {
|
||||
op,
|
||||
signatures: Vec::new(),
|
||||
note: "recorded resource-scoped node capability grant; auth explain can show the grant path".to_owned(),
|
||||
signatures,
|
||||
note: "recorded signed resource-scoped node capability grant; auth explain can show the grant path".to_owned(),
|
||||
})
|
||||
}
|
||||
ControlRequest::NodeRevokeGrant { resource, grant_id } => {
|
||||
ControlRequest::NodeRevokeGrant {
|
||||
resource,
|
||||
grant_id,
|
||||
signing_key_path,
|
||||
admin_key_path,
|
||||
} => {
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let op = AuthOp {
|
||||
id: generated_auth_op_id("grant-revoke", &resource, &grant_id, created_at),
|
||||
|
|
@ -5679,11 +5698,82 @@ pub fn handle_request(
|
|||
created_at,
|
||||
kind: AuthOpKind::GrantRevoke { grant_id },
|
||||
};
|
||||
store_auth_op(&store, &op)?;
|
||||
let signatures = if let Some(signing_key_path) = signing_key_path {
|
||||
store_and_sign_auth_ops(
|
||||
&store,
|
||||
node,
|
||||
std::slice::from_ref(&op),
|
||||
&signing_key_path,
|
||||
admin_key_path.as_deref(),
|
||||
)?
|
||||
} else {
|
||||
store_auth_op(&store, &op)?;
|
||||
Vec::new()
|
||||
};
|
||||
Ok(ControlResponse::NodeGrantUpdated {
|
||||
op,
|
||||
signatures: Vec::new(),
|
||||
note: "recorded capability grant revocation".to_owned(),
|
||||
signatures,
|
||||
note: "recorded signed capability grant revocation".to_owned(),
|
||||
})
|
||||
}
|
||||
ControlRequest::NodeEndpointAdd {
|
||||
node: target,
|
||||
endpoint,
|
||||
signing_key_path,
|
||||
} => {
|
||||
let signing_key_path = signing_key_path
|
||||
.ok_or_else(|| NodeError::SigningKeyRequired("node endpoint add".to_owned()))?;
|
||||
let target = resolve_keychain_node(&store, &target)?;
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let op = KeychainOp {
|
||||
id: generated_keychain_op_id("node-endpoint-add", &endpoint, created_at),
|
||||
created_at,
|
||||
kind: KeychainOpKind::NodeEndpointAdd {
|
||||
node: target,
|
||||
endpoint,
|
||||
},
|
||||
};
|
||||
let signatures = store_and_sign_keychain_ops(
|
||||
&store,
|
||||
node,
|
||||
std::slice::from_ref(&op),
|
||||
Some(signing_key_path.as_path()),
|
||||
None,
|
||||
)?;
|
||||
Ok(ControlResponse::NodeKeychainUpdated {
|
||||
ops: vec![op],
|
||||
signatures,
|
||||
note: "recorded signed node endpoint binding".to_owned(),
|
||||
})
|
||||
}
|
||||
ControlRequest::NodeEndpointRevoke {
|
||||
node: target,
|
||||
endpoint,
|
||||
signing_key_path,
|
||||
} => {
|
||||
let signing_key_path = signing_key_path
|
||||
.ok_or_else(|| NodeError::SigningKeyRequired("node endpoint revoke".to_owned()))?;
|
||||
let target = resolve_keychain_node(&store, &target)?;
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let op = KeychainOp {
|
||||
id: generated_keychain_op_id("node-endpoint-revoke", &endpoint, created_at),
|
||||
created_at,
|
||||
kind: KeychainOpKind::NodeEndpointRevoke {
|
||||
node: target,
|
||||
endpoint,
|
||||
},
|
||||
};
|
||||
let signatures = store_and_sign_keychain_ops(
|
||||
&store,
|
||||
node,
|
||||
std::slice::from_ref(&op),
|
||||
Some(signing_key_path.as_path()),
|
||||
None,
|
||||
)?;
|
||||
Ok(ControlResponse::NodeKeychainUpdated {
|
||||
ops: vec![op],
|
||||
signatures,
|
||||
note: "recorded signed node endpoint revocation".to_owned(),
|
||||
})
|
||||
}
|
||||
ControlRequest::NodeEnrollRequest {
|
||||
|
|
@ -5985,6 +6075,8 @@ pub fn handle_request(
|
|||
resource,
|
||||
capability,
|
||||
grant_id,
|
||||
signing_key_path,
|
||||
admin_key_path,
|
||||
} => {
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let grant_id =
|
||||
|
|
@ -5999,10 +6091,26 @@ pub fn handle_request(
|
|||
capabilities: vec![Capability::new(capability)],
|
||||
},
|
||||
};
|
||||
store_auth_op(&store, &op)?;
|
||||
Ok(ControlResponse::AuthOpRecorded { op })
|
||||
let signatures = if let Some(signing_key_path) = signing_key_path {
|
||||
store_and_sign_auth_ops(
|
||||
&store,
|
||||
node,
|
||||
std::slice::from_ref(&op),
|
||||
&signing_key_path,
|
||||
admin_key_path.as_deref(),
|
||||
)?
|
||||
} else {
|
||||
store_auth_op(&store, &op)?;
|
||||
Vec::new()
|
||||
};
|
||||
Ok(ControlResponse::AuthOpRecorded { op, signatures })
|
||||
}
|
||||
ControlRequest::AuthRevoke { resource, grant_id } => {
|
||||
ControlRequest::AuthRevoke {
|
||||
resource,
|
||||
grant_id,
|
||||
signing_key_path,
|
||||
admin_key_path,
|
||||
} => {
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let op = AuthOp {
|
||||
id: generated_auth_op_id("grant-revoke", &resource, &grant_id, created_at),
|
||||
|
|
@ -6010,8 +6118,19 @@ pub fn handle_request(
|
|||
created_at,
|
||||
kind: AuthOpKind::GrantRevoke { grant_id },
|
||||
};
|
||||
store_auth_op(&store, &op)?;
|
||||
Ok(ControlResponse::AuthOpRecorded { op })
|
||||
let signatures = if let Some(signing_key_path) = signing_key_path {
|
||||
store_and_sign_auth_ops(
|
||||
&store,
|
||||
node,
|
||||
std::slice::from_ref(&op),
|
||||
&signing_key_path,
|
||||
admin_key_path.as_deref(),
|
||||
)?
|
||||
} else {
|
||||
store_auth_op(&store, &op)?;
|
||||
Vec::new()
|
||||
};
|
||||
Ok(ControlResponse::AuthOpRecorded { op, signatures })
|
||||
}
|
||||
ControlRequest::SshCertRequest {
|
||||
public_key_path,
|
||||
|
|
@ -7733,9 +7852,20 @@ fn record_node_grant(
|
|||
capability: &str,
|
||||
grant_id: Option<String>,
|
||||
) -> Result<AuthOp, NodeError> {
|
||||
let op = node_grant_op(node_id, resource, capability, grant_id);
|
||||
store_auth_op(store, &op)?;
|
||||
Ok(op)
|
||||
}
|
||||
|
||||
fn node_grant_op(
|
||||
node_id: &str,
|
||||
resource: &str,
|
||||
capability: &str,
|
||||
grant_id: Option<String>,
|
||||
) -> AuthOp {
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let grant_id = grant_id.unwrap_or_else(|| generated_grant_id(node_id, resource, capability));
|
||||
let op = AuthOp {
|
||||
AuthOp {
|
||||
id: generated_auth_op_id("grant-create", resource, &grant_id, created_at),
|
||||
resource: ResourceId::new(resource.to_owned()),
|
||||
created_at,
|
||||
|
|
@ -7744,9 +7874,7 @@ fn record_node_grant(
|
|||
principal: PrincipalId::new(node_id.to_owned()),
|
||||
capabilities: vec![Capability::new(capability.to_owned())],
|
||||
},
|
||||
};
|
||||
store_auth_op(store, &op)?;
|
||||
Ok(op)
|
||||
}
|
||||
}
|
||||
|
||||
fn stable_slug(value: &str) -> String {
|
||||
|
|
@ -9447,6 +9575,8 @@ mod tests {
|
|||
resource: "resource:cas:local".to_owned(),
|
||||
capability: "cas.fetch".to_owned(),
|
||||
grant_id: Some("grant:left-cas-fetch".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left peer");
|
||||
|
|
@ -9457,6 +9587,8 @@ mod tests {
|
|||
resource: "resource:cas-tree:shared".to_owned(),
|
||||
capability: "cas.fetch".to_owned(),
|
||||
grant_id: Some("grant:left-cas-tree-fetch".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left file root fetch");
|
||||
|
|
@ -9467,6 +9599,8 @@ mod tests {
|
|||
resource: "resource:kv:prefs".to_owned(),
|
||||
capability: "kv.read".to_owned(),
|
||||
grant_id: Some("grant:left-kv-read".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left kv read");
|
||||
|
|
@ -9477,6 +9611,8 @@ mod tests {
|
|||
resource: "resource:pubsub:presence/test".to_owned(),
|
||||
capability: "pubsub.publish".to_owned(),
|
||||
grant_id: Some("grant:left-pubsub-publish".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left pubsub publish");
|
||||
|
|
@ -9487,6 +9623,8 @@ mod tests {
|
|||
resource: "resource:pubsub:presence/test".to_owned(),
|
||||
capability: "pubsub.subscribe".to_owned(),
|
||||
grant_id: Some("grant:left-pubsub-subscribe".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left pubsub subscribe");
|
||||
|
|
@ -9497,6 +9635,8 @@ mod tests {
|
|||
resource: "resource:pipe:inbox".to_owned(),
|
||||
capability: "pipe.connect".to_owned(),
|
||||
grant_id: Some("grant:left-pipe-connect".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left pipe connect");
|
||||
|
|
@ -9507,6 +9647,8 @@ mod tests {
|
|||
resource: "resource:pipe:remote-inbox".to_owned(),
|
||||
capability: "pipe.listen".to_owned(),
|
||||
grant_id: Some("grant:left-pipe-listen".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left pipe listen");
|
||||
|
|
@ -9517,6 +9659,8 @@ mod tests {
|
|||
resource: "resource:ssh-proxy:local".to_owned(),
|
||||
capability: "ssh_proxy.connect".to_owned(),
|
||||
grant_id: Some("grant:left-ssh-proxy-connect".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left ssh proxy connect");
|
||||
|
|
@ -9527,6 +9671,8 @@ mod tests {
|
|||
resource: "resource:ssh-proxy:local".to_owned(),
|
||||
capability: "ssh_proxy.admin_shell".to_owned(),
|
||||
grant_id: Some("grant:left-ssh-admin-shell".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left ssh admin shell");
|
||||
|
|
@ -9537,6 +9683,8 @@ mod tests {
|
|||
resource: "resource:document:notes".to_owned(),
|
||||
capability: "document.read".to_owned(),
|
||||
grant_id: Some("grant:left-document-read".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left document read");
|
||||
|
|
@ -9547,6 +9695,8 @@ mod tests {
|
|||
resource: "resource:db:notes".to_owned(),
|
||||
capability: "db.sync".to_owned(),
|
||||
grant_id: Some("grant:left-db-sync".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left db sync");
|
||||
|
|
@ -9562,6 +9712,8 @@ mod tests {
|
|||
resource: format!("resource:pipe-tcp:{tcp_target}"),
|
||||
capability: "pipe.forward".to_owned(),
|
||||
grant_id: Some("grant:left-pipe-tcp-forward".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left pipe tcp forward");
|
||||
|
|
@ -9638,6 +9790,8 @@ mod tests {
|
|||
resource: format!("resource:pipe-unix:{unix_target_display}"),
|
||||
capability: "pipe.forward".to_owned(),
|
||||
grant_id: Some("grant:left-pipe-unix-forward".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left pipe unix forward");
|
||||
|
|
@ -10201,6 +10355,8 @@ mod tests {
|
|||
resource: "resource:ssh:certs".to_owned(),
|
||||
capability: "ssh_cert.sync".to_owned(),
|
||||
grant_id: Some("grant:left-ssh-cert-sync".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left ssh cert sync");
|
||||
|
|
@ -10211,6 +10367,8 @@ mod tests {
|
|||
resource: "resource:ssh:revocations".to_owned(),
|
||||
capability: "ssh_revocation.sync".to_owned(),
|
||||
grant_id: Some("grant:left-ssh-revocation-sync".to_owned()),
|
||||
signing_key_path: None,
|
||||
admin_key_path: None,
|
||||
},
|
||||
)
|
||||
.expect("grant left ssh revocation sync");
|
||||
|
|
|
|||
Loading…
Reference in a new issue