Cybersecurity-Projects/PROJECTS/beginner/linux-cis-hardening-auditor/src/checks/02_services.sh

447 lines
13 KiB
Bash
Executable File

#!/usr/bin/env bash
# ©AngelaMos | 2026
# 02_services.sh
#
# CIS Section 2 checks: Services
#
# Implements controls 2.1.1-2.2.16 verifying that unnecessary network
# services are not installed on the system. Checks legacy super-servers
# (xinetd, openbsd-inetd), graphical environment (X Window System),
# and fourteen network daemons: Avahi, CUPS, ISC DHCP, OpenLDAP, NFS,
# BIND DNS, vsftpd, Apache/nginx HTTP, Dovecot IMAP/POP3, Samba,
# Squid proxy, SNMP, and NIS. Each check uses package_is_installed
# with file_exists fallback for binary/config detection. The MTA
# check (2.2.15) inspects Postfix inet_interfaces, Exim
# dc_local_interfaces, and ss port 25 listeners to verify local-only
# mail delivery. Also checks rsync daemon installation.
#
# Connects to:
# lib/registry.sh - record_result for each control
# lib/utils.sh - package_is_installed, file_exists, read_file,
# service_is_enabled, run_cmd
check_2_1_1() {
local id="2.1.1"
local status="$STATUS_PASS"
local evidence=""
local pkg="xinetd"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/etc/xinetd.conf" || file_exists "/etc/xinetd.d"; then
status="$STATUS_FAIL"
evidence="${pkg} configuration found at /etc/xinetd.conf"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_1_2() {
local id="2.1.2"
local status="$STATUS_PASS"
local evidence=""
local pkg="openbsd-inetd"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/etc/inetd.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} configuration found at /etc/inetd.conf"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_1() {
local id="2.2.1"
local status="$STATUS_PASS"
local evidence=""
local pkg="xserver-xorg"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/bin/X" || file_exists "/usr/bin/Xorg"; then
status="$STATUS_FAIL"
evidence="X Window System binary found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_2() {
local id="2.2.2"
local status="$STATUS_PASS"
local evidence=""
local pkg="avahi-daemon"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/avahi-daemon" || file_exists "/etc/avahi/avahi-daemon.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_3() {
local id="2.2.3"
local status="$STATUS_PASS"
local evidence=""
local pkg="cups"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/cupsd" || file_exists "/etc/cups/cupsd.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_4() {
local id="2.2.4"
local status="$STATUS_PASS"
local evidence=""
local pkg="isc-dhcp-server"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/dhcpd" || file_exists "/etc/dhcp/dhcpd.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_5() {
local id="2.2.5"
local status="$STATUS_PASS"
local evidence=""
local pkg="slapd"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/slapd" || file_exists "/etc/ldap/slapd.d"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_6() {
local id="2.2.6"
local status="$STATUS_PASS"
local evidence=""
local pkg="nfs-kernel-server"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/rpc.nfsd" || file_exists "/etc/exports"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_7() {
local id="2.2.7"
local status="$STATUS_PASS"
local evidence=""
local pkg="bind9"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/named" || file_exists "/etc/bind/named.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_8() {
local id="2.2.8"
local status="$STATUS_PASS"
local evidence=""
local pkg="vsftpd"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/vsftpd" || file_exists "/etc/vsftpd.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_9() {
local id="2.2.9"
local status="$STATUS_PASS"
local evidence=""
local installed=""
if package_is_installed "apache2"; then
installed="apache2"
elif package_is_installed "nginx"; then
installed="nginx"
elif file_exists "/usr/sbin/apache2" || file_exists "/etc/apache2/apache2.conf"; then
installed="apache2"
elif file_exists "/usr/sbin/nginx" || file_exists "/etc/nginx/nginx.conf"; then
installed="nginx"
fi
if [[ -n "$installed" ]]; then
status="$STATUS_FAIL"
evidence="${installed} is installed"
else
evidence="No HTTP server is installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_10() {
local id="2.2.10"
local status="$STATUS_PASS"
local evidence=""
local installed=""
if package_is_installed "dovecot-imapd"; then
installed="dovecot-imapd"
fi
if package_is_installed "dovecot-pop3d"; then
if [[ -n "$installed" ]]; then
installed="${installed} and dovecot-pop3d"
else
installed="dovecot-pop3d"
fi
fi
if [[ -z "$installed" ]]; then
if file_exists "/usr/sbin/dovecot" || file_exists "/etc/dovecot/dovecot.conf"; then
installed="dovecot"
fi
fi
if [[ -n "$installed" ]]; then
status="$STATUS_FAIL"
evidence="${installed} is installed"
else
evidence="No IMAP or POP3 server is installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_11() {
local id="2.2.11"
local status="$STATUS_PASS"
local evidence=""
local pkg="samba"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/smbd" || file_exists "/etc/samba/smb.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_12() {
local id="2.2.12"
local status="$STATUS_PASS"
local evidence=""
local pkg="squid"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/squid" || file_exists "/etc/squid/squid.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_13() {
local id="2.2.13"
local status="$STATUS_PASS"
local evidence=""
local pkg="snmpd"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/snmpd" || file_exists "/etc/snmp/snmpd.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_14() {
local id="2.2.14"
local status="$STATUS_PASS"
local evidence=""
local pkg="nis"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif file_exists "/usr/sbin/ypserv" || file_exists "/etc/yp.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary or configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}
check_2_2_15() {
local id="2.2.15"
local status="$STATUS_PASS"
local evidence=""
local listening_external=""
if run_cmd ss -lntp | grep -qE ':25\s' 2>/dev/null; then
local listeners
listeners=$(run_cmd ss -lntp | grep -E ':25\s' 2>/dev/null) || true
if echo "$listeners" | grep -qvE '127\.0\.0\.1:25|::1:25|\[::1\]:25|\*:25'; then
local bound_addrs
bound_addrs=$(echo "$listeners" | awk '{print $4}')
if echo "$bound_addrs" | grep -qvE '^127\.0\.0\.1:|^\[::1\]:|^::1:'; then
listening_external="true"
fi
fi
fi
if [[ "$listening_external" == "true" ]]; then
status="$STATUS_FAIL"
evidence="MTA is listening on non-loopback interface on port 25"
record_result "$id" "$status" "$evidence"
return
fi
local main_cf
main_cf=$(read_file "/etc/postfix/main.cf" 2>/dev/null) || true
if [[ -n "$main_cf" ]]; then
local inet_interfaces
inet_interfaces=$(echo "$main_cf" | grep -Ei '^\s*inet_interfaces\s*=' | tail -1 | awk -F= '{print $2}' | tr -d ' ') || true
if [[ -z "$inet_interfaces" ]]; then
status="$STATUS_FAIL"
evidence="Postfix inet_interfaces not configured (defaults to all)"
elif [[ "$inet_interfaces" == "loopback-only" || "$inet_interfaces" == "localhost" || "$inet_interfaces" == "127.0.0.1" ]]; then
evidence="Postfix configured for local-only (inet_interfaces = ${inet_interfaces})"
else
status="$STATUS_FAIL"
evidence="Postfix inet_interfaces = ${inet_interfaces} (expected loopback-only or localhost)"
fi
record_result "$id" "$status" "$evidence"
return
fi
local exim_conf
exim_conf=$(read_file "/etc/exim4/update-exim4.conf.conf" 2>/dev/null) || true
if [[ -n "$exim_conf" ]]; then
local listen_addrs
listen_addrs=$(echo "$exim_conf" | grep -Ei '^\s*dc_local_interfaces' | tail -1 | awk -F= "'{print \$2}'" | tr -d "' ") || true
if [[ -z "$listen_addrs" || "$listen_addrs" == "127.0.0.1;::1" || "$listen_addrs" == "127.0.0.1" ]]; then
evidence="Exim configured for local-only (dc_local_interfaces = ${listen_addrs:-127.0.0.1;::1})"
else
status="$STATUS_FAIL"
evidence="Exim dc_local_interfaces = ${listen_addrs} (expected 127.0.0.1;::1)"
fi
record_result "$id" "$status" "$evidence"
return
fi
if file_exists "/usr/sbin/sendmail" || file_exists "/usr/lib/sendmail"; then
status="$STATUS_WARN"
evidence="Sendmail detected but configuration not checked in test mode"
record_result "$id" "$status" "$evidence"
return
fi
evidence="No MTA detected or MTA not listening on port 25"
record_result "$id" "$status" "$evidence"
}
check_2_2_16() {
local id="2.2.16"
local status="$STATUS_PASS"
local evidence=""
local pkg="rsync"
if package_is_installed "$pkg"; then
status="$STATUS_FAIL"
evidence="${pkg} is installed"
elif service_is_enabled "rsync"; then
status="$STATUS_FAIL"
evidence="rsync service is enabled"
elif file_exists "/usr/bin/rsync" && file_exists "/etc/rsyncd.conf"; then
status="$STATUS_FAIL"
evidence="${pkg} binary and daemon configuration found"
else
evidence="${pkg} is not installed"
fi
record_result "$id" "$status" "$evidence"
}