mirror of
https://github.com/goauthentik/authentik
synced 2026-05-15 11:26:31 +02:00
move to cert store
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -293,6 +293,7 @@ dependencies = [
|
||||
"config",
|
||||
"console-subscriber",
|
||||
"eyre",
|
||||
"futures",
|
||||
"glob",
|
||||
"ipnet",
|
||||
"json-subscriber",
|
||||
@@ -317,6 +318,7 @@ dependencies = [
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -298,7 +298,6 @@ serde_json.workspace = true
|
||||
serde_repr.workspace = true
|
||||
sqlx = { workspace = true, optional = true }
|
||||
time.workspace = true
|
||||
tokio-retry2.workspace = true
|
||||
tokio-tungstenite.workspace = true
|
||||
tokio.workspace = true
|
||||
tower.workspace = true
|
||||
|
||||
@@ -22,6 +22,7 @@ axum-server.workspace = true
|
||||
config-rs.workspace = true
|
||||
console-subscriber.workspace = true
|
||||
eyre.workspace = true
|
||||
futures.workspace = true
|
||||
glob.workspace = true
|
||||
ipnet.workspace = true
|
||||
json-subscriber.workspace = true
|
||||
@@ -44,6 +45,7 @@ tracing-error.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
nix.workspace = true
|
||||
|
||||
@@ -8,6 +8,7 @@ use tracing::trace;
|
||||
use crate::config;
|
||||
|
||||
pub mod self_signed;
|
||||
pub mod store;
|
||||
|
||||
/// Dummy resolver for FIPS compliance check.
|
||||
#[derive(Debug)]
|
||||
|
||||
92
packages/ak-common/src/tls/store.rs
Normal file
92
packages/ak-common/src/tls/store.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use ak_client::apis::{
|
||||
configuration::Configuration,
|
||||
crypto_api::{
|
||||
crypto_certificatekeypairs_retrieve, crypto_certificatekeypairs_view_certificate_retrieve,
|
||||
crypto_certificatekeypairs_view_private_key_retrieve,
|
||||
},
|
||||
};
|
||||
use eyre::{Report, Result};
|
||||
use futures::FutureExt as _;
|
||||
use rustls::{
|
||||
crypto::CryptoProvider,
|
||||
pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject as _},
|
||||
sign::CertifiedKey,
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Certificate {
|
||||
pub fingerprint: String,
|
||||
|
||||
pub certificate: String,
|
||||
pub key: String,
|
||||
|
||||
pub certified_key: Arc<CertifiedKey>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CertificateStore {
|
||||
certificates: Arc<Mutex<HashMap<Uuid, Arc<Certificate>>>>,
|
||||
}
|
||||
|
||||
impl CertificateStore {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub async fn ensure_keypair(
|
||||
&self,
|
||||
api_config: &Configuration,
|
||||
kp_uuid: Uuid,
|
||||
) -> Result<Arc<Certificate>> {
|
||||
let kp_uuid_s = kp_uuid.to_string();
|
||||
|
||||
let fingerprint = crypto_certificatekeypairs_retrieve(api_config, &kp_uuid_s)
|
||||
.await?
|
||||
.fingerprint_sha256;
|
||||
|
||||
if let Some(certificate) = self.certificates.lock().await.get(&kp_uuid)
|
||||
&& let Some(fingerprint) = &fingerprint
|
||||
&& &certificate.fingerprint == fingerprint
|
||||
{
|
||||
return Ok(Arc::clone(certificate));
|
||||
}
|
||||
|
||||
let (cert, key) = tokio::try_join!(
|
||||
crypto_certificatekeypairs_view_certificate_retrieve(api_config, &kp_uuid_s, None,)
|
||||
.map(|res| res.map_err(Report::from)),
|
||||
crypto_certificatekeypairs_view_private_key_retrieve(api_config, &kp_uuid_s, None,)
|
||||
.map(|res| res.map_err(Report::from)),
|
||||
)?;
|
||||
|
||||
let certified_key = {
|
||||
let cert_chain = CertificateDer::pem_reader_iter(cert.data.as_bytes())
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let key_der = PrivateKeyDer::from_pem_reader(key.data.as_bytes())?;
|
||||
let provider = CryptoProvider::get_default().expect("no rustls provider installed");
|
||||
Arc::new(CertifiedKey::new(
|
||||
cert_chain,
|
||||
provider.key_provider.load_private_key(key_der)?,
|
||||
))
|
||||
};
|
||||
|
||||
let cert = Arc::new(Certificate {
|
||||
fingerprint: fingerprint.unwrap_or_default(),
|
||||
certificate: cert.data,
|
||||
key: key.data,
|
||||
certified_key,
|
||||
});
|
||||
|
||||
if !cert.fingerprint.is_empty() {
|
||||
self.certificates
|
||||
.lock()
|
||||
.await
|
||||
.insert(kp_uuid, Arc::clone(&cert));
|
||||
}
|
||||
|
||||
Ok(cert)
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,24 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ak_client::{
|
||||
apis::crypto_api::{
|
||||
crypto_certificatekeypairs_view_certificate_retrieve,
|
||||
crypto_certificatekeypairs_view_private_key_retrieve,
|
||||
},
|
||||
models::ProxyOutpostConfig,
|
||||
};
|
||||
use ak_client::models::ProxyOutpostConfig;
|
||||
use ak_common::tls::store::Certificate;
|
||||
use axum::Router;
|
||||
use eyre::{Result, eyre};
|
||||
use rustls::{
|
||||
crypto::CryptoProvider,
|
||||
pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject as _},
|
||||
sign::CertifiedKey,
|
||||
};
|
||||
use tracing::instrument;
|
||||
use url::Url;
|
||||
|
||||
use crate::outpost::proxy::ProxyOutpost;
|
||||
|
||||
const REDIRECT_PARAM: &str = "rd";
|
||||
const _REDIRECT_PARAM: &str = "rd";
|
||||
const CALLBACK_SIGNATURE: &str = "X-authentik-auth-callback";
|
||||
const LOGOUT_SIGNATURE: &str = "X-authentik-logout";
|
||||
const _LOGOUT_SIGNATURE: &str = "X-authentik-logout";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Application {
|
||||
pub(super) host: String,
|
||||
pub(super) provider: ProxyOutpostConfig,
|
||||
pub(super) router: Router,
|
||||
pub(super) cert: Option<Arc<CertifiedKey>>,
|
||||
pub(super) cert: Option<Arc<Certificate>>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
@@ -42,26 +32,12 @@ impl Application {
|
||||
|
||||
// TODO: extract this to a certificate store to avoid re-fetching the certificate every time
|
||||
let cert = if let Some(Some(kp_uuid)) = provider.certificate {
|
||||
let cert = crypto_certificatekeypairs_view_certificate_retrieve(
|
||||
&outpost.controller.api_config,
|
||||
&kp_uuid.to_string(),
|
||||
None,
|
||||
Some(
|
||||
outpost
|
||||
.certificate_store
|
||||
.ensure_keypair(&outpost.controller.api_config, kp_uuid)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
let key = crypto_certificatekeypairs_view_private_key_retrieve(
|
||||
&outpost.controller.api_config,
|
||||
&kp_uuid.to_string(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let cert_chain = CertificateDer::pem_reader_iter(cert.data.as_bytes())
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let key_der = PrivateKeyDer::from_pem_reader(key.data.as_bytes())?;
|
||||
let provider = CryptoProvider::get_default().expect("no rustls provider installed");
|
||||
Some(Arc::new(CertifiedKey::new(
|
||||
cert_chain,
|
||||
provider.key_provider.load_private_key(key_der)?,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -2,7 +2,12 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use ak_axum::router::wrap_router;
|
||||
use ak_client::{apis::outposts_api::outposts_proxy_list, models::ProxyMode};
|
||||
use ak_common::{Tasks, api::fetch_all, config, tls};
|
||||
use ak_common::{
|
||||
Tasks,
|
||||
api::fetch_all,
|
||||
config,
|
||||
tls::{self, store::CertificateStore},
|
||||
};
|
||||
use arc_swap::ArcSwap;
|
||||
use argh::FromArgs;
|
||||
use axum::Router;
|
||||
@@ -33,6 +38,7 @@ pub(crate) struct Cli {}
|
||||
pub(crate) struct ProxyOutpost {
|
||||
controller: Arc<OutpostController>,
|
||||
apps: ArcSwap<HashMap<String, Arc<Application>>>,
|
||||
certificate_store: CertificateStore,
|
||||
default_cert: Arc<CertifiedKey>,
|
||||
}
|
||||
|
||||
@@ -46,6 +52,7 @@ impl Outpost for ProxyOutpost {
|
||||
Ok(Self {
|
||||
controller,
|
||||
apps: ArcSwap::from_pointee(HashMap::with_capacity(0)),
|
||||
certificate_store: CertificateStore::new(),
|
||||
default_cert: Arc::new(tls::self_signed::generate_certifiedkey()?),
|
||||
})
|
||||
}
|
||||
@@ -149,7 +156,7 @@ impl ResolvesServerCert for ProxyOutpost {
|
||||
&& let Some(app) = self.apps.load().get(server_name)
|
||||
&& let Some(cert) = &app.cert
|
||||
{
|
||||
return Some(Arc::clone(cert));
|
||||
return Some(Arc::clone(&cert.certified_key));
|
||||
}
|
||||
Some(Arc::clone(&self.default_cert))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user