Skip to content

Commit

Permalink
Rework for more consistency with other providers
Browse files Browse the repository at this point in the history
  • Loading branch information
Bastien Hubert committed Dec 18, 2023
1 parent 6f74c03 commit cfa9429
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 0 deletions.
2 changes: 2 additions & 0 deletions KeyVault.Acmebot/Options/AcmebotOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class AcmebotOptions

public GoogleDnsOptions GoogleDns { get; set; }

public OvhOptions OVH { get; set; }

public Route53Options Route53 { get; set; }

public TransIpOptions TransIp { get; set; }
Expand Down
10 changes: 10 additions & 0 deletions KeyVault.Acmebot/Options/OVHOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace KeyVault.Acmebot.Options;

public class OvhOptions
{
public string ApplicationKey { get; set; }

public string ApplicationSecret { get; set; }

public string ConsumerKey { get; set; }
}
259 changes: 259 additions & 0 deletions KeyVault.Acmebot/Providers/OVHProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

using KeyVault.Acmebot.Options;

using Newtonsoft.Json;

namespace KeyVault.Acmebot.Providers;

public class OvhDnsProvider : IDnsProvider
{

public OvhDnsProvider(OvhOptions options)
{
_client = new OvhClient(options.ApplicationKey, options.ApplicationSecret, options.ConsumerKey);
}

private readonly OvhClient _client;

public string Name => "OVH";

public int PropagationSeconds => 300;

public async Task<IReadOnlyList<DnsZone>> ListZonesAsync()
{
var zones = await _client.ListZonesAsync();

return zones.Select(x => new DnsZone(this) { Id = x, Name = x }).ToArray();
}

public Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable<string> values)
{
var entries = values.Select(x => new DnsEntry { Name = relativeRecordName, Type = "TXT", TTL = 600, Data = x }).ToArray();
return _client.AddRecordAsync(zone.Name, entries);
}

public Task DeleteTxtRecordAsync(DnsZone zone, string relativeRecordName)
{
return _client.DeleteRecordAsync(zone.Name, "TXT", relativeRecordName);
}

private class OvhClient
{

public OvhClient(string applicationKey, string applicationSecret, string consumerKey)
{
_httpClient = new HttpClient();

ArgumentNullException.ThrowIfNull(applicationKey);
ArgumentNullException.ThrowIfNull(applicationSecret);
ArgumentNullException.ThrowIfNull(consumerKey);

_applicationKey = applicationKey;
_applicationSecret = applicationSecret;
_consumerKey = consumerKey;
}

private readonly HttpClient _httpClient;
private readonly string _applicationKey;
private readonly string _applicationSecret;
private readonly string _consumerKey;

public async Task<IReadOnlyList<string>> ListZonesAsync()
{
var url = "https://api.ovh.com/1.0/domain/zone";
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Get, url))
{
var time = await GetTime();
var signature = GenerateSignature(_applicationSecret, _consumerKey,
time, requestMessage.Method.Method, url);

requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
requestMessage.Headers.Add("X-Ovh-Signature", signature);
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());


var response = await _httpClient.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
var domains = await response.Content.ReadAsAsync<string[]>();
return domains;
}
}


public async Task DeleteRecordAsync(string zone, string type, string relativeRecordName)
{

var recordIds = Array.Empty<string>();
var url = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record";
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Get, url))
{
var time = await GetTime();
var signature = GenerateSignature(_applicationSecret, _consumerKey,
time, requestMessage.Method.Method, url);

requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
requestMessage.Headers.Add("X-Ovh-Signature", signature);
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());


var response = await _httpClient.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
recordIds = await response.Content.ReadAsAsync<string[]>();
}



foreach (var recordId in recordIds)
{
url = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record/" + recordId;
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Get, url))
{
var time = await GetTime();
var signature = GenerateSignature(_applicationSecret, _consumerKey,
time, requestMessage.Method.Method, url);

requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
requestMessage.Headers.Add("X-Ovh-Signature", signature);
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());

var response = await _httpClient.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
var ovhRecord = await response.Content.ReadAsAsync<OvhRecord>();

if (ovhRecord.fieldType == type && ovhRecord.subDomain == relativeRecordName)
{
url = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record/" + recordId;
using (var requestMessage2 =
new HttpRequestMessage(HttpMethod.Delete, url))
{
var time2 = await GetTime();
var signature2 = GenerateSignature(_applicationSecret, _consumerKey,
time2, requestMessage2.Method.Method, url);

requestMessage2.Headers.Add("X-Ovh-Application", _applicationKey);
requestMessage2.Headers.Add("X-Ovh-Consumer", _consumerKey);
requestMessage2.Headers.Add("X-Ovh-Signature", signature2);
requestMessage2.Headers.Add("X-Ovh-Timestamp", time2.ToString());

var response2 = await _httpClient.SendAsync(requestMessage2);
response2.EnsureSuccessStatusCode();
}
}
}
}

}
public async Task AddRecordAsync(string zone, IReadOnlyList<DnsEntry> dnsEntries)
{
foreach (var dnsEntry in dnsEntries)
{
var recordUrl = "https://api.ovh.com/1.0/domain/zone/" + zone + "/record";
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Post, recordUrl))
{
var body = new
{
fieldType = dnsEntry.Type,
subDomain = dnsEntry.Name,
target = dnsEntry.Data,
ttl = dnsEntry.TTL
};
var bodyString = JsonConvert.SerializeObject(body);
requestMessage.Content = new StringContent(bodyString, Encoding.UTF8, "application/json");
var time = await GetTime();
var signature = GenerateSignature(_applicationSecret, _consumerKey,
time, requestMessage.Method.Method, recordUrl, bodyString);

requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
requestMessage.Headers.Add("X-Ovh-Signature", signature);
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());


var response = await _httpClient.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
}
}
var refreshUrl = "https://api.ovh.com/1.0/domain/zone/" + zone + "/refresh";
using (var requestMessage =
new HttpRequestMessage(HttpMethod.Post, refreshUrl))
{
var time = await GetTime();
var signature = GenerateSignature(_applicationSecret, _consumerKey,
time, requestMessage.Method.Method, refreshUrl);

requestMessage.Headers.Add("X-Ovh-Application", _applicationKey);
requestMessage.Headers.Add("X-Ovh-Consumer", _consumerKey);
requestMessage.Headers.Add("X-Ovh-Signature", signature);
requestMessage.Headers.Add("X-Ovh-Timestamp", time.ToString());


var responseRefresh = await _httpClient.SendAsync(requestMessage);
responseRefresh.EnsureSuccessStatusCode();
}

}


private static string GenerateSignature(string applicationSecret, string consumerKey,
long currentTimestamp, string method, string target, string data = null)
{

using (var sha1Hasher = SHA1.Create())
{
var toSign =
string.Join("+", applicationSecret, consumerKey, method,
target, data, currentTimestamp);
var binaryHash = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(toSign));
var signature = string.Join("",
binaryHash.Select(x => x.ToString("X2"))).ToLower();
return $"$1${signature}";
}
}

private async Task<long> GetTime()
{
var response = await _httpClient.GetAsync("https://api.ovh.com/1.0/auth/time");

response.EnsureSuccessStatusCode();
var time = await response.Content.ReadAsAsync<long>();
return time;
}
}

private class OvhRecord
{
public string id { get; set; }
public string zone { get; set; }
public string subDomain { get; set; }
public string fieldType { get; set; }
public string target { get; set; }
public int ttl { get; set; }
}


private class DnsEntry
{
public string Data { get; set; }

public string Name { get; set; }

public int TTL { get; set; }

public string Type { get; set; }
}

}
1 change: 1 addition & 0 deletions KeyVault.Acmebot/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public override void Configure(IFunctionsHostBuilder builder)
dnsProviders.TryAdd(options.GandiLiveDns, o => new GandiLiveDnsProvider(o));
dnsProviders.TryAdd(options.GoDaddy, o => new GoDaddyProvider(o));
dnsProviders.TryAdd(options.GoogleDns, o => new GoogleDnsProvider(o));
dnsProviders.TryAdd(options.OVH, o => new OvhDnsProvider(o));
dnsProviders.TryAdd(options.Route53, o => new Route53Provider(o));
dnsProviders.TryAdd(options.TransIp, o => new TransIpProvider(options, o, credential));

Expand Down

0 comments on commit cfa9429

Please sign in to comment.