{"id":26004,"date":"2016-04-07T23:29:35","date_gmt":"2016-04-07T21:29:35","guid":{"rendered":"https:\/\/mamchenkov.net\/wordpress\/?p=26004"},"modified":"2016-04-07T23:29:35","modified_gmt":"2016-04-07T21:29:35","slug":"ansible-safety-net-for-dns-wildcard-hosts","status":"publish","type":"post","link":"https:\/\/mamchenkov.net\/wordpress\/2016\/04\/07\/ansible-safety-net-for-dns-wildcard-hosts\/","title":{"rendered":"Ansible safety net for DNS wildcard hosts"},"content":{"rendered":"<!-- google_ad_section_start -->\n<p>After using <a href=\"https:\/\/www.ansible.com\/\">Ansible<\/a> for <a href=\"https:\/\/mamchenkov.net\/wordpress\/2016\/04\/01\/first-attempt-at-ansible\/\">only a week<\/a>, I am deeply in love. \u00a0I am doing more and more with less and less, and that&#8217;s exactly how I want my automation.<\/p>\n<p>Today I had to solve an interesting problem. \u00a0Ansible operates, based on the <a href=\"http:\/\/docs.ansible.com\/ansible\/intro_inventory.html\">host and group inventory<\/a>. \u00a0As <a href=\"https:\/\/mamchenkov.net\/wordpress\/2016\/04\/02\/top-level-domain-nonsense-and-how-it-can-break-your-stuff\/\">I mentioned before<\/a>, I am now always relying on FQDNs (fully qualified domain names) for my host names. \u00a0But what happens when <a href=\"https:\/\/en.wikipedia.org\/wiki\/Wildcard_DNS_record\">DNS wildcards<\/a> come into play with\u00a0things like\u00a0load balancers and reverse proxies \u00a0Consider an example:<\/p>\n<ol>\n<li>Nginx configured as reverse proxy on the machine <em>proxy1.example.com<\/em> with <em>10.0.0.10<\/em> IP address.<\/li>\n<li>DNS wildcard is in place: <em>*.example.com 3600 IN CNAME proxy1.example.com<\/em>.<\/li>\n<li>Ansible contains <em>proxy1.example.com<\/em> in host inventory and a playbook to setup the reverse proxy with Nginx.<\/li>\n<li>Ansible contains a few other hosts in inventory and a playbook to setup Nginx as a web server.<\/li>\n<li>Somebody adds a new host to inventory: <em>another-web-server.example.com<\/em>, without specifying any other host details, like <em>ansible_ssh_host<\/em> variable. \u00a0And he also forgets to update the DNS zone with a new A or CNAME record.<\/li>\n<\/ol>\n<p>Now, Ansible play is executed for the web servers configuration. \u00a0All previously existing machines are fine. \u00a0But the new machine&#8217;s <em>another-web-server.example.com<\/em> host name resolves to <em>proxy1.example.com<\/em>, which is where Ansible connects and runs the Nginx setup, overwriting the existing configuration, triggering a service restart, and screwing up your life. \u00a0Just kidding, of course. :) \u00a0It&#8217;ll be trivial to find out what happened. \u00a0Fixing the Nginx isn&#8217;t too difficult either. \u00a0Especially if you <a href=\"https:\/\/mamchenkov.net\/wordpress\/2016\/01\/19\/tao-of-backup\/\">have backups in place<\/a>. \u00a0But it&#8217;s still better to avoid the whole mess altogether.<\/p>\n<p>To help prevent these cases, I decided to create a new safety net role. \u00a0Given a variable like:<\/p>\n<pre class=\"brush: plain; light: true; title: ; notranslate\" title=\"\">\r\n---\r\n# Aliased IPs is a list of hosts, which can be reached in \r\n# multiple ways due to DNS wildcards. Both IPv4 and IPv6 \r\n# can be used. The hostname value is the primary hostname \r\n# for the IP - any other inventory hostname having any of \r\n# these IPs will cause a failure in the play.\r\naliased_ips:\r\n  &quot;10.0.0.10&quot;: &quot;proxy1.example.com&quot;\r\n  &quot;192.168.0.10&quot;: &quot;proxy1.example.com&quot;\r\n<\/pre>\n<p>And the following code in the role&#8217;s <em>tasks\/main.yml<\/em>:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n---\r\n- debug: msg=&quot;Safety net - before IPv4&quot;\r\n\r\n- name: Check all IPv4 addresses against aliased IPs\r\n  fail: msg=&quot;DNS is not configured for host '{{ inventory_hostname}}'. It resolves to '{{ aliased_ips&#x5B; item.0 ] }}'.&quot;\r\n  when: &quot;('{{ item&#x5B;0] }}' == '{{ item&#x5B;1] }}') and ('{{ inventory_hostname }}' != '{{ aliased_ips&#x5B; item.0 ] }}')&quot;\r\n  with_nested:\r\n    - &quot;{{ aliased_ips | default({}) }}&quot;\r\n    - &quot;{{ ansible_all_ipv4_addresses }}&quot;\r\n\r\n- debug: msg=&quot;Safety net - after IPv4 and before IPv6&quot;\r\n\r\n- name: Check all IPv6 addresses against aliased IPs\r\n  fail: msg=&quot;DNS is not configured for host '{{ inventory_hostname}}'. It resolves to '{{ aliased_ips&#x5B; item.0 ] }}'.&quot;\r\n  when: &quot;('{{ item&#x5B;0] }}' == '{{ item&#x5B;1] }}') and ('{{ inventory_hostname }}' != '{{ aliased_ips&#x5B; item.0 ] }}')&quot;\r\n  with_nested:\r\n    - &quot;{{ aliased_ips | default({}) }}&quot;\r\n    - &quot;{{ ansible_all_ipv6_addresses }}&quot;\r\n\r\n- debug: msg=&quot;Safety net - after IPv6&quot;\r\n<\/pre>\n<p>the safety net is in place. \u00a0The first check will connect to the remote server, get the list of all configured IPv4 addresses, and then compare each one with each IP address in the <em>aliased_ips<\/em> variable. \u00a0For every matching pair, it will check if the remote server&#8217;s host name from the inventory file matches the host name from the <em>aliased_ips<\/em> value for the matched IP address. \u00a0If the host names match, it&#8217;ll continue. \u00a0If not &#8211; a failure in the play occurs (Ansible speak for thrown exception). \u00a0Other tasks will continue execution for other hosts, but nothing else will be done during this play run for this particular host.<\/p>\n<p>The second check will do the same but with IPv6 addresses. \u00a0You can mix and match both IPv4 and IPv6 in the same <em>aliased_ips<\/em> variable. \u00a0And Ansible is smart enough to exclude the localhost IPs too, so things shouldn&#8217;t break too much.<\/p>\n<p>I&#8217;ve tested the above and it seems to work well for me.<\/p>\n<p>There is a tiny issue with elegance here though: host name to IP mappings are already configured in the DNS zone &#8211; duplicating this configuration in the <em>aliased_ips<\/em> variable seems annoying. \u00a0Personally, I don&#8217;t have that many reverse proxies and load balancers to handle, and they don&#8217;t change too often either, so I don&#8217;t mind. \u00a0Also, there is something about relying on DNS while trying to protect against DNS mis-configuration that rubs me the wrong way. \u00a0But if you are the adventurous type, have a look at the <a href=\"http:\/\/docs.ansible.com\/ansible\/playbooks_lookups.html#the-dns-lookup-dig\">Ansible&#8217;s dig lookup<\/a>, which you can use to fetch the IP addresses from the DNS server of your choice.<\/p>\n<p>As always, if you see any potential issues with the above or know of a better way to solve it, please let me know.<\/p>\n<!-- google_ad_section_end -->\n","protected":false},"excerpt":{"rendered":"<!-- google_ad_section_start -->\n<p>After using Ansible for only a week, I am deeply in love. \u00a0I am doing more and more with less and less, and that&#8217;s exactly how I want my automation. Today I had to solve an interesting problem. \u00a0Ansible operates, based on the host and group inventory. \u00a0As I mentioned before, I am now always &hellip; <a href=\"https:\/\/mamchenkov.net\/wordpress\/2016\/04\/07\/ansible-safety-net-for-dns-wildcard-hosts\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Ansible safety net for DNS wildcard hosts<\/span><\/a><\/p>\n<!-- google_ad_section_end -->\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_links_to":"","_links_to_target":""},"categories":[1,6,133,62,1334],"tags":[3383,1762,281,2289],"keyring_services":[],"class_list":["post-26004","post","type-post","status-publish","format-standard","hentry","category-general","category-linux","category-sysadmin","category-technology","category-web-work","tag-ansible","tag-domain-names","tag-networks","tag-web-hosting"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":26279,"url":"https:\/\/mamchenkov.net\/wordpress\/2016\/07\/24\/ssh-multiplexing-and-ansible-via-bastion-host\/","url_meta":{"origin":26004,"position":0},"title":"SSH multiplexing and Ansible via bastion host","author":"Leonid Mamchenkov","date":"July 24, 2016","format":false,"excerpt":"It never ceases to amaze me how even after years and years of working with some technologies I keep finding out about super useful features in those technologies, that could have saved me lots of time if I knew about them earlier. \u00a0Today was a day just like that. I\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":25993,"url":"https:\/\/mamchenkov.net\/wordpress\/2016\/04\/02\/top-level-domain-nonsense-and-how-it-can-break-your-stuff\/","url_meta":{"origin":26004,"position":1},"title":"Top level domain nonsense and how it can break your stuff","author":"Leonid Mamchenkov","date":"April 2, 2016","format":false,"excerpt":"Call me old school, but I really (I mean REALLY) don't like the recent explosion of the top level domains. \u00a0I understand that most good names are taken in .com, .org, and .net zones, but do we really need all those .blue, .parts, and .yoga TLDs? Why am I whining\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"aws","src":"https:\/\/i0.wp.com\/mamchenkov.net\/wordpress\/wp-content\/uploads\/2016\/04\/aws-500x305.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":26860,"url":"https:\/\/mamchenkov.net\/wordpress\/2016\/11\/08\/install-ansible-2-0-on-amazon-ami\/","url_meta":{"origin":26004,"position":2},"title":"Install Ansible 2.0+ on Amazon AMI","author":"Leonid Mamchenkov","date":"November 8, 2016","format":false,"excerpt":"Today, while upgrading some of my Ansible roles I've hit the problem. \u00a0Some of the newer roles require Ansible 2.0. \u00a0My Amazon AMI machine that runs the playbooks was still on version 1.9. \u00a0EPEL repository doesn't seem to have the newer Ansible version yet. \u00a0Gladly, Google brough in this StackOverflow\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":26208,"url":"https:\/\/mamchenkov.net\/wordpress\/2016\/06\/27\/lets-encrypt-on-centos-7-and-amazon-ami\/","url_meta":{"origin":26004,"position":3},"title":"Let&#8217;s Encrypt on CentOS 7 and Amazon AMI","author":"Leonid Mamchenkov","date":"June 27, 2016","format":false,"excerpt":"The last few weeks were super busy at work, so I accidentally let a few SSL certificates expire. \u00a0Renewing them is always annoying and time consuming, so I was pushing it until the last minute, and then some. Instead of going the usual way for the renewal, I decided to\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":27259,"url":"https:\/\/mamchenkov.net\/wordpress\/2017\/01\/20\/parsing-text-printouts-within-ansible-playbooks\/","url_meta":{"origin":26004,"position":4},"title":"Parsing text printouts within Ansible playbooks","author":"Leonid Mamchenkov","date":"January 20, 2017","format":false,"excerpt":"I'm sure this will come handy soon, and I'll be spending too much time trying to figure it out without this article:\u00a0Parsing text printouts within Ansible playbooks. It's not every day that you see regular expression examples in the Ansible playbooks...","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":27263,"url":"https:\/\/mamchenkov.net\/wordpress\/2017\/01\/20\/immutable-infrastructure-with-aws-and-ansible\/","url_meta":{"origin":26004,"position":5},"title":"Immutable Infrastructure with AWS and Ansible","author":"Leonid Mamchenkov","date":"January 20, 2017","format":false,"excerpt":"Immutable infrastructure is a very powerful concept that brings stability, efficiency, and fidelity to your applications through automation and the use of successful patterns from programming. \u00a0The general idea is that you never make changes to running infrastructure. \u00a0Instead, you ensure that all infrastructure is created through automation, and to\u2026","rel":"","context":"In &quot;All&quot;","block_context":{"text":"All","link":"https:\/\/mamchenkov.net\/wordpress\/category\/general\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts\/26004","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/comments?post=26004"}],"version-history":[{"count":0,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/posts\/26004\/revisions"}],"wp:attachment":[{"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/media?parent=26004"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/categories?post=26004"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/tags?post=26004"},{"taxonomy":"keyring_services","embeddable":true,"href":"https:\/\/mamchenkov.net\/wordpress\/wp-json\/wp\/v2\/keyring_services?post=26004"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}