Skip to content

Commit cfa9429

Browse files
committed
Rework for more consistency with other providers
1 parent 6f74c03 commit cfa9429

4 files changed

Lines changed: 272 additions & 0 deletions

File tree

KeyVault.Acmebot/Options/AcmebotOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public class AcmebotOptions
4949

5050
public GoogleDnsOptions GoogleDns { get; set; }
5151

52+
public OvhOptions OVH { get; set; }
53+
5254
public Route53Options Route53 { get; set; }
5355

5456
public TransIpOptions TransIp { get; set; }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace KeyVault.Acmebot.Options;
2+
3+
public class OvhOptions
4+
{
5+
public string ApplicationKey { get; set; }
6+
7+
public string ApplicationSecret { get; set; }
8+
9+
public string ConsumerKey { get; set; }
10+
}
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net.Http;
5+
using System.Security.Cryptography;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
using KeyVault.Acmebot.Options;
10+
11+
using Newtonsoft.Json;
12+
13+
namespace KeyVault.Acmebot.Providers;
14+
15+
public class OvhDnsProvider : IDnsProvider
16+
{
17+
18+
public OvhDnsProvider(OvhOptions options)
19+
{
20+
_client = new OvhClient(options.ApplicationKey, options.ApplicationSecret, options.ConsumerKey);
21+
}
22+
23+
private readonly OvhClient _client;
24+
25+
public string Name => "OVH";
26+
27+
public int PropagationSeconds => 300;
28+
29+
public async Task<IReadOnlyList<DnsZone>> ListZonesAsync()
30+
{
31+
var zones = await _client.ListZonesAsync();
32+
33+
return zones.Select(x => new DnsZone(this) { Id = x, Name = x }).ToArray();
34+
}
35+
36+
public Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable<string> values)
37+
{
38+
var entries = values.Select(x => new DnsEntry { Name = relativeRecordName, Type = "TXT", TTL = 600, Data = x }).ToArray();
39+
return _client.AddRecordAsync(zone.Name, entries);
40+
}
41+
42+
public Task DeleteTxtRecordAsync(DnsZone zone, string relativeRecordName)
43+
{
44+
return _client.DeleteRecordAsync(zone.Name, "TXT", relativeRecordName);
45+
}
46+
47+
private class OvhClient
48+
{
49+
50+
public OvhClient(string applicationKey, string applicationSecret, string consumerKey)
51+
{
52+
_httpClient = new HttpClient();
53+
54+
ArgumentNullException.ThrowIfNull(applicationKey);
55+
ArgumentNullException.ThrowIfNull(applicationSecret);
56+
ArgumentNullException.ThrowIfNull(consumerKey);
57+
58+
_applicationKey = applicationKey;
59+
_applicationSecret = applicationSecret;
60+
_consumerKey = consumerKey;
61+
}
62+
63+
private readonly HttpClient _httpClient;
64+
private readonly string _applicationKey;
65+
private readonly string _applicationSecret;
66+
private readonly string _consumerKey;
67+
68+
public async Task<IReadOnlyList<string>> ListZonesAsync()
69+
{
70+
var url = "https://api.ovh.com/1.0/domain/zone";
71+
using (var requestMessage =
72+
new HttpRequestMessage(HttpMethod.Get, url))
73+
{
74+
var time = await GetTime();
75+
var signature = GenerateSignature(_applicationSecret, _consumerKey,
76+
time, requestMessage.Method.Method, url);
77+
78+
requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
79+
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
80+
requestMessage.Headers.Add("X-Ovh-Signature", signature);
81+
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());
82+
83+
84+
var response = await _httpClient.SendAsync(requestMessage);
85+
response.EnsureSuccessStatusCode();
86+
var domains = await response.Content.ReadAsAsync<string[]>();
87+
return domains;
88+
}
89+
}
90+
91+
92+
public async Task DeleteRecordAsync(string zone, string type, string relativeRecordName)
93+
{
94+
95+
var recordIds = Array.Empty<string>();
96+
var url = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record";
97+
using (var requestMessage =
98+
new HttpRequestMessage(HttpMethod.Get, url))
99+
{
100+
var time = await GetTime();
101+
var signature = GenerateSignature(_applicationSecret, _consumerKey,
102+
time, requestMessage.Method.Method, url);
103+
104+
requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
105+
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
106+
requestMessage.Headers.Add("X-Ovh-Signature", signature);
107+
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());
108+
109+
110+
var response = await _httpClient.SendAsync(requestMessage);
111+
response.EnsureSuccessStatusCode();
112+
recordIds = await response.Content.ReadAsAsync<string[]>();
113+
}
114+
115+
116+
117+
foreach (var recordId in recordIds)
118+
{
119+
url = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record/" + recordId;
120+
using (var requestMessage =
121+
new HttpRequestMessage(HttpMethod.Get, url))
122+
{
123+
var time = await GetTime();
124+
var signature = GenerateSignature(_applicationSecret, _consumerKey,
125+
time, requestMessage.Method.Method, url);
126+
127+
requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
128+
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
129+
requestMessage.Headers.Add("X-Ovh-Signature", signature);
130+
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());
131+
132+
var response = await _httpClient.SendAsync(requestMessage);
133+
response.EnsureSuccessStatusCode();
134+
var ovhRecord = await response.Content.ReadAsAsync<OvhRecord>();
135+
136+
if (ovhRecord.fieldType == type && ovhRecord.subDomain == relativeRecordName)
137+
{
138+
url = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record/" + recordId;
139+
using (var requestMessage2 =
140+
new HttpRequestMessage(HttpMethod.Delete, url))
141+
{
142+
var time2 = await GetTime();
143+
var signature2 = GenerateSignature(_applicationSecret, _consumerKey,
144+
time2, requestMessage2.Method.Method, url);
145+
146+
requestMessage2.Headers.Add("X-Ovh-Application", _applicationKey);
147+
requestMessage2.Headers.Add("X-Ovh-Consumer", _consumerKey);
148+
requestMessage2.Headers.Add("X-Ovh-Signature", signature2);
149+
requestMessage2.Headers.Add("X-Ovh-Timestamp", time2.ToString());
150+
151+
var response2 = await _httpClient.SendAsync(requestMessage2);
152+
response2.EnsureSuccessStatusCode();
153+
}
154+
}
155+
}
156+
}
157+
158+
}
159+
public async Task AddRecordAsync(string zone, IReadOnlyList<DnsEntry> dnsEntries)
160+
{
161+
foreach (var dnsEntry in dnsEntries)
162+
{
163+
var recordUrl = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record";
164+
using (var requestMessage =
165+
new HttpRequestMessage(HttpMethod.Post, recordUrl))
166+
{
167+
var body = new
168+
{
169+
fieldType = dnsEntry.Type,
170+
subDomain = dnsEntry.Name,
171+
target = dnsEntry.Data,
172+
ttl = dnsEntry.TTL
173+
};
174+
var bodyString = JsonConvert.SerializeObject(body);
175+
requestMessage.Content = new StringContent(bodyString, Encoding.UTF8, "application/json");
176+
var time = await GetTime();
177+
var signature = GenerateSignature(_applicationSecret, _consumerKey,
178+
time, requestMessage.Method.Method, recordUrl, bodyString);
179+
180+
requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
181+
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
182+
requestMessage.Headers.Add("X-Ovh-Signature", signature);
183+
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());
184+
185+
186+
var response = await _httpClient.SendAsync(requestMessage);
187+
response.EnsureSuccessStatusCode();
188+
}
189+
}
190+
var refreshUrl = "https://api.ovh.com/1.0/domain/zone/" + zone + "/refresh";
191+
using (var requestMessage =
192+
new HttpRequestMessage(HttpMethod.Post, refreshUrl))
193+
{
194+
var time = await GetTime();
195+
var signature = GenerateSignature(_applicationSecret, _consumerKey,
196+
time, requestMessage.Method.Method, refreshUrl);
197+
198+
requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
199+
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
200+
requestMessage.Headers.Add("X-Ovh-Signature", signature);
201+
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());
202+
203+
204+
var responseRefresh = await _httpClient.SendAsync(requestMessage);
205+
responseRefresh.EnsureSuccessStatusCode();
206+
}
207+
208+
}
209+
210+
211+
private static string GenerateSignature(string applicationSecret, string consumerKey,
212+
long currentTimestamp, string method, string target, string data = null)
213+
{
214+
215+
using (var sha1Hasher = SHA1.Create())
216+
{
217+
var toSign =
218+
string.Join("+", applicationSecret, consumerKey, method,
219+
target, data, currentTimestamp);
220+
var binaryHash = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(toSign));
221+
var signature = string.Join("",
222+
binaryHash.Select(x => x.ToString("X2"))).ToLower();
223+
return $"$1${signature}";
224+
}
225+
}
226+
227+
private async Task<long> GetTime()
228+
{
229+
var response = await _httpClient.GetAsync("https://api.ovh.com/1.0/auth/time");
230+
231+
response.EnsureSuccessStatusCode();
232+
var time = await response.Content.ReadAsAsync<long>();
233+
return time;
234+
}
235+
}
236+
237+
private class OvhRecord
238+
{
239+
public string id { get; set; }
240+
public string zone { get; set; }
241+
public string subDomain { get; set; }
242+
public string fieldType { get; set; }
243+
public string target { get; set; }
244+
public int ttl { get; set; }
245+
}
246+
247+
248+
private class DnsEntry
249+
{
250+
public string Data { get; set; }
251+
252+
public string Name { get; set; }
253+
254+
public int TTL { get; set; }
255+
256+
public string Type { get; set; }
257+
}
258+
259+
}

KeyVault.Acmebot/Startup.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ public override void Configure(IFunctionsHostBuilder builder)
123123
dnsProviders.TryAdd(options.GandiLiveDns, o => new GandiLiveDnsProvider(o));
124124
dnsProviders.TryAdd(options.GoDaddy, o => new GoDaddyProvider(o));
125125
dnsProviders.TryAdd(options.GoogleDns, o => new GoogleDnsProvider(o));
126+
dnsProviders.TryAdd(options.OVH, o => new OvhDnsProvider(o));
126127
dnsProviders.TryAdd(options.Route53, o => new Route53Provider(o));
127128
dnsProviders.TryAdd(options.TransIp, o => new TransIpProvider(options, o, credential));
128129

0 commit comments

Comments
 (0)