Update Linux/SSH/secure_ssh.sh
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# Secure-SSH Setup for Modern Linux (2025-edition)
|
# Secure‑SSH Setup – Modern Linux (2025‑edition, PAM‑aware)
|
||||||
# ------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# * Generates hardened OpenSSH configuration (IPv4-only)
|
# ✦ Generates hardened OpenSSH config (IPv4‑only)
|
||||||
# * Installs/updates OpenSSH-server + creates host keys
|
# ✦ Installs / updates OpenSSH‑server + creates host keys
|
||||||
# * Creates users, adds SSH public keys, restricts login
|
# ✦ Creates users, adds SSH public keys, restricts login
|
||||||
# * Removes all legacy / deprecated directives
|
# ✦ Removes deprecated directives – fully OpenSSH ≥9.x compliant
|
||||||
# * Adds post-quantum KEX, rate-limiting, strict auth flow & root lockdown
|
# ✦ Dynamically omits UsePAM if PAM isn’t present (e.g. Alpine)
|
||||||
|
# ✦ Adds post‑quantum KEX, minimal cipher suite, strict auth‑flow,
|
||||||
|
# rate‑limiting & root lockdown
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
printf "\033c" # clear terminal
|
printf "\033c" # clear terminal
|
||||||
@@ -19,128 +21,105 @@ ED25519_KEY="/etc/ssh/ssh_host_ed25519_key"
|
|||||||
ECDSA_KEY="/etc/ssh/ssh_host_ecdsa_key"
|
ECDSA_KEY="/etc/ssh/ssh_host_ecdsa_key"
|
||||||
|
|
||||||
# ─── ANSI colors ──────────────────────────────────────────────────────────────
|
# ─── ANSI colors ──────────────────────────────────────────────────────────────
|
||||||
RED='\033[1;31m'
|
RED='\033[1;31m'; GREEN='\033[1;32m'; YELLOW='\033[1;33m'; BLUE='\033[1;34m'; WHITE='\033[1;37m'; RESET='\033[0m'
|
||||||
GREEN='\033[1;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
BLUE='\033[1;34m'
|
|
||||||
WHITE='\033[1;37m'
|
|
||||||
RESET='\033[0m'
|
|
||||||
|
|
||||||
log() { echo -e "${GREEN}$1${RESET}"; }
|
log() { echo -e "${GREEN}$1${RESET}"; }
|
||||||
warn() { echo -e "${YELLOW}$1${RESET}"; }
|
warn() { echo -e "${YELLOW}$1${RESET}"; }
|
||||||
error() { echo -e "${RED}$1${RESET}"; }
|
error() { echo -e "${RED}$1${RESET}"; }
|
||||||
|
|
||||||
# ─── Service-Manager detection (systemd │ sysvinit │ openrc) ───────────────────
|
# ─── Service‑manager detection ───────────────────────────────────────────────
|
||||||
detect_service_manager() {
|
detect_service_manager() {
|
||||||
if command -v systemctl &>/dev/null; then echo "systemd"
|
if command -v systemctl &>/dev/null; then echo systemd
|
||||||
elif command -v service &>/dev/null; then echo "sysvinit"
|
elif command -v rc-service &>/dev/null; then echo openrc # Alpine, Gentoo…
|
||||||
elif [ -x /sbin/rc-service ]; then echo "openrc"
|
elif command -v service &>/dev/null; then echo sysvinit
|
||||||
else echo "unknown"; fi
|
else echo unknown; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ─── SSH service name detection ───────────────────────────────────────────────
|
# ─── SSH service‑name detection ───────────────────────────────────────────────
|
||||||
detect_ssh_service() {
|
detect_ssh_service() {
|
||||||
local manager=$1
|
local manager=$1 services=(sshd ssh openssh-server openssh)
|
||||||
local services=(sshd ssh openssh-server openssh)
|
|
||||||
case $manager in
|
case $manager in
|
||||||
systemd) for s in "${services[@]}"; do systemctl list-units --type=service | grep -q $s && echo $s && return; done ;;
|
systemd)
|
||||||
sysvinit) for s in "${services[@]}"; do service --status-all | grep -q $s && echo $s && return; done ;;
|
for s in "${services[@]}"; do systemctl list-units --type=service | grep -q "^$s" && { echo $s; return; }; done ;;
|
||||||
openrc) for s in "${services[@]}"; do rc-service -l | grep -q $s && echo $s && return; done ;;
|
openrc)
|
||||||
|
for s in "${services[@]}"; do rc-service -l | grep -q "^$s$" && { echo $s; return; }; done ;;
|
||||||
|
sysvinit)
|
||||||
|
for s in "${services[@]}"; do [ -x "/etc/init.d/$s" ] && { echo $s; return; }; done ;;
|
||||||
esac
|
esac
|
||||||
[ -x /usr/sbin/sshd ] && echo sshd || echo unknown
|
[ -x /usr/sbin/sshd ] && echo sshd || echo unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
restart_ssh_service() {
|
restart_ssh_service() {
|
||||||
local manager=$1 service=$2
|
local manager=$1 service=$2; warn "Starte SSH‑Dienst ($service) neu…"
|
||||||
warn "Starte SSH-Dienst ($service) neu…"
|
|
||||||
case $manager in
|
case $manager in
|
||||||
systemd) sudo systemctl restart "$service" && return 0 ;;
|
systemd) sudo systemctl restart "$service" && return 0 ;;
|
||||||
sysvinit) sudo service "$service" restart && return 0 ;;
|
openrc) sudo rc-service "$service" restart && return 0 ;;
|
||||||
openrc) sudo rc-service "$service" restart && return 0 ;;
|
sysvinit) sudo "/etc/init.d/$service" restart 2>/dev/null && return 0 ;;
|
||||||
esac
|
esac
|
||||||
sudo killall -HUP sshd 2>/dev/null && return 0
|
sudo killall -HUP sshd 2>/dev/null && return 0
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
install_package() {
|
install_package() {
|
||||||
local pkg=$1
|
local p=$1; warn "Installiere $p…"
|
||||||
warn "Installiere $pkg…"
|
if command -v apt &>/dev/null; then sudo apt update -qq && sudo apt install -y $p -qq
|
||||||
if command -v apt &>/dev/null; then sudo apt update -qq && sudo apt install -y $pkg -qq ;
|
elif command -v dnf &>/dev/null; then sudo dnf install -y $p &>/dev/null
|
||||||
elif command -v dnf &>/dev/null; then sudo dnf install -y $pkg &>/dev/null ;
|
elif command -v yum &>/dev/null; then sudo yum install -y $p &>/dev/null
|
||||||
elif command -v yum &>/dev/null; then sudo yum install -y $pkg &>/dev/null ;
|
elif command -v apk &>/dev/null; then sudo apk add $p &>/dev/null
|
||||||
elif command -v apk &>/dev/null; then sudo apk add $pkg &>/dev/null ;
|
elif command -v pacman &>/dev/null; then sudo pacman -Sy --noconfirm $p &>/dev/null
|
||||||
elif command -v pacman&>/dev/null; then sudo pacman -Sy --noconfirm $pkg &>/dev/null ;
|
elif command -v zypper &>/dev/null; then sudo zypper install -y $p &>/dev/null
|
||||||
elif command -v zypper&>/dev/null; then sudo zypper install -y $pkg &>/dev/null ;
|
else error "Paketmanager nicht erkannt – installiere $p manuell."; return 1; fi
|
||||||
else error "Kein unterstützter Paketmanager – installiere $pkg manuell."; return 1; fi
|
log "$p installiert."
|
||||||
log "$pkg wurde installiert."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ─── Setup prerequisites ─────────────────────────────────────────────────────
|
# ─── Preparations ─────────────────────────────────────────────────────────────
|
||||||
SERVICE_MANAGER=$(detect_service_manager); log "Service-Manager: $SERVICE_MANAGER"
|
SERVICE_MANAGER=$(detect_service_manager); log "Service‑Manager: $SERVICE_MANAGER"
|
||||||
|
|
||||||
command -v sudo &>/dev/null || { warn "sudo fehlt – Installation…"; install_package sudo; }
|
command -v sudo &>/dev/null || { warn "sudo fehlt – Installation…"; install_package sudo; }
|
||||||
command -v sshd &>/dev/null || install_package openssh-server
|
command -v sshd &>/dev/null || install_package openssh-server
|
||||||
|
|
||||||
[ -d "$SSH_CONFIG_DIR" ] || { warn "Erstelle $SSH_CONFIG_DIR"; sudo mkdir -p "$SSH_CONFIG_DIR"; }
|
[ -d "$SSH_CONFIG_DIR" ] || { warn "Erstelle $SSH_CONFIG_DIR"; sudo mkdir -p "$SSH_CONFIG_DIR"; }
|
||||||
|
|
||||||
# ─── Host keys (ed25519 & ecdsa-p384) ─────────────────────────────────────────
|
# ─── Host keys ────────────────────────────────────────────────────────────────
|
||||||
for key in "$ED25519_KEY ed25519" "$ECDSA_KEY ecdsa -b 384"; do
|
for entry in "$ED25519_KEY ed25519" "$ECDSA_KEY ecdsa -b 384"; do
|
||||||
set -- $key; file=$1; shift; args=$*;
|
set -- $entry; key=$1; shift; args=$*
|
||||||
[ -f "$file" ] || {
|
[ -f "$key" ] || { warn "Generiere Host-Key $(basename $key)…"; sudo ssh-keygen -t $args -f "$key" -N "" -q && sudo chmod 600 "$key" && log "Key erstellt."; }
|
||||||
warn "Generiere Host-Schlüssel $(basename $file)…";
|
|
||||||
sudo ssh-keygen -t $args -f "$file" -N "" -q && sudo chmod 600 "$file" && log "Schlüssel $(basename $file) erstellt.";
|
|
||||||
}
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# ─── Replace main config with include only ────────────────────────────────────
|
# ─── Replace main config with include only ────────────────────────────────────
|
||||||
warn "Setze $SSH_MAIN_CONFIG auf Include…"
|
warn "Setze $SSH_MAIN_CONFIG auf Include…"
|
||||||
echo "Include $SSH_CONFIG_DIR/*.conf" | sudo tee "$SSH_MAIN_CONFIG" >/dev/null
|
echo "Include $SSH_CONFIG_DIR/*.conf" | sudo tee "$SSH_MAIN_CONFIG" >/dev/null
|
||||||
|
|
||||||
# ─── Remove legacy configs ────────────────────────────────────────────────────
|
|
||||||
sudo rm -f $SSH_CONFIG_DIR/*
|
sudo rm -f $SSH_CONFIG_DIR/*
|
||||||
|
|
||||||
# ─── Collect allowed users + optional creation ───────────────────────────────
|
# ─── User handling ────────────────────────────────────────────────────────────
|
||||||
read -rp $'\e[1;34mWelche Benutzer dürfen sich per SSH anmelden? (Leerzeichen-getrennt): \e[0m' SSH_USERS
|
read -rp $'\e[1;34mWelche Benutzer dürfen sich per SSH anmelden? (Leerzeichen-getrennt): \e[0m' SSH_USERS
|
||||||
VALID_USERS=""
|
VALID_USERS=""
|
||||||
|
|
||||||
# distribution-specific sudo group
|
# distribution‑specific sudo group
|
||||||
declare -A distro_sudo=( [ubuntu]=sudo [debian]=sudo [raspbian]=sudo [centos]=wheel [fedora]=wheel [rhel]=wheel [almalinux]=wheel [rocky]=wheel [ol]=wheel [arch]=wheel [manjaro]=wheel [alpine]=wheel )
|
declare -A distro_sudo=( [ubuntu]=sudo [debian]=sudo [raspbian]=sudo [centos]=wheel [fedora]=wheel [rhel]=wheel [almalinux]=wheel [rocky]=wheel [ol]=wheel [arch]=wheel [manjaro]=wheel [alpine]=wheel )
|
||||||
SUDO_GROUP="sudo"
|
SUDO_GROUP=sudo; [ -f /etc/os-release ] && { . /etc/os-release; SUDO_GROUP=${distro_sudo[$ID]:-sudo}; }
|
||||||
[ -f /etc/os-release ] && { . /etc/os-release; SUDO_GROUP=${distro_sudo[$ID]:-sudo}; }
|
|
||||||
log "Benutze sudo-Gruppe: $SUDO_GROUP"
|
log "Benutze sudo-Gruppe: $SUDO_GROUP"
|
||||||
|
|
||||||
for user in $SSH_USERS; do
|
for u in $SSH_USERS; do
|
||||||
if id "$user" &>/dev/null; then
|
if id "$u" &>/dev/null; then log "Benutzer $u existiert."; VALID_USERS+="$u "; continue; fi
|
||||||
log "Benutzer $user existiert."
|
read -rp $'\e[1;34mBenutzer '"$u"$' anlegen? [y/N]: \e[0m' create; [[ $create =~ ^[Yy]$ ]] || continue
|
||||||
VALID_USERS+="$user "
|
read -rsp $'\e[1;34mPasswort für '"$u"$': \e[0m' pw; echo
|
||||||
continue
|
sudo useradd -m -s /bin/bash "$u" && echo "$u:$pw" | sudo chpasswd
|
||||||
fi
|
sudo usermod -aG "$SUDO_GROUP" "$u"
|
||||||
read -rp $'\e[1;34mBenutzer '"$user"$' existiert nicht. Anlegen? [y/N]: \e[0m' CREATE
|
sudo install -d -m700 -o "$u" -g "$u" "/home/$u/.ssh"
|
||||||
[[ $CREATE =~ ^[Yy]$ ]] || continue
|
sudo touch "/home/$u/.ssh/authorized_keys" && sudo chmod 600 "/home/$u/.ssh/authorized_keys" && sudo chown "$u":"$u" "/home/$u/.ssh/authorized_keys"
|
||||||
read -rsp $'\e[1;34mPasswort für '"$user"$': \e[0m' PW; echo
|
read -rp $'\e[1;34mSSH Public Key für '"$u"$' hinzufügen? [y/N]: \e[0m' addkey
|
||||||
sudo useradd -m -s /bin/bash "$user" && echo "$user:$PW" | sudo chpasswd
|
if [[ $addkey =~ ^[Yy]$ ]]; then read -rp $'\e[1;34mPublic Key: \e[0m' key; echo "$key" | sudo tee -a "/home/$u/.ssh/authorized_keys" >/dev/null; fi
|
||||||
sudo usermod -aG "$SUDO_GROUP" "$user"
|
VALID_USERS+="$u "
|
||||||
sudo install -d -m700 -o "$user" -g "$user" "/home/$user/.ssh"
|
|
||||||
sudo touch "/home/$user/.ssh/authorized_keys" && sudo chmod 600 "/home/$user/.ssh/authorized_keys" && sudo chown "$user":"$user" "/home/$user/.ssh/authorized_keys"
|
|
||||||
read -rp $'\e[1;34mSSH Public Key für '"$user"$' hinzufügen? [y/N]: \e[0m' ADDKEY
|
|
||||||
if [[ $ADDKEY =~ ^[Yy]$ ]]; then
|
|
||||||
read -rp $'\e[1;34mPublic Key: \e[0m' KEY
|
|
||||||
echo "$KEY" | sudo tee -a "/home/$user/.ssh/authorized_keys" >/dev/null
|
|
||||||
fi
|
|
||||||
VALID_USERS+="$user "
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# ─── Determine SFTP-server binary ─────────────────────────────────────────────
|
# ─── SFTP binary discovery ───────────────────────────────────────────────────
|
||||||
for path in /usr/lib/openssh/sftp-server /usr/libexec/openssh/sftp-server /usr/lib/ssh/sftp-server; do [ -x "$path" ] && SFTP_PATH=$path && break; done
|
for p in /usr/lib/openssh/sftp-server /usr/libexec/openssh/sftp-server /usr/lib/ssh/sftp-server; do [ -x "$p" ] && SFTP_PATH=$p && break; done
|
||||||
SFTP_PATH=${SFTP_PATH:-internal-sftp}
|
SFTP_PATH=${SFTP_PATH:-internal-sftp}; log "SFTP-Pfad: $SFTP_PATH"
|
||||||
log "SFTP-Pfad: $SFTP_PATH"
|
|
||||||
|
|
||||||
# ─── Check for PAM ────────────────────────────────────────────────────────────
|
# ─── PAM detection ────────────────────────────────────────────────────────────
|
||||||
PAM_AVAILABLE=false
|
PAM_AVAILABLE=false
|
||||||
[ -d /etc/pam.d ] && { find / -maxdepth 2 -name 'libpam.so*' -quit | grep -q libpam && PAM_AVAILABLE=true; }
|
[ -d /etc/pam.d ] && { find / -maxdepth 2 -name 'libpam.so*' -quit | grep -q libpam && PAM_AVAILABLE=true; }
|
||||||
$PAM_AVAILABLE && log "PAM verfügbar." || warn "PAM nicht gefunden – UsePAM wird deaktiviert."
|
$PAM_AVAILABLE && log "PAM verfügbar." || warn "PAM nicht gefunden – UsePAM wird ausgelassen."
|
||||||
|
|
||||||
# ─── Generate modern, hardened secure.conf ────────────────────────────────────
|
# ─── Generate secure.conf ─────────────────────────────────────────────────────
|
||||||
warn "Erstelle $SSH_CONFIG_FILE (modern hardened)…"
|
warn "Erstelle $SSH_CONFIG_FILE (modern hardened)…"
|
||||||
|
|
||||||
sudo tee "$SSH_CONFIG_FILE" >/dev/null <<EOF
|
sudo tee "$SSH_CONFIG_FILE" >/dev/null <<EOF
|
||||||
@@ -148,54 +127,47 @@ sudo tee "$SSH_CONFIG_FILE" >/dev/null <<EOF
|
|||||||
# Generated $(date -u +'%Y-%m-%dT%H:%MZ')
|
# Generated $(date -u +'%Y-%m-%dT%H:%MZ')
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
AddressFamily inet # IPv4 only
|
AddressFamily inet
|
||||||
Port 22 # default port – change if needed
|
Port 22
|
||||||
|
|
||||||
# Modern host keys only
|
|
||||||
HostKey $ED25519_KEY
|
HostKey $ED25519_KEY
|
||||||
HostKey $ECDSA_KEY
|
HostKey $ECDSA_KEY
|
||||||
|
|
||||||
# Post-quantum hybrid first
|
|
||||||
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,diffie-hellman-group14-sha256
|
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,diffie-hellman-group14-sha256
|
||||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
|
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
|
||||||
MACs hmac-sha2-512,hmac-sha2-256
|
MACs hmac-sha2-512,hmac-sha2-256
|
||||||
|
|
||||||
# Logging & rate-limits
|
|
||||||
SyslogFacility AUTHPRIV
|
SyslogFacility AUTHPRIV
|
||||||
LogLevel VERBOSE
|
LogLevel VERBOSE
|
||||||
MaxStartups 10:30:100
|
MaxStartups 10:30:100
|
||||||
|
|
||||||
# Login settings
|
|
||||||
LoginGraceTime 30s
|
LoginGraceTime 30s
|
||||||
PermitRootLogin no
|
PermitRootLogin no
|
||||||
PermitEmptyPasswords no
|
PermitEmptyPasswords no
|
||||||
IgnoreRhosts yes
|
IgnoreRhosts yes
|
||||||
StrictModes yes
|
StrictModes yes
|
||||||
|
|
||||||
# Authentication flow – publickey only (FIDO2/U2F etc.)
|
|
||||||
PubkeyAuthentication yes
|
PubkeyAuthentication yes
|
||||||
PasswordAuthentication no
|
PasswordAuthentication no
|
||||||
KbdInteractiveAuthentication no
|
KbdInteractiveAuthentication no
|
||||||
AuthenticationMethods publickey
|
AuthenticationMethods publickey
|
||||||
|
|
||||||
# PAM (if available)
|
# X11 & SFTP
|
||||||
UsePAM $($PAM_AVAILABLE && echo yes || echo no)
|
|
||||||
PrintMotd no
|
|
||||||
|
|
||||||
# X11 forwarding disabled
|
|
||||||
X11Forwarding no
|
X11Forwarding no
|
||||||
|
|
||||||
# SFTP subsystem
|
|
||||||
Subsystem sftp $SFTP_PATH
|
Subsystem sftp $SFTP_PATH
|
||||||
|
|
||||||
# Keepalive
|
|
||||||
ClientAliveInterval 300
|
ClientAliveInterval 300
|
||||||
ClientAliveCountMax 2
|
ClientAliveCountMax 2
|
||||||
|
|
||||||
AllowUsers ${VALID_USERS%% } # strip trailing space
|
AllowUsers ${VALID_USERS%% }
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
log "Secure-Config geschrieben."
|
# Append PAM directives only if available
|
||||||
|
if $PAM_AVAILABLE; then
|
||||||
|
printf '\nUsePAM yes\nPrintMotd no\n' | sudo tee -a "$SSH_CONFIG_FILE" >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Secure‑Config geschrieben."
|
||||||
|
|
||||||
# ─── Validate & restart SSH ──────────────────────────────────────────────────
|
# ─── Validate & restart SSH ──────────────────────────────────────────────────
|
||||||
[ -d /run/sshd ] || { sudo mkdir -p /run/sshd; sudo chmod 0755 /run/sshd; }
|
[ -d /run/sshd ] || { sudo mkdir -p /run/sshd; sudo chmod 0755 /run/sshd; }
|
||||||
|
|||||||
Reference in New Issue
Block a user