-
Notifications
You must be signed in to change notification settings - Fork 50
Load the HTTPS certificate while validating settings instead of at Kestrel bind time #5891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ namespace ServiceControl.Infrastructure; | |
|
|
||
| using System; | ||
| using System.IO; | ||
| using System.Security.Cryptography.X509Certificates; | ||
| using System.Text.Json.Serialization; | ||
| using Microsoft.Extensions.Logging; | ||
| using ServiceControl.Configuration; | ||
|
|
@@ -57,6 +58,12 @@ public HttpsSettings(SettingsRootNamespace rootNamespace) | |
| [JsonIgnore] | ||
| public string CertificatePassword { get; } | ||
|
|
||
| /// <summary> | ||
| /// The certificate loaded from <see cref="CertificatePath"/>, or null when HTTPS is disabled. | ||
| /// </summary> | ||
| [JsonIgnore] | ||
| public X509Certificate2 Certificate { get; private set; } | ||
|
|
||
| /// <summary> | ||
| /// When true, HTTP requests will be redirected to HTTPS. | ||
| /// Requires HTTPS to be properly configured. Default is false. | ||
|
|
@@ -103,6 +110,29 @@ void ValidateCertificateConfiguration() | |
| logger.LogCritical(message); | ||
| throw new InvalidOperationException(message); | ||
| } | ||
|
|
||
| // Loaded here rather than when Kestrel binds its endpoints: an unusable certificate is a | ||
| // configuration error, and binding happens only after every hosted service has started. | ||
| try | ||
| { | ||
| Certificate = string.IsNullOrEmpty(CertificatePassword) | ||
| ? X509CertificateLoader.LoadPkcs12FromFile(CertificatePath, null) | ||
| : X509CertificateLoader.LoadPkcs12FromFile(CertificatePath, CertificatePassword); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| // .NET reports several unrelated causes as "the password may be incorrect", so describe | ||
| // the file itself too. Never the password, only whether one was configured. | ||
| var file = new FileInfo(CertificatePath); | ||
| var message = $"The HTTPS certificate could not be loaded, so this instance cannot start. " + | ||
| $"Https.CertificatePath: '{CertificatePath}' ({file.Length} bytes, last modified {file.LastWriteTimeUtc:u}). " + | ||
| $"Https.CertificatePassword configured: {!string.IsNullOrEmpty(CertificatePassword)}. " + | ||
| $"{ex.GetType().Name}: {ex.Message} " + | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this hide the actual issue? Should we use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @warwickschroeder In the logs it showed: 2026-09-14 12:27:48.2887|00:00:12.500|14|Error|Microsoft.Extensions.Hosting.Internal.Host|Hosting failed to start|System.Security.Cryptography.CryptographicException: The certificate data cannot be read with the provided password, the password may be incorrect.We should be good but I'll test just to be sure. |
||
| $"Check that the file is a PKCS#12/PFX holding both the certificate and its private key, and that Https.CertificatePassword matches it. " + | ||
| $"To start without HTTPS while investigating, set Https.Enabled to false."; | ||
| logger.LogCritical(message); | ||
| throw new InvalidOperationException(message, ex); | ||
| } | ||
| } | ||
|
|
||
| void LogConfiguration() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another scenario to consider is that the certificate file has no private key. This currently wont throw but just not allow any conections. Could we also check for
!Certificate.HasPrivateKeyand throw if false?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense indeed as we will be using the private key.