Merge branch 'split_components' of git.shimun.net:shimun/ssh-cert-dist into split_components
This commit is contained in:
commit
dffbcceeba
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -1520,6 +1520,12 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shell-escape"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signature"
|
name = "signature"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -1634,6 +1640,7 @@ dependencies = [
|
|||||||
"jwt-compact",
|
"jwt-compact",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
"shell-escape",
|
||||||
"ssh-cert-dist-common",
|
"ssh-cert-dist-common",
|
||||||
"ssh-key",
|
"ssh-key",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
@ -32,6 +32,7 @@ tower-http = { version = "0.3.4", features = ["map-request-body", "trace"] }
|
|||||||
tracing = { version = "0.1.37", features = ["release_max_level_debug"] }
|
tracing = { version = "0.1.37", features = ["release_max_level_debug"] }
|
||||||
tracing-subscriber = "0.3.16"
|
tracing-subscriber = "0.3.16"
|
||||||
ssh-cert-dist-common = { path = "../common" }
|
ssh-cert-dist-common = { path = "../common" }
|
||||||
|
shell-escape = "0.1.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
|
@ -4,12 +4,13 @@ use std::collections::HashMap;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::{self, PathBuf};
|
use std::path::{self, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use axum::body;
|
use axum::body;
|
||||||
use axum::extract::{Query, State};
|
use axum::extract::{Query, State};
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
|
use shell_escape::escape;
|
||||||
use ssh_cert_dist_common::*;
|
use ssh_cert_dist_common::*;
|
||||||
|
|
||||||
use axum::{http::StatusCode, response::IntoResponse, Json, Router};
|
use axum::{http::StatusCode, response::IntoResponse, Json, Router};
|
||||||
@ -298,12 +299,36 @@ struct CertInfo {
|
|||||||
impl From<&Certificate> for CertInfo {
|
impl From<&Certificate> for CertInfo {
|
||||||
fn from(cert: &Certificate) -> Self {
|
fn from(cert: &Certificate) -> Self {
|
||||||
let validity = cert.valid_before_time().duration_since(cert.valid_after_time()).unwrap_or(Duration::zero().to_std().unwrap());
|
let validity = cert.valid_before_time().duration_since(cert.valid_after_time()).unwrap_or(Duration::zero().to_std().unwrap());
|
||||||
let validity_days = validity.as_secs() / ((60*60) * 24);
|
let expiry = cert.valid_before_time().checked_add(validity).unwrap();
|
||||||
|
let expiry_date = expiry.duration_since(UNIX_EPOCH).unwrap();
|
||||||
let host_key = if cert.cert_type().is_host() {
|
let host_key = if cert.cert_type().is_host() {
|
||||||
" -h"
|
" -h"
|
||||||
} else { "" };
|
} else {
|
||||||
let opts = cert.critical_options().iter().map(|(opt, val)| if val.is_empty() { opt.clone() } else { format!("{opt}={val}") }).map(|arg| format!("-O {arg}")).collect::<Vec<_>>().join(" ");
|
""
|
||||||
let renew_command = format!("ssh-keygen -s ./ca_key {host_key} -I {} -n {} -V {validity_days}d {opts}", cert.key_id(), cert.valid_principals().join(","));
|
};
|
||||||
|
let opts = cert
|
||||||
|
.critical_options()
|
||||||
|
.iter()
|
||||||
|
.map(|(opt, val)| {
|
||||||
|
if val.is_empty() {
|
||||||
|
opt.clone()
|
||||||
|
} else {
|
||||||
|
format!("{opt}={val}")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|arg| format!("-O {}", escape(arg.into())))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
let opts = opts.trim();
|
||||||
|
let renew_command = format!(
|
||||||
|
"ssh-keygen -s ./ca_key {host_key} -I {} -n {} -z {} -V {:#x}:{:#x} {opts} {}.pub",
|
||||||
|
escape(cert.key_id().into()),
|
||||||
|
escape(cert.valid_principals().join(",").into()),
|
||||||
|
cert.serial() + 1,
|
||||||
|
cert.valid_after(),
|
||||||
|
expiry_date.as_secs(),
|
||||||
|
escape(cert.key_id().into())
|
||||||
|
);
|
||||||
CertInfo {
|
CertInfo {
|
||||||
principals: cert.valid_principals().to_vec(),
|
principals: cert.valid_principals().to_vec(),
|
||||||
ca: cert.signature_key().clone().into(),
|
ca: cert.signature_key().clone().into(),
|
||||||
@ -312,7 +337,7 @@ impl From<&Certificate> for CertInfo {
|
|||||||
identity_hash: cert.public_key().fingerprint(ssh_key::HashAlg::Sha256),
|
identity_hash: cert.public_key().fingerprint(ssh_key::HashAlg::Sha256),
|
||||||
key_id: cert.key_id().to_string(),
|
key_id: cert.key_id().to_string(),
|
||||||
expiry: cert.valid_before_time(),
|
expiry: cert.valid_before_time(),
|
||||||
renew_command
|
renew_command,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,7 +540,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
let res = put_cert_update(PutCert, State(state.clone()), CertificateBody(cert_first)).await;
|
let res = put_cert_update(PutCert, State(state.clone()), CertificateBody(cert_first)).await;
|
||||||
assert!(res.is_ok());
|
assert!(dbg!(res).is_ok());
|
||||||
let res = put_cert_update(PutCert, State(state.clone()), CertificateBody(cert_newer)).await;
|
let res = put_cert_update(PutCert, State(state.clone()), CertificateBody(cert_newer)).await;
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let res = put_cert_update(
|
let res = put_cert_update(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user