Files

67 lines
3.7 KiB
Bash

# The firewall that runs, if any: firewalld or ufw.
fw_on() { systemctl is-active --quiet firewalld || { command -v ufw >/dev/null && ufw status | grep -q "Status: active"; }; }
# fw_allows PORT/PROTO [SERVICE]: it lets PORT in from the lab network
# (le-host), now and permanently.
fw_allows() {
local port=$1 svc=${2:-} z p
if systemctl is-active --quiet firewalld; then
z=$(firewall-cmd --get-zone-of-interface=le-host 2>/dev/null || firewall-cmd --get-default-zone)
[ "$(firewall-cmd --permanent --zone="$z" --get-target)" = ACCEPT ] && return 0
for p in "" --permanent; do
firewall-cmd $p --zone="$z" --query-port="$port" ||
{ [ -n "$svc" ] && firewall-cmd $p --zone="$z" --query-service="$svc"; } ||
firewall-cmd $p --zone="$z" --list-rich-rules | grep -Eq "port=\"${port%/*}\"|service name=\"$svc\"|10\.99\.0\." ||
return 1
done
else
ufw status | grep ALLOW | grep -Eqiw "${port%/*}|${svc:-none}|10\.99\.0\.[0-9/]+"
fi
}
# Does an NTP server answer at $1? Asked from the peer.
ntp_answers() {
in_peer python3 -c '
import socket, sys
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(3)
s.sendto(b"\x23" + bytes(47), (sys.argv[1], 123))
d = s.recv(512)
sys.exit(0 if len(d) >= 48 and d[0] & 7 == 4 else 1)' "$1"
}
# Every source line chrony reads (RHEL locations).
chrony_sources() {
cat /etc/chrony.conf /etc/chrony.d/*.conf /etc/chrony/conf.d/*.conf /etc/chrony/sources.d/*.sources 2>/dev/null |
grep -E '^[[:space:]]*(server|pool|peer|sourcedir)[[:space:]]'
}
task 1 "Time zone Europe/Berlin"
need "the time zone is Europe/Berlin" '[ "$(timedatectl show -p Timezone --value)" = Europe/Berlin ]'
need "date shows CET or CEST" 'date +%Z | grep -Eq "^CES?T$"'
task 2 "chrony uses only time.cloudflare.com" rhel
need "chrony's configuration has time.cloudflare.com" 'chrony_sources | grep -Eq "^[[:space:]]*(server|pool)[[:space:]]+time\.cloudflare\.com"'
need "chrony's configuration has no other sources, sourcedir included" '! chrony_sources | grep -Ev "^[[:space:]]*(server|pool)[[:space:]]+time\.cloudflare\.com([[:space:]]|$)"'
need "chronyd is running and enabled" 'active chronyd && enabled chronyd'
need "chronyc sources lists only time.cloudflare.com" 'chronyc -N -c sources | cut -d, -f3 | sort -u | grep -qx time.cloudflare.com && [ "$(chronyc -N -c sources | cut -d, -f3 | sort -u | wc -l)" = 1 ]'
task 3 "systemd-timesyncd uses time.google.com" debian
need "timesyncd's configuration has NTP=time.google.com" 'conf_has /etc/systemd/timesyncd.conf /etc/systemd/timesyncd.conf.d "^[[:space:]]*NTP=.*time\.google\.com"'
# Once chrony has replaced systemd-timesyncd (task 5), only the
# configuration is left to grade.
if is_debian && ! pkg_installed chrony; then
need "systemd-timesyncd is running" active systemd-timesyncd
need "timedatectl timesync-status shows time.google.com" '[ "$(timedatectl show-timesync -p ServerName --value)" = time.google.com ]'
fi
task 4 "This VM serves NTP to 10.99.0.0/24" rhel
need "chrony.conf allows the lab network" 'grep -Eq "^[[:space:]]*allow([[:space:]]|$)" /etc/chrony.conf /etc/chrony.d/*.conf 2>/dev/null'
need "chronyd is running and enabled" 'active chronyd && enabled chronyd'
fw_on && need "the firewall lets NTP (123/udp) in, now and permanently" 'fw_allows 123/udp ntp'
need "10.99.0.1 answers NTP requests from the peer" 'ntp_answers 10.99.0.1'
task 5 "chrony client of 10.99.0.2" debian
need "chrony is installed" pkg_installed chrony
need "chrony is running and enabled" 'active chrony && enabled chrony'
need "chrony's configuration has 10.99.0.2" 'grep -rEqs "^[[:space:]]*(server|pool)[[:space:]]+(10\.99\.0\.2|peer|peer\.lab\.example)([[:space:]]|$)" /etc/chrony'
need "chronyc sources shows 10.99.0.2 answering" 'chronyc -n -c sources | awk -F, "\$3 == \"10.99.0.2\" && \$6 != 0 {ok=1} END {exit !ok}"'