Beispiel #1
0
        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));
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        public void Handle(Challenge c)
        {
            AssertNotDisposed();
            DnsChallenge challenge = (DnsChallenge)c;
            var          helper    = new ClouDNSHelper(AuthId, AuthPassword, DomainName);

            helper.AddOrUpdateDnsRecord(challenge.RecordName, GetCleanedRecordValue(challenge.RecordValue));
        }
Beispiel #9
0
        public void CleanUp(Challenge c)
        {
            AssertNotDisposed();
            DnsChallenge challenge = (DnsChallenge)c;
            var          helper    = new ClouDNSHelper(AuthId, AuthPassword, DomainName);

            helper.DeleteDnsRecord(challenge.RecordName);
        }
Beispiel #10
0
        public void CleanUp(ChallengeHandlingContext ctx)
        {
            AssertNotDisposed();
            DnsChallenge challenge = (DnsChallenge)ctx.Challenge;
            var          helper    = new OvhHelper(Endpoint, ApplicationKey, ApplicationSecret, ConsumerKey);

            helper.DeleteDnsRecord(challenge.RecordName);
        }
Beispiel #11
0
        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
        }
Beispiel #15
0
        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);
        }
Beispiel #16
0
        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.");
            }
        }
Beispiel #18
0
        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);
 }
Beispiel #21
0
        /// <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);
        }
Beispiel #25
0
 public Task Validate(DnsChallenge challenge)
 {
     _logger.LogInformation($"NoOp validate`{challenge.Name}:{challenge.Value}`");
     return(Task.Delay(TimeSpan.FromSeconds(15)));
 }
Beispiel #26
0
 public Task AddChallenge(DnsChallenge challenge, string zoneName)
 {
     _logger.LogInformation($"NoOp challenge `{challenge.Name}:{zoneName}`");
     return(Task.FromResult(""));
 }
Beispiel #27
0
        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));
        }
Beispiel #29
0
 private Task CleanupChallenge(DnsChallenge httpChallenge)
 {
     throw new NotImplementedException();
 }
Beispiel #30
0
 private Task PersistsChallenge(DnsChallenge httpChallenge)
 {
     throw new NotImplementedException();
 }
 private string GetRelativeRecordSetName(DnsChallenge dnsChallenge)
 {
     return(dnsChallenge.RecordName.Replace($".{this.environment.ZoneName}", ""));
 }