1728 lines
66 KiB
Bash
1728 lines
66 KiB
Bash
#!/bin/bash
|
|
############################################################################
|
|
SCRIPT_VERSION="2.0.2"
|
|
|
|
# Colors for output (optional)
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
NC='\033[0m' # No Color
|
|
|
|
############################################################################
|
|
# HELPER FUNCTIONS
|
|
############################################################################
|
|
|
|
# Function to output result in JSON format
|
|
output_result() {
|
|
local check_id="$1"
|
|
local check_name="$2"
|
|
local status="$3"
|
|
echo "{\"${check_id}. ${check_name}\" : \"${status}\"}"
|
|
}
|
|
|
|
# Function to check if module is disabled
|
|
check_module_disabled() {
|
|
local module="$1"
|
|
local module_underscore="${module//-/_}"
|
|
|
|
# Check if module is loaded
|
|
if lsmod | grep -qw "$module_underscore"; then
|
|
return 1
|
|
fi
|
|
|
|
# Check if module is set to install /bin/true or /bin/false
|
|
if modprobe -n -v "$module" 2>/dev/null | grep -qE "^[[:space:]]*install[[:space:]]+(/bin/(true|false)|${module}[[:space:]]+/bin/(true|false))"; then
|
|
return 0
|
|
fi
|
|
|
|
# Check if module is blacklisted
|
|
if grep -s -h -qE "^[[:space:]]*blacklist[[:space:]]+${module}([[:space:]]|$)" /etc/modprobe.d/*.conf 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
|
|
# Check kernel cmdline
|
|
if grep -qw "modprobe.blacklist=${module}" /proc/cmdline 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Function to check mount option
|
|
check_mount_option() {
|
|
local mount_point="$1"
|
|
local option="$2"
|
|
|
|
if ! findmnt --kernel "$mount_point" >/dev/null 2>&1; then
|
|
return 0 # Not mounted, considered PASSED
|
|
fi
|
|
|
|
if findmnt --kernel "$mount_point" | grep -q "$option" && \
|
|
grep -E "\s+${mount_point}\s+.*${option}" /etc/fstab >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Function to check service status
|
|
check_service_disabled() {
|
|
local service="$1"
|
|
|
|
if systemctl is-enabled "$service" 2>/dev/null | grep -q "^enabled"; then
|
|
return 1
|
|
fi
|
|
if systemctl is-active "$service" 2>/dev/null | grep -q "^active"; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Function to check package installed
|
|
check_package_installed() {
|
|
local package="$1"
|
|
rpm -qa "$package" 2>/dev/null | grep -q .
|
|
}
|
|
|
|
# Function to check package not installed
|
|
check_package_not_installed() {
|
|
local package="$1"
|
|
! rpm -qa "$package" 2>/dev/null | grep -q .
|
|
}
|
|
|
|
# Function to check file permissions
|
|
check_file_perms() {
|
|
local file="$1"
|
|
local expected_mode="$2"
|
|
local expected_uid="$3"
|
|
local expected_gid="$4"
|
|
|
|
if [ ! -e "$file" ]; then
|
|
return 0
|
|
fi
|
|
|
|
local actual=$(stat -c "%a:%u:%g" "$file" 2>/dev/null)
|
|
if echo "$actual" | grep -qP "^${expected_mode}:${expected_uid}:${expected_gid}$"; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Function to check sysctl parameter
|
|
check_sysctl() {
|
|
local param="$1"
|
|
local expected="$2"
|
|
|
|
local current=$(sysctl -n "$param" 2>/dev/null)
|
|
if [ "$current" = "$expected" ]; then
|
|
# Also check persistent config
|
|
if grep -qE "^[[:space:]]*${param}[[:space:]]*=[[:space:]]*${expected}" /etc/sysctl.conf /etc/sysctl.d/*.conf 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Function to log failed check details
|
|
log_failed() {
|
|
local recommendation="$1"
|
|
echo "######################################"
|
|
echo "# Khuyến nghị:"
|
|
echo "$recommendation"
|
|
echo "######################################"
|
|
}
|
|
|
|
############################################################################
|
|
# SYSTEM INFORMATION
|
|
############################################################################
|
|
echo "============================================================================"
|
|
echo " THÔNG TIN HỆ THỐNG"
|
|
echo "============================================================================"
|
|
echo ""
|
|
echo "Script Version: $SCRIPT_VERSION"
|
|
echo ""
|
|
echo "--- Thông tin cơ bản ---"
|
|
echo "Operating System: $(cat /etc/system-release 2>/dev/null || cat /etc/redhat-release 2>/dev/null || cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"')"
|
|
echo "Kernel Version: $(uname -r)"
|
|
echo "Architecture: $(uname -m)"
|
|
echo "Hostname: $(hostname)"
|
|
echo "FQDN: $(hostname -f 2>/dev/null || echo 'N/A')"
|
|
echo "IP Address: $(hostname -I 2>/dev/null | awk '{print $1}')"
|
|
echo "All IP Addresses: $(hostname -I 2>/dev/null | tr ' ' ', ' | sed 's/,$//')"
|
|
echo "Audit Time: $(date +"%Y-%m-%d %H:%M:%S")"
|
|
echo "Timezone: $(timedatectl 2>/dev/null | grep 'Time zone' | awk '{print $3}' || date +%Z)"
|
|
echo "Uptime: $(uptime -p 2>/dev/null || uptime | awk -F'up ' '{print $2}' | awk -F',' '{print $1, $2}')"
|
|
echo ""
|
|
|
|
echo "--- Thông tin CPU ---"
|
|
echo "CPU Model: $(grep 'model name' /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | sed 's/^[ \t]*//')"
|
|
echo "CPU Cores: $(nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo)"
|
|
echo "CPU Architecture: $(lscpu 2>/dev/null | grep 'Architecture' | awk '{print $2}' || uname -p)"
|
|
echo ""
|
|
|
|
echo "--- Thông tin Memory ---"
|
|
echo "Total Memory: $(free -h 2>/dev/null | awk '/^Mem:/{print $2}' || grep MemTotal /proc/meminfo | awk '{print $2/1024/1024 " GB"}')"
|
|
echo "Used Memory: $(free -h 2>/dev/null | awk '/^Mem:/{print $3}')"
|
|
echo "Free Memory: $(free -h 2>/dev/null | awk '/^Mem:/{print $4}')"
|
|
echo "Swap Total: $(free -h 2>/dev/null | awk '/^Swap:/{print $2}')"
|
|
echo ""
|
|
|
|
echo "--- Thông tin Disk ---"
|
|
echo "Disk Usage:"
|
|
df -h / /home /var /tmp 2>/dev/null | grep -v "^Filesystem" | awk '{printf " %-20s %s used of %s (%s)\n", $6, $3, $2, $5}'
|
|
echo ""
|
|
|
|
echo "--- Thông tin Network Interfaces ---"
|
|
echo "Primary Interface: $(ip route 2>/dev/null | grep default | awk '{print $5}' | head -1)"
|
|
echo "MAC Address: $(ip link 2>/dev/null | grep -A1 "$(ip route | grep default | awk '{print $5}' | head -1)" | grep ether | awk '{print $2}')"
|
|
echo "Default Gateway: $(ip route 2>/dev/null | grep default | awk '{print $3}' | head -1)"
|
|
echo "DNS Servers: $(grep nameserver /etc/resolv.conf 2>/dev/null | awk '{print $2}' | tr '\n' ', ' | sed 's/,$//')"
|
|
echo ""
|
|
|
|
echo "--- Thông tin bổ sung ---"
|
|
echo "SELinux Status: $(getenforce 2>/dev/null || echo 'Not installed')"
|
|
echo "Firewall: $(systemctl is-active firewalld 2>/dev/null || echo 'inactive') (firewalld) / $(systemctl is-active iptables 2>/dev/null || echo 'inactive') (iptables)"
|
|
echo "Last Boot: $(who -b 2>/dev/null | awk '{print $3, $4}')"
|
|
echo "Current Users: $(who 2>/dev/null | wc -l)"
|
|
echo "Load Average: $(cat /proc/loadavg 2>/dev/null | awk '{print $1, $2, $3}')"
|
|
echo ""
|
|
|
|
echo "============================================================================"
|
|
|
|
# Detect CentOS version
|
|
CENTOS_VERSION=$(grep -oE '[0-9]+\.[0-9]+' /etc/system-release 2>/dev/null | head -1 | cut -d. -f1)
|
|
echo "CentOS Version Detected: $CENTOS_VERSION"
|
|
|
|
# Check IPv6 status
|
|
IPV6_ENABLED=0
|
|
if [ -f /proc/net/if_inet6 ] && [ -s /proc/net/if_inet6 ]; then
|
|
if ip -6 addr show 2>/dev/null | grep -q "inet6" && \
|
|
ip -6 addr show 2>/dev/null | grep "inet6" | grep -vq "::1/128"; then
|
|
IPV6_ENABLED=1
|
|
echo "IPv6 Status: ENABLED"
|
|
else
|
|
echo "IPv6 Status: DISABLED (no IPv6 addresses assigned)"
|
|
fi
|
|
else
|
|
echo "IPv6 Status: DISABLED (kernel support not available)"
|
|
fi
|
|
echo "############################################################################"
|
|
|
|
############################################################################
|
|
# 1. INITIAL SETUP
|
|
############################################################################
|
|
echo ""
|
|
echo "=== 1. Thiết lập ban đầu ==="
|
|
echo ""
|
|
|
|
############################################################################
|
|
# 1.1 Filesystem Configuration
|
|
############################################################################
|
|
echo "--- 1.1. Cấu hình filesystem ---"
|
|
|
|
# 1.1.1 Disable unused filesystems
|
|
FILESYSTEMS=("cramfs" "freevxfs" "hfs" "hfsplus" "jffs2" "squashfs" "udf" "usb-storage")
|
|
FS_NAMES=("cramfs filesystem" "freevxfs filesystem" "hfs filesystem" "hfsplus filesystem" "jffs2 filesystem" "squashfs filesystem" "udf filesystem" "usb-storage filesystem")
|
|
|
|
for i in "${!FILESYSTEMS[@]}"; do
|
|
fs="${FILESYSTEMS[$i]}"
|
|
fs_name="${FS_NAMES[$i]}"
|
|
check_num="1.1.1.$((i+1))"
|
|
check_name="Cấu hình vô hiệu hoá ${fs_name}"
|
|
|
|
if check_module_disabled "$fs"; then
|
|
output_result "$check_num" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_num" "$check_name" "FAILED"
|
|
log_failed "# Thêm vào /etc/modprobe.d/filesystem.conf:
|
|
# install ${fs} /bin/true
|
|
# blacklist ${fs}
|
|
# Gỡ module nếu đang nạp: rmmod ${fs}"
|
|
fi
|
|
done
|
|
|
|
# 1.1.2 /tmp partition options
|
|
echo ""
|
|
echo "--- 1.1.2. Cấu hình phân vùng /tmp ---"
|
|
|
|
TMP_OPTIONS=("nodev" "nosuid" "noexec")
|
|
for i in "${!TMP_OPTIONS[@]}"; do
|
|
opt="${TMP_OPTIONS[$i]}"
|
|
check_num="1.1.2.$((i+1))"
|
|
check_name="Cấu hình tuỳ chọn ${opt} cho phân vùng /tmp"
|
|
|
|
if check_mount_option "/tmp" "$opt"; then
|
|
output_result "$check_num" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_num" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
|
|
# 1.1.3 /var/tmp partition options
|
|
echo ""
|
|
echo "--- 1.1.3. Cấu hình phân vùng /var/tmp ---"
|
|
|
|
for i in "${!TMP_OPTIONS[@]}"; do
|
|
opt="${TMP_OPTIONS[$i]}"
|
|
check_num="1.1.3.$((i+1))"
|
|
check_name="Cấu hình tuỳ chọn ${opt} cho phân vùng /var/tmp"
|
|
|
|
if check_mount_option "/var/tmp" "$opt"; then
|
|
output_result "$check_num" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_num" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
|
|
# 1.1.4 /home partition options
|
|
echo ""
|
|
echo "--- 1.1.4. Cấu hình phân vùng /home ---"
|
|
|
|
HOME_OPTIONS=("nodev" "nosuid")
|
|
for i in "${!HOME_OPTIONS[@]}"; do
|
|
opt="${HOME_OPTIONS[$i]}"
|
|
check_num="1.1.4.$((i+1))"
|
|
check_name="Cấu hình tuỳ chọn ${opt} cho phân vùng /home"
|
|
|
|
if check_mount_option "/home" "$opt"; then
|
|
output_result "$check_num" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_num" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
|
|
# 1.1.5 /dev/shm partition options
|
|
echo ""
|
|
echo "--- 1.1.5. Cấu hình phân vùng /dev/shm ---"
|
|
|
|
for i in "${!TMP_OPTIONS[@]}"; do
|
|
opt="${TMP_OPTIONS[$i]}"
|
|
check_num="1.1.5.$((i+1))"
|
|
check_name="Cấu hình tuỳ chọn ${opt} cho phân vùng /dev/shm"
|
|
|
|
if check_mount_option "/dev/shm" "$opt"; then
|
|
output_result "$check_num" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_num" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
|
|
############################################################################
|
|
# 1.2 Software Updates
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 1.2. Cấu hình cập nhật phần mềm ---"
|
|
|
|
# 1.2.1 GPG Check
|
|
gpg_failed=0
|
|
if grep -qP "^gpgcheck\h*=\h*[^1]" /etc/yum.repos.d/*.repo 2>/dev/null; then
|
|
gpg_failed=1
|
|
fi
|
|
if grep -qP "^gpgcheck\h*=\h*[^1]" /etc/yum.conf 2>/dev/null; then
|
|
gpg_failed=1
|
|
fi
|
|
|
|
if [ $gpg_failed -eq 0 ]; then
|
|
output_result "1.2.1" "Cấu hình kích hoạt gpgcheck" "PASSED"
|
|
else
|
|
output_result "1.2.1" "Cấu hình kích hoạt gpgcheck" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 1.3 Filesystem Integrity
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 1.3. Kiểm tra tính toàn vẹn của filesystem ---"
|
|
|
|
# 1.3.1 AIDE installed
|
|
if check_package_installed "aide"; then
|
|
output_result "1.3.1" "Kiểm tra cài đặt AIDE" "PASSED"
|
|
else
|
|
output_result "1.3.1" "Kiểm tra cài đặt AIDE" "FAILED"
|
|
fi
|
|
|
|
# 1.3.2 AIDE cron job
|
|
if check_package_installed "aide" && { \
|
|
(crontab -l 2>/dev/null | grep -Eq '^\s*[^#].*\baide\b.*--check') || \
|
|
grep -rsEq '^\s*[^#].*\baide\b.*--check' /etc/cron* /var/spool/cron 2>/dev/null || \
|
|
systemctl is-enabled aidecheck.timer 2>/dev/null | grep -q "^enabled" || \
|
|
systemctl is-enabled aide-check.timer 2>/dev/null | grep -q "^enabled"; }; then
|
|
output_result "1.3.2" "Cấu hình kiểm tra tính toàn vẹn của filesystem" "PASSED"
|
|
else
|
|
output_result "1.3.2" "Cấu hình kiểm tra tính toàn vẹn của filesystem" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 1.4 Secure Boot Settings
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 1.4. Cấu hình khởi động an toàn ---"
|
|
|
|
# 1.4.1 Bootloader permissions
|
|
grubdir=$(dirname "$(find /boot -type f \( -name 'grubenv' -o -name 'grub.conf' -o -name 'grub.cfg' \) -exec grep -Pl '^\h*(kernelopts=|linux|kernel)' {} \; 2>/dev/null | head -1)" 2>/dev/null)
|
|
|
|
grub_perms_ok=1
|
|
for f in "$grubdir/grub.cfg" "$grubdir/grubenv" "$grubdir/user.cfg"; do
|
|
if [ -f "$f" ]; then
|
|
perms=$(stat -c "%a:%u:%g" "$f" 2>/dev/null)
|
|
if ! echo "$perms" | grep -qP '^[0-7]00:0:0$'; then
|
|
grub_perms_ok=0
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ $grub_perms_ok -eq 1 ]; then
|
|
output_result "1.4.1" "Phân quyền đối với file cấu hình bootloader" "PASSED"
|
|
else
|
|
output_result "1.4.1" "Phân quyền đối với file cấu hình bootloader" "FAILED"
|
|
fi
|
|
|
|
# 1.4.2 Rescue mode authentication
|
|
rescue_ok=0
|
|
if [ "$CENTOS_VERSION" -ge 8 ] 2>/dev/null; then
|
|
if grep -Er "^ExecStart.*/systemd-sulogin-shell" /usr/lib/systemd/system/rescue.service /etc/systemd/system/rescue.service.d 2>/dev/null | grep -q .; then
|
|
rescue_ok=1
|
|
fi
|
|
else
|
|
if grep -q "/sbin/sulogin" /usr/lib/systemd/system/rescue.service 2>/dev/null && \
|
|
grep -q "/sbin/sulogin" /usr/lib/systemd/system/emergency.service 2>/dev/null; then
|
|
rescue_ok=1
|
|
fi
|
|
fi
|
|
|
|
if [ $rescue_ok -eq 1 ]; then
|
|
output_result "1.4.2" "Cấu hình xác thực khi truy cập rescue mode" "PASSED"
|
|
else
|
|
output_result "1.4.2" "Cấu hình xác thực khi truy cập rescue mode" "FAILED"
|
|
fi
|
|
|
|
# 1.4.3 Single user mode (CentOS 6 only)
|
|
if [ "$CENTOS_VERSION" -eq 6 ] 2>/dev/null; then
|
|
if grep "^SINGLE" /etc/sysconfig/init 2>/dev/null | grep -q "/sbin/sulogin"; then
|
|
output_result "1.4.3" "Cấu hình xác thực khi truy cập single user mode" "PASSED"
|
|
else
|
|
output_result "1.4.3" "Cấu hình xác thực khi truy cập single user mode" "FAILED"
|
|
fi
|
|
else
|
|
output_result "1.4.3" "Cấu hình xác thực khi truy cập single user mode" "PASSED"
|
|
fi
|
|
|
|
# 1.4.4 Interactive boot (CentOS 6 only)
|
|
if [ "$CENTOS_VERSION" -eq 6 ] 2>/dev/null; then
|
|
if grep "^PROMPT" /etc/sysconfig/init 2>/dev/null | grep -q "no"; then
|
|
output_result "1.4.4" "Cấu hình vô hiệu hoá interactive boot" "PASSED"
|
|
else
|
|
output_result "1.4.4" "Cấu hình vô hiệu hoá interactive boot" "FAILED"
|
|
fi
|
|
else
|
|
output_result "1.4.4" "Cấu hình vô hiệu hoá interactive boot" "PASSED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 1.5 Additional Process Hardening
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 1.5. Additional Process Hardening ---"
|
|
|
|
# 1.5.1 Core dumps
|
|
core_dump_ok=1
|
|
if ! grep -qP "^\h*\*\s+hard\s+core\s+0" /etc/security/limits.conf /etc/security/limits.d/*.conf 2>/dev/null; then
|
|
core_dump_ok=0
|
|
fi
|
|
if ! sysctl fs.suid_dumpable 2>/dev/null | grep -q "= 0"; then
|
|
core_dump_ok=0
|
|
fi
|
|
|
|
if [ $core_dump_ok -eq 1 ]; then
|
|
output_result "1.5.1" "Cấu hình vô hiệu hoá core dump" "PASSED"
|
|
else
|
|
output_result "1.5.1" "Cấu hình vô hiệu hoá core dump" "FAILED"
|
|
fi
|
|
|
|
# 1.5.2 ASLR
|
|
if check_sysctl "kernel.randomize_va_space" "2"; then
|
|
output_result "1.5.2" "Cấu hình kích hoạt ASLR (address space layout randomization)" "PASSED"
|
|
else
|
|
output_result "1.5.2" "Cấu hình kích hoạt ASLR (address space layout randomization)" "FAILED"
|
|
fi
|
|
|
|
# 1.5.3 Prelink
|
|
if check_package_not_installed "prelink"; then
|
|
output_result "1.5.3" "Cấu hình vô hiệu hoá prelink" "PASSED"
|
|
else
|
|
output_result "1.5.3" "Cấu hình vô hiệu hoá prelink" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 1.6 Warning Banners
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 1.6. Kiểm soát nội dung cảnh báo ---"
|
|
|
|
# Function to check banner content
|
|
check_banner_content() {
|
|
local file="$1"
|
|
local os_name=$(grep '^ID=' /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"')
|
|
|
|
if [ ! -f "$file" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if grep -Ei "(\\\\v|\\\\r|\\\\m|\\\\s|${os_name})" "$file" 2>/dev/null | grep -q .; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# 1.6.1 MOTD content
|
|
if check_banner_content "/etc/motd"; then
|
|
output_result "1.6.1" "Kiểm soát nội dung motd (Message Of The Day)" "PASSED"
|
|
else
|
|
output_result "1.6.1" "Kiểm soát nội dung motd (Message Of The Day)" "FAILED"
|
|
fi
|
|
|
|
# 1.6.2 /etc/issue content
|
|
if check_banner_content "/etc/issue"; then
|
|
output_result "1.6.2" "Kiểm soát nội dung thông báo khi đăng nhập" "PASSED"
|
|
else
|
|
output_result "1.6.2" "Kiểm soát nội dung thông báo khi đăng nhập" "FAILED"
|
|
fi
|
|
|
|
# 1.6.3 /etc/issue.net content
|
|
if check_banner_content "/etc/issue.net"; then
|
|
output_result "1.6.3" "Kiểm soát nội dung thông báo khi đăng nhập từ xa" "PASSED"
|
|
else
|
|
output_result "1.6.3" "Kiểm soát nội dung thông báo khi đăng nhập từ xa" "FAILED"
|
|
fi
|
|
|
|
# 1.6.4-1.6.6 File permissions
|
|
BANNER_FILES=("/etc/motd" "/etc/issue" "/etc/issue.net")
|
|
BANNER_CHECKS=("1.6.4" "1.6.5" "1.6.6")
|
|
BANNER_NAMES=("Cấu hình phân quyền đối với file /etc/motd" "Cấu hình phân quyền đối với file /etc/issue" "Cấu hình phân quyền đối với file /etc/issue.net")
|
|
|
|
for i in "${!BANNER_FILES[@]}"; do
|
|
file="${BANNER_FILES[$i]}"
|
|
check_num="${BANNER_CHECKS[$i]}"
|
|
check_name="${BANNER_NAMES[$i]}"
|
|
|
|
if [ ! -f "$file" ] || stat -c "%a:%u:%g" "$file" 2>/dev/null | grep -qP "^[0-6][0-4][0-4]:0:0$"; then
|
|
output_result "$check_num" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_num" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
|
|
# 1.6.7 GNOME banner
|
|
gnome_ok=1
|
|
if ! systemctl get-default 2>/dev/null | grep -q "multi-user.target"; then
|
|
# GUI is default, check GNOME settings
|
|
if [ ! -f /etc/dconf/profile/gdm ] || \
|
|
! grep -qP "^\h*user-db:user" /etc/dconf/profile/gdm 2>/dev/null || \
|
|
! grep -qP "^\h*system-db:gdm" /etc/dconf/profile/gdm 2>/dev/null || \
|
|
! grep -qP "^\h*banner-message-enable=true" /etc/dconf/db/gdm.d/* 2>/dev/null; then
|
|
gnome_ok=0
|
|
fi
|
|
fi
|
|
|
|
if [ $gnome_ok -eq 1 ]; then
|
|
output_result "1.6.7" "Kiểm soát nội dung thông báo khi truy cập GNOME" "PASSED"
|
|
else
|
|
output_result "1.6.7" "Kiểm soát nội dung thông báo khi truy cập GNOME" "FAILED"
|
|
fi
|
|
|
|
|
|
############################################################################
|
|
# 2. SERVICES
|
|
############################################################################
|
|
echo ""
|
|
echo "=== 2. Service ==="
|
|
echo ""
|
|
|
|
############################################################################
|
|
# 2.1 Time Synchronization
|
|
############################################################################
|
|
echo "--- 2.1. Cấu hình Time Synchronization ---"
|
|
|
|
# Count active NTP services
|
|
ntp_count=0
|
|
chrony_active=0
|
|
ntp_active=0
|
|
|
|
if systemctl is-active chrony 2>/dev/null | grep -q "^active"; then
|
|
chrony_active=1
|
|
((ntp_count++))
|
|
fi
|
|
if systemctl is-active ntpd 2>/dev/null | grep -q "^active" || \
|
|
systemctl is-active ntp 2>/dev/null | grep -q "^active"; then
|
|
ntp_active=1
|
|
((ntp_count++))
|
|
fi
|
|
|
|
# 2.1.1 Chrony
|
|
if [ $ntp_count -gt 1 ]; then
|
|
output_result "2.1.1" "Cấu hình sử dụng chrony" "FAILED"
|
|
echo "# Cảnh báo: Nhiều dịch vụ NTP đang chạy đồng thời"
|
|
elif [ $chrony_active -eq 1 ]; then
|
|
if grep -Eq '^\s*(server|pool)\s+\S+' /etc/chrony.conf 2>/dev/null; then
|
|
output_result "2.1.1" "Cấu hình sử dụng chrony" "PASSED"
|
|
else
|
|
output_result "2.1.1" "Cấu hình sử dụng chrony" "FAILED"
|
|
fi
|
|
elif [ $ntp_active -eq 0 ]; then
|
|
output_result "2.1.1" "Cấu hình sử dụng chrony" "FAILED"
|
|
else
|
|
output_result "2.1.1" "Cấu hình sử dụng chrony" "PASSED"
|
|
fi
|
|
|
|
# 2.1.2 NTP
|
|
if [ $ntp_count -gt 1 ]; then
|
|
output_result "2.1.2" "Cấu hình sử dụng NTP" "FAILED"
|
|
elif [ $ntp_active -eq 1 ]; then
|
|
if grep -Eq '^\s*(server|pool)\s+\S+' /etc/ntp.conf 2>/dev/null; then
|
|
output_result "2.1.2" "Cấu hình sử dụng NTP" "PASSED"
|
|
else
|
|
output_result "2.1.2" "Cấu hình sử dụng NTP" "FAILED"
|
|
fi
|
|
elif [ $chrony_active -eq 0 ]; then
|
|
output_result "2.1.2" "Cấu hình sử dụng NTP" "FAILED"
|
|
else
|
|
output_result "2.1.2" "Cấu hình sử dụng NTP" "PASSED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 2.2 Special Purpose Services
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 2.2. Các Service với mục đích riêng biệt ---"
|
|
|
|
# Service checks array: service_name:package_name:check_id:check_name
|
|
declare -a SERVICES=(
|
|
"xinetd:xinetd:2.2.1:Cấu hình vô hiệu hoá xinetd services"
|
|
"autofs:autofs:2.2.9:Cấu hình vô hiệu hoá autofs services"
|
|
"avahi-daemon:avahi:2.2.11:Cấu hình vô hiệu hoá avahi daemon services"
|
|
"cups:cups:2.2.12:Cấu hình vô hiệu hoá cups services"
|
|
"dhcpd:dhcp-server:2.2.13:Cấu hình vô hiệu hoá dhcp server services"
|
|
"slapd:openldap-servers:2.2.14:Cấu hình vô hiệu hoá ldap server services"
|
|
"named:bind:2.2.15:Cấu hình vô hiệu hoá dns server services"
|
|
"dnsmasq:dnsmasq:2.2.16:Cấu hình vô hiệu hoá dnsmasq services"
|
|
"vsftpd:vsftpd:2.2.17:Cấu hình vô hiệu hoá ftp server services"
|
|
"tftp:tftp-server:2.2.18:Cấu hình vô hiệu hoá tftp server services"
|
|
"httpd:httpd:2.2.19:Cấu hình vô hiệu hoá web server services"
|
|
"dovecot:dovecot:2.2.20:Cấu hình vô hiệu hoá imap and pop3 server services"
|
|
"smb:samba:2.2.21:Cấu hình vô hiệu hoá samba file server services"
|
|
"squid:squid:2.2.22:Cấu hình vô hiệu hoá web proxy server services"
|
|
"snmpd:net-snmp:2.2.23:Cấu hình vô hiệu hoá snmp services"
|
|
"ypserv:ypserv:2.2.24:Cấu hình vô hiệu hoá nis server services"
|
|
"telnet.socket:telnet-server:2.2.25:Cấu hình vô hiệu hoá telnet server services"
|
|
"nfs-server:nfs-utils:2.2.27:Cấu hình vô hiệu hoá network file system services"
|
|
"rpcbind:rpcbind:2.2.28:Cấu hình vô hiệu hoá rpcbind services"
|
|
"rsyncd:rsync:2.2.29:Cấu hình vô hiệu hoá rsync services"
|
|
)
|
|
|
|
for service_info in "${SERVICES[@]}"; do
|
|
IFS=':' read -r service_name package_name check_id check_name <<< "$service_info"
|
|
|
|
service_ok=1
|
|
|
|
# Check if package is installed
|
|
if check_package_installed "$package_name"; then
|
|
# Check if service is enabled or active
|
|
if ! check_service_disabled "$service_name"; then
|
|
service_ok=0
|
|
fi
|
|
fi
|
|
|
|
if [ $service_ok -eq 1 ]; then
|
|
output_result "$check_id" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_id" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
|
|
# 2.2.2-2.2.8 xinetd services (CentOS 6 only)
|
|
if [ "$CENTOS_VERSION" -eq 6 ] 2>/dev/null; then
|
|
XINETD_SERVICES=(
|
|
"chargen-dgram:chargen-stream:2.2.2:Cấu hình vô hiệu hoá chargen services"
|
|
"daytime-dgram:daytime-stream:2.2.3:Cấu hình vô hiệu hoá daytime services"
|
|
"discard-dgram:discard-stream:2.2.4:Cấu hình vô hiệu hoá discard services"
|
|
"echo-dgram:echo-stream:2.2.5:Cấu hình vô hiệu hoá echo services"
|
|
"time-dgram:time-stream:2.2.6:Cấu hình vô hiệu hoá time services"
|
|
"rexec:rlogin:rsh:2.2.7:Cấu hình vô hiệu hoá rsh server"
|
|
"talk::2.2.8:Cấu hình vô hiệu hoá talk server"
|
|
)
|
|
|
|
for svc_info in "${XINETD_SERVICES[@]}"; do
|
|
IFS=':' read -r svc1 svc2 svc3 check_id check_name <<< "$svc_info"
|
|
|
|
xinetd_ok=1
|
|
for svc in $svc1 $svc2 $svc3; do
|
|
[ -z "$svc" ] && continue
|
|
if ! chkconfig --list 2>/dev/null | grep -q "${svc}:.*off"; then
|
|
xinetd_ok=0
|
|
fi
|
|
done
|
|
|
|
if [ $xinetd_ok -eq 1 ]; then
|
|
output_result "$check_id" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_id" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
else
|
|
# For CentOS 7+, these services don't exist
|
|
for check_id in "2.2.2" "2.2.3" "2.2.4" "2.2.5" "2.2.6" "2.2.7" "2.2.8"; do
|
|
case $check_id in
|
|
"2.2.2") output_result "$check_id" "Cấu hình vô hiệu hoá chargen services" "PASSED" ;;
|
|
"2.2.3") output_result "$check_id" "Cấu hình vô hiệu hoá daytime services" "PASSED" ;;
|
|
"2.2.4") output_result "$check_id" "Cấu hình vô hiệu hoá discard services" "PASSED" ;;
|
|
"2.2.5") output_result "$check_id" "Cấu hình vô hiệu hoá echo services" "PASSED" ;;
|
|
"2.2.6") output_result "$check_id" "Cấu hình vô hiệu hoá time services" "PASSED" ;;
|
|
"2.2.7") output_result "$check_id" "Cấu hình vô hiệu hoá rsh server" "PASSED" ;;
|
|
"2.2.8") output_result "$check_id" "Cấu hình vô hiệu hoá talk server" "PASSED" ;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
# 2.2.10 X Window
|
|
if rpm -qa xorg-x11-server* 2>/dev/null | grep -q . || \
|
|
systemctl get-default 2>/dev/null | grep -q "graphical.target"; then
|
|
output_result "2.2.10" "Cấu hình vô hiệu hoá X window server services" "FAILED"
|
|
else
|
|
output_result "2.2.10" "Cấu hình vô hiệu hoá X window server services" "PASSED"
|
|
fi
|
|
|
|
# 2.2.26 Mail Transfer Agent
|
|
if ss -lntu 2>/dev/null | grep -E ':25\s' | grep -qv '127.0.0.1\|::1'; then
|
|
output_result "2.2.26" "Cấu hình mail transfer agents sang chế độ local-only" "FAILED"
|
|
else
|
|
output_result "2.2.26" "Cấu hình mail transfer agents sang chế độ local-only" "PASSED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 2.3 Service Clients
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 2.3. Service Clients ---"
|
|
|
|
CLIENT_PACKAGES=(
|
|
"ypbind:2.3.1:Cấu hình vô hiệu hoá NIS Client"
|
|
"rsh:2.3.2:Cấu hình vô hiệu hoá rsh client"
|
|
"talk:2.3.3:Cấu hình vô hiệu hoá talk client"
|
|
"telnet:2.3.4:Cấu hình vô hiệu hoá telnet client"
|
|
"openldap-clients:2.3.5:Cấu hình vô hiệu hoá LDAP client"
|
|
"ftp:2.3.6:Cấu hình vô hiệu hoá ftp client"
|
|
"tftp:2.3.7:Cấu hình vô hiệu hoá tftp client"
|
|
)
|
|
|
|
for pkg_info in "${CLIENT_PACKAGES[@]}"; do
|
|
IFS=':' read -r package check_id check_name <<< "$pkg_info"
|
|
|
|
if check_package_not_installed "$package"; then
|
|
output_result "$check_id" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_id" "$check_name" "FAILED"
|
|
fi
|
|
done
|
|
|
|
|
|
############################################################################
|
|
# 3. NETWORK CONFIGURATION
|
|
############################################################################
|
|
echo ""
|
|
echo "=== 3. Cấu hình mạng ==="
|
|
echo ""
|
|
|
|
############################################################################
|
|
# 3.1 Network Parameters (Host Only)
|
|
############################################################################
|
|
echo "--- 3.1. Tham số cấu hình mạng (Host Only) ---"
|
|
|
|
# 3.1.1 IP Forwarding
|
|
ipv4_forward=$(sysctl -n net.ipv4.ip_forward 2>/dev/null)
|
|
ipv6_forward=$(sysctl -n net.ipv6.conf.all.forwarding 2>/dev/null)
|
|
|
|
if [ "$ipv4_forward" = "0" ] && ([ "${IPV6_ENABLED:-0}" -eq 0 ] || [ "$ipv6_forward" = "0" ]); then
|
|
output_result "3.1.1" "Cấu hình vô hiệu hoá IP forwarding" "PASSED"
|
|
else
|
|
output_result "3.1.1" "Cấu hình vô hiệu hoá IP forwarding" "FAILED"
|
|
fi
|
|
|
|
# 3.1.2 Packet Redirect
|
|
send_redirects_all=$(sysctl -n net.ipv4.conf.all.send_redirects 2>/dev/null)
|
|
send_redirects_default=$(sysctl -n net.ipv4.conf.default.send_redirects 2>/dev/null)
|
|
|
|
if [ "$send_redirects_all" = "0" ] && [ "$send_redirects_default" = "0" ]; then
|
|
output_result "3.1.2" "Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)" "PASSED"
|
|
else
|
|
output_result "3.1.2" "Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 3.2 Network Parameters (Host and Router)
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 3.2. Tham số cấu hình mạng (Host và Router) ---"
|
|
|
|
# 3.2.1 Source Routed Packets
|
|
src_route_all=$(sysctl -n net.ipv4.conf.all.accept_source_route 2>/dev/null)
|
|
src_route_default=$(sysctl -n net.ipv4.conf.default.accept_source_route 2>/dev/null)
|
|
|
|
if [ "$src_route_all" = "0" ] && [ "$src_route_default" = "0" ]; then
|
|
output_result "3.2.1" "Cấu hình từ chối các gói tin với nguồn được định tuyến trước" "PASSED"
|
|
else
|
|
output_result "3.2.1" "Cấu hình từ chối các gói tin với nguồn được định tuyến trước" "FAILED"
|
|
fi
|
|
|
|
# 3.2.2 ICMP Redirects
|
|
icmp_redirect_all=$(sysctl -n net.ipv4.conf.all.accept_redirects 2>/dev/null)
|
|
icmp_redirect_default=$(sysctl -n net.ipv4.conf.default.accept_redirects 2>/dev/null)
|
|
|
|
if [ "$icmp_redirect_all" = "0" ] && [ "$icmp_redirect_default" = "0" ]; then
|
|
output_result "3.2.2" "Cấu hình từ chối các ICMP redirect message" "PASSED"
|
|
else
|
|
output_result "3.2.2" "Cấu hình từ chối các ICMP redirect message" "FAILED"
|
|
fi
|
|
|
|
# 3.2.3 Secure ICMP Redirects
|
|
secure_redirect_all=$(sysctl -n net.ipv4.conf.all.secure_redirects 2>/dev/null)
|
|
secure_redirect_default=$(sysctl -n net.ipv4.conf.default.secure_redirects 2>/dev/null)
|
|
|
|
if [ "$secure_redirect_all" = "0" ] && [ "$secure_redirect_default" = "0" ]; then
|
|
output_result "3.2.3" "Cấu hình từ chối các secure ICMP redirect message" "PASSED"
|
|
else
|
|
output_result "3.2.3" "Cấu hình từ chối các secure ICMP redirect message" "FAILED"
|
|
fi
|
|
|
|
# 3.2.4 ICMP Broadcast
|
|
icmp_echo_ignore=$(sysctl -n net.ipv4.icmp_echo_ignore_broadcasts 2>/dev/null)
|
|
|
|
if [ "$icmp_echo_ignore" = "1" ]; then
|
|
output_result "3.2.4" "Cấu hình từ chối các gói tin ICMP request broadcast" "PASSED"
|
|
else
|
|
output_result "3.2.4" "Cấu hình từ chối các gói tin ICMP request broadcast" "FAILED"
|
|
fi
|
|
|
|
# 3.2.5 Bogus ICMP Responses
|
|
icmp_ignore_bogus=$(sysctl -n net.ipv4.icmp_ignore_bogus_error_responses 2>/dev/null)
|
|
|
|
if [ "$icmp_ignore_bogus" = "1" ]; then
|
|
output_result "3.2.5" "Cấu hình bỏ qua phản hồi ICMP không hợp lệ" "PASSED"
|
|
else
|
|
output_result "3.2.5" "Cấu hình bỏ qua phản hồi ICMP không hợp lệ" "FAILED"
|
|
fi
|
|
|
|
# 3.2.6 Reverse Path Filtering
|
|
rp_filter_all=$(sysctl -n net.ipv4.conf.all.rp_filter 2>/dev/null)
|
|
rp_filter_default=$(sysctl -n net.ipv4.conf.default.rp_filter 2>/dev/null)
|
|
|
|
if [ "$rp_filter_all" = "1" ] && [ "$rp_filter_default" = "1" ]; then
|
|
output_result "3.2.6" "Cấu hình Reverse Path Filtering" "PASSED"
|
|
else
|
|
output_result "3.2.6" "Cấu hình Reverse Path Filtering" "FAILED"
|
|
fi
|
|
|
|
# 3.2.7 TCP SYN Cookies
|
|
tcp_syncookies=$(sysctl -n net.ipv4.tcp_syncookies 2>/dev/null)
|
|
|
|
if [ "$tcp_syncookies" = "1" ]; then
|
|
output_result "3.2.7" "Cấu hình TCP SYN Cookies" "PASSED"
|
|
else
|
|
output_result "3.2.7" "Cấu hình TCP SYN Cookies" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 3.4 Firewall Configuration
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 3.4. Cấu hình Firewall ---"
|
|
|
|
# Detect which firewall is in use
|
|
FIREWALLD_ACTIVE=0
|
|
IPTABLES_ACTIVE=0
|
|
FIREWALLD_PASS=0
|
|
IPTABLES_PASS=0
|
|
|
|
if systemctl is-active firewalld 2>/dev/null | grep -q "^active"; then
|
|
FIREWALLD_ACTIVE=1
|
|
fi
|
|
if systemctl is-active iptables 2>/dev/null | grep -q "^active"; then
|
|
IPTABLES_ACTIVE=1
|
|
fi
|
|
|
|
# 3.4.1 Firewalld
|
|
echo "--- 3.4.1. Cấu hình firewalld ---"
|
|
|
|
# 3.4.1.1 Firewalld enabled
|
|
# Nếu 1 trong 2 loại firewall đang active → cả 2 mục "kích hoạt" đều PASSED
|
|
if [ $FIREWALLD_ACTIVE -eq 1 ] || [ $IPTABLES_ACTIVE -eq 1 ]; then
|
|
output_result "3.4.1.1" "Cấu hình kích hoạt firewalld" "PASSED"
|
|
if [ $FIREWALLD_ACTIVE -eq 1 ]; then
|
|
FIREWALLD_PASS=1
|
|
fi
|
|
if [ $IPTABLES_ACTIVE -eq 1 ]; then
|
|
IPTABLES_PASS=1
|
|
fi
|
|
else
|
|
output_result "3.4.1.1" "Cấu hình kích hoạt firewalld" "FAILED"
|
|
log_failed "# Kích hoạt firewalld:
|
|
# systemctl --now enable firewalld
|
|
# Hoặc kích hoạt iptables:
|
|
# systemctl --now enable iptables"
|
|
fi
|
|
|
|
# 3.4.1.3 Firewalld rules for open ports
|
|
if [ $IPTABLES_PASS -eq 1 ] && [ $FIREWALLD_PASS -eq 0 ]; then
|
|
# Iptables đang xử lý firewall → firewalld checks tự động PASSED
|
|
output_result "3.4.1.3" "Cấu hình firewalld rule cho tất cả các port và protocol đang mở" "PASSED"
|
|
elif [ $FIREWALLD_ACTIVE -eq 1 ]; then
|
|
# Check if there are rules configured
|
|
if firewall-cmd --list-all 2>/dev/null | grep -qE "(services:|ports:)"; then
|
|
output_result "3.4.1.3" "Cấu hình firewalld rule cho tất cả các port và protocol đang mở" "PASSED"
|
|
else
|
|
output_result "3.4.1.3" "Cấu hình firewalld rule cho tất cả các port và protocol đang mở" "FAILED"
|
|
fi
|
|
else
|
|
output_result "3.4.1.3" "Cấu hình firewalld rule cho tất cả các port và protocol đang mở" "FAILED"
|
|
fi
|
|
|
|
# 3.4.1.4 Default deny policy
|
|
if [ $IPTABLES_PASS -eq 1 ] && [ $FIREWALLD_PASS -eq 0 ]; then
|
|
# Iptables đang xử lý firewall → firewalld checks tự động PASSED
|
|
output_result "3.4.1.4" "Cấu hình chính sách từ chối mặc định cho firewalld" "PASSED"
|
|
elif [ $FIREWALLD_ACTIVE -eq 1 ]; then
|
|
# Kiểm tra default policy (target) của tất cả các zone đang active
|
|
# Dùng --list-all-zones để parse active zone + target trong một lần gọi
|
|
# Với mỗi zone active, target phải là DROP hoặc REJECT
|
|
fw_default_deny=$(firewall-cmd --list-all-zones 2>/dev/null | awk '
|
|
/\(active\)/ { in_active=1; next }
|
|
/^[^ \t]/ && !/\(active\)/ { in_active=0 }
|
|
in_active && /target:/ {
|
|
t = $2
|
|
gsub(/%%/, "", t)
|
|
if (t != "DROP" && t != "REJECT") bad=1
|
|
}
|
|
END { print (bad ? "0" : "1") }
|
|
')
|
|
|
|
# --- DEBUG INFO ---
|
|
echo "DEBUG [3.4.1.4]: FIREWALLD_ACTIVE=[$FIREWALLD_ACTIVE]" >&2
|
|
echo "DEBUG [3.4.1.4]: fw_default_deny=[$fw_default_deny]" >&2
|
|
# ------------------
|
|
|
|
if [ "$fw_default_deny" = "1" ]; then
|
|
output_result "3.4.1.4" "Cấu hình chính sách từ chối mặc định cho firewalld" "PASSED"
|
|
else
|
|
output_result "3.4.1.4" "Cấu hình chính sách từ chối mặc định cho firewalld" "FAILED"
|
|
fi
|
|
else
|
|
output_result "3.4.1.4" "Cấu hình chính sách từ chối mặc định cho firewalld" "FAILED"
|
|
fi
|
|
|
|
# 3.4.2 Iptables
|
|
echo ""
|
|
echo "--- 3.4.2. Iptables ---"
|
|
|
|
# 3.4.2.1 Iptables enabled
|
|
if [ $IPTABLES_ACTIVE -eq 1 ] || [ $FIREWALLD_ACTIVE -eq 1 ]; then
|
|
output_result "3.4.2.1" "Cấu hình kích hoạt Iptables" "PASSED"
|
|
else
|
|
output_result "3.4.2.1" "Cấu hình kích hoạt Iptables" "FAILED"
|
|
fi
|
|
|
|
# 3.4.2.3 Loopback traffic
|
|
if [ $FIREWALLD_PASS -eq 1 ] && [ $IPTABLES_PASS -eq 0 ]; then
|
|
# Firewalld đang xử lý firewall → iptables checks tự động PASSED
|
|
output_result "3.4.2.3" "Cấu hình iptables loopback traffic" "PASSED"
|
|
else
|
|
lo_input=$(iptables -L INPUT -v -n 2>/dev/null | grep -E "ACCEPT.*lo.*0.0.0.0/0.*0.0.0.0/0")
|
|
lo_output=$(iptables -L OUTPUT -v -n 2>/dev/null | grep -E "ACCEPT.*lo.*0.0.0.0/0.*0.0.0.0/0")
|
|
lo_drop=$(iptables -L INPUT -v -n 2>/dev/null | grep -E "DROP.*127.0.0.0/8")
|
|
|
|
if [ -n "$lo_input" ] && [ -n "$lo_output" ] && [ -n "$lo_drop" ]; then
|
|
output_result "3.4.2.3" "Cấu hình iptables loopback traffic" "PASSED"
|
|
else
|
|
output_result "3.4.2.3" "Cấu hình iptables loopback traffic" "FAILED"
|
|
fi
|
|
fi
|
|
|
|
# 3.4.2.4 Iptables rules for open ports
|
|
if [ $FIREWALLD_PASS -eq 1 ] && [ $IPTABLES_PASS -eq 0 ]; then
|
|
# Firewalld đang xử lý firewall → iptables checks tự động PASSED
|
|
output_result "3.4.2.4" "Cấu hình iptables rule cho tất cả các port và protocol đang mở" "PASSED"
|
|
else
|
|
if iptables -L -n 2>/dev/null | grep -qE "(ACCEPT|DROP|REJECT)"; then
|
|
output_result "3.4.2.4" "Cấu hình iptables rule cho tất cả các port và protocol đang mở" "PASSED"
|
|
else
|
|
output_result "3.4.2.4" "Cấu hình iptables rule cho tất cả các port và protocol đang mở" "FAILED"
|
|
fi
|
|
fi
|
|
|
|
# 3.4.2.5 Default deny policy
|
|
if [ $FIREWALLD_PASS -eq 1 ] && [ $IPTABLES_PASS -eq 0 ]; then
|
|
# Firewalld đang xử lý firewall → iptables checks tự động PASSED
|
|
output_result "3.4.2.5" "Cấu hình chính sách từ chối mặc định cho iptables" "PASSED"
|
|
else
|
|
input_policy=$(iptables -L INPUT 2>/dev/null | head -1 | grep -oE "(ACCEPT|DROP|REJECT)")
|
|
forward_policy=$(iptables -L FORWARD 2>/dev/null | head -1 | grep -oE "(ACCEPT|DROP|REJECT)")
|
|
output_policy=$(iptables -L OUTPUT 2>/dev/null | head -1 | grep -oE "(ACCEPT|DROP|REJECT)")
|
|
|
|
if [ "$input_policy" = "DROP" ] || [ "$input_policy" = "REJECT" ]; then
|
|
if [ "$forward_policy" = "DROP" ] || [ "$forward_policy" = "REJECT" ]; then
|
|
output_result "3.4.2.5" "Cấu hình chính sách từ chối mặc định cho iptables" "PASSED"
|
|
else
|
|
output_result "3.4.2.5" "Cấu hình chính sách từ chối mặc định cho iptables" "FAILED"
|
|
fi
|
|
else
|
|
output_result "3.4.2.5" "Cấu hình chính sách từ chối mặc định cho iptables" "FAILED"
|
|
fi
|
|
fi
|
|
|
|
|
|
############################################################################
|
|
# 4. LOGGING AND AUDITING
|
|
############################################################################
|
|
echo ""
|
|
echo "=== 4. Logging và Auditing ==="
|
|
echo ""
|
|
|
|
############################################################################
|
|
# 4.1 Configure Logging
|
|
############################################################################
|
|
echo "--- 4.1. Cấu hình logging ---"
|
|
echo "--- 4.1.1. Cấu hình rsyslog ---"
|
|
|
|
# 4.1.1.1 rsyslog enabled
|
|
if systemctl is-enabled rsyslog 2>/dev/null | grep -q "enabled"; then
|
|
output_result "4.1.1.1" "Cấu hình kích hoạt rsyslog service" "PASSED"
|
|
else
|
|
output_result "4.1.1.1" "Cấu hình kích hoạt rsyslog service" "FAILED"
|
|
fi
|
|
|
|
# 4.1.1.2 rsyslog file permissions
|
|
rsyslog_perms_ok=1
|
|
if grep -q '^\$FileCreateMode' /etc/rsyslog.conf 2>/dev/null; then
|
|
mode=$(grep '^\$FileCreateMode' /etc/rsyslog.conf | awk '{print $2}')
|
|
if [ "$mode" != "0640" ] && [ "$mode" != "0600" ]; then
|
|
rsyslog_perms_ok=0
|
|
fi
|
|
else
|
|
rsyslog_perms_ok=0
|
|
fi
|
|
|
|
if [ $rsyslog_perms_ok -eq 1 ]; then
|
|
output_result "4.1.1.2" "Phân quyền đối với file log sinh ra từ rsyslog" "PASSED"
|
|
else
|
|
output_result "4.1.1.2" "Phân quyền đối với file log sinh ra từ rsyslog" "FAILED"
|
|
fi
|
|
|
|
# 4.1.1.3 Remote logging
|
|
if grep -qE '^\s*\*\.\*\s+@' /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2>/dev/null; then
|
|
output_result "4.1.1.3" "Cấu hình lưu trữ log sinh ra từ rsyslog tập trung" "PASSED"
|
|
else
|
|
output_result "4.1.1.3" "Cấu hình lưu trữ log sinh ra từ rsyslog tập trung" "FAILED"
|
|
fi
|
|
|
|
# 4.1.1.4 Log file permissions
|
|
log_perms_ok=1
|
|
for logfile in /var/log/messages /var/log/secure /var/log/maillog /var/log/cron /var/log/boot.log; do
|
|
if [ -f "$logfile" ]; then
|
|
perms=$(stat -c "%a" "$logfile" 2>/dev/null)
|
|
if [ "$perms" != "600" ] && [ "$perms" != "640" ]; then
|
|
log_perms_ok=0
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ $log_perms_ok -eq 1 ]; then
|
|
output_result "4.1.1.4" "Phân quyền đối với tất cả các file log" "PASSED"
|
|
else
|
|
output_result "4.1.1.4" "Phân quyền đối với tất cả các file log" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 5. ACCESS, AUTHENTICATION AND AUTHORIZATION
|
|
############################################################################
|
|
echo ""
|
|
echo "=== 5. Cấu hình truy cập, xác thực và ủy quyền ==="
|
|
echo ""
|
|
|
|
############################################################################
|
|
# 5.1 Configure cron
|
|
############################################################################
|
|
echo "--- 5.1. Cấu hình cron ---"
|
|
|
|
# 5.1.1 Cron daemon enabled
|
|
if systemctl is-enabled crond 2>/dev/null | grep -q "enabled"; then
|
|
output_result "5.1.1" "Cấu hình kích hoạt cron daemon" "PASSED"
|
|
else
|
|
output_result "5.1.1" "Cấu hình kích hoạt cron daemon" "FAILED"
|
|
fi
|
|
|
|
# 5.1.2-5.1.7 Cron file permissions
|
|
CRON_FILES=(
|
|
"/etc/crontab:5.1.2:Cấu hình phân quyền cho file /etc/crontab"
|
|
"/etc/cron.hourly:5.1.3:Cấu hình phân quyền cho file /etc/cron.hourly"
|
|
"/etc/cron.daily:5.1.4:Cấu hình phân quyền cho file /etc/cron.daily"
|
|
"/etc/cron.weekly:5.1.5:Cấu hình phân quyền cho file /etc/cron.weekly"
|
|
"/etc/cron.monthly:5.1.6:Cấu hình phân quyền cho của file /etc/cron.monthly"
|
|
"/etc/cron.d:5.1.7:Cấu hình phân quyền cho file /etc/cron.d"
|
|
)
|
|
|
|
for cron_info in "${CRON_FILES[@]}"; do
|
|
IFS=':' read -r file check_id check_name <<< "$cron_info"
|
|
|
|
if [ -e "$file" ]; then
|
|
perms=$(stat -c "%a:%u:%g" "$file" 2>/dev/null)
|
|
if echo "$perms" | grep -qP "^[0-7]00:0:0$"; then
|
|
output_result "$check_id" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_id" "$check_name" "FAILED"
|
|
fi
|
|
else
|
|
output_result "$check_id" "$check_name" "PASSED"
|
|
fi
|
|
done
|
|
|
|
# 5.1.8 at/cron restricted
|
|
if [ -f /etc/cron.allow ] && [ ! -f /etc/cron.deny ]; then
|
|
if [ -f /etc/at.allow ] && [ ! -f /etc/at.deny ]; then
|
|
output_result "5.1.8" "Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền" "PASSED"
|
|
else
|
|
output_result "5.1.8" "Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền" "FAILED"
|
|
fi
|
|
else
|
|
output_result "5.1.8" "Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 5.2 SSH Server Configuration
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 5.2. Cấu hình máy chủ SSH ---"
|
|
|
|
SSHD_CONFIG="/etc/ssh/sshd_config"
|
|
|
|
# Helper function to get SSH config value
|
|
get_ssh_config() {
|
|
local param="$1"
|
|
grep -iE "^\s*${param}\s+" "$SSHD_CONFIG" /etc/ssh/sshd_config.d/*.conf 2>/dev/null | tail -1 | awk '{print $2}'
|
|
}
|
|
|
|
# 5.2.1 sshd_config permissions
|
|
if stat -c "%a:%u:%g" "$SSHD_CONFIG" 2>/dev/null | grep -qP "^[0-6]00:0:0$"; then
|
|
output_result "5.2.1" "Cấu hình phân quyền cho file /etc/ssh/sshd_config" "PASSED"
|
|
else
|
|
output_result "5.2.1" "Cấu hình phân quyền cho file /etc/ssh/sshd_config" "FAILED"
|
|
fi
|
|
|
|
# 5.2.2 SSH private key permissions
|
|
ssh_priv_ok=1
|
|
for keyfile in /etc/ssh/ssh_host_*_key; do
|
|
if [ -f "$keyfile" ]; then
|
|
perms=$(stat -c "%a:%u:%g" "$keyfile" 2>/dev/null)
|
|
if ! echo "$perms" | grep -qP "^[0-6]00:0:0$"; then
|
|
ssh_priv_ok=0
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ $ssh_priv_ok -eq 1 ]; then
|
|
output_result "5.2.2" "Cấu hình phân quyền cho các file SSH private host key" "PASSED"
|
|
else
|
|
output_result "5.2.2" "Cấu hình phân quyền cho các file SSH private host key" "FAILED"
|
|
fi
|
|
|
|
# 5.2.3 SSH public key permissions
|
|
ssh_pub_ok=1
|
|
for keyfile in /etc/ssh/ssh_host_*_key.pub; do
|
|
if [ -f "$keyfile" ]; then
|
|
perms=$(stat -c "%a:%u:%g" "$keyfile" 2>/dev/null)
|
|
if ! echo "$perms" | grep -qP "^[0-6][0-4][0-4]:0:0$"; then
|
|
ssh_pub_ok=0
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ $ssh_pub_ok -eq 1 ]; then
|
|
output_result "5.2.3" "Cấu hình phân quyền cho các file SSH public host key" "PASSED"
|
|
else
|
|
output_result "5.2.3" "Cấu hình phân quyền cho các file SSH public host key" "FAILED"
|
|
fi
|
|
|
|
# 5.2.4 SSH access restriction
|
|
if grep -qiE "^\s*(AllowUsers|AllowGroups|DenyUsers|DenyGroups)\s+" "$SSHD_CONFIG" /etc/ssh/sshd_config.d/*.conf 2>/dev/null; then
|
|
output_result "5.2.4" "Cấu hình giới hạn truy cập cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.4" "Cấu hình giới hạn truy cập cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.5 SSH LogLevel
|
|
loglevel=$(get_ssh_config "LogLevel")
|
|
if [ "$loglevel" = "INFO" ] || [ "$loglevel" = "VERBOSE" ]; then
|
|
output_result "5.2.5" "Cấu hình LogLevel cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.5" "Cấu hình LogLevel cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.6 SSH PAM
|
|
usepam=$(get_ssh_config "UsePAM")
|
|
if [ "$usepam" = "yes" ]; then
|
|
output_result "5.2.6" "Cấu hình sử dụng SSH PAM" "PASSED"
|
|
else
|
|
output_result "5.2.6" "Cấu hình sử dụng SSH PAM" "FAILED"
|
|
fi
|
|
|
|
# 5.2.7 SSH root login
|
|
permitroot=$(get_ssh_config "PermitRootLogin")
|
|
if [ "$permitroot" = "no" ]; then
|
|
output_result "5.2.7" "Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.7" "Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.8 SSH HostbasedAuthentication
|
|
hostbased=$(get_ssh_config "HostbasedAuthentication")
|
|
if [ "$hostbased" = "no" ]; then
|
|
output_result "5.2.8" "Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.8" "Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.9 SSH PermitEmptyPasswords
|
|
emptypass=$(get_ssh_config "PermitEmptyPasswords")
|
|
if [ "$emptypass" = "no" ]; then
|
|
output_result "5.2.9" "Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.9" "Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.10 SSH PermitUserEnvironment
|
|
userenv=$(get_ssh_config "PermitUserEnvironment")
|
|
if [ "$userenv" = "no" ]; then
|
|
output_result "5.2.10" "Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.10" "Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.11 SSH IgnoreRhosts
|
|
ignorerhosts=$(get_ssh_config "IgnoreRhosts")
|
|
if [ "$ignorerhosts" = "yes" ]; then
|
|
output_result "5.2.11" "Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.11" "Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.12 SSH X11Forwarding
|
|
x11forward=$(get_ssh_config "X11Forwarding")
|
|
if [ "$x11forward" = "no" ]; then
|
|
output_result "5.2.12" "Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.12" "Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.13 SSH AllowTcpForwarding
|
|
tcpforward=$(get_ssh_config "AllowTcpForwarding")
|
|
if [ "$tcpforward" = "no" ]; then
|
|
output_result "5.2.13" "Cấu hình vô hiệu hoá SSH AllowTcpForwarding" "PASSED"
|
|
else
|
|
output_result "5.2.13" "Cấu hình vô hiệu hoá SSH AllowTcpForwarding" "FAILED"
|
|
fi
|
|
|
|
# 5.2.14 SSH Banner
|
|
banner=$(get_ssh_config "Banner")
|
|
if [ -n "$banner" ] && [ "$banner" != "none" ]; then
|
|
output_result "5.2.14" "Cấu hình cảnh báo SSH" "PASSED"
|
|
else
|
|
output_result "5.2.14" "Cấu hình cảnh báo SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.15 SSH MaxAuthTries
|
|
maxauth=$(get_ssh_config "MaxAuthTries")
|
|
if [ -n "$maxauth" ] && [ "$maxauth" -le 4 ] 2>/dev/null; then
|
|
output_result "5.2.15" "Cấu hình SSH MaxAuthTries" "PASSED"
|
|
else
|
|
output_result "5.2.15" "Cấu hình SSH MaxAuthTries" "FAILED"
|
|
fi
|
|
|
|
# 5.2.16 SSH MaxStartups
|
|
maxstartups=$(get_ssh_config "MaxStartups")
|
|
if [ -n "$maxstartups" ]; then
|
|
output_result "5.2.16" "Cấu hình SSH MaxStartups" "PASSED"
|
|
else
|
|
output_result "5.2.16" "Cấu hình SSH MaxStartups" "FAILED"
|
|
fi
|
|
|
|
# 5.2.17 SSH MaxSessions
|
|
maxsessions=$(get_ssh_config "MaxSessions")
|
|
if [ -n "$maxsessions" ] && [ "$maxsessions" -le 10 ] 2>/dev/null; then
|
|
output_result "5.2.17" "Cấu hình SSH MaxSessions" "PASSED"
|
|
else
|
|
output_result "5.2.17" "Cấu hình SSH MaxSessions" "FAILED"
|
|
fi
|
|
|
|
# 5.2.18 SSH LoginGraceTime
|
|
logingrace=$(get_ssh_config "LoginGraceTime")
|
|
if [ -n "$logingrace" ] && [ "$logingrace" -le 60 ] 2>/dev/null; then
|
|
output_result "5.2.18" "Cấu hình SSH LoginGraceTime" "PASSED"
|
|
else
|
|
output_result "5.2.18" "Cấu hình SSH LoginGraceTime" "FAILED"
|
|
fi
|
|
|
|
# 5.2.19 SSH Idle Timeout
|
|
clientalive=$(get_ssh_config "ClientAliveInterval")
|
|
clientcount=$(get_ssh_config "ClientAliveCountMax")
|
|
if [ -n "$clientalive" ] && [ "$clientalive" -gt 0 ] 2>/dev/null; then
|
|
output_result "5.2.19" "Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH" "PASSED"
|
|
else
|
|
output_result "5.2.19" "Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH" "FAILED"
|
|
fi
|
|
|
|
# 5.2.20 SSH MAC algorithms
|
|
macs=$(get_ssh_config "MACs")
|
|
if [ -n "$macs" ]; then
|
|
output_result "5.2.20" "Cấu hình các thuật toán MAC được cho phép" "PASSED"
|
|
else
|
|
output_result "5.2.20" "Cấu hình các thuật toán MAC được cho phép" "FAILED"
|
|
fi
|
|
|
|
|
|
############################################################################
|
|
# 5.3 Configure PAM
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 5.3. Cấu hình PAM ---"
|
|
|
|
# 5.3.1 Password creation requirements
|
|
if grep -qE "^\s*password\s+requisite\s+pam_pwquality.so" /etc/pam.d/system-auth /etc/pam.d/password-auth 2>/dev/null; then
|
|
if grep -qE "^\s*minlen\s*=\s*([8-9]|[1-9][0-9]+)" /etc/security/pwquality.conf 2>/dev/null; then
|
|
output_result "5.3.1" "Cấu hình điều kiện tạo mật khẩu" "PASSED"
|
|
else
|
|
output_result "5.3.1" "Cấu hình điều kiện tạo mật khẩu" "FAILED"
|
|
fi
|
|
else
|
|
output_result "5.3.1" "Cấu hình điều kiện tạo mật khẩu" "FAILED"
|
|
fi
|
|
|
|
# 5.3.2 Lockout for failed password attempts
|
|
if grep -qE "^\s*auth\s+required\s+pam_faillock.so" /etc/pam.d/system-auth /etc/pam.d/password-auth 2>/dev/null || \
|
|
grep -qE "^\s*auth\s+required\s+pam_tally2.so" /etc/pam.d/system-auth /etc/pam.d/password-auth 2>/dev/null; then
|
|
output_result "5.3.2" "Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại" "PASSED"
|
|
else
|
|
output_result "5.3.2" "Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại" "FAILED"
|
|
fi
|
|
|
|
# 5.3.3 Password reuse limitation
|
|
if grep -qE "^\s*password\s+(sufficient|required)\s+pam_unix.so.*remember=([5-9]|[1-9][0-9]+)" /etc/pam.d/system-auth 2>/dev/null; then
|
|
output_result "5.3.3" "Giới hạn việc sử dụng lại mật khẩu" "PASSED"
|
|
else
|
|
output_result "5.3.3" "Giới hạn việc sử dụng lại mật khẩu" "FAILED"
|
|
fi
|
|
|
|
# 5.3.4 Password hashing algorithm
|
|
if grep -qE "^\s*password\s+sufficient\s+pam_unix.so.*sha512" /etc/pam.d/system-auth 2>/dev/null; then
|
|
output_result "5.3.4" "Cấu hình thuật toán hash mật khẩu sang SHA-512" "PASSED"
|
|
else
|
|
output_result "5.3.4" "Cấu hình thuật toán hash mật khẩu sang SHA-512" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 5.4 User Accounts and Environment
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 5.4. Cấu hình tài khoản người dùng và môi trường ---"
|
|
echo "--- 5.4.1. Cấu hình mật khẩu người dùng ---"
|
|
|
|
# 5.4.1.1 Password expiration
|
|
pass_max_days=$(grep "^PASS_MAX_DAYS" /etc/login.defs 2>/dev/null | awk '{print $2}')
|
|
if [ -n "$pass_max_days" ] && [ "$pass_max_days" -le 365 ] 2>/dev/null; then
|
|
output_result "5.4.1.1" "Cấu hình thời gian hết hạn sử dụng mật khẩu" "PASSED"
|
|
else
|
|
output_result "5.4.1.1" "Cấu hình thời gian hết hạn sử dụng mật khẩu" "FAILED"
|
|
fi
|
|
|
|
# 5.4.1.2 Minimum days between password changes
|
|
pass_min_days=$(grep "^PASS_MIN_DAYS" /etc/login.defs 2>/dev/null | awk '{print $2}')
|
|
if [ -n "$pass_min_days" ] && [ "$pass_min_days" -ge 1 ] 2>/dev/null; then
|
|
output_result "5.4.1.2" "Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu" "PASSED"
|
|
else
|
|
output_result "5.4.1.2" "Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu" "FAILED"
|
|
fi
|
|
|
|
# 5.4.1.3 Password expiration warning
|
|
pass_warn_age=$(grep "^PASS_WARN_AGE" /etc/login.defs 2>/dev/null | awk '{print $2}')
|
|
if [ -n "$pass_warn_age" ] && [ "$pass_warn_age" -ge 7 ] 2>/dev/null; then
|
|
output_result "5.4.1.3" "Cấu hình thời gian cảnh báo mật khẩu hết hạn" "PASSED"
|
|
else
|
|
output_result "5.4.1.3" "Cấu hình thời gian cảnh báo mật khẩu hết hạn" "FAILED"
|
|
fi
|
|
|
|
# 5.4.1.4 Inactive password lock
|
|
inactive=$(useradd -D 2>/dev/null | grep INACTIVE | cut -d= -f2)
|
|
if [ -n "$inactive" ] && [ "$inactive" -le 30 ] && [ "$inactive" -ge 0 ] 2>/dev/null; then
|
|
output_result "5.4.1.4" "Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn" "PASSED"
|
|
else
|
|
output_result "5.4.1.4" "Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn" "FAILED"
|
|
fi
|
|
|
|
# 5.4.1.5 Last password change date
|
|
today=$(date +%s)
|
|
today_days=$((today / 86400))
|
|
pass_change_ok=1
|
|
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ "$pass" != "*" ] && [ "$pass" != "!" ] && [ "$pass" != "!!" ]; then
|
|
lastchange=$(chage -l "$user" 2>/dev/null | grep "Last password change" | cut -d: -f2 | xargs)
|
|
if [ "$lastchange" != "never" ] && [ -n "$lastchange" ]; then
|
|
change_date=$(date -d "$lastchange" +%s 2>/dev/null)
|
|
if [ -n "$change_date" ] && [ "$change_date" -gt "$today" ]; then
|
|
pass_change_ok=0
|
|
fi
|
|
fi
|
|
fi
|
|
done < /etc/shadow 2>/dev/null
|
|
|
|
if [ $pass_change_ok -eq 1 ]; then
|
|
output_result "5.4.1.5" "Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ" "PASSED"
|
|
else
|
|
output_result "5.4.1.5" "Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ" "FAILED"
|
|
fi
|
|
|
|
# 5.4.2 System accounts non-login
|
|
sys_accounts_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ "$uid" -lt 1000 ] && [ "$user" != "root" ]; then
|
|
if [ "$shell" != "/sbin/nologin" ] && [ "$shell" != "/usr/sbin/nologin" ] && [ "$shell" != "/bin/false" ]; then
|
|
sys_accounts_ok=0
|
|
fi
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $sys_accounts_ok -eq 1 ]; then
|
|
output_result "5.4.2" "Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống" "PASSED"
|
|
else
|
|
output_result "5.4.2" "Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống" "FAILED"
|
|
fi
|
|
|
|
# 5.4.3 Default shell timeout
|
|
if grep -qE "^\s*TMOUT=([1-9][0-9]{0,2}|[1-8][0-9]{3}|900)" /etc/bashrc /etc/profile /etc/profile.d/*.sh 2>/dev/null; then
|
|
output_result "5.4.3" "Cấu hình shell timeout mặc định" "PASSED"
|
|
else
|
|
output_result "5.4.3" "Cấu hình shell timeout mặc định" "FAILED"
|
|
fi
|
|
|
|
# 5.4.4 Root default group
|
|
root_gid=$(grep "^root:" /etc/passwd | cut -d: -f4)
|
|
if [ "$root_gid" = "0" ]; then
|
|
output_result "5.4.4" "Cấu hình group mặc định của tài khoản root" "PASSED"
|
|
else
|
|
output_result "5.4.4" "Cấu hình group mặc định của tài khoản root" "FAILED"
|
|
fi
|
|
|
|
# 5.4.5 Default umask
|
|
if grep -qE "^\s*umask\s+0[2-7][2-7]" /etc/bashrc /etc/profile /etc/profile.d/*.sh 2>/dev/null; then
|
|
output_result "5.4.5" "Cấu hình user umask mặc định" "PASSED"
|
|
else
|
|
output_result "5.4.5" "Cấu hình user umask mặc định" "FAILED"
|
|
fi
|
|
|
|
# 5.4.6 Restrict su command
|
|
if grep -qE "^\s*auth\s+required\s+pam_wheel.so\s+use_uid" /etc/pam.d/su 2>/dev/null; then
|
|
output_result "5.4.6" "Cấu hình hạn chế truy cập cho câu lệnh su" "PASSED"
|
|
else
|
|
output_result "5.4.6" "Cấu hình hạn chế truy cập cho câu lệnh su" "FAILED"
|
|
fi
|
|
|
|
|
|
############################################################################
|
|
# 6. SYSTEM MAINTENANCE
|
|
############################################################################
|
|
echo ""
|
|
echo "=== 6. System Maintenance ==="
|
|
echo ""
|
|
|
|
############################################################################
|
|
# 6.1 System File Permissions
|
|
############################################################################
|
|
echo "--- 6.1. Quyền của file hệ thống ---"
|
|
|
|
# 6.1.1 Sticky bit on world-writable directories
|
|
sticky_ok=1
|
|
world_writable_dirs=$(df --local -P 2>/dev/null | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -type d -perm -0002 ! -perm -1000 2>/dev/null | head -1)
|
|
|
|
if [ -z "$world_writable_dirs" ]; then
|
|
output_result "6.1.1" "Cấu hình sticky bit cho tất cả các thư mục dùng chung" "PASSED"
|
|
else
|
|
output_result "6.1.1" "Cấu hình sticky bit cho tất cả các thư mục dùng chung" "FAILED"
|
|
fi
|
|
|
|
# 6.1.2-6.1.9 System file permissions
|
|
SYS_FILES=(
|
|
"/etc/passwd:644:0:0:6.1.2:Cấu hình phân quyền cho file /etc/passwd"
|
|
"/etc/shadow:0:0:0:6.1.3:Cấu hình phân quyền cho file /etc/shadow"
|
|
"/etc/group:644:0:0:6.1.4:Cấu hình phân quyền cho file /etc/group"
|
|
"/etc/gshadow:0:0:0:6.1.5:Cấu hình phân quyền cho file /etc/gshadow"
|
|
"/etc/passwd-:644:0:0:6.1.6:Cấu hình phân quyền cho file /etc/passwd-"
|
|
"/etc/shadow-:0:0:0:6.1.7:Cấu hình phân quyền cho file /etc/shadow-"
|
|
"/etc/group-:644:0:0:6.1.8:Cấu hình phân quyền cho file /etc/group-"
|
|
"/etc/gshadow-:0:0:0:6.1.9:Cấu hình phân quyền cho file /etc/gshadow-"
|
|
)
|
|
|
|
for file_info in "${SYS_FILES[@]}"; do
|
|
IFS=':' read -r file expected_mode expected_uid expected_gid check_id check_name <<< "$file_info"
|
|
|
|
if [ -f "$file" ]; then
|
|
actual=$(stat -c "%a:%u:%g" "$file" 2>/dev/null)
|
|
actual_mode=$(echo "$actual" | cut -d: -f1)
|
|
actual_uid=$(echo "$actual" | cut -d: -f2)
|
|
actual_gid=$(echo "$actual" | cut -d: -f3)
|
|
|
|
if [ "$actual_mode" -le "$expected_mode" ] 2>/dev/null && \
|
|
[ "$actual_uid" = "$expected_uid" ] && \
|
|
[ "$actual_gid" = "$expected_gid" ]; then
|
|
output_result "$check_id" "$check_name" "PASSED"
|
|
else
|
|
output_result "$check_id" "$check_name" "FAILED"
|
|
fi
|
|
else
|
|
output_result "$check_id" "$check_name" "PASSED"
|
|
fi
|
|
done
|
|
|
|
# 6.1.10 No world-writable files
|
|
world_writable=$(df --local -P 2>/dev/null | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -type f -perm -0002 2>/dev/null | head -1)
|
|
if [ -z "$world_writable" ]; then
|
|
output_result "6.1.10" "Đảm bảo không có file world-writable tồn tại" "PASSED"
|
|
else
|
|
output_result "6.1.10" "Đảm bảo không có file world-writable tồn tại" "FAILED"
|
|
fi
|
|
|
|
# 6.1.11 No unowned files
|
|
unowned=$(df --local -P 2>/dev/null | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -nouser 2>/dev/null | head -1)
|
|
if [ -z "$unowned" ]; then
|
|
output_result "6.1.11" "Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại" "PASSED"
|
|
else
|
|
output_result "6.1.11" "Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại" "FAILED"
|
|
fi
|
|
|
|
# 6.1.12 No ungrouped files
|
|
ungrouped=$(df --local -P 2>/dev/null | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -nogroup 2>/dev/null | head -1)
|
|
if [ -z "$ungrouped" ]; then
|
|
output_result "6.1.12" "Đảm bảo các file hoặc thư mục không có nhóm không tồn tại" "PASSED"
|
|
else
|
|
output_result "6.1.12" "Đảm bảo các file hoặc thư mục không có nhóm không tồn tại" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# 6.2 User and Group Settings
|
|
############################################################################
|
|
echo ""
|
|
echo "--- 6.2. Thiết lập cho người dùng và nhóm ---"
|
|
|
|
# 6.2.1 No empty password fields
|
|
empty_pass=$(awk -F: '($2 == "") {print $1}' /etc/shadow 2>/dev/null)
|
|
if [ -z "$empty_pass" ]; then
|
|
output_result "6.2.1" "Đảm bảo trường mật khẩu không để trống" "PASSED"
|
|
else
|
|
output_result "6.2.1" "Đảm bảo trường mật khẩu không để trống" "FAILED"
|
|
fi
|
|
|
|
# 6.2.2 All groups in /etc/passwd exist in /etc/group
|
|
groups_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if ! grep -q "^[^:]*:[^:]*:${gid}:" /etc/group 2>/dev/null; then
|
|
groups_ok=0
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $groups_ok -eq 1 ]; then
|
|
output_result "6.2.2" "Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group" "PASSED"
|
|
else
|
|
output_result "6.2.2" "Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group" "FAILED"
|
|
fi
|
|
|
|
# 6.2.3 No duplicate UIDs
|
|
dup_uids=$(cut -d: -f3 /etc/passwd 2>/dev/null | sort | uniq -d)
|
|
if [ -z "$dup_uids" ]; then
|
|
output_result "6.2.3" "Đảm bảo UID không bị lặp" "PASSED"
|
|
else
|
|
output_result "6.2.3" "Đảm bảo UID không bị lặp" "FAILED"
|
|
fi
|
|
|
|
# 6.2.4 No duplicate GIDs
|
|
dup_gids=$(cut -d: -f3 /etc/group 2>/dev/null | sort | uniq -d)
|
|
if [ -z "$dup_gids" ]; then
|
|
output_result "6.2.4" "Đảm bảo GID không bị lặp" "PASSED"
|
|
else
|
|
output_result "6.2.4" "Đảm bảo GID không bị lặp" "FAILED"
|
|
fi
|
|
|
|
# 6.2.5 No duplicate user names
|
|
dup_users=$(cut -d: -f1 /etc/passwd 2>/dev/null | sort | uniq -d)
|
|
if [ -z "$dup_users" ]; then
|
|
output_result "6.2.5" "Đảm bảo tên người dùng không bị lặp" "PASSED"
|
|
else
|
|
output_result "6.2.5" "Đảm bảo tên người dùng không bị lặp" "FAILED"
|
|
fi
|
|
|
|
# 6.2.6 No duplicate group names
|
|
dup_groups=$(cut -d: -f1 /etc/group 2>/dev/null | sort | uniq -d)
|
|
if [ -z "$dup_groups" ]; then
|
|
output_result "6.2.6" "Đảm bảo tên group không bị lặp" "PASSED"
|
|
else
|
|
output_result "6.2.6" "Đảm bảo tên group không bị lặp" "FAILED"
|
|
fi
|
|
|
|
# 6.2.7 Root PATH integrity
|
|
path_ok=1
|
|
oldIFS="$IFS"
|
|
IFS=':'
|
|
for p in $PATH; do
|
|
IFS="$oldIFS"
|
|
if [ "$p" = "." ] || [ "$p" = "" ]; then
|
|
path_ok=0
|
|
fi
|
|
if [ -d "$p" ]; then
|
|
perms=$(stat -c "%a" "$p" 2>/dev/null)
|
|
# Check if permissions end with 2, 3, 6, or 7 (group/other writable)
|
|
case "$perms" in
|
|
*[2367]) path_ok=0 ;;
|
|
esac
|
|
fi
|
|
done
|
|
IFS="$oldIFS"
|
|
|
|
if [ $path_ok -eq 1 ]; then
|
|
output_result "6.2.7" "Đảm bảo tính toàn vẹn cho biến môi trường PATH của root" "PASSED"
|
|
else
|
|
output_result "6.2.7" "Đảm bảo tính toàn vẹn cho biến môi trường PATH của root" "FAILED"
|
|
fi
|
|
|
|
# 6.2.8 Root is the only UID 0 account
|
|
uid0_count=$(awk -F: '($3 == 0) {print $1}' /etc/passwd 2>/dev/null | wc -l)
|
|
if [ "$uid0_count" -eq 1 ]; then
|
|
output_result "6.2.8" "Đảm bảo root là tài khoản duy nhất có UID là 0" "PASSED"
|
|
else
|
|
output_result "6.2.8" "Đảm bảo root là tài khoản duy nhất có UID là 0" "FAILED"
|
|
fi
|
|
|
|
# 6.2.9 All users have home directories
|
|
home_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ "$uid" -ge 1000 ] && [ "$shell" != "/sbin/nologin" ] && [ "$shell" != "/bin/false" ]; then
|
|
if [ ! -d "$home" ]; then
|
|
home_ok=0
|
|
fi
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $home_ok -eq 1 ]; then
|
|
output_result "6.2.9" "Đảm bảo mọi người dùng đều tồn tại thư mục home" "PASSED"
|
|
else
|
|
output_result "6.2.9" "Đảm bảo mọi người dùng đều tồn tại thư mục home" "FAILED"
|
|
fi
|
|
|
|
# 6.2.10 Users own their home directories
|
|
home_owner_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ "$uid" -ge 1000 ] && [ -d "$home" ]; then
|
|
owner=$(stat -c "%U" "$home" 2>/dev/null)
|
|
if [ "$owner" != "$user" ]; then
|
|
home_owner_ok=0
|
|
fi
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $home_owner_ok -eq 1 ]; then
|
|
output_result "6.2.10" "Đảm bảo người dùng sở hữu thư mục home của chính họ" "PASSED"
|
|
else
|
|
output_result "6.2.10" "Đảm bảo người dùng sở hữu thư mục home của chính họ" "FAILED"
|
|
fi
|
|
|
|
# 6.2.11 Home directory permissions
|
|
home_perms_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ "$uid" -ge 1000 ] && [ -d "$home" ]; then
|
|
perms=$(stat -c "%a" "$home" 2>/dev/null)
|
|
if [ "$perms" -gt 750 ] 2>/dev/null; then
|
|
home_perms_ok=0
|
|
fi
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $home_perms_ok -eq 1 ]; then
|
|
output_result "6.2.11" "Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao" "PASSED"
|
|
else
|
|
output_result "6.2.11" "Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao" "FAILED"
|
|
fi
|
|
|
|
# 6.2.12 User dot files permissions
|
|
dot_files_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ "$uid" -ge 1000 ] && [ -d "$home" ]; then
|
|
for dotfile in "$home"/.*; do
|
|
if [ -f "$dotfile" ]; then
|
|
perms=$(stat -c "%a" "$dotfile" 2>/dev/null)
|
|
# Check if group or other has write permission
|
|
case "$perms" in
|
|
*[2367]|?[2367]?) dot_files_ok=0 ;;
|
|
esac
|
|
fi
|
|
done
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $dot_files_ok -eq 1 ]; then
|
|
output_result "6.2.12" "Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other" "PASSED"
|
|
else
|
|
output_result "6.2.12" "Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other" "FAILED"
|
|
fi
|
|
|
|
# 6.2.13 No .forward files
|
|
forward_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ -f "$home/.forward" ]; then
|
|
forward_ok=0
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $forward_ok -eq 1 ]; then
|
|
output_result "6.2.13" "Đảm bảo không người dùng nào có file .forward" "PASSED"
|
|
else
|
|
output_result "6.2.13" "Đảm bảo không người dùng nào có file .forward" "FAILED"
|
|
fi
|
|
|
|
# 6.2.14 No .netrc files
|
|
netrc_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ -f "$home/.netrc" ]; then
|
|
netrc_ok=0
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $netrc_ok -eq 1 ]; then
|
|
output_result "6.2.14" "Đảm bảo không người dùng nào có file .netrc" "PASSED"
|
|
else
|
|
output_result "6.2.14" "Đảm bảo không người dùng nào có file .netrc" "FAILED"
|
|
fi
|
|
|
|
# 6.2.15 No .rhosts files
|
|
rhosts_ok=1
|
|
while IFS=: read -r user pass uid gid gecos home shell; do
|
|
if [ -f "$home/.rhosts" ]; then
|
|
rhosts_ok=0
|
|
fi
|
|
done < /etc/passwd 2>/dev/null
|
|
|
|
if [ $rhosts_ok -eq 1 ]; then
|
|
output_result "6.2.15" "Đảm bảo không người dùng nào có file .rhosts" "PASSED"
|
|
else
|
|
output_result "6.2.15" "Đảm bảo không người dùng nào có file .rhosts" "FAILED"
|
|
fi
|
|
|
|
############################################################################
|
|
# SUMMARY
|
|
############################################################################
|
|
echo ""
|
|
echo "############################################################################"
|
|
echo "# Audit completed at: $(date +"%Y-%m-%d %H:%M:%S")"
|
|
echo "############################################################################"
|