Example #1
0
        public override async Task <bool> CreateRecord(DnsValidationRecord record)
        {
            try
            {
                var recordName    = record.Authority.Domain;
                var token         = record.Value;
                var hostedZoneIds = await GetHostedZoneIds(recordName);

                if (hostedZoneIds == null)
                {
                    return(false);
                }
                _log.Information("Creating TXT record {recordName} with value {token}", recordName, token);
                var updateTasks = hostedZoneIds.Select(hostedZoneId =>
                                                       _route53Client.ChangeResourceRecordSetsAsync(
                                                           new ChangeResourceRecordSetsRequest(
                                                               hostedZoneId,
                                                               new ChangeBatch(new List <Change> {
                    new Change(
                        ChangeAction.UPSERT,
                        CreateResourceRecordSet(recordName, token))
                }))));
                var results = await Task.WhenAll(updateTasks);

                var propagationTasks = results.Select(result => WaitChangesPropagation(result.ChangeInfo));
                await Task.WhenAll(propagationTasks);

                return(true);
            }
            catch (Exception ex)
            {
                _log.Warning($"Error creating TXT record: {ex.Message}");
                return(false);
            }
        }
Example #2
0
        public override async Task <bool> CreateRecord(string recordName, string token)
        {
            try
            {
                var hostedZoneId = await GetHostedZoneId(recordName);

                if (hostedZoneId == null)
                {
                    return(false);
                }
                _log.Information("Creating TXT record {recordName} with value {token}", recordName, token);
                var response = await _route53Client.ChangeResourceRecordSetsAsync(
                    new ChangeResourceRecordSetsRequest(
                        hostedZoneId,
                        new ChangeBatch(new List <Change> {
                    new Change(
                        ChangeAction.UPSERT,
                        CreateResourceRecordSet(recordName, token))
                })));
                await WaitChangesPropagation(response.ChangeInfo);

                return(true);
            }
            catch (Exception ex)
            {
                _log.Warning($"Error creating TXT record: {ex.Message}");
                return(false);
            }
        }
Example #3
0
        public override async Task CreateRecord(string recordName, string token)
        {
            var hostedZoneId = await GetHostedZoneId(recordName);

            _log.Information("Creating TXT record {recordName} with value {token}", recordName, token);
            var response = await _route53Client.ChangeResourceRecordSetsAsync(
                new ChangeResourceRecordSetsRequest(
                    hostedZoneId,
                    new ChangeBatch(new List <Change> {
                new Change(
                    ChangeAction.UPSERT,
                    CreateResourceRecordSet(recordName, token))
            })));

            await WaitChangesPropagation(response.ChangeInfo);
        }
Example #4
0
        /// <summary>
        /// Creates a record set if none exists, else updates an existing record set of type A, assigning the supplied public ip address to the supplied domain
        /// </summary>
        /// <param name="hostedZoneId">the hosted zone id to create or update a recordset for</param>
        /// <param name="domainName">the domain to assign the public ip to</param>
        /// <param name="publicIp">the public ip to assign to the supplied domain</param>
        /// <returns>the change request id and </returns>
        public async Task <(string, string)> UpsertRecordset(string hostedZoneId, string domainName, string publicIp)
        {
            // Create recordset change request
            var recordsetRequest = new ChangeResourceRecordSetsRequest(hostedZoneId, new ChangeBatch(new List <Change>()
            {
                new Change
                {
                    ResourceRecordSet = new ResourceRecordSet(domainName, RRType.A)
                    {
                        TTL             = 60,
                        ResourceRecords = new List <ResourceRecord>()
                        {
                            new ResourceRecord(publicIp)
                        }
                    },
                    Action = ChangeAction.UPSERT
                }
            }));

            // Reqeust change
            var recordsetResponse = await _route53Client.ChangeResourceRecordSetsAsync(recordsetRequest);

            if (recordsetResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
            {
                return(recordsetResponse.ChangeInfo.Id, recordsetResponse.ChangeInfo.Status);
            }

            else
            {
                throw new System.Exception($"Failed to Upsert recordset, ChangeResourceRecordSetsRequest status: {recordsetResponse.HttpStatusCode}");
            }
        }
Example #5
0
        public async Task HandleInitR53(Instance inst, Dictionary <string, string> tags)
        {
            _logger.LogInformation("Handling CREATING R53 records");

            var r53Spec    = ResolveR53Spec(inst, tags);
            var r53Routing = ResolveR53RoutingSpec(inst, tags);

            var rrset = new ResourceRecordSet
            {
                Name            = r53Spec.Name,
                Type            = RRType.FindValue(r53Spec.Type),
                TTL             = r53Spec.TTL,
                ResourceRecords = new List <ResourceRecord>
                {
                    new ResourceRecord(r53Spec.Value),
                },
            };

            // Optional routing policy configuration
            r53Routing?.Apply(rrset);

            var changeRequ = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = r53Spec.Zone,
                ChangeBatch  = new ChangeBatch
                {
                    Changes = new List <Change>
                    {
                        new Change
                        {
                            Action            = ChangeAction.UPSERT,
                            ResourceRecordSet = rrset,
                        }
                    }
                }
            };

            var changeResp = await _r53.ChangeResourceRecordSetsAsync(changeRequ);

            _logger.LogInformation("UPSERT request completed, response:");
            _logger.LogInformation(JsonSerializer.Serialize(changeResp));
        }
Example #6
0
        public async Task UpdateSingleAsync(string ipAddress, HostedDomainInfo domain)
        {
            var currentExternalIp = ipAddress;

            // Ensure the domain name doesn't end with a dot. This is how they are returned from AWS API but they will be trimmed
            // as well so that they match
            var domainName = domain.DomainName;

            domainName = domainName.TrimEnd(".".ToCharArray());

            var zoneId = domain.ZoneId;

            var listResourceRecordSetsResponse = await amazonClient.ListResourceRecordSetsAsync(new ListResourceRecordSetsRequest()
            {
                HostedZoneId = zoneId
            });

            if (listResourceRecordSetsResponse.ResourceRecordSets.Count == 0)
            {
                throw new InvalidOperationException($"Could not find any ResourceRecordSets for zone: {zoneId}");
            }

            var resourceRecordSet = listResourceRecordSetsResponse.ResourceRecordSets.FirstOrDefault(recordset => recordset.Name.TrimEnd(".".ToCharArray()) == domainName);

            if (resourceRecordSet == null)
            {
                throw new InvalidOperationException($"Could not find any resource record set for domain: {domainName}");
            }

            var resourceRecord = resourceRecordSet.ResourceRecords.FirstOrDefault();

            if (resourceRecord == null)
            {
                throw new InvalidOperationException($"Could not find any resouce record in the set");
            }

            if (resourceRecord.Value != currentExternalIp)
            {
                resourceRecord.Value = currentExternalIp;

                await amazonClient.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest()
                {
                    HostedZoneId = zoneId,
                    ChangeBatch  = new ChangeBatch()
                    {
                        Changes = new List <Change>()
                        {
                            new Change(ChangeAction.UPSERT, resourceRecordSet)
                        }
                    }
                });
            }
        }
Example #7
0
        private async Task UpdateRoute53(HostedZone zone, Instance instance, ILambdaContext context)
        {
            if (zone == null)
            {
                return;
            }

            string hostName = instance.Tags.GetTag("HostName");

            if (string.IsNullOrWhiteSpace(hostName))
            {
                context.Logger.LogLine($"Hostname missing!");
                return;
            }

            string ipAddress = instance.PublicIpAddress;

            if (string.IsNullOrWhiteSpace(ipAddress))
            {
                context.Logger.LogLine($"ipAddress missing!");
                return;
            }

            context.Logger.LogLine($"Update Zone: {zone.Name} host: {hostName} with IpAddress: {ipAddress}");

            ChangeResourceRecordSetsRequest request = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = zone.Id,
                ChangeBatch  = new ChangeBatch
                {
                    Changes = new List <Change>
                    {
                        new Change
                        {
                            Action            = ChangeAction.UPSERT,
                            ResourceRecordSet = new ResourceRecordSet
                            {
                                Name            = $"{hostName}.{zone.Name}",
                                Type            = "A",
                                ResourceRecords = new List <ResourceRecord>
                                {
                                    new ResourceRecord {
                                        Value = ipAddress
                                    }
                                },
                                TTL = 60,
                            }
                        }
                    }
                }
            };
            await _amazonRoute53Client.ChangeResourceRecordSetsAsync(request);
        }
        internal void Update(string hostname, IPAddress ipAddress)
        {
            var lhzTask = route53Client.ListHostedZonesAsync();

            ResourceRecord    resourceRecord    = new ResourceRecord(ipAddress.ToString());
            ResourceRecordSet resourceRecordSet = new ResourceRecordSet
            {
                Name            = hostname,
                TTL             = 30,
                Type            = RRType.A,
                ResourceRecords = new List <ResourceRecord> {
                    resourceRecord
                }
            };
            Change change = new Change
            {
                Action            = ChangeAction.UPSERT,
                ResourceRecordSet = resourceRecordSet
            };
            ChangeBatch changeBatch = new ChangeBatch
            {
                Changes = new List <Change> {
                    change
                },
                Comment = DateTime.UtcNow.ToString("s", System.Globalization.CultureInfo.InvariantCulture)
            };

            // This is wrong and does The Wrong Thing, I'm fairly certain
            string domain       = DomainName.Parse(hostname).Domain;
            var    zones        = lhzTask.Result.HostedZones;
            string hostedZoneId = zones.Single(z => z.Name.StartsWith(domain)).Id;

            ChangeResourceRecordSetsRequest request = new ChangeResourceRecordSetsRequest
            {
                ChangeBatch  = changeBatch,
                HostedZoneId = hostedZoneId
            };

            var crrsTask = route53Client.ChangeResourceRecordSetsAsync(request);
            var response = crrsTask.Result;
        }
Example #9
0
 private Amazon.Route53.Model.ChangeResourceRecordSetsResponse CallAWSServiceOperation(IAmazonRoute53 client, Amazon.Route53.Model.ChangeResourceRecordSetsRequest request)
 {
     Utils.Common.WriteVerboseEndpointMessage(this, client.Config, "Amazon Route 53", "ChangeResourceRecordSets");
     try
     {
         #if DESKTOP
         return(client.ChangeResourceRecordSets(request));
         #elif CORECLR
         return(client.ChangeResourceRecordSetsAsync(request).GetAwaiter().GetResult());
         #else
                 #error "Unknown build edition"
         #endif
     }
     catch (AmazonServiceException exc)
     {
         var webException = exc.InnerException as System.Net.WebException;
         if (webException != null)
         {
             throw new Exception(Utils.Common.FormatNameResolutionFailureMessage(client.Config, webException.Message), webException);
         }
         throw;
     }
 }
        protected override async Task DeleteAsync(string recordName, string recordType, string recordValue)
        {
            _logger.LogDebug("Starting deletion of {RecordType} {RecordName} with value {RecordValue}", recordType, recordName, recordValue);

            var zone = await FindHostedZoneAsync(recordName);

            if (zone == null)
            {
                _logger.LogDebug("No zone was found");
                return;
            }

            if (recordType == TxtRecordType)
            {
                recordValue = NormalizeTxtValue(recordValue);
            }

            var recordSets = await FindRecordSetsAsync(zone, recordName, recordType);

            var changeBatch = new ChangeBatch();

            if (!recordSets.Any())
            {
                _logger.LogInformation("No DNS records matching {RecordType} {RecordName} were found to delete in zone {Zone}", recordType, recordName, zone.Name);
                return;
            }

            foreach (var recordSet in recordSets)
            {
                if (!recordSet.ResourceRecords.Any(x => x.Value != recordValue))
                {
                    _logger.LogDebug("Will delete record {RecordType} {RecordName}", recordType, recordName);
                    changeBatch.Changes.Add(new Change()
                    {
                        Action = ChangeAction.DELETE, ResourceRecordSet = recordSet
                    });
                }
                else
                {
                    recordSet.ResourceRecords.RemoveAll(x => x.Value == recordValue);

                    _logger.LogDebug("Will retain remaining answers for {RecordType} {RecordName}", recordType, recordName);
                    changeBatch.Changes.Add(new Change()
                    {
                        Action = ChangeAction.UPSERT, ResourceRecordSet = recordSet
                    });
                }
            }

            _logger.LogInformation("Deleting or modifying {Count} DNS records matching {RecordType} {RecordName} with value {RecordValue} in zone {Zone}", changeBatch.Changes.Count, recordType, recordName, recordValue, zone.Name);

            var deleteResponse = await _route53Client.ChangeResourceRecordSetsAsync(
                new ChangeResourceRecordSetsRequest()
            {
                ChangeBatch  = changeBatch,
                HostedZoneId = zone.Id
            });

            var changeRequest = new GetChangeRequest
            {
                Id = deleteResponse.ChangeInfo.Id
            };

            while ((await _route53Client.GetChangeAsync(changeRequest)).ChangeInfo.Status == ChangeStatus.PENDING)
            {
                _logger.LogDebug("Deletion or modification of {RecordType} {RecordName} is pending. Checking for status update in {StatusPollIntervalSeconds} seconds.", recordType, recordName, StatusPollIntervalSeconds);
                Thread.Sleep(TimeSpan.FromSeconds(StatusPollIntervalSeconds));
            }
        }
        public async Task <Response> Delete()
        {
            var props = Request.ResourceProperties;
            IAmazonCertificateManager acmClient = await acmFactory.Create(props.CreationRoleArn);

            IAmazonRoute53 route53Client = await route53Factory.Create(props.ValidationRoleArn);

            var describeResponse = await acmClient.DescribeCertificateAsync(new DescribeCertificateRequest
            {
                CertificateArn = Request.PhysicalResourceId,
            });

            Console.WriteLine($"Got describe certificate response: {JsonSerializer.Serialize(describeResponse)}");

            var names   = new HashSet <string>();
            var changes = new List <Change>();

            foreach (var option in describeResponse.Certificate.DomainValidationOptions)
            {
                var query = from name in names where name == option.ResourceRecord.Name select name;

                if (query.Count() != 0)
                {
                    continue;
                }

                names.Add(option.ResourceRecord.Name);
                changes.Add(new Change
                {
                    Action            = ChangeAction.DELETE,
                    ResourceRecordSet = new ResourceRecordSet
                    {
                        Name            = option.ResourceRecord.Name,
                        Type            = new RRType(option.ResourceRecord.Type.Value),
                        SetIdentifier   = Request.PhysicalResourceId,
                        Weight          = 1,
                        TTL             = 60,
                        ResourceRecords = new List <ResourceRecord> {
                            new ResourceRecord {
                                Value = option.ResourceRecord.Value
                            }
                        }
                    }
                });
            }

            if (changes.Count() != 0)
            {
                try
                {
                    var roleArn = Request.ResourceProperties.ValidationRoleArn;
                    var changeRecordsResponse = await route53Client.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest
                    {
                        HostedZoneId = Request.ResourceProperties.HostedZoneId,
                        ChangeBatch  = new ChangeBatch
                        {
                            Changes = changes
                        }
                    });

                    Console.WriteLine($"Got delete record response: {JsonSerializer.Serialize(changeRecordsResponse)}");
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Error deleting old resource records: {e.Message} {e.StackTrace}");
                }
            }

            var deleteResponse = await acmClient.DeleteCertificateAsync(new DeleteCertificateRequest
            {
                CertificateArn = Request.PhysicalResourceId,
            });

            Console.WriteLine($"Received delete certificate response: {JsonSerializer.Serialize(deleteResponse)}");

            return(new Response
            {
                PhysicalResourceId = Request.PhysicalResourceId
            });
        }
        public async Task <Response?> Create()
        {
            var props = Request.ResourceProperties;

            IAmazonCertificateManager acmClient = await acmFactory.Create(props.CreationRoleArn);

            IAmazonRoute53 route53Client = await route53Factory.Create(props.ValidationRoleArn);

            var request = new RequestCertificateRequest
            {
                DomainName       = props.DomainName,
                ValidationMethod = props.ValidationMethod
            };

            if (props.CertificateAuthorityArn != null)
            {
                request.CertificateAuthorityArn = props.CertificateAuthorityArn;
            }
            if (props.DomainValidationOptions != null)
            {
                request.DomainValidationOptions = props.DomainValidationOptions;
            }
            if (props.Options != null)
            {
                request.Options = props.Options;
            }
            if (props.SubjectAlternativeNames != null)
            {
                request.SubjectAlternativeNames = props.SubjectAlternativeNames;
            }

            var requestCertificateResponse = await acmClient.RequestCertificateAsync(request);

            Console.WriteLine($"Got Request Certificate Response: {JsonSerializer.Serialize(requestCertificateResponse)}");

            PhysicalResourceId = requestCertificateResponse.CertificateArn;
            var describeCertificateRequest = new DescribeCertificateRequest {
                CertificateArn = PhysicalResourceId
            };
            var tasks = new List <Task>();

            Thread.Sleep(500);
            bool foundValidationOptions = false;
            List <DomainValidation> validationOptions = new List <DomainValidation>();

            // For some reason, the domain validation options aren't immediately populated.
            while (!foundValidationOptions)
            {
                var describeCertificateResponse = await acmClient.DescribeCertificateAsync(describeCertificateRequest);

                Console.WriteLine($"Got Describe Certificate Response: {JsonSerializer.Serialize(describeCertificateResponse)}");

                validationOptions      = describeCertificateResponse.Certificate.DomainValidationOptions;
                foundValidationOptions = true;

                if (validationOptions.Count() == 0)
                {
                    foundValidationOptions = false;
                }

                foreach (var option in validationOptions)
                {
                    if (option.ResourceRecord?.Name == null)
                    {
                        foundValidationOptions = false;
                    }
                }

                Thread.Sleep(1000);
            }

            if (props.Tags != null)
            {
                tasks.Add(Task.Run(async delegate
                {
                    var addTagsResponse = await acmClient.AddTagsToCertificateAsync(new AddTagsToCertificateRequest
                    {
                        Tags           = props.Tags,
                        CertificateArn = PhysicalResourceId,
                    });

                    Console.WriteLine($"Got Add Tags Response: {JsonSerializer.Serialize(addTagsResponse)}");
                }));
            }

            // add DNS validation records if applicable
            var names   = new HashSet <string>();
            var changes = new List <Change>();

            if (props.ValidationMethod == ValidationMethod.DNS)
            {
                foreach (var option in validationOptions)
                {
                    var query = from name in names where name == option.ResourceRecord.Name select name;

                    if (query.Count() != 0)
                    {
                        continue;
                    }

                    names.Add(option.ResourceRecord.Name);
                    changes.Add(new Change
                    {
                        Action            = ChangeAction.UPSERT,
                        ResourceRecordSet = new ResourceRecordSet
                        {
                            Name            = option.ResourceRecord.Name,
                            Type            = new RRType(option.ResourceRecord.Type.Value),
                            SetIdentifier   = PhysicalResourceId,
                            Weight          = 1,
                            TTL             = 60,
                            ResourceRecords = new List <ResourceRecord> {
                                new ResourceRecord {
                                    Value = option.ResourceRecord.Value
                                }
                            }
                        }
                    });
                }

                tasks.Add(
                    Task.Run(async delegate
                {
                    var changeRecordsResponse = await route53Client.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest
                    {
                        HostedZoneId = props.HostedZoneId,
                        ChangeBatch  = new ChangeBatch
                        {
                            Changes = changes
                        }
                    });

                    Console.WriteLine($"Got Change Record Sets Response: {JsonSerializer.Serialize(changeRecordsResponse)}");
                })
                    );
            }

            Task.WaitAll(tasks.ToArray());

            Request.PhysicalResourceId = PhysicalResourceId;
            Request.RequestType        = RequestType.Wait;

            return(await Wait());
        }