Add binary OpenSSH KRL export
This commit is contained in:
parent
5751748458
commit
b9e7c61e67
11 changed files with 209 additions and 20 deletions
11
AGENTS.md
11
AGENTS.md
|
|
@ -147,9 +147,10 @@ Roadmap items should be actionable and checkable:
|
||||||
Bearer access metadata can be created/listed/revoked as resource-scoped auth
|
Bearer access metadata can be created/listed/revoked as resource-scoped auth
|
||||||
ops and must not allow trust graph mutation capabilities. Payload encryption,
|
ops and must not allow trust graph mutation capabilities. Payload encryption,
|
||||||
key envelopes, and bearer challenge-response are still roadmap work.
|
key envelopes, and bearer challenge-response are still roadmap work.
|
||||||
- SSH revocations can be exported as JSONL or OpenSSH KRL specification text.
|
- SSH revocations can be exported as JSONL, OpenSSH KRL specification text, or
|
||||||
Binary KRL generation/import is still roadmap work.
|
binary OpenSSH KRL files generated through `ssh-keygen`. Existing KRL import
|
||||||
|
is still roadmap work.
|
||||||
- Signed peer-card LAN discovery payloads, peer auth over Iroh, cr-sqlite,
|
- Signed peer-card LAN discovery payloads, peer auth over Iroh, cr-sqlite,
|
||||||
iroh-docs, iroh-blobs, Automerge sync, real auth enforcement,
|
iroh-docs, iroh-blobs, Automerge sync, broader auth enforcement, KRL import,
|
||||||
OpenSSH KRL generation, and Keyhive/BeeKEM-style authorization are future
|
and Keyhive/BeeKEM-style authorization are future roadmap items unless
|
||||||
roadmap items unless implemented later.
|
implemented later.
|
||||||
|
|
|
||||||
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1293,6 +1293,7 @@ dependencies = [
|
||||||
"geth-types",
|
"geth-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tempfile",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,9 @@ can create a certificate request, another machine can approve it and receive an
|
||||||
explicit `ssh-keygen -s ...` command suitable for a CA key or YubiKey-backed CA,
|
explicit `ssh-keygen -s ...` command suitable for a CA key or YubiKey-backed CA,
|
||||||
and the resulting `-cert.pub` can be imported for distribution. Certificate and
|
and the resulting `-cert.pub` can be imported for distribution. Certificate and
|
||||||
key revocation entries are tracked locally and can be exported as JSONL or as an
|
key revocation entries are tracked locally and can be exported as JSONL or as an
|
||||||
OpenSSH KRL specification file for later `ssh-keygen -k` use. Future Iroh
|
OpenSSH KRL specification file or a binary OpenSSH KRL generated through
|
||||||
replication will distribute these records between authorized nodes.
|
`ssh-keygen -k`. Future Iroh replication will distribute these records between
|
||||||
|
authorized nodes.
|
||||||
|
|
||||||
## MVP Features
|
## MVP Features
|
||||||
|
|
||||||
|
|
@ -111,7 +112,7 @@ The bootstrap implementation provides:
|
||||||
- `geth ssh cert list`
|
- `geth ssh cert list`
|
||||||
- `geth ssh revocation add <kind> <target>`
|
- `geth ssh revocation add <kind> <target>`
|
||||||
- `geth ssh revocation list`
|
- `geth ssh revocation list`
|
||||||
- `geth ssh revocation export --out <path> [--format jsonl|openssh-krl-spec]`
|
- `geth ssh revocation export --out <path> [--format jsonl|openssh-krl-spec|openssh-krl]`
|
||||||
- local pipe registry commands: `geth pipe listen/connect`
|
- local pipe registry commands: `geth pipe listen/connect`
|
||||||
|
|
||||||
`geth peer export/import/list` is for untrusted peer-card exchange while live
|
`geth peer export/import/list` is for untrusted peer-card exchange while live
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,8 @@ pub enum SshRevocationCommand {
|
||||||
out: PathBuf,
|
out: PathBuf,
|
||||||
#[arg(long, default_value = "jsonl")]
|
#[arg(long, default_value = "jsonl")]
|
||||||
format: String,
|
format: String,
|
||||||
|
#[arg(long)]
|
||||||
|
ca_public: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -660,9 +662,15 @@ fn request_for_command(command: Command) -> Result<ControlRequest> {
|
||||||
reason,
|
reason,
|
||||||
},
|
},
|
||||||
SshRevocationCommand::List => ControlRequest::SshRevocationList,
|
SshRevocationCommand::List => ControlRequest::SshRevocationList,
|
||||||
SshRevocationCommand::Export { out, format } => {
|
SshRevocationCommand::Export {
|
||||||
ControlRequest::SshRevocationExport { out, format }
|
out,
|
||||||
}
|
format,
|
||||||
|
ca_public,
|
||||||
|
} => ControlRequest::SshRevocationExport {
|
||||||
|
out,
|
||||||
|
format,
|
||||||
|
ca_public,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Command::Init | Command::Daemon { .. } => bail!("command is handled directly"),
|
Command::Init | Command::Daemon { .. } => bail!("command is handled directly"),
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ pub enum ControlRequest {
|
||||||
SshRevocationExport {
|
SshRevocationExport {
|
||||||
out: PathBuf,
|
out: PathBuf,
|
||||||
format: String,
|
format: String,
|
||||||
|
ca_public: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
DbAdd {
|
DbAdd {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -469,6 +470,7 @@ mod tests {
|
||||||
let request = ControlRequest::SshRevocationExport {
|
let request = ControlRequest::SshRevocationExport {
|
||||||
out: PathBuf::from("revocations.krl-spec"),
|
out: PathBuf::from("revocations.krl-spec"),
|
||||||
format: "openssh-krl-spec".to_owned(),
|
format: "openssh-krl-spec".to_owned(),
|
||||||
|
ca_public: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ use geth_ssh_identity::{
|
||||||
SshCertApproval, SshCertKind, SshCertRequest, SshCertRequestStatus, SshCertificateRecord,
|
SshCertApproval, SshCertKind, SshCertRequest, SshCertRequestStatus, SshCertificateRecord,
|
||||||
SshRevocationEntry, SshRevocationExportFormat, SshRevocationKind, build_ssh_cert_sign_command,
|
SshRevocationEntry, SshRevocationExportFormat, SshRevocationKind, build_ssh_cert_sign_command,
|
||||||
cert_request_id, certificate_id, openssh_krl_spec, revocation_id, ssh_public_key_fingerprint,
|
cert_request_id, certificate_id, openssh_krl_spec, revocation_id, ssh_public_key_fingerprint,
|
||||||
|
write_openssh_krl,
|
||||||
};
|
};
|
||||||
use geth_store::{
|
use geth_store::{
|
||||||
Store, StoredAuthOp, StoredDbResource, StoredDocumentResource, StoredFileConflict,
|
Store, StoredAuthOp, StoredDbResource, StoredDocumentResource, StoredFileConflict,
|
||||||
|
|
@ -897,7 +898,11 @@ pub fn handle_request(
|
||||||
.map(ssh_revocation_from_stored)
|
.map(ssh_revocation_from_stored)
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
}),
|
}),
|
||||||
ControlRequest::SshRevocationExport { out, format } => {
|
ControlRequest::SshRevocationExport {
|
||||||
|
out,
|
||||||
|
format,
|
||||||
|
ca_public,
|
||||||
|
} => {
|
||||||
let revocations = store
|
let revocations = store
|
||||||
.list_ssh_revocations()?
|
.list_ssh_revocations()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -925,8 +930,17 @@ pub fn handle_request(
|
||||||
openssh_krl_spec(&revocations)?,
|
openssh_krl_spec(&revocations)?,
|
||||||
"OpenSSH KRL specification; generate a binary KRL with ssh-keygen -k -f <krl> [-s <ca.pub>] <spec>".to_owned(),
|
"OpenSSH KRL specification; generate a binary KRL with ssh-keygen -k -f <krl> [-s <ca.pub>] <spec>".to_owned(),
|
||||||
),
|
),
|
||||||
|
SshRevocationExportFormat::OpenSshKrl => {
|
||||||
|
write_openssh_krl(&revocations, &out, ca_public.as_deref())?;
|
||||||
|
(
|
||||||
|
String::new(),
|
||||||
|
"OpenSSH binary KRL generated with ssh-keygen; use ssh-keygen -Q -f <krl> <key-or-cert> to query it".to_owned(),
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
if format != SshRevocationExportFormat::OpenSshKrl {
|
||||||
std::fs::write(&out, body)?;
|
std::fs::write(&out, body)?;
|
||||||
|
}
|
||||||
Ok(ControlResponse::SshRevocationExported {
|
Ok(ControlResponse::SshRevocationExported {
|
||||||
out,
|
out,
|
||||||
format: format.to_string(),
|
format: format.to_string(),
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,4 @@ geth-types = { path = "../geth-types" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
tempfile.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ pub const SSH_CERT_ISSUANCE_NAMESPACE: &str = "geth.ssh-cert-issuance.v1@geth.lo
|
||||||
pub const SSH_REVOCATION_LIST_NAMESPACE: &str = "geth.ssh-revocation-list.v1@geth.local";
|
pub const SSH_REVOCATION_LIST_NAMESPACE: &str = "geth.ssh-revocation-list.v1@geth.local";
|
||||||
|
|
||||||
pub fn ensure_ssh_keygen_available() -> Result<(), SshIdentityError> {
|
pub fn ensure_ssh_keygen_available() -> Result<(), SshIdentityError> {
|
||||||
let status = Command::new("ssh-keygen").arg("-?").status();
|
let output = Command::new("ssh-keygen").arg("-?").output();
|
||||||
match status {
|
match output {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
|
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
|
||||||
Err(SshIdentityError::SshKeygenUnavailable)
|
Err(SshIdentityError::SshKeygenUnavailable)
|
||||||
|
|
@ -210,6 +210,7 @@ pub struct SshRevocationEntry {
|
||||||
pub enum SshRevocationExportFormat {
|
pub enum SshRevocationExportFormat {
|
||||||
Jsonl,
|
Jsonl,
|
||||||
OpenSshKrlSpec,
|
OpenSshKrlSpec,
|
||||||
|
OpenSshKrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SshRevocationExportFormat {
|
impl SshRevocationExportFormat {
|
||||||
|
|
@ -218,6 +219,7 @@ impl SshRevocationExportFormat {
|
||||||
match self {
|
match self {
|
||||||
Self::Jsonl => "jsonl",
|
Self::Jsonl => "jsonl",
|
||||||
Self::OpenSshKrlSpec => "openssh-krl-spec",
|
Self::OpenSshKrlSpec => "openssh-krl-spec",
|
||||||
|
Self::OpenSshKrl => "openssh-krl",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -235,6 +237,7 @@ impl FromStr for SshRevocationExportFormat {
|
||||||
match value {
|
match value {
|
||||||
"jsonl" => Ok(Self::Jsonl),
|
"jsonl" => Ok(Self::Jsonl),
|
||||||
"openssh-krl-spec" | "krl-spec" => Ok(Self::OpenSshKrlSpec),
|
"openssh-krl-spec" | "krl-spec" => Ok(Self::OpenSshKrlSpec),
|
||||||
|
"openssh-krl" | "krl" => Ok(Self::OpenSshKrl),
|
||||||
_ => Err(SshIdentityError::InvalidRevocationExportFormat(
|
_ => Err(SshIdentityError::InvalidRevocationExportFormat(
|
||||||
value.to_owned(),
|
value.to_owned(),
|
||||||
)),
|
)),
|
||||||
|
|
@ -331,6 +334,34 @@ pub fn openssh_krl_spec(entries: &[SshRevocationEntry]) -> Result<String, SshIde
|
||||||
Ok(spec)
|
Ok(spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_openssh_krl(
|
||||||
|
entries: &[SshRevocationEntry],
|
||||||
|
out: &Path,
|
||||||
|
ca_public: Option<&Path>,
|
||||||
|
) -> Result<(), SshIdentityError> {
|
||||||
|
ensure_ssh_keygen_available()?;
|
||||||
|
if let Some(parent) = out.parent() {
|
||||||
|
std::fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let spec = openssh_krl_spec(entries)?;
|
||||||
|
let spec_path = out.with_extension("geth-krl-spec.tmp");
|
||||||
|
std::fs::write(&spec_path, spec)?;
|
||||||
|
let mut command = Command::new("ssh-keygen");
|
||||||
|
command.arg("-k").arg("-f").arg(out);
|
||||||
|
if let Some(ca_public) = ca_public {
|
||||||
|
command.arg("-s").arg(ca_public);
|
||||||
|
}
|
||||||
|
command.arg(&spec_path);
|
||||||
|
let output = command.output()?;
|
||||||
|
let _ = std::fs::remove_file(&spec_path);
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_owned();
|
||||||
|
return Err(SshIdentityError::SshKeygenFailed(stderr));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn openssh_krl_spec_line(entry: &SshRevocationEntry) -> Result<String, SshIdentityError> {
|
pub fn openssh_krl_spec_line(entry: &SshRevocationEntry) -> Result<String, SshIdentityError> {
|
||||||
let target = entry.target.trim();
|
let target = entry.target.trim();
|
||||||
if target.is_empty() || target.bytes().any(|byte| byte == b'\n' || byte == b'\r') {
|
if target.is_empty() || target.bytes().any(|byte| byte == b'\n' || byte == b'\r') {
|
||||||
|
|
@ -351,6 +382,8 @@ pub fn openssh_krl_spec_line(entry: &SshRevocationEntry) -> Result<String, SshId
|
||||||
pub enum SshIdentityError {
|
pub enum SshIdentityError {
|
||||||
#[error("ssh-keygen failed or is unavailable")]
|
#[error("ssh-keygen failed or is unavailable")]
|
||||||
SshKeygenUnavailable,
|
SshKeygenUnavailable,
|
||||||
|
#[error("ssh-keygen failed: {0}")]
|
||||||
|
SshKeygenFailed(String),
|
||||||
#[error("invalid ssh certificate kind: {0}")]
|
#[error("invalid ssh certificate kind: {0}")]
|
||||||
InvalidCertKind(String),
|
InvalidCertKind(String),
|
||||||
#[error("invalid ssh certificate request status: {0}")]
|
#[error("invalid ssh certificate request status: {0}")]
|
||||||
|
|
@ -465,4 +498,53 @@ mod tests {
|
||||||
|
|
||||||
assert!(openssh_krl_spec_line(&entry).is_err());
|
assert!(openssh_krl_spec_line(&entry).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn openssh_krl_binary_export_revokes_public_key_when_ssh_keygen_available() {
|
||||||
|
if ensure_ssh_keygen_available().is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dir = tempfile::tempdir().expect("tempdir");
|
||||||
|
let key_path = dir.path().join("revoked");
|
||||||
|
let status = Command::new("ssh-keygen")
|
||||||
|
.arg("-q")
|
||||||
|
.arg("-t")
|
||||||
|
.arg("ed25519")
|
||||||
|
.arg("-N")
|
||||||
|
.arg("")
|
||||||
|
.arg("-f")
|
||||||
|
.arg(&key_path)
|
||||||
|
.status()
|
||||||
|
.expect("run ssh-keygen");
|
||||||
|
assert!(status.success());
|
||||||
|
|
||||||
|
let public_key_path = key_path.with_extension("pub");
|
||||||
|
let public_key = std::fs::read_to_string(&public_key_path).expect("read public key");
|
||||||
|
let entry = SshRevocationEntry {
|
||||||
|
id: "ssh-revocation:key".into(),
|
||||||
|
kind: SshRevocationKind::PublicKey,
|
||||||
|
target: public_key,
|
||||||
|
reason: Some("test".to_owned()),
|
||||||
|
created_at: UnixMillis(1),
|
||||||
|
published: true,
|
||||||
|
};
|
||||||
|
let krl_path = dir.path().join("revoked.krl");
|
||||||
|
|
||||||
|
write_openssh_krl(&[entry], &krl_path, None).expect("write krl");
|
||||||
|
|
||||||
|
let output = Command::new("ssh-keygen")
|
||||||
|
.arg("-Q")
|
||||||
|
.arg("-f")
|
||||||
|
.arg(&krl_path)
|
||||||
|
.arg(&public_key_path)
|
||||||
|
.output()
|
||||||
|
.expect("query krl");
|
||||||
|
assert!(!output.status.success());
|
||||||
|
assert!(
|
||||||
|
String::from_utf8_lossy(&output.stdout)
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("revoked")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1331,6 +1331,7 @@ fn ssh_cert_request_approval_and_revocation_export_use_local_state() {
|
||||||
geth_control::ControlRequest::SshRevocationExport {
|
geth_control::ControlRequest::SshRevocationExport {
|
||||||
out: export_path.clone(),
|
out: export_path.clone(),
|
||||||
format: "jsonl".to_owned(),
|
format: "jsonl".to_owned(),
|
||||||
|
ca_public: None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("export revocations");
|
.expect("export revocations");
|
||||||
|
|
@ -1346,6 +1347,7 @@ fn ssh_cert_request_approval_and_revocation_export_use_local_state() {
|
||||||
geth_control::ControlRequest::SshRevocationExport {
|
geth_control::ControlRequest::SshRevocationExport {
|
||||||
out: krl_spec_path.clone(),
|
out: krl_spec_path.clone(),
|
||||||
format: "openssh-krl-spec".to_owned(),
|
format: "openssh-krl-spec".to_owned(),
|
||||||
|
ca_public: None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("export revocation krl spec");
|
.expect("export revocation krl spec");
|
||||||
|
|
@ -1368,3 +1370,76 @@ fn ssh_cert_request_approval_and_revocation_export_use_local_state() {
|
||||||
.contains("key: ssh:blake3:test")
|
.contains("key: ssh:blake3:test")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ssh_revocation_export_can_write_binary_openssh_krl() {
|
||||||
|
if Command::new("ssh-keygen").arg("-?").output().is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let home = tempfile::tempdir().expect("tempdir");
|
||||||
|
let paths = geth_config::GethPaths::from_home(home.path());
|
||||||
|
let node = geth_node::init_node(&paths).expect("init node");
|
||||||
|
let key_path = home.path().join("revoked");
|
||||||
|
let status = Command::new("ssh-keygen")
|
||||||
|
.arg("-q")
|
||||||
|
.arg("-t")
|
||||||
|
.arg("ed25519")
|
||||||
|
.arg("-N")
|
||||||
|
.arg("")
|
||||||
|
.arg("-f")
|
||||||
|
.arg(&key_path)
|
||||||
|
.status()
|
||||||
|
.expect("generate ssh key");
|
||||||
|
assert!(status.success());
|
||||||
|
let public_key_path = key_path.with_extension("pub");
|
||||||
|
let public_key = std::fs::read_to_string(&public_key_path).expect("read public key");
|
||||||
|
|
||||||
|
geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::SshRevocationAdd {
|
||||||
|
kind: "public-key".to_owned(),
|
||||||
|
target: public_key,
|
||||||
|
reason: Some("test binary krl".to_owned()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("add revocation");
|
||||||
|
let krl_path = home.path().join("revocations.krl");
|
||||||
|
let response = geth_node::handle_request(
|
||||||
|
&node,
|
||||||
|
geth_control::ControlRequest::SshRevocationExport {
|
||||||
|
out: krl_path.clone(),
|
||||||
|
format: "openssh-krl".to_owned(),
|
||||||
|
ca_public: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("export binary krl");
|
||||||
|
match response {
|
||||||
|
geth_control::ControlResponse::SshRevocationExported {
|
||||||
|
format,
|
||||||
|
count,
|
||||||
|
note,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assert_eq!(format, "openssh-krl");
|
||||||
|
assert_eq!(count, 1);
|
||||||
|
assert!(note.contains("binary KRL"));
|
||||||
|
}
|
||||||
|
other => panic!("unexpected response: {other:?}"),
|
||||||
|
}
|
||||||
|
assert!(krl_path.exists());
|
||||||
|
|
||||||
|
let query = Command::new("ssh-keygen")
|
||||||
|
.arg("-Q")
|
||||||
|
.arg("-f")
|
||||||
|
.arg(&krl_path)
|
||||||
|
.arg(&public_key_path)
|
||||||
|
.output()
|
||||||
|
.expect("query krl");
|
||||||
|
assert!(!query.status.success());
|
||||||
|
assert!(
|
||||||
|
String::from_utf8_lossy(&query.stdout)
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("revoked")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,8 +138,10 @@ streams yet.
|
||||||
`geth-ssh-identity` defines SSH trust namespaces plus certificate request,
|
`geth-ssh-identity` defines SSH trust namespaces plus certificate request,
|
||||||
approval, certificate import, and revocation-list data models. The bootstrap
|
approval, certificate import, and revocation-list data models. The bootstrap
|
||||||
persists these flows locally and exports revocations as JSONL or OpenSSH KRL
|
persists these flows locally and exports revocations as JSONL or OpenSSH KRL
|
||||||
specification text. It does not yet generate OpenSSH KRL binaries or replicate
|
specification text. It can also invoke `ssh-keygen -k` to produce a binary
|
||||||
the lists over Iroh.
|
OpenSSH KRL; serial and key-ID KRL entries require a CA public key via
|
||||||
|
`--ca-public`, matching OpenSSH behavior. It does not yet import existing KRL
|
||||||
|
files or replicate the lists over Iroh.
|
||||||
|
|
||||||
## Keychain, Auth, And Secrets
|
## Keychain, Auth, And Secrets
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ resource-scoped capability decisions.
|
||||||
- `[x]` `geth ssh revocation add/list/export` persists and exports
|
- `[x]` `geth ssh revocation add/list/export` persists and exports
|
||||||
revocations.
|
revocations.
|
||||||
- `[x]` Revocations can be exported as JSONL and OpenSSH KRL specification
|
- `[x]` Revocations can be exported as JSONL and OpenSSH KRL specification
|
||||||
text.
|
text or as a binary OpenSSH KRL through `ssh-keygen`.
|
||||||
- `[ ]` Future completion requires auth checks for request, approve, import,
|
- `[ ]` Future completion requires auth checks for request, approve, import,
|
||||||
publish, and read capabilities.
|
publish, and read capabilities.
|
||||||
|
|
||||||
|
|
@ -295,10 +295,12 @@ Goal: add authorized stream-oriented management workflows over Iroh.
|
||||||
Acceptance criteria:
|
Acceptance criteria:
|
||||||
- `[x]` Revocation records can produce an OpenSSH KRL specification file.
|
- `[x]` Revocation records can produce an OpenSSH KRL specification file.
|
||||||
- `[x]` Tests cover serial, key ID, and public key revocation spec lines.
|
- `[x]` Tests cover serial, key ID, and public key revocation spec lines.
|
||||||
- `[ ]` Revocation records can produce an OpenSSH binary KRL file.
|
- `[x]` Revocation records can produce an OpenSSH binary KRL file.
|
||||||
|
- `[x]` Tests cover binary KRL export for public-key revocations when
|
||||||
|
`ssh-keygen` is available.
|
||||||
- `[ ]` Existing KRL files can be imported into revocation metadata where
|
- `[ ]` Existing KRL files can be imported into revocation metadata where
|
||||||
possible.
|
possible.
|
||||||
- `[ ]` Tests cover binary KRL export/import and certificate revocations.
|
- `[ ]` Tests cover KRL import and certificate revocations.
|
||||||
|
|
||||||
## Phase 5: DB And Documents
|
## Phase 5: DB And Documents
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue