summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/TEST-03-JOBS/test-jobs.sh28
-rwxr-xr-xtest/networkd-test.py190
-rw-r--r--test/test-execute/exec-inaccessiblepaths-mount-propagation.service7
-rw-r--r--test/test-execute/exec-privatedevices-no-capability-mknod.service7
-rw-r--r--test/test-execute/exec-privatedevices-yes-capability-mknod.service7
-rw-r--r--test/test-execute/exec-readonlypaths-mount-propagation.service7
-rw-r--r--test/test-execute/exec-readonlypaths.service7
-rw-r--r--test/test-execute/exec-readwritepaths-mount-propagation.service7
8 files changed, 231 insertions, 29 deletions
diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/TEST-03-JOBS/test-jobs.sh
index 0c7d4439a2..fa6cf4181a 100755
--- a/test/TEST-03-JOBS/test-jobs.sh
+++ b/test/TEST-03-JOBS/test-jobs.sh
@@ -49,4 +49,32 @@ systemctl stop --job-mode=replace-irreversibly unstoppable.service || exit 1
# Shutdown of the container/VM will hang if not.
systemctl start unstoppable.service || exit 1
+# Test waiting for a started unit(s) to terminate again
+cat <<EOF > /run/systemd/system/wait2.service
+[Unit]
+Description=Wait for 2 seconds
+[Service]
+ExecStart=/bin/sh -ec 'sleep 2'
+EOF
+cat <<EOF > /run/systemd/system/wait5fail.service
+[Unit]
+Description=Wait for 5 seconds and fail
+[Service]
+ExecStart=/bin/sh -ec 'sleep 5; false'
+EOF
+
+# wait2 succeeds
+START_SEC=$(date -u '+%s')
+systemctl start --wait wait2.service || exit 1
+END_SEC=$(date -u '+%s')
+ELAPSED=$(($END_SEC-$START_SEC))
+[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 3 ]] || exit 1
+
+# wait5fail fails, so systemctl should fail
+START_SEC=$(date -u '+%s')
+! systemctl start --wait wait2.service wait5fail.service || exit 1
+END_SEC=$(date -u '+%s')
+ELAPSED=$(($END_SEC-$START_SEC))
+[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
+
touch /testok
diff --git a/test/networkd-test.py b/test/networkd-test.py
index bfa1bf3580..3091722fc1 100755
--- a/test/networkd-test.py
+++ b/test/networkd-test.py
@@ -54,7 +54,6 @@ class ClientTestBase:
self.workdir_obj = tempfile.TemporaryDirectory()
self.workdir = self.workdir_obj.name
self.config = '/run/systemd/network/test_eth42.network'
- os.makedirs(os.path.dirname(self.config), exist_ok=True)
# avoid "Failed to open /dev/tty" errors in containers
os.environ['SYSTEMD_LOG_TARGET'] = 'journal'
@@ -77,10 +76,14 @@ class ClientTestBase:
def tearDown(self):
self.shutdown_iface()
- if os.path.exists(self.config):
- os.unlink(self.config)
subprocess.call(['systemctl', 'stop', 'systemd-networkd'])
+ def writeConfig(self, fname, contents):
+ os.makedirs(os.path.dirname(fname), exist_ok=True)
+ with open(fname, 'w') as f:
+ f.write(contents)
+ self.addCleanup(os.remove, fname)
+
def show_journal(self, unit):
'''Show journal of given unit since start of the test'''
@@ -107,8 +110,8 @@ class ClientTestBase:
def do_test(self, coldplug=True, ipv6=False, extra_opts='',
online_timeout=10, dhcp_mode='yes'):
subprocess.check_call(['systemctl', 'start', 'systemd-resolved'])
- with open(self.config, 'w') as f:
- f.write('''[Match]
+ self.writeConfig(self.config, '''\
+[Match]
Name=%s
[Network]
DHCP=%s
@@ -225,6 +228,32 @@ DHCP=%s
self.do_test(coldplug=False, ipv6=True)
def test_route_only_dns(self):
+ self.writeConfig('/run/systemd/network/myvpn.netdev', '''\
+[NetDev]
+Name=dummy0
+Kind=dummy
+MACAddress=12:34:56:78:9a:bc''')
+ self.writeConfig('/run/systemd/network/myvpn.network', '''\
+[Match]
+Name=dummy0
+[Network]
+Address=192.168.42.100
+DNS=192.168.42.1
+Domains= ~company''')
+
+ self.do_test(coldplug=True, ipv6=False,
+ extra_opts='IPv6AcceptRouterAdvertisements=False')
+
+ with open(RESOLV_CONF) as f:
+ contents = f.read()
+ # ~company is not a search domain, only a routing domain
+ self.assertNotRegex(contents, 'search.*company')
+ # our global server should appear
+ self.assertIn('nameserver 192.168.5.1\n', contents)
+ # should not have domain-restricted server as global server
+ self.assertNotIn('nameserver 192.168.42.1\n', contents)
+
+ def test_route_only_dns_all_domains(self):
with open('/run/systemd/network/myvpn.netdev', 'w') as f:
f.write('''[NetDev]
Name=dummy0
@@ -236,7 +265,7 @@ Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1
-Domains= ~company''')
+Domains= ~company ~.''')
self.addCleanup(os.remove, '/run/systemd/network/myvpn.netdev')
self.addCleanup(os.remove, '/run/systemd/network/myvpn.network')
@@ -245,10 +274,14 @@ Domains= ~company''')
with open(RESOLV_CONF) as f:
contents = f.read()
- # ~company is not a search domain, only a routing domain
- self.assertNotRegex(contents, 'search.*company')
- # our global server should appear
- self.assertIn('nameserver 192.168.5.1\n', contents)
+
+ # ~company is not a search domain, only a routing domain
+ self.assertNotRegex(contents, 'search.*company')
+
+ # our global server should appear
+ self.assertIn('nameserver 192.168.5.1\n', contents)
+ # should have company server as global server due to ~.
+ self.assertIn('nameserver 192.168.42.1\n', contents)
@unittest.skipUnless(have_dnsmasq, 'dnsmasq not installed')
@@ -259,7 +292,7 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase):
super().setUp()
self.dnsmasq = None
- def create_iface(self, ipv6=False):
+ def create_iface(self, ipv6=False, dnsmasq_opts=None):
'''Create test interface with DHCP server behind it'''
# add veth pair
@@ -280,6 +313,8 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase):
extra_opts = ['--enable-ra', '--dhcp-range=2600::10,2600::20']
else:
extra_opts = []
+ if dnsmasq_opts:
+ extra_opts += dnsmasq_opts
self.dnsmasq = subprocess.Popen(
['dnsmasq', '--keep-in-foreground', '--log-queries',
'--log-facility=' + self.dnsmasq_log, '--conf-file=/dev/null',
@@ -304,6 +339,80 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase):
with open(self.dnsmasq_log) as f:
sys.stdout.write('\n\n---- dnsmasq log ----\n%s\n------\n\n' % f.read())
+ def test_resolved_domain_restricted_dns(self):
+ '''resolved: domain-restricted DNS servers'''
+
+ # create interface for generic connections; this will map all DNS names
+ # to 192.168.42.1
+ self.create_iface(dnsmasq_opts=['--address=/#/192.168.42.1'])
+ self.writeConfig('/run/systemd/network/general.network', '''\
+[Match]
+Name=%s
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=False''' % self.iface)
+
+ # create second device/dnsmasq for a .company/.lab VPN interface
+ # static IPs for simplicity
+ subprocess.check_call(['ip', 'link', 'add', 'name', 'testvpnclient', 'type',
+ 'veth', 'peer', 'name', 'testvpnrouter'])
+ self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', 'testvpnrouter'])
+ subprocess.check_call(['ip', 'a', 'flush', 'dev', 'testvpnrouter'])
+ subprocess.check_call(['ip', 'a', 'add', '10.241.3.1/24', 'dev', 'testvpnrouter'])
+ subprocess.check_call(['ip', 'link', 'set', 'testvpnrouter', 'up'])
+
+ vpn_dnsmasq_log = os.path.join(self.workdir, 'dnsmasq-vpn.log')
+ vpn_dnsmasq = subprocess.Popen(
+ ['dnsmasq', '--keep-in-foreground', '--log-queries',
+ '--log-facility=' + vpn_dnsmasq_log, '--conf-file=/dev/null',
+ '--dhcp-leasefile=/dev/null', '--bind-interfaces',
+ '--interface=testvpnrouter', '--except-interface=lo',
+ '--address=/math.lab/10.241.3.3', '--address=/cantina.company/10.241.4.4'])
+ self.addCleanup(vpn_dnsmasq.wait)
+ self.addCleanup(vpn_dnsmasq.kill)
+
+ self.writeConfig('/run/systemd/network/vpn.network', '''\
+[Match]
+Name=testvpnclient
+[Network]
+IPv6AcceptRA=False
+Address=10.241.3.2/24
+DNS=10.241.3.1
+Domains= ~company ~lab''')
+
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+ subprocess.check_call([self.networkd_wait_online, '--interface', self.iface,
+ '--interface=testvpnclient', '--timeout=20'])
+
+ # ensure we start fresh with every test
+ subprocess.check_call(['systemctl', 'restart', 'systemd-resolved'])
+
+ # test vpnclient specific domains; these should *not* be answered by
+ # the general DNS
+ out = subprocess.check_output(['systemd-resolve', 'math.lab'])
+ self.assertIn(b'math.lab: 10.241.3.3', out)
+ out = subprocess.check_output(['systemd-resolve', 'kettle.cantina.company'])
+ self.assertIn(b'kettle.cantina.company: 10.241.4.4', out)
+
+ # test general domains
+ out = subprocess.check_output(['systemd-resolve', 'megasearch.net'])
+ self.assertIn(b'megasearch.net: 192.168.42.1', out)
+
+ with open(self.dnsmasq_log) as f:
+ general_log = f.read()
+ with open(vpn_dnsmasq_log) as f:
+ vpn_log = f.read()
+
+ # VPN domains should only be sent to VPN DNS
+ self.assertRegex(vpn_log, 'query.*math.lab')
+ self.assertRegex(vpn_log, 'query.*cantina.company')
+ self.assertNotIn('lab', general_log)
+ self.assertNotIn('company', general_log)
+
+ # general domains should not be sent to the VPN DNS
+ self.assertRegex(general_log, 'query.*megasearch.net')
+ self.assertNotIn('megasearch.net', vpn_log)
+
class NetworkdClientTest(ClientTestBase, unittest.TestCase):
'''Test networkd client against networkd server'''
@@ -320,7 +429,8 @@ class NetworkdClientTest(ClientTestBase, unittest.TestCase):
(fd, script) = tempfile.mkstemp(prefix='networkd-router.sh')
self.addCleanup(os.remove, script)
with os.fdopen(fd, 'w+') as f:
- f.write('''#!/bin/sh -eu
+ f.write('''\
+#!/bin/sh -eu
mkdir -p /run/systemd/network
mkdir -p /run/systemd/netif
mount -t tmpfs none /run/systemd/network
@@ -398,20 +508,18 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ { s/^.*=//
# we don't use this interface for this test
self.if_router = None
- with open('/run/systemd/network/test.netdev', 'w') as f:
- f.write('''[NetDev]
+ self.writeConfig('/run/systemd/network/test.netdev', '''\
+[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- with open('/run/systemd/network/test.network', 'w') as f:
- f.write('''[Match]
+ self.writeConfig('/run/systemd/network/test.network', '''\
+[Match]
Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1
Domains= one two three four five six seven eight nine ten''')
- self.addCleanup(os.remove, '/run/systemd/network/test.netdev')
- self.addCleanup(os.remove, '/run/systemd/network/test.network')
subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
@@ -432,23 +540,18 @@ Domains= one two three four five six seven eight nine ten''')
name_prefix = 'a' * 60
- with open('/run/systemd/network/test.netdev', 'w') as f:
- f.write('''[NetDev]
+ self.writeConfig('/run/systemd/network/test.netdev', '''\
+[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- with open('/run/systemd/network/test.network', 'w') as f:
- f.write('''[Match]
+ self.writeConfig('/run/systemd/network/test.network', '''\
+[Match]
Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1
-Domains=''')
- for i in range(5):
- f.write('%s%i ' % (name_prefix, i))
-
- self.addCleanup(os.remove, '/run/systemd/network/test.netdev')
- self.addCleanup(os.remove, '/run/systemd/network/test.network')
+Domains={p}0 {p}1 {p}2 {p}3 {p}4'''.format(p=name_prefix))
subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
@@ -458,9 +561,38 @@ Domains=''')
if ' one' in contents:
break
time.sleep(0.1)
- self.assertRegex(contents, 'search .*%(p)s0 %(p)s1 %(p)s2' % {'p': name_prefix})
+ self.assertRegex(contents, 'search .*{p}0 {p}1 {p}2'.format(p=name_prefix))
self.assertIn('# Total length of all search domains is too long, remaining ones ignored.', contents)
+ def test_dropin(self):
+ # we don't use this interface for this test
+ self.if_router = None
+
+ self.writeConfig('/run/systemd/network/test.netdev', '''\
+[NetDev]
+Name=dummy0
+Kind=dummy
+MACAddress=12:34:56:78:9a:bc''')
+ self.writeConfig('/run/systemd/network/test.network', '''\
+[Match]
+Name=dummy0
+[Network]
+Address=192.168.42.100
+DNS=192.168.42.1''')
+ self.writeConfig('/run/systemd/network/test.network.d/dns.conf', '''\
+[Network]
+DNS=127.0.0.1''')
+
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+
+ for timeout in range(50):
+ with open(RESOLV_CONF) as f:
+ contents = f.read()
+ if ' 127.0.0.1' in contents:
+ break
+ time.sleep(0.1)
+ self.assertIn('nameserver 192.168.42.1\n', contents)
+ self.assertIn('nameserver 127.0.0.1\n', contents)
if __name__ == '__main__':
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
diff --git a/test/test-execute/exec-inaccessiblepaths-mount-propagation.service b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service
new file mode 100644
index 0000000000..23c6ff3f93
--- /dev/null
+++ b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Test to make sure that InaccessiblePaths= disconnect mount propagation
+
+[Service]
+InaccessiblePaths=-/i-dont-exist
+ExecStart=/bin/sh -x -c 'mkdir -p /TEST; mount -t tmpfs tmpfs /TEST; grep TEST /proc/self/mountinfo && ! grep TEST /proc/$${PPID}/mountinfo && ! grep TEST /proc/1/mountinfo'
+Type=oneshot
diff --git a/test/test-execute/exec-privatedevices-no-capability-mknod.service b/test/test-execute/exec-privatedevices-no-capability-mknod.service
new file mode 100644
index 0000000000..6d39469da8
--- /dev/null
+++ b/test/test-execute/exec-privatedevices-no-capability-mknod.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Test CAP_MKNOD capability for PrivateDevices=no
+
+[Service]
+PrivateDevices=no
+ExecStart=/bin/sh -x -c 'capsh --print | grep cap_mknod'
+Type=oneshot
diff --git a/test/test-execute/exec-privatedevices-yes-capability-mknod.service b/test/test-execute/exec-privatedevices-yes-capability-mknod.service
new file mode 100644
index 0000000000..fb1fc2875a
--- /dev/null
+++ b/test/test-execute/exec-privatedevices-yes-capability-mknod.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Test CAP_MKNOD capability for PrivateDevices=yes
+
+[Service]
+PrivateDevices=yes
+ExecStart=/bin/sh -x -c '! capsh --print | grep cap_mknod'
+Type=oneshot
diff --git a/test/test-execute/exec-readonlypaths-mount-propagation.service b/test/test-execute/exec-readonlypaths-mount-propagation.service
new file mode 100644
index 0000000000..237cbb2efb
--- /dev/null
+++ b/test/test-execute/exec-readonlypaths-mount-propagation.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Test to make sure that passing ReadOnlyPaths= disconnect mount propagation
+
+[Service]
+ReadOnlyPaths=-/i-dont-exist
+ExecStart=/bin/sh -x -c 'mkdir -p /TEST; mount -t tmpfs tmpfs /TEST; grep TEST /proc/self/mountinfo && ! grep TEST /proc/$${PPID}/mountinfo && ! grep TEST /proc/1/mountinfo'
+Type=oneshot
diff --git a/test/test-execute/exec-readonlypaths.service b/test/test-execute/exec-readonlypaths.service
new file mode 100644
index 0000000000..6866fdc700
--- /dev/null
+++ b/test/test-execute/exec-readonlypaths.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Test for ReadOnlyPaths=
+
+[Service]
+ReadOnlyPaths=/etc -/i-dont-exist /usr
+ExecStart=/bin/sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var'
+Type=oneshot
diff --git a/test/test-execute/exec-readwritepaths-mount-propagation.service b/test/test-execute/exec-readwritepaths-mount-propagation.service
new file mode 100644
index 0000000000..466ce6c747
--- /dev/null
+++ b/test/test-execute/exec-readwritepaths-mount-propagation.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Test to make sure that passing ReadWritePaths= disconnect mount propagation
+
+[Service]
+ReadWritePaths=-/i-dont-exist
+ExecStart=/bin/sh -x -c 'mkdir -p /TEST; mount -t tmpfs tmpfs /TEST; grep TEST /proc/self/mountinfo && ! grep TEST /proc/$${PPID}/mountinfo && ! grep TEST /proc/1/mountinfo'
+Type=oneshot