added: cert/SHA256:...
This commit is contained in:
parent
ba77091de7
commit
e696663aec
@ -116,11 +116,13 @@ async fn fetch(
|
|||||||
args: ClientArgs { api, interactive },
|
args: ClientArgs { api, interactive },
|
||||||
}: FetchArgs,
|
}: FetchArgs,
|
||||||
) -> anyhow::Result<()> {
|
) -> 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 client = reqwest::Client::new();
|
||||||
let threshold_exp = min_delta.and_then(|min_delta| {
|
let threshold_exp = min_delta.and_then(|min_delta| {
|
||||||
SystemTime::now().checked_add(Duration::from_secs(60 * 60 * 24 * min_delta as u64))
|
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
|
let updates = certs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|cert| {
|
.filter(|cert| {
|
||||||
|
@ -24,11 +24,11 @@ pub async fn read_certs(
|
|||||||
if !ca_dir.exists() {
|
if !ca_dir.exists() {
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
read_dir(&ca_dir).await
|
read_certs_dir(&ca_dir).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument]
|
#[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())
|
let mut dir = fs::read_dir(path.as_ref())
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("read certs dir '{:?}'", path.as_ref()))?;
|
.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)
|
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> {
|
fn parse_utf8(bytes: Vec<u8>) -> anyhow::Result<String> {
|
||||||
String::from_utf8(bytes).context("invalid utf-8")
|
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),
|
|| 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),
|
||||||
|
)?))
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,24 +1,39 @@
|
|||||||
|
|
||||||
|
|
||||||
use axum_extra::routing::TypedPath;
|
use axum_extra::routing::TypedPath;
|
||||||
use serde::Deserialize;
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ssh_key::Fingerprint;
|
||||||
|
|
||||||
#[derive(TypedPath, Deserialize)]
|
#[derive(TypedPath, Deserialize)]
|
||||||
#[typed_path("/certs")]
|
#[typed_path("/certs")]
|
||||||
pub struct CertList;
|
pub struct CertList;
|
||||||
|
|
||||||
#[derive(TypedPath, Deserialize)]
|
#[derive(TypedPath, Deserialize)]
|
||||||
#[typed_path("/certs/:identifier")]
|
#[typed_path("/cert/:identifier")]
|
||||||
pub struct GetCert {
|
pub struct GetCert {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TypedPath, Deserialize)]
|
#[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 struct GetCertInfo {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TypedPath, Deserialize)]
|
#[derive(TypedPath, Deserialize)]
|
||||||
#[typed_path("/certs/:identifier")]
|
#[typed_path("/cert/:identifier")]
|
||||||
pub struct PostCertInfo {
|
pub struct PostCertInfo {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ use std::time::SystemTime;
|
|||||||
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 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};
|
||||||
@ -291,12 +292,12 @@ 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_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 validity_days = validity.as_secs() / ((60*60) * 24);
|
||||||
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}")).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(","));
|
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 {
|
CertInfo {
|
||||||
principals: cert.valid_principals().to_vec(),
|
principals: cert.valid_principals().to_vec(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user