diff --git a/config/hooks/installed/install-scripts.sh b/config/hooks/installed/install-scripts.sh index 58839ed..6234f04 100755 --- a/config/hooks/installed/install-scripts.sh +++ b/config/hooks/installed/install-scripts.sh @@ -4,9 +4,104 @@ set -euo pipefail echo "Installing source scripts..." -# Install source scripts -install -m 755 /workspace/src/firewall-setup.sh /usr/local/bin/ -install -m 755 /workspace/src/security-hardening.sh /usr/local/bin/ +# Install firewall-setup script (embedded - /workspace not available in installed system) +cat >/usr/local/bin/firewall-setup.sh <<'FIREWALL_SCRIPT' +#!/bin/bash +set -euo pipefail + +parse_wg_endpoint() { + local wg_config="${1:-/etc/wireguard/wg0.conf}" + if [[ ! -f $wg_config ]]; then + echo "Error: WireGuard config not found at $wg_config" + return 1 + fi + grep -oP 'Endpoint = \K[0-9.]+:[0-9]+' "$wg_config" || { + echo "Error: Could not parse endpoint from WireGuard config" + return 1 + } +} + +generate_nftables_rules() { + local endpoint="$1" + local ip="${endpoint%:*}" + local port="${endpoint#*:}" + cat </etc/nftables.conf + systemctl enable nftables + systemctl restart nftables + echo "Firewall configured for endpoint: $endpoint" + else + echo "Warning: Could not parse WireGuard endpoint, using default deny policy" + fi + else + echo "Warning: WireGuard config not found, using default deny policy" + fi +} + +main() { + echo "Setting up dynamic firewall..." + apply_firewall "$@" + echo "Firewall setup completed." +} + +if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then + main "$@" +fi +FIREWALL_SCRIPT +chmod +x /usr/local/bin/firewall-setup.sh + +# Install security-hardening script (embedded) +cat >/usr/local/bin/security-hardening.sh <<'HARDENING_SCRIPT' +#!/bin/bash +set -euo pipefail + +check_encryption_status() { + echo "Checking encryption status..." + if command -v cryptsetup >/dev/null 2>&1; then + for dev in /dev/mapper/*; do + if [ -e "$dev" ]; then + echo "Encrypted device: $dev" + fi + done + fi +} + +main() { + echo "KNEL-Football Security Hardening Utility" + check_encryption_status +} + +if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then + main "$@" +fi +HARDENING_SCRIPT +chmod +x /usr/local/bin/security-hardening.sh # Create VPN configuration apply script cat >/usr/local/bin/apply-vpn-config.sh <<'EOF' diff --git a/config/hooks/installed/mount-hardening.sh b/config/hooks/installed/mount-hardening.sh new file mode 100755 index 0000000..440aa68 --- /dev/null +++ b/config/hooks/installed/mount-hardening.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Mount point hardening - PRD FR-007, CIS Benchmark 1.1 +# Reference: CIS Benchmark for Debian, NIST SP 800-53 CM-7 +set -euo pipefail + +echo "Applying mount point hardening..." + +# Create fstab security entries for temporary filesystems +# These are added via a systemd mount helper or tmpfiles.d +# since fstab is managed by the installer for the main partitions + +# Harden /tmp via tmpfiles.d (systemd-tmpfiles) +mkdir -p /etc/tmpfiles.d + +cat >/etc/tmpfiles.d/knel-mount-hardening.conf <<'EOF' +# KNEL-Football Mount Hardening +# Ensure /tmp is mounted with nodev, nosuid, noexec +# This supplements the installer-created fstab +d /tmp 1777 root root 0d +EOF + +# Add security mount options to fstab if entries exist +if [ -f /etc/fstab ]; then + # Harden /tmp if present + if grep -q '/tmp' /etc/fstab 2>/dev/null; then + sed -i '/\/tmp/s/defaults/defaults,nodev,nosuid,noexec/' /etc/fstab 2>/dev/null || true + fi + + # Harden /var/tmp if present + if grep -q '/var/tmp' /etc/fstab 2>/dev/null; then + sed -i '/\/var\/tmp/s/defaults/defaults,nodev,nosuid,noexec/' /etc/fstab 2>/dev/null || true + fi + + # Harden /home if present + if grep -q '/home' /etc/fstab 2>/dev/null; then + sed -i '/\/home/s/defaults/defaults,nodev,nosuid/' /etc/fstab 2>/dev/null || true + fi + + # Harden /dev/shm if present + if grep -q '/dev/shm' /etc/fstab 2>/dev/null; then + sed -i '/\/dev\/shm/s/defaults/defaults,nodev,nosuid,noexec/' /etc/fstab 2>/dev/null || true + fi +fi + +# If /tmp is NOT in fstab, add a tmpfs entry with hardening +if ! grep -q '/tmp' /etc/fstab 2>/dev/null; then + echo "tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec,size=2G 0 0" >> /etc/fstab +fi + +echo "Mount hardening completed." diff --git a/config/hooks/live/firewall-setup.sh b/config/hooks/live/firewall-setup.sh index 7c161fc..7bb5d37 100755 --- a/config/hooks/live/firewall-setup.sh +++ b/config/hooks/live/firewall-setup.sh @@ -4,11 +4,6 @@ set -euo pipefail echo "Setting up firewall configuration..." -# Load firewall setup functions from proper volume path -# Note: Source path exists at build time in Docker container -# shellcheck disable=SC1091 -source /build/src/firewall-setup.sh - # Install nftables rules (default deny policy) cat >/etc/nftables.conf <<'EOF' #!/usr/sbin/nft -f diff --git a/config/hooks/live/kernel-hardening.sh b/config/hooks/live/kernel-hardening.sh new file mode 100755 index 0000000..5c932d8 --- /dev/null +++ b/config/hooks/live/kernel-hardening.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# Kernel parameter hardening - PRD FR-007 +# Reference: CIS Benchmark, NIST SP 800-53, PRD security-model.md +set -euo pipefail + +echo "Applying kernel hardening parameters..." + +# Configure sysctl security parameters +mkdir -p /etc/sysctl.d + +cat >/etc/sysctl.d/99-knel-security.conf <<'EOF' +# KNEL-Football Kernel Security Parameters +# Reference: PRD FR-007, CIS Benchmark 3.1-3.4, NIST SP 800-53 + +# Enable ASLR (Address Space Layout Randomization) +kernel.randomize_va_space = 2 + +# Restrict ptrace (prevent process inspection by unprivileged users) +kernel.yama.ptrace_scope = 2 + +# Restrict access to kernel pointer addresses +kernel.kptr_restrict = 2 + +# Restrict dmesg to privileged users +kernel.dmesg_restrict = 1 + +# Restrict unprivileged use of BPF +kernel.unprivileged_bpf_disabled = 1 + +# Enable ExecShield-like protection +kernel.exec-shield = 1 + +# Restrict kernel profiling +kernel.perf_event_paranoid = 3 + +# Disable kexec (prevent kernel replacement) +kernel.kexec_load = 0 + +# Restrict access to kernel logs +dev.tty.ldisc_autoload = 0 + +# Restrict user namespaces +user.max_user_namespaces = 0 + +# Disable core dumps for SUID binaries +fs.suid_dumpable = 0 + +# Protect hardlinks and symlinks +fs.protected_hardlinks = 1 +fs.protected_symlinks = 1 +fs.protected_fifos = 2 +fs.protected_regular = 2 + +# Network security +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.send_redirects = 0 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.default.secure_redirects = 0 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.default.accept_source_route = 0 +net.ipv4.conf.all.log_martians = 1 +net.ipv4.conf.default.log_martians = 1 +net.ipv4.icmp_echo_ignore_broadcasts = 1 +net.ipv4.icmp_ignore_bogus_error_responses = 1 +net.ipv4.tcp_syncookies = 1 +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.default.rp_filter = 1 +net.ipv6.conf.all.accept_redirects = 0 +net.ipv6.conf.default.accept_redirects = 0 +net.ipv6.conf.all.accept_source_route = 0 +net.ipv6.conf.default.accept_source_route = 0 +EOF + +# Apply sysctl settings +sysctl --system 2>/dev/null || true + +echo "Kernel hardening completed." diff --git a/config/hooks/live/security-hardening.sh b/config/hooks/live/security-hardening.sh index c4ada36..b4977f2 100755 --- a/config/hooks/live/security-hardening.sh +++ b/config/hooks/live/security-hardening.sh @@ -1,34 +1,191 @@ #!/bin/bash -# Security hardening hook for live system +# Security hardening hook for live system (self-contained) +# Reference: PRD FR-005, FR-006, FR-007 set -euo pipefail echo "Applying security hardening..." -# Apply security hardening functions from proper volume path -# Note: Source path exists at build time in Docker container -# shellcheck disable=SC1091 -source /build/src/security-hardening.sh +# WiFi module blacklist +cat >/etc/modprobe.d/blacklist-wifi.conf <<'EOF' +# WiFi module blacklisting - PRD FR-005 +blacklist cfg80211 +blacklist mac80211 +blacklist brcmfmac +blacklist iwlwifi +blacklist ath9k +blacklist ath9k_htc +blacklist ath10k_pci +blacklist rtl8188ee +blacklist rtl8192ce +blacklist rtl8192se +blacklist rtl8723ae +blacklist rtl8821ae +blacklist rt73usb +blacklist rt2800usb +blacklist rt2x00lib +blacklist rt2x00usb +blacklist mwifiex +blacklist mwifiex_pcie +blacklist mwifiex_sdio +EOF -# Create WiFi module blacklist -create_wifi_blacklist +# Bluetooth module blacklist +cat >/etc/modprobe.d/blacklist-bluetooth.conf <<'EOF' +# Bluetooth module blacklisting - PRD FR-005 +blacklist btusb +blacklist bluetooth +blacklist btrtl +blacklist btintel +blacklist btbcm +blacklist bnep +blacklist rfcomm +blacklist hidp +EOF -# Create Bluetooth module blacklist -create_bluetooth_blacklist +# SSH client configuration (client only - no server per PRD FR-006) +mkdir -p /etc/ssh +cat >/etc/ssh/ssh_config <<'EOF' +# SSH Client Configuration +# Reference: PRD FR-006 - Client-only, no inbound SSH services -# Configure SSH client (client only - no server per security requirements) -configure_ssh_client +Host * + PasswordAuthentication no + PubkeyAuthentication yes + KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 + Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com + MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com + ConnectTimeout 30 + ServerAliveInterval 300 + ServerAliveCountMax 2 + StrictHostKeyChecking ask + UserKnownHostsFile ~/.ssh/known_hosts +EOF -# Configure password policy -configure_password_policy +# SSH server config (defense-in-depth - sshd not installed per PRD FR-006) +cat >/etc/ssh/sshd_config <<'EOF' +# SSH Server Hardening (defense-in-depth) +# Reference: PRD FR-006 - Client-only system, sshd not installed +Protocol 2 +PermitRootLogin no +PermitEmptyPasswords no +MaxAuthTries 3 +ClientAliveInterval 300 +ClientAliveCountMax 2 +X11Forwarding no +EOF -# Configure File Integrity Monitoring (AIDE) -configure_fim +# Password policy - PRD FR-007, NIST SP 800-63B +mkdir -p /etc/security +cat >/etc/security/pwquality.conf <<'EOF' +# KNEL-Football Password Quality Requirements +# Reference: NIST SP 800-63B, CIS Benchmarks for Debian +minlen = 14 +dcredit = -1 +ucredit = -1 +lcredit = -1 +ocredit = -1 +difok = 4 +maxrepeat = 2 +maxclassrepeat = 2 +maxsequence = 2 +usercheck = 1 +dictcheck = 1 +gecoscheck = 1 +enforcing = 1 +badwords = password secret admin root knel football tier0 12345 qwerty +minclass = 3 +EOF -# Configure system limits -configure_system_limits +# File Integrity Monitoring (AIDE) - CIS 1.4, FedRAMP AU-7, CMMC AU.3.059 +mkdir -p /etc/aide +cat >/etc/aide/aide.conf <<'EOF' +# AIDE Configuration - CIS/FedRAMP/CMMC Compliance +database_out=file:/var/lib/aide/aide.db.new +database=file:/var/lib/aide/aide.db +report_url=stdout +SECURITY = p+u+g+s+m+c+md5+sha256+sha512 +/etc SECURITY +/boot SECURITY +/usr SECURITY +/bin SECURITY +/sbin SECURITY +/lib SECURITY +/lib64 SECURITY +/etc/ssh SECURITY +/etc/wireguard SECURITY +/etc/security SECURITY +/etc/audit SECURITY +/etc/modprobe.d SECURITY +/etc/nftables.conf SECURITY +/etc/sudoers SECURITY +/etc/sudoers.d SECURITY +/etc/pam.d SECURITY +!/proc +!/sys +!/dev +!/run +!/tmp +!/var/log +!/var/cache +!/var/lib/aide +!/var/tmp +EOF -# Configure audit rules -configure_audit_rules +# System resource limits +mkdir -p /etc/security/limits.d +cat >/etc/security/limits.d/security.conf <<'EOF' +* hard core 0 +* soft nproc 1024 +* hard nproc 2048 +EOF + +# Audit rules - CIS 6.2, FedRAMP AU-2, CMMC AU.2.042 +mkdir -p /etc/audit/rules.d +cat >/etc/audit/rules.d/audit.rules <<'EOF' +# Comprehensive Audit Rules - CIS 6.2, FedRAMP AU-2, CMMC AU.2.042 +-w /etc/passwd -p wa -k identity +-w /etc/shadow -p wa -k identity +-w /etc/group -p wa -k identity +-w /etc/gshadow -p wa -k identity +-w /etc/sudoers -p wa -k privilege_escalation +-w /etc/sudoers.d/ -p wa -k privilege_escalation +-w /etc/pam.d/ -p wa -k authentication +-w /etc/security/ -p wa -k authentication +-w /etc/login.defs -p wa -k authentication +-w /var/log/faillog -p wa -k authentication +-w /var/log/lastlog -p wa -k authentication +-w /var/log/tallylog -p wa -k authentication +-w /etc/network/ -p wa -k network_config +-w /etc/hosts -p wa -k network_config +-w /etc/hostname -p wa -k network_config +-w /etc/resolv.conf -p wa -k network_config +-w /etc/nftables.conf -p wa -k firewall +-w /etc/wireguard/ -p wa -k wireguard_config +-w /etc/ssh/ssh_config -p wa -k ssh_config +-w /etc/fstab -p wa -k filesystem +-w /etc/crypttab -p wa -k encryption +-w /etc/modprobe.d/ -p wa -k kernel_modules +-w /etc/sysctl.conf -p wa -k kernel_parameters +-w /etc/sysctl.d/ -p wa -k kernel_parameters +-w /boot/ -p wa -k boot_config +-w /efi/ -p wa -k boot_config +-w /etc/default/grub -p wa -k boot_config +-w /etc/grub.d/ -p wa -k boot_config +-w /etc/audit/ -p wa -k audit_config +-w /var/log/audit/ -p wa -k audit_logs +-w /etc/chrony/ -p wa -k time_sync +-w /etc/ntp.conf -p wa -k time_sync +-w /usr/bin/sudo -p x -k privilege_escalation +-w /usr/bin/su -p x -k privilege_escalation +-w /usr/bin/passwd -p x -k password_change +-w /usr/bin/chsh -p x -k user_modification +-w /usr/bin/usermod -p x -k user_modification +-w /var/run/utmp -p wa -k session +-w /var/log/wtmp -p wa -k session +-w /var/log/btmp -p wa -k session +-a always,exit -F arch=b64 -S init_module -S finit_module -S delete_module -k kernel_modules +-w /var/lib/aide/ -p wa -k file_integrity +EOF # Enable auditd service systemctl enable auditd diff --git a/config/hooks/live/service-hardening.sh b/config/hooks/live/service-hardening.sh new file mode 100755 index 0000000..92bd1d4 --- /dev/null +++ b/config/hooks/live/service-hardening.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Disable unnecessary services - PRD FR-007 +# Reference: PRD "Disabled Services" list, CIS Benchmark 2.1 +set -euo pipefail + +echo "Disabling unnecessary services..." + +# List of services to disable per PRD FR-007 +SERVICES_TO_DISABLE=( + avahi-daemon + cups + bluetooth + NetworkManager + ModemManager + whoopsie + apport + speech-dispatcher + PackageKit +) + +for service in "${SERVICES_TO_DISABLE[@]}"; do + if systemctl is-enabled "$service" 2>/dev/null | grep -q "enabled"; then + systemctl disable "$service" 2>/dev/null || true + systemctl stop "$service" 2>/dev/null || true + echo "Disabled service: $service" + elif systemctl list-unit-files "$service.service" 2>/dev/null | grep -q "$service"; then + systemctl disable "$service" 2>/dev/null || true + systemctl mask "$service" 2>/dev/null || true + echo "Masked service: $service" + else + echo "Service not found (OK): $service" + fi +done + +# Mask services to prevent re-enabling +for service in avahi-daemon cups bluetooth ModemManager whoopsie apport; do + systemctl mask "$service" 2>/dev/null || true +done + +echo "Service hardening completed." diff --git a/config/hooks/live/sudo-hardening.sh b/config/hooks/live/sudo-hardening.sh new file mode 100755 index 0000000..070e594 --- /dev/null +++ b/config/hooks/live/sudo-hardening.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Sudo hardening - PRD FR-007 Access Control Layer +# Reference: CIS Benchmark 5.4, NIST SP 800-53 AC-6 +set -euo pipefail + +echo "Configuring sudo access controls..." + +# Create sudoers configuration for restricted access +mkdir -p /etc/sudoers.d +chmod 750 /etc/sudoers.d + +# Default sudoers hardening +cat >/etc/sudoers.d/99-knel-hardening <<'EOF' +# KNEL-Football Sudo Configuration +# Reference: PRD FR-007, CIS Benchmark 5.4, NIST SP 800-53 AC-6 + +# Require tty for sudo (prevents script injection) +Defaults requiretty + +# Lecture user on first sudo use +Defaults lecture = always +Defaults lecture_file = /etc/sudo.lecture + +# Logging and timeout +Defaults logfile = "/var/log/sudo.log" +Defaults log_input +Defaults log_output +Defaults timestamp_timeout = 15 + +# Restrict which environment variables are preserved +Defaults env_reset +Defaults env_delete += "HOME" +Defaults secure_path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +# football user can run specific admin commands +football ALL=(root) /usr/local/bin/apply-vpn-config.sh, /usr/local/bin/convert-luks-kdf.sh, /usr/bin/systemctl restart nftables, /usr/bin/systemctl restart wg-quick@wg0, /usr/local/bin/check-encryption.sh + +# Root can run anything (standard) +root ALL=(ALL:ALL) ALL +EOF + +chmod 440 /etc/sudoers.d/99-knel-hardening + +# Create sudo lecture file +cat >/etc/sudo.lecture <<'EOF' +==================================================================== + KNEL-Football Secure OS - Privileged Access Warning +==================================================================== + +You are about to execute a command with elevated privileges. + +All sudo commands are logged and audited. +Unauthorized use of privileged access is a security violation. + +If you did not intend to run a privileged command, press Ctrl+C now. +==================================================================== +EOF + +# Ensure sudo.log exists with correct permissions +touch /var/log/sudo.log 2>/dev/null || true +chmod 600 /var/log/sudo.log 2>/dev/null || true + +echo "Sudo hardening completed." diff --git a/run.sh b/run.sh index 30f5374..a473dc0 100755 --- a/run.sh +++ b/run.sh @@ -586,7 +586,7 @@ uki_build() { ukify build \ --linux "$kernel" \ --initrd "$initrd" \ - --cmdline "quiet splash" \ + --cmdline "quiet splash lockdown=confidentiality module.sig_enforce=1" \ --output "$uki_output" \ --efi-arch x64 else @@ -603,7 +603,7 @@ uki_build() { # Create cmdline file local cmdline_file="${build_dir}/cmdline.txt" - echo "quiet splash" > "$cmdline_file" + echo "quiet splash lockdown=confidentiality module.sig_enforce=1" > "$cmdline_file" # Build UKI with objcopy objcopy \ diff --git a/src/security-hardening.sh b/src/security-hardening.sh index 74760eb..be29334 100755 --- a/src/security-hardening.sh +++ b/src/security-hardening.sh @@ -7,13 +7,26 @@ create_wifi_blacklist() { local output_file="${1:-/etc/modprobe.d/blacklist-wifi.conf}" cat >"$output_file" <<'EOF' -# WiFi module blacklisting +# WiFi module blacklisting - PRD FR-005 blacklist cfg80211 blacklist mac80211 blacklist brcmfmac blacklist iwlwifi blacklist ath9k +blacklist ath9k_htc +blacklist ath10k_pci +blacklist rtl8188ee +blacklist rtl8192ce +blacklist rtl8192se +blacklist rtl8723ae +blacklist rtl8821ae blacklist rt73usb +blacklist rt2800usb +blacklist rt2x00lib +blacklist rt2x00usb +blacklist mwifiex +blacklist mwifiex_pcie +blacklist mwifiex_sdio EOF echo "WiFi blacklist created at $output_file" @@ -24,12 +37,15 @@ create_bluetooth_blacklist() { local output_file="${1:-/etc/modprobe.d/blacklist-bluetooth.conf}" cat >"$output_file" <<'EOF' -# Bluetooth module blacklisting +# Bluetooth module blacklisting - PRD FR-005 blacklist btusb blacklist bluetooth blacklist btrtl blacklist btintel blacklist btbcm +blacklist bnep +blacklist rfcomm +blacklist hidp EOF echo "Bluetooth blacklist created at $output_file" diff --git a/test-iso.sh b/test-iso.sh index ef174b1..3e5ea3b 100755 --- a/test-iso.sh +++ b/test-iso.sh @@ -10,12 +10,14 @@ set -euo pipefail # Configuration variables SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR -readonly VM_NAME="knel-test-$(date +%Y%m%d-%H%M%S)" +_vm_name="knel-test-$(date +%Y%m%d-%H%M%S)" +readonly VM_NAME="$_vm_name" readonly ISO_PATH="${SCRIPT_DIR}/output/knel-football-secure-v1.0.0.iso" readonly DISK_SIZE="20G" readonly DISK_PATH="${SCRIPT_DIR}/tmp/${VM_NAME}.qcow2" readonly RAM="4096" # 4GB RAM readonly VCPUS="2" +# shellcheck disable=SC2034 readonly NETWORK="none" readonly CPU_MODEL="host-model" @@ -205,7 +207,7 @@ list_vms() { echo "" log_info "Disk images:" - ls -lh ${SCRIPT_DIR}/tmp/knel-test-*.qcow2 2>/dev/null || log_warn "No test disk images found" + ls -lh "${SCRIPT_DIR}"/tmp/knel-test-*.qcow2 2>/dev/null || log_warn "No test disk images found" } # Parse command line arguments diff --git a/tests/unit/firewall-setup_comprehensive_test.bats b/tests/unit/firewall-setup_comprehensive_test.bats index e3361f5..8865502 100644 --- a/tests/unit/firewall-setup_comprehensive_test.bats +++ b/tests/unit/firewall-setup_comprehensive_test.bats @@ -1,176 +1,126 @@ #!/usr/bin/env bats -# Comprehensive unit tests for firewall-setup.sh (100% coverage) +# Behavioral tests for firewall-setup.sh +# Reference: PRD FR-004 -# Test parse_wg_endpoint function exists -@test "parse_wg_endpoint function is defined" { - source /workspace/src/firewall-setup.sh - declare -f parse_wg_endpoint +setup() { + export TEST_TMPDIR=$(mktemp -d) } -@test "parse_wg_endpoint accepts optional config parameter" { - grep -q 'wg_config=.*${1:-' /workspace/src/firewall-setup.sh +teardown() { + rm -rf "$TEST_TMPDIR" } -@test "parse_wg_endpoint checks for WireGuard config file" { - grep -q '\[\[ ! -f.*wg_config \]\]' /workspace/src/firewall-setup.sh +# ============================================================================= +# parse_wg_endpoint - PRD FR-004 +# ============================================================================= + +@test "parse_wg_endpoint extracts endpoint from valid config" { + source /workspace/src/firewall-setup.sh + cat >"$TEST_TMPDIR/wg0.conf" <<'EOF' +[Interface] +PrivateKey = test123 +Address = 10.0.0.2/24 + +[Peer] +PublicKey = peer123 +Endpoint = 203.0.113.1:51820 +AllowedIPs = 0.0.0.0/0 +EOF + run parse_wg_endpoint "$TEST_TMPDIR/wg0.conf" + [ "$status" -eq 0 ] + [ "$output" = "203.0.113.1:51820" ] } -@test "parse_wg_endpoint returns error when config not found" { - grep -q 'return 1' /workspace/src/firewall-setup.sh +@test "parse_wg_endpoint fails when config missing" { + source /workspace/src/firewall-setup.sh + run parse_wg_endpoint "$TEST_TMPDIR/nonexistent.conf" + [ "$status" -ne 0 ] } -@test "parse_wg_endpoint parses endpoint from config" { - grep -q 'grep -oP.*Endpoint.*' /workspace/src/firewall-setup.sh +@test "parse_wg_endpoint fails when no Endpoint line" { + source /workspace/src/firewall-setup.sh + cat >"$TEST_TMPDIR/wg0.conf" <<'EOF' +[Interface] +PrivateKey = test123 +EOF + run parse_wg_endpoint "$TEST_TMPDIR/wg0.conf" + [ "$status" -ne 0 ] } -@test "parse_wg_endpoint returns error on parse failure" { - grep -q 'Could not parse endpoint' /workspace/src/firewall-setup.sh +# ============================================================================= +# generate_nftables_rules - PRD FR-004 +# ============================================================================= + +@test "generate_nftables_rules produces valid nftables config" { + source /workspace/src/firewall-setup.sh + run generate_nftables_rules "203.0.113.1:51820" + [ "$status" -eq 0 ] + echo "$output" | grep -q "flush ruleset" + echo "$output" | grep -q "table inet filter" } -# Test generate_nftables_rules function exists -@test "generate_nftables_rules function is defined" { - source /workspace/src/firewall-setup.sh - declare -f generate_nftables_rules +@test "Firewall input chain has DROP policy" { + source /workspace/src/firewall-setup.sh + result=$(generate_nftables_rules "203.0.113.1:51820") + echo "$result" | grep -q "type filter hook input priority 0; policy drop" } -@test "generate_nftables_rules accepts endpoint parameter" { - grep -q 'endpoint="$1"' /workspace/src/firewall-setup.sh +@test "Firewall forward chain has DROP policy" { + source /workspace/src/firewall-setup.sh + result=$(generate_nftables_rules "203.0.113.1:51820") + echo "$result" | grep -q "type filter hook forward priority 0; policy drop" } -@test "generate_nftables_rules parses IP from endpoint" { - grep -q 'local ip=' /workspace/src/firewall-setup.sh +@test "Firewall output chain has DROP policy" { + source /workspace/src/firewall-setup.sh + result=$(generate_nftables_rules "203.0.113.1:51820") + echo "$result" | grep -q "type filter hook output priority 0; policy drop" } -@test "generate_nftables_rules parses port from endpoint" { - grep -q 'local port=' /workspace/src/firewall-setup.sh +@test "Firewall allows loopback traffic" { + source /workspace/src/firewall-setup.sh + result=$(generate_nftables_rules "203.0.113.1:51820") + echo "$result" | grep -q "iif lo accept" + echo "$result" | grep -q "oif lo accept" } -@test "generate_nftables_rules generates nftables config" { - grep -q 'cat </etc/nftables.conf' /workspace/src/firewall-setup.sh -} - -@test "apply_firewall enables nftables service" { - grep -q 'systemctl enable nftables' /workspace/src/firewall-setup.sh -} - -@test "apply_firewall restarts nftables service" { - grep -q 'systemctl restart nftables' /workspace/src/firewall-setup.sh -} - -@test "apply_firewall handles missing config" { - grep -q 'Warning: WireGuard config not found' /workspace/src/firewall-setup.sh -} - -@test "apply_firewall handles parse failure" { - grep -q 'Warning: Could not parse WireGuard endpoint' /workspace/src/firewall-setup.sh -} - -# Test main function exists -@test "main function is defined" { - source /workspace/src/firewall-setup.sh - declare -f main -} - -@test "main calls apply_firewall" { - grep -q 'apply_firewall' /workspace/src/firewall-setup.sh -} - -@test "main outputs setup messages" { - grep -q 'Setting up' /workspace/src/firewall-setup.sh - grep -q 'completed' /workspace/src/firewall-setup.sh -} - -# Test script behavior -@test "script uses set -euo pipefail" { - grep -q "set -euo pipefail" /workspace/src/firewall-setup.sh -} - -@test "script is executable" { - [ -x "/workspace/src/firewall-setup.sh" ] -} - -@test "script has proper shebang" { - head -n1 /workspace/src/firewall-setup.sh | grep -q "#!/bin/bash" -} - -@test "script has comments explaining functions" { - grep -q "# Function to" /workspace/src/firewall-setup.sh -} - -@test "script checks if executed directly" { - grep -q 'BASH_SOURCE' /workspace/src/firewall-setup.sh -} - -@test "script calls main only when executed directly" { - grep -q '== "${0}"' /workspace/src/firewall-setup.sh -} - -@test "script has proper error messages" { - grep -q "Error:" /workspace/src/firewall-setup.sh -} - -@test "script has proper warning messages" { - grep -q "Warning:" /workspace/src/firewall-setup.sh +@test "firewall-setup.sh runs main when executed directly" { + grep -q 'BASH_SOURCE\[0\]' /workspace/src/firewall-setup.sh } diff --git a/tests/unit/new-hooks_test.bats b/tests/unit/new-hooks_test.bats new file mode 100644 index 0000000..7ba5363 --- /dev/null +++ b/tests/unit/new-hooks_test.bats @@ -0,0 +1,230 @@ +#!/usr/bin/env bats +# Behavioral tests for new PRD hooks +# Reference: PRD FR-005, FR-007 + +setup() { + export TEST_TMPDIR=$(mktemp -d) +} + +teardown() { + rm -rf "$TEST_TMPDIR" +} + +# ============================================================================= +# kernel-hardening.sh - PRD FR-007 +# ============================================================================= + +@test "kernel-hardening.sh hook exists and is executable" { + [ -f "/workspace/config/hooks/live/kernel-hardening.sh" ] + [ -x "/workspace/config/hooks/live/kernel-hardening.sh" ] +} + +@test "kernel-hardening.sh uses strict mode" { + head -5 /workspace/config/hooks/live/kernel-hardening.sh | grep -q "set -euo pipefail" +} + +@test "Kernel hardening enables ASLR" { + grep -q "randomize_va_space = 2" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening restricts ptrace scope" { + grep -q "ptrace_scope = 2" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening restricts kernel pointers" { + grep -q "kptr_restrict = 2" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening restricts dmesg" { + grep -q "dmesg_restrict = 1" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening disables kexec" { + grep -q "kexec_load = 0" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening disables SUID core dumps" { + grep -q "suid_dumpable = 0" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening protects hardlinks and symlinks" { + grep -q "protected_hardlinks = 1" /workspace/config/hooks/live/kernel-hardening.sh + grep -q "protected_symlinks = 1" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening disables IPv4 redirects" { + grep -q "send_redirects = 0" /workspace/config/hooks/live/kernel-hardening.sh + grep -q "accept_redirects = 0" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening enables SYN cookies" { + grep -q "tcp_syncookies = 1" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening enables reverse path filtering" { + grep -q "rp_filter = 1" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening disables IPv6 redirects" { + grep -q "ipv6.*accept_redirects = 0" /workspace/config/hooks/live/kernel-hardening.sh +} + +@test "Kernel hardening config installs to sysctl.d" { + grep -q "/etc/sysctl.d" /workspace/config/hooks/live/kernel-hardening.sh +} + +# ============================================================================= +# service-hardening.sh - PRD FR-007 +# ============================================================================= + +@test "service-hardening.sh hook exists and is executable" { + [ -f "/workspace/config/hooks/live/service-hardening.sh" ] + [ -x "/workspace/config/hooks/live/service-hardening.sh" ] +} + +@test "service-hardening.sh uses strict mode" { + head -5 /workspace/config/hooks/live/service-hardening.sh | grep -q "set -euo pipefail" +} + +@test "Service hardening disables avahi-daemon" { + grep -q "avahi-daemon" /workspace/config/hooks/live/service-hardening.sh +} + +@test "Service hardening disables cups" { + grep -q "cups" /workspace/config/hooks/live/service-hardening.sh +} + +@test "Service hardening disables bluetooth service" { + grep -q "bluetooth" /workspace/config/hooks/live/service-hardening.sh +} + +@test "Service hardening disables NetworkManager" { + grep -q "NetworkManager" /workspace/config/hooks/live/service-hardening.sh +} + +@test "Service hardening masks services to prevent re-enabling" { + grep -q "systemctl mask" /workspace/config/hooks/live/service-hardening.sh +} + +# ============================================================================= +# sudo-hardening.sh - PRD FR-007 +# ============================================================================= + +@test "sudo-hardening.sh hook exists and is executable" { + [ -f "/workspace/config/hooks/live/sudo-hardening.sh" ] + [ -x "/workspace/config/hooks/live/sudo-hardening.sh" ] +} + +@test "sudo-hardening.sh uses strict mode" { + head -5 /workspace/config/hooks/live/sudo-hardening.sh | grep -q "set -euo pipefail" +} + +@test "Sudo hardening requires TTY" { + grep -q "requiretty" /workspace/config/hooks/live/sudo-hardening.sh +} + +@test "Sudo hardening configures logging" { + grep -q "logfile" /workspace/config/hooks/live/sudo-hardening.sh + grep -q "log_input" /workspace/config/hooks/live/sudo-hardening.sh + grep -q "log_output" /workspace/config/hooks/live/sudo-hardening.sh +} + +@test "Sudo hardening sets timestamp timeout" { + grep -q "timestamp_timeout" /workspace/config/hooks/live/sudo-hardening.sh +} + +@test "Sudo hardening resets environment" { + grep -q "env_reset" /workspace/config/hooks/live/sudo-hardening.sh +} + +@test "Sudo hardening restricts football user to specific commands" { + grep -q "football" /workspace/config/hooks/live/sudo-hardening.sh + grep -q "apply-vpn-config.sh" /workspace/config/hooks/live/sudo-hardening.sh +} + +@test "Sudo hardening sets correct permissions (440)" { + grep -q "chmod 440" /workspace/config/hooks/live/sudo-hardening.sh +} + +# ============================================================================= +# mount-hardening.sh - PRD FR-007 +# ============================================================================= + +@test "mount-hardening.sh hook exists and is executable" { + [ -f "/workspace/config/hooks/installed/mount-hardening.sh" ] + [ -x "/workspace/config/hooks/installed/mount-hardening.sh" ] +} + +@test "mount-hardening.sh uses strict mode" { + head -5 /workspace/config/hooks/installed/mount-hardening.sh | grep -q "set -euo pipefail" +} + +@test "Mount hardening adds nodev to /tmp" { + grep -q "nodev" /workspace/config/hooks/installed/mount-hardening.sh +} + +@test "Mount hardening adds nosuid to /tmp" { + grep -q "nosuid" /workspace/config/hooks/installed/mount-hardening.sh +} + +@test "Mount hardening adds noexec to /tmp" { + grep -q "noexec" /workspace/config/hooks/installed/mount-hardening.sh +} + +# ============================================================================= +# Live hook self-containment (BUG FIX VERIFICATION) +# ============================================================================= + +@test "security-hardening.sh live hook is self-contained (no source from /build)" { + ! grep -q "source /build/" /workspace/config/hooks/live/security-hardening.sh +} + +@test "firewall-setup.sh live hook is self-contained (no source from /build)" { + ! grep -q "source /build/" /workspace/config/hooks/live/firewall-setup.sh +} + +@test "install-scripts.sh does not reference /workspace/src/" { + ! grep -q "/workspace/src/" /workspace/config/hooks/installed/install-scripts.sh +} + +@test "install-scripts.sh embeds firewall-setup.sh inline" { + grep -q "parse_wg_endpoint" /workspace/config/hooks/installed/install-scripts.sh + grep -q "generate_nftables_rules" /workspace/config/hooks/installed/install-scripts.sh +} + +# ============================================================================= +# WiFi blacklist completeness (BUG FIX VERIFICATION) +# ============================================================================= + +@test "WiFi blacklist covers rtl* family (PRD FR-005)" { + source /workspace/src/security-hardening.sh + tmpfile=$(mktemp) + create_wifi_blacklist "$tmpfile" + grep -q "rtl8" "$tmpfile" + rm -f "$tmpfile" +} + +@test "WiFi blacklist covers mwifi* family (PRD FR-005)" { + source /workspace/src/security-hardening.sh + tmpfile=$(mktemp) + create_wifi_blacklist "$tmpfile" + grep -q "mwifiex" "$tmpfile" + rm -f "$tmpfile" +} + +@test "WiFi blacklist covers rt2* family (PRD FR-005)" { + source /workspace/src/security-hardening.sh + tmpfile=$(mktemp) + create_wifi_blacklist "$tmpfile" + grep -q "rt2x00" "$tmpfile" + rm -f "$tmpfile" +} + +@test "WiFi blacklist covers ath* family (PRD FR-005)" { + source /workspace/src/security-hardening.sh + tmpfile=$(mktemp) + create_wifi_blacklist "$tmpfile" + grep -q "ath9k" "$tmpfile" + grep -q "ath10k" "$tmpfile" + rm -f "$tmpfile" +} diff --git a/tests/unit/security-hardening_comprehensive_test.bats b/tests/unit/security-hardening_comprehensive_test.bats index 8b84879..0342cee 100644 --- a/tests/unit/security-hardening_comprehensive_test.bats +++ b/tests/unit/security-hardening_comprehensive_test.bats @@ -1,348 +1,226 @@ #!/usr/bin/env bats -# Comprehensive unit tests for security-hardening.sh (100% coverage) +# Behavioral tests for security-hardening.sh functions +# Reference: PRD FR-005, FR-006, FR-007 -# Test create_wifi_blacklist function exists -@test "create_wifi_blacklist function is defined" { - source /workspace/src/security-hardening.sh - declare -f create_wifi_blacklist +setup() { + export TEST_TMPDIR=$(mktemp -d) } -@test "create_wifi_blacklist accepts optional output parameter" { - grep -q 'output_file=.*${1:-' /workspace/src/security-hardening.sh +teardown() { + rm -rf "$TEST_TMPDIR" } -@test "create_wifi_blacklist creates modprobe.d file" { - grep -q '/etc/modprobe.d/blacklist-wifi.conf' /workspace/src/security-hardening.sh +# ============================================================================= +# WiFi Blacklist - PRD FR-005 +# ============================================================================= + +@test "create_wifi_blacklist generates file with correct content" { + source /workspace/src/security-hardening.sh + create_wifi_blacklist "$TEST_TMPDIR/blacklist-wifi.conf" + [ -f "$TEST_TMPDIR/blacklist-wifi.conf" ] + grep -q "blacklist cfg80211" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "blacklist mac80211" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "blacklist iwlwifi" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "blacklist brcmfmac" "$TEST_TMPDIR/blacklist-wifi.conf" } -@test "create_wifi_blacklist blacklists cfg80211" { - grep -q 'blacklist cfg80211' /workspace/src/security-hardening.sh -} - -@test "create_wifi_blacklist blacklists mac80211" { - grep -q 'blacklist mac80211' /workspace/src/security-hardening.sh -} - -@test "create_wifi_blacklist blacklists brcmfmac" { - grep -q 'blacklist brcmfmac' /workspace/src/security-hardening.sh -} - -@test "create_wifi_blacklist blacklists iwlwifi" { - grep -q 'blacklist iwlwifi' /workspace/src/security-hardening.sh -} - -@test "create_wifi_blacklist blacklists ath9k" { - grep -q 'blacklist ath9k' /workspace/src/security-hardening.sh -} - -@test "create_wifi_blacklist blacklists rt73usb" { - grep -q 'blacklist rt73usb' /workspace/src/security-hardening.sh +@test "WiFi blacklist includes PRD-specified driver families" { + source /workspace/src/security-hardening.sh + create_wifi_blacklist "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "rtl8" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "iwlwifi" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "ath9k" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "brcmfmac" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "mwifiex" "$TEST_TMPDIR/blacklist-wifi.conf" + grep -q "rt2x00" "$TEST_TMPDIR/blacklist-wifi.conf" } @test "create_wifi_blacklist outputs completion message" { - grep -q 'created at' /workspace/src/security-hardening.sh + source /workspace/src/security-hardening.sh + run create_wifi_blacklist "$TEST_TMPDIR/blacklist-wifi.conf" + [ "$status" -eq 0 ] + [[ "$output" == *"created at"* ]] } -# Test create_bluetooth_blacklist function exists -@test "create_bluetooth_blacklist function is defined" { - source /workspace/src/security-hardening.sh - declare -f create_bluetooth_blacklist +# ============================================================================= +# Bluetooth Blacklist - PRD FR-005 +# ============================================================================= + +@test "create_bluetooth_blacklist generates file with correct content" { + source /workspace/src/security-hardening.sh + create_bluetooth_blacklist "$TEST_TMPDIR/blacklist-bt.conf" + [ -f "$TEST_TMPDIR/blacklist-bt.conf" ] + grep -q "blacklist btusb" "$TEST_TMPDIR/blacklist-bt.conf" + grep -q "blacklist bluetooth" "$TEST_TMPDIR/blacklist-bt.conf" + grep -q "blacklist btrtl" "$TEST_TMPDIR/blacklist-bt.conf" + grep -q "blacklist btintel" "$TEST_TMPDIR/blacklist-bt.conf" + grep -q "blacklist btbcm" "$TEST_TMPDIR/blacklist-bt.conf" } -@test "create_bluetooth_blacklist accepts optional output parameter" { - grep -q 'output_file=.*${1:-' /workspace/src/security-hardening.sh +@test "Bluetooth blacklist includes additional modules (bnep, rfcomm, hidp)" { + source /workspace/src/security-hardening.sh + create_bluetooth_blacklist "$TEST_TMPDIR/blacklist-bt.conf" + grep -q "blacklist bnep" "$TEST_TMPDIR/blacklist-bt.conf" + grep -q "blacklist rfcomm" "$TEST_TMPDIR/blacklist-bt.conf" + grep -q "blacklist hidp" "$TEST_TMPDIR/blacklist-bt.conf" } -@test "create_bluetooth_blacklist creates modprobe.d file" { - grep -q '/etc/modprobe.d/blacklist-bluetooth.conf' /workspace/src/security-hardening.sh +# ============================================================================= +# SSH Client Config - PRD FR-006 +# ============================================================================= + +@test "configure_ssh_client generates correct ssh_config" { + source /workspace/src/security-hardening.sh + configure_ssh_client "$TEST_TMPDIR/ssh_config" + [ -f "$TEST_TMPDIR/ssh_config" ] + grep -q "PasswordAuthentication no" "$TEST_TMPDIR/ssh_config" + grep -q "PubkeyAuthentication yes" "$TEST_TMPDIR/ssh_config" } -@test "create_bluetooth_blacklist blacklists btusb" { - grep -q 'blacklist btusb' /workspace/src/security-hardening.sh +@test "SSH client uses modern key exchange algorithms" { + source /workspace/src/security-hardening.sh + configure_ssh_client "$TEST_TMPDIR/ssh_config" + grep -q "KexAlgorithms" "$TEST_TMPDIR/ssh_config" + grep -q "curve25519-sha256" "$TEST_TMPDIR/ssh_config" } -@test "create_bluetooth_blacklist blacklists bluetooth" { - grep -q 'blacklist bluetooth' /workspace/src/security-hardening.sh +@test "SSH client uses modern ciphers" { + source /workspace/src/security-hardening.sh + configure_ssh_client "$TEST_TMPDIR/ssh_config" + grep -q "Ciphers" "$TEST_TMPDIR/ssh_config" + grep -q "chacha20-poly1305" "$TEST_TMPDIR/ssh_config" } -@test "create_bluetooth_blacklist blacklists btrtl" { - grep -q 'blacklist btrtl' /workspace/src/security-hardening.sh +@test "SSH client enables strict host key checking" { + source /workspace/src/security-hardening.sh + configure_ssh_client "$TEST_TMPDIR/ssh_config" + grep -q "StrictHostKeyChecking ask" "$TEST_TMPDIR/ssh_config" } -@test "create_bluetooth_blacklist blacklists btintel" { - grep -q 'blacklist btintel' /workspace/src/security-hardening.sh +# ============================================================================= +# Password Policy - PRD FR-007 +# ============================================================================= + +@test "configure_password_policy generates correct pwquality.conf" { + source /workspace/src/security-hardening.sh + configure_password_policy "$TEST_TMPDIR/pwquality.conf" + [ -f "$TEST_TMPDIR/pwquality.conf" ] + grep -q "minlen = 14" "$TEST_TMPDIR/pwquality.conf" + grep -q "dcredit = -1" "$TEST_TMPDIR/pwquality.conf" + grep -q "ucredit = -1" "$TEST_TMPDIR/pwquality.conf" + grep -q "lcredit = -1" "$TEST_TMPDIR/pwquality.conf" + grep -q "ocredit = -1" "$TEST_TMPDIR/pwquality.conf" } -@test "create_bluetooth_blacklist blacklists btbcm" { - grep -q 'blacklist btbcm' /workspace/src/security-hardening.sh +@test "Password policy requires 3 of 4 character classes" { + source /workspace/src/security-hardening.sh + configure_password_policy "$TEST_TMPDIR/pwquality.conf" + grep -q "minclass = 3" "$TEST_TMPDIR/pwquality.conf" } -@test "create_bluetooth_blacklist outputs completion message" { - grep -q 'created at' /workspace/src/security-hardening.sh +@test "Password policy enforces complexity (enforcing=1)" { + source /workspace/src/security-hardening.sh + configure_password_policy "$TEST_TMPDIR/pwquality.conf" + grep -q "enforcing = 1" "$TEST_TMPDIR/pwquality.conf" } -# Test configure_ssh function exists -@test "configure_ssh function is defined" { - source /workspace/src/security-hardening.sh - declare -f configure_ssh +@test "Password policy rejects common bad words" { + source /workspace/src/security-hardening.sh + configure_password_policy "$TEST_TMPDIR/pwquality.conf" + grep -q "badwords" "$TEST_TMPDIR/pwquality.conf" } -@test "configure_ssh accepts optional output parameter" { - grep -q 'output_file=.*${1:-' /workspace/src/security-hardening.sh +# ============================================================================= +# FIM (AIDE) - CIS 1.4 +# ============================================================================= + +@test "configure_fim generates valid AIDE config" { + source /workspace/src/security-hardening.sh + configure_fim "$TEST_TMPDIR/aide.conf" "$TEST_TMPDIR/aide.db" + [ -f "$TEST_TMPDIR/aide.conf" ] + grep -q "SECURITY = " "$TEST_TMPDIR/aide.conf" + grep -q "/etc SECURITY" "$TEST_TMPDIR/aide.conf" + grep -q "/boot SECURITY" "$TEST_TMPDIR/aide.conf" + grep -q "/usr SECURITY" "$TEST_TMPDIR/aide.conf" } -@test "configure_ssh creates sshd_config file" { - grep -q '/etc/ssh/sshd_config' /workspace/src/security-hardening.sh +@test "FIM config excludes volatile paths" { + source /workspace/src/security-hardening.sh + configure_fim "$TEST_TMPDIR/aide.conf" "$TEST_TMPDIR/aide.db" + grep -q "!/proc" "$TEST_TMPDIR/aide.conf" + grep -q "!/sys" "$TEST_TMPDIR/aide.conf" + grep -q "!/dev" "$TEST_TMPDIR/aide.conf" + grep -q "!/tmp" "$TEST_TMPDIR/aide.conf" } -@test "configure_ssh sets Protocol to 2" { - grep -q 'Protocol 2' /workspace/src/security-hardening.sh -} - -@test "configure_ssh disables root login" { - grep -q 'PermitRootLogin no' /workspace/src/security-hardening.sh -} - -@test "configure_ssh disables empty passwords" { - grep -q 'PermitEmptyPasswords no' /workspace/src/security-hardening.sh -} - -@test "configure_ssh sets MaxAuthTries to 3" { - grep -q 'MaxAuthTries 3' /workspace/src/security-hardening.sh -} - -@test "configure_ssh sets ClientAliveInterval to 300" { - grep -q 'ClientAliveInterval 300' /workspace/src/security-hardening.sh -} - -@test "configure_ssh sets ClientAliveCountMax to 2" { - grep -q 'ClientAliveCountMax 2' /workspace/src/security-hardening.sh -} - -@test "configure_ssh disables X11 forwarding" { - grep -q 'X11Forwarding no' /workspace/src/security-hardening.sh -} - -@test "configure_ssh outputs completion message" { - grep -q 'created at' /workspace/src/security-hardening.sh -} - -# Test configure_password_policy function exists -@test "configure_password_policy function is defined" { - source /workspace/src/security-hardening.sh - declare -f configure_password_policy -} - -@test "configure_password_policy accepts optional output parameter" { - grep -q 'output_file=.*${1:-' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy creates pwquality.conf file" { - grep -q '/etc/security/pwquality.conf' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy sets minlen to 14" { - grep -q 'minlen = 14' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy requires 1 digit" { - grep -q 'dcredit = -1' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy requires 1 uppercase" { - grep -q 'ucredit = -1' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy requires 1 lowercase" { - grep -q 'lcredit = -1' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy requires 1 special char" { - grep -q 'ocredit = -1' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy enforces minimum requirements" { - grep -q 'enforcing = 1' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy checks dictionary" { - grep -q 'dictcheck = 1' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy checks username" { - grep -q 'usercheck = 1' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy sets maxrepeat to 2" { - grep -q 'maxrepeat = 2' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy sets maxsequence to 2" { - grep -q 'maxsequence = 2' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy sets minclass to 3" { - grep -q 'minclass = 3' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy has security comments" { - grep -q 'NIST SP 800-63B' /workspace/src/security-hardening.sh -} - -@test "configure_password_policy outputs completion message" { - grep -q 'configured at' /workspace/src/security-hardening.sh -} - -# Test configure_system_limits function exists -@test "configure_system_limits function is defined" { - source /workspace/src/security-hardening.sh - declare -f configure_system_limits -} - -@test "configure_system_limits accepts optional output parameter" { - grep -q 'output_file=.*${1:-' /workspace/src/security-hardening.sh -} - -@test "configure_system_limits creates limits file" { - grep -q '/etc/security/limits.d/security.conf' /workspace/src/security-hardening.sh -} +# ============================================================================= +# System Limits - PRD FR-007 +# ============================================================================= @test "configure_system_limits disables core dumps" { - grep -q 'hard core 0' /workspace/src/security-hardening.sh + source /workspace/src/security-hardening.sh + configure_system_limits "$TEST_TMPDIR/limits.conf" + [ -f "$TEST_TMPDIR/limits.conf" ] + grep -q "hard core 0" "$TEST_TMPDIR/limits.conf" } -@test "configure_system_limits sets nproc limits" { - grep -q 'nproc' /workspace/src/security-hardening.sh +# ============================================================================= +# Audit Rules - CIS 6.2, FedRAMP AU-2 +# ============================================================================= + +@test "configure_audit_rules generates comprehensive audit config" { + source /workspace/src/security-hardening.sh + configure_audit_rules "$TEST_TMPDIR/audit.rules" + [ -f "$TEST_TMPDIR/audit.rules" ] + grep -q "/etc/passwd" "$TEST_TMPDIR/audit.rules" + grep -q "/etc/shadow" "$TEST_TMPDIR/audit.rules" + grep -q "/etc/sudoers" "$TEST_TMPDIR/audit.rules" + grep -q "/etc/wireguard/" "$TEST_TMPDIR/audit.rules" + grep -q "init_module" "$TEST_TMPDIR/audit.rules" } -@test "configure_system_limits outputs completion message" { - grep -q 'configured at' /workspace/src/security-hardening.sh +@test "Audit rules monitor privilege escalation" { + source /workspace/src/security-hardening.sh + configure_audit_rules "$TEST_TMPDIR/audit.rules" + grep -q "privilege_escalation" "$TEST_TMPDIR/audit.rules" } -# Test configure_audit_rules function exists -@test "configure_audit_rules function is defined" { - source /workspace/src/security-hardening.sh - declare -f configure_audit_rules +@test "Audit rules monitor network configuration" { + source /workspace/src/security-hardening.sh + configure_audit_rules "$TEST_TMPDIR/audit.rules" + grep -q "network_config" "$TEST_TMPDIR/audit.rules" } -@test "configure_audit_rules accepts optional output parameter" { - grep -q 'output_file=.*${1:-' /workspace/src/security-hardening.sh +# ============================================================================= +# apply_security_hardening - PRD FR-007 +# ============================================================================= + +@test "apply_security_hardening calls all config functions" { + grep -q "create_wifi_blacklist" /workspace/src/security-hardening.sh + grep -q "create_bluetooth_blacklist" /workspace/src/security-hardening.sh + grep -q "configure_ssh" /workspace/src/security-hardening.sh + grep -q "configure_password_policy" /workspace/src/security-hardening.sh + grep -q "configure_system_limits" /workspace/src/security-hardening.sh + grep -q "configure_audit_rules" /workspace/src/security-hardening.sh } -@test "configure_audit_rules creates audit.rules file" { - grep -q '/etc/audit/rules.d/audit.rules' /workspace/src/security-hardening.sh +# ============================================================================= +# Script Structure +# ============================================================================= + +@test "security-hardening.sh uses strict mode" { + head -5 /workspace/src/security-hardening.sh | grep -q "set -euo pipefail" } -@test "configure_audit_rules monitors passwd file" { - grep -q '/etc/passwd' /workspace/src/security-hardening.sh +@test "security-hardening.sh is executable" { + [ -x "/workspace/src/security-hardening.sh" ] } -@test "configure_audit_rules monitors shadow file" { - grep -q '/etc/shadow' /workspace/src/security-hardening.sh +@test "security-hardening.sh has valid bash syntax" { + run bash -n /workspace/src/security-hardening.sh + [ "$status" -eq 0 ] } -@test "configure_audit_rules monitors sshd_config" { - grep -q '/etc/ssh/sshd_config' /workspace/src/security-hardening.sh -} - -@test "configure_audit_rules monitors wireguard directory" { - grep -q '/etc/wireguard/' /workspace/src/security-hardening.sh -} - -@test "configure_audit_rules monitors audit logs" { - grep -q '/var/log/audit/' /workspace/src/security-hardening.sh -} - -@test "configure_audit_rules outputs completion message" { - grep -q 'configured at' /workspace/src/security-hardening.sh -} - -# Test apply_security_hardening function exists -@test "apply_security_hardening function is defined" { - source /workspace/src/security-hardening.sh - declare -f apply_security_hardening -} - -@test "apply_security_hardening calls create_wifi_blacklist" { - grep -q 'create_wifi_blacklist' /workspace/src/security-hardening.sh -} - -@test "apply_security_hardening calls create_bluetooth_blacklist" { - grep -q 'create_bluetooth_blacklist' /workspace/src/security-hardening.sh -} - -@test "apply_security_hardening calls configure_ssh" { - grep -q 'configure_ssh' /workspace/src/security-hardening.sh -} - -@test "apply_security_hardening calls configure_password_policy" { - grep -q 'configure_password_policy' /workspace/src/security-hardening.sh -} - -@test "apply_security_hardening calls configure_system_limits" { - grep -q 'configure_system_limits' /workspace/src/security-hardening.sh -} - -@test "apply_security_hardening calls configure_audit_rules" { - grep -q 'configure_audit_rules' /workspace/src/security-hardening.sh -} - -@test "apply_security_hardening outputs progress messages" { - grep -q 'Applying security hardening' /workspace/src/security-hardening.sh -} - -@test "apply_security_hardening outputs completion message" { - grep -q 'completed' /workspace/src/security-hardening.sh -} - -# Test main function exists -@test "main function is defined" { - source /workspace/src/security-hardening.sh - declare -f main -} - -@test "main calls apply_security_hardening" { - grep -q 'apply_security_hardening' /workspace/src/security-hardening.sh -} - -@test "main outputs start message" { - grep -q 'Starting KNEL-Football security hardening' /workspace/src/security-hardening.sh -} - -@test "main outputs completion message" { - grep -q 'completed successfully' /workspace/src/security-hardening.sh -} - -# Test script behavior -@test "script uses set -euo pipefail" { - grep -q "set -euo pipefail" /workspace/src/security-hardening.sh -} - -@test "script is executable" { - [ -x "/workspace/src/security-hardening.sh" ] -} - -@test "script has proper shebang" { - head -n1 /workspace/src/security-hardening.sh | grep -q "#!/bin/bash" -} - -@test "script checks if executed directly" { - grep -q 'BASH_SOURCE' /workspace/src/security-hardening.sh -} - -@test "script calls main only when executed directly" { - grep -q '== "${0}"' /workspace/src/security-hardening.sh -} - -@test "script has comments explaining security requirements" { - grep -q 'NIST' /workspace/src/security-hardening.sh - grep -q 'CIS' /workspace/src/security-hardening.sh -} - -@test "script has mandatory password requirements" { - grep -q 'MANDATORY' /workspace/src/security-hardening.sh -} - -@test "script has compliance references" { - grep -q 'tier0' /workspace/src/security-hardening.sh +@test "security-hardening.sh runs main when executed directly" { + grep -q 'BASH_SOURCE\[0\]' /workspace/src/security-hardening.sh } diff --git a/verify.sh b/verify.sh index f1e7875..45a97e2 100755 --- a/verify.sh +++ b/verify.sh @@ -57,7 +57,7 @@ fi # 5. ISO artifact check echo "Phase 4: ISO artifact..." if ls output/*.iso &>/dev/null; then - ISO_FILE=$(ls output/*.iso | head -1) + ISO_FILE=$(find output -name '*.iso' -type f | head -1) ISO_SIZE=$(du -h "$ISO_FILE" | cut -f1) log_pass "ISO exists: $ISO_FILE ($ISO_SIZE)" # Check checksums