Skip to content

Commit 6ea9d7f

Browse files
committed
send acme logs to core
1 parent 1b78937 commit 6ea9d7f

3 files changed

Lines changed: 64 additions & 20 deletions

File tree

src/acme.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,19 @@ pub async fn run_acme_http01(
5757
progress_tx: mpsc::UnboundedSender<AcmeStep>,
5858
) -> anyhow::Result<AcmeCertResult> {
5959
info!("Starting ACME HTTP-01 certificate issuance for domain: {domain}");
60+
let env_label = if use_staging { "staging" } else { "production" };
61+
info!("Using Let's Encrypt {env_label} environment");
6062

6163
// Restore or create account.
6264
let (account, credentials) = if existing_credentials_json.is_empty() {
63-
info!("Creating new ACME account");
65+
info!("No stored ACME account found; creating a new one with Let's Encrypt");
6466
let builder = Account::builder().context("Failed to create ACME account builder")?;
6567
let dir_url = if use_staging {
6668
LetsEncrypt::Staging.url().to_owned()
6769
} else {
6870
LetsEncrypt::Production.url().to_owned()
6971
};
70-
info!("Using ACME directory URL: {dir_url}");
72+
info!("Registering account at ACME directory: {dir_url}");
7173
let (account, credentials) = builder
7274
.create(
7375
&NewAccount {
@@ -80,6 +82,7 @@ pub async fn run_acme_http01(
8082
)
8183
.await
8284
.context("Failed to create ACME account")?;
85+
info!("ACME account registered successfully");
8386
(account, credentials)
8487
} else {
8588
info!("Restoring existing ACME account from stored credentials");
@@ -90,6 +93,7 @@ pub async fn run_acme_http01(
9093
.from_credentials(creds)
9194
.await
9295
.context("Failed to restore ACME account from credentials")?;
96+
info!("ACME account restored successfully");
9397
// After restoring there are no new credentials returned - re-serialize the same ones.
9498
let restored_creds: AccountCredentials =
9599
serde_json::from_str(&existing_credentials_json)
@@ -104,6 +108,7 @@ pub async fn run_acme_http01(
104108
.new_order(&NewOrder::new(&[Identifier::Dns(domain.clone())]))
105109
.await
106110
.context("Failed to create ACME order")?;
111+
info!("ACME order placed for domain: {domain}");
107112

108113
// Collect all (token, key_authorization) pairs we need to serve.
109114
let challenge_map: Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(HashMap::new()));
@@ -175,7 +180,7 @@ pub async fn run_acme_http01(
175180
let token = challenge.token.clone();
176181
let key_auth = challenge.key_authorization().as_str().to_owned();
177182

178-
debug!("HTTP-01 challenge token: {token}");
183+
info!("Preparing HTTP-01 challenge for domain: {domain} (token: {token})");
179184

180185
{
181186
let mut map = challenge_map.lock().unwrap();
@@ -186,32 +191,36 @@ pub async fn run_acme_http01(
186191
.set_ready()
187192
.await
188193
.context("Failed to signal ACME challenge as ready")?;
194+
info!("HTTP-01 challenge signalled as ready; waiting for Let's Encrypt to validate");
189195
}
190196

191197
// LE will now attempt HTTP-01 validation against our challenge server.
192198
let _ = progress_tx.send(AcmeStep::ValidatingDomain);
199+
info!("Polling Let's Encrypt for domain validation result...");
193200

194201
// Wait for the order to become ready for finalization.
195202
let status = order
196203
.poll_ready(&RetryPolicy::default())
197204
.await
198205
.context("ACME order did not become ready")?;
199-
debug!("ACME order status after poll_ready: {status:?}");
206+
info!("Domain validation complete, order status: {status:?}");
200207

201208
server_handle.abort();
202-
info!("ACME challenge server shut down");
209+
info!("ACME challenge server shut down; port 80 released");
203210

204211
if let Some(done_tx) = port80_permit {
205212
let _ = done_tx.send(());
206213
}
207214

208215
// Domain validated; finalizing order and retrieving the certificate.
209216
let _ = progress_tx.send(AcmeStep::IssuingCertificate);
217+
info!("Finalizing ACME order and requesting certificate issuance...");
210218

211219
let key_pem = order
212220
.finalize()
213221
.await
214222
.context("Failed to finalize ACME order")?;
223+
info!("ACME order finalized; polling for certificate...");
215224

216225
// Poll until the certificate is issued.
217226
let cert_pem = order

src/grpc.rs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ use tower::ServiceBuilder;
2525
use tracing::Instrument;
2626

2727
use crate::{
28-
MIN_CORE_VERSION, VERSION, acme,
28+
LogsReceiver, MIN_CORE_VERSION, VERSION, acme,
2929
acme::Port80Permit,
3030
error::ApiError,
3131
http::{GRPC_CERT_NAME, GRPC_KEY_NAME},
3232
proto::{
33-
AcmeCertificate, AcmeChallenge, AcmeIssueEvent, AcmeProgress, AcmeStep, CoreRequest,
34-
CoreResponse, DeviceInfo, acme_issue_event, core_request, core_response, proxy_server,
33+
AcmeCertificate, AcmeChallenge, AcmeIssueEvent, AcmeLogs, AcmeProgress, AcmeStep,
34+
CoreRequest, CoreResponse, DeviceInfo, acme_issue_event, core_request, core_response,
35+
proxy_server,
3536
},
3637
};
3738

@@ -58,6 +59,9 @@ pub(crate) struct ProxyServer {
5859
/// `Some` only when the main HTTP server is bound to port 80.
5960
/// Used to hand off port 80 gracefully during ACME HTTP-01 challenges.
6061
port80_pause_tx: Option<mpsc::Sender<(oneshot::Sender<()>, oneshot::Receiver<()>)>>,
62+
/// Shared log receiver - written by `GrpcLogLayer` for every tracing event.
63+
/// Drained during ACME execution to collect proxy log lines for error reporting.
64+
logs_rx: LogsReceiver,
6165
}
6266

6367
impl ProxyServer {
@@ -69,6 +73,7 @@ impl ProxyServer {
6973
reset_tx: broadcast::Sender<()>,
7074
https_cert_tx: broadcast::Sender<(String, String)>,
7175
port80_pause_tx: Option<mpsc::Sender<(oneshot::Sender<()>, oneshot::Receiver<()>)>>,
76+
logs_rx: LogsReceiver,
7277
) -> Self {
7378
Self {
7479
cookie_key,
@@ -82,6 +87,7 @@ impl ProxyServer {
8287
reset_tx,
8388
https_cert_tx,
8489
port80_pause_tx,
90+
logs_rx,
8591
}
8692
}
8793

@@ -205,6 +211,7 @@ impl Clone for ProxyServer {
205211
reset_tx: self.reset_tx.clone(),
206212
https_cert_tx: self.https_cert_tx.clone(),
207213
port80_pause_tx: self.port80_pause_tx.clone(),
214+
logs_rx: Arc::clone(&self.logs_rx),
208215
}
209216
}
210217
}
@@ -460,14 +467,21 @@ impl proxy_server::Proxy for ProxyServer {
460467

461468
let (tx, rx) = mpsc::unbounded_channel::<Result<AcmeIssueEvent, Status>>();
462469

463-
// Emit the first progress step immediately we are connected and about to start.
470+
// Emit the first progress step immediately - we are connected and about to start.
464471
let _ = tx.send(Ok(AcmeIssueEvent {
465472
payload: Some(acme_issue_event::Payload::Progress(AcmeProgress {
466473
step: AcmeStep::Connecting as i32,
467474
})),
468475
}));
469476

477+
// Drain any stale log entries accumulated before this ACME run started.
478+
{
479+
let mut rx_guard = self.logs_rx.lock().await;
480+
while rx_guard.try_recv().is_ok() {}
481+
}
482+
470483
let pause_tx = self.port80_pause_tx.clone();
484+
let logs_rx = Arc::clone(&self.logs_rx);
471485
tokio::spawn(async move {
472486
// Request a graceful hand-off of port 80 from the main HTTP server if it is bound
473487
// there, so the ACME challenge listener can bind.
@@ -505,7 +519,7 @@ impl proxy_server::Proxy for ProxyServer {
505519
})),
506520
};
507521
if tx_fwd.send(Ok(event)).is_err() {
508-
// Core disconnected stop forwarding.
522+
// Core disconnected - stop forwarding.
509523
break;
510524
}
511525
}
@@ -538,6 +552,30 @@ impl proxy_server::Proxy for ProxyServer {
538552
}
539553
Err(err) => {
540554
error!("ACME HTTP-01 failed for domain '{domain}': {err}");
555+
556+
// Drain collected log lines and forward them to Core before the error status.
557+
let log_lines: Vec<String> = {
558+
let mut rx_guard = logs_rx.lock().await;
559+
let mut lines = Vec::new();
560+
while let Ok(entry) = rx_guard.try_recv() {
561+
lines.push(format!(
562+
"{} {} {}: message={}",
563+
entry.timestamp,
564+
entry.level.to_uppercase(),
565+
entry.target,
566+
entry.message
567+
));
568+
}
569+
lines
570+
};
571+
if !log_lines.is_empty() {
572+
let _ = tx.send(Ok(AcmeIssueEvent {
573+
payload: Some(acme_issue_event::Payload::Logs(AcmeLogs {
574+
lines: log_lines,
575+
})),
576+
}));
577+
}
578+
541579
let _ = tx.send(Err(Status::internal(format!(
542580
"ACME HTTP-01 certificate issuance failed: {err}"
543581
))));

src/http.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,19 @@ pub async fn run_server(
326326
(None, None)
327327
};
328328

329+
// Both the gRPC setup loop and the permanent ProxyServer
330+
// share the same Arc<Mutex<Receiver<LogEntry>>> so they draw from the same channel.
331+
let logs_rx = logs_rx
332+
.ok_or_else(|| anyhow::anyhow!("Log receiver not available; cannot start server"))?;
333+
329334
// connect to upstream gRPC server
330335
let grpc_server = ProxyServer::new(
331336
Arc::clone(&cookie_key),
332337
env_config.cert_dir.clone(),
333338
reset_tx,
334339
https_cert_tx,
335340
port80_pause_tx,
341+
Arc::clone(&logs_rx),
336342
);
337343

338344
// Preload existing TLS configuration so /api/v1/info can report "disconnected"
@@ -347,11 +353,6 @@ pub async fn run_server(
347353
// Start gRPC server.
348354
debug!("Spawning gRPC server task");
349355
tasks.spawn(async move {
350-
let logs_rx = logs_rx.ok_or_else(|| {
351-
anyhow::anyhow!(
352-
"gRPC logs receiver not available for setup process; reset cannot be handled"
353-
)
354-
})?;
355356
let mut proxy_configuration = config;
356357

357358
loop {
@@ -360,11 +361,7 @@ pub async fn run_server(
360361
conf
361362
} else {
362363
info!("gRPC certificates not found, running setup process");
363-
let conf = run_setup(
364-
&env_config_clone,
365-
Arc::clone(&logs_rx),
366-
)
367-
.await?;
364+
let conf = run_setup(&env_config_clone, Arc::clone(&logs_rx)).await?;
368365
info!("Setup process completed successfully");
369366
proxy_configuration = Some(conf.clone());
370367
conf

0 commit comments

Comments
 (0)