#!/bin/bash ############################################################################ # SCRIPT HARDENING CIS BENCHMARK v2.0.2 CHO UBUNTU 24.04 LTS # Tương ứng với file audit: audit_cis_ubuntu_v202.sh ############################################################################ # CẢNH BÁO: Script này sẽ thay đổi cấu hình hệ thống. # - Chạy với quyền root # - Backup hệ thống trước khi chạy # - Test trên môi trường staging trước khi áp dụng production ############################################################################ SCRIPT_VERSION="2.0.2" BACKUP_DIR="/root/cis_hardening_backup_$(date +%Y%m%d_%H%M%S)" LOG_FILE="/var/log/cis_hardening_$(date +%Y%m%d_%H%M%S).log" ############################################################################ # HÀM TIỆN ÍCH ############################################################################ # Màu sắc RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # No Color log_info() { echo -e "${GREEN}[INFO]${NC} $1" | tee -a "$LOG_FILE" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE" } log_error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE" } log_section() { echo "" | tee -a "$LOG_FILE" echo -e "${CYAN}============================================================================${NC}" | tee -a "$LOG_FILE" echo -e "${CYAN} $1${NC}" | tee -a "$LOG_FILE" echo -e "${CYAN}============================================================================${NC}" | tee -a "$LOG_FILE" } log_subsection() { echo -e "${CYAN}--- $1 ---${NC}" | tee -a "$LOG_FILE" } backup_file() { local file="$1" if [ -f "$file" ]; then local backup_path="${BACKUP_DIR}$(dirname $file)" mkdir -p "$backup_path" cp -a "$file" "$backup_path/" 2>/dev/null log_info "Đã backup: $file" fi } # Hàm set_sysctl_param: đặt tham số sysctl vào file config và apply ngay # $1: tên tham số (vd: net.ipv4.ip_forward) # $2: giá trị (vd: 0) # $3: file config (vd: /etc/sysctl.d/99-cis.conf) set_sysctl_param() { local param="$1" local value="$2" local conf_file="${3:-/etc/sysctl.d/99-cis-hardening.conf}" # Xoá tham số cũ nếu có trong tất cả các file sysctl sed -i "/^\s*${param}\s*=/d" /etc/sysctl.conf 2>/dev/null for f in /etc/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf /run/sysctl.d/*.conf; do [ -f "$f" ] && [ "$f" != "$conf_file" ] && sed -i "/^\s*${param}\s*=/d" "$f" 2>/dev/null done # Thêm vào file config grep -qP "^\s*${param}\s*=" "$conf_file" 2>/dev/null && \ sed -i "s|^\s*${param}\s*=.*|${param} = ${value}|" "$conf_file" || \ echo "${param} = ${value}" >> "$conf_file" # Apply ngay sysctl -w "${param}=${value}" >/dev/null 2>&1 } # Hàm set_ssh_param: đặt tham số trong sshd_config # $1: tên tham số # $2: giá trị set_ssh_param() { local param="$1" local value="$2" local config="/etc/ssh/sshd_config" backup_file "$config" # Đảm bảo thư mục privilege separation tồn tại (nếu thiếu sshd -T sẽ báo lỗi 255) mkdir -p -m 0755 /run/sshd 2>/dev/null # Xoá cấu hình cũ trong cả /etc/ssh/sshd_config và các file trong /etc/ssh/sshd_config.d/*.conf sed -i "/^\s*#\?\s*${param}\b/Id" "$config" 2>/dev/null if [ -d /etc/ssh/sshd_config.d ]; then sed -i "/^\s*#\?\s*${param}\b/Id" /etc/ssh/sshd_config.d/*.conf 2>/dev/null fi # Thêm dòng mới vào cuối file echo "${param} ${value}" >> "$config" log_info "SSH: ${param} = ${value}" } ############################################################################ # KIỂM TRA QUYỀN ROOT ############################################################################ if [ "$(id -u)" -ne 0 ]; then log_error "Script này cần được chạy với quyền root!" exit 1 fi ############################################################################ # TẠO THƯ MỤC BACKUP ############################################################################ mkdir -p "$BACKUP_DIR" log_info "Thư mục backup: $BACKUP_DIR" log_info "File log: $LOG_FILE" ############################################################################ # THÔNG TIN HỆ THỐNG ############################################################################ log_section "THÔNG TIN HỆ THỐNG" echo "Script Version: $SCRIPT_VERSION" echo "Operating System: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"')" echo "Kernel Version: $(uname -r)" echo "Hostname: $(hostname)" echo "IP Address: $(hostname -I 2>/dev/null | awk '{print $1}')" echo "Thời gian chạy: $(date +"%Y-%m-%d %H:%M:%S")" # Detect Ubuntu version UBUNTU_VERSION=$(grep 'VERSION_ID' /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"') echo "Ubuntu Version: $UBUNTU_VERSION" # Kiểm tra OS if ! grep -qi 'ubuntu' /etc/os-release 2>/dev/null; then log_error "Script này chỉ hỗ trợ Ubuntu. OS hiện tại không phải Ubuntu!" exit 1 fi ############################################################################ # PHÁT HIỆN IPv6 ############################################################################ 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 log_info "IPv6 Status: ENABLED" else log_info "IPv6 Status: DISABLED (no IPv6 addresses)" fi else log_info "IPv6 Status: DISABLED (kernel not available)" fi ############################################################################ # CẤU HÌNH TƯƠNG TÁC ############################################################################ log_section "CẤU HÌNH TƯƠNG TÁC" echo "" echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${YELLOW}║ Vui lòng nhập các thông số cấu hình trước khi hardening ║${NC}" echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" # 1. Firewall echo -e "${CYAN}[1/4] Chọn Firewall:${NC}" echo " 1) UFW (khuyến nghị cho Ubuntu)" echo " 2) iptables" read -rp "Lựa chọn [1/2] (mặc định: 1): " FW_CHOICE FW_CHOICE="${FW_CHOICE:-1}" if [ "$FW_CHOICE" = "2" ]; then FIREWALL="iptables" else FIREWALL="ufw" fi log_info "Firewall đã chọn: $FIREWALL" echo "" # 2. NTP Server echo -e "${CYAN}[2/4] Nhập địa chỉ NTP Server:${NC}" echo " Ví dụ: pool.ntp.org, 10.0.0.1, ntp.vnpt.vn" read -rp "NTP Server (mặc định: pool.ntp.org): " NTP_SERVER NTP_SERVER="${NTP_SERVER:-pool.ntp.org}" log_info "NTP Server: $NTP_SERVER" echo "" # 3. SSH Port echo -e "${CYAN}[3/4] Nhập SSH Port:${NC}" read -rp "SSH Port (mặc định: 22): " SSH_PORT SSH_PORT="${SSH_PORT:-22}" # Validate port if ! [[ "$SSH_PORT" =~ ^[0-9]+$ ]] || [ "$SSH_PORT" -lt 1 ] || [ "$SSH_PORT" -gt 65535 ]; then log_warn "Port không hợp lệ, sử dụng port 22" SSH_PORT=22 fi log_info "SSH Port: $SSH_PORT" echo "" # 4. SSH AllowUsers echo -e "${CYAN}[4/4] Nhập danh sách user được phép SSH:${NC}" echo " Cách nhau bằng dấu cách. Ví dụ: admin sysadmin deploy" echo " Để trống nếu muốn skip (không cấu hình AllowUsers)" read -rp "SSH AllowUsers: " SSH_ALLOW_USERS if [ -n "$SSH_ALLOW_USERS" ]; then log_info "SSH AllowUsers: $SSH_ALLOW_USERS" else log_warn "SSH AllowUsers: SKIP (không cấu hình)" fi echo "" # Xác nhận echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${YELLOW}║ XÁC NHẬN CẤU HÌNH ║${NC}" echo -e "${YELLOW}╠══════════════════════════════════════════════════════════════╣${NC}" echo -e "${YELLOW}║${NC} Firewall: ${GREEN}$FIREWALL${NC}" echo -e "${YELLOW}║${NC} NTP Server: ${GREEN}$NTP_SERVER${NC}" echo -e "${YELLOW}║${NC} SSH Port: ${GREEN}$SSH_PORT${NC}" if [ -n "$SSH_ALLOW_USERS" ]; then echo -e "${YELLOW}║${NC} SSH AllowUsers: ${GREEN}$SSH_ALLOW_USERS${NC}" else echo -e "${YELLOW}║${NC} SSH AllowUsers: ${RED}SKIP${NC}" fi echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" read -rp "Bạn có muốn tiếp tục hardening? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^[yY]$ ]]; then log_info "Đã huỷ hardening theo yêu cầu người dùng." exit 0 fi echo "" log_info "Bắt đầu quá trình hardening..." echo "" ############################################################################ ############################################################################ ## SECTION 1: FILESYSTEM ## ############################################################################ ############################################################################ log_section "SECTION 1: INITIAL ACCESS & FILESYSTEM" ############################################################################ # 1.1.1.1 - 1.1.1.7: Vô hiệu hoá các filesystem không cần thiết ############################################################################ log_subsection "1.1.1.x - Vô hiệu hoá filesystem không cần thiết" MODPROBE_FS_CONF="/etc/modprobe.d/cis-filesystem.conf" backup_file "$MODPROBE_FS_CONF" cat > "$MODPROBE_FS_CONF" << 'EOF' # CIS Benchmark v2.0.2 - Vô hiệu hoá filesystem không cần thiết # 1.1.1.1 - cramfs install cramfs /bin/true blacklist cramfs # 1.1.1.2 - freevxfs install freevxfs /bin/true blacklist freevxfs # 1.1.1.3 - hfs install hfs /bin/true blacklist hfs # 1.1.1.4 - hfsplus install hfsplus /bin/true blacklist hfsplus # 1.1.1.5 - jffs2 install jffs2 /bin/true blacklist jffs2 # 1.1.1.6 - squashfs install squashfs /bin/true blacklist squashfs # 1.1.1.7 - udf install udf /bin/true blacklist udf EOF # Gỡ các module đang load for mod in cramfs freevxfs hfs hfsplus jffs2 squashfs udf; do if lsmod | grep -qw "$mod"; then rmmod "$mod" 2>/dev/null log_info "Đã gỡ module: $mod" fi done log_info "1.1.1.x - Đã vô hiệu hoá các filesystem không cần thiết" ############################################################################ # 1.1.2.x: /tmp mount options (nodev, nosuid, noexec) ############################################################################ log_subsection "1.1.2.x - Cấu hình mount options cho /tmp" backup_file "/etc/fstab" if findmnt --kernel /tmp >/dev/null 2>&1; then # Kiểm tra xem /tmp đã có trong fstab chưa if grep -qE '\s+/tmp\s+' /etc/fstab; then # Cập nhật options sed -i '/\s\/tmp\s/ s/defaults[^ ]*/defaults,nodev,nosuid,noexec/' /etc/fstab # Nếu chưa có defaults if ! grep -qE '\s+/tmp\s+.*nodev' /etc/fstab; then sed -i '/\s\/tmp\s/ s/\(.*\)\(\s\+[0-9]\+\s\+[0-9]\+\)/\1,nodev,nosuid,noexec\2/' /etc/fstab fi else echo "tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0" >> /etc/fstab fi mount -o remount /tmp 2>/dev/null log_info "1.1.2.x - Đã cấu hình nodev,nosuid,noexec cho /tmp" else # /tmp không được mount riêng, tạo entry if ! grep -qE '\s+/tmp\s+' /etc/fstab; then echo "tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0" >> /etc/fstab fi log_info "1.1.2.x - Đã thêm cấu hình /tmp vào fstab" fi ############################################################################ # 1.1.3.x: /var/tmp mount options ############################################################################ log_subsection "1.1.3.x - Cấu hình mount options cho /var/tmp" if findmnt --kernel /var/tmp >/dev/null 2>&1; then if grep -qE '\s+/var/tmp\s+' /etc/fstab; then sed -i '/\s\/var\/tmp\s/ s/defaults[^ ]*/defaults,nodev,nosuid,noexec/' /etc/fstab else echo "tmpfs /var/tmp tmpfs defaults,nodev,nosuid,noexec 0 0" >> /etc/fstab fi mount -o remount /var/tmp 2>/dev/null log_info "1.1.3.x - Đã cấu hình nodev,nosuid,noexec cho /var/tmp" else if ! grep -qE '\s+/var/tmp\s+' /etc/fstab; then echo "tmpfs /var/tmp tmpfs defaults,nodev,nosuid,noexec 0 0" >> /etc/fstab fi log_info "1.1.3.x - Đã thêm cấu hình /var/tmp vào fstab" fi ############################################################################ # 1.1.4.x: /home mount options (nodev, nosuid) ############################################################################ log_subsection "1.1.4.x - Cấu hình mount options cho /home" if findmnt --kernel /home >/dev/null 2>&1; then if grep -qE '\s+/home\s+' /etc/fstab; then # Thêm nodev,nosuid nếu chưa có if ! grep -qE '\s+/home\s+.*nodev' /etc/fstab; then sed -i '/\s\/home\s/ s/defaults/defaults,nodev,nosuid/' /etc/fstab fi fi mount -o remount /home 2>/dev/null log_info "1.1.4.x - Đã cấu hình nodev,nosuid cho /home" else log_info "1.1.4.x - /home không mount riêng, skip" fi ############################################################################ # 1.1.5.x: /dev/shm mount options ############################################################################ log_subsection "1.1.5.x - Cấu hình mount options cho /dev/shm" if grep -qE '\s+/dev/shm\s+' /etc/fstab; then if ! grep -qE '\s+/dev/shm\s+.*nodev' /etc/fstab; then sed -i '/\s\/dev\/shm\s/ s/defaults/defaults,nodev,nosuid,noexec/' /etc/fstab fi else echo "tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0" >> /etc/fstab fi mount -o remount /dev/shm 2>/dev/null log_info "1.1.5.x - Đã cấu hình nodev,nosuid,noexec cho /dev/shm" ############################################################################ # 1.1.6: Sticky bit cho thư mục dùng chung ############################################################################ log_subsection "1.1.6 - Sticky bit cho thư mục dùng chung" df --local -P | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -type d \( -perm -0002 -a ! -perm -1000 \) 2>/dev/null | xargs -I '{}' chmod a+t '{}' 2>/dev/null log_info "1.1.6 - Đã đặt sticky bit cho các thư mục dùng chung" ############################################################################ # 1.1.8: Vô hiệu hoá USB storage ############################################################################ log_subsection "1.1.8 - Vô hiệu hoá USB storage" USB_CONF="/etc/modprobe.d/cis-usb-storage.conf" backup_file "$USB_CONF" cat > "$USB_CONF" << 'EOF' # CIS 1.1.8 - Vô hiệu hoá USB storage install usb-storage /bin/true blacklist usb-storage EOF if lsmod | grep -q usb-storage; then rmmod usb-storage 2>/dev/null fi log_info "1.1.8 - Đã vô hiệu hoá USB storage" ############################################################################ # 1.2.1 - 1.2.2: Cài đặt AIDE + cron ############################################################################ log_subsection "1.2.1-2 - AIDE" if ! dpkg -l | grep -q "ii aide"; then DEBIAN_FRONTEND=noninteractive apt-get install -y aide aide-common >> "$LOG_FILE" 2>&1 log_info "1.2.1 - Đã cài đặt AIDE" else log_info "1.2.1 - AIDE đã được cài đặt" fi # Khởi tạo AIDE nếu chưa có database if [ ! -f /var/lib/aide/aide.db ]; then log_info "1.2.1 - Đang khởi tạo AIDE database (có thể mất vài phút)..." aideinit >> "$LOG_FILE" 2>&1 if [ -f /var/lib/aide/aide.db.new ]; then mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db fi log_info "1.2.1 - Đã khởi tạo AIDE database" fi # Cấu hình cron cho AIDE AIDE_CRON="0 5 * * * /usr/bin/aide.wrapper --config /etc/aide/aide.conf --check" if ! crontab -u root -l 2>/dev/null | grep -q "aide"; then (crontab -u root -l 2>/dev/null; echo "$AIDE_CRON") | crontab -u root - log_info "1.2.2 - Đã cấu hình AIDE cron check" else log_info "1.2.2 - AIDE cron đã tồn tại" fi ############################################################################ # 1.3.1: Phân quyền file cấu hình bootloader ############################################################################ log_subsection "1.3.1 - Phân quyền bootloader" if [ -f /boot/grub/grub.cfg ]; then chown root:root /boot/grub/grub.cfg chmod u-wx,go-rwx /boot/grub/grub.cfg log_info "1.3.1 - Đã phân quyền /boot/grub/grub.cfg" fi ############################################################################ # 1.4.1: ASLR ############################################################################ log_subsection "1.4.1 - ASLR" SYSCTL_CIS="/etc/sysctl.d/99-cis-hardening.conf" touch "$SYSCTL_CIS" set_sysctl_param "kernel.randomize_va_space" "2" "$SYSCTL_CIS" log_info "1.4.1 - Đã kích hoạt ASLR (kernel.randomize_va_space=2)" ############################################################################ # 1.4.2: Gỡ prelink ############################################################################ log_subsection "1.4.2 - Gỡ prelink" if dpkg -l | grep -q "ii prelink"; then prelink -ua 2>/dev/null apt-get purge -y prelink >> "$LOG_FILE" 2>&1 log_info "1.4.2 - Đã gỡ prelink" else log_info "1.4.2 - prelink không được cài đặt" fi ############################################################################ # 1.4.3: Vô hiệu hoá core dump ############################################################################ log_subsection "1.4.3 - Vô hiệu hoá core dump" # limits.conf LIMITS_CORE="/etc/security/limits.d/99-cis-coredump.conf" echo "* hard core 0" > "$LIMITS_CORE" log_info "1.4.3 - Đã cấu hình limits: * hard core 0" # sysctl set_sysctl_param "fs.suid_dumpable" "0" "$SYSCTL_CIS" log_info "1.4.3 - Đã cấu hình sysctl: fs.suid_dumpable=0" # systemd-coredump if systemctl list-unit-files --type=service --type=socket 2>/dev/null | grep -q '^systemd-coredump'; then mkdir -p /etc/systemd/coredump.conf.d cat > /etc/systemd/coredump.conf.d/99-cis-override.conf << 'EOF' [Coredump] Storage=none ProcessSizeMax=0 EOF systemctl daemon-reload log_info "1.4.3 - Đã cấu hình systemd-coredump override" fi ############################################################################ # 1.5.1 - 1.5.3: Banner login ############################################################################ log_subsection "1.5.1-3 - Banner login" BANNER_TEXT="Authorized uses only. All activity may be monitored and reported." # /etc/motd - xoá hoặc cấu hình backup_file "/etc/motd" if [ -f /etc/motd ]; then rm -f /etc/motd log_info "1.5.1 - Đã xoá /etc/motd" fi # /etc/issue backup_file "/etc/issue" echo "$BANNER_TEXT" > /etc/issue log_info "1.5.2 - Đã cấu hình /etc/issue" # /etc/issue.net backup_file "/etc/issue.net" echo "$BANNER_TEXT" > /etc/issue.net log_info "1.5.3 - Đã cấu hình /etc/issue.net" ############################################################################ # 1.5.4 - 1.5.6: Phân quyền file banner ############################################################################ log_subsection "1.5.4-6 - Phân quyền file banner" for f in /etc/issue /etc/issue.net; do if [ -f "$f" ]; then chown root:root "$f" chmod u-x,go-wx "$f" log_info "Đã phân quyền $f" fi done ############################################################################ # 1.5.7: GDM banner (nếu có GNOME) ############################################################################ log_subsection "1.5.7 - GDM banner" HAS_GNOME=0 if dpkg -l gdm3 gnome-shell 2>/dev/null | awk '$1=="ii"{f=1} END{exit !f}'; then HAS_GNOME=1 elif [ -d /etc/gdm3 ]; then HAS_GNOME=1 fi if [ "$HAS_GNOME" -eq 1 ]; then GDM_CONF="/etc/gdm3/greeter.dconf-defaults" backup_file "$GDM_CONF" mkdir -p /etc/gdm3 # Kiểm tra và thêm section nếu chưa có if ! grep -q '\[org/gnome/login-screen\]' "$GDM_CONF" 2>/dev/null; then cat >> "$GDM_CONF" << EOF [org/gnome/login-screen] banner-message-enable=true banner-message-text='${BANNER_TEXT}' disable-user-list=true EOF fi dpkg-reconfigure gdm3 2>/dev/null log_info "1.5.7 - Đã cấu hình GDM banner" else log_info "1.5.7 - GNOME không cài đặt, skip" fi ############################################################################ ############################################################################ ## SECTION 2: SERVICES ## ############################################################################ ############################################################################ log_section "SECTION 2: SERVICES" ############################################################################ # 2.1.1 - 2.1.2: Cấu hình NTP/chrony ############################################################################ log_subsection "2.1.1-2 - Cấu hình NTP (chrony)" # Cài đặt chrony nếu chưa có if ! dpkg -l | grep -q "ii chrony"; then DEBIAN_FRONTEND=noninteractive apt-get install -y chrony >> "$LOG_FILE" 2>&1 fi # Cấu hình chrony CHRONY_CONF="/etc/chrony/chrony.conf" backup_file "$CHRONY_CONF" # Thêm NTP server nếu chưa có if ! grep -qE "^(server|pool)\s+${NTP_SERVER}" "$CHRONY_CONF" 2>/dev/null; then # Comment các server/pool cũ (không phải NTP_SERVER) sed -i "s/^pool /#pool /" "$CHRONY_CONF" 2>/dev/null sed -i "s/^server /#server /" "$CHRONY_CONF" 2>/dev/null # Thêm server mới echo "server ${NTP_SERVER} iburst" >> "$CHRONY_CONF" fi systemctl enable chrony --now >> "$LOG_FILE" 2>&1 # Vô hiệu hoá ntp và systemd-timesyncd nếu dùng chrony systemctl stop ntp 2>/dev/null; systemctl disable ntp 2>/dev/null systemctl stop systemd-timesyncd 2>/dev/null; systemctl disable systemd-timesyncd 2>/dev/null log_info "2.1.1-2 - Đã cấu hình chrony với server: $NTP_SERVER" ############################################################################ # 2.2.1: Vô hiệu hoá xinetd ############################################################################ log_subsection "2.2.x - Vô hiệu hoá dịch vụ không cần thiết" # 2.2.1 - xinetd if dpkg -l | grep -q xinetd; then systemctl stop xinetd 2>/dev/null systemctl disable xinetd 2>/dev/null apt-get purge -y xinetd >> "$LOG_FILE" 2>&1 log_info "2.2.1 - Đã gỡ xinetd" fi # 2.2.2 - autofs if dpkg -l | grep -q "ii autofs"; then systemctl stop autofs 2>/dev/null systemctl disable autofs 2>/dev/null apt-get purge -y autofs >> "$LOG_FILE" 2>&1 log_info "2.2.2 - Đã gỡ autofs" fi # 2.2.3 - X Window if dpkg -l | grep -q "ii xserver-xorg"; then log_warn "2.2.3 - xserver-xorg đã cài đặt. Vui lòng gỡ thủ công nếu không cần: apt remove --purge '^xserver-xorg'" fi # 2.2.4 - Avahi if dpkg -l | grep -q "ii avahi-daemon"; then systemctl stop avahi-daemon.service avahi-daemon.socket 2>/dev/null apt-get purge -y avahi-daemon >> "$LOG_FILE" 2>&1 log_info "2.2.4 - Đã gỡ avahi-daemon" fi # 2.2.5 - CUPS if systemctl is-enabled cups 2>/dev/null | grep -q "enabled" || systemctl is-active cups 2>/dev/null | grep -q "^active"; then systemctl stop cups 2>/dev/null systemctl disable cups 2>/dev/null systemctl mask cups 2>/dev/null log_info "2.2.5 - Đã vô hiệu hoá CUPS" fi # 2.2.6 - DHCP Server if dpkg -l | grep -q "ii isc-dhcp-server"; then apt-get purge -y isc-dhcp-server >> "$LOG_FILE" 2>&1 log_info "2.2.6 - Đã gỡ isc-dhcp-server" fi # 2.2.7 - LDAP Server if dpkg -l | grep -q "ii slapd"; then apt-get purge -y slapd >> "$LOG_FILE" 2>&1 log_info "2.2.7 - Đã gỡ slapd" fi # 2.2.6b - NFS if dpkg -l | grep -q "ii nfs-kernel-server"; then apt-get purge -y nfs-kernel-server >> "$LOG_FILE" 2>&1 log_info "2.2.6b - Đã gỡ nfs-kernel-server" fi # 2.2.8 - DNS Server (bind9 etc.) for dns_pkg in bind9 dnsmasq unbound pdns-server; do if dpkg -l | grep -q "ii ${dns_pkg}"; then systemctl stop "${dns_pkg}" 2>/dev/null systemctl disable "${dns_pkg}" 2>/dev/null log_info "2.2.8 - Đã vô hiệu hoá ${dns_pkg}" fi done # 2.2.9 - dnsmasq if dpkg -l 2>/dev/null | grep -q "ii dnsmasq"; then systemctl stop dnsmasq 2>/dev/null systemctl disable dnsmasq 2>/dev/null log_info "2.2.9 - Đã vô hiệu hoá dnsmasq" fi # 2.2.10 - FTP Server if dpkg -l | grep -q "ii vsftpd"; then apt-get purge -y vsftpd >> "$LOG_FILE" 2>&1 log_info "2.2.10 - Đã gỡ vsftpd" fi # 2.2.11 - TFTP Server if dpkg -l | grep -q "ii tftpd-hpa"; then systemctl stop tftp.socket tftpd-hpa.service 2>/dev/null systemctl disable tftp.socket tftpd-hpa.service 2>/dev/null apt-get purge -y tftpd-hpa >> "$LOG_FILE" 2>&1 log_info "2.2.11 - Đã gỡ tftpd-hpa" fi # 2.2.12 - HTTP Server (Apache) if dpkg -l | grep -q "ii apache2"; then apt-get purge -y apache2 >> "$LOG_FILE" 2>&1 log_info "2.2.12 - Đã gỡ apache2" fi # 2.2.13 - IMAP/POP3 for mail_pkg in dovecot-imapd dovecot-pop3d; do if dpkg -l | grep -q "ii ${mail_pkg}"; then apt-get purge -y "${mail_pkg}" >> "$LOG_FILE" 2>&1 log_info "2.2.13 - Đã gỡ ${mail_pkg}" fi done # 2.2.14 - Samba if systemctl is-enabled smbd 2>/dev/null | grep -q "enabled" || systemctl is-active smbd 2>/dev/null | grep -q "^active"; then systemctl stop smbd nmbd 2>/dev/null systemctl disable smbd nmbd 2>/dev/null systemctl mask smbd nmbd 2>/dev/null log_info "2.2.14 - Đã vô hiệu hoá Samba" fi # 2.2.14b - telnetd if dpkg -l | grep -q "ii telnetd"; then apt-get purge -y telnetd >> "$LOG_FILE" 2>&1 log_info "2.2.14b - Đã gỡ telnetd" fi # 2.2.15 - Squid (HTTP Proxy) if dpkg -l | grep -q "ii squid"; then apt-get purge -y squid >> "$LOG_FILE" 2>&1 log_info "2.2.15 - Đã gỡ squid" fi # 2.2.15b - rsh-server if dpkg -l | grep -q "ii rsh-server"; then apt-get purge -y rsh-server >> "$LOG_FILE" 2>&1 log_info "2.2.15b - Đã gỡ rsh-server" fi # 2.2.16 - SNMP for snmp_pkg in snmp snmpd; do if dpkg -l | grep -q "ii ${snmp_pkg}"; then apt-get purge -y "${snmp_pkg}" >> "$LOG_FILE" 2>&1 log_info "2.2.16 - Đã gỡ ${snmp_pkg}" fi done # 2.2.17 - NIS Server if dpkg -l | grep -q ypserv; then systemctl stop ypserv 2>/dev/null systemctl disable ypserv 2>/dev/null log_info "2.2.17 - Đã vô hiệu hoá ypserv" fi # 2.2.18 - MTA local-only (Postfix) if [ -f /etc/postfix/main.cf ]; then backup_file "/etc/postfix/main.cf" if grep -q "^inet_interfaces" /etc/postfix/main.cf; then sed -i 's/^inet_interfaces.*/inet_interfaces = loopback-only/' /etc/postfix/main.cf else echo "inet_interfaces = loopback-only" >> /etc/postfix/main.cf fi systemctl restart postfix 2>/dev/null log_info "2.2.18 - Đã cấu hình postfix local-only" fi # 2.2.19 - NFS service if dpkg -l | grep -q nfs-kernel-server; then systemctl stop nfs-server 2>/dev/null systemctl disable nfs-server 2>/dev/null log_info "2.2.19 - Đã vô hiệu hoá NFS server" fi # 2.2.20 - rsync if systemctl is-enabled rsync 2>/dev/null | grep -q "enabled" || systemctl is-active rsync 2>/dev/null | grep -q "^active"; then systemctl stop rsync 2>/dev/null systemctl disable rsync 2>/dev/null systemctl mask rsync 2>/dev/null log_info "2.2.20 - Đã vô hiệu hoá rsync" fi ############################################################################ # 2.3.x: Gỡ client không cần thiết ############################################################################ log_subsection "2.3.x - Gỡ client không cần thiết" declare -A CLIENTS_TO_REMOVE=( ["nis"]="2.3.1" ["rsh-client"]="2.3.2" ["talk"]="2.3.3" ["telnet"]="2.3.4" ["ldap-utils"]="2.3.5" ["rpcbind"]="2.3.6" ["ftp"]="2.3.7" ) for pkg in "${!CLIENTS_TO_REMOVE[@]}"; do if dpkg -l | grep -q "ii ${pkg}"; then apt-get purge -y "$pkg" >> "$LOG_FILE" 2>&1 log_info "${CLIENTS_TO_REMOVE[$pkg]} - Đã gỡ ${pkg}" fi done ############################################################################ ############################################################################ ## SECTION 3: NETWORK ## ############################################################################ ############################################################################ log_section "SECTION 3: NETWORK" ############################################################################ # 3.1.1: Vô hiệu hoá IP forwarding ############################################################################ log_subsection "3.1.1-2 - Network parameters (host)" set_sysctl_param "net.ipv4.ip_forward" "0" "$SYSCTL_CIS" if [ "$IPV6_ENABLED" -eq 1 ]; then set_sysctl_param "net.ipv6.conf.all.forwarding" "0" "$SYSCTL_CIS" fi log_info "3.1.1 - Đã vô hiệu hoá IP forwarding" # 3.1.2: Vô hiệu hoá packet redirect set_sysctl_param "net.ipv4.conf.all.send_redirects" "0" "$SYSCTL_CIS" set_sysctl_param "net.ipv4.conf.default.send_redirects" "0" "$SYSCTL_CIS" log_info "3.1.2 - Đã vô hiệu hoá packet redirect" ############################################################################ # 3.2.x: Network parameters (host and router) ############################################################################ log_subsection "3.2.x - Network parameters (host and router)" # 3.2.1: Source routed packets set_sysctl_param "net.ipv4.conf.all.accept_source_route" "0" "$SYSCTL_CIS" set_sysctl_param "net.ipv4.conf.default.accept_source_route" "0" "$SYSCTL_CIS" if [ "$IPV6_ENABLED" -eq 1 ]; then set_sysctl_param "net.ipv6.conf.all.accept_source_route" "0" "$SYSCTL_CIS" set_sysctl_param "net.ipv6.conf.default.accept_source_route" "0" "$SYSCTL_CIS" fi log_info "3.2.1 - Đã từ chối source routed packets" # 3.2.2: ICMP redirects set_sysctl_param "net.ipv4.conf.all.accept_redirects" "0" "$SYSCTL_CIS" set_sysctl_param "net.ipv4.conf.default.accept_redirects" "0" "$SYSCTL_CIS" if [ "$IPV6_ENABLED" -eq 1 ]; then set_sysctl_param "net.ipv6.conf.all.accept_redirects" "0" "$SYSCTL_CIS" set_sysctl_param "net.ipv6.conf.default.accept_redirects" "0" "$SYSCTL_CIS" fi log_info "3.2.2 - Đã từ chối ICMP redirects" # 3.2.3: Secure ICMP redirects set_sysctl_param "net.ipv4.conf.all.secure_redirects" "0" "$SYSCTL_CIS" set_sysctl_param "net.ipv4.conf.default.secure_redirects" "0" "$SYSCTL_CIS" log_info "3.2.3 - Đã từ chối secure ICMP redirects" # 3.2.4: Log suspicious packets set_sysctl_param "net.ipv4.conf.all.log_martians" "1" "$SYSCTL_CIS" set_sysctl_param "net.ipv4.conf.default.log_martians" "1" "$SYSCTL_CIS" log_info "3.2.4 - Đã bật log cho gói tin đáng ngờ" # 3.2.5: Ignore broadcast ICMP set_sysctl_param "net.ipv4.icmp_echo_ignore_broadcasts" "1" "$SYSCTL_CIS" log_info "3.2.5 - Đã bỏ qua broadcast ICMP" # 3.2.6: Ignore bogus ICMP set_sysctl_param "net.ipv4.icmp_ignore_bogus_error_responses" "1" "$SYSCTL_CIS" log_info "3.2.6 - Đã bỏ qua bogus ICMP" # 3.2.7: Reverse path filtering set_sysctl_param "net.ipv4.conf.all.rp_filter" "1" "$SYSCTL_CIS" set_sysctl_param "net.ipv4.conf.default.rp_filter" "1" "$SYSCTL_CIS" log_info "3.2.7 - Đã bật reverse path filtering" # 3.2.8: TCP SYN Cookies set_sysctl_param "net.ipv4.tcp_syncookies" "1" "$SYSCTL_CIS" log_info "3.2.8 - Đã bật TCP SYN Cookies" ############################################################################ # 3.3.x: IPv6 parameters ############################################################################ if [ "$IPV6_ENABLED" -eq 1 ]; then log_subsection "3.3.x - IPv6 parameters" set_sysctl_param "net.ipv6.conf.all.accept_ra" "0" "$SYSCTL_CIS" set_sysctl_param "net.ipv6.conf.default.accept_ra" "0" "$SYSCTL_CIS" log_info "3.3.1 - Đã vô hiệu hoá IPv6 router advertisements" fi # Apply all sysctl sysctl --system >> "$LOG_FILE" 2>&1 log_info "Đã apply tất cả sysctl parameters" ############################################################################ # 3.4.x: Firewall ############################################################################ log_subsection "3.4.x - Cấu hình Firewall ($FIREWALL)" if [ "$FIREWALL" = "ufw" ]; then # Cài đặt UFW if ! dpkg -l | grep -Eq '^ii\s+ufw(\s|$)'; then DEBIAN_FRONTEND=noninteractive apt-get install -y ufw >> "$LOG_FILE" 2>&1 fi # Vô hiệu hoá nftables nếu có if dpkg -l | grep -q "ii nftables"; then systemctl mask nftables 2>/dev/null fi # Cấu hình UFW systemctl unmask ufw.service 2>/dev/null systemctl enable ufw --now >> "$LOG_FILE" 2>&1 # Default policies ufw default deny incoming >> "$LOG_FILE" 2>&1 ufw default deny outgoing >> "$LOG_FILE" 2>&1 ufw default deny routed >> "$LOG_FILE" 2>&1 # Loopback ufw allow in on lo >> "$LOG_FILE" 2>&1 ufw allow out on lo >> "$LOG_FILE" 2>&1 ufw deny in from 127.0.0.0/8 >> "$LOG_FILE" 2>&1 if [ "$IPV6_ENABLED" -eq 1 ]; then ufw deny in from ::1 >> "$LOG_FILE" 2>&1 fi # Allow SSH ufw allow in "${SSH_PORT}/tcp" >> "$LOG_FILE" 2>&1 # Allow essential outbound ufw allow out 53 >> "$LOG_FILE" 2>&1 # DNS ufw allow out 80/tcp >> "$LOG_FILE" 2>&1 # HTTP (apt updates) ufw allow out 443/tcp >> "$LOG_FILE" 2>&1 # HTTPS ufw allow out 123/udp >> "$LOG_FILE" 2>&1 # NTP # Enable UFW echo "y" | ufw enable >> "$LOG_FILE" 2>&1 log_info "3.4.1.x - UFW đã được cấu hình và kích hoạt" log_info "3.4.1.x - Default: deny incoming, deny outgoing, deny routed" log_info "3.4.1.x - Đã allow SSH port ${SSH_PORT}, DNS, HTTP, HTTPS, NTP outbound" else # iptables if ! dpkg -l | grep -Eq '^ii\s+iptables(\s|$)'; then DEBIAN_FRONTEND=noninteractive apt-get install -y iptables >> "$LOG_FILE" 2>&1 fi if ! dpkg -l | grep -Eq '^ii\s+iptables-persistent(\s|$)'; then echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent >> "$LOG_FILE" 2>&1 fi # Vô hiệu hoá ufw nếu có if dpkg -l | grep -Eq '^ii\s+ufw(\s|$)'; then ufw disable 2>/dev/null systemctl stop ufw 2>/dev/null systemctl disable ufw 2>/dev/null fi # Vô hiệu hoá nftables if dpkg -l | grep -q "ii nftables"; then systemctl mask nftables 2>/dev/null fi # Flush existing rules iptables -F iptables -X # Loopback iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT iptables -A INPUT -s 127.0.0.0/8 -j DROP # Allow established connections iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow outbound iptables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT iptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT iptables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT iptables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT iptables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT # Allow SSH inbound iptables -A INPUT -p tcp --dport "$SSH_PORT" -m state --state NEW -j ACCEPT # Default policies - DROP iptables -P INPUT DROP iptables -P OUTPUT DROP iptables -P FORWARD DROP # IPv6 if [ "$IPV6_ENABLED" -eq 1 ]; then ip6tables -F ip6tables -X ip6tables -A INPUT -i lo -j ACCEPT ip6tables -A OUTPUT -o lo -j ACCEPT ip6tables -A INPUT -s ::1 -j DROP ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT ip6tables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT ip6tables -A INPUT -p tcp --dport "$SSH_PORT" -m state --state NEW -j ACCEPT ip6tables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT ip6tables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT ip6tables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT ip6tables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT ip6tables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT ip6tables -P INPUT DROP ip6tables -P OUTPUT DROP ip6tables -P FORWARD DROP fi # Save rules iptables-save > /etc/iptables/rules.v4 2>/dev/null ip6tables-save > /etc/iptables/rules.v6 2>/dev/null log_info "3.4.2.x - iptables đã được cấu hình" log_info "3.4.2.x - Default policy: DROP cho INPUT, OUTPUT, FORWARD" log_info "3.4.2.x - Đã allow SSH port ${SSH_PORT}, DNS, HTTP, HTTPS, NTP outbound" fi ############################################################################ ############################################################################ ## SECTION 4: LOGGING ## ############################################################################ ############################################################################ log_section "SECTION 4: LOGGING & AUDITING" ############################################################################ # 4.1.1.1: rsyslog ############################################################################ log_subsection "4.1.1.x - rsyslog" if ! dpkg -l | grep -q "ii rsyslog"; then DEBIAN_FRONTEND=noninteractive apt-get install -y rsyslog >> "$LOG_FILE" 2>&1 fi systemctl enable rsyslog --now >> "$LOG_FILE" 2>&1 log_info "4.1.1.1 - rsyslog đã được kích hoạt" # 4.1.1.2: FileCreateMode RSYSLOG_CONF="/etc/rsyslog.conf" backup_file "$RSYSLOG_CONF" if grep -q '^\$FileCreateMode' "$RSYSLOG_CONF"; then sed -i 's/^\$FileCreateMode.*/\$FileCreateMode 0640/' "$RSYSLOG_CONF" else echo '$FileCreateMode 0640' >> "$RSYSLOG_CONF" fi systemctl restart rsyslog >> "$LOG_FILE" 2>&1 log_info "4.1.1.2 - rsyslog FileCreateMode = 0640" ############################################################################ # 4.1.2: Phân quyền file log ############################################################################ log_subsection "4.1.2 - Phân quyền file log" find /var/log -type f -exec chmod g-wx,o-rwx {} + 2>/dev/null log_info "4.1.2 - Đã phân quyền cho tất cả file trong /var/log" ############################################################################ ############################################################################ ## SECTION 5: ACCESS & AUTHENTICATION ## ############################################################################ ############################################################################ log_section "SECTION 5: ACCESS, AUTHENTICATION & AUTHORIZATION" ############################################################################ # 5.1.1 - 5.1.8: Cron ############################################################################ log_subsection "5.1.x - Cron daemon & phân quyền" systemctl enable cron --now >> "$LOG_FILE" 2>&1 log_info "5.1.1 - cron daemon đã được kích hoạt" # Phân quyền cron files/dirs for cron_path in /etc/crontab /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly /etc/cron.d; do if [ -e "$cron_path" ]; then chown root:root "$cron_path" chmod og-rwx "$cron_path" fi done log_info "5.1.2-7 - Đã phân quyền cho cron files/dirs" # 5.1.8: cron.allow / at.allow rm -f /etc/cron.deny /etc/at.deny 2>/dev/null touch /etc/cron.allow /etc/at.allow chmod og-rwx /etc/cron.allow /etc/at.allow chown root:root /etc/cron.allow /etc/at.allow log_info "5.1.8 - Đã cấu hình cron.allow/at.allow" ############################################################################ # 5.2.x: SSH Hardening ############################################################################ log_subsection "5.2.x - SSH Hardening" # 5.2.1: Phân quyền sshd_config chown root:root /etc/ssh/sshd_config chmod og-rwx /etc/ssh/sshd_config log_info "5.2.1 - Đã phân quyền /etc/ssh/sshd_config" # 5.2.2: SSH private host key permissions find /etc/ssh -xdev -type f -name 'ssh_host_*_key' -exec chown root:root {} \; find /etc/ssh -xdev -type f -name 'ssh_host_*_key' -exec chmod u-x,go-rwx {} \; log_info "5.2.2 - Đã phân quyền SSH private host keys" # 5.2.3: SSH public host key permissions find /etc/ssh -xdev -type f -name 'ssh_host_*_key.pub' -exec chown root:root {} \; find /etc/ssh -xdev -type f -name 'ssh_host_*_key.pub' -exec chmod u-x,go-wx {} \; log_info "5.2.3 - Đã phân quyền SSH public host keys" # 5.2.4: SSH Access - AllowUsers (nếu user đã nhập) if [ -n "$SSH_ALLOW_USERS" ]; then set_ssh_param "AllowUsers" "$SSH_ALLOW_USERS" log_info "5.2.4 - SSH AllowUsers: $SSH_ALLOW_USERS" else log_warn "5.2.4 - SSH AllowUsers: SKIP (không cấu hình)" fi # 5.2.5: LogLevel set_ssh_param "LogLevel" "VERBOSE" # 5.2.6: UsePAM set_ssh_param "UsePAM" "yes" # 5.2.7: PermitRootLogin set_ssh_param "PermitRootLogin" "no" # 5.2.8: HostbasedAuthentication set_ssh_param "HostbasedAuthentication" "no" # 5.2.9: PermitEmptyPasswords set_ssh_param "PermitEmptyPasswords" "no" # 5.2.10: PermitUserEnvironment set_ssh_param "PermitUserEnvironment" "no" # 5.2.11: IgnoreRhosts set_ssh_param "IgnoreRhosts" "yes" # 5.2.12: X11Forwarding set_ssh_param "X11Forwarding" "no" # 5.2.13: Ciphers set_ssh_param "Ciphers" "chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr" # 5.2.14: MACs set_ssh_param "MACs" "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256" # 5.2.15: KexAlgorithms set_ssh_param "KexAlgorithms" "curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256" # 5.2.16: AllowTcpForwarding set_ssh_param "AllowTcpForwarding" "no" # 5.2.17: Banner set_ssh_param "Banner" "/etc/issue.net" # 5.2.18: MaxAuthTries set_ssh_param "MaxAuthTries" "4" # 5.2.19: MaxStartups set_ssh_param "MaxStartups" "10:30:60" # 5.2.20: MaxSessions set_ssh_param "MaxSessions" "10" # 5.2.21: LoginGraceTime set_ssh_param "LoginGraceTime" "60" # 5.2.22: ClientAlive set_ssh_param "ClientAliveInterval" "900" set_ssh_param "ClientAliveCountMax" "0" # SSH Port (nếu khác mặc định) if [ "$SSH_PORT" != "22" ]; then set_ssh_param "Port" "$SSH_PORT" log_info "SSH Port đã được đổi sang: $SSH_PORT" fi # Đảm bảo thư mục privilege separation tồn tại vĩnh viễn (khắc phục lỗi Missing privilege separation directory /run/sshd của Ubuntu 24.04) mkdir -p -m 0755 /run/sshd 2>/dev/null cat > /etc/tmpfiles.d/cis-sshd.conf << 'EOF' d /run/sshd 0755 root root - EOF systemd-tmpfiles --create /etc/tmpfiles.d/cis-sshd.conf 2>/dev/null # Restart SSH sshd -t 2>/dev/null if [ $? -eq 0 ]; then systemctl restart ssh.socket sshd.service 2>/dev/null || systemctl restart ssh 2>/dev/null log_info "5.2.x - SSH đã được restart thành công (/run/sshd ok)" else log_error "5.2.x - Cấu hình SSH có lỗi! Vui lòng kiểm tra /etc/ssh/sshd_config" fi ############################################################################ # 5.3.1: Password quality (pwquality) ############################################################################ log_subsection "5.3.x - Password quality & lockout" if ! dpkg -l | grep -q "ii libpam-pwquality"; then DEBIAN_FRONTEND=noninteractive apt-get install -y libpam-pwquality >> "$LOG_FILE" 2>&1 fi PWQUALITY_CONF="/etc/security/pwquality.conf" backup_file "$PWQUALITY_CONF" cat > "$PWQUALITY_CONF" << 'EOF' # CIS Benchmark v2.0.2 - Password Quality minlen = 8 enforce_for_root minclass = 4 dcredit = -1 ucredit = -1 ocredit = -1 lcredit = -1 EOF log_info "5.3.1 - Đã cấu hình password quality" ############################################################################ # 5.3.2: Account lockout (faillock - Ubuntu 22.04+) ############################################################################ log_subsection "5.3.2 - Account lockout (faillock)" if grep -Eq 'VERSION_ID="(2[2-9]|[3-9][0-9])\.' /etc/os-release; then # Ubuntu 22.04+ sử dụng faillock FAILLOCK_CONF="/etc/security/faillock.conf" backup_file "$FAILLOCK_CONF" cat > "$FAILLOCK_CONF" << 'EOF' # CIS Benchmark v2.0.2 - Account Lockout deny = 5 fail_interval = 900 unlock_time = 900 EOF # Cấu hình PAM common-auth COMMON_AUTH="/etc/pam.d/common-auth" backup_file "$COMMON_AUTH" # Kiểm tra và thêm faillock nếu chưa có if ! grep -q "pam_faillock.so" "$COMMON_AUTH"; then # Tạo file mới với faillock cat > "$COMMON_AUTH" << 'EOF' # CIS Benchmark v2.0.2 - PAM common-auth with faillock auth required pam_faillock.so preauth auth [success=1 default=ignore] pam_unix.so nullok auth [default=die] pam_faillock.so authfail auth sufficient pam_faillock.so authsucc auth requisite pam_deny.so auth required pam_permit.so auth optional pam_cap.so EOF fi # Thêm faillock vào common-account COMMON_ACCOUNT="/etc/pam.d/common-account" backup_file "$COMMON_ACCOUNT" if ! grep -q "pam_faillock.so" "$COMMON_ACCOUNT"; then echo "account required pam_faillock.so" >> "$COMMON_ACCOUNT" fi log_info "5.3.2 - Đã cấu hình faillock (deny=5, fail_interval=900, unlock_time=900)" else # Ubuntu 20.04 trở về trước sử dụng pam_tally2 COMMON_AUTH="/etc/pam.d/common-auth" backup_file "$COMMON_AUTH" if ! grep -q "pam_tally2.so" "$COMMON_AUTH"; then echo "auth required pam_tally2.so onerr=fail audit silent deny=5 unlock_time=900" >> "$COMMON_AUTH" fi COMMON_ACCOUNT="/etc/pam.d/common-account" backup_file "$COMMON_ACCOUNT" if ! grep -q "pam_tally2.so" "$COMMON_ACCOUNT"; then echo "account requisite pam_deny.so" >> "$COMMON_ACCOUNT" echo "account required pam_tally2.so" >> "$COMMON_ACCOUNT" fi log_info "5.3.2 - Đã cấu hình pam_tally2 (deny=5, unlock_time=900)" fi ############################################################################ # 5.3.3: Password reuse limit ############################################################################ COMMON_PASSWORD="/etc/pam.d/common-password" backup_file "$COMMON_PASSWORD" if ! grep -q "pam_pwhistory.so" "$COMMON_PASSWORD"; then # Thêm pam_pwhistory trước pam_unix.so sed -i '/pam_unix.so/i password required pam_pwhistory.so remember=5' "$COMMON_PASSWORD" fi log_info "5.3.3 - Đã cấu hình password reuse limit (remember=5)" ############################################################################ # 5.3.4: Password hashing algorithm ############################################################################ ############################################################################ # 5.3.4: Password hashing algorithm ############################################################################ if grep -Eq 'VERSION_ID="(2[2-9]|[3-9][0-9])\.' /etc/os-release; then # Ubuntu 22.04+ / 24.04: sử dụng yescrypt (chỉ khai báo ENCRYPT_METHOD yescrypt trong login.defs) sed -i 's/^\s*ENCRYPT_METHOD.*/ENCRYPT_METHOD yescrypt/' "$LOGIN_DEFS" 2>/dev/null || echo "ENCRYPT_METHOD yescrypt" >> "$LOGIN_DEFS" # Theo chuẩn CIS trên Ubuntu 22.04+/24.04, KHÔNG được khai báo tên thuật toán (yescrypt/sha512/md5/...) trực tiếp tại dòng pam_unix.so trong common-password sed -E -i '/pam_unix\.so/ s/\s+(yescrypt|sha512|sha256|md5|bigcrypt|blowfish)\b//g' "$COMMON_PASSWORD" 2>/dev/null log_info "5.3.4 - Đã cấu hình ENCRYPT_METHOD yescrypt cho Ubuntu 22.04+/24.04" else # Ubuntu 20.04 trở về trước: sử dụng sha512 if ! grep -q "sha512" "$COMMON_PASSWORD"; then sed -i '/pam_unix.so/ s/$/ sha512/' "$COMMON_PASSWORD" fi log_info "5.3.4 - Đã cấu hình SHA-512 password hashing" fi ############################################################################ # 5.4.1.x: Password policy ############################################################################ log_subsection "5.4.1.x - Password policy" LOGIN_DEFS="/etc/login.defs" backup_file "$LOGIN_DEFS" # 5.4.1.1: PASS_MAX_DAYS = 90 sed -i 's/^\s*PASS_MAX_DAYS.*/PASS_MAX_DAYS\t90/' "$LOGIN_DEFS" log_info "5.4.1.1 - PASS_MAX_DAYS = 90" # 5.4.1.2: PASS_MIN_DAYS = 7 sed -i 's/^\s*PASS_MIN_DAYS.*/PASS_MIN_DAYS\t7/' "$LOGIN_DEFS" log_info "5.4.1.2 - PASS_MIN_DAYS = 7" # 5.4.1.3: PASS_WARN_AGE = 7 sed -i 's/^\s*PASS_WARN_AGE.*/PASS_WARN_AGE\t7/' "$LOGIN_DEFS" log_info "5.4.1.3 - PASS_WARN_AGE = 7" # 5.4.1.4: INACTIVE = 30 useradd -D -f 30 2>/dev/null log_info "5.4.1.4 - INACTIVE = 30 (default for new users)" # Apply PASS_MAX_DAYS, PASS_MIN_DAYS cho các user (UID >= 1000 và root) _UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) _UID_MIN="${_UID_MIN:-1000}" # 1. Với các human user có mật khẩu hợp lệ (không bị khóa ! hoặc *, không để trống) -> đặt maxdays=90, mindays=7 awk -F: -v uid_min="$_UID_MIN" 'NR==FNR {shadow[$1]=$2; next} ($3>=uid_min || $1=="root") && $1!="nobody" {if (shadow[$1]!="" && shadow[$1]!~/^[!*]/) print $1}' /etc/shadow /etc/passwd | while read -r user; do chage --maxdays 90 "$user" 2>/dev/null chage --mindays 7 "$user" 2>/dev/null chage --warndays 7 "$user" 2>/dev/null chage --inactive 30 "$user" 2>/dev/null done # 2. Với các human user bị khóa hoặc chưa đặt mật khẩu (! hoặc * hoặc rỗng) như ldnobody -> khôi phục maxdays=99999, mindays=0 để tránh vi phạm tiêu chí 6.2.2 của CIS audit awk -F: -v uid_min="$_UID_MIN" 'NR==FNR {shadow[$1]=$2; next} ($3>=uid_min || $1=="root") && $1!="nobody" {if (shadow[$1]=="" || shadow[$1]~/^[!*]/) print $1}' /etc/shadow /etc/passwd | while read -r user; do chage -M 99999 -m 0 "$user" 2>/dev/null done log_info "5.4.1.x & 6.2.2 - Đã chuẩn hoá password aging policy cho active và locked users" ############################################################################ # ĐẶT MẬT KHẨU MỚI CHO NGƯỜI DÙNG HIỆN CÓ ############################################################################ log_subsection "ĐẶT MẬT KHẨU MỚI CHO NGƯỜI DÙNG HIỆN CÓ" echo "" echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${YELLOW}║ ĐẶT MẬT KHẨU MỚI CHO NGƯỜI DÙNG HIỆN CÓ ║${NC}" echo -e "${YELLOW}║ ║${NC}" echo -e "${YELLOW}║ Password policy mới đã được áp dụng: ║${NC}" echo -e "${YELLOW}║ - Tối thiểu 8 ký tự, 4 loại (hoa, thường, số, đặc biệt)║${NC}" echo -e "${YELLOW}║ - Hết hạn sau 90 ngày ║${NC}" echo -e "${YELLOW}║ ║${NC}" echo -e "${YELLOW}║ Bạn CẦN đặt mật khẩu mới tuân thủ policy để login được. ║${NC}" echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" # Lấy UID_MIN để phân biệt system vs human users _UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) _UID_MIN="${_UID_MIN:-1000}" # Lấy danh sách tất cả human users (UID >= UID_MIN) + root HUMAN_USERS=() while IFS=: read -r _uname _x _uid _rest; do if [ "$_uid" -ge "$_UID_MIN" ] 2>/dev/null || [ "$_uname" = "root" ]; then # Chỉ lấy user có shell đăng nhập được (không phải nologin/false) _shell=$(getent passwd "$_uname" | cut -d: -f7) if [[ "$_shell" != */nologin && "$_shell" != */false ]]; then HUMAN_USERS+=("$_uname") fi fi done < /etc/passwd if [ ${#HUMAN_USERS[@]} -eq 0 ]; then log_info "Không tìm thấy user cần đặt mật khẩu" else echo -e "${CYAN}Danh sách người dùng có thể đăng nhập:${NC}" echo "" for i in "${!HUMAN_USERS[@]}"; do _u="${HUMAN_USERS[$i]}" _uid=$(id -u "$_u" 2>/dev/null) _groups=$(id -Gn "$_u" 2>/dev/null | tr ' ' ',') printf " ${GREEN}%2d)${NC} %-20s ${CYAN}(UID: %s, Groups: %s)${NC}\n" $((i+1)) "$_u" "$_uid" "$_groups" done echo "" echo -e "${CYAN}Chọn cách xử lý:${NC}" echo " 1) Đặt mật khẩu cho TỪNG user (khuyến nghị)" echo " 2) Đặt MỘT mật khẩu chung cho TẤT CẢ user" echo " 3) Bỏ qua (CẢNH BÁO: có thể không login được nếu mật khẩu cũ không tuân thủ policy)" read -rp "Lựa chọn [1/2/3] (mặc định: 1): " PW_CHOICE PW_CHOICE="${PW_CHOICE:-1}" case "$PW_CHOICE" in 1) # Đặt mật khẩu cho từng user echo "" for _u in "${HUMAN_USERS[@]}"; do echo -e "${YELLOW}────────────────────────────────────────${NC}" echo -e "Đặt mật khẩu cho user: ${GREEN}${_u}${NC}" echo -e "${CYAN}(Yêu cầu: ≥8 ký tự, gồm chữ hoa + chữ thường + số + ký tự đặc biệt)${NC}" echo -e "Nhấn Enter để bỏ qua user này." echo "" # Đọc mật khẩu (ẩn) read -rsp "Mật khẩu mới cho ${_u}: " _pw1 echo "" if [ -z "$_pw1" ]; then log_warn "Bỏ qua đặt mật khẩu cho: $_u" continue fi read -rsp "Xác nhận mật khẩu: " _pw2 echo "" if [ "$_pw1" != "$_pw2" ]; then log_error "Mật khẩu không khớp! Bỏ qua user: $_u" continue fi echo "$_u:$_pw1" | chpasswd 2>>"$LOG_FILE" if [ $? -eq 0 ]; then log_info "Đã đặt mật khẩu mới cho: $_u" # Reset faillock counter faillock --user "$_u" --reset 2>/dev/null else log_error "Lỗi khi đặt mật khẩu cho: $_u (có thể mật khẩu không đủ phức tạp)" fi done echo -e "${YELLOW}────────────────────────────────────────${NC}" ;; 2) # Đặt mật khẩu chung echo "" echo -e "${CYAN}(Yêu cầu: ≥8 ký tự, gồm chữ hoa + chữ thường + số + ký tự đặc biệt)${NC}" read -rsp "Nhập mật khẩu chung cho tất cả user: " _pw1 echo "" read -rsp "Xác nhận mật khẩu: " _pw2 echo "" if [ "$_pw1" != "$_pw2" ]; then log_error "Mật khẩu không khớp! Bỏ qua phần đặt mật khẩu." elif [ -z "$_pw1" ]; then log_warn "Mật khẩu trống! Bỏ qua phần đặt mật khẩu." else for _u in "${HUMAN_USERS[@]}"; do echo "$_u:$_pw1" | chpasswd 2>>"$LOG_FILE" if [ $? -eq 0 ]; then log_info "Đã đặt mật khẩu mới cho: $_u" faillock --user "$_u" --reset 2>/dev/null else log_error "Lỗi khi đặt mật khẩu cho: $_u" fi done fi ;; 3) log_warn "Đã bỏ qua đặt mật khẩu. Hãy đảm bảo mật khẩu hiện tại tuân thủ policy mới!" ;; *) log_warn "Lựa chọn không hợp lệ. Bỏ qua phần đặt mật khẩu." ;; esac # Xoá biến mật khẩu khỏi bộ nhớ unset _pw1 _pw2 fi echo "" ############################################################################ # 5.4.2: Vô hiệu hoá đăng nhập bằng tài khoản hệ thống ############################################################################ log_subsection "5.4.2 - System account hardening" UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs) # Set nologin shell cho system accounts awk -F: -v uid_min="$UID_MIN" '($1!~/^(root|halt|sync|shutdown|nfsnobody)$/ && $3/dev/null done # Lock system accounts (ngoại trừ root) awk -F: -v uid_min="$UID_MIN" '($1!="root" && $1!~/^\+/ && $3/dev/null | awk '($2!="L" && $2!="LK") {print $1}' | while read -r user; do usermod -L "$user" 2>/dev/null done log_info "5.4.2 - Đã vô hiệu hoá đăng nhập cho system accounts" ############################################################################ # 5.4.3: Root GID 0 ############################################################################ usermod -g 0 root 2>/dev/null log_info "5.4.3 - Root GID = 0" ############################################################################ # 5.4.4: umask 027 ############################################################################ log_subsection "5.4.4 - umask 027" # /etc/login.defs sed -i 's/^\s*UMASK.*/UMASK\t\t027/' "$LOGIN_DEFS" if ! grep -q "^USERGROUPS_ENAB" "$LOGIN_DEFS"; then echo 'USERGROUPS_ENAB no' >> "$LOGIN_DEFS" else sed -i 's/^\s*USERGROUPS_ENAB.*/USERGROUPS_ENAB no/' "$LOGIN_DEFS" fi # /etc/profile.d/ UMASK_PROFILE="/etc/profile.d/cis-umask.sh" cat > "$UMASK_PROFILE" << 'EOF' # CIS Benchmark v2.0.2 - Default umask umask 027 EOF chmod 644 "$UMASK_PROFILE" log_info "5.4.4 - Đã cấu hình umask 027" ############################################################################ # 5.4.5: Shell timeout TMOUT=900 ############################################################################ log_subsection "5.4.5 - Shell timeout" TMOUT_PROFILE="/etc/profile.d/cis-tmout.sh" cat > "$TMOUT_PROFILE" << 'EOF' # CIS Benchmark v2.0.2 - Shell timeout readonly TMOUT=900 ; export TMOUT EOF chmod 644 "$TMOUT_PROFILE" log_info "5.4.5 - Đã cấu hình TMOUT=900" ############################################################################ # 5.4.6: Hạn chế su ############################################################################ log_subsection "5.4.6 - Hạn chế truy cập su" # Tạo group sugroup nếu chưa có if ! grep -q "^sugroup:" /etc/group; then groupadd sugroup 2>/dev/null log_info "5.4.6 - Đã tạo group sugroup" fi # Cấu hình PAM su PAM_SU="/etc/pam.d/su" backup_file "$PAM_SU" if ! grep -q "pam_wheel.so.*use_uid.*group=sugroup" "$PAM_SU"; then # Thêm dòng auth required pam_wheel.so sed -i '/pam_rootok.so/a auth required pam_wheel.so use_uid group=sugroup' "$PAM_SU" fi log_info "5.4.6 - Đã cấu hình hạn chế su (pam_wheel.so + sugroup)" ############################################################################ ############################################################################ ## SECTION 6: SYSTEM MAINTENANCE ## ############################################################################ ############################################################################ log_section "SECTION 6: SYSTEM MAINTENANCE" ############################################################################ # 6.1.1 - 6.1.8: Phân quyền system files ############################################################################ log_subsection "6.1.1-8 - Phân quyền system files" # /etc/passwd chown root:root /etc/passwd chmod 644 /etc/passwd log_info "6.1.1 - /etc/passwd: 644 root:root" # /etc/shadow chown root:root /etc/shadow chmod 0640 /etc/shadow log_info "6.1.2 - /etc/shadow: 0640 root:root" # /etc/group chown root:root /etc/group chmod 644 /etc/group log_info "6.1.3 - /etc/group: 644 root:root" # /etc/gshadow chown root:root /etc/gshadow chmod 0640 /etc/gshadow log_info "6.1.4 - /etc/gshadow: 0640 root:root" # /etc/passwd- chown root:root /etc/passwd- chmod u-x,go-wx /etc/passwd- log_info "6.1.5 - /etc/passwd-: phân quyền" # /etc/shadow- chown root:root /etc/shadow- chmod 0640 /etc/shadow- log_info "6.1.6 - /etc/shadow-: 0640 root:root" # /etc/group- chown root:root /etc/group- chmod u-x,go-wx /etc/group- log_info "6.1.7 - /etc/group-: phân quyền" # /etc/gshadow- chown root:root /etc/gshadow- chmod 0640 /etc/gshadow- log_info "6.1.8 - /etc/gshadow-: 0640 root:root" ############################################################################ # 6.1.9: Xoá file world-writable ############################################################################ log_subsection "6.1.9 - World-writable files" WW_COUNT=$(df --local -P | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -path /proc -prune -o -type f -perm -0002 -print 2>/dev/null | wc -l) if [ "$WW_COUNT" -ne 0 ]; then df --local -P | awk '{if (NR!=1) print $6}' | xargs -I '{}' find '{}' -xdev -path /proc -prune -o -type f -perm -0002 -print 2>/dev/null | while read -r f; do chmod o-w "$f" 2>/dev/null done log_info "6.1.9 - Đã xoá quyền world-writable cho $WW_COUNT files" else log_info "6.1.9 - Không có file world-writable" fi ############################################################################ # 6.2.1: Shadowed passwords ############################################################################ log_subsection "6.2.x - User/group integrity & permissions" if awk -F: '($2 != "x" ) { print $1 }' /etc/passwd | grep -q .; then sed -e 's/^\([a-zA-Z0-9_]*\):[^:]*:/\1:x:/' -i /etc/passwd log_info "6.2.1 - Đã cấu hình shadowed passwords" else log_info "6.2.1 - Shadowed passwords đã OK" fi ############################################################################ # 6.2.9: Đảm bảo tính toàn vẹn cho biến môi trường PATH của root ############################################################################ log_subsection "6.2.9 - PATH của root integrity" RPCV="$(sudo -Hiu root env 2>/dev/null | grep '^PATH=' | cut -d= -f2)" for x in $(echo "$RPCV" | tr ":" " "); do if [ ! -d "$x" ]; then # Nếu đường dẫn trong PATH (như /snap/bin) chưa tồn tại, tạo thư mục để pass audit check mkdir -p "$x" 2>/dev/null chmod 755 "$x" 2>/dev/null chown root:root "$x" 2>/dev/null log_info "6.2.9 - Đã tạo thư mục missing trong PATH: $x" else chmod go-w "$x" 2>/dev/null chown root:root "$x" 2>/dev/null fi done log_info "6.2.9 - Đã chuẩn hoá quyền và tính toàn vẹn cho PATH của root" ############################################################################ # 6.2.13: Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao (max 750) ############################################################################ log_subsection "6.2.13 - Quyền thư mục home của người dùng" awk -F: '($1!~/^(halt|sync|shutdown|nfsnobody)$/ && $7!~/^(\/usr)?\/sbin\/nologin(\/)?$/ && $7!~/(\/usr)?\/bin\/false(\/)?$/) {print $6}' /etc/passwd | while read -r dir; do if [ -d "$dir" ]; then chmod g-w,o-rwx "$dir" 2>/dev/null fi done log_info "6.2.13 - Đã phân quyền g-w,o-rwx (tối đa 750) cho tất cả thư mục home của người dùng" ############################################################################ ############################################################################ ## HOÀN THÀNH ## ############################################################################ ############################################################################ log_section "HOÀN THÀNH HARDENING" echo "" echo -e "${GREEN}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ HARDENING CIS BENCHMARK v2.0.2 ĐÃ HOÀN THÀNH ║${NC}" echo -e "${GREEN}╠══════════════════════════════════════════════════════════════╣${NC}" echo -e "${GREEN}║${NC} " echo -e "${GREEN}║${NC} Backup: ${CYAN}$BACKUP_DIR${NC}" echo -e "${GREEN}║${NC} Log file: ${CYAN}$LOG_FILE${NC}" echo -e "${GREEN}║${NC} " echo -e "${GREEN}║${NC} ${YELLOW}BƯỚC TIẾP THEO:${NC} " echo -e "${GREEN}║${NC} 1. Chạy audit script để verify: " echo -e "${GREEN}║${NC} ${CYAN}bash audit_cis_ubuntu_v202.sh${NC} " echo -e "${GREEN}║${NC} 2. Kiểm tra SSH login vẫn hoạt động " echo -e "${GREEN}║${NC} 3. Reboot server và xác nhận " echo -e "${GREEN}║${NC} " echo -e "${GREEN}║${NC} ${RED}LƯU Ý QUAN TRỌNG:${NC} " echo -e "${GREEN}║${NC} - Đặt mật khẩu root (nếu chưa): ${CYAN}passwd root${NC} " echo -e "${GREEN}║${NC} - Kiểm tra SSH: ${CYAN}ssh -p $SSH_PORT user@hostname${NC} " if [ -n "$SSH_ALLOW_USERS" ]; then echo -e "${GREEN}║${NC} - SSH AllowUsers: ${CYAN}$SSH_ALLOW_USERS${NC} " fi echo -e "${GREEN}║${NC} - Cấu hình rsyslog remote log (4.1.1.3): thủ công " echo -e "${GREEN}║${NC} " echo -e "${GREEN}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" log_info "Script hoàn thành tại $(date +"%Y-%m-%d %H:%M:%S")" log_info "Tổng thời gian: $SECONDS giây"