diff options
Diffstat (limited to '')
-rw-r--r-- | debian/tests/control | 13 | ||||
-rw-r--r-- | debian/tests/kea-ctrl-agent-debconf | 266 | ||||
-rw-r--r-- | debian/tests/kea-dhcp4 | 277 | ||||
-rw-r--r-- | debian/tests/kea-dhcp4.conf.template | 71 | ||||
-rw-r--r-- | debian/tests/smoke-tests | 63 |
5 files changed, 690 insertions, 0 deletions
diff --git a/debian/tests/control b/debian/tests/control new file mode 100644 index 0000000..bbed706 --- /dev/null +++ b/debian/tests/control @@ -0,0 +1,13 @@ +# Keep this test as the first, as it will verify the default installation +# behavior wrt kea-ctrl-agent password configuration +Tests: kea-ctrl-agent-debconf +Restrictions: needs-root, allow-stderr +Depends: kea-ctrl-agent + +Tests: smoke-tests +Restrictions: needs-root, allow-stderr +Depends: kea, curl, jq + +Tests: kea-dhcp4 +Restrictions: needs-root, allow-stderr, breaks-testbed +Depends: kea-dhcp4-server, kea-ctrl-agent, isc-dhcp-client, bridge-utils, iproute2, jq diff --git a/debian/tests/kea-ctrl-agent-debconf b/debian/tests/kea-ctrl-agent-debconf new file mode 100644 index 0000000..5790977 --- /dev/null +++ b/debian/tests/kea-ctrl-agent-debconf @@ -0,0 +1,266 @@ +#!/bin/bash + +set -e + +pw_file="/etc/kea/kea-api-password" +pw_secret="secret_password_${RANDOM}" +service="kea-ctrl-agent.service" + +cleanup() { + /bin/true +} + +trap cleanup EXIT + +override_systemd_throttling() { + mkdir -p /run/systemd/system/kea-ctrl-agent.service.d + cat > /run/systemd/system/kea-ctrl-agent.service.d/override.conf <<EOF +[Unit] +StartLimitIntervalSec=0 +EOF + systemctl daemon-reload +} + +check_perms() { + local file="${1}" + local wanted_perms="${2}" + local perms + + perms=$(stat -c %U:%G:%a "${file}") + if [ "${perms}" != "${wanted_perms}" ]; then + echo "## ERROR: permissions are ${perms} and should be ${wanted_perms}" + return 1 + else + echo "## OK, permissions are ${perms}" + fi +} + +service_status_must_be() { + local service_status + local wanted_status="${1}" + service_status=$(systemctl is-active "${service}" || /bin/true) + systemctl status "${service}" || /bin/true + if [ "${service_status}" != "${wanted_status}" ]; then + echo "## ERROR, service is ${service_status}" + return 1 + else + echo "## OK, service is ${service_status}" + fi +} + +reconfigure_unconfigured() { + debconf-set-selections << EOF +kea-ctrl-agent kea-ctrl-agent/make_a_choice select unconfigured +EOF + dpkg-reconfigure kea-ctrl-agent +} + +reconfigure_password() { + local password="${1}" + debconf-set-selections << EOF +kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_password +kea-ctrl-agent kea-ctrl-agent/kea_api_password password ${password} +kea-ctrl-agent kea-ctrl-agent/kea_api_password_again password ${password} +EOF + dpkg-reconfigure kea-ctrl-agent +} + +reconfigure_random() { + debconf-set-selections << EOF +kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_random_password +EOF + dpkg-reconfigure kea-ctrl-agent +} + +test_fresh_install() { + echo + echo "## Running ${FUNCNAME[0]}" + # On a fresh install, which is the situation we are in as this is the first + # test being run, there is no kea-api-password file, and the service isn't + # running + echo "## Fresh install, default options, there must be no ${pw_file} file" + ls -la "$(dirname ${pw_file})" + test ! -f "${pw_file}" + + echo + echo "## With no ${pw_file}, the service must not be running" + service_status_must_be inactive + echo +} + +test_service_wont_start_without_pwfile() { + echo + echo "## Running ${FUNCNAME[0]}" + echo "## With no ${pw_file}, service must not start" + ls -la "$(dirname ${pw_file})" + test ! -f "${pw_file}" + echo "## Current status:" + systemctl status "${service}" || /bin/true + echo + echo "## Attempting to start ${service}" + systemctl start "${service}" + service_status_must_be inactive + echo +} + +test_configured_password() { + echo + echo "## Running ${FUNCNAME[0]}" + echo "## Reconfiguring kea-ctrl-agent with password ${pw_secret}" + reconfigure_password "${pw_secret}" + + echo "## Checking that ${pw_file} exists and has ${pw_secret}" + ls -la "$(dirname ${pw_file})" + test -f "${pw_file}" + generated_pw=$(cat "${pw_file}") + if [ "${generated_pw}" != "${pw_secret}" ]; then + echo "## ERROR, password from ${pw_file} is not equal to ${pw_secret}: ${generated_pw}" + return 1 + else + echo "## OK, password from ${pw_file} is ${generated_pw}" + fi + + echo "## Checking that ${pw_file} has expected permissions and ownership" + check_perms "${pw_file}" "root:_kea:640" + echo + + echo + echo "## Checking that the service is running" + service_status_must_be active +} + +test_configured_random_password() { + local generated_pw + + echo + echo "## Running ${FUNCNAME[0]}" + echo "## Reconfiguring kea-ctrl-agent with random password option" + reconfigure_random + + echo "## Checking that ${pw_file} exists and has a password different from ${pw_secret}" + ls -la "$(dirname ${pw_file})" + test -f "${pw_file}" + + generated_pw=$(cat "${pw_file}") + if [ "${generated_pw}" = "${pw_secret}" ]; then + echo "## ERROR, generated random password \"${generated_pw}\" is equal to \"${pw_secret}\"" + return 1 + else + echo "## OK, generated random password is \"${generated_pw}\"" + fi + echo + echo "## Checking that ${pw_file} has expected permissions and ownership" + check_perms "${pw_file}" "root:_kea:640" + echo + + echo + echo "## Checking that the service is running" + service_status_must_be active +} + +test_unconfigured() { + local -r new_secret="${pw_secret}${pw_secret}" + local contents + + echo + echo "## Running ${FUNCNAME[0]}" + echo "## Reconfiguring kea-ctrl-agent with option \"unconfigured\" should leave things as they were" + echo + echo "## Overwriting ${pw_file} with ${new_secret}" + printf "%s" "${new_secret}" > "${pw_file}" + + echo "## Reconfiguring" + reconfigure_unconfigured + + echo + echo "## ${pw_file} should still contain ${new_secret}" + contents=$(cat "${pw_file}") + if [ "${contents}" != "${new_secret}" ]; then + echo "## ERROR, ${pw_file} now contains \"${contents}\"" + return 1 + else + echo "## OK, same content" + fi + + echo "## Removing ${pw_file} and reconfiguring, a new one should not be created, and the service must be stopped" + rm -f "${pw_file}" + ls -la $(dirname "${pw_file}") + echo "## Reconfiguring" + reconfigure_unconfigured + + echo "## ${pw_file} was not recreated" + ls -la $(dirname "${pw_file}") + test ! -f "${pw_file}" + echo "## With no ${pw_file}, the service must not be running" + service_status_must_be inactive +} + +test_no_start_with_empty_password() { + echo + echo "## Running ${FUNCNAME[0]}" + echo "## kea-ctrl-agent must not start with an empty password file" + echo + echo "## Truncating ${pw_file}" + truncate -s 0 "${pw_file}" + ls -la $(dirname "${pw_file}") + test ! -s "${pw_file}" + echo + echo "## Restarting kea-ctrl-agent" + systemctl restart "${service}" + echo + echo "## Service must not be started" + service_status_must_be inactive +} + +test_empty_password_via_debconf() { + local service_status + local contents + + echo + echo "## Running ${FUNCNAME[0]}" + echo "## Reconfiguring with password set to ${pw_secret}" + reconfigure_password "${pw_secret}" + + echo + echo "## ${pw_file} must now contain ${pw_secret}" + contents=$(cat "${pw_file}") + if [ "${contents}" != "${pw_secret}" ]; then + echo "## ERROR, ${pw_file} now contains \"${contents}\"" + return 1 + else + echo "## OK, same content" + fi + + echo + echo "## Service must be running" + service_status_must_be active + + echo + echo "## Reconfiguring with an empty password should not change the existing password" + # set an empty password (no args) + reconfigure_password + ls -la $(dirname "${pw_file}") + contents=$(cat "${pw_file}") + if [ "${contents}" != "${pw_secret}" ]; then + echo "## ERROR, ${pw_file} now contains \"${contents}\"" + return 1 + else + echo "## OK, same content" + fi + + echo + echo "## Service must be running" + service_status_must_be active +} + + +# we restart kea-ctrl-agent a lot during this test +override_systemd_throttling + +test_fresh_install +test_service_wont_start_without_pwfile +test_configured_password +test_configured_random_password +test_unconfigured +test_no_start_with_empty_password +test_empty_password_via_debconf diff --git a/debian/tests/kea-dhcp4 b/debian/tests/kea-dhcp4 new file mode 100644 index 0000000..66ce927 --- /dev/null +++ b/debian/tests/kea-dhcp4 @@ -0,0 +1,277 @@ +#!/bin/bash + +set -e +set -o pipefail + +bridge="keabr0" +bridge_ip="192.168.127.1/24" +subnetcidr="192.168.127.0/24" +pool_range="192.168.127.10 - 192.168.127.250" +test_domain="example.autopkgtest" +server_iface="p1" +client_iface="client0" +client_ns="clientns" +declare -A dhcp4_config +resolv_conf_bkp=$(mktemp) +kea_password_file="/etc/kea/kea-api-password" + +# kea-ctrl-agent needs a password file, or else it won't start +# this also tests the debconf mechanism +debconf-set-selections << eof +kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_random_password +eof +dpkg-reconfigure kea-ctrl-agent +[ -s "${kea_password_file}" ] || { + echo "ERROR, debconf-set-selections failed to set a password for kea-ctrl-agent" + exit 1 +} + +auth_params="--auth-user kea-api --auth-password $(cat ${kea_password_file})" + +cleanup() { + rc=$? + set +e # so we don't exit midcleanup + if [ ${rc} -ne 0 ]; then + echo "## FAIL" + echo + echo "## dmesg" + dmesg -T | tail -n 500 + echo + echo "## kea logs" + journalctl -u kea-dhcp4-server.service + fi + echo + echo "## Cleaning up" + ip link set "${server_iface}" down + ip link del "${server_iface}" + ip link set "${bridge}" down + brctl delbr "${bridge}" + ip netns delete "${client_ns}" + sed -r -i "/example.autopkgtest/d" /etc/hosts + if [ -s "${resolv_conf_bkp}" ]; then + cat "${resolv_conf_bkp}" > /etc/resolv.conf + fi + rm -f "${resolv_conf_bkp}" + # restore it for when we are called from the main script, and not the trap + set -e +} + +trap cleanup EXIT + +run_on_client() { + ip netns exec "${client_ns}" "$@" +} + +setup() { + cleanup 2>/dev/null + # so we don't have to worry about it being a symlink + cat /etc/resolv.conf > "${resolv_conf_bkp}" + echo "127.0.1.1 $(hostname).${test_domain} $(hostname)" >> /etc/hosts + ip netns add "${client_ns}" + ip link add "${server_iface}" type veth peer "${client_iface}" netns "${client_ns}" + brctl addbr "${bridge}" + brctl addif "${bridge}" "${server_iface}" + ip link set "${server_iface}" up + ip link set "${bridge}" up + ip addr add "${bridge_ip}" dev "${bridge}" +} + +render_dhcp4_conf() { + local -n config="${1}" + local -r service="dhcp4" + + template="debian/tests/kea-${service}.conf.template" + [ -f "${template}" ] || return 1 + output="/etc/kea/kea-${service}.conf" + + cat "${template}" | sed -r \ + -e "s,@interface@,${config[interface]}," \ + -e "s,@dnsip@,${config[dnsip]}," \ + -e "s,@domain@,${config[domain]}," \ + -e "s/@domainsearch@/${config[domainsearch]}/" \ + -e "s,@router@,${config[router]}," \ + -e "s,@subnetcidr@,${config[subnetcidr]}," \ + -e "s,@poolrange@,${config[poolrange]}," \ + -e "s,@multiarch@,$(dpkg-architecture -qDEB_HOST_MULTIARCH)," \ + > "${output}" +} + +json_get_length() { + echo "${1}" | jq '. | length' +} + +kea_get_leases_by_mac() { + local mac="${1}" + echo "\"hw-address\": \"${mac}\"" | kea-shell ${auth_params} --service dhcp4 lease4-get-by-hw-address +} + +get_result_from_lease() { + echo "${1}" | jq -r '.[0].result' +} + +get_number_of_leases() { + echo "${1}" | jq '.[0].arguments.leases | length' +} + +get_ip_from_lease() { + echo "${1}" | jq -r '.[0]["arguments"]["leases"][0]["ip-address"]' +} + +get_mac_from_lease() { + echo "${1}" | jq -r '.[0]["arguments"]["leases"][0]["hw-address"]' +} + +get_valid_lifetime_from_lease() { + echo "${1}" | jq -r '.[0]["arguments"]["leases"][0]["valid-lft"]' +} + +check_leases() { + local data="${1}" + local if_mac="${2}" + local if_ip="${3}" + local res + + res=$(json_get_length "${data}") + if [ ${res} != 1 ]; then + echo "## ERROR" + echo "## Expected 1 result, got ${res}:" + return 1 + fi + + res=$(get_result_from_lease "${data}") + if [ ${res} != 0 ]; then + echo "## ERROR" + echo "## Failed to obtain leases from server, code ${res}" + return 1 + fi + + res=$(get_number_of_leases "${data}") + if [ ${res} -ne 1 ]; then + echo "## ERROR" + echo "## Expected 1 lease, got ${res}:" + return 1 + fi + + res=$(get_ip_from_lease "${data}") + if [ "${if_ip}" != "${res}" ]; then + echo "## ERROR" + echo "## IP from lease (${res}) does not match IP from interface: ${if_ip}" + run_on_client ip a show + return 1 + fi + + res=$(get_mac_from_lease "${data}") + if [ "${if_mac}" != "${res}" ]; then + echo "## ERROR" + echo "## MAC from lease (${res}) does not match MAC from client interface: ${if_mac}" + run_on_client ip l show + return 1 + fi +} + + +setup + +dhcp4_config["interface"]="${bridge}" +# get rid of the CIDR part at the end +dhcp4_config["dnsip"]="${bridge_ip%%/*}" +dhcp4_config["domain"]="${test_domain}" +dhcp4_config["domainsearch"]="${test_domain}" +# get rid of the CIDR part at the end +dhcp4_config["router"]="${bridge_ip%%/*}" +dhcp4_config["subnetcidr"]="${subnetcidr}" +dhcp4_config["poolrange"]="${pool_range}" + +echo +echo "## Configuring kea-dhcp4 and restarting the service" +render_dhcp4_conf dhcp4_config +systemctl restart kea-dhcp4-server.service +sleep 2s + +echo +echo "## Obtaining IP via dhclient" +run_on_client timeout -v 60s dhclient -v "${client_iface}" +echo "## OK" + +ip=$(run_on_client ip -4 -o addr show dev "${client_iface}" | awk '{print $4}') +ip=${ip%%/*} # remove the CIDR part +mac=$(run_on_client ip -4 link show dev "${client_iface}" | grep "link/ether" | awk '{print $2}') + +echo +echo "## Got ip=${ip}" + +echo +echo "## Checking leases that match client's ethernet address ${mac}" +# this will break if/when we close LP: #2007312 +leases=$(kea_get_leases_by_mac "${mac}") +echo "## Leases:" +echo "${leases}" | jq . + +check_leases "${leases}" "${mac}" "${ip}" +echo "## OK" + +echo +echo "## INFO: Networking in the ${client_ns} namespace:" +echo +echo "## Interfaces" +run_on_client ip a +echo +echo "## Routes" +run_on_client ip route +echo +echo "## DNS" +if command -v resolvectl > /dev/null 2>&1; then + run_on_client resolvectl status +else + echo "## Skipping DNS info (no resolvectl installed)" +fi + +echo +echo "## Checking that the DNS domain \"${test_domain}\" was added to resolv.conf" +if grep -E "^search[[:blank:]]" /etc/resolv.conf | grep -q -w -F "${test_domain}"; then + echo "## OK" +else + echo "## ERROR" + echo "## /etc/resolv.conf does not contain ${test_domain}" + cat /etc/resolv.conf + exit 1 +fi + +echo +echo "## Releasing IP via dhclient -r" +run_on_client timeout -v 60s dhclient -v -r +echo "## OK" + +echo +# As per entry 2072 in +# https://downloads.isc.org/isc/kea/2.4.0/Kea-2.4.0-ReleaseNotes.txt, starting +# from kea 2.3.2, a lease is no longer deleted from the lease database after a +# release request. Instead, it is expired to enable lease affinity. It is kept +# for `hold-reclaimed-time` seconds. Its default value is 3600 seconds. +# https://kea.readthedocs.io/en/kea-2.4.0/arm/lease-expiration.html +echo "## Checking that the lease was expired" +leases=$(kea_get_leases_by_mac "${mac}") +echo "${leases}" | jq . +n_results=$(json_get_length "${leases}") +if [ ${n_results} -ne 1 ]; then + echo "## ERROR, expected 1 result, got ${n_results}" + echo "${leases}" | jq . + exit 1 +fi + +n_leases=$(get_number_of_leases "${leases}") +if [ ${n_leases} -ne 1 ]; then + echo "## ERROR" + echo "## Expected 1 lease, got ${n_leases}:" + echo "${leases}" | jq . + exit 1 +fi +lft=$(get_valid_lifetime_from_lease "${leases}") +if [ ${lft} -gt 0 ]; then + echo "## ERROR" + echo "## Expected expired lease lifetime (0), got ${lft}" + echo "${leases}" | jq . + exit 1 +fi + +echo "## OK" diff --git a/debian/tests/kea-dhcp4.conf.template b/debian/tests/kea-dhcp4.conf.template new file mode 100644 index 0000000..2addefd --- /dev/null +++ b/debian/tests/kea-dhcp4.conf.template @@ -0,0 +1,71 @@ +{ +"Dhcp4": { + "interfaces-config": { + "interfaces": [ "@interface@" ], + "service-sockets-max-retries": 10, + "service-sockets-retry-wait-time": 1000 + }, + "control-socket": { + "socket-type": "unix", + "socket-name": "/run/kea/kea4-ctrl-socket" + }, + "hooks-libraries": [ + { + "library": "/usr/lib/@multiarch@/kea/hooks/libdhcp_lease_cmds.so" + } + ], + "lease-database": { + "type": "memfile", + "lfc-interval": 3600 + }, + "expired-leases-processing": { + "reclaim-timer-wait-time": 10, + "flush-reclaimed-timer-wait-time": 25, + "hold-reclaimed-time": 3600, + "max-reclaim-leases": 100, + "max-reclaim-time": 250, + "unwarned-reclaim-cycles": 5 + }, + "renew-timer": 900, + "rebind-timer": 1800, + "valid-lifetime": 3600, + "option-data": [ + { + "name": "domain-name-servers", + "data": "@dnsip@" + }, + { + "code": 15, + "data": "@domain@" + }, + { + "name": "domain-search", + "data": "@domainsearch@" + } + ], + "subnet4": [ + { + "subnet": "@subnetcidr@", + "pools": [ { "pool": "@poolrange@" } ], + "option-data": [ + { + "name": "routers", + "data": "@router@" + } + ] + } + ], + "loggers": [ + { + "name": "kea-dhcp4", + "output_options": [ + { + "output": "stdout" + } + ], + "severity": "INFO", + "debuglevel": 0 + } + ] +} +} diff --git a/debian/tests/smoke-tests b/debian/tests/smoke-tests new file mode 100644 index 0000000..2de85c4 --- /dev/null +++ b/debian/tests/smoke-tests @@ -0,0 +1,63 @@ +#!/bin/bash + +set -exo pipefail + +# kea-ctrl-agent needs a password file, or else it won't start +# this also tests the debconf mechanism +debconf-set-selections << eof +kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_random_password +eof + +dpkg-reconfigure kea-ctrl-agent +kea_password_file="/etc/kea/kea-api-password" +[ -s "${kea_password_file}" ] || { + echo "ERROR, debconf-set-selections failed to set a password for kea-ctrl-agent" + exit 1 +} + +# Arbitrary wait to allow for the services to start. +# This is needed to avoid having racy/flaky tests. +sleep 5 + +# Check that the PID files are in the right location +for f in kea-dhcp4.kea-dhcp4.pid kea-dhcp6.kea-dhcp6.pid kea-ctrl-agent.kea-ctrl-agent.pid kea-dhcp-ddns.kea-dhcp-ddns.pid; do + test -f "/run/kea/$f" +done + +# Check that the sockets are in the right location +for socket in kea-ddns-ctrl-socket kea4-ctrl-socket kea6-ctrl-socket; do + test -S "/run/kea/$socket" +done + +# Check that lock files are in the right location +test -f /run/lock/kea/logger_lockfile + +check_kea_version() { + CHECKED_VERSION=$1 + if [[ ! ${CHECKED_VERSION} =~ [0-9]+(\.[0-9]+){2} ]]; then + echo "Version [ ${CHECKED_VERSION} ] does not match X.Y.Z format" + exit 1 + fi +} + +# Check dhcp4 server configuration file +kea-dhcp4 -t /etc/kea/kea-dhcp4.conf > /dev/null + +# Check dhcp6 server configuration file +kea-dhcp6 -t /etc/kea/kea-dhcp6.conf > /dev/null + +# Check if we need to provide authentication +auth_params="" +basic_auth_params="" +if [ -s /etc/kea/kea-api-password ]; then + auth_params="--auth-user kea-api --auth-password $(cat /etc/kea/kea-api-password)" + basic_auth_params="-u kea-api:$(cat /etc/kea/kea-api-password)" +fi + +# Check control agent API +TEST_KEA_VERSION=$(curl ${basic_auth_params} -s -X POST -H "Content-Type: application/json" -d '{ "command": "version-get", "service": [ "dhcp4" ] }' 127.0.0.1:8000 | jq -r '.[0].text') +check_kea_version "${TEST_KEA_VERSION}" + +# Check control agent API through kea-shell +TEST_KEA_VERSION=$(echo | kea-shell --service dhcp4 --host 127.0.0.1 --port 8000 ${auth_params} version-get | jq -r '.[0].text') +check_kea_version "${TEST_KEA_VERSION}" |