diff options
author | Wouter Tinus <wouter.tinus@gmail.com> | 2020-06-13 21:02:06 +0200 |
---|---|---|
committer | Wouter Tinus <wouter.tinus@gmail.com> | 2020-06-13 21:02:06 +0200 |
commit | 07b22bf0c9252b1dd31baed18b0dbfcab8e9ca5f (patch) | |
tree | affd2bdb435a61059ec295bc361fad11402b9878 /src | |
parent | adc9ee46a8035538e4ac713aa34069697e5bc886 (diff) | |
download | letsencrypt-win-simple-07b22bf0c9252b1dd31baed18b0dbfcab8e9ca5f.zip letsencrypt-win-simple-07b22bf0c9252b1dd31baed18b0dbfcab8e9ca5f.tar.gz letsencrypt-win-simple-07b22bf0c9252b1dd31baed18b0dbfcab8e9ca5f.tar.bz2 |
refactor validation plugisn to remove challenge specific items, allowing them to handle multiple challenges, which is required for parallel validation
Diffstat (limited to 'src')
21 files changed, 149 insertions, 161 deletions
diff --git a/src/main.lib/Clients/EmailClient.cs b/src/main.lib/Clients/EmailClient.cs index 5c98239..4f77115 100644 --- a/src/main.lib/Clients/EmailClient.cs +++ b/src/main.lib/Clients/EmailClient.cs @@ -122,6 +122,7 @@ namespace PKISharp.WACS.Clients bodyBuilder.HtmlBody = content + $"<p>Sent by win-acme version {_version} from {_computerName}</p>"; message.Body = bodyBuilder.ToMessageBody(); await client.SendAsync(message); + await client.DisconnectAsync(true); } } catch (Exception ex) @@ -130,7 +131,7 @@ namespace PKISharp.WACS.Clients } finally { - await client.DisconnectAsync(true); + } } } diff --git a/src/main.lib/Plugins/Interfaces/IValidationPlugin.cs b/src/main.lib/Plugins/Interfaces/IValidationPlugin.cs index ba899db..df5746e 100644 --- a/src/main.lib/Plugins/Interfaces/IValidationPlugin.cs +++ b/src/main.lib/Plugins/Interfaces/IValidationPlugin.cs @@ -1,5 +1,5 @@ using ACMESharp.Authorizations; -using System; +using PKISharp.WACS.DomainObjects; using System.Threading.Tasks; namespace PKISharp.WACS.Plugins.Interfaces @@ -16,11 +16,23 @@ namespace PKISharp.WACS.Plugins.Interfaces /// <param name="target"></param> /// <param name="challenge"></param> /// <returns></returns> - Task PrepareChallenge(IChallengeValidationDetails challengeDetails); + Task PrepareChallenge(ValidationContext context, IChallengeValidationDetails challenge); /// <summary> /// Clean up after validation attempt /// </summary> - Task CleanUp(); + Task CleanUp(ValidationContext context, IChallengeValidationDetails challenge); } + + public class ValidationContext + { + public ValidationContext(string identifier, TargetPart targetPart) + { + Identifier = identifier; + TargetPart = targetPart; + } + public string Identifier { get; set; } + public TargetPart TargetPart { get; set; } + } + } diff --git a/src/main.lib/Plugins/ValidationPlugins/Dns/Acme/Acme.cs b/src/main.lib/Plugins/ValidationPlugins/Dns/Acme/Acme.cs index ddcff59..b24cc38 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Dns/Acme/Acme.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Dns/Acme/Acme.cs @@ -1,5 +1,6 @@ using PKISharp.WACS.Clients; using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System; using System.Threading.Tasks; @@ -11,7 +12,6 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns private readonly IInputService _input; private readonly ProxyService _proxy; private readonly AcmeOptions _options; - private readonly string _identifier; public Acme( LookupClientProvider dnsClient, @@ -19,12 +19,10 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns ISettingsService settings, IInputService input, ProxyService proxy, - AcmeOptions options, - string identifier) : + AcmeOptions options) : base(dnsClient, log, settings) { _options = options; - _identifier = identifier; _input = input; _proxy = proxy; } @@ -34,12 +32,12 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns /// </summary> /// <param name="recordName"></param> /// <param name="token"></param> - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { var client = new AcmeDnsClient(_dnsClient, _proxy, _log, _settings, _input, new Uri(_options.BaseUri)); - return await client.Update(_identifier, token); + return await client.Update(context.Identifier, token); } - public override Task DeleteRecord(string recordName, string token) => Task.CompletedTask; + public override Task DeleteRecord(ValidationContext context, string recordName, string token) => Task.CompletedTask; } } diff --git a/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs b/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs index 87e5273..d88cc47 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs @@ -1,5 +1,7 @@ using ACMESharp.Authorizations;
using PKISharp.WACS.Clients.DNS;
+using PKISharp.WACS.DomainObjects;
+using PKISharp.WACS.Plugins.Interfaces;
using PKISharp.WACS.Services;
using System;
using System.Collections.Generic;
@@ -30,17 +32,17 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins _settings = settings;
}
- public override async Task PrepareChallenge()
+ public override async Task PrepareChallenge(ValidationContext context, Dns01ChallengeValidationDetails challenge)
{
// Check for substitute domains
_authority = await _dnsClient.GetAuthority(
- Challenge.DnsRecordName,
+ challenge.DnsRecordName,
followCnames: _settings.Validation.AllowDnsSubstitution);
var success = false;
while (!success)
{
- success = await CreateRecord(_authority.Domain, Challenge.DnsRecordValue);
+ success = await CreateRecord(context, _authority.Domain, challenge.DnsRecordValue);
if (!success)
{
if (_authority.From == null)
@@ -61,7 +63,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins var retrySeconds = _settings.Validation.PreValidateDnsRetryInterval;
while (_settings.Validation.PreValidateDns)
{
- if (await PreValidate())
+ if (await PreValidate(challenge.DnsRecordValue))
{
break;
}
@@ -82,7 +84,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins }
}
- protected async Task<bool> PreValidate()
+ protected async Task<bool> PreValidate(string expectedValue)
{
try
{
@@ -100,7 +102,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins _log.Warning("Preliminary validation failed: no TXT records found");
return false;
}
- if (!answers.Contains(Challenge.DnsRecordValue))
+ if (!answers.Contains(expectedValue))
{
_log.Debug("Preliminary validation found values: {answers}", answers);
_log.Warning("Preliminary validation failed: incorrect TXT record(s) found");
@@ -121,13 +123,13 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// <summary>
/// Delete record when we're done
/// </summary>
- public override async Task CleanUp()
+ public override async Task CleanUp(ValidationContext context, Dns01ChallengeValidationDetails challenge)
{
- if (HasChallenge && _authority != null)
+ if (_authority != null)
{
try
{
- await DeleteRecord(_authority.Domain, Challenge.DnsRecordValue);
+ await DeleteRecord(context, _authority.Domain, challenge.DnsRecordValue);
}
catch (Exception ex)
{
@@ -140,14 +142,14 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// Delete validation record
/// </summary>
/// <param name="recordName">Name of the record</param>
- public abstract Task DeleteRecord(string recordName, string token);
+ public abstract Task DeleteRecord(ValidationContext context, string recordName, string token);
/// <summary>
/// Create validation record
/// </summary>
/// <param name="recordName">Name of the record</param>
/// <param name="token">Contents of the record</param>
- public abstract Task<bool> CreateRecord(string recordName, string token);
+ public abstract Task<bool> CreateRecord(ValidationContext context, string recordName, string token);
/// <summary>
/// Match DNS zone to use from a list of all zones
diff --git a/src/main.lib/Plugins/ValidationPlugins/Dns/Manual/Manual.cs b/src/main.lib/Plugins/ValidationPlugins/Dns/Manual/Manual.cs index c584efb..b65ea07 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Dns/Manual/Manual.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Dns/Manual/Manual.cs @@ -1,4 +1,5 @@ using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System.Threading.Tasks; @@ -7,28 +8,24 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns internal class Manual : DnsValidation<Manual> { private readonly IInputService _input; - private readonly string _identifier; public Manual( LookupClientProvider dnsClient, ILogService log, IInputService input, - ISettingsService settings, - string identifier) - : base(dnsClient, log, settings) + ISettingsService settings) : base(dnsClient, log, settings) { // Usually it's a big no-no to rely on user input in validation plugin // because this should be able to run unattended. This plugin is for testing // only and therefor we will allow it. Future versions might be more advanced, // e.g. shoot an email to an admin and complete the order later. _input = input; - _identifier = identifier; } - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { _input.CreateSpace(); - _input.Show("Domain", _identifier); + _input.Show("Domain", context.Identifier); _input.Show("Record", recordName); _input.Show("Type", "TXT"); _input.Show("Content", $"\"{token}\""); @@ -42,7 +39,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns // Pre-pre-validate, allowing the manual user to correct mistakes while (true) { - if (await PreValidate()) + if (await PreValidate(token)) { return true; } @@ -61,10 +58,10 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns } } - public override Task DeleteRecord(string recordName, string token) + public override Task DeleteRecord(ValidationContext context, string recordName, string token) { _input.CreateSpace(); - _input.Show("Domain", _identifier); + _input.Show("Domain", context.Identifier); _input.Show("Record", recordName); _input.Show("Type", "TXT"); _input.Show("Content", $"\"{token}\""); diff --git a/src/main.lib/Plugins/ValidationPlugins/Dns/Script/Script.cs b/src/main.lib/Plugins/ValidationPlugins/Dns/Script/Script.cs index 80ece5e..0f2c6f4 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Dns/Script/Script.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Dns/Script/Script.cs @@ -1,5 +1,6 @@ using PKISharp.WACS.Clients; using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System.Threading.Tasks; @@ -9,7 +10,6 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns { private readonly ScriptClient _scriptClient; private readonly ScriptOptions _options; - private readonly string _identifier; internal const string DefaultCreateArguments = "create {Identifier} {RecordName} {Token}"; internal const string DefaultDeleteArguments = "delete {Identifier} {RecordName} {Token}"; @@ -19,16 +19,14 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns LookupClientProvider dnsClient, ScriptClient client, ILogService log, - ISettingsService settings, - string identifier) : + ISettingsService settings) : base(dnsClient, log, settings) { - _identifier = identifier; _options = options; _scriptClient = client; } - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { var script = _options.Script ?? _options.CreateScript; if (!string.IsNullOrWhiteSpace(script)) @@ -38,7 +36,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns { args = _options.CreateScriptArguments; } - await _scriptClient.RunScript(script, ProcessArguments(recordName, token, args, script.EndsWith(".ps1"))); + await _scriptClient.RunScript(script, ProcessArguments(context.Identifier, recordName, token, args, script.EndsWith(".ps1"))); return true; } else @@ -48,7 +46,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns } } - public override async Task DeleteRecord(string recordName, string token) + public override async Task DeleteRecord(ValidationContext context, string recordName, string token) { var script = _options.Script ?? _options.DeleteScript; if (!string.IsNullOrWhiteSpace(script)) @@ -58,7 +56,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns { args = _options.DeleteScriptArguments; } - await _scriptClient.RunScript(script, ProcessArguments(recordName, token, args, script.EndsWith(".ps1"))); + await _scriptClient.RunScript(script, ProcessArguments(context.Identifier, recordName, token, args, script.EndsWith(".ps1"))); } else { @@ -66,10 +64,10 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns } } - private string ProcessArguments(string recordName, string token, string args, bool escapeToken) + private string ProcessArguments(string identifier, string recordName, string token, string args, bool escapeToken) { var ret = args; - ret = ret.Replace("{Identifier}", _identifier); + ret = ret.Replace("{Identifier}", identifier); ret = ret.Replace("{RecordName}", recordName); // Some tokens start with - which confuses Powershell. We did not want to // make a breaking change for .bat or .exe files, so instead escape the diff --git a/src/main.lib/Plugins/ValidationPlugins/Http/FileSystem/FileSystem.cs b/src/main.lib/Plugins/ValidationPlugins/Http/FileSystem/FileSystem.cs index f8d5b0b..ff23950 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Http/FileSystem/FileSystem.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Http/FileSystem/FileSystem.cs @@ -1,4 +1,5 @@ using PKISharp.WACS.Clients.IIS; +using PKISharp.WACS.DomainObjects; using System; using System.IO; using System.Linq; @@ -56,12 +57,12 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Http /// Update webroot /// </summary> /// <param name="scheduled"></param> - protected override void Refresh() + protected override void Refresh(TargetPart targetPart) { if (string.IsNullOrEmpty(_options.Path)) { // Update web root path - var siteId = _options.SiteId ?? _targetPart.SiteId; + var siteId = _options.SiteId ?? targetPart.SiteId; if (siteId > 0) { _path = _iisClient.GetWebSite(siteId.Value).Path; diff --git a/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidation.cs b/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidation.cs index bc811fe..844b92e 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidation.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidation.cs @@ -42,13 +42,6 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins private readonly ProxyService _proxy; /// <summary> - /// Current TargetPart that we are working on. A TargetPart is mainly used by - /// the IISSites TargetPlugin to indicate that we are working with different - /// IIS sites - /// </summary> - protected TargetPart _targetPart; - - /// <summary> /// Where to find the template for the web.config that's copied to the webroot /// </summary> protected string TemplateWebConfig => Path.Combine(Path.GetDirectoryName(_settings.ExePath), "web_config.xml"); @@ -79,25 +72,24 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins _proxy = pars.ProxyService; _settings = pars.Settings; _renewal = pars.Renewal; - _targetPart = pars.TargetPart; } /// <summary> /// Handle http challenge /// </summary> - public async override Task PrepareChallenge() + public async override Task PrepareChallenge(ValidationContext context, Http01ChallengeValidationDetails challenge) { - Refresh(); - WriteAuthorizationFile(); - WriteWebConfig(); - _log.Information("Answer should now be browsable at {answerUri}", Challenge.HttpResourceUrl); + Refresh(context.TargetPart); + WriteAuthorizationFile(challenge); + WriteWebConfig(challenge); + _log.Information("Answer should now be browsable at {answerUri}", challenge.HttpResourceUrl); if (_runLevel.HasFlag(RunLevel.Test) && _renewal.New) { if (await _input.PromptYesNo("[--test] Try in default browser?", false)) { Process.Start(new ProcessStartInfo { - FileName = Challenge.HttpResourceUrl, + FileName = challenge.HttpResourceUrl, UseShellExecute = true }); await _input.Wait(); @@ -107,8 +99,8 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins string? foundValue = null; try { - var value = await WarmupSite(); - if (Equals(value, Challenge.HttpResourceValue)) + var value = await WarmupSite(challenge); + if (Equals(value, challenge.HttpResourceValue)) { _log.Information("Preliminary validation looks good, but the ACME server will be more thorough"); } @@ -116,7 +108,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins { _log.Warning("Preliminary validation failed, the server answered '{value}' instead of '{expected}'. The ACME server might have a different perspective", foundValue ?? "(null)", - Challenge.HttpResourceValue); + challenge.HttpResourceValue); } } catch (HttpRequestException hrex) @@ -135,10 +127,10 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// Mostly relevant to classic FileSystem validation /// </summary> /// <param name="uri"></param> - private async Task<string> WarmupSite() + private async Task<string> WarmupSite(Http01ChallengeValidationDetails challenge) { using var client = _proxy.GetHttpClient(false); - var response = await client.GetAsync(Challenge.HttpResourceUrl); + var response = await client.GetAsync(challenge.HttpResourceUrl); return await response.Content.ReadAsStringAsync(); } @@ -147,13 +139,13 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// </summary> /// <param name="answerPath">where the answerFile should be located</param> /// <param name="fileContents">the contents of the file to write</param> - private void WriteAuthorizationFile() + private void WriteAuthorizationFile(Http01ChallengeValidationDetails challenge) { if (_path == null) { throw new InvalidOperationException(); } - WriteFile(CombinePath(_path, Challenge.HttpResourcePath), Challenge.HttpResourceValue); + WriteFile(CombinePath(_path, challenge.HttpResourcePath), challenge.HttpResourceValue); _challengeWritten = true; } @@ -163,7 +155,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// <param name="target"></param> /// <param name="answerPath"></param> /// <param name="token"></param> - private void WriteWebConfig() + private void WriteWebConfig(Http01ChallengeValidationDetails challenge) { if (_path == null) { @@ -174,8 +166,8 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins try { _log.Debug("Writing web.config"); - var partialPath = Challenge.HttpResourcePath.Split('/').Last(); - var destination = CombinePath(_path, Challenge.HttpResourcePath.Replace(partialPath, "web.config")); + var partialPath = challenge.HttpResourcePath.Split('/').Last(); + var destination = CombinePath(_path, challenge.HttpResourcePath.Replace(partialPath, "web.config")); var content = GetWebConfig(); WriteFile(destination, content); _webConfigWritten = true; @@ -199,7 +191,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// <param name="target"></param> /// <param name="answerPath"></param> /// <param name="token"></param> - private void DeleteWebConfig() + private void DeleteWebConfig(Http01ChallengeValidationDetails challenge) { if (_path == null) { @@ -208,8 +200,8 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins if (_webConfigWritten) { _log.Debug("Deleting web.config"); - var partialPath = Challenge.HttpResourcePath.Split('/').Last(); - var destination = CombinePath(_path, Challenge.HttpResourcePath.Replace(partialPath, "web.config")); + var partialPath = challenge.HttpResourcePath.Split('/').Last(); + var destination = CombinePath(_path, challenge.HttpResourcePath.Replace(partialPath, "web.config")); DeleteFile(destination); } } @@ -221,15 +213,15 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// <param name="token">the token</param> /// <param name="webRootPath">the website root path</param> /// <param name="filePath">the file path for the authorization file</param> - private void DeleteAuthorization() + private void DeleteAuthorization(Http01ChallengeValidationDetails challenge) { try { if (_path != null && _challengeWritten) { _log.Debug("Deleting answer"); - var path = CombinePath(_path, Challenge.HttpResourcePath); - var partialPath = Challenge.HttpResourcePath.Split('/').Last(); + var path = CombinePath(_path, challenge.HttpResourcePath); + var partialPath = challenge.HttpResourcePath.Split('/').Last(); DeleteFile(path); if (_settings.Validation.CleanupFolders) { @@ -319,15 +311,15 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// </summary> /// <param name="scheduled"></param> /// <returns></returns> - protected virtual void Refresh() { } + protected virtual void Refresh(TargetPart targetPart) { } /// <summary> /// Dispose /// </summary> - public override Task CleanUp() + public override Task CleanUp(ValidationContext context, Http01ChallengeValidationDetails challenge) { - DeleteWebConfig(); - DeleteAuthorization(); + DeleteWebConfig(challenge); + DeleteAuthorization(challenge); return Task.CompletedTask; } } diff --git a/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidationParameters.cs b/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidationParameters.cs index b1958b4..7092cdd 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidationParameters.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Http/HttpValidationParameters.cs @@ -7,9 +7,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins { public ISettingsService Settings { get; private set; } public Renewal Renewal { get; private set; } - public TargetPart TargetPart { get; private set; } public RunLevel RunLevel { get; private set; } - public string Identifier { get; private set; } public ILogService LogService { get; private set; } public IInputService InputService { get; private set; } public ProxyService ProxyService { get; private set; } @@ -20,14 +18,10 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins ISettingsService settings, ProxyService proxy, Renewal renewal, - TargetPart target, - RunLevel runLevel, - string identifier) + RunLevel runLevel) { Renewal = renewal; - TargetPart = target; RunLevel = runLevel; - Identifier = identifier; Settings = settings; ProxyService = proxy; LogService = log; diff --git a/src/main.lib/Plugins/ValidationPlugins/Http/SelfHosting/SelfHosting.cs b/src/main.lib/Plugins/ValidationPlugins/Http/SelfHosting/SelfHosting.cs index 6e165af..ad3d3be 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Http/SelfHosting/SelfHosting.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Http/SelfHosting/SelfHosting.cs @@ -1,4 +1,5 @@ using ACMESharp.Authorizations; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System; using System.Collections.Generic; @@ -61,7 +62,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Http } } - public override Task CleanUp() + public override Task CleanUp(ValidationContext context, Http01ChallengeValidationDetails challenge) { if (HasListener) { @@ -77,9 +78,9 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Http return Task.CompletedTask; } - public override Task PrepareChallenge() + public override Task PrepareChallenge(ValidationContext context, Http01ChallengeValidationDetails challenge) { - _files.Add("/" + Challenge.HttpResourcePath, Challenge.HttpResourceValue); + _files.Add("/" + challenge.HttpResourcePath, challenge.HttpResourceValue); var protocol = _options.Https == true ? "https" : "http"; var port = _options.Port ?? (_options.Https == true ? DefaultHttpsValidationPort : diff --git a/src/main.lib/Plugins/ValidationPlugins/Http/WebDav/WebDav.cs b/src/main.lib/Plugins/ValidationPlugins/Http/WebDav/WebDav.cs index 990f338..9afd9d1 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Http/WebDav/WebDav.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Http/WebDav/WebDav.cs @@ -1,4 +1,6 @@ -using PKISharp.WACS.Client; +using ACMESharp.Authorizations; +using PKISharp.WACS.Client; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System.Threading.Tasks; @@ -22,9 +24,9 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Http protected override char PathSeparator => '/'; protected override void WriteFile(string path, string content) => _webdavClient.Upload(path, content); - public override Task CleanUp() + public override Task CleanUp(ValidationContext context, Http01ChallengeValidationDetails challenge) { - base.CleanUp(); + base.CleanUp(context, challenge); _webdavClient.Dispose(); return Task.CompletedTask; } diff --git a/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs b/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs index d43d58b..baefd16 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs @@ -1,5 +1,7 @@ using ACMESharp.Authorizations; using Org.BouncyCastle.Asn1; +using PKISharp.WACS.DomainObjects; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System; using System.Collections.Generic; @@ -20,7 +22,6 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls internal const int DefaultValidationPort = 443; private TcpListener? _listener; private X509Certificate2? _certificate; - private readonly string _identifier; private readonly SelfHostingOptions _options; private readonly ILogService _log; private readonly IUserRoleService _userRoleService; @@ -39,9 +40,8 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls set => _listener = value; } - public SelfHosting(ILogService log, string identifier, SelfHostingOptions options, IUserRoleService userRoleService) + public SelfHosting(ILogService log, SelfHostingOptions options, IUserRoleService userRoleService) { - _identifier = identifier; _log = log; _options = options; _userRoleService = userRoleService; @@ -69,7 +69,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls } } - public override Task CleanUp() + public override Task CleanUp(ValidationContext context, TlsAlpn01ChallengeValidationDetails challenge) { try { @@ -82,12 +82,12 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls return Task.CompletedTask; } - public override Task PrepareChallenge() + public override Task PrepareChallenge(ValidationContext context, TlsAlpn01ChallengeValidationDetails challenge) { try { using var rsa = RSA.Create(2048); - var name = new X500DistinguishedName($"CN={_identifier}"); + var name = new X500DistinguishedName($"CN={context.Identifier}"); var request = new CertificateRequest( name, @@ -96,7 +96,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls RSASignaturePadding.Pkcs1); using var sha = SHA256.Create(); - var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(Challenge.TokenValue)); + var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(challenge.TokenValue)); request.CertificateExtensions.Add( new X509Extension( new AsnEncodedData("1.3.6.1.5.5.7.1.31", @@ -104,7 +104,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls true)); var sanBuilder = new SubjectAlternativeNameBuilder(); - sanBuilder.AddDnsName(_identifier); + sanBuilder.AddDnsName(context.Identifier); request.CertificateExtensions.Add(sanBuilder.Build()); _certificate = request.CreateSelfSigned( @@ -112,8 +112,8 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls new DateTimeOffset(DateTime.UtcNow.AddDays(1))); _certificate = new X509Certificate2( - _certificate.Export(X509ContentType.Pfx, _identifier), - _identifier, + _certificate.Export(X509ContentType.Pfx, context.Identifier), + context.Identifier, X509KeyStorageFlags.MachineKeySet); _listener = new TcpListener(IPAddress.Any, _options.Port ?? DefaultValidationPort); diff --git a/src/main.lib/Plugins/ValidationPlugins/Validation.cs b/src/main.lib/Plugins/ValidationPlugins/Validation.cs index cb9da68..10906eb 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Validation.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Validation.cs @@ -10,31 +10,15 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// </summary> public abstract class Validation<TChallenge> : IValidationPlugin where TChallenge : class, IChallengeValidationDetails { - public bool HasChallenge => _challenge != null; - public TChallenge Challenge - { - get - { - if (_challenge == null) - { - throw new InvalidOperationException(); - } - return _challenge; - } - } - private TChallenge? _challenge; - - /// <summary> /// Handle the challenge /// </summary> /// <param name="challenge"></param> - public async Task PrepareChallenge(IChallengeValidationDetails challenge) + public async Task PrepareChallenge(ValidationContext context, IChallengeValidationDetails challenge) { if (challenge is TChallenge typed) { - _challenge = typed; - await PrepareChallenge(); + await PrepareChallenge(context, typed); } else { @@ -46,12 +30,24 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins /// Handle the challenge /// </summary> /// <param name="challenge"></param> - public abstract Task PrepareChallenge(); + public abstract Task PrepareChallenge(ValidationContext context, TChallenge typed); /// <summary> /// Clean up after validation /// </summary> - public abstract Task CleanUp(); + public async Task CleanUp(ValidationContext context, IChallengeValidationDetails challenge) + { + if (challenge is TChallenge typed) + { + await CleanUp(context, typed); + } + else + { + throw new InvalidOperationException("Unexpected challenge type"); + } + } + + public abstract Task CleanUp(ValidationContext context, TChallenge typed); /// <summary> /// Is the plugin currently disabled diff --git a/src/main.lib/RenewalExecutor.cs b/src/main.lib/RenewalExecutor.cs index aa38dca..68b4ff7 100644 --- a/src/main.lib/RenewalExecutor.cs +++ b/src/main.lib/RenewalExecutor.cs @@ -1,4 +1,5 @@ -using Autofac; +using ACMESharp.Authorizations; +using Autofac; using PKISharp.WACS.Clients.Acme; using PKISharp.WACS.Configuration; using PKISharp.WACS.DomainObjects; @@ -407,8 +408,10 @@ namespace PKISharp.WACS var client = context.Scope.Resolve<AcmeClient>(); var identifier = authorization.Identifier.Value; var options = context.Renewal.ValidationPluginOptions; + IChallengeValidationDetails? challengeDetails = null; IValidationPlugin? validationPlugin = null; - using var validation = _scopeBuilder.Validation(context.Scope, options, targetPart, identifier); + ValidationContext? validationContext = null; + using var validation = _scopeBuilder.Validation(context.Scope, options); try { if (authorization.Status == AcmeClient.AuthorizationValid) @@ -495,8 +498,9 @@ namespace PKISharp.WACS options.Name); try { - var details = await client.DecodeChallengeValidation(authorization, challenge); - await validationPlugin.PrepareChallenge(details); + var challengeValidationDetails = await client.DecodeChallengeValidation(authorization, challenge); + validationContext = new ValidationContext(identifier, targetPart); + await validationPlugin.PrepareChallenge(validationContext, challengeValidationDetails); } catch (Exception ex) { @@ -531,12 +535,14 @@ namespace PKISharp.WACS } finally { - if (validationPlugin != null) + if (validationPlugin != null && + challengeDetails != null && + validationContext != null) { try { _log.Verbose("Starting post-validation cleanup"); - await validationPlugin.CleanUp(); + await validationPlugin.CleanUp(validationContext, challengeDetails); _log.Verbose("Post-validation cleanup was succesful"); } catch (Exception ex) diff --git a/src/main.lib/Services/AutofacBuilder.cs b/src/main.lib/Services/AutofacBuilder.cs index 5a5e4f9..00080fd 100644 --- a/src/main.lib/Services/AutofacBuilder.cs +++ b/src/main.lib/Services/AutofacBuilder.cs @@ -202,19 +202,12 @@ namespace PKISharp.WACS.Services /// <param name="target"></param> /// <param name="identifier"></param> /// <returns></returns> - public ILifetimeScope Validation(ILifetimeScope execution, ValidationPluginOptions options, TargetPart target, string identifier) + public ILifetimeScope Validation(ILifetimeScope execution, ValidationPluginOptions options) { return execution.BeginLifetimeScope(builder => { - builder.RegisterType<HttpValidationParameters>(). - WithParameters(new[] { - new TypedParameter(typeof(string), identifier), - new TypedParameter(typeof(TargetPart), target) - }); + builder.RegisterType<HttpValidationParameters>(); builder.RegisterType(options.Instance). - WithParameters(new[] { - new TypedParameter(typeof(string), identifier), - }). As<IValidationPlugin>(). SingleInstance(); }); diff --git a/src/main.lib/Services/Interfaces/IAutofacBuilder.cs b/src/main.lib/Services/Interfaces/IAutofacBuilder.cs index c2a3893..eb11a39 100644 --- a/src/main.lib/Services/Interfaces/IAutofacBuilder.cs +++ b/src/main.lib/Services/Interfaces/IAutofacBuilder.cs @@ -50,6 +50,6 @@ namespace PKISharp.WACS.Services /// <param name="target"></param> /// <param name="identifier"></param> /// <returns></returns> - ILifetimeScope Validation(ILifetimeScope execution, ValidationPluginOptions options, TargetPart target, string identifier); + ILifetimeScope Validation(ILifetimeScope execution, ValidationPluginOptions options); } } diff --git a/src/plugin.validation.dns.azure/Azure.cs b/src/plugin.validation.dns.azure/Azure.cs index 2d8c1e0..f3bffd3 100755 --- a/src/plugin.validation.dns.azure/Azure.cs +++ b/src/plugin.validation.dns.azure/Azure.cs @@ -4,6 +4,7 @@ using Microsoft.Azure.Services.AppAuthentication; using Microsoft.Rest; using Microsoft.Rest.Azure.Authentication; using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System; using System.Collections.Generic; @@ -16,24 +17,20 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns internal class Azure : DnsValidation<Azure> { private DnsManagementClient _azureDnsClient; - private readonly DomainParseService _domainParser; private readonly ProxyService _proxyService; - private readonly AzureOptions _options; + public Azure(AzureOptions options, - DomainParseService domainParser, LookupClientProvider dnsClient, ProxyService proxyService, ILogService log, - ISettingsService settings) - : base(dnsClient, log, settings) + ISettingsService settings) : base(dnsClient, log, settings) { _options = options; - _domainParser = domainParser; _proxyService = proxyService; } - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { var client = await GetClient(); var zone = await GetHostedZone(recordName); @@ -128,7 +125,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns return null; } - public override async Task DeleteRecord(string recordName, string token) + public override async Task DeleteRecord(ValidationContext context, string recordName, string token) { var client = await GetClient(); var zone = await GetHostedZone(recordName); diff --git a/src/plugin.validation.dns.cloudflare/Cloudflare.cs b/src/plugin.validation.dns.cloudflare/Cloudflare.cs index e770735..fb6a69a 100644 --- a/src/plugin.validation.dns.cloudflare/Cloudflare.cs +++ b/src/plugin.validation.dns.cloudflare/Cloudflare.cs @@ -3,6 +3,7 @@ using FluentCloudflare.Api; using FluentCloudflare.Api.Entities; using FluentCloudflare.Extensions; using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System; using System.Linq; @@ -23,8 +24,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns ProxyService proxyService, LookupClientProvider dnsClient, ILogService log, - ISettingsService settings) - : base(dnsClient, log, settings) + ISettingsService settings) : base(dnsClient, log, settings) { _options = options; _hc = proxyService.GetHttpClient(); @@ -54,7 +54,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns return zonesResp.Unpack().First(); } - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { var ctx = GetContext(); var zone = await GetHostedZone(ctx, recordName).ConfigureAwait(false); @@ -105,7 +105,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns } - public override async Task DeleteRecord(string recordName, string token) + public override async Task DeleteRecord(ValidationContext context, string recordName, string token) { var ctx = GetContext(); var zone = await GetHostedZone(ctx, recordName).ConfigureAwait(false); diff --git a/src/plugin.validation.dns.dreamhost/DreamhostDnsValidation.cs b/src/plugin.validation.dns.dreamhost/DreamhostDnsValidation.cs index 2e6e5db..4d5f0b2 100644 --- a/src/plugin.validation.dns.dreamhost/DreamhostDnsValidation.cs +++ b/src/plugin.validation.dns.dreamhost/DreamhostDnsValidation.cs @@ -1,4 +1,5 @@ using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Plugins.ValidationPlugins.Dreamhost; using PKISharp.WACS.Services; using System; @@ -18,7 +19,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins : base(dnsClient, logService, settings) => _client = new DnsManagementClient(options.ApiKey.Value, logService); - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { try { @@ -31,7 +32,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins } } - public override async Task DeleteRecord(string recordName, string token) + public override async Task DeleteRecord(ValidationContext context, string recordName, string token) { try { diff --git a/src/plugin.validation.dns.luadns/luadns.cs b/src/plugin.validation.dns.luadns/luadns.cs index 4935681..c03fbd7 100644 --- a/src/plugin.validation.dns.luadns/luadns.cs +++ b/src/plugin.validation.dns.luadns/luadns.cs @@ -1,4 +1,5 @@ using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Services; using System; using System.Collections.Generic; @@ -64,7 +65,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns _apiKey = options.APIKey.Value; } - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { _log.Information("Creating LuaDNS verification record"); @@ -101,7 +102,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns return true; } - public override async Task DeleteRecord(string recordName, string token) + public override async Task DeleteRecord(ValidationContext context, string recordName, string token) { if (!_recordsMap.ContainsKey(recordName)) { diff --git a/src/plugin.validation.dns.route53/Route53.cs b/src/plugin.validation.dns.route53/Route53.cs index b8bdeaf..ea0c2a2 100644 --- a/src/plugin.validation.dns.route53/Route53.cs +++ b/src/plugin.validation.dns.route53/Route53.cs @@ -8,23 +8,20 @@ using PKISharp.WACS.Services; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using System.Data.Common; +using PKISharp.WACS.Plugins.Interfaces; namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns { internal sealed class Route53 : DnsValidation<Route53> { private readonly IAmazonRoute53 _route53Client; - private readonly DomainParseService _domainParser; public Route53( LookupClientProvider dnsClient, - DomainParseService domainParser, ILogService log, ProxyService proxy, ISettingsService settings, - Route53Options options) - : base(dnsClient, log, settings) + Route53Options options) : base(dnsClient, log, settings) { var region = RegionEndpoint.USEast1; var config = new AmazonRoute53Config() { RegionEndpoint = region }; @@ -34,7 +31,6 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns : !string.IsNullOrWhiteSpace(options.AccessKeyId) && !string.IsNullOrWhiteSpace(options.SecretAccessKey.Value) ? new AmazonRoute53Client(options.AccessKeyId, options.SecretAccessKey.Value, config) : new AmazonRoute53Client(config); - _domainParser = domainParser; } private static ResourceRecordSet CreateResourceRecordSet(string name, string value) @@ -49,7 +45,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns }; } - public override async Task<bool> CreateRecord(string recordName, string token) + public override async Task<bool> CreateRecord(ValidationContext context, string recordName, string token) { try { @@ -80,7 +76,7 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns } } - public override async Task DeleteRecord(string recordName, string token) + public override async Task DeleteRecord(ValidationContext context, string recordName, string token) { var hostedZoneIds = await GetHostedZoneIds(recordName); _log.Information($"Deleting TXT record {recordName} with value {token}"); |