test: Introduce systemd-resolved test suite

Resolves: #19599
This commit is contained in:
Frantisek Sumsal 2022-04-17 15:50:16 +02:00
parent 17082e8ac1
commit fb6f25d7b9
11 changed files with 545 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
all setup run clean clean-again:
@TEST_BASE_DIR=../ ./test.sh --$@
.PHONY: all setup run clean clean-again

43
test/TEST-75-RESOLVED/test.sh Executable file
View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Tests for systemd-resolved"
TEST_NO_QEMU=1
NSPAWN_ARGUMENTS="--private-network"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
if ! command -v knotd >/dev/null; then
echo "This test requires Knot DNS server, skipping..."
exit 0
fi
# We need at least Knot 3.0 which support (among others) the ds-push directive
if ! knotc -c "${TEST_BASE_DIR:?}/knot-data/knot.conf" conf-check; then
echo "This test requires at least Knot 3.0. skipping..."
exit 0
fi
test_append_files() {
local workspace="${1:?}"
# Install knot
image_install kzonecheck keymgr kjournalprint knotc knotd
image_install /lib/tmpfiles.d/knot.conf
image_install "${ROOTLIBDIR:?}/system/knot.service"
image_install -o /etc/dbus-1/system.d/cz.nic.knotd.conf
# Copy over our configuration
mkdir -p "${workspace:?}/var/lib/knot/zones/" "${workspace:?}/etc/knot/"
cp -rfv "${TEST_BASE_DIR:?}"/knot-data/zones/* "$workspace/var/lib/knot/zones/"
cp -fv "${TEST_BASE_DIR:?}/knot-data/knot.conf" "$workspace/etc/knot/knot.conf"
chgrp -R knot "$workspace/etc/knot/" "$workspace/var/lib/knot/"
chmod -R ug+rwX "$workspace/var/lib/knot/"
chmod -R g+r "$workspace/etc/knot/"
# Install DNS-related utilities (usually found in the bind-utils package)
image_install delv dig host nslookup
}
do_test "$@"

116
test/knot-data/knot.conf Normal file
View file

@ -0,0 +1,116 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
server:
rundir: "/run/knot"
user: knot:knot
listen: 10.0.0.1@53
log:
- target: syslog
any: info
database:
storage: "/var/lib/knot"
acl:
- id: update_acl
address: 10.0.0.0/24
action: update
remote:
- id: parent_zone_server
address: 10.0.0.1@53
submission:
- id: parent_zone_sbm
check-interval: 2s
parent: [parent_zone_server]
# Auto ZSK/KSK rollover for DNSSEC-enabled zones + pushing the respective DS
# records to the parent zone
policy:
- id: auto_rollover
algorithm: ECDSAP256SHA256
cds-cdnskey-publish: always
ds-push: parent_zone_server
ksk-lifetime: 365d
ksk-submission: parent_zone_sbm
propagation-delay: 1s
signing-threads: 4
zone-max-ttl: 1s
zsk-lifetime: 60d
# Same as auto_rollover, but with NSEC3 turned on
policy:
- id: auto_rollover_nsec3
algorithm: ECDSAP256SHA256
cds-cdnskey-publish: always
ds-push: parent_zone_server
ksk-lifetime: 365d
ksk-submission: parent_zone_sbm
nsec3: on
nsec3-iterations: 10
propagation-delay: 1s
signing-threads: 4
zone-max-ttl: 1s
zsk-lifetime: 60d
policy:
- id: untrusted
cds-cdnskey-publish: none
# Manual ZSK/KSK management
policy:
- id: manual
manual: on
# Sign everything by default and propagate the respective DS records to the parent
template:
- id: default
acl: update_acl
dnssec-policy: auto_rollover
dnssec-signing: on
file: "%s.zone"
semantic-checks: on
storage: "/var/lib/knot/zones"
# A template for unsigned zones (i.e. without DNSSEC)
template:
- id: unsigned
dnssec-signing: off
file: "%s.zone"
semantic-checks: on
storage: "/var/lib/knot/zones"
zone:
# Create our own DNSSEC-aware root zone, so we can test the whole chain of
# trust. This needs a ZSK/KSK keypair to be generated before running knot +
# adding the respective key(s) to resolved's trust anchor store (see the
# test script for the setup steps).
- domain: .
dnssec-policy: manual
file: "root.zone"
# Turn NSEC3 on for the test. zone to spice things up
- domain: test
dnssec-policy: auto_rollover_nsec3
# A fully (pre-)signed zone
- domain: signed.test
# A fully (online)-signed zone
# See: https://www.knot-dns.cz/docs/3.1/singlehtml/index.html#mod-onlinesign
# Note: ds-push is not supported in mod-onlinesign, so we have to push
# the DS records to the parent zone manually (see the test script)
- domain: onlinesign.test
module: mod-onlinesign
dnssec-signing: off
# Signed zone without propagated DS records to test the allow-downgrade
# feature
- domain: untrusted.test
dnssec-policy: untrusted
# An unsigned zone
- domain: unsigned.test
template: unsigned

View file

@ -0,0 +1,21 @@
; SPDX-License-Identifier: LGPL-2.1-or-later
$TTL 86400
$ORIGIN onlinesign.test.
@ IN SOA ns1.unsigned.test. root.unsigned.test. (
42 ; serial
3H ; refresh
15M ; retry
1W ; expire
1D ; minimum TTL
)
; NS info
NS ns1.unsigned.test.
TXT "hello from onlinesign"
*.wild TXT "this is an onlinesign wildcard"
; No A/AAAA record for the $ORIGIN
sub A 10.0.0.133

View file

@ -0,0 +1,14 @@
; SPDX-License-Identifier: LGPL-2.1-or-later
$TTL 300
. IN SOA ns1.unsigned.test. root.unsigned.test. (
20220416 ; serial
3H ; refresh
15M ; retry
1W ; expire
1D ; minimum TTL
)
. NS ns1.unsigned.test
ns1.unsigned.test A 10.0.0.1
test NS ns1.unsigned.test

View file

@ -0,0 +1,42 @@
; SPDX-License-Identifier: LGPL-2.1-or-later
$TTL 86400
$ORIGIN signed.test.
@ IN SOA ns1.unsigned.test. root.unsigned.test. (
42 ; serial
3H ; refresh
15M ; retry
1W ; expire
1D ; minimum TTL
)
; NS info
NS ns1.unsigned.test.
*.wild TXT "this is a wildcard"
@ MX 10 mail.signed.test.
A 10.0.0.10
mail A 10.0.0.11
; https://github.com/systemd/systemd/issues/22002
dupe A 10.0.0.12
dupe A 10.0.0.13
; CNAME_REDIRECTS_MAX is 16, so let's test something close to that
cname-chain CNAME follow1.signed.test.
follow1 CNAME follow2.signed.test.
follow2 CNAME follow3.nested.signed.test.
follow3.nested CNAME follow4.signed.test.
follow4 CNAME follow5.a.b.c.d.signed.test.
follow5.a.b.c.d CNAME follow6.signed.test.
follow6 CNAME follow7.what.is.love.signed.test.
follow7.what.is.love CNAME follow8.signed.test.
follow8 CNAME follow9.almost.there.signed.test.
follow9.almost.there CNAME follow10.so.close.signed.test.
follow10.so.close CNAME follow11.yet.so.far.signed.test.
follow11.yet.so.far CNAME follow12.getting.hot.signed.test.
follow12.getting.hot CNAME follow13.almost.final.signed.test.
follow13.almost.final CNAME follow14.final.signed.test.
follow14.final A 10.0.0.14

View file

@ -0,0 +1,19 @@
; SPDX-License-Identifier: LGPL-2.1-or-later
$TTL 86400
$ORIGIN test.
@ IN SOA ns1.unsigned.test. root.unsigned.test. (
42 ; serial
3H ; refresh
15M ; retry
1W ; expire
1D ; minimum TTL
)
; NS info
@ NS ns1.unsigned
ns1.signed A 10.0.0.1
onlinesign NS ns1.unsigned
signed NS ns1.unsigned
unsigned NS ns1.unsigned

View file

@ -0,0 +1,20 @@
; SPDX-License-Identifier: LGPL-2.1-or-later
$TTL 86400
$ORIGIN unsigned.test.
@ IN SOA ns1.unsigned.test. root.unsigned.test. (
42 ; serial
3H ; refresh
15M ; retry
1W ; expire
1D ; minimum TTL
)
; NS info
@ NS ns1.unsigned.test.
ns1 A 10.0.0.1
@ MX 15 mail.unsigned.test.
A 10.0.0.101
mail A 10.0.0.111

View file

@ -0,0 +1,21 @@
; SPDX-License-Identifier: LGPL-2.1-or-later
$TTL 86400
$ORIGIN untrusted.test.
@ IN SOA ns1.unsigned.test. root.unsigned.test. (
42 ; serial
3H ; refresh
15M ; retry
1W ; expire
1D ; minimum TTL
)
; NS info
@ NS ns1.unsigned.test.
*.wild TXT "this is an untrusted wildcard"
@ MX 10 mail.untrusted.test.
A 10.0.0.121
mail A 10.0.0.121

View file

@ -0,0 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Tests for systemd-resolved
[Service]
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot
StandardOutput=journal+console
StandardError=journal+console

233
test/units/testsuite-75.sh Executable file
View file

@ -0,0 +1,233 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# vi: ts=4 sw=4 tw=0 et:
set -eux
set -o pipefail
: >/failed
RUN_OUT="$(mktemp)"
run() {
"$@" |& tee "$RUN_OUT"
}
### SETUP ###
# Configure network
hostnamectl hostname ns1.unsigned.test
echo "10.0.0.1 ns1.unsigned.test" >>/etc/hosts
mkdir -p /etc/systemd/network
cat >/etc/systemd/network/dns0.netdev <<EOF
[NetDev]
Name=dns0
Kind=dummy
EOF
cat >/etc/systemd/network/dns0.network <<EOF
[Match]
Name=dns0
[Network]
Address=10.0.0.1/24
DNSSEC=allow-downgrade
DNS=10.0.0.1
EOF
{
echo "FallbackDNS="
echo "DNSSEC=allow-downgrade"
echo "DNSOverTLS=opportunistic"
} >>/etc/systemd/resolved.conf
ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
# Override the default NTA list, which turns off DNSSEC validation for (among
# others) the test. domain
mkdir -p "/etc/dnssec-trust-anchors.d/"
echo local >/etc/dnssec-trust-anchors.d/local.negative
# Sign the root zone
keymgr . generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes
# Create a trust anchor for resolved with our root zone
keymgr . ds | sed 's/ DS/ IN DS/g' >/etc/dnssec-trust-anchors.d/root.positive
# Create a bind-compatible trust anchor (for delv)
# Note: the trust-anchors directive is relatively new, so use the original
# managed-keys one until it's widespread enough
{
echo 'managed-keys {'
keymgr . dnskey | sed -r 's/^\. DNSKEY ([0-9]+ [0-9]+ [0-9]+) (.+)$/. static-key \1 "\2";/g'
echo '};'
} >/etc/bind.keys
# Start the services
systemctl unmask systemd-networkd systemd-resolved
systemctl start systemd-networkd systemd-resolved
systemctl start knot
# Wait a bit for the keys to propagate
sleep 4
networkctl status
resolvectl status
resolvectl log-level debug
# We need to manually propagate the DS records of onlinesign.test. to the parent
# zone, since they're generated online
knotc zone-begin test.
while read -ra line; do
knotc zone-set test. "${line[@]}"
done < <(keymgr onlinesign.test ds)
knotc zone-commit test.
### SETUP END ###
: "--- nss-resolve/nss-myhostname tests"
# Sanity check
run getent -s resolve hosts ns1.unsigned.test
grep -qE "^10\.0\.0\.1\s+ns1\.unsigned\.test" "$RUN_OUT"
# Issue: https://github.com/systemd/systemd/issues/18812
# PR: https://github.com/systemd/systemd/pull/18896
# Follow-up issue: https://github.com/systemd/systemd/issues/23152
# Follow-up PR: https://github.com/systemd/systemd/pull/23161
# With IPv6 enabled
run getent -s resolve hosts localhost
grep -qE "^::1\s+localhost" "$RUN_OUT"
run getent -s myhostname hosts localhost
grep -qE "^::1\s+localhost" "$RUN_OUT"
# With IPv6 disabled
sysctl -w net.ipv6.conf.all.disable_ipv6=1
run getent -s resolve hosts localhost
grep -qE "^127\.0\.0\.1\s+localhost" "$RUN_OUT"
run getent -s myhostname hosts localhost
grep -qE "^127\.0\.0\.1\s+localhost" "$RUN_OUT"
sysctl -w net.ipv6.conf.all.disable_ipv6=0
: "--- Basic resolved tests ---"
# Issue: https://github.com/systemd/systemd/issues/22229
# PR: https://github.com/systemd/systemd/pull/22231
FILTERED_NAMES=(
"0.in-addr.arpa"
"255.255.255.255.in-addr.arpa"
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"
"hello.invalid"
)
for name in "${FILTERED_NAMES[@]}"; do
(! run host "$name")
grep -qF "NXDOMAIN" "$RUN_OUT"
done
# Follow-up
# Issue: https://github.com/systemd/systemd/issues/22401
# PR: https://github.com/systemd/systemd/pull/22414
run dig +noall +authority +comments SRV .
grep -qF "status: NOERROR" "$RUN_OUT"
grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT"
: "--- ZONE: unsigned.test. ---"
run dig @10.0.0.1 +short unsigned.test
grep -qF "10.0.0.101" "$RUN_OUT"
run resolvectl query unsigned.test
grep -qF "unsigned.test: 10.0.0.10" "$RUN_OUT"
grep -qF "authenticated: no" "$RUN_OUT"
run dig @10.0.0.1 +short MX unsigned.test
grep -qF "15 mail.unsigned.test." "$RUN_OUT"
run resolvectl query --legend=no -t MX unsigned.test
grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT"
: "--- ZONE: signed.systemd (static DNSSEC) ---"
# Check the trust chain (with and without systemd-resolved in between
# Issue: https://github.com/systemd/systemd/issues/22002
# PR: https://github.com/systemd/systemd/pull/23289
run delv @10.0.0.1 signed.test
grep -qF "; fully validated" "$RUN_OUT"
run delv signed.test
grep -qF "; fully validated" "$RUN_OUT"
run dig +short signed.test
grep -qF "10.0.0.10" "$RUN_OUT"
run resolvectl query signed.test
grep -qF "signed.test: 10.0.0.10" "$RUN_OUT"
grep -qF "authenticated: yes" "$RUN_OUT"
run dig @10.0.0.1 +short MX signed.test
grep -qF "10 mail.signed.test." "$RUN_OUT"
run resolvectl query --legend=no -t MX signed.test
grep -qF "signed.test IN MX 10 mail.signed.test" "$RUN_OUT"
# Check a non-existent domain
run dig +dnssec this.does.not.exist.signed.test
grep -qF "status: NXDOMAIN" "$RUN_OUT"
# Check a wildcard record
run resolvectl query -t TXT this.should.be.authenticated.wild.signed.test
grep -qF 'this.should.be.authenticated.wild.signed.test IN TXT "this is a wildcard"' "$RUN_OUT"
grep -qF "authenticated: yes" "$RUN_OUT"
# DNSSEC validation with multiple records of the same type for the same name
# Issue: https://github.com/systemd/systemd/issues/22002
# PR: https://github.com/systemd/systemd/pull/23289
run delv @10.0.0.1 dupe.signed.test
grep -qF "; fully validated" "$RUN_OUT"
run delv dupe.signed.test
grep -qF "; fully validated" "$RUN_OUT"
# Test resolution of CNAME chains
run resolvectl query -t A cname-chain.signed.test
grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT"
grep -qF "authenticated: yes" "$RUN_OUT"
# Non-existing RR + CNAME chain
run dig +dnssec AAAA cname-chain.signed.test
grep -qF "status: NOERROR" "$RUN_OUT"
grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---"
# Check the trust chain (with and without systemd-resolved in between
# Issue: https://github.com/systemd/systemd/issues/22002
# PR: https://github.com/systemd/systemd/pull/23289
run delv @10.0.0.1 sub.onlinesign.test
grep -qF "; fully validated" "$RUN_OUT"
run delv sub.onlinesign.test
grep -qF "; fully validated" "$RUN_OUT"
run dig +short sub.onlinesign.test
grep -qF "10.0.0.133" "$RUN_OUT"
run resolvectl query sub.onlinesign.test
grep -qF "sub.onlinesign.test: 10.0.0.133" "$RUN_OUT"
grep -qF "authenticated: yes" "$RUN_OUT"
run dig @10.0.0.1 +short TXT onlinesign.test
grep -qF '"hello from onlinesign"' "$RUN_OUT"
run resolvectl query --legend=no -t TXT onlinesign.test
grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT"
# Check a non-existent domain
# Note: mod-onlinesign utilizes Minimally Covering NSEC Records, hence the
# different response than with "standard" DNSSEC
run dig +dnssec this.does.not.exist.onlinesign.test
grep -qF "status: NOERROR" "$RUN_OUT"
grep -qF "NSEC \\000.this.does.not.exist.onlinesign.test." "$RUN_OUT"
# Check a wildcard record
run resolvectl query -t TXT this.should.be.authenticated.wild.onlinesign.test
grep -qF 'this.should.be.authenticated.wild.onlinesign.test IN TXT "this is an onlinesign wildcard"' "$RUN_OUT"
grep -qF "authenticated: yes" "$RUN_OUT"
: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---"
run dig +short untrusted.test
grep -qF "10.0.0.121" "$RUN_OUT"
run resolvectl query untrusted.test
grep -qF "untrusted.test: 10.0.0.121" "$RUN_OUT"
grep -qF "authenticated: no" "$RUN_OUT"
# Issue: https://github.com/systemd/systemd/issues/19472
# 1) Query for a non-existing RR should return NOERROR + NSEC (?), not NXDOMAIN
# FIXME: re-enable once the issue is resolved
#run dig +dnssec AAAA untrusted.test
#grep -qF "status: NOERROR" "$RUN_OUT"
#grep -qE "^untrusted\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
## 2) Query for a non-existing name should return NXDOMAIN, not SERVFAIL
#run dig +dnssec this.does.not.exist.untrusted.test
#grep -qF "status: NXDOMAIN" "$RUN_OUT"
touch /testok
rm /failed