save private key generated during acme handshake
This commit is contained in:
parent
06cda77772
commit
209daacf1b
11
TODO
Normal file
11
TODO
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
- make acme store implement https://docs.rs/rustls/latest/rustls/server/trait.ResolvesServerCert.html
|
||||||
|
|
||||||
|
- pass that into https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html#
|
||||||
|
|
||||||
|
- turn that into a TlsAcceptor (From is implemented here:
|
||||||
|
https://docs.rs/tokio-rustls/latest/tokio_rustls/struct.TlsAcceptor.html#impl-From%3CArc%3CServerConfig%3E%3E-for-TlsAcceptor)
|
||||||
|
|
||||||
|
- use tls-listener crate to wrap hyper accepter: https://github.com/tmccombs/tls-listener/blob/main/examples/http.rs#L24
|
||||||
|
- https://github.com/tmccombs/tls-listener/blob/main/examples/tls_config/mod.rs
|
||||||
|
|
||||||
|
- logging
|
@ -1,5 +1,6 @@
|
|||||||
pub mod manager;
|
pub mod manager;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
|
||||||
pub type AccountKey = openssl::pkey::PKey<openssl::pkey::Private>;
|
pub type PrivateKey = openssl::pkey::PKey<openssl::pkey::Private>;
|
||||||
|
|
||||||
pub type Certificate = openssl::x509::X509;
|
pub type Certificate = openssl::x509::X509;
|
||||||
|
@ -84,7 +84,7 @@ where
|
|||||||
// if there's an existing cert, and its expiry (determined by the soonest value of
|
// if there's an existing cert, and its expiry (determined by the soonest value of
|
||||||
// not_after amongst its parts) is later than 30 days from now, then we consider it to be
|
// not_after amongst its parts) is later than 30 days from now, then we consider it to be
|
||||||
// synced.
|
// synced.
|
||||||
if let Ok(cert) = self.store.get_certificate(domain.as_str()) {
|
if let Ok((_, cert)) = self.store.get_certificate(domain.as_str()) {
|
||||||
let thirty_days = openssl::asn1::Asn1Time::days_from_now(30)
|
let thirty_days = openssl::asn1::Asn1Time::days_from_now(30)
|
||||||
.expect("parsed thirty days from now as Asn1Time");
|
.expect("parsed thirty days from now as Asn1Time");
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ where
|
|||||||
// Create a certificate signing request for the order, and request
|
// Create a certificate signing request for the order, and request
|
||||||
// the certificate.
|
// the certificate.
|
||||||
let order = order
|
let order = order
|
||||||
.finalize(acme2::Csr::Automatic(pkey))
|
.finalize(acme2::Csr::Automatic(pkey.clone()))
|
||||||
.await
|
.await
|
||||||
.map_unexpected()?;
|
.map_unexpected()?;
|
||||||
|
|
||||||
@ -260,7 +260,7 @@ where
|
|||||||
|
|
||||||
println!("certificate for {} successfully retrieved", domain.as_str());
|
println!("certificate for {} successfully retrieved", domain.as_str());
|
||||||
self.store
|
self.store
|
||||||
.set_certificate(domain.as_str(), cert)
|
.set_certificate(domain.as_str(), &pkey, cert)
|
||||||
.map_unexpected()?;
|
.map_unexpected()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::{fs, io, path, sync};
|
use std::{fs, io, path, sync};
|
||||||
|
|
||||||
use crate::domain::acme::{AccountKey, Certificate};
|
use crate::domain::acme::{Certificate, PrivateKey};
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::error::{MapUnexpected, ToUnexpected};
|
use crate::error::{MapUnexpected, ToUnexpected};
|
||||||
|
|
||||||
use hex::ToHex;
|
use hex::ToHex;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
@ -37,8 +38,8 @@ pub enum GetCertificateError {
|
|||||||
|
|
||||||
#[mockall::automock]
|
#[mockall::automock]
|
||||||
pub trait Store {
|
pub trait Store {
|
||||||
fn set_account_key(&self, k: &AccountKey) -> Result<(), error::Unexpected>;
|
fn set_account_key(&self, k: &PrivateKey) -> Result<(), error::Unexpected>;
|
||||||
fn get_account_key(&self) -> Result<AccountKey, GetAccountKeyError>;
|
fn get_account_key(&self) -> Result<PrivateKey, GetAccountKeyError>;
|
||||||
|
|
||||||
fn set_http01_challenge_key(&self, token: &str, key: &str) -> Result<(), error::Unexpected>;
|
fn set_http01_challenge_key(&self, token: &str, key: &str) -> Result<(), error::Unexpected>;
|
||||||
fn get_http01_challenge_key(&self, token: &str) -> Result<String, GetHttp01ChallengeKeyError>;
|
fn get_http01_challenge_key(&self, token: &str) -> Result<String, GetHttp01ChallengeKeyError>;
|
||||||
@ -47,15 +48,25 @@ pub trait Store {
|
|||||||
fn set_certificate(
|
fn set_certificate(
|
||||||
&self,
|
&self,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
|
key: &PrivateKey,
|
||||||
cert: Vec<Certificate>,
|
cert: Vec<Certificate>,
|
||||||
) -> Result<(), error::Unexpected>;
|
) -> Result<(), error::Unexpected>;
|
||||||
|
|
||||||
/// Returned vec is guaranteed to have len > 0
|
/// Returned vec is guaranteed to have len > 0
|
||||||
fn get_certificate(&self, domain: &str) -> Result<Vec<Certificate>, GetCertificateError>;
|
fn get_certificate(
|
||||||
|
&self,
|
||||||
|
domain: &str,
|
||||||
|
) -> Result<(PrivateKey, Vec<Certificate>), GetCertificateError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BoxedStore: Store + Send + Sync + Clone + 'static {}
|
pub trait BoxedStore: Store + Send + Sync + Clone + 'static {}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct StoredPKeyCert {
|
||||||
|
private_key_pem: String,
|
||||||
|
cert_pems: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
struct FSStore {
|
struct FSStore {
|
||||||
dir_path: path::PathBuf,
|
dir_path: path::PathBuf,
|
||||||
}
|
}
|
||||||
@ -86,21 +97,24 @@ impl FSStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn certificate_path(&self, domain: &str) -> path::PathBuf {
|
fn certificate_path(&self, domain: &str) -> path::PathBuf {
|
||||||
self.dir_path.join("certificates").join(domain)
|
self.dir_path
|
||||||
|
.join("certificates")
|
||||||
|
.join(domain)
|
||||||
|
.with_extension("json")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BoxedStore for sync::Arc<FSStore> {}
|
impl BoxedStore for sync::Arc<FSStore> {}
|
||||||
|
|
||||||
impl Store for sync::Arc<FSStore> {
|
impl Store for sync::Arc<FSStore> {
|
||||||
fn set_account_key(&self, k: &AccountKey) -> Result<(), error::Unexpected> {
|
fn set_account_key(&self, k: &PrivateKey) -> Result<(), error::Unexpected> {
|
||||||
let mut file = fs::File::create(self.account_key_path()).map_unexpected()?;
|
let mut file = fs::File::create(self.account_key_path()).map_unexpected()?;
|
||||||
let pem = k.private_key_to_pem_pkcs8().map_unexpected()?;
|
let pem = k.private_key_to_pem_pkcs8().map_unexpected()?;
|
||||||
file.write_all(&pem).map_unexpected()?;
|
file.write_all(&pem).map_unexpected()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_account_key(&self) -> Result<AccountKey, GetAccountKeyError> {
|
fn get_account_key(&self) -> Result<PrivateKey, GetAccountKeyError> {
|
||||||
let mut file = fs::File::open(self.account_key_path()).map_err(|e| match e.kind() {
|
let mut file = fs::File::open(self.account_key_path()).map_err(|e| match e.kind() {
|
||||||
io::ErrorKind::NotFound => GetAccountKeyError::NotFound,
|
io::ErrorKind::NotFound => GetAccountKeyError::NotFound,
|
||||||
_ => e.to_unexpected().into(),
|
_ => e.to_unexpected().into(),
|
||||||
@ -109,7 +123,7 @@ impl Store for sync::Arc<FSStore> {
|
|||||||
let mut pem = Vec::<u8>::new();
|
let mut pem = Vec::<u8>::new();
|
||||||
file.read_to_end(&mut pem).map_unexpected()?;
|
file.read_to_end(&mut pem).map_unexpected()?;
|
||||||
|
|
||||||
let k = AccountKey::private_key_from_pem(&pem).map_unexpected()?;
|
let k = PrivateKey::private_key_from_pem(&pem).map_unexpected()?;
|
||||||
Ok(k)
|
Ok(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,38 +154,50 @@ impl Store for sync::Arc<FSStore> {
|
|||||||
fn set_certificate(
|
fn set_certificate(
|
||||||
&self,
|
&self,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
|
key: &PrivateKey,
|
||||||
cert: Vec<Certificate>,
|
cert: Vec<Certificate>,
|
||||||
) -> Result<(), error::Unexpected> {
|
) -> Result<(), error::Unexpected> {
|
||||||
let cert: Vec<String> = cert
|
let to_store = StoredPKeyCert {
|
||||||
|
private_key_pem: String::from_utf8(key.private_key_to_pem_pkcs8().map_unexpected()?)
|
||||||
|
.map_unexpected()?,
|
||||||
|
cert_pems: cert
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|cert| {
|
.map(|cert| {
|
||||||
let cert_pem = cert.to_pem().map_unexpected()?;
|
let cert_pem = cert.to_pem().map_unexpected()?;
|
||||||
let cert_pem = String::from_utf8(cert_pem).map_unexpected()?;
|
let cert_pem = String::from_utf8(cert_pem).map_unexpected()?;
|
||||||
Ok::<String, error::Unexpected>(cert_pem)
|
Ok::<String, error::Unexpected>(cert_pem)
|
||||||
})
|
})
|
||||||
.try_collect()?;
|
.try_collect()?,
|
||||||
|
};
|
||||||
|
|
||||||
let cert_file = fs::File::create(self.certificate_path(domain)).map_unexpected()?;
|
let cert_file = fs::File::create(self.certificate_path(domain)).map_unexpected()?;
|
||||||
|
|
||||||
serde_json::to_writer(cert_file, &cert).map_unexpected()?;
|
serde_json::to_writer(cert_file, &to_store).map_unexpected()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_certificate(&self, domain: &str) -> Result<Vec<Certificate>, GetCertificateError> {
|
fn get_certificate(
|
||||||
|
&self,
|
||||||
|
domain: &str,
|
||||||
|
) -> Result<(PrivateKey, Vec<Certificate>), GetCertificateError> {
|
||||||
let file = fs::File::open(self.certificate_path(domain)).map_err(|e| match e.kind() {
|
let file = fs::File::open(self.certificate_path(domain)).map_err(|e| match e.kind() {
|
||||||
io::ErrorKind::NotFound => GetCertificateError::NotFound,
|
io::ErrorKind::NotFound => GetCertificateError::NotFound,
|
||||||
_ => e.to_unexpected().into(),
|
_ => e.to_unexpected().into(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let cert: Vec<String> = serde_json::from_reader(file).map_unexpected()?;
|
let stored: StoredPKeyCert = serde_json::from_reader(file).map_unexpected()?;
|
||||||
|
|
||||||
let cert: Vec<Certificate> = cert
|
let key =
|
||||||
|
PrivateKey::private_key_from_pem(stored.private_key_pem.as_bytes()).map_unexpected()?;
|
||||||
|
|
||||||
|
let cert: Vec<Certificate> = stored
|
||||||
|
.cert_pems
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|cert| openssl::x509::X509::from_pem(cert.as_bytes()).map_unexpected())
|
.map(|cert| openssl::x509::X509::from_pem(cert.as_bytes()).map_unexpected())
|
||||||
.try_collect()?;
|
.try_collect()?;
|
||||||
|
|
||||||
Ok(cert)
|
Ok((key, cert))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +213,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
store.get_account_key(),
|
store.get_account_key(),
|
||||||
Err::<AccountKey, GetAccountKeyError>(GetAccountKeyError::NotFound)
|
Err::<PrivateKey, GetAccountKeyError>(GetAccountKeyError::NotFound)
|
||||||
));
|
));
|
||||||
|
|
||||||
let k = acme2::gen_rsa_private_key(4096).expect("private key generated");
|
let k = acme2::gen_rsa_private_key(4096).expect("private key generated");
|
||||||
|
Loading…
Reference in New Issue
Block a user