public Challenge Decode(IdentifierPart ip, ChallengePart cp, ISigner signer) { if (cp.Type != AcmeProtocol.CHALLENGE_TYPE_DNS) { throw new InvalidDataException("unsupported Challenge type") .With("challengeType", cp.Type) .With("supportedChallengeTypes", AcmeProtocol.CHALLENGE_TYPE_DNS); } //var token = (string)cp["token"]; var token = cp.Token; // This response calculation is described in: // https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.5 var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, token); var keyAuthzDig = JwsHelper.ComputeKeyAuthorizationDigest(signer, token); var ca = new DnsChallengeAnswer { KeyAuthorization = keyAuthz, }; var c = new DnsChallenge(cp.Type, ca) { Token = token, RecordName = $"{AcmeProtocol.DNS_CHALLENGE_NAMEPREFIX}{ip.Value}", RecordValue = keyAuthzDig, }; return(c); }
public Challenge Decode(IdentifierPart ip, ChallengePart cp, ISigner signer) { if (cp.Type != AcmeProtocol.CHALLENGE_TYPE_DNS) throw new InvalidDataException("unsupported Challenge type") .With("challengeType", cp.Type) .With("supportedChallengeTypes", AcmeProtocol.CHALLENGE_TYPE_DNS); //var token = (string)cp["token"]; var token = cp.Token; // This response calculation is described in: // https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.5 var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, token); var keyAuthzDig = JwsHelper.ComputeKeyAuthorizationDigest(signer, token); var ca = new DnsChallengeAnswer { KeyAuthorization = keyAuthz, }; var c = new DnsChallenge(cp.Type, ca) { Token = token, RecordName = $"{AcmeProtocol.DNS_CHALLENGE_NAMEPREFIX}{ip.Value}", RecordValue = keyAuthzDig, }; return c; }
DomainDetails GetDomainId(DnsChallenge dnsChallenge, int startIndex) { try { var domainName = dnsChallenge.RecordName.Substring(startIndex); var wr = CreateRequest(managedPath + nameQuery + domainName); using (var response = wr.GetResponse()) { using (var content = new StreamReader(response.GetResponseStream())) { var dr = JsonConvert.DeserializeObject <DomainResponse>(content.ReadToEnd()); return(new DomainDetails() { DomainId = dr.id, DomainName = domainName }); } } } catch (WebException wex) { startIndex = dnsChallenge.RecordName.IndexOf(".", startIndex) + 1; return(GetDomainId(dnsChallenge, startIndex)); } }
public override async Task PersistsChallenge(DnsChallenge dnsChallenge) { List <TxtRecord> records = new List <TxtRecord>() { new TxtRecord() { Value = new[] { dnsChallenge.RecordValue } } }; if ((await dnsClient.RecordSets.ListByTypeAsync(environment.ResourceGroupName, environment.ZoneName, RecordType.TXT)).Any()) { var existingRecords = await SafeGetExistingRecords(dnsChallenge); if (existingRecords != null) { if (existingRecords.TxtRecords.Any(s => s.Value.Contains(dnsChallenge.RecordValue))) { records = existingRecords.TxtRecords.ToList(); } else { records.AddRange(existingRecords.TxtRecords); } } } await this.dnsClient.RecordSets.CreateOrUpdateAsync(this.environment.ResourceGroupName, this.environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT, new RecordSet() { TxtRecords = records, TTL = 60 }); }
public async Task Validate(DnsChallenge challenge) { var challengeConext = (IChallengeContext)challenge.Context; _logger.LogInformation("Wait 2 minutes while dns records apply and let's encrypt can validate challenge"); await Task.Delay(TimeSpan.FromMinutes(2)); var challengeValidation = await challengeConext.Validate(); _logger.LogInformation("Validation status:{status}", challengeValidation?.Status); var validateCount = 1; while (challengeValidation?.Status != ChallengeStatus.Valid && validateCount++ < 30) { await Task.Delay(TimeSpan.FromMinutes(1)); challengeValidation = await challengeConext.Resource(); if (!string.IsNullOrEmpty(challengeValidation?.Error?.Detail)) { _logger.LogWarning("Error detail:{errorsDetail}\nError statusCode:{errorsStatusCode}", challengeValidation.Error?.Detail, challengeValidation.Error?.Status); } _logger.LogInformation("Validation status:{status}", challengeValidation?.Status); } }
public void Handle(Challenge c) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)c; var helper = new CloudFlareHelper(AuthKey, EmailAddress, DomainName); helper.AddOrUpdateDnsRecord(challenge.RecordName, GetCleanedRecordValue(challenge.RecordValue)); }
public override async Task CleanupChallenge(DnsChallenge dnsChallenge) { var existingRecords = await SafeGetExistingRecords(dnsChallenge); var dnsClient = await ArmHelper.GetDnsManagementClient(this.environment); await dnsClient.RecordSets.DeleteAsync(this.environment.ResourceGroupName, this.environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT); }
public void Handle(Challenge c) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)c; var helper = new ClouDNSHelper(AuthId, AuthPassword, DomainName); helper.AddOrUpdateDnsRecord(challenge.RecordName, GetCleanedRecordValue(challenge.RecordValue)); }
public void CleanUp(Challenge c) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)c; var helper = new ClouDNSHelper(AuthId, AuthPassword, DomainName); helper.DeleteDnsRecord(challenge.RecordName); }
public void CleanUp(ChallengeHandlingContext ctx) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)ctx.Challenge; var helper = new OvhHelper(Endpoint, ApplicationKey, ApplicationSecret, ConsumerKey); helper.DeleteDnsRecord(challenge.RecordName); }
public void Handle(ChallengeHandlingContext ctx) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)ctx.Challenge; var helper = new OvhHelper(Endpoint, ApplicationKey, ApplicationSecret, ConsumerKey); helper.AddOrUpdateDnsRecord(challenge.RecordName, GetCleanedRecordValue(challenge.RecordValue)); }
public void CleanUp(Challenge c) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)c; var helper = new CloudFlareHelper(AuthKey, EmailAddress, DomainName); helper.DeleteDnsRecord(challenge.RecordName); }
public void TestHandlerDefineAndCleanUpResourceRecord() { var r = new Random(); var bn = new byte[4]; var bv = new byte[10]; r.NextBytes(bn); r.NextBytes(bv); var rn = BitConverter.ToString(bn); var rv = BitConverter.ToString(bv); var c = new DnsChallenge(AcmeProtocol.CHALLENGE_TYPE_DNS, new DnsChallengeAnswer()) { Token = "FOOBAR", RecordName = $"{rn}.{_handlerParams.DefaultDomain}", RecordValue = rv, }; var r53 = new Route53Helper { HostedZoneId = _handlerParams.HostedZoneId, }; r53.CommonParams.InitParams(_handlerParams); var p = GetProvider(); using (var h = p.GetHandler(c, _handlerParams)) { // Assert test record does *not* exist var rr = r53.GetRecords(c.RecordName); var rrFirst = rr.ResourceRecordSets.FirstOrDefault(x => x.Name.ToLower().StartsWith(c.RecordName.ToLower()))?.Name; Assert.IsNull(rrFirst); // Create the record... h.Handle(c); // ...and assert it does exist rr = r53.GetRecords(c.RecordName); rrFirst = rr.ResourceRecordSets.FirstOrDefault(x => x.Name.ToLower().StartsWith(c.RecordName.ToLower()))?.Name; Assert.IsNotNull(rrFirst); StringAssert.StartsWith(rrFirst.ToLower(), c.RecordName.ToLower()); // Clean up the record... h.CleanUp(c); // ...and assert it does not exist once more rr = r53.GetRecords(c.RecordName); rrFirst = rr.ResourceRecordSets.FirstOrDefault(x => x.Name.ToLower().StartsWith(c.RecordName.ToLower()))?.Name; Assert.IsNull(rrFirst); } }
static string AddRecordToDNS(IDnsProvider dnsProvider, DnsChallenge dnsChallenge) { var dnsName = dnsChallenge.RecordName; var dnsValue = Regex.Replace(dnsChallenge.RecordValue, "\\s", ""); return(dnsProvider.AddTxtRecord(dnsName, dnsValue)); // todo: resolve the txt record }
public void CleanUp(ChallengeHandlingContext ctx) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)ctx.Challenge; var helper = new CloudFlareHelper(AuthKey, EmailAddress, DomainName); helper.DeleteDnsRecord(challenge.RecordName); ctx.Out.WriteLine("DNS record deleted of type [TXT] with name [{0}]", challenge.RecordName); }
public void Handle(ChallengeHandlingContext ctx) { AssertNotDisposed(); DnsChallenge challenge = (DnsChallenge)ctx.Challenge; var helper = new CloudFlareHelper(AuthKey, EmailAddress, DomainName); helper.AddOrUpdateDnsRecord(challenge.RecordName, GetCleanedRecordValue(challenge.RecordValue)); ctx.Out.WriteLine("DNS record created of type [TXT] with name [{0}]", challenge.RecordName); }
public void Handle(ChallengeHandlingContext ctx) { DnsChallenge dnsChallenge = ctx.Challenge as DnsChallenge; ManagementScope mgmtScope = new ManagementScope(@"\\.\Root\MicrosoftDNS"); ManagementClass mgmtClass = null; ManagementBaseObject mgmtParams = null; ManagementObjectSearcher mgmtSearch = null; ManagementObjectCollection mgmtDNSRecords = null; string strQuery; strQuery = string.Format("SELECT * FROM MicrosoftDNS_TXTType WHERE OwnerName = '{0}'", dnsChallenge.RecordName); mgmtScope.Connect(); mgmtSearch = new ManagementObjectSearcher(mgmtScope, new ObjectQuery(strQuery)); mgmtDNSRecords = mgmtSearch.Get(); if (mgmtDNSRecords.Count == 1) { foreach (ManagementObject mgmtDNSRecord in mgmtDNSRecords) { mgmtParams = mgmtDNSRecord.GetMethodParameters("modify"); mgmtParams["DescriptiveText"] = dnsChallenge.RecordValue; mgmtDNSRecord.InvokeMethod("modify", mgmtParams, null); break; } ctx.Out.WriteLine("Updated DNS record of type [TXT] with name [{0}]", dnsChallenge.RecordName); } else if (mgmtDNSRecords.Count == 0) { mgmtClass = new ManagementClass(mgmtScope, new ManagementPath("MicrosoftDNS_TXTType"), null); mgmtParams = mgmtClass.GetMethodParameters("CreateInstanceFromPropertyData"); mgmtParams["DnsServerName"] = Environment.MachineName; mgmtParams["ContainerName"] = dnsChallenge.RecordName.Split('.')[dnsChallenge.RecordName.Split('.').Count() - 2] + "." + dnsChallenge.RecordName.Split('.')[dnsChallenge.RecordName.Split('.').Count() - 1]; mgmtParams["OwnerName"] = dnsChallenge.RecordName; mgmtParams["DescriptiveText"] = dnsChallenge.RecordValue; mgmtClass.InvokeMethod("CreateInstanceFromPropertyData", mgmtParams, null); ctx.Out.WriteLine("Created DNS record of type [TXT] with name [{0}]", dnsChallenge.RecordName); } else { throw new InvalidOperationException("There should not be more than one DNS txt record for the name."); } }
public async Task AddChallenge(DnsChallenge challenge, string zoneName) { _logger.LogInformation("Add txt record '{txtName} to zone {zoneName}'", challenge.Name, zoneName); await Task.Delay(TimeSpan.FromSeconds(10)); await GoToZoneDns(zoneName); await Task.Delay(TimeSpan.FromSeconds(6)); await AddTxtRecord(NormalizeDnsName(challenge.Name, zoneName), challenge.Value); }
private void CleanUp(DnsChallenge dnsChallenge, DomainDetails domainDetails, string records) { string recordId = GetRecordId(dnsChallenge, domainDetails, records); if (!string.IsNullOrEmpty(recordId)) { var wr = CreateRequest(records + "/" + recordId); wr.Method = "DELETE"; using (var response = wr.GetResponse()) { } } }
private async Task <RecordSet> SafeGetExistingRecords(DnsChallenge dnsChallenge) { try { return(await dnsClient.RecordSets.GetAsync(environment.ResourceGroupName, environment.ZoneName, GetRelativeRecordSetName(dnsChallenge), RecordType.TXT)); } catch (CloudException cex) { if (!cex.Message.StartsWith("The resource record '_acme-challenge")) { throw; } } return(null); }
/// <summary> /// Gets challenges used to verify domain ownership. /// </summary> /// <param name="account">Existing account.</param> /// <param name="acmeCertificateFulfillmentPromise">The certificate fulfillment promise retrieved from the RequestCertificate call.</param> /// <param name="challengeType">The challenge type expected back.</param> /// <returns>Challenge used to verify domain ownership</returns> /// <remarks>If requesting a challenge for a wildcard domain, only dns challenge is supported.</remarks> /// <exception cref="NotSupportedException">If the challenge type is not supported.</exception> /// <exception cref="AcmeProtocolException">On all other Acme related exceptions</exception> public async Task <ChallengeCollection> GetChallengesAsync(AcmeAccount account, AcmeCertificateFulfillmentPromise acmeCertificateFulfillmentPromise, ChallengeType challengeType) { var response = await _acmeApi.GetChallengesAsync(acmeCertificateFulfillmentPromise); var errorResponse = response.Where(t => t.Status == AcmeApiResponseStatus.Error); if (errorResponse.Any()) { throw new AcmeProtocolException(string.Join(" | ", errorResponse.Select(t => t.Message))); } ChallengeCollection challenges = new ChallengeCollection(); foreach (var resp in response) { AcmeChallenge sChallenge = resp.Data.Challenges.FirstOrDefault(t => t.Type.Equals(challengeType.Value)); if (sChallenge == null) { throw new NotSupportedException($"{challengeType.Value} challenge type not supported in this context."); } IAcmeChallengeContent challengeContent = null; switch (challengeType.Value) { case ProtoacmeContants.CHALLENGE_HTTP: challengeContent = new HttpChallenge(account, sChallenge, resp.Data.Identifier?.Value); challenges.Add(challengeContent); break; case ProtoacmeContants.CHALLENGE_DNS: challengeContent = new DnsChallenge(account, sChallenge, resp.Data.Identifier?.Value); challenges.Add(challengeContent); break; case ProtoacmeContants.CHALLENGE_TLS: challengeContent = new TlsChallenge(account, sChallenge, resp.Data.Identifier?.Value); challenges.Add(challengeContent); break; default: break; } } return(challenges); }
private void EditDns(DnsChallenge dnsChallenge, bool delete, TextWriter msg) { var dnsName = dnsChallenge.RecordName; var dnsValue = Regex.Replace(dnsChallenge.RecordValue, "\\s", ""); var dnsValues = Regex.Replace(dnsValue, "(.{100,100})", "$1\n").Split('\n'); var r53 = new Route53Helper { HostedZoneId = HostedZoneId, ResourceRecordTtl = ResourceRecordTtl, CommonParams = CommonParams, }; if (ResourceRecordType == "TXT") { r53.EditTxtRecord(dnsName, dnsValues, delete); } else { throw new NotImplementedException($"RR type of [{ResourceRecordType}] not implemented"); } }
public async Task AddChallenge(DnsChallenge challenge, string zoneName) { var zones = await Retry.Do(async() => await _client.GetZones(), RetryCount, RetryDelay); var neededZone = zones.FirstOrDefault(z => z.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase)); if (neededZone == null) { throw new Exception($"Zone ({zoneName}) not found"); } var dnsRecord = new DnsRecord(NormalizeDnsName(challenge.Name, zoneName), DnsRecordType.TXT) { Ttl = 3600, Value = challenge.Value }; await Retry.Do(async() => await _client.AddDnsRecord(neededZone, dnsRecord), RetryCount, RetryDelay); _logger.LogInformation( "{Date}: Wait 7 minutes because freenom name servers is so buggy. (Waiting for apply dns record)", DateTime.UtcNow); await Task.Delay(TimeSpan.FromMinutes(7)); }
private string GetRecordId(DnsChallenge dnsChallenge, DomainDetails domainDetails, string records) { var wr = CreateRequest(records); wr.Method = "GET"; var recordNameToFind = dnsChallenge.RecordName.Replace("." + domainDetails.DomainName, string.Empty); using (var response = wr.GetResponse()) using (var content = new StreamReader(response.GetResponseStream())) { var resp = content.ReadToEnd(); var respObject = JsonConvert.DeserializeObject <DomainResponseCollection>(resp).data; var record = respObject.FirstOrDefault(a => a.name == recordNameToFind); if (record != null) { return(record.id); } } return(null); }
public Task Validate(DnsChallenge challenge) { _logger.LogInformation($"NoOp validate`{challenge.Name}:{challenge.Value}`"); return(Task.Delay(TimeSpan.FromSeconds(15))); }
public Task AddChallenge(DnsChallenge challenge, string zoneName) { _logger.LogInformation($"NoOp challenge `{challenge.Name}:{zoneName}`"); return(Task.FromResult("")); }
public void Handle(ChallengeHandlingContext ctx) { if (string.IsNullOrEmpty(SecretId)) { throw new ArgumentNullException(nameof(SecretId)); } if (string.IsNullOrEmpty(SecretKey)) { throw new ArgumentNullException(nameof(SecretKey)); } if (string.IsNullOrEmpty(Line)) { throw new ArgumentNullException(nameof(Line)); } DnsChallenge dnsChallenge = (DnsChallenge)ctx.Challenge; var cns = new QCloudAPI_SDK.Module.Cns(); cns.setConfig(new SortedDictionary <string, object>(StringComparer.Ordinal) { { "SecretId", SecretId }, { "SecretKey", SecretKey }, { "RequestMethod", "GET" } }); var recordName = dnsChallenge.RecordName; var topAndSecondLevelNameBuilder = new StringBuilder(); var subDomainNameBuilder = new StringBuilder(); byte level = 0; for (int i = recordName.Length - 1; i >= 0; i--) { if (recordName[i] == '.' && level < 2) { level++; } if (level < 2) { topAndSecondLevelNameBuilder.Insert(0, recordName[i]); } else { subDomainNameBuilder.Insert(0, recordName[i]); } } var topAndSecondLevelName = topAndSecondLevelNameBuilder.ToString(); var subDomainName = subDomainNameBuilder.ToString(); subDomainName = subDomainName.Substring(0, subDomainName.Length - 1); ctx.Out.WriteLine("Getting domain information for " + recordName + '.'); var recordListResponse = (JObject)JsonConvert.DeserializeObject(cns.Call("RecordList", new SortedDictionary <string, object>(StringComparer.Ordinal) { { "domain", topAndSecondLevelName }, { "offset", 0 }, { "length", 100 }, { "subDomain", subDomainName }, { "recordType", "TXT" }, })); ThrowQCloudError(recordListResponse); var record = (JArray)recordListResponse["data"]["records"]; ctx.Out.WriteLine(record.Count + " existing record for " + recordName + " is found."); if (record.Count == 1) { //If record already exist. ctx.Out.WriteLine("Adding new record " + subDomainName + " in domain " + topAndSecondLevelName + "."); var domainListResponse = (JObject)JsonConvert.DeserializeObject(cns.Call("RecordModify", new SortedDictionary <string, object>(StringComparer.Ordinal) { { "domain", topAndSecondLevelName }, { "recordId", (int)record[0]["id"] }, { "subDomain", subDomainName }, { "recordType", "TXT" }, { "recordLine", (string)record[0]["line"] }, { "value", dnsChallenge.RecordValue }, })); ThrowQCloudError(domainListResponse); ctx.Out.WriteLine("Updated DNS record of type [TXT] with name [{0}]", dnsChallenge.RecordName); } else if (record.Count == 0) { //If record does not exist. ctx.Out.WriteLine("Updating record " + subDomainName + " in domain " + topAndSecondLevelName + "."); var domainListResponse = (JObject)JsonConvert.DeserializeObject(cns.Call("RecordCreate", new SortedDictionary <string, object>(StringComparer.Ordinal) { { "domain", topAndSecondLevelName }, { "subDomain", subDomainName }, { "recordType", "TXT" }, { "recordLine", Line }, { "value", dnsChallenge.RecordValue }, })); ThrowQCloudError(domainListResponse); ctx.Out.WriteLine("Created DNS record of type [TXT] with name [{0}]", dnsChallenge.RecordName); } else { throw new InvalidOperationException("There should not be more than one DNS txt record for the name."); } }
DomainDetails GetDomainId(DnsChallenge dnsChallenge) { var startIndex = dnsChallenge.RecordName.IndexOf(".") + 1; return(GetDomainId(dnsChallenge, startIndex)); }
private Task CleanupChallenge(DnsChallenge httpChallenge) { throw new NotImplementedException(); }
private Task PersistsChallenge(DnsChallenge httpChallenge) { throw new NotImplementedException(); }
private string GetRelativeRecordSetName(DnsChallenge dnsChallenge) { return(dnsChallenge.RecordName.Replace($".{this.environment.ZoneName}", "")); }