diff --git a/KeyVault.Acmebot/Internal/HttpClientExtensions.cs b/KeyVault.Acmebot/Internal/HttpClientExtensions.cs new file mode 100644 index 00000000..cf1eb6f7 --- /dev/null +++ b/KeyVault.Acmebot/Internal/HttpClientExtensions.cs @@ -0,0 +1,29 @@ +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +using Newtonsoft.Json; + +namespace KeyVault.Acmebot.Internal +{ + internal static class HttpClientExtensions + { + public static Task PostAsync(this HttpClient client, string requestUri, T value) => client.PostAsync(requestUri, SerializeToJson(value)); + + public static Task PutAsync(this HttpClient client, string requestUri, T value) => client.PutAsync(requestUri, SerializeToJson(value)); + + public static Task PatchAsync(this HttpClient client, string requestUri, T value) => client.PatchAsync(requestUri, SerializeToJson(value)); + + public static Task DeleteAsync(this HttpClient client, string requestUri, T value) + { + var request = new HttpRequestMessage(HttpMethod.Delete, requestUri) + { + Content = SerializeToJson(value) + }; + + return client.SendAsync(request); + } + + private static HttpContent SerializeToJson(T value) => new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json"); + } +} diff --git a/KeyVault.Acmebot/Options/AcmebotOptions.cs b/KeyVault.Acmebot/Options/AcmebotOptions.cs index 10755f38..516657f6 100644 --- a/KeyVault.Acmebot/Options/AcmebotOptions.cs +++ b/KeyVault.Acmebot/Options/AcmebotOptions.cs @@ -31,6 +31,8 @@ public class AcmebotOptions public CloudflareOptions Cloudflare { get; set; } + public CustomDnsOptions CustomDns { get; set; } + public DnsMadeEasyOptions DnsMadeEasy { get; set; } public GoDaddyOptions GoDaddy { get; set; } diff --git a/KeyVault.Acmebot/Options/CustomDnsOptions.cs b/KeyVault.Acmebot/Options/CustomDnsOptions.cs new file mode 100644 index 00000000..2e0fab8d --- /dev/null +++ b/KeyVault.Acmebot/Options/CustomDnsOptions.cs @@ -0,0 +1,13 @@ +namespace KeyVault.Acmebot.Options +{ + public class CustomDnsOptions + { + public int PropagationSeconds { get; set; } = 180; + + public string Endpoint { get; set; } + + public string ApiKey { get; set; } + + public string ApiKeyHeaderName { get; set; } = "X-Api-Key"; + } +} diff --git a/KeyVault.Acmebot/Providers/CloudflareProvider.cs b/KeyVault.Acmebot/Providers/CloudflareProvider.cs index 0e23abcc..cdf951f5 100644 --- a/KeyVault.Acmebot/Providers/CloudflareProvider.cs +++ b/KeyVault.Acmebot/Providers/CloudflareProvider.cs @@ -6,6 +6,7 @@ using System.Net.Http.Headers; using System.Threading.Tasks; +using KeyVault.Acmebot.Internal; using KeyVault.Acmebot.Options; using Newtonsoft.Json; @@ -73,7 +74,7 @@ public CloudflareDnsClient(string apiToken) public async Task> ListAllZonesAsync() { - int page = 1; + var page = 1; var zones = new List(); ApiResult result; @@ -102,7 +103,7 @@ public async Task> GetDnsRecordsAsync(string zone public async Task CreateDnsRecordAsync(string zone, string name, string content) { - var response = await _httpClient.PostAsJsonAsync($"/client/v4/zones/{zone}/dns_records", new { type = "TXT", name, content, ttl = 60 }); + var response = await _httpClient.PostAsync($"/client/v4/zones/{zone}/dns_records", new { type = "TXT", name, content, ttl = 60 }); response.EnsureSuccessStatusCode(); } diff --git a/KeyVault.Acmebot/Providers/CustomDnsProvider.cs b/KeyVault.Acmebot/Providers/CustomDnsProvider.cs new file mode 100644 index 00000000..fe88a254 --- /dev/null +++ b/KeyVault.Acmebot/Providers/CustomDnsProvider.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +using KeyVault.Acmebot.Internal; +using KeyVault.Acmebot.Options; + +namespace KeyVault.Acmebot.Providers +{ + public class CustomDnsProvider : IDnsProvider + { + public CustomDnsProvider(CustomDnsOptions options) + { + _httpClient = new HttpClient + { + BaseAddress = new Uri(options.Endpoint) + }; + + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + _httpClient.DefaultRequestHeaders.TryAddWithoutValidation(options.ApiKeyHeaderName, options.ApiKey); + + PropagationSeconds = options.PropagationSeconds; + } + + private readonly HttpClient _httpClient; + + public int PropagationSeconds { get; } + + public async Task> ListZonesAsync() + { + var response = await _httpClient.GetAsync("zones"); + + response.EnsureSuccessStatusCode(); + + var zones = await response.Content.ReadAsAsync(); + + return zones; + } + + public async Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable values) + { + var recordName = $"{relativeRecordName}.{zone.Name}"; + + var response = await _httpClient.PutAsync($"zones/{zone.Id}/records/{recordName}", new { type = "TXT", ttl = 60, values }); + + response.EnsureSuccessStatusCode(); + } + + public async Task DeleteTxtRecordAsync(DnsZone zone, string relativeRecordName) + { + var recordName = $"{relativeRecordName}.{zone.Name}"; + + var response = await _httpClient.DeleteAsync($"zones/{zone.Id}/records/{recordName}"); + + response.EnsureSuccessStatusCode(); + } + } +} diff --git a/KeyVault.Acmebot/Providers/DnsMadeEasyProvider.cs b/KeyVault.Acmebot/Providers/DnsMadeEasyProvider.cs index e809f512..5fa0b868 100644 --- a/KeyVault.Acmebot/Providers/DnsMadeEasyProvider.cs +++ b/KeyVault.Acmebot/Providers/DnsMadeEasyProvider.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; +using KeyVault.Acmebot.Internal; using KeyVault.Acmebot.Options; using Newtonsoft.Json; @@ -97,17 +98,14 @@ public async Task> ListRecordsAsync(string zoneId) public async Task DeleteRecordAsync(string zoneId, DnsEntry entry) { - var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Delete, $"dns/managed/{zoneId}/records/{entry.Id}")); + var response = await _httpClient.DeleteAsync($"dns/managed/{zoneId}/records/{entry.Id}"); response.EnsureSuccessStatusCode(); } public async Task AddRecordAsync(string zoneId, DnsEntry entry) { - var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"dns/managed/{zoneId}/records") - { - Content = new StringContent(JsonConvert.SerializeObject(entry), Encoding.UTF8, "application/json") - }); + var response = await _httpClient.PostAsync($"dns/managed/{zoneId}/records", entry); response.EnsureSuccessStatusCode(); } diff --git a/KeyVault.Acmebot/Providers/GoDaddyProvider.cs b/KeyVault.Acmebot/Providers/GoDaddyProvider.cs index a2996798..99ebc2e5 100644 --- a/KeyVault.Acmebot/Providers/GoDaddyProvider.cs +++ b/KeyVault.Acmebot/Providers/GoDaddyProvider.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; -using System.Text; using System.Threading.Tasks; +using KeyVault.Acmebot.Internal; using KeyVault.Acmebot.Options; using Newtonsoft.Json; @@ -33,19 +33,19 @@ public async Task> ListZonesAsync() public async Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable values) { var entries = new List(); + foreach (var value in values) { entries.Add(new DnsEntry { Name = relativeRecordName, Type = "TXT", - TTL = 600, + TTL = 60, Data = value }); } await _client.AddRecordAsync(zone.Id, entries); - } public async Task DeleteTxtRecordAsync(DnsZone zone, string relativeRecordName) @@ -74,15 +74,13 @@ public GoDaddyClient(string apiKey, string apiSecret) throw new ArgumentNullException(nameof(apiSecret)); } - - _httpClient = new HttpClient() + _httpClient = new HttpClient { BaseAddress = new Uri("https://api.godaddy.com") }; _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("sso-key", $"{apiKey}:{apiSecret}"); - } private readonly HttpClient _httpClient; @@ -93,19 +91,18 @@ public async Task> ListZonesAsync() response.EnsureSuccessStatusCode(); - var domains = await response.Content.ReadAsAsync>(); + var domains = await response.Content.ReadAsAsync(); return domains; } public async Task> ListRecordsAsync(string zoneId) { - var response = await _httpClient.GetAsync($"v1/domains/{zoneId}/records"); response.EnsureSuccessStatusCode(); - var entries = await response.Content.ReadAsAsync>(); + var entries = await response.Content.ReadAsAsync(); return entries; } @@ -113,14 +110,13 @@ public async Task> ListRecordsAsync(string zoneId) public async Task DeleteRecordAsync(string zoneId, DnsEntry entry) { var response = await _httpClient.DeleteAsync($"v1/domains/{zoneId}/records/{entry.Type}/{entry.Name}"); + response.EnsureSuccessStatusCode(); } - public async Task AddRecordAsync(string zoneId, List entries) + public async Task AddRecordAsync(string zoneId, IReadOnlyList entries) { - var content = new StringContent(JsonConvert.SerializeObject(entries, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }), Encoding.UTF8, "application/json"); - - var response = await _httpClient.PatchAsync($"v1/domains/{zoneId}/records", content); + var response = await _httpClient.PatchAsync($"v1/domains/{zoneId}/records", entries); response.EnsureSuccessStatusCode(); } diff --git a/KeyVault.Acmebot/Providers/TransIpProvider.cs b/KeyVault.Acmebot/Providers/TransIpProvider.cs index f5a30c0e..286ae540 100644 --- a/KeyVault.Acmebot/Providers/TransIpProvider.cs +++ b/KeyVault.Acmebot/Providers/TransIpProvider.cs @@ -127,10 +127,7 @@ public async Task DeleteRecordAsync(string zoneName, DnsEntry entry) DnsEntry = entry }; - var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Delete, $"domains/{zoneName}/dns") - { - Content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json") - }); + var response = await _httpClient.DeleteAsync($"domains/{zoneName}/dns", request); response.EnsureSuccessStatusCode(); } @@ -144,10 +141,7 @@ public async Task AddRecordAsync(string zoneName, DnsEntry entry) DnsEntry = entry }; - var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"domains/{zoneName}/dns") - { - Content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json") - }); + var response = await _httpClient.PostAsync($"domains/{zoneName}/dns", request); response.EnsureSuccessStatusCode(); } diff --git a/KeyVault.Acmebot/Startup.cs b/KeyVault.Acmebot/Startup.cs index 29e23ca5..85e837ae 100644 --- a/KeyVault.Acmebot/Startup.cs +++ b/KeyVault.Acmebot/Startup.cs @@ -90,6 +90,11 @@ public override void Configure(IFunctionsHostBuilder builder) return new CloudflareProvider(options.Cloudflare); } + if (options.CustomDns != null) + { + return new CustomDnsProvider(options.CustomDns); + } + if (options.DnsMadeEasy != null) { return new DnsMadeEasyProvider(options.DnsMadeEasy); @@ -120,12 +125,12 @@ public override void Configure(IFunctionsHostBuilder builder) return new TransIpProvider(options, options.TransIp, environment); } + // Backward compatibility if (options.AzureDns != null) { return new AzureDnsProvider(options.AzureDns, environment); } - // Backward compatibility if (options.SubscriptionId != null) { return new AzureDnsProvider(new AzureDnsOptions { SubscriptionId = options.SubscriptionId }, environment);