From c27c4b1698eea6fd9a21795e2b6f45c283b47e3b Mon Sep 17 00:00:00 2001 From: William Hirigoyen Date: Fri, 16 Sep 2022 15:02:00 +0200 Subject: [PATCH] evodomains: implement config files include and exclude domains, and config file to allow external IPs --- evolinux-base/files/evodomains.py | 80 +++++++++++++++++++++--------- evolinux-base/tasks/evodomains.yml | 35 +++++++++++++ 2 files changed, 91 insertions(+), 24 deletions(-) diff --git a/evolinux-base/files/evodomains.py b/evolinux-base/files/evodomains.py index ad8e1d97..613d4456 100755 --- a/evolinux-base/files/evodomains.py +++ b/evolinux-base/files/evodomains.py @@ -11,9 +11,9 @@ # Developped by Will & Brice # -list_domains_path = '/usr/local/sbin/list_domains.py' excludes_path = '/etc/evolinux/evodomains_exclude.list' includes_path = '/etc/evolinux/evodomains_include.list' +allowed_ips_path = '/etc/evolinux/evodomains_allowed_ips.list' import os import sys @@ -24,9 +24,6 @@ import time import argparse import json -#import importlib.machinery -#list_domains = importlib.machinery.SourceFileLoader('list_domains.py', list_domains_path).load_module() - def execute(cmd): """Execute Bash command cmd. @@ -41,12 +38,20 @@ def execute(cmd): return stdout_lines, stderr_lines -def get_my_ips(): - """Return localhost IPs.""" +def get_allowed_ips(): + """Return allowed IPs.""" + stdout, stderr = execute('hostname -I') if not stdout: return [] - return stdout[0].strip(' \t').split() + ips = stdout[0].strip(' \t\n').split() + + # Other allowed IPs + with open(allowed_ips_path, encoding='utf-8') as f: + for line in f: + ip = strip_comments(line).strip(' \t;') + ips.append(ip) + return ips def dig(domain): @@ -144,7 +149,7 @@ def list_nginx_domains(): return domains -class ResolutionThread(threading.Thread): +class DNSResolutionThread(threading.Thread): def __init__(self, domain): threading.Thread.__init__(self, daemon=True) @@ -177,25 +182,32 @@ def run_check_domains(domains): excludes = ['_'] timeout = 5 - my_ips = get_my_ips() + allowed_ips = get_allowed_ips() - domains_noexcludes = [dom for dom in domains if dom not in excludes] - - jobs = [] - for dom in domains_noexcludes: - #print(d) - t = ResolutionThread(dom) - t.start() - jobs.append(t) + with open(excludes_path, encoding='utf-8') as f: + for line in f: + domain = strip_comments(line).strip(' \t\n') + if not domain: continue + excludes.append(domain) - # Let secs to DNS servers to answer in jobs threads - time.sleep(timeout) - + jobs = [] timeout_domains = [] none_domains = [] outside_ips = {} ok_domains = [] + for d in domains: + if d in excludes: + ok_domains.append(d) + continue + + t = DNSResolutionThread(d) + t.start() + jobs.append(t) + + # Let secs to DNS servers to reply to jobs threads queries + time.sleep(timeout) + for j in jobs: if j.is_alive(): timeout_domains.append(j.domain) @@ -207,7 +219,7 @@ def run_check_domains(domains): is_outside = False for ip in j.ips: - if ip not in my_ips: + if ip not in allowed_ips: is_outside = True break if is_outside: @@ -218,7 +230,7 @@ def run_check_domains(domains): return timeout_domains, none_domains, outside_ips, ok_domains -def output_check_mode(timeout_domains, none_domains, outside_ips, ok_domains): +def output_nrpe_mode(timeout_domains, none_domains, outside_ips, ok_domains): """Output result for check mode. For now, consider everyting as warnings to avoid too much alerts. """ @@ -302,10 +314,22 @@ def main(argv): sys.exit(1) if args.action == 'check-dns': + + # Add included domains to domains dict + with open(includes_path, encoding='utf-8') as f: + line_number = 0 + for line in f: + line_number += 1 + domain = strip_comments(line).strip(' \t\n') + if not domain: continue + if domain not in doms: + doms[domain] = [] + doms[domain].append('evodomains:{}:{}'.format(includes_path, line_number)) + timeout_domains, none_domains, outside_ips, ok_domains = run_check_domains(doms.keys()) if args.output_style == 'nrpe': - output_check_mode(timeout_domains, none_domains, outside_ips, ok_domains) + output_nrpe_mode(timeout_domains, none_domains, outside_ips, ok_domains) elif args.output_style == 'json': print('Option --output-style json not implemented yet for action check-dns.') @@ -314,6 +338,7 @@ def main(argv): output_human_mode(doms, timeout_domains, none_domains, outside_ips) elif args.action == 'list': + # Note: do not use evodomains include and exclude lists for listing. if args.output_style == 'nrpe': print('Action list is not for --output-style nrpe.') @@ -324,7 +349,14 @@ def main(argv): else: print('Option --output-style human not implemented yet for action list, fallback to --output-style json.') print(json.dumps(doms, sort_keys=True, indent=4)) - + + #elif args.action == 'brice_action': + # #doms est un dict avec le nom de domaine comme clé, pour voir la structure de données : + # # evodomains --output-style json list + # + # print(doms) + # brice_function(doms) + # if __name__ == '__main__': main(sys.argv[1:]) diff --git a/evolinux-base/tasks/evodomains.yml b/evolinux-base/tasks/evodomains.yml index 6a8d6c77..6f44b46d 100644 --- a/evolinux-base/tasks/evodomains.yml +++ b/evolinux-base/tasks/evodomains.yml @@ -4,5 +4,40 @@ dest: /usr/local/sbin/evodomains mode: '0700' +- name: Create config file 'evodomains_exclude.list' + ansible.builtin.blockinfile: + path: /etc/evolinux/evodomains_exclude.list + create: true + marker: "### {mark} ANSIBLE MANAGED HEADER" + insertbefore: BOF + block: | + # Domains present in vhosts or SSL certificates whose DNS records should + # not be checked by 'evodomains --check-dns'. + # Note: custom record IPs can also be added to /etc/evolinux/evodomains_allowed_ips.list, + # this is useful for load-balanced domains or NAT. + # Format: one domain per line, regex and wildcards not supported. +- name: Create config file 'evodomains_include.list' + ansible.builtin.blockinfile: + path: /etc/evolinux/evodomains_include.list + create: true + marker: "### {mark} ANSIBLE MANAGED HEADER" + insertbefore: BOF + block: | + # Domains absent from vhosts or SSL certificates whose DNS records must + # be checked by 'evodomains --check-dns'. + # Format: one domain per line, regex and wildcards not supported. + +- name: Create config file 'evodomains_allowed_ips.list' + ansible.builtin.blockinfile: + path: /etc/evolinux/evodomains_allowed_ips.list + create: true + marker: "### {mark} ANSIBLE MANAGED HEADER" + insertbefore: BOF + block: | + # External IPs the domains of this server are allowed to point for + # 'evodomains --check-dns'. + # This is useful for load-balanced domains or NAT. + # Note: the network interfaces IPs of the server are allowed by default. + # Format: one IP per line, regex and wildcards not supported.