Example #1
0
        public async Task <bool> UpdateIpAddressForSubdomain(string hostedZoneId, string fqdn, string newIpAddress)
        {
            using (AmazonRoute53Client route53Client = GetAmazonRoute53Client())
            {
                ListResourceRecordSetsResponse records = await route53Client.ListResourceRecordSetsAsync(new ListResourceRecordSetsRequest(hostedZoneId));

                // Look for an A record matching the FQDN that was passed in
                ResourceRecordSet matchingRecordSet = records?.ResourceRecordSets.FirstOrDefault(prop => prop.Name == fqdn && prop.Type == RRType.A);

                if (matchingRecordSet != null && matchingRecordSet.ResourceRecords.Any())
                {
                    if (matchingRecordSet.ResourceRecords.First().Value != newIpAddress)
                    {
                        matchingRecordSet.ResourceRecords.First().Value = newIpAddress;
                        ChangeBatch change = new ChangeBatch();
                        change.Changes.Add(new Change(ChangeAction.UPSERT, matchingRecordSet));

                        ChangeResourceRecordSetsResponse changeRequest = await route53Client.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest(hostedZoneId, change));

                        Log.Information("[Runtime = {StartTime}] Change request submitted to change subdomain {Subdomain} IP address to {IPAddress}.", Settings.StartTime, fqdn, newIpAddress);

                        return(changeRequest.HttpStatusCode == System.Net.HttpStatusCode.OK);
                    }
                    else
                    {
                        Log.Information("[Runtime = {StartTime}] Subdomain {Subdomain} found, but the IP address was already {IPAddress}.", Settings.StartTime, fqdn, newIpAddress);
                    }
                }

                return(false);
            }
        }
Example #2
0
        public async Task DeleteTxtRecordAsync(DnsZone zone, string relativeRecordName)
        {
            var recordName = $"{relativeRecordName}.{zone.Name}.";

            var listRequest = new ListResourceRecordSetsRequest(zone.Id)
            {
                StartRecordName = recordName,
                StartRecordType = RRType.TXT
            };

            var listResponse = await _amazonRoute53Client.ListResourceRecordSetsAsync(listRequest);

            if (listResponse.ResourceRecordSets.Count == 0)
            {
                return;
            }

            var changes = listResponse.ResourceRecordSets
                          .Select(x => new Change {
                Action = ChangeAction.DELETE, ResourceRecordSet = x
            })
                          .ToList();

            var request = new ChangeResourceRecordSetsRequest(zone.Id, new ChangeBatch(changes));

            await _amazonRoute53Client.ChangeResourceRecordSetsAsync(request);
        }
Example #3
0
        /// <summary>
        /// To retrieve a list of record sets for a particular hosted zone.
        /// </summary>
        /// <param name="hostedZoneId">The ID of the hosted zone whos record sets you want to list</param>
        /// <param name="settings">The <see cref="Route53Settings"/> required to connect to Route53.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        public async Task <IList <ResourceRecordSet> > GetResourceRecordSets(string hostedZoneId, Route53Settings settings, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (String.IsNullOrEmpty(hostedZoneId))
            {
                throw new ArgumentNullException("hostedZoneId");
            }



            ListResourceRecordSetsRequest request = new ListResourceRecordSetsRequest(hostedZoneId);

            AmazonRoute53Client            client   = this.GetClient(settings);
            ListResourceRecordSetsResponse response = await client.ListResourceRecordSetsAsync(request);

            if (response.HttpStatusCode == HttpStatusCode.OK)
            {
                _Log.Verbose("Listing record sets");
                return(response.ResourceRecordSets);
            }
            else
            {
                _Log.Error("Could not list record sets");
                return(null);
            }
        }
Example #4
0
        public static void ListHostNames()
        {
            if (!quiet)
            {
                Console.WriteLine($"Domain = {domain}");
            }
            string zoneId = FindHostedZoneID(domain);

            if (zoneId == null)
            {
                throw new Exception("Zone not found");
            }
            bool   more  = true;
            bool   first = true;
            string start = "";

            while (more)
            {
                ListResourceRecordSetsRequest r = new ListResourceRecordSetsRequest(zoneId);
                r.MaxItems = "150";
                if (!first)
                {
                    r.StartRecordName = start;
                }
                first = false;
                ListResourceRecordSetsResponse res = c.ListResourceRecordSetsAsync(r).Result;
                foreach (var s in res.ResourceRecordSets)
                {
                    string firstVal = (s.ResourceRecords != null && s.ResourceRecords.Count > 0) ? s.ResourceRecords[0].Value : "";
                    Console.WriteLine($"{s.Name.PadRight(60)}" + (quiet ? "" : $"\t{s.Type}\t{firstVal}"));

                    /*
                     * if (!quiet)
                     * {
                     *      foreach(var rr in s.ResourceRecords)
                     *      {
                     *              Console.WriteLine($"\t{rr.Value}");
                     *      }
                     * }
                     */
                }
                more = res.IsTruncated;
                if (more)
                {
                    start = res.NextRecordName;
                }
            }
        }
Example #5
0
        private static async Task <ResourceRecordSet> GetResourceRecordSet(string hostedZoneID, string domainName)
        {
            try
            {
                var listResourceRecordSetsRequest = new ListResourceRecordSetsRequest(hostedZoneID);

                var listResourceRecordSetsResponse = await client.ListResourceRecordSetsAsync(listResourceRecordSetsRequest);

                return(listResourceRecordSetsResponse.ResourceRecordSets.SingleOrDefault(x => x.Name == domainName && x.Type == RRType.A));
            }
            catch (Exception exception)
            {
                logger.Error(exception, "");
                throw;
            }
        }
Example #6
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="hostzoneId">Sample: /hostedzone/ZW9CIJG5IAQYD</param>
        /// <param name="resourceRecordSetName">Sample api.infra.safearrival.testschoolmessenger.com.</param>
        /// <returns>Example: infra-Admin-ALB-747523918.us-east-2.elb.amazonaws.com (Regular)Or
        /// admin.infra.safearrival.testschoolmessenger.com.s3-website.us-east-2.amazonaws.com (Maintenance)</returns>
        public async Task <List <string> > GetRecorSetValues(string hostzoneId, List <string> resourceRecordSetNames)
        {
            var ret     = new List <string>();
            var request = new ListResourceRecordSetsRequest()
            {
                HostedZoneId = hostzoneId,
                //StartRecordName = resourceRecordSetName
            };
            var response = await client.ListResourceRecordSetsAsync(request);

            foreach (var name in resourceRecordSetNames)
            {
                var recordSet = response.ResourceRecordSets.Find(o => o.Name == name);
                if (recordSet != null && recordSet.ResourceRecords.Count > 0)
                {
                    ret.Add(recordSet.ResourceRecords[0].Value);
                }
            }
            return(ret);
        }
Example #7
0
        private async static Task <bool> UpdateIpAddressForSubdomain(string hostedZoneId, string fqdn, string newIpAddress)
        {
            AmazonRoute53Config config = new AmazonRoute53Config()
            {
                RegionEndpoint = RegionEndpoint.GetBySystemName(Settings.AWSRegion) // TODO: inject
            };

            using (AmazonRoute53Client route53Client = new AmazonRoute53Client(Settings.AWSAccessKeyId, Settings.AWSAccessKeySecret, config))
            {
                ListResourceRecordSetsResponse records = await route53Client.ListResourceRecordSetsAsync(new ListResourceRecordSetsRequest(hostedZoneId));

                ResourceRecordSet matchingRecordSet = records?.ResourceRecordSets.FirstOrDefault(prop => prop.Name == fqdn && prop.Type == RRType.A);

                if (matchingRecordSet != null && matchingRecordSet.ResourceRecords.FirstOrDefault() != null)
                {
                    if (matchingRecordSet.ResourceRecords.FirstOrDefault().Value != newIpAddress)
                    {
                        matchingRecordSet.ResourceRecords.FirstOrDefault().Value = newIpAddress;
                        ChangeBatch change = new ChangeBatch();
                        change.Changes.Add(new Change(ChangeAction.UPSERT, matchingRecordSet));

                        ChangeResourceRecordSetsResponse changeRequest = await route53Client.ChangeResourceRecordSetsAsync(new ChangeResourceRecordSetsRequest(hostedZoneId, change));

                        Log.Information("[Runtime = {StartTime}] Change request submitted to change subdomain {Subdomain} IP address to {IPAddress}.", startTime, fqdn, newIpAddress);

                        return(changeRequest.HttpStatusCode == System.Net.HttpStatusCode.OK);
                    }
                    else
                    {
                        Log.Information("[Runtime = {StartTime}] Subdomain {Subdomain} found, but the IP address was already {IPAddress}.", startTime, fqdn, newIpAddress);
                    }
                }
                else
                {
                    // New subdomain
                    Log.Information("[Runtime = {StartTime}] Subdomain {Subdomain} record not found.", startTime, fqdn);
                }

                return(false);
            }
        }
Example #8
0
        public async Task <ActionResult> CreateRecord(DnsRecord request)
        {
            // https://docs.aws.amazon.com/sdk-for-net/v2/developer-guide/route53-apis-intro.html
            // find zone
            var zone = await ResolveMatchingZone(request);

            if (zone != null)
            {
                // get existing record set for current TXT records with this name
                var response = await _route53Client.ListResourceRecordSetsAsync(
                    new ListResourceRecordSetsRequest
                {
                    StartRecordName = request.RecordName,
                    StartRecordType = "TXT",
                    MaxItems        = "1",
                    HostedZoneId    = zone.Id
                }
                    );

                var targetRecordSet = response.ResourceRecordSets.FirstOrDefault(r => (r.Name == request.RecordName || r.Name == request.RecordName + ".") && r.Type.Value == "TXT");

                if (targetRecordSet != null)
                {
                    targetRecordSet.ResourceRecords.Add(
                        new ResourceRecord {
                        Value = "\"" + request.RecordValue + "\""
                    }
                        );
                }
                else
                {
                    targetRecordSet = new ResourceRecordSet()
                    {
                        Name            = request.RecordName,
                        TTL             = 5,
                        Type            = RRType.TXT,
                        ResourceRecords = new List <ResourceRecord>
                        {
                            new ResourceRecord {
                                Value = "\"" + request.RecordValue + "\""
                            }
                        }
                    };
                }

                try
                {
                    // requests for *.domain.com + domain.com use the same TXT record name, so we
                    // need to allow multiple entires rather than doing Upsert
                    var result = await ApplyDnsChange(zone, targetRecordSet, ChangeAction.UPSERT);

                    return(new ActionResult {
                        IsSuccess = true, Message = $"Dns Record Created/Updated: {request.RecordName}"
                    });
                }
                catch (AmazonRoute53Exception exp)
                {
                    return(new ActionResult {
                        IsSuccess = false, Message = $"Dns Record Create/Update: {request.RecordName} - {exp.Message}"
                    });
                }
            }
            else
            {
                return(new ActionResult {
                    IsSuccess = false, Message = "DNS Zone match could not be determined."
                });
            }
        }
        public List <MultiClientEntry> GetClients()
        {
            //Output
            Output($"MCS: Starting Get Clients");

            //1) Get the current data-text from R53
            //New the Route 53 Client
            AmazonRoute53Client r53Client =
                new AmazonRoute53Client(
                    Config.BaseSettings.AWSAccessKeyID,
                    Config.BaseSettings.AWSSecretAccessKey,
                    new AmazonRoute53Config
            {
                RegionEndpoint = RegionEndpoint
            });

            //Define the request
            ListResourceRecordSetsRequest listResourceRecordSetsRequest =
                new ListResourceRecordSetsRequest
            {
                HostedZoneId    = Config.MultiClientSettings.Route53.R53ZoneId,
                MaxItems        = 1.ToString(),
                StartRecordName = Config.MultiClientSettings.Route53.Name
            };

            //Get the response
            ListResourceRecordSetsResponse listResourceRecordSetsResponse =
                r53Client
                .ListResourceRecordSetsAsync(listResourceRecordSetsRequest)
                .GetAwaiter()
                .GetResult();

            //Get the Data Text (if any)
            //listResourceRecordSetsResponse.ResourceRecordSets[0].ResourceRecords[0].Value
            string dataText =
                listResourceRecordSetsResponse
                .ResourceRecordSets?
                .Where(rrsi => string.Equals(rrsi.Name, Config.MultiClientSettings.Route53.Name, StringComparison.InvariantCultureIgnoreCase))
                .FirstOrDefault()?
                .ResourceRecords?
                .FirstOrDefault()?
                .Value?
                .Trim('"');

            if (string.IsNullOrWhiteSpace(dataText) || string.IsNullOrWhiteSpace(dataText.Trim('"')))
            {
                //Since there somehow were no existing data in R53
                //We just return an empty list

                //Output
                Output($"MCS: Found no existing record");

                //return
                return(new List <MultiClientEntry>());
            }

            //2) Decode the data string to a JSON string (decompress, decrypt)
            //Decrypt and Decompress
            dataText = dataText.DecryptThenDecompress(Config.MultiClientSettings.Route53.EncryptionPassword);

            //3) Turn the Data Text into List
            //De-serialize the JSON to the return value
            List <MultiClientEntry> multiClientEntries =
                JsonConvert.DeserializeObject <List <MultiClientEntry> >(dataText);

            //Output
            Output($"MCS: Completed Get Clients. Got {multiClientEntries.Count}");

            //return
            return(multiClientEntries);
        }
        /// <summary>
        /// This method does all of the work to update the DNS records with our current WAN IP. This is separated from the main
        /// method to make it easier in the future to perform batch updates (if needed).
        /// </summary>
        /// <param name="id">The AWS Access key ID</param>
        /// <param name="secret">The AWS Access key secret</param>
        /// <param name="domain">The domain name we are going to be updating records for (e.g. shawnlehner.com)</param>
        /// <param name="subdomain">The subdomain we would like to update (e.g. "house" if we were updating house.shawnlehner.com)</param>
        private static async Task PerformDnsUpdate(string id, string secret, string domain, string subdomain)
        {
            #region Lookup Current WAN IP

            // We use ipify.org to quickly/easily get our external WAN IP address which we can later use
            // for updating our DNS records.

            string ip = null;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.ipify.org");
            using (Stream s = (await request.GetResponseAsync()).GetResponseStream())
                using (StreamReader r = new StreamReader(s))
                {
                    ip = r.ReadToEnd();
                }

            #endregion

            // Combine our domain and subdomain to get the full record name
            string recordName = subdomain.Trim('.') + "." + domain.Trim('.');

            // Create our AWS API client for sending requests to the Route53 API
            AmazonRoute53Client client = new AmazonRoute53Client(id, secret, RegionEndpoint.USEast1);

            #region Lookup current state of the domain on Route53

            // Lookup the zone for our domain
            HostedZone zone = (await client.ListHostedZonesByNameAsync(new ListHostedZonesByNameRequest
            {
                DNSName = domain
            })).HostedZones.First();

            // Lookup our current records to see if we need to make an update
            ListResourceRecordSetsResponse recordSet = await client.ListResourceRecordSetsAsync(new ListResourceRecordSetsRequest
            {
                HostedZoneId    = zone.Id,
                MaxItems        = "1",
                StartRecordName = recordName
            });

            #endregion

            // Check to see if our IP is already up to date. No sense making a change if we don't need to.
            if (recordSet.ResourceRecordSets.Count > 0 &&
                recordSet.ResourceRecordSets[0].Name.Trim('.') == recordName &&
                recordSet.ResourceRecordSets[0].ResourceRecords[0].Value == ip)
            {
                return;
            }

            #region Request DNS record update with new IP

            // Our IP address is not up-to-date so we need to make a change request. We use UPSERT action which
            // will work whether or not a record already exists.
            ChangeResourceRecordSetsRequest changeRequest = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = zone.Id,
                ChangeBatch  = new ChangeBatch
                {
                    Changes = new List <Change>
                    {
                        new Change
                        {
                            ResourceRecordSet = new ResourceRecordSet
                            {
                                Name            = recordName,
                                TTL             = 60,
                                Type            = RRType.A,
                                ResourceRecords = new List <ResourceRecord> {
                                    new ResourceRecord {
                                        Value = ip
                                    }
                                }
                            },
                            Action = ChangeAction.UPSERT
                        }
                    }
                }
            };

            // Send our change request to the API
            ChangeResourceRecordSetsResponse response =
                await client.ChangeResourceRecordSetsAsync(changeRequest);

            // Check our response code to verify everything worked
            if (response.HttpStatusCode != HttpStatusCode.OK)
            {
                throw new Exception("API request to update DNS record has failed.");
            }

            #endregion
        }