From a84f8e6165034053c6709ff7dff249aa9b2aa17a Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Mon, 9 Mar 2026 23:58:30 +0100 Subject: [PATCH] agent production logging --- .../Security/EnrollmentAwaiter.cs | 100 +++++++++++++++--- .../AutopilotMonitor.Agent/buildcounter.txt | 2 +- 2 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Security/EnrollmentAwaiter.cs b/src/Agent/AutopilotMonitor.Agent.Core/Security/EnrollmentAwaiter.cs index 8bab37d3..a7cc4b94 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Security/EnrollmentAwaiter.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Security/EnrollmentAwaiter.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; @@ -19,11 +21,6 @@ namespace AutopilotMonitor.Agent.Core.Security /// /// Polls the certificate store until a valid MDM certificate is found or the timeout expires. /// - /// Optional specific certificate thumbprint to wait for - /// Maximum wait time in minutes (0 = wait indefinitely) - /// Logger for status output - /// Cancellation token for clean shutdown - /// The MDM certificate, or null if the timeout expired or was cancelled public static async Task WaitForMdmCertificateAsync( string thumbprint = null, int timeoutMinutes = 480, @@ -39,30 +36,36 @@ namespace AutopilotMonitor.Agent.Core.Security while (!cancellationToken.IsCancellationRequested) { - // Lightweight check: opens cert store, scans by issuer+EKU, closes — takes milliseconds - var cert = CertificateHelper.FindMdmCertificate(thumbprint); + var cert = CertificateHelper.FindMdmCertificate(thumbprint, logger); - if (cert != null && CertificateHelper.IsCertificateValid(cert)) + if (cert != null) { + // No IsCertificateValid check here: freshly provisioned SCEP certs can have + // a NotBefore slightly in the future due to clock skew between device and CA. + // The cert existing in the store with correct issuer+EKU is sufficient. var elapsed = DateTime.UtcNow - startTime; - logger?.Info($"Await-enrollment: MDM certificate found after {FormatElapsed(elapsed)} — Issuer={cert.Issuer}, Thumbprint={cert.Thumbprint}"); + logger?.Info($"Await-enrollment: MDM certificate found after {FormatElapsed(elapsed)} — " + + $"Issuer={cert.Issuer}, Thumbprint={cert.Thumbprint}, " + + $"Valid={cert.NotBefore:yyyy-MM-dd HH:mm:ss}..{cert.NotAfter:yyyy-MM-dd HH:mm:ss}"); return cert; } var totalElapsed = DateTime.UtcNow - startTime; - // Check timeout if (totalElapsed >= timeout) { logger?.Warning($"Await-enrollment: Timeout after {FormatElapsed(totalElapsed)} — no MDM certificate found"); + LogCertificateStoreDetails(logger); return null; } - // Periodic status log (every 1 minute) + // Periodic status with cert counts (every 1 minute) if (DateTime.UtcNow - lastLogTime >= LogInterval) { var remaining = timeout - totalElapsed; - logger?.Info($"Await-enrollment: Waiting for MDM certificate... (elapsed: {FormatElapsed(totalElapsed)}, remaining: {FormatElapsed(remaining)})"); + var storeSummary = GetCertificateStoreSummary(); + logger?.Info($"Await-enrollment: Waiting for MDM certificate... " + + $"(elapsed: {FormatElapsed(totalElapsed)}, remaining: {FormatElapsed(remaining)}, {storeSummary})"); lastLogTime = DateTime.UtcNow; } @@ -80,6 +83,79 @@ namespace AutopilotMonitor.Agent.Core.Security return null; } + /// + /// Returns a short summary of cert counts per store, e.g. "LocalMachine=2, CurrentUser=0". + /// + private static string GetCertificateStoreSummary() + { + try + { + int lmCount = 0, cuCount = 0; + + using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine)) + { + store.Open(OpenFlags.ReadOnly); + lmCount = store.Certificates.Count; + } + + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + { + store.Open(OpenFlags.ReadOnly); + cuCount = store.Certificates.Count; + } + + return $"certs in store: LM={lmCount}, CU={cuCount}"; + } + catch + { + return "certs in store: unknown"; + } + } + + /// + /// Full cert store dump — only on timeout to help diagnose why no MDM cert was matched. + /// + private static void LogCertificateStoreDetails(AgentLogger logger) + { + if (logger == null) return; + + try + { + var stores = new[] + { + new { Location = StoreLocation.LocalMachine, Name = StoreName.My }, + new { Location = StoreLocation.CurrentUser, Name = StoreName.My } + }; + + foreach (var storeInfo in stores) + { + using (var store = new X509Store(storeInfo.Name, storeInfo.Location)) + { + store.Open(OpenFlags.ReadOnly); + var certs = store.Certificates.OfType().ToList(); + + logger.Info($"Await-enrollment: {storeInfo.Location}\\{storeInfo.Name} — {certs.Count} certificate(s):"); + + foreach (var cert in certs) + { + var hasClientAuth = cert.Extensions + .OfType() + .Any(ext => ext.EnhancedKeyUsages + .OfType() + .Any(oid => oid.Value == "1.3.6.1.5.5.7.3.2")); + + logger.Info($" - Issuer={cert.Issuer}, Thumbprint={cert.Thumbprint}, " + + $"ClientAuth={hasClientAuth}, Valid={cert.NotBefore:yyyy-MM-dd}..{cert.NotAfter:yyyy-MM-dd}"); + } + } + } + } + catch (Exception ex) + { + logger.Warning($"Await-enrollment: Could not read certificate store: {ex.Message}"); + } + } + private static string FormatElapsed(TimeSpan elapsed) { if (elapsed.TotalHours >= 1) diff --git a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt index b570ddbf..8b84f570 100644 --- a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt +++ b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt @@ -1 +1 @@ -391 +397