Skip to content

Commit

Permalink
refactor(gateway): renew gateway certificate returns more info about …
Browse files Browse the repository at this point in the history
…success
  • Loading branch information
iulianbarbu committed Dec 22, 2023
1 parent e8bb1a0 commit 76ab1b2
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 47 deletions.
46 changes: 42 additions & 4 deletions gateway/src/api/latest.rs
Expand Up @@ -48,7 +48,7 @@ use x509_parser::parse_x509_certificate;
use x509_parser::pem::parse_x509_pem;
use x509_parser::time::ASN1Time;

use crate::acme::{AcmeClient, CustomDomain};
use crate::acme::{AccountWrapper, AcmeClient, CustomDomain};
use crate::auth::{ScopedUser, User};
use crate::project::{ContainerInspectResponseExt, Project, ProjectCreating};
use crate::service::GatewayService;
Expand Down Expand Up @@ -880,10 +880,48 @@ async fn renew_gateway_acme_certificate(
Extension(resolver): Extension<Arc<GatewayCertResolver>>,
AxumJson(credentials): AxumJson<AccountCredentials<'_>>,
) -> Result<String, Error> {
service
.renew_certificate(&acme_client, resolver, credentials)
let account = AccountWrapper::from(credentials).0;
let certs = service
.fetch_certificate(&acme_client, account.credentials())
.await;
Ok(r#""Renewed the gateway certificate.""#.to_string())
// Safe to unwrap because a 'ChainAndPrivateKey' is built from a PEM.
let chain_and_pk = certs.into_pem().unwrap();

let (_, pem) = parse_x509_pem(chain_and_pk.as_bytes())
.unwrap_or_else(|_| panic!("Malformed existing PEM certificate for the gateway."));
let (_, x509_cert) = parse_x509_certificate(pem.contents.as_bytes())
.unwrap_or_else(|_| panic!("Malformed existing X509 certificate for the gateway."));

// We compute the difference between the certificate expiry date and current timestamp because we want to trigger the
// gateway certificate renewal only during it's last 30 days of validity or if the certificate is expired.
let diff = x509_cert.validity().not_after.sub(ASN1Time::now());

// Renew only when the difference is `None` (meaning certificate expired) or we're within the last 30 days of validity.
if diff.is_none()
|| diff
.expect("to be Some given we checked for None previously")
.whole_days()
<= RENEWAL_VALIDITY_THRESHOLD_IN_DAYS
{
let tls_path = service.state_location.join("ssl.pem");
let certs = service
.create_certificate(&acme_client, account.credentials())
.await;
resolver
.serve_default_der(certs.clone())
.await
.expect("Failed to serve the default certs");
certs
.save_pem(&tls_path)
.expect("to save the certificate locally");
return Ok(r#""Renewed the gateway certificate.""#.to_string());
}

return Ok(format!(
"\"Gateway certificate was not renewed. There are {} days until the certificate expires.\"",
diff.expect("to be Some given we checked for None previously")
.whole_days()
));
}

#[utoipa::path(
Expand Down
45 changes: 2 additions & 43 deletions gateway/src/service.rs
Expand Up @@ -254,7 +254,7 @@ pub struct GatewayService {
provider: GatewayContextProvider,
db: SqlitePool,
task_router: TaskRouter,
state_location: PathBuf,
pub state_location: PathBuf,

/// Maximum number of containers the gateway can start before blocking cch projects
cch_container_limit: u32,
Expand Down Expand Up @@ -854,7 +854,7 @@ impl GatewayService {
}
}

async fn create_certificate<'a>(
pub async fn create_certificate<'a>(
&self,
acme: &AcmeClient,
creds: AccountCredentials<'a>,
Expand Down Expand Up @@ -900,47 +900,6 @@ impl GatewayService {
}
}

/// Renew the gateway certificate if there less than 30 days until the current
/// certificate expiration.
pub(crate) async fn renew_certificate(
&self,
acme: &AcmeClient,
resolver: Arc<GatewayCertResolver>,
creds: AccountCredentials<'_>,
) {
let account = AccountWrapper::from(creds).0;
let certs = self.fetch_certificate(acme, account.credentials()).await;
// Safe to unwrap because a 'ChainAndPrivateKey' is built from a PEM.
let chain_and_pk = certs.into_pem().unwrap();

let (_, pem) = parse_x509_pem(chain_and_pk.as_bytes())
.unwrap_or_else(|_| panic!("Malformed existing PEM certificate for the gateway."));
let (_, x509_cert) = parse_x509_certificate(pem.contents.as_bytes())
.unwrap_or_else(|_| panic!("Malformed existing X509 certificate for the gateway."));

// We compute the difference between the certificate expiry date and current timestamp because we want to trigger the
// gateway certificate renewal only during it's last 30 days of validity or if the certificate is expired.
let diff = x509_cert.validity().not_after.sub(ASN1Time::now());

// Renew only when the difference is `None` (meaning certificate expired) or we're within the last 30 days of validity.
if diff.is_none()
|| diff
.expect("to be Some given we checked for None previously")
.whole_days()
<= RENEWAL_VALIDITY_THRESHOLD_IN_DAYS
{
let tls_path = self.state_location.join("ssl.pem");
let certs = self.create_certificate(acme, account.credentials()).await;
resolver
.serve_default_der(certs.clone())
.await
.expect("Failed to serve the default certs");
certs
.save_pem(&tls_path)
.expect("to save the certificate locally");
}
}

pub fn context(&self) -> GatewayContext {
self.provider.context()
}
Expand Down

0 comments on commit 76ab1b2

Please sign in to comment.