evodomains: implement config files include and exclude domains, and config file to allow external IPs
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good

This commit is contained in:
William Hirigoyen 2022-09-16 15:02:00 +02:00
parent 6f178d92ab
commit c27c4b1698
2 changed files with 91 additions and 24 deletions

View file

@ -11,9 +11,9 @@
# Developped by Will & Brice # Developped by Will & Brice
# #
list_domains_path = '/usr/local/sbin/list_domains.py'
excludes_path = '/etc/evolinux/evodomains_exclude.list' excludes_path = '/etc/evolinux/evodomains_exclude.list'
includes_path = '/etc/evolinux/evodomains_include.list' includes_path = '/etc/evolinux/evodomains_include.list'
allowed_ips_path = '/etc/evolinux/evodomains_allowed_ips.list'
import os import os
import sys import sys
@ -24,9 +24,6 @@ import time
import argparse import argparse
import json import json
#import importlib.machinery
#list_domains = importlib.machinery.SourceFileLoader('list_domains.py', list_domains_path).load_module()
def execute(cmd): def execute(cmd):
"""Execute Bash command cmd. """Execute Bash command cmd.
@ -41,12 +38,20 @@ def execute(cmd):
return stdout_lines, stderr_lines return stdout_lines, stderr_lines
def get_my_ips(): def get_allowed_ips():
"""Return localhost IPs.""" """Return allowed IPs."""
stdout, stderr = execute('hostname -I') stdout, stderr = execute('hostname -I')
if not stdout: if not stdout:
return [] 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): def dig(domain):
@ -144,7 +149,7 @@ def list_nginx_domains():
return domains return domains
class ResolutionThread(threading.Thread): class DNSResolutionThread(threading.Thread):
def __init__(self, domain): def __init__(self, domain):
threading.Thread.__init__(self, daemon=True) threading.Thread.__init__(self, daemon=True)
@ -177,25 +182,32 @@ def run_check_domains(domains):
excludes = ['_'] excludes = ['_']
timeout = 5 timeout = 5
my_ips = get_my_ips() allowed_ips = get_allowed_ips()
domains_noexcludes = [dom for dom in domains if dom not in excludes] with open(excludes_path, encoding='utf-8') as f:
for line in f:
jobs = [] domain = strip_comments(line).strip(' \t\n')
for dom in domains_noexcludes: if not domain: continue
#print(d) excludes.append(domain)
t = ResolutionThread(dom)
t.start()
jobs.append(t)
# Let <timeout> secs to DNS servers to answer in jobs threads jobs = []
time.sleep(timeout)
timeout_domains = [] timeout_domains = []
none_domains = [] none_domains = []
outside_ips = {} outside_ips = {}
ok_domains = [] ok_domains = []
for d in domains:
if d in excludes:
ok_domains.append(d)
continue
t = DNSResolutionThread(d)
t.start()
jobs.append(t)
# Let <timeout> secs to DNS servers to reply to jobs threads queries
time.sleep(timeout)
for j in jobs: for j in jobs:
if j.is_alive(): if j.is_alive():
timeout_domains.append(j.domain) timeout_domains.append(j.domain)
@ -207,7 +219,7 @@ def run_check_domains(domains):
is_outside = False is_outside = False
for ip in j.ips: for ip in j.ips:
if ip not in my_ips: if ip not in allowed_ips:
is_outside = True is_outside = True
break break
if is_outside: if is_outside:
@ -218,7 +230,7 @@ def run_check_domains(domains):
return timeout_domains, none_domains, outside_ips, ok_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. """Output result for check mode.
For now, consider everyting as warnings to avoid too much alerts. For now, consider everyting as warnings to avoid too much alerts.
""" """
@ -302,10 +314,22 @@ def main(argv):
sys.exit(1) sys.exit(1)
if args.action == 'check-dns': 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()) timeout_domains, none_domains, outside_ips, ok_domains = run_check_domains(doms.keys())
if args.output_style == 'nrpe': 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': elif args.output_style == 'json':
print('Option --output-style json not implemented yet for action check-dns.') 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) output_human_mode(doms, timeout_domains, none_domains, outside_ips)
elif args.action == 'list': elif args.action == 'list':
# Note: do not use evodomains include and exclude lists for listing.
if args.output_style == 'nrpe': if args.output_style == 'nrpe':
print('Action list is not for --output-style nrpe.') print('Action list is not for --output-style nrpe.')
@ -324,7 +349,14 @@ def main(argv):
else: else:
print('Option --output-style human not implemented yet for action list, fallback to --output-style json.') 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)) 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__': if __name__ == '__main__':
main(sys.argv[1:]) main(sys.argv[1:])

View file

@ -4,5 +4,40 @@
dest: /usr/local/sbin/evodomains dest: /usr/local/sbin/evodomains
mode: '0700' 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.