diff options
author | Wouter Tinus <win.acme.simple@gmail.com> | 2020-04-28 19:42:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-28 19:42:31 +0200 |
commit | ec86a9425274a60af6d9d3c55ab4ed2f34be46fb (patch) | |
tree | b92ab9525522b1151dc2560dc12c6db8b74c3204 /src | |
parent | 85d69f9ddb45671fc0082676ea40b0a9c7f0a4f2 (diff) | |
parent | 2de989799eaf2659838783e0b792511f5ccce532 (diff) | |
download | letsencrypt-win-simple-ec86a9425274a60af6d9d3c55ab4ed2f34be46fb.zip letsencrypt-win-simple-ec86a9425274a60af6d9d3c55ab4ed2f34be46fb.tar.gz letsencrypt-win-simple-ec86a9425274a60af6d9d3c55ab4ed2f34be46fb.tar.bz2 |
Merge branch '2.1.7' into azure-environment-agnostic
Diffstat (limited to 'src')
13 files changed, 284 insertions, 30 deletions
diff --git a/src/main.lib/Clients/IIS/IISSiteWrapper.cs b/src/main.lib/Clients/IIS/IISSiteWrapper.cs index 9bc204b..c0b96f7 100644 --- a/src/main.lib/Clients/IIS/IISSiteWrapper.cs +++ b/src/main.lib/Clients/IIS/IISSiteWrapper.cs @@ -50,7 +50,22 @@ namespace PKISharp.WACS.Clients.IIS } } } - public byte[] CertificateHash => Binding.CertificateHash; + + public byte[]? CertificateHash + { + get + { + try + { + return Binding.CertificateHash; + } + catch + { + return null; + } + } + } + public string CertificateStoreName => Binding.CertificateStoreName; public string BindingInformation => Binding.NormalizedBindingInformation(); public SSLFlags SSLFlags => Binding.SSLFlags(); diff --git a/src/main.lib/Plugins/InstallationPlugins/IISWeb/IISWebOptionsFactory.cs b/src/main.lib/Plugins/InstallationPlugins/IISWeb/IISWebOptionsFactory.cs index 5ac813b..4bb9431 100644 --- a/src/main.lib/Plugins/InstallationPlugins/IISWeb/IISWebOptionsFactory.cs +++ b/src/main.lib/Plugins/InstallationPlugins/IISWeb/IISWebOptionsFactory.cs @@ -32,14 +32,9 @@ namespace PKISharp.WACS.Plugins.InstallationPlugins var ask = true; if (target.IIS) { - if (runLevel.HasFlag(RunLevel.Advanced)) - { - ask = await inputService.PromptYesNo("Use different site for installation?", false); - } - else - { - ask = false; - } + ask = runLevel.HasFlag(RunLevel.Advanced) ? + await inputService.PromptYesNo("Use different site for installation?", false) : + false; } if (ask) { diff --git a/src/main.lib/Plugins/StorePlugins/CentralSsl/CentralSsl.cs b/src/main.lib/Plugins/StorePlugins/CentralSsl/CentralSsl.cs index fdd8782..45b0282 100644 --- a/src/main.lib/Plugins/StorePlugins/CentralSsl/CentralSsl.cs +++ b/src/main.lib/Plugins/StorePlugins/CentralSsl/CentralSsl.cs @@ -51,8 +51,10 @@ namespace PKISharp.WACS.Plugins.StorePlugins _log.Information("Saving certificate to Central SSL location {dest}", dest); try { - var collection = new X509Certificate2Collection(); - collection.Add(input.Certificate); + var collection = new X509Certificate2Collection + { + input.Certificate + }; collection.AddRange(input.Chain.ToArray()); File.WriteAllBytes(dest, collection.Export(X509ContentType.Pfx, _password)); } diff --git a/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs b/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs index 4470890..d43d58b 100644 --- a/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs +++ b/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs @@ -26,7 +26,6 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins.Tls private readonly IUserRoleService _userRoleService; private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); - private bool HasListener => _listener != null; private TcpListener Listener { get diff --git a/src/main.lib/RenewalManager.cs b/src/main.lib/RenewalManager.cs index 424e1fe..66de9cd 100644 --- a/src/main.lib/RenewalManager.cs +++ b/src/main.lib/RenewalManager.cs @@ -359,17 +359,6 @@ namespace PKISharp.WACS return await chosen.Invoke(); } - /// <summary> - /// Filter specific renewals by list index - /// </summary> - /// <param name="current"></param> - /// <returns></returns> - private async Task<IEnumerable<Renewal>> FilterRenewalsById(IEnumerable<Renewal> current) - { - var rawInput = await _input.RequestString("Please input the list index of the renewal(s) you'd like to select"); - return await FilterRenewalsById(current, rawInput); - } - private async Task<IEnumerable<Renewal>> FilterRenewalsById(IEnumerable<Renewal> current, string input) { var parts = input.ParseCsv(); diff --git a/src/plugin.validation.dns.azure/AzureArgumentsProvider.cs b/src/plugin.validation.dns.azure/AzureArgumentsProvider.cs index f27f37a..7716fd2 100755 --- a/src/plugin.validation.dns.azure/AzureArgumentsProvider.cs +++ b/src/plugin.validation.dns.azure/AzureArgumentsProvider.cs @@ -10,25 +10,25 @@ namespace PKISharp.WACS.Plugins.ValidationPlugins public override string Condition => "--validationmode dns-01 --validation azure"; public override void Configure(FluentCommandLineParser<AzureArguments> parser) { - parser.Setup(o => o.AzureEnvironment) + _ = parser.Setup(o => o.AzureEnvironment) .As("azureenvironment") .WithDescription("Public Azure service instance endpoint"); - parser.Setup(o => o.AzureUseMsi) + _ = parser.Setup(o => o.AzureUseMsi) .As("azureusemsi") .WithDescription("Use Managed Service Identity for authentication."); - parser.Setup(o => o.AzureTenantId) + _ = parser.Setup(o => o.AzureTenantId) .As("azuretenantid") .WithDescription("Tenant ID to login into Microsoft Azure."); - parser.Setup(o => o.AzureClientId) + _ = parser.Setup(o => o.AzureClientId) .As("azureclientid") .WithDescription("Client ID to login into Microsoft Azure."); - parser.Setup(o => o.AzureSecret) + _ = parser.Setup(o => o.AzureSecret) .As("azuresecret") .WithDescription("Secret to login into Microsoft Azure."); - parser.Setup(o => o.AzureSubscriptionId) + _ = parser.Setup(o => o.AzureSubscriptionId) .As("azuresubscriptionid") .WithDescription("Subscription ID to login into Microsoft Azure DNS."); - parser.Setup(o => o.AzureResourceGroupName) + _ = parser.Setup(o => o.AzureResourceGroupName) .As("azureresourcegroupname") .WithDescription("The name of the resource group within Microsoft Azure DNS."); } diff --git a/src/plugin.validation.dns.luadns/luadns.cs b/src/plugin.validation.dns.luadns/luadns.cs new file mode 100644 index 0000000..2cd561c --- /dev/null +++ b/src/plugin.validation.dns.luadns/luadns.cs @@ -0,0 +1,145 @@ +using PKISharp.WACS.Clients.DNS; +using PKISharp.WACS.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns +{ + internal sealed class LUADNS : DnsValidation<LUADNS> + { + private class ZoneData + { + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + } + + private class RecordData + { + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("zone_id")] + public int ZoneId { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("content")] + public string Content { get; set; } + + [JsonPropertyName("ttl")] + public int TTL { get; set; } + } + + private static readonly Uri _luaDNSApiEndpoint = new Uri("https://api.luadns.com/v1/", UriKind.Absolute); + private static readonly Dictionary<string, RecordData> _recordsMap = new Dictionary<string, RecordData>(); + + private readonly DomainParseService _domainParser; + private readonly ProxyService _proxyService; + + private readonly string _userName; + private readonly string _apiKey; + + public LUADNS( + LookupClientProvider dnsClient, + DomainParseService domainParser, + ProxyService proxy, + ILogService log, + ISettingsService settings, + LUADNSOptions options) + : base(dnsClient, log, settings) + { + _domainParser = domainParser; + _proxyService = proxy; + + _userName = options.Username; + _apiKey = options.APIKey.Value; + } + + public override async Task CreateRecord(string recordName, string token) + { + _log.Information("Creating LUADNS verification record"); + + using (var client = GetClient()) + { + var response = await client.GetAsync(new Uri(_luaDNSApiEndpoint, "zones")); + if (!response.IsSuccessStatusCode) + { + _log.Information("Failed to get DNS zones list for account. Aborting."); + return; + } + + var payload = await response.Content.ReadAsStringAsync(); + var zones = JsonSerializer.Deserialize<ZoneData[]>(payload); + var targetZone = zones.Where(d => recordName.EndsWith(d.Name, StringComparison.InvariantCultureIgnoreCase)).OrderByDescending(d => d.Name.Length).FirstOrDefault(); + if (targetZone == null) + { + _log.Information("No matching zone found in LUADNS account. Aborting"); + return; + } + + var newRecord = new RecordData { Name = $"{recordName}.", Type = "TXT", Content = token, TTL = 300 }; + payload = JsonSerializer.Serialize(newRecord); + + response = await client.PostAsync(new Uri(_luaDNSApiEndpoint, $"zones/{targetZone.Id}/records"), new StringContent(payload, Encoding.UTF8, "application/json")); + if (!response.IsSuccessStatusCode) + { + _log.Information("Failed to create DNS verification record"); + return; + } + + payload = await response.Content.ReadAsStringAsync(); + newRecord = JsonSerializer.Deserialize<RecordData>(payload); + + _recordsMap[recordName] = newRecord; + + _log.Information("DNS Record created. Waiting 30 seconds to allow propagation."); + await Task.Delay(30000); + } + } + + public override async Task DeleteRecord(string recordName, string token) + { + if (!_recordsMap.ContainsKey(recordName)) + { + _log.Information($"No record with name {recordName} was created"); + return; + } + + _log.Information("Deleting LUADNS verification record"); + + using (var client = GetClient()) + { + var response = await client.DeleteAsync(new Uri(_luaDNSApiEndpoint, $"zones/{_recordsMap[recordName].ZoneId}/records/{_recordsMap[recordName].Id}")); + if (!response.IsSuccessStatusCode) + { + _log.Information("Failed to delete DNS verification record"); + return; + } + + _recordsMap.Remove(recordName); + } + } + + private HttpClient GetClient() + { + var client = _proxyService.GetHttpClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_userName}:{_apiKey}"))); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + return client; + } + } +}
\ No newline at end of file diff --git a/src/plugin.validation.dns.luadns/luadnsArguments.cs b/src/plugin.validation.dns.luadns/luadnsArguments.cs new file mode 100644 index 0000000..13ab07d --- /dev/null +++ b/src/plugin.validation.dns.luadns/luadnsArguments.cs @@ -0,0 +1,8 @@ +namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns +{ + public sealed class LUADNSArguments + { + public string LUADNSUsername { get; set; } + public string LUADNSAPIKey { get; set; } + } +}
\ No newline at end of file diff --git a/src/plugin.validation.dns.luadns/luadnsArgumentsProvider.cs b/src/plugin.validation.dns.luadns/luadnsArgumentsProvider.cs new file mode 100644 index 0000000..9cdf9e6 --- /dev/null +++ b/src/plugin.validation.dns.luadns/luadnsArgumentsProvider.cs @@ -0,0 +1,22 @@ +using Fclp; +using PKISharp.WACS.Configuration; + +namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns +{ + public sealed class LUADNSArgumentsProvider : BaseArgumentsProvider<LUADNSArguments> + { + public override string Name { get; } = "LUADNS"; + public override string Group { get; } = "Validation"; + public override string Condition { get; } = "--validationmode dns-01 --validation luadns"; + public override void Configure(FluentCommandLineParser<LUADNSArguments> parser) + { + parser.Setup(_ => _.LUADNSUsername) + .As("LUADNSUsername") + .WithDescription("LUADN account useername (email address)"); + + parser.Setup(_ => _.LUADNSAPIKey) + .As("LUADNSAPIKey") + .WithDescription("LUADNS API Key"); + } + } +}
\ No newline at end of file diff --git a/src/plugin.validation.dns.luadns/luadnsOptions.cs b/src/plugin.validation.dns.luadns/luadnsOptions.cs new file mode 100644 index 0000000..348b4bd --- /dev/null +++ b/src/plugin.validation.dns.luadns/luadnsOptions.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using PKISharp.WACS.Plugins.Base; +using PKISharp.WACS.Plugins.Base.Options; +using PKISharp.WACS.Services.Serialization; + +namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns +{ + [Plugin("3b0c3cca-db98-40b7-b678-b34791070d42")] + internal sealed class LUADNSOptions : ValidationPluginOptions<LUADNS> + { + public override string Name { get; } = "LUADNS"; + public override string Description { get; } = "Create verification records in LUADNS"; + public override string ChallengeType { get; } = Constants.Dns01ChallengeType; + + public string Username { get; set; } + [JsonProperty(propertyName: "APIKeySafe")] + public ProtectedString APIKey { get; set; } + } +}
\ No newline at end of file diff --git a/src/plugin.validation.dns.luadns/luadnsOptionsFactory.cs b/src/plugin.validation.dns.luadns/luadnsOptionsFactory.cs new file mode 100644 index 0000000..c6357cd --- /dev/null +++ b/src/plugin.validation.dns.luadns/luadnsOptionsFactory.cs @@ -0,0 +1,39 @@ +using ACMESharp.Authorizations; +using PKISharp.WACS.DomainObjects; +using PKISharp.WACS.Plugins.Base.Factories; +using PKISharp.WACS.Services; +using PKISharp.WACS.Services.Serialization; +using System.Threading.Tasks; + +namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns +{ + internal sealed class LUADNSOptionsFactory : ValidationPluginOptionsFactory<LUADNS, LUADNSOptions> + { + private readonly IArgumentsService _arguments; + public LUADNSOptionsFactory(IArgumentsService arguments) : base(Dns01ChallengeValidationDetails.Dns01ChallengeType) => _arguments = arguments; + + public override async Task<LUADNSOptions> Aquire(Target target, IInputService input, RunLevel runLevel) + { + var args = _arguments.GetArguments<LUADNSArguments>(); + var opts = new LUADNSOptions + { + Username = await _arguments.TryGetArgument(args.LUADNSUsername, input, "LUADNS Account username"), + APIKey = new ProtectedString(await _arguments.TryGetArgument(args.LUADNSAPIKey, input, "LUADNS API key", true)) + }; + return opts; + } + + public override Task<LUADNSOptions> Default(Target target) + { + var args = _arguments.GetArguments<LUADNSArguments>(); + var opts = new LUADNSOptions + { + Username = _arguments.TryGetRequiredArgument(nameof(args.LUADNSUsername), args.LUADNSUsername), + APIKey = new ProtectedString(_arguments.TryGetRequiredArgument(nameof(args.LUADNSAPIKey), args.LUADNSAPIKey)) + }; + return Task.FromResult(opts); + } + + public override bool CanValidate(Target target) => true; + } +} diff --git a/src/plugin.validation.dns.luadns/wacs.validation.dns.luadns.csproj b/src/plugin.validation.dns.luadns/wacs.validation.dns.luadns.csproj new file mode 100644 index 0000000..3749682 --- /dev/null +++ b/src/plugin.validation.dns.luadns/wacs.validation.dns.luadns.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + <AssemblyName>PKISharp.WACS.Plugins.ValidationPlugins.LUADNS</AssemblyName> + <RootNamespace>PKISharp.WACS.Plugins.ValidationPlugins</RootNamespace> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\main.lib\wacs.lib.csproj" /> + </ItemGroup> + +</Project> diff --git a/src/wacs.sln b/src/wacs.sln index aef64b5..fe889a8 100644 --- a/src/wacs.sln +++ b/src/wacs.sln @@ -36,6 +36,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1FD6 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "wacs.validation.dns.cloudflare", "plugin.validation.dns.cloudflare\wacs.validation.dns.cloudflare.csproj", "{DB1D4461-5563-425E-8D5E-D57893015D73}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "wacs.validation.dns.luadns", "plugin.validation.dns.luadns\wacs.validation.dns.luadns.csproj", "{424630AC-3029-4188-B78A-A630316A4B99}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -97,6 +99,12 @@ Global {DB1D4461-5563-425E-8D5E-D57893015D73}.Release|Any CPU.Build.0 = Release|Any CPU {DB1D4461-5563-425E-8D5E-D57893015D73}.ReleasePluggable|Any CPU.ActiveCfg = Release|Any CPU {DB1D4461-5563-425E-8D5E-D57893015D73}.ReleasePluggable|Any CPU.Build.0 = Release|Any CPU + {424630AC-3029-4188-B78A-A630316A4B99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {424630AC-3029-4188-B78A-A630316A4B99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {424630AC-3029-4188-B78A-A630316A4B99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {424630AC-3029-4188-B78A-A630316A4B99}.Release|Any CPU.Build.0 = Release|Any CPU + {424630AC-3029-4188-B78A-A630316A4B99}.ReleasePluggable|Any CPU.ActiveCfg = Release|Any CPU + {424630AC-3029-4188-B78A-A630316A4B99}.ReleasePluggable|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE |