You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
348 lines
10 KiB
348 lines
10 KiB
9 months ago
|
#!/usr/bin/env python
|
||
|
#
|
||
|
# Tests for bind configuration parsing
|
||
|
|
||
|
from leapp.libraries.common import isccfg
|
||
|
|
||
|
#
|
||
|
# Sample configuration stubs
|
||
|
#
|
||
|
named_conf_default = isccfg.MockConfig("""
|
||
|
//
|
||
|
// named.conf
|
||
|
//
|
||
|
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
|
||
|
// server as a caching only nameserver (as a localhost DNS resolver only).
|
||
|
//
|
||
|
// See /usr/share/doc/bind*/sample/ for example named configuration files.
|
||
|
//
|
||
|
|
||
|
options {
|
||
|
listen-on port 53 { 127.0.0.1; };
|
||
|
listen-on-v6 port 53 { ::1; };
|
||
|
directory "/var/named";
|
||
|
dump-file "/var/named/data/cache_dump.db";
|
||
|
statistics-file "/var/named/data/named_stats.txt";
|
||
|
memstatistics-file "/var/named/data/named_mem_stats.txt";
|
||
|
secroots-file "/var/named/data/named.secroots";
|
||
|
recursing-file "/var/named/data/named.recursing";
|
||
|
allow-query { localhost; };
|
||
|
|
||
|
/*
|
||
|
- If you are building an AUTHORITATIVE DNS server, do NOT enable recursion.
|
||
|
- If you are building a RECURSIVE (caching) DNS server, you need to enable
|
||
|
recursion.
|
||
|
- If your recursive DNS server has a public IP address, you MUST enable access
|
||
|
control to limit queries to your legitimate users. Failing to do so will
|
||
|
cause your server to become part of large scale DNS amplification
|
||
|
attacks. Implementing BCP38 within your network would greatly
|
||
|
reduce such attack surface
|
||
|
*/
|
||
|
recursion yes;
|
||
|
|
||
|
dnssec-enable yes;
|
||
|
dnssec-validation yes;
|
||
|
|
||
|
managed-keys-directory "/var/named/dynamic";
|
||
|
|
||
|
pid-file "/run/named/named.pid";
|
||
|
session-keyfile "/run/named/session.key";
|
||
|
};
|
||
|
|
||
|
logging {
|
||
|
channel default_debug {
|
||
|
file "data/named.run";
|
||
|
severity dynamic;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
zone "." IN {
|
||
|
type hint;
|
||
|
file "named.ca";
|
||
|
};
|
||
|
|
||
|
# Avoid including files from bind package, may be not installed
|
||
|
# include "/etc/named.rfc1912.zones";
|
||
|
# include "/etc/named.root.key";
|
||
|
include "/dev/null";
|
||
|
""")
|
||
|
|
||
|
|
||
|
options_lookaside_no = isccfg.MockConfig("""
|
||
|
options {
|
||
|
dnssec-lookaside no;
|
||
|
};
|
||
|
""")
|
||
|
|
||
|
|
||
|
options_lookaside_auto = isccfg.MockConfig("""
|
||
|
options {
|
||
|
dnssec-lookaside /* no */ auto;
|
||
|
};
|
||
|
""")
|
||
|
|
||
|
|
||
|
options_lookaside_manual = isccfg.MockConfig("""
|
||
|
options {
|
||
|
# make sure parser handles comments
|
||
|
dnssec-lookaside "." /* comment to confuse parser */trust-anchor "dlv.isc.org";
|
||
|
};
|
||
|
""")
|
||
|
|
||
|
|
||
|
options_lookaside_commented = isccfg.MockConfig("""
|
||
|
options {
|
||
|
/* dnssec-lookaside auto; */
|
||
|
};
|
||
|
""")
|
||
|
|
||
|
|
||
|
views_lookaside = isccfg.MockConfig("""
|
||
|
view "v1" IN {
|
||
|
// This is auto
|
||
|
dnssec-lookaside auto;
|
||
|
};
|
||
|
|
||
|
options {
|
||
|
/* This is multi
|
||
|
* line
|
||
|
* comment */
|
||
|
dnssec-lookaside no;
|
||
|
};
|
||
|
|
||
|
view "v2" {
|
||
|
# Note no IN
|
||
|
dnssec-lookaside "." trust-anchor "dlv.isc.org";
|
||
|
};
|
||
|
""")
|
||
|
|
||
|
|
||
|
def check_in_section(parser, section, key, value):
|
||
|
""" Helper to check some section was found
|
||
|
in configuration section and has expected value
|
||
|
|
||
|
:type parser: IscConfigParser
|
||
|
:type section: bind.ConfigSection
|
||
|
:type key: str
|
||
|
:param value: expected value """
|
||
|
assert isinstance(section, isccfg.ConfigSection)
|
||
|
cfgval = parser.find_val_section(section, key)
|
||
|
assert isinstance(cfgval, isccfg.ConfigSection)
|
||
|
assert cfgval.value() == value
|
||
|
return cfgval
|
||
|
|
||
|
|
||
|
def cb_state(statement, state):
|
||
|
"""Callback used in IscConfigParser.walk()"""
|
||
|
key = statement.var(0).value()
|
||
|
state[key] = statement
|
||
|
|
||
|
|
||
|
def find_options(parser):
|
||
|
"""Replace IscConfigParser.find_option with walk use"""
|
||
|
state = {}
|
||
|
callbacks = {
|
||
|
'options': cb_state,
|
||
|
}
|
||
|
assert len(parser.FILES_TO_CHECK) >= 1
|
||
|
cfg = parser.FILES_TO_CHECK[0]
|
||
|
parser.walk(cfg.root_section(), callbacks, state)
|
||
|
options = state['options']
|
||
|
if options:
|
||
|
assert isinstance(options, isccfg.ConfigVariableSection)
|
||
|
return options.firstblock()
|
||
|
return None
|
||
|
|
||
|
|
||
|
# End of helpers
|
||
|
#
|
||
|
# Begin of tests
|
||
|
|
||
|
|
||
|
def test_lookaside_no():
|
||
|
parser = isccfg.IscConfigParser(options_lookaside_no)
|
||
|
assert len(parser.FILES_TO_CHECK) == 1
|
||
|
opt = find_options(parser)
|
||
|
check_in_section(parser, opt, "dnssec-lookaside", "no")
|
||
|
|
||
|
|
||
|
def test_lookaside_commented():
|
||
|
parser = isccfg.IscConfigParser(options_lookaside_commented)
|
||
|
assert len(parser.FILES_TO_CHECK) == 1
|
||
|
opt = find_options(parser)
|
||
|
assert isinstance(opt, isccfg.ConfigSection)
|
||
|
lookaside = parser.find_val_section(opt, "dnssec-lookaside")
|
||
|
assert lookaside is None
|
||
|
|
||
|
|
||
|
def test_default():
|
||
|
parser = isccfg.IscConfigParser(named_conf_default)
|
||
|
assert len(parser.FILES_TO_CHECK) >= 2
|
||
|
opt = find_options(parser)
|
||
|
check_in_section(parser, opt, "directory", '"/var/named"')
|
||
|
check_in_section(parser, opt, "session-keyfile", '"/run/named/session.key"')
|
||
|
check_in_section(parser, opt, "allow-query", '{ localhost; }')
|
||
|
check_in_section(parser, opt, "recursion", 'yes')
|
||
|
check_in_section(parser, opt, "dnssec-validation", 'yes')
|
||
|
check_in_section(parser, opt, "dnssec-enable", 'yes')
|
||
|
|
||
|
|
||
|
def test_key_lookaside():
|
||
|
parser = isccfg.IscConfigParser(options_lookaside_manual)
|
||
|
opt = find_options(parser)
|
||
|
key = parser.find_next_key(opt.config, opt.start+1, opt.end)
|
||
|
assert isinstance(key, isccfg.ConfigSection)
|
||
|
assert key.value() == 'dnssec-lookaside'
|
||
|
value = parser.find_next_val(opt.config, None, key.end+1, opt.end)
|
||
|
assert value.value() == '"."'
|
||
|
key2 = parser.find_next_key(opt.config, value.end+1, opt.end)
|
||
|
assert key2.value() == 'trust-anchor'
|
||
|
value2a = parser.find_next_val(opt.config, None, key2.end+1, opt.end)
|
||
|
value2b = parser.find_val(opt.config, 'trust-anchor', value.end+1, opt.end)
|
||
|
assert value2b.value() == '"dlv.isc.org"'
|
||
|
assert value2a.value() == value2b.value()
|
||
|
value3 = parser.find_next_key(opt.config, value2b.end+1, opt.end, end_report=True)
|
||
|
assert value3.value() == ';'
|
||
|
|
||
|
|
||
|
def test_key_lookaside_all():
|
||
|
""" Test getting variable arguments after keyword """
|
||
|
parser = isccfg.IscConfigParser(options_lookaside_manual)
|
||
|
assert len(parser.FILES_TO_CHECK) == 1
|
||
|
opt = find_options(parser)
|
||
|
assert isinstance(opt, isccfg.ConfigSection)
|
||
|
values = parser.find_values(opt, "dnssec-lookaside")
|
||
|
assert values is not None
|
||
|
assert len(values) >= 4
|
||
|
key = values[0].value()
|
||
|
assert key == 'dnssec-lookaside'
|
||
|
assert values[1].value() == '"."'
|
||
|
assert values[2].value() == 'trust-anchor'
|
||
|
assert values[3].value() == '"dlv.isc.org"'
|
||
|
assert values[4].value() == ';'
|
||
|
|
||
|
|
||
|
def test_key_lookaside_simple():
|
||
|
""" Test getting variable arguments after keyword """
|
||
|
parser = isccfg.IscConfigParser(options_lookaside_manual)
|
||
|
assert len(parser.FILES_TO_CHECK) == 1
|
||
|
stmts = parser.find('options.dnssec-lookaside')
|
||
|
assert stmts is not None
|
||
|
assert len(stmts) == 1
|
||
|
assert isinstance(stmts[0], isccfg.ConfigVariableSection)
|
||
|
values = stmts[0].values
|
||
|
assert len(values) >= 4
|
||
|
key = values[0].value()
|
||
|
assert key == 'dnssec-lookaside'
|
||
|
assert values[1].value() == '"."'
|
||
|
assert values[2].value() == 'trust-anchor'
|
||
|
assert values[3].value() == '"dlv.isc.org"'
|
||
|
assert values[4].value() == ';'
|
||
|
|
||
|
|
||
|
def test_find_index():
|
||
|
""" Test simplified searching for values in sections """
|
||
|
parser = isccfg.IscConfigParser(named_conf_default)
|
||
|
assert len(parser.FILES_TO_CHECK) >= 1
|
||
|
stmts = parser.find('logging.channel.severity')
|
||
|
assert stmts is not None and len(stmts) == 1
|
||
|
assert isinstance(stmts[0], isccfg.ConfigVariableSection)
|
||
|
values = stmts[0].values
|
||
|
assert len(values) >= 1
|
||
|
key = values[0].value()
|
||
|
assert key == 'severity'
|
||
|
assert values[1].value() == 'dynamic'
|
||
|
recursion = parser.find('options.recursion')
|
||
|
assert len(recursion) == 1 and len(recursion[0].values) >= 2
|
||
|
assert recursion[0].values[0].value() == 'recursion'
|
||
|
assert recursion[0].values[1].value() == 'yes'
|
||
|
|
||
|
|
||
|
def cb_view(statement, state):
|
||
|
if 'view' not in state:
|
||
|
state['view'] = {}
|
||
|
name = statement.var(1).invalue()
|
||
|
second = statement.var(2)
|
||
|
if second.type() != isccfg.ConfigSection.TYPE_BLOCK:
|
||
|
name = second.value() + '_' + name
|
||
|
state['view'][name] = statement
|
||
|
|
||
|
|
||
|
def test_key_views_lookaside():
|
||
|
""" Test getting variable arguments for views """
|
||
|
|
||
|
parser = isccfg.IscConfigParser(views_lookaside)
|
||
|
assert len(parser.FILES_TO_CHECK) == 1
|
||
|
opt = find_options(parser)
|
||
|
assert isinstance(opt, isccfg.ConfigSection)
|
||
|
opt_val = parser.find_values(opt, "dnssec-lookaside")
|
||
|
assert isinstance(opt_val[1], isccfg.ConfigSection)
|
||
|
assert opt_val[1].value() == 'no'
|
||
|
|
||
|
state = {}
|
||
|
callbacks = {
|
||
|
'view': cb_view,
|
||
|
}
|
||
|
assert len(parser.FILES_TO_CHECK) >= 1
|
||
|
cfg = parser.FILES_TO_CHECK[0]
|
||
|
parser.walk(cfg.root_section(), callbacks, state)
|
||
|
|
||
|
views = state['view']
|
||
|
assert len(views) == 2
|
||
|
|
||
|
v1 = views['IN_v1']
|
||
|
assert isinstance(v1, isccfg.ConfigVariableSection)
|
||
|
v1b = v1.firstblock()
|
||
|
assert isinstance(v1b, isccfg.ConfigSection)
|
||
|
v1_la = parser.find_val_section(v1b, "dnssec-lookaside")
|
||
|
assert isinstance(v1_la, isccfg.ConfigSection)
|
||
|
assert v1_la.value() == 'auto'
|
||
|
|
||
|
v2 = views['v2']
|
||
|
assert isinstance(v2, isccfg.ConfigVariableSection)
|
||
|
v2b = v2.firstblock()
|
||
|
assert isinstance(v2b, isccfg.ConfigSection)
|
||
|
v2_la = parser.find_values(v2b, "dnssec-lookaside")
|
||
|
assert isinstance(v2_la[1], isccfg.ConfigSection)
|
||
|
assert v2_la[1].value() == '"."'
|
||
|
assert isinstance(v2_la[3], isccfg.ConfigSection)
|
||
|
assert v2_la[3].value() == '"dlv.isc.org"'
|
||
|
|
||
|
|
||
|
def test_remove_comments():
|
||
|
""" Test removing comments works as expected """
|
||
|
|
||
|
parser = isccfg.IscConfigParser(views_lookaside)
|
||
|
assert len(parser.FILES_TO_CHECK) == 1
|
||
|
cfg = parser.FILES_TO_CHECK[0]
|
||
|
assert isinstance(cfg, isccfg.ConfigFile)
|
||
|
removed_comments = parser._remove_comments(cfg.buffer)
|
||
|
assert len(removed_comments) < len(cfg.buffer)
|
||
|
replaced_comments = parser._replace_comments(cfg.buffer)
|
||
|
assert len(replaced_comments) == len(cfg.buffer)
|
||
|
assert 'This is auto' not in replaced_comments
|
||
|
assert 'comment' not in replaced_comments
|
||
|
assert 'Note no IN' not in replaced_comments
|
||
|
|
||
|
|
||
|
def test_walk():
|
||
|
""" Test walk function of parser """
|
||
|
|
||
|
callbacks = {
|
||
|
'options': cb_state,
|
||
|
'dnssec-lookaside': cb_state,
|
||
|
'dnssec-validation': cb_state,
|
||
|
}
|
||
|
state = {}
|
||
|
parser = isccfg.IscConfigParser(views_lookaside)
|
||
|
assert len(parser.FILES_TO_CHECK) == 1
|
||
|
cfg = parser.FILES_TO_CHECK[0]
|
||
|
parser.walk(cfg.root_section(), callbacks, state)
|
||
|
assert 'options' in state
|
||
|
assert 'dnssec-lookaside' in state
|
||
|
assert 'dnssec-validation' not in state
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
test_key_views_lookaside()
|