summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWouter Tinus <win.acme.simple@gmail.com>2020-07-28 17:16:24 +0200
committerGitHub <noreply@github.com>2020-07-28 17:16:24 +0200
commit1dd5741bb05cdc8d7016ec93bf8d0f1b831ca76a (patch)
treec849b918bebb195cbf974d5f60c624a07ab08730
parent6b2636c2d9540487e43d1b44bd1e33a6375c9e26 (diff)
parent7a0f121322385eb2bed5127a5da64fc90b3c986d (diff)
downloadletsencrypt-win-simple-1dd5741bb05cdc8d7016ec93bf8d0f1b831ca76a.zip
letsencrypt-win-simple-1dd5741bb05cdc8d7016ec93bf8d0f1b831ca76a.tar.gz
letsencrypt-win-simple-1dd5741bb05cdc8d7016ec93bf8d0f1b831ca76a.tar.bz2
Merge pull request #1626 from Skulblaka/feature/digitalocean-validation
Add DigitalOcean validation plugin
-rw-r--r--src/main.lib/Clients/Acme/AcmeClient.cs2
-rw-r--r--src/plugin.validation.dns.cloudflare/wacs.validation.dns.cloudflare.csproj2
-rw-r--r--src/plugin.validation.dns.digitalocean/DigitalOcean.cs68
-rw-r--r--src/plugin.validation.dns.digitalocean/DigitalOceanArguments.cs7
-rw-r--r--src/plugin.validation.dns.digitalocean/DigitalOceanArgumentsProvider.cs18
-rw-r--r--src/plugin.validation.dns.digitalocean/DigitalOceanOptions.cs15
-rw-r--r--src/plugin.validation.dns.digitalocean/DigitalOceanOptionsFactory.cs40
-rw-r--r--src/plugin.validation.dns.digitalocean/wacs.validation.dns.digitalocean.csproj17
-rw-r--r--src/wacs.sln8
9 files changed, 175 insertions, 2 deletions
diff --git a/src/main.lib/Clients/Acme/AcmeClient.cs b/src/main.lib/Clients/Acme/AcmeClient.cs
index e3a3091..64f716e 100644
--- a/src/main.lib/Clients/Acme/AcmeClient.cs
+++ b/src/main.lib/Clients/Acme/AcmeClient.cs
@@ -582,7 +582,7 @@ namespace PKISharp.WACS.Clients.Acme
_log.Warning("First chance error calling into ACME server, retrying with new nonce...");
var client = await GetClient();
await client.GetNonceAsync();
- return await Retry(executor, attempt += 1);
+ return await Retry(executor, attempt + 1);
}
else
{
diff --git a/src/plugin.validation.dns.cloudflare/wacs.validation.dns.cloudflare.csproj b/src/plugin.validation.dns.cloudflare/wacs.validation.dns.cloudflare.csproj
index 0ea9d8c..3f04238 100644
--- a/src/plugin.validation.dns.cloudflare/wacs.validation.dns.cloudflare.csproj
+++ b/src/plugin.validation.dns.cloudflare/wacs.validation.dns.cloudflare.csproj
@@ -15,7 +15,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
+ <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>
diff --git a/src/plugin.validation.dns.digitalocean/DigitalOcean.cs b/src/plugin.validation.dns.digitalocean/DigitalOcean.cs
new file mode 100644
index 0000000..f25ff9d
--- /dev/null
+++ b/src/plugin.validation.dns.digitalocean/DigitalOcean.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Threading.Tasks;
+using DigitalOcean.API;
+using DigitalOcean.API.Models.Requests;
+using PKISharp.WACS.Clients.DNS;
+using PKISharp.WACS.Services;
+
+namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns
+{
+ internal class DigitalOcean : DnsValidation<DigitalOcean>
+ {
+ private readonly IDigitalOceanClient _doClient;
+ private long? _recordId;
+
+ public DigitalOcean(DigitalOceanOptions options, LookupClientProvider dnsClient, ILogService log, ISettingsService settings) : base(dnsClient, log, settings)
+ {
+ _doClient = new DigitalOceanClient(options.ApiToken.Value);
+ }
+
+ public override async Task DeleteRecord(DnsValidationRecord record)
+ {
+ try
+ {
+ var (_, zone) = SplitDomain(record.Authority.Domain);
+ if (_recordId == null)
+ {
+ _log.Warning("Not deleting DNS records on DigitalOcean because of missing record id.");
+ return;
+ }
+
+ await _doClient.DomainRecords.Delete(zone, _recordId.Value);
+ _log.Information("Successfully deleted DNS record on DigitalOcean.");
+ }
+ catch (Exception ex)
+ {
+ _log.Error(ex, "Failed to delete DNS record on DigitalOcean.");
+ }
+ }
+
+ public override async Task<bool> CreateRecord(DnsValidationRecord record)
+ {
+ try
+ {
+ var (name, zone) = SplitDomain(record.Authority.Domain);
+ var createdRecord = await _doClient.DomainRecords.Create(zone, new DomainRecord
+ {
+ Type = "TXT",
+ Name = name,
+ Data = record.Value,
+ Ttl = 300
+ });
+ _recordId = createdRecord.Id;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _log.Error(ex, "Failed to create DNS record on DigitalOcean.");
+ return false;
+ }
+ }
+
+ private (string, string) SplitDomain(string domain)
+ {
+ var index = domain.IndexOf('.');
+ return (domain.Substring(0, index), domain.Substring(index + 1));
+ }
+ }
+}
diff --git a/src/plugin.validation.dns.digitalocean/DigitalOceanArguments.cs b/src/plugin.validation.dns.digitalocean/DigitalOceanArguments.cs
new file mode 100644
index 0000000..072d0d8
--- /dev/null
+++ b/src/plugin.validation.dns.digitalocean/DigitalOceanArguments.cs
@@ -0,0 +1,7 @@
+namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns
+{
+ public class DigitalOceanArguments
+ {
+ public string ApiToken { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/plugin.validation.dns.digitalocean/DigitalOceanArgumentsProvider.cs b/src/plugin.validation.dns.digitalocean/DigitalOceanArgumentsProvider.cs
new file mode 100644
index 0000000..394021c
--- /dev/null
+++ b/src/plugin.validation.dns.digitalocean/DigitalOceanArgumentsProvider.cs
@@ -0,0 +1,18 @@
+using Fclp;
+using PKISharp.WACS.Configuration;
+
+namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns
+{
+ public class DigitalOceanArgumentsProvider : BaseArgumentsProvider<DigitalOceanArguments>
+ {
+ public override string Name => "DigitalOcean";
+ public override string Group => "Validation";
+ public override string Condition => "--validationmode dns-01 --validation digitalocean";
+ public override void Configure(FluentCommandLineParser<DigitalOceanArguments> parser)
+ {
+ _ = parser.Setup(o => o.ApiToken)
+ .As("digitaloceanapitoken")
+ .WithDescription("The API token to authenticate against the DigitalOcean API");
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugin.validation.dns.digitalocean/DigitalOceanOptions.cs b/src/plugin.validation.dns.digitalocean/DigitalOceanOptions.cs
new file mode 100644
index 0000000..19cba1e
--- /dev/null
+++ b/src/plugin.validation.dns.digitalocean/DigitalOceanOptions.cs
@@ -0,0 +1,15 @@
+using PKISharp.WACS.Plugins.Base;
+using PKISharp.WACS.Plugins.Base.Options;
+using PKISharp.WACS.Services.Serialization;
+
+namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns
+{
+ [Plugin("1a87d670-3fa3-4a2a-bb10-491d48feb5db")]
+ internal class DigitalOceanOptions : ValidationPluginOptions<DigitalOcean>
+ {
+ public override string Name => "DigitalOcean";
+ public override string Description => "Create verification records on DigitalOcean";
+ public override string ChallengeType => Constants.Dns01ChallengeType;
+ public ProtectedString ApiToken { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/plugin.validation.dns.digitalocean/DigitalOceanOptionsFactory.cs b/src/plugin.validation.dns.digitalocean/DigitalOceanOptionsFactory.cs
new file mode 100644
index 0000000..d52860e
--- /dev/null
+++ b/src/plugin.validation.dns.digitalocean/DigitalOceanOptionsFactory.cs
@@ -0,0 +1,40 @@
+using System.Threading.Tasks;
+using ACMESharp.Authorizations;
+using PKISharp.WACS.DomainObjects;
+using PKISharp.WACS.Plugins.Base.Factories;
+using PKISharp.WACS.Services;
+using PKISharp.WACS.Services.Serialization;
+
+namespace PKISharp.WACS.Plugins.ValidationPlugins.Dns
+{
+ internal class DigitalOceanOptionsFactory : ValidationPluginOptionsFactory<DigitalOcean, DigitalOceanOptions>
+ {
+ private readonly IArgumentsService _arguments;
+
+ public DigitalOceanOptionsFactory(IArgumentsService arguments) : base(Dns01ChallengeValidationDetails.Dns01ChallengeType)
+ {
+ _arguments = arguments;
+ }
+
+ public override Task<DigitalOceanOptions> Aquire(Target target, IInputService inputService, RunLevel runLevel)
+ {
+ var arguments = _arguments.GetArguments<DigitalOceanArguments>();
+ return Task.FromResult(new DigitalOceanOptions
+ {
+ ApiToken = new ProtectedString(arguments.ApiToken)
+ });
+ }
+
+ public override Task<DigitalOceanOptions> Default(Target target)
+ {
+ var arguments = _arguments.GetArguments<DigitalOceanArguments>();
+ return Task.FromResult(new DigitalOceanOptions
+ {
+ ApiToken = new ProtectedString(
+ _arguments.TryGetRequiredArgument(nameof(arguments.ApiToken), arguments.ApiToken))
+ });
+ }
+
+ public override bool CanValidate(Target target) => true;
+ }
+} \ No newline at end of file
diff --git a/src/plugin.validation.dns.digitalocean/wacs.validation.dns.digitalocean.csproj b/src/plugin.validation.dns.digitalocean/wacs.validation.dns.digitalocean.csproj
new file mode 100644
index 0000000..5a4ec0b
--- /dev/null
+++ b/src/plugin.validation.dns.digitalocean/wacs.validation.dns.digitalocean.csproj
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+ <RootNamespace>PKISharp.WACS.Plugins.ValidationPlugins.Dns</RootNamespace>
+ <AssemblyName>PKISharp.WACS.Plugins.ValidationPlugins.DigitalOcean</AssemblyName>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="DigitalOcean.API" Version="5.1.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\main.lib\wacs.lib.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/wacs.sln b/src/wacs.sln
index fe889a8..d30363f 100644
--- a/src/wacs.sln
+++ b/src/wacs.sln
@@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "wacs.validation.dns.cloudfl
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
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "wacs.validation.dns.digitalocean", "plugin.validation.dns.digitalocean\wacs.validation.dns.digitalocean.csproj", "{D8BD50E6-0759-476B-A021-0CF1325B4DDB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -105,6 +107,12 @@ Global
{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
+ {D8BD50E6-0759-476B-A021-0CF1325B4DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8BD50E6-0759-476B-A021-0CF1325B4DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8BD50E6-0759-476B-A021-0CF1325B4DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8BD50E6-0759-476B-A021-0CF1325B4DDB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8BD50E6-0759-476B-A021-0CF1325B4DDB}.ReleasePluggable|Any CPU.ActiveCfg = Release|Any CPU
+ {D8BD50E6-0759-476B-A021-0CF1325B4DDB}.ReleasePluggable|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE