added: cert/SHA256:...

This commit is contained in:
shimun 2023-03-10 10:38:31 +01:00
parent ba77091de7
commit e696663aec
Signed by: shimun
GPG Key ID: E0420647856EA39E
4 changed files with 65 additions and 9 deletions

View File

@ -116,11 +116,13 @@ async fn fetch(
args: ClientArgs { api, interactive },
}: FetchArgs,
) -> anyhow::Result<()> {
let certs = read_dir(&cert_dir).await?;
let certs = read_certs_dir(&cert_dir).await?;
let publics_keys = read_pubkey_dir(&cert_dir).await?;
let client = reqwest::Client::new();
let threshold_exp = min_delta.and_then(|min_delta| {
SystemTime::now().checked_add(Duration::from_secs(60 * 60 * 24 * min_delta as u64))
});
let standalone_certs = publics_keys.into_iter().map(|(name, key)| )
let updates = certs
.into_iter()
.filter(|cert| {

View File

@ -24,11 +24,11 @@ pub async fn read_certs(
if !ca_dir.exists() {
return Ok(Vec::new());
}
read_dir(&ca_dir).await
read_certs_dir(&ca_dir).await
}
#[instrument]
pub async fn read_dir(path: impl AsRef<Path> + Debug) -> anyhow::Result<Vec<Certificate>> {
pub async fn read_certs_dir(path: impl AsRef<Path> + Debug) -> anyhow::Result<Vec<Certificate>> {
let mut dir = fs::read_dir(path.as_ref())
.await
.with_context(|| format!("read certs dir '{:?}'", path.as_ref()))?;
@ -55,6 +55,31 @@ pub async fn read_dir(path: impl AsRef<Path> + Debug) -> anyhow::Result<Vec<Cert
Ok(certs)
}
pub async fn read_pubkey_dir(path: impl AsRef<Path> + Debug) -> anyhow::Result<Vec<PublicKey>> {
let mut dir = fs::read_dir(path.as_ref())
.await
.with_context(|| format!("read certs dir '{:?}'", path.as_ref()))?;
let mut pubs = Vec::new();
while let Some(entry) = dir.next_entry().await? {
//TODO: investigate why path().ends_with doesn't work
let file_name = entry.file_name().into_string().unwrap();
if !file_name.ends_with(".pub") || file_name.ends_with("-cert.pub")
{
trace!(
"skipped {:?} due to missing '.pub' extension",
entry.path()
);
continue;
}
let cert = load_public_key(entry.path()).await?;
if let Some(cert) = cert {
pubs.push(cert);
}
}
Ok(pubs)
}
fn parse_utf8(bytes: Vec<u8>) -> anyhow::Result<String> {
String::from_utf8(bytes).context("invalid utf-8")
}
@ -122,3 +147,16 @@ pub async fn load_cert(file: impl AsRef<Path> + Debug) -> anyhow::Result<Option<
|| format!("parse {:?} as openssh certificate", &file),
)?))
}
pub async fn load_public_key(file: impl AsRef<Path> + Debug) -> anyhow::Result<Option<PublicKey>> {
let contents = match fs::read(&file).await {
Ok(contents) => contents,
Err(e) if e.kind() == ErrorKind::NotFound => return Ok(None),
Err(e) => return Err(e).with_context(|| format!("read {:?}", &file)),
};
let string_repr = parse_utf8(contents)?;
Ok(Some(PublicKey::from_openssh(&string_repr).with_context(
|| format!("parse {:?} as openssh public key", &file),
)?))
}

View File

@ -1,24 +1,39 @@
use axum_extra::routing::TypedPath;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use ssh_key::Fingerprint;
#[derive(TypedPath, Deserialize)]
#[typed_path("/certs")]
pub struct CertList;
#[derive(TypedPath, Deserialize)]
#[typed_path("/certs/:identifier")]
#[typed_path("/cert/:identifier")]
pub struct GetCert {
pub identifier: String,
}
#[derive(TypedPath, Deserialize)]
#[typed_path("/certs/:identifier/info")]
#[typed_path("/certs/:pubkey_hash")]
pub struct GetCertsPubkey {
pub pubkey_hash: Fingerprint,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct CertIds {
pub ids: Vec<String>
}
#[derive(TypedPath, Deserialize)]
#[typed_path("/cert/:identifier/info")]
pub struct GetCertInfo {
pub identifier: String,
}
#[derive(TypedPath, Deserialize)]
#[typed_path("/certs/:identifier")]
#[typed_path("/cert/:identifier")]
pub struct PostCertInfo {
pub identifier: String,
}

View File

@ -9,6 +9,7 @@ use std::time::SystemTime;
use anyhow::Context;
use axum::body;
use axum::extract::{Query, State};
use chrono::Duration;
use ssh_cert_dist_common::*;
use axum::{http::StatusCode, response::IntoResponse, Json, Router};
@ -291,12 +292,12 @@ struct CertInfo {
impl From<&Certificate> for CertInfo {
fn from(cert: &Certificate) -> Self {
let validity = cert.valid_after_time().duration_since(cert.valid_before_time()).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 host_key = if cert.cert_type().is_host() {
" -h"
} 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}")).join(" ");
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(","));
CertInfo {
principals: cert.valid_principals().to_vec(),