(&self, domain: &str, config: &ZtDomainConfig)
| 229 | } |
| 230 | |
| 231 | async fn do_renew(&self, domain: &str, config: &ZtDomainConfig) -> Result<bool> { |
| 232 | let acme_client = self.get_or_create_acme_client(domain, config).await?; |
| 233 | |
| 234 | // Generate new key pair (always use new key for each renewal) |
| 235 | let key = KeyPair::generate().context("failed to generate key")?; |
| 236 | let key_pem = key.serialize_pem(); |
| 237 | let public_key_der = key.public_key_der(); |
| 238 | |
| 239 | // Verify there's a current cert (for audit trail, even though we don't use its key) |
| 240 | if self.kv_store.get_cert_data(domain).is_none() { |
| 241 | bail!("no current certificate to renew"); |
| 242 | } |
| 243 | |
| 244 | // Renew with new key (request wildcard certificate) |
| 245 | let wildcard_domain = format!("*.{}", domain); |
| 246 | info!( |
| 247 | "renewing certificate with new key from ACME for {}...", |
| 248 | wildcard_domain |
| 249 | ); |
| 250 | let new_cert_pem = tokio::time::timeout( |
| 251 | self.config().renew_timeout, |
| 252 | // Note: we request a new cert rather than renew, since we have a new key |
| 253 | acme_client.request_new_certificate(&key_pem, &[wildcard_domain]), |
| 254 | ) |
| 255 | .await |
| 256 | .context("certificate renewal timed out")? |
| 257 | .context("failed to renew certificate")?; |
| 258 | |
| 259 | let not_after = |
| 260 | get_cert_expiry(&new_cert_pem).context("failed to parse certificate expiry")?; |
| 261 | |
| 262 | // Save to KvStore |
| 263 | self.save_cert_to_kvstore(domain, &new_cert_pem, &key_pem, not_after)?; |
| 264 | info!("renewed certificate saved to KvStore"); |
| 265 | |
| 266 | // Generate and save attestation |
| 267 | self.generate_and_save_attestation(domain, &public_key_der) |
| 268 | .await?; |
| 269 | |
| 270 | // Load into memory cert store |
| 271 | let cert_data = CertData { |
| 272 | cert_pem: new_cert_pem, |
| 273 | key_pem, |
| 274 | not_after, |
| 275 | issued_by: self.kv_store.my_node_id(), |
| 276 | issued_at: now_secs(), |
| 277 | }; |
| 278 | self.cert_resolver.update_cert(domain, &cert_data)?; |
| 279 | |
| 280 | info!( |
| 281 | "renewed certificate loaded (expires in {} days)", |
| 282 | (not_after - now_secs()) / 86400 |
| 283 | ); |
| 284 | Ok(true) |
| 285 | } |
| 286 | |
| 287 | async fn get_or_create_acme_client( |
| 288 | &self, |
no test coverage detected