1713 lines
67 KiB
Bash
1713 lines
67 KiB
Bash
#!/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<uid_min && $7!~/^(\/usr)?\/sbin\/nologin(\/)?\$/ && $7!~/(\/usr)?\/bin\/false(\/)?\$/) { print $1 }' /etc/passwd | while read -r user; do
|
|
usermod -s "$(which nologin)" "$user" 2>/dev/null
|
|
done
|
|
|
|
# Lock system accounts (ngoại trừ root)
|
|
awk -F: -v uid_min="$UID_MIN" '($1!="root" && $1!~/^\+/ && $3<uid_min) {print $1}' /etc/passwd | xargs -I '{}' passwd -S '{}' 2>/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"
|