-
Notifications
You must be signed in to change notification settings - Fork 801
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Healthcheck checks all hosts if multiple added with AddHost (#1713)
- Loading branch information
1 parent
6a44aac
commit 60d25ab
Showing
19 changed files
with
261 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,93 @@ | ||
using System.Net.Security; | ||
using System.Net.Sockets; | ||
using System.Security.Cryptography.X509Certificates; | ||
#if !NET5_0_OR_GREATER | ||
using HealthChecks.Network.Extensions; | ||
#endif | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
|
||
namespace HealthChecks.Network; | ||
|
||
public class SslHealthCheck : IHealthCheck | ||
{ | ||
private readonly SslHealthCheckOptions _options; | ||
|
||
public SslHealthCheck(SslHealthCheckOptions options) | ||
{ | ||
_options = Guard.ThrowIfNull(options); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
foreach (var (host, port, checkLeftDays) in _options.ConfiguredHosts) | ||
{ | ||
using var tcpClient = new TcpClient(_options.AddressFamily); | ||
#if NET5_0_OR_GREATER | ||
await tcpClient.ConnectAsync(host, port, cancellationToken).ConfigureAwait(false); | ||
#else | ||
await tcpClient.ConnectAsync(host, port).WithCancellationTokenAsync(cancellationToken).ConfigureAwait(false); | ||
#endif | ||
if (!tcpClient.Connected) | ||
return new HealthCheckResult(context.Registration.FailureStatus, description: $"Connection to host {host}:{port} failed"); | ||
|
||
var certificate = await GetSslCertificateAsync(tcpClient, host).ConfigureAwait(false); | ||
|
||
if (certificate is null || !certificate.Verify()) | ||
return new HealthCheckResult(context.Registration.FailureStatus, description: $"Ssl certificate not present or not valid for {host}:{port}"); | ||
|
||
if (certificate.NotAfter.Subtract(DateTime.Now).TotalDays <= checkLeftDays) | ||
return new HealthCheckResult(context.Registration.FailureStatus, description: $"Ssl certificate for {host}:{port} is about to expire in {checkLeftDays} days"); | ||
|
||
return HealthCheckResult.Healthy(); | ||
} | ||
|
||
return HealthCheckResult.Healthy(); | ||
} | ||
catch (Exception ex) | ||
{ | ||
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); | ||
} | ||
} | ||
|
||
private async Task<X509Certificate2?> GetSslCertificateAsync(TcpClient client, string host) | ||
{ | ||
var ssl = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback((sender, cert, ca, sslPolicyErrors) => sslPolicyErrors == SslPolicyErrors.None), null); | ||
|
||
try | ||
{ | ||
await ssl.AuthenticateAsClientAsync(host).ConfigureAwait(false); | ||
var cert = ssl.RemoteCertificate; | ||
return cert == null ? null : new X509Certificate2(cert); | ||
} | ||
catch (Exception) | ||
{ | ||
return null; | ||
} | ||
finally | ||
{ | ||
ssl.Close(); | ||
} | ||
} | ||
} | ||
using System.Net.Security; | ||
using System.Net.Sockets; | ||
using System.Security.Cryptography.X509Certificates; | ||
#if !NET5_0_OR_GREATER | ||
using HealthChecks.Network.Extensions; | ||
#endif | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
|
||
namespace HealthChecks.Network; | ||
|
||
public class SslHealthCheck : IHealthCheck | ||
{ | ||
private readonly SslHealthCheckOptions _options; | ||
|
||
public SslHealthCheck(SslHealthCheckOptions options) | ||
{ | ||
_options = Guard.ThrowIfNull(options); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
List<string>? errorList = null; | ||
foreach (var (host, port, checkLeftDays) in _options.ConfiguredHosts) | ||
{ | ||
using var tcpClient = new TcpClient(_options.AddressFamily); | ||
#if NET5_0_OR_GREATER | ||
await tcpClient.ConnectAsync(host, port, cancellationToken).ConfigureAwait(false); | ||
#else | ||
await tcpClient.ConnectAsync(host, port).WithCancellationTokenAsync(cancellationToken).ConfigureAwait(false); | ||
#endif | ||
if (!tcpClient.Connected) | ||
{ | ||
(errorList ??= new()).Add($"Connection to host {host}:{port} failed"); | ||
if (!_options.CheckAllHosts) | ||
{ | ||
break; | ||
} | ||
continue; | ||
} | ||
|
||
var certificate = await GetSslCertificateAsync(tcpClient, host).ConfigureAwait(false); | ||
|
||
if (certificate is null || !certificate.Verify()) | ||
{ | ||
(errorList ??= new()).Add($"Ssl certificate not present or not valid for {host}:{port}"); | ||
if (!_options.CheckAllHosts) | ||
{ | ||
break; | ||
} | ||
continue; | ||
} | ||
|
||
if (certificate.NotAfter.Subtract(DateTime.Now).TotalDays <= checkLeftDays) | ||
{ | ||
(errorList ??= new()).Add($"Ssl certificate for {host}:{port} is about to expire in {checkLeftDays} days"); | ||
if (!_options.CheckAllHosts) | ||
{ | ||
break; | ||
} | ||
} | ||
} | ||
|
||
return errorList.GetHealthState(context); | ||
} | ||
catch (Exception ex) | ||
{ | ||
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); | ||
} | ||
} | ||
|
||
private async Task<X509Certificate2?> GetSslCertificateAsync(TcpClient client, string host) | ||
{ | ||
var ssl = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback((sender, cert, ca, sslPolicyErrors) => sslPolicyErrors == SslPolicyErrors.None), null); | ||
|
||
try | ||
{ | ||
await ssl.AuthenticateAsClientAsync(host).ConfigureAwait(false); | ||
var cert = ssl.RemoteCertificate; | ||
return cert == null ? null : new X509Certificate2(cert); | ||
} | ||
catch (Exception) | ||
{ | ||
return null; | ||
} | ||
finally | ||
{ | ||
ssl.Close(); | ||
} | ||
} | ||
} |
Oops, something went wrong.