From 11343cc75d4c78e2a112098e8ccb2c1f067b1d36 Mon Sep 17 00:00:00 2001 From: Ian Wienand Date: Thu, 13 Sep 2018 13:58:52 +1000 Subject: [PATCH] dns_[a|aaaa] filter; use host for lookup After adding iptables configuration to allow bridge.o.o to send stats to graphite.o.o in I299c0ab5dc3dea4841e560d8fb95b8f3e7df89f2, I encountered the weird failure that ipv6 rules seemed to be applied on graphite.o.o, but not the ipv4 ones. Eventually I realised that the dns_a filter as written is using socket.getaddrinfo() on bridge.o.o and querying for itself. It thus gets matches the loopback entry in /etc/hosts and passes along a rule for 127.0.1.1 or similar. The ipv6 hostname is not in /etc/hosts so this works there. What we really want the dns_ filters to do is lookup the address in DNS, rather than the local resolver. Without wanting to get involved in new libraries, etc. the simplest option seems to be to use the well-known 'host' tool. We can easily parse the output of this to ensure we're getting the actual DNS addresses for hostnames. An ipv6 match is added to the existing test. This is effectively tested by the existing usage of the iptables role which sets up rules for cacti.o.o access. Change-Id: Ia7988626e9b1fba998fee796d4016fc66332ec03 --- playbooks/filter_plugins/getaddrinfo.py | 24 +++++++++++++------ .../roles/iptables/templates/rules.v6.j2 | 2 +- testinfra/test_base.py | 9 ++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/playbooks/filter_plugins/getaddrinfo.py b/playbooks/filter_plugins/getaddrinfo.py index ae98bab2ae..27c46ba7e7 100644 --- a/playbooks/filter_plugins/getaddrinfo.py +++ b/playbooks/filter_plugins/getaddrinfo.py @@ -13,26 +13,36 @@ # See the License for the specific language governing permissions and # limitations under the License. -import socket +import subprocess class FilterModule(object): def dns(self, value, family): ret = set() + if family == '4': + match = 'has address' + elif family == '6': + match = 'has IPv6 address' try: - addr_info = socket.getaddrinfo(value, None, family) - except socket.gaierror: + # Note we use 'host' rather than something like + # getaddrinfo so we actually query DNS and don't get any + # local-only results from /etc/hosts + output = subprocess.check_output( + ['/usr/bin/host', value], universal_newlines=True) + for line in output.split('\n'): + if match in line: + address = line.split()[-1] + ret.add(address) + except Exception as e: return ret - for addr in addr_info: - ret.add(addr[4][0]) return sorted(ret) def dns_a(self, value): - return self.dns(value, socket.AF_INET) + return self.dns(value, '4') def dns_aaaa(self, value): - return self.dns(value, socket.AF_INET6) + return self.dns(value, '6') def filters(self): return { diff --git a/playbooks/roles/iptables/templates/rules.v6.j2 b/playbooks/roles/iptables/templates/rules.v6.j2 index 3b38657fbd..c7549bb4fc 100644 --- a/playbooks/roles/iptables/templates/rules.v6.j2 +++ b/playbooks/roles/iptables/templates/rules.v6.j2 @@ -23,7 +23,7 @@ {% endfor -%} {% for host in iptables_allowed_hosts -%} {% for addr in host.hostname | dns_aaaa -%} --A openstack-INPUT {% if host.protocol == 'tcp' %}-m state --state NEW {% endif %} -m {{ host.protocol }} -p {{ host.protocol }} -s {{ addr }} --dport {{ host.port }} -j ACCEPT +-A openstack-INPUT {% if host.protocol == 'tcp' %}-m state --state NEW {% endif %}-m {{ host.protocol }} -p {{ host.protocol }} -s {{ addr }} --dport {{ host.port }} -j ACCEPT {% endfor -%} {% endfor -%} -A openstack-INPUT -j REJECT --reject-with icmp6-adm-prohibited diff --git a/testinfra/test_base.py b/testinfra/test_base.py index 9b961491e1..08449e270d 100644 --- a/testinfra/test_base.py +++ b/testinfra/test_base.py @@ -83,12 +83,19 @@ def test_iptables(host): ' -m tcp --dport 19885 -j ACCEPT') assert zuul in rules - # Ensure all IPv4 addresses for cacti are allowed + # Ensure all IPv4+6 addresses for cacti are allowed for ip in get_ips('cacti.openstack.org', socket.AF_INET): snmp = ('-A openstack-INPUT -s %s/32 -p udp -m udp' ' --dport 161 -j ACCEPT' % ip) assert snmp in rules + # TODO(ianw) add ip6tables support to testinfra iptables module + ip6rules = host.check_output('ip6tables -S') + for ip in get_ips('cacti.openstack.org', socket.AF_INET6): + snmp = ('-A openstack-INPUT -s %s/128 -p udp -m udp' + ' --dport 161 -j ACCEPT' % ip) + assert snmp in ip6rules + def test_ntp(host): package = host.package("ntp")