refactor(harden): remove ssh key-only module and improve port detection
- drop DO_SSH flag, --no-ssh option, and setup_ssh() function entirely - replace inline sshd_config port read with sshd_effective_port() that also scans sshd_config.d drop-ins - replace raw DEBIAN_FRONTEND export with apt_noninteractive helper and add wait_for_apt guard - replace hardcoded separator strings with show_separator calls
This commit is contained in:
+16
-62
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# LXS - Server Hardening
|
# LXS - Server Hardening
|
||||||
# Description: Apply baseline security hardening (UFW + fail2ban + SSH key-only + unattended-upgrades)
|
# Description: Apply baseline security hardening (UFW + fail2ban + unattended-upgrades)
|
||||||
# Author: LXS
|
# Author: LXS
|
||||||
# Date: 2025
|
# Date: 2025
|
||||||
|
|
||||||
@@ -22,7 +22,6 @@ set -u
|
|||||||
|
|
||||||
DO_UFW=1
|
DO_UFW=1
|
||||||
DO_FAIL2BAN=1
|
DO_FAIL2BAN=1
|
||||||
DO_SSH=1
|
|
||||||
DO_UNATTENDED=1
|
DO_UNATTENDED=1
|
||||||
ASSUME_YES=0
|
ASSUME_YES=0
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ for arg in "$@"; do
|
|||||||
case "$arg" in
|
case "$arg" in
|
||||||
--no-ufw) DO_UFW=0 ;;
|
--no-ufw) DO_UFW=0 ;;
|
||||||
--no-fail2ban) DO_FAIL2BAN=0 ;;
|
--no-fail2ban) DO_FAIL2BAN=0 ;;
|
||||||
--no-ssh) DO_SSH=0 ;;
|
|
||||||
--no-unattended) DO_UNATTENDED=0 ;;
|
--no-unattended) DO_UNATTENDED=0 ;;
|
||||||
-y|--yes) ASSUME_YES=1 ;;
|
-y|--yes) ASSUME_YES=1 ;;
|
||||||
-h|--help)
|
-h|--help)
|
||||||
@@ -40,7 +38,6 @@ Usage: harden.sh [options]
|
|||||||
Options:
|
Options:
|
||||||
--no-ufw Skip UFW firewall setup
|
--no-ufw Skip UFW firewall setup
|
||||||
--no-fail2ban Skip fail2ban setup
|
--no-fail2ban Skip fail2ban setup
|
||||||
--no-ssh Skip SSH key-only enforcement
|
|
||||||
--no-unattended Skip unattended-upgrades setup
|
--no-unattended Skip unattended-upgrades setup
|
||||||
-y, --yes Skip confirmation prompt
|
-y, --yes Skip confirmation prompt
|
||||||
-h, --help Show this help
|
-h, --help Show this help
|
||||||
@@ -60,17 +57,25 @@ done
|
|||||||
|
|
||||||
require_debian_ubuntu || exit 1
|
require_debian_ubuntu || exit 1
|
||||||
|
|
||||||
SSH_PORT=$(awk '/^[[:space:]]*Port[[:space:]]+/ {print $2; exit}' /etc/ssh/sshd_config 2>/dev/null)
|
# Read the effective Port from sshd_config + any drop-in under sshd_config.d/
|
||||||
|
sshd_effective_port() {
|
||||||
|
local files=(/etc/ssh/sshd_config)
|
||||||
|
if compgen -G "/etc/ssh/sshd_config.d/*.conf" >/dev/null; then
|
||||||
|
files+=(/etc/ssh/sshd_config.d/*.conf)
|
||||||
|
fi
|
||||||
|
awk '/^[[:space:]]*Port[[:space:]]+/ {print $2}' "${files[@]}" 2>/dev/null | tail -1
|
||||||
|
}
|
||||||
|
|
||||||
|
SSH_PORT=$(sshd_effective_port)
|
||||||
SSH_PORT=${SSH_PORT:-22}
|
SSH_PORT=${SSH_PORT:-22}
|
||||||
|
|
||||||
echo -e "${WHITE}${BOLD}LXS Server Hardening${NC}"
|
echo -e "${WHITE}${BOLD}LXS Server Hardening${NC}"
|
||||||
echo -e "${GRAY}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
show_separator
|
||||||
echo "The following actions will be applied to this server:"
|
echo "The following actions will be applied to this server:"
|
||||||
[ $DO_UFW -eq 1 ] && echo " • UFW firewall: default deny incoming, allow SSH on port ${SSH_PORT}"
|
[ $DO_UFW -eq 1 ] && echo " • UFW firewall: default deny incoming, allow SSH on port ${SSH_PORT}"
|
||||||
[ $DO_FAIL2BAN -eq 1 ] && echo " • fail2ban: enable sshd jail (bantime 1h, maxretry 5)"
|
[ $DO_FAIL2BAN -eq 1 ] && echo " • fail2ban: enable sshd jail (bantime 1h, maxretry 5)"
|
||||||
[ $DO_SSH -eq 1 ] && echo " • SSH: disable password auth (key-only), prohibit-password root login"
|
|
||||||
[ $DO_UNATTENDED -eq 1 ] && echo " • unattended-upgrades: enable automatic security updates"
|
[ $DO_UNATTENDED -eq 1 ] && echo " • unattended-upgrades: enable automatic security updates"
|
||||||
echo -e "${GRAY}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
show_separator
|
||||||
|
|
||||||
if [ $ASSUME_YES -ne 1 ]; then
|
if [ $ASSUME_YES -ne 1 ]; then
|
||||||
read -r -p "Proceed? [y/N] " reply
|
read -r -p "Proceed? [y/N] " reply
|
||||||
@@ -80,7 +85,8 @@ if [ $ASSUME_YES -ne 1 ]; then
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
apt_noninteractive
|
||||||
|
wait_for_apt || exit 1
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
# UFW
|
# UFW
|
||||||
@@ -121,55 +127,6 @@ EOF
|
|||||||
ok "fail2ban enabled (sshd jail active)"
|
ok "fail2ban enabled (sshd jail active)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
# SSH key-only
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
has_authorized_key() {
|
|
||||||
local user_home
|
|
||||||
for user_home in /root "/home/${SUDO_USER:-}"; do
|
|
||||||
[ -z "$user_home" ] && continue
|
|
||||||
[ "$user_home" = "/home/" ] && continue
|
|
||||||
if [ -s "${user_home}/.ssh/authorized_keys" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_ssh() {
|
|
||||||
info "Enforcing SSH key-only authentication..."
|
|
||||||
if ! has_authorized_key; then
|
|
||||||
err "No authorized_keys found for root or ${SUDO_USER:-current user}."
|
|
||||||
err "Refusing to disable password auth — you would lock yourself out."
|
|
||||||
err "Add a public key first, then re-run: harden.sh --no-ufw --no-fail2ban --no-unattended"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local cfg=/etc/ssh/sshd_config
|
|
||||||
cp -n "$cfg" "${cfg}.lxs-backup"
|
|
||||||
|
|
||||||
sed -i -E \
|
|
||||||
-e 's/^[#[:space:]]*PasswordAuthentication[[:space:]]+.*/PasswordAuthentication no/' \
|
|
||||||
-e 's/^[#[:space:]]*ChallengeResponseAuthentication[[:space:]]+.*/ChallengeResponseAuthentication no/' \
|
|
||||||
-e 's/^[#[:space:]]*KbdInteractiveAuthentication[[:space:]]+.*/KbdInteractiveAuthentication no/' \
|
|
||||||
-e 's/^[#[:space:]]*PermitRootLogin[[:space:]]+.*/PermitRootLogin prohibit-password/' \
|
|
||||||
"$cfg"
|
|
||||||
|
|
||||||
grep -q '^PasswordAuthentication ' "$cfg" || echo 'PasswordAuthentication no' >> "$cfg"
|
|
||||||
grep -q '^PermitRootLogin ' "$cfg" || echo 'PermitRootLogin prohibit-password' >> "$cfg"
|
|
||||||
|
|
||||||
if sshd -t 2>/tmp/lxs_sshd_test; then
|
|
||||||
systemctl reload ssh 2>/dev/null || systemctl reload sshd
|
|
||||||
ok "SSH configured for key-only auth (backup: ${cfg}.lxs-backup)"
|
|
||||||
else
|
|
||||||
err "sshd config test failed; restoring backup."
|
|
||||||
cp "${cfg}.lxs-backup" "$cfg"
|
|
||||||
cat /tmp/lxs_sshd_test >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
# unattended-upgrades
|
# unattended-upgrades
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -192,7 +149,6 @@ EOF
|
|||||||
|
|
||||||
[ $DO_UFW -eq 1 ] && setup_ufw
|
[ $DO_UFW -eq 1 ] && setup_ufw
|
||||||
[ $DO_FAIL2BAN -eq 1 ] && setup_fail2ban
|
[ $DO_FAIL2BAN -eq 1 ] && setup_fail2ban
|
||||||
[ $DO_SSH -eq 1 ] && setup_ssh || true
|
|
||||||
[ $DO_UNATTENDED -eq 1 ] && setup_unattended
|
[ $DO_UNATTENDED -eq 1 ] && setup_unattended
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -201,11 +157,9 @@ EOF
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${WHITE}${BOLD}Summary${NC}"
|
echo -e "${WHITE}${BOLD}Summary${NC}"
|
||||||
echo -e "${GRAY}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
show_separator
|
||||||
command -v ufw >/dev/null && echo "UFW: $(ufw status | head -1)"
|
command -v ufw >/dev/null && echo "UFW: $(ufw status | head -1)"
|
||||||
systemctl is-active fail2ban >/dev/null 2>&1 && echo "fail2ban: active" || echo "fail2ban: inactive"
|
systemctl is-active fail2ban >/dev/null 2>&1 && echo "fail2ban: active" || echo "fail2ban: inactive"
|
||||||
echo "SSH password auth: $(grep -E '^PasswordAuthentication' /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' || echo unknown)"
|
|
||||||
echo "SSH root login: $(grep -E '^PermitRootLogin' /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' || echo unknown)"
|
|
||||||
systemctl is-active unattended-upgrades >/dev/null 2>&1 && echo "unattended-upgrades: active" || echo "unattended-upgrades: inactive"
|
systemctl is-active unattended-upgrades >/dev/null 2>&1 && echo "unattended-upgrades: active" || echo "unattended-upgrades: inactive"
|
||||||
echo ""
|
echo ""
|
||||||
ok "Hardening complete."
|
ok "Hardening complete."
|
||||||
|
|||||||
Reference in New Issue
Block a user