IF=$(state_get if) A=/root/lab-answers # has N VALUE: a line of answer N is VALUE (spaces ignored). has() { sed 's/^[[:space:]]*//; s/[[:space:]]*$//' "$A/$1" 2>/dev/null | grep -qxF -- "$2"; } hasre() { sed 's/^[[:space:]]*//; s/[[:space:]]*$//' "$A/$1" 2>/dev/null | grep -qxE -- "$2"; } ADDR=$(ip -4 -o addr show dev "$IF" scope global | awk '{print $4; exit}') GW=$(ip -4 route show default | awk '{print $3; exit}') SSHOPT=(-o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o PreferredAuthentications=publickey) # The firewall: firewalld if it's running, otherwise ufw. fwd=''; active firewalld && fwd=1 ufw_on() { command -v ufw >/dev/null && ufw status | grep -q 'Status: active'; } zones() { { firewall-cmd --get-zone-of-interface="$IF" 2>/dev/null || firewall-cmd --get-default-zone; firewall-cmd --get-default-zone; } | sort -u; } only_ssh() { local z p if [ -n "$fwd" ]; then for z in $(zones); do for p in "" --permanent; do firewall-cmd $p --zone="$z" --list-services | tr ' ' '\n' | grep -Evx 'ssh|dhcpv6-client|' && return 1 [ -z "$(firewall-cmd $p --zone="$z" --list-ports)" ] || return 1 [ "$(firewall-cmd $p --zone="$z" --get-target)" != ACCEPT ] || return 1 done done return 0 fi ! ufw status | grep ALLOW | grep -v FWD | grep -Evq '^(22(/tcp)?|OpenSSH)[[:space:]]' && ufw status verbose | grep -Eq 'Default: (deny|reject) \(incoming\)' } peer_ssh() { in_peer timeout 5 bash -c 'exec 3<>/dev/tcp/10.99.0.1/22; head -c 4 <&3' 2>/dev/null | grep -q SSH; } peer_web() { in_peer curl -s --max-time 5 http://10.99.0.1:8080/ | grep -q "Lab web server"; } task 1 "Address with prefix and default gateway in /root/lab-answers/1" need "the answer has the address with its prefix, $ADDR" 'has 1 "$ADDR"' need "the answer has the default gateway, $GW" 'has 1 "$GW"' task 2 "10.20.30.77/26 worked out in /root/lab-answers/2" need "the answer has the network address" 'hasre 2 "10\.20\.30\.64(/26)?"' need "the answer has the broadcast address" 'hasre 2 "10\.20\.30\.127(/26)?"' need "the answer has the first usable address" 'hasre 2 "10\.20\.30\.65(/26)?"' need "the answer has the last usable address" 'hasre 2 "10\.20\.30\.126(/26)?"' task 3 "The program on TCP port 8080 in /root/lab-answers/3" need "the answer names the program listening on 8080" '[ -n "$(answer 3)" ] && ss -Htlpn "sport = :8080" | grep -qF "\"$(answer 3)"' task 4 "dig installed; a domain looked up" need "dig is installed" 'command -v dig' need "/root/lab-answers/4 is a domain with an A record" '[ -n "$(answer 4)" ] && dig +short +time=3 +tries=2 A "$(answer 4)" | grep -Eq "^[0-9.]+$"' task 5 "ssh alice@localhost logs root in with a key" need "root has a key pair" 'ls /root/.ssh/id_*.pub' need "ssh alice@localhost logs in with the key, without a password" 'timeout 15 ssh "${SSHOPT[@]}" alice@localhost id -un