Skip to content

Commit

Permalink
Introduce a function to extract all includes from bind configuration …
Browse files Browse the repository at this point in the history
…file

It should support chroot directories because we
use chroot environment for bind configurations in plesk.

Also we should not encounter relative paths in include directives
because bind handles they in a preatty specific way
(starting from the directory bind was started)
  • Loading branch information
Mikhail Sandakov committed Oct 4, 2024
1 parent 2666d01 commit b9df963
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 0 deletions.
1 change: 1 addition & 0 deletions pleskdistup/common/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from . import action
from . import dist
from . import dns
from . import dpkg
from . import log
from . import mariadb
Expand Down
42 changes: 42 additions & 0 deletions pleskdistup/common/src/dns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2023-2024. WebPros International GmbH. All rights reserved.
import os
from typing import Deque, List
from collections import deque

from . import log


def get_all_includes_from_bind_config(config_file: str, chroot_dir: str = "") -> List[str]:
includes: List[str] = []

if os.path.isabs(config_file):
config_file = chroot_dir + config_file
else:
config_file = os.path.join(chroot_dir, config_file)

if not os.path.exists(config_file):
return includes

queue: Deque[str] = deque([config_file])

while queue:
current_file = queue.popleft()
if not os.path.exists(current_file):
continue

with open(current_file) as f:
for line in f:
line = line.strip()
if line.startswith("include"):
include_file = line.split('"')[1]

if not os.path.isabs(include_file):
log.warn(f"Relative include path directive {line!r} from {current_file!r} is not supported. Skipping.")
continue

include_file = chroot_dir + include_file

includes.append(include_file)
queue.append(include_file)

return includes
162 changes: 162 additions & 0 deletions pleskdistup/common/tests/dnstests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Copyright 2023-2024. WebPros International GmbH. All rights reserved.
import shutil
import os
import unittest

import src.dns as dns


class TestGetIncludeFromBindConfiguration(unittest.TestCase):

def tearDown(self):
if os.path.exists("test.conf"):
os.remove("test.conf")

if os.path.exists("chroot"):
shutil.rmtree("chroot")

def test_no_config_file(self):
with open("test.conf", "w") as test_bind_config:
test_bind_config.write('''
options {
allow-recursion {
localnets;
};
directory "/var";
pid-file "/var/run/named/named.pid";
};
''')
self.assertEqual(dns.get_all_includes_from_bind_config("nonexistent.conf"), [])

def test_one_include(self):
with open("test.conf", "w") as test_file:
test_file.write('''
options {
include "/included.conf";
allow-recursion {
localnets;
};
directory "/var";
pid-file "/var/run/named/named.pid";
};
''')

self.assertEqual(dns.get_all_includes_from_bind_config("test.conf"), ["/included.conf"])

def test_include_with_spaces(self):
with open("test.conf", "w") as test_file:
test_file.write('''
options {
include "/included .conf" ; # comment
allow-recursion {
localnets;
};
directory "/var";
pid-file "/var/run/named/named.pid";
};
''')
self.assertEqual(dns.get_all_includes_from_bind_config("test.conf"), ["/included .conf"])

def test_include_with_tabs(self):
with open("test.conf", "w") as test_file:
test_file.write('''
options {
include "/included .conf" ;
allow-recursion {
localnets;
};
directory "/var";
pid-file "/var/run/named/named.pid";
};
''')
self.assertEqual(dns.get_all_includes_from_bind_config("test.conf"), ["/included .conf"])

def test_multiple_includes(self):
with open("test.conf", "w") as test_file:
test_file.write('''
options {
include "/included1.conf";
include "/included2.conf";
allow-recursion {
localnets;
};
directory "/var";
pid-file "/var/run/named/named.pid";
};
''')

self.assertEqual(dns.get_all_includes_from_bind_config("test.conf"), ["/included1.conf", "/included2.conf"])

def test_nested_includes(self):
current_dir = os.getcwd()
with open("test.conf", "w") as test_file:
test_file.write(f'''
options {{
include "{current_dir}/included1.conf";
include "{current_dir}/included2.conf";
allow-recursion {{
localnets;
}};
directory "/var";
pid-file "/var/run/named/named.pid";
}};
''')

with open("included1.conf", "w") as test_file:
test_file.write(f'include "{current_dir}/included3.conf";\n')

with open("included2.conf", "w") as test_file:
test_file.write(f'include "{current_dir}/included4.conf";\n')

self.assertEqual(dns.get_all_includes_from_bind_config("test.conf"), [f"{current_dir}/included1.conf",
f"{current_dir}/included2.conf",
f"{current_dir}/included3.conf",
f"{current_dir}/included4.conf"])

os.remove("included1.conf")
os.remove("included2.conf")

def test_chroot_with_absolute_path(self):
os.makedirs("chroot", exist_ok=True)

with open("chroot/test.conf", "w") as test_file:
test_file.write('''
options {
include "/included.conf";
allow-recursion {
localnets;
};
directory "/var";
pid-file "/var/run/named/named.pid";
};
''')
self.assertEqual(dns.get_all_includes_from_bind_config("/test.conf", chroot_dir="chroot"), ["chroot/included.conf"])

def test_nested_includes_with_chroot(self):
os.makedirs("chroot", exist_ok=True)

with open("chroot/test.conf", "w") as test_file:
test_file.write('''
options {
include "/included1.conf";
include "/included2.conf";
allow-recursion {
localnets;
};
directory "/var";
pid-file "/var/run/named/named.pid";
};
''')

with open("chroot/included1.conf", "w") as test_file:
test_file.write('include "/subdir/included3.conf";\n')

with open("chroot/included2.conf", "w") as test_file:
test_file.write('include "/subdir/included4.conf";\n')

self.assertEqual(dns.get_all_includes_from_bind_config("test.conf", chroot_dir="chroot"), [
"chroot/included1.conf",
"chroot/included2.conf",
"chroot/subdir/included3.conf",
"chroot/subdir/included4.conf"
])

0 comments on commit b9df963

Please sign in to comment.