Update Linux/SSH/secure_ssh.sh
This commit is contained in:
@@ -1,15 +1,24 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
printf "\033c"
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Secure-SSH Setup for Modern Linux (2025-edition)
|
||||||
|
# ------------------------------------------------
|
||||||
|
# * Generates hardened OpenSSH configuration (IPv4-only)
|
||||||
|
# * Installs/updates OpenSSH-server + creates host keys
|
||||||
|
# * Creates users, adds SSH public keys, restricts login
|
||||||
|
# * Removes all legacy / deprecated directives
|
||||||
|
# * Adds post-quantum KEX, rate-limiting, strict auth flow & root lockdown
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
printf "\033c" # clear terminal
|
||||||
|
|
||||||
SSH_CONFIG_DIR="/etc/ssh/sshd_config.d"
|
SSH_CONFIG_DIR="/etc/ssh/sshd_config.d"
|
||||||
SSH_MAIN_CONFIG="/etc/ssh/sshd_config"
|
SSH_MAIN_CONFIG="/etc/ssh/sshd_config"
|
||||||
SSH_CONFIG_FILE="$SSH_CONFIG_DIR/secure.conf"
|
SSH_CONFIG_FILE="$SSH_CONFIG_DIR/secure.conf"
|
||||||
ED25519_KEY="/etc/ssh/ssh_host_ed25519_key"
|
ED25519_KEY="/etc/ssh/ssh_host_ed25519_key"
|
||||||
ECDSA_KEY="/etc/ssh/ssh_host_ecdsa_key"
|
ECDSA_KEY="/etc/ssh/ssh_host_ecdsa_key"
|
||||||
RSA_KEY="/etc/ssh/ssh_host_rsa_key" # Hinzugefügt für Kompatibilität mit älteren Clients
|
|
||||||
|
|
||||||
# Farben für bessere Lesbarkeit
|
# ─── ANSI colors ──────────────────────────────────────────────────────────────
|
||||||
RED='\033[1;31m'
|
RED='\033[1;31m'
|
||||||
GREEN='\033[1;32m'
|
GREEN='\033[1;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
@@ -17,665 +26,185 @@ BLUE='\033[1;34m'
|
|||||||
WHITE='\033[1;37m'
|
WHITE='\033[1;37m'
|
||||||
RESET='\033[0m'
|
RESET='\033[0m'
|
||||||
|
|
||||||
# Logging-Funktionen
|
log() { echo -e "${GREEN}$1${RESET}"; }
|
||||||
log() {
|
warn() { echo -e "${YELLOW}$1${RESET}"; }
|
||||||
echo -e "${GREEN}$1${RESET}"
|
error() { echo -e "${RED}$1${RESET}"; }
|
||||||
}
|
|
||||||
|
|
||||||
warn() {
|
# ─── Service-Manager detection (systemd │ sysvinit │ openrc) ───────────────────
|
||||||
echo -e "${YELLOW}$1${RESET}"
|
|
||||||
}
|
|
||||||
|
|
||||||
error() {
|
|
||||||
echo -e "${RED}$1${RESET}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Funktion zur Erkennung des Service-Managers (systemd, init, etc.)
|
|
||||||
detect_service_manager() {
|
detect_service_manager() {
|
||||||
if command -v systemctl &> /dev/null; then
|
if command -v systemctl &>/dev/null; then echo "systemd"
|
||||||
echo "systemd"
|
elif command -v service &>/dev/null; then echo "sysvinit"
|
||||||
elif command -v service &> /dev/null; then
|
elif [ -x /sbin/rc-service ]; then echo "openrc"
|
||||||
echo "sysvinit"
|
else echo "unknown"; fi
|
||||||
elif [ -x /sbin/rc-service ]; then
|
|
||||||
echo "openrc"
|
|
||||||
else
|
|
||||||
echo "unknown"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Funktion zur Erkennung des SSH-Dienstnamens
|
# ─── SSH service name detection ───────────────────────────────────────────────
|
||||||
detect_ssh_service() {
|
detect_ssh_service() {
|
||||||
local service_manager=$1
|
local manager=$1
|
||||||
|
local services=(sshd ssh openssh-server openssh)
|
||||||
# Liste möglicher SSH-Dienstnamen
|
case $manager in
|
||||||
local ssh_services=("sshd" "ssh" "openssh-server" "openssh")
|
systemd) for s in "${services[@]}"; do systemctl list-units --type=service | grep -q $s && echo $s && return; done ;;
|
||||||
|
sysvinit) for s in "${services[@]}"; do service --status-all | grep -q $s && echo $s && return; done ;;
|
||||||
case "$service_manager" in
|
openrc) for s in "${services[@]}"; do rc-service -l | grep -q $s && echo $s && return; done ;;
|
||||||
systemd)
|
esac
|
||||||
for service in "${ssh_services[@]}"; do
|
[ -x /usr/sbin/sshd ] && echo sshd || echo unknown
|
||||||
if systemctl list-units --type=service | grep -q "$service"; then
|
|
||||||
echo "$service"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
sysvinit)
|
|
||||||
for service in "${ssh_services[@]}"; do
|
|
||||||
if service --status-all 2>&1 | grep -q "$service"; then
|
|
||||||
echo "$service"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
openrc)
|
|
||||||
for service in "${ssh_services[@]}"; do
|
|
||||||
if rc-service -l | grep -q "$service"; then
|
|
||||||
echo "$service"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Fallback: Prüfe die üblichen Verzeichnisse auf ausführbare SSHd-Dateien
|
|
||||||
if [ -x /usr/sbin/sshd ]; then
|
|
||||||
echo "sshd"
|
|
||||||
elif [ -x /usr/bin/sshd ]; then
|
|
||||||
echo "sshd"
|
|
||||||
else
|
|
||||||
echo "unknown"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Funktion zum Neustart des SSH-Dienstes
|
|
||||||
restart_ssh_service() {
|
restart_ssh_service() {
|
||||||
local service_manager=$1
|
local manager=$1 service=$2
|
||||||
local service_name=$2
|
warn "Starte SSH-Dienst ($service) neu…"
|
||||||
|
case $manager in
|
||||||
warn "Starte SSH-Dienst ($service_name) neu..."
|
systemd) sudo systemctl restart "$service" && return 0 ;;
|
||||||
|
sysvinit) sudo service "$service" restart && return 0 ;;
|
||||||
case "$service_manager" in
|
openrc) sudo rc-service "$service" restart && return 0 ;;
|
||||||
systemd)
|
esac
|
||||||
if sudo systemctl restart "$service_name"; then
|
sudo killall -HUP sshd 2>/dev/null && return 0
|
||||||
log "SSH-Dienst erfolgreich neu gestartet."
|
return 1
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
sysvinit)
|
|
||||||
if sudo service "$service_name" restart; then
|
|
||||||
log "SSH-Dienst erfolgreich neu gestartet."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
openrc)
|
|
||||||
if sudo rc-service "$service_name" restart; then
|
|
||||||
log "SSH-Dienst erfolgreich neu gestartet."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Direkter Neustart als letzter Ausweg
|
|
||||||
if sudo killall -HUP sshd 2>/dev/null; then
|
|
||||||
log "SSH-Dienst durch Signal neu gestartet."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
error "Fehler beim Neustart des SSH-Dienstes."
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Funktion zur Paketinstallation basierend auf dem Paketmanager
|
|
||||||
install_package() {
|
install_package() {
|
||||||
local package=$1
|
local pkg=$1
|
||||||
|
warn "Installiere $pkg…"
|
||||||
warn "Installiere $package..."
|
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 $pkg &>/dev/null ;
|
||||||
# Erkennung des Paketmanagers
|
elif command -v yum &>/dev/null; then sudo yum install -y $pkg &>/dev/null ;
|
||||||
if command -v apt &> /dev/null; then
|
elif command -v apk &>/dev/null; then sudo apk add $pkg &>/dev/null ;
|
||||||
# Debian/Ubuntu
|
elif command -v pacman&>/dev/null; then sudo pacman -Sy --noconfirm $pkg &>/dev/null ;
|
||||||
if sudo apt update &> /dev/null && sudo apt install -y "$package" &> /dev/null; then
|
elif command -v zypper&>/dev/null; then sudo zypper install -y $pkg &>/dev/null ;
|
||||||
log "$package wurde erfolgreich installiert."
|
else error "Kein unterstützter Paketmanager – installiere $pkg manuell."; return 1; fi
|
||||||
return 0
|
log "$pkg wurde installiert."
|
||||||
fi
|
|
||||||
elif command -v dnf &> /dev/null; then
|
|
||||||
# RHEL 8+/Fedora
|
|
||||||
if sudo dnf install -y "$package" &> /dev/null; then
|
|
||||||
log "$package wurde erfolgreich installiert."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
elif command -v yum &> /dev/null; then
|
|
||||||
# Ältere RHEL/CentOS
|
|
||||||
if sudo yum install -y "$package" &> /dev/null; then
|
|
||||||
log "$package wurde erfolgreich installiert."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
elif command -v apk &> /dev/null; then
|
|
||||||
# Alpine Linux
|
|
||||||
if sudo apk add "$package" &> /dev/null; then
|
|
||||||
log "$package wurde erfolgreich installiert."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
elif command -v pacman &> /dev/null; then
|
|
||||||
# Arch Linux
|
|
||||||
if sudo pacman -Sy --noconfirm "$package" &> /dev/null; then
|
|
||||||
log "$package wurde erfolgreich installiert."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
elif command -v zypper &> /dev/null; then
|
|
||||||
# openSUSE
|
|
||||||
if sudo zypper install -y "$package" &> /dev/null; then
|
|
||||||
log "$package wurde erfolgreich installiert."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
error "Nicht unterstütztes System. Bitte $package manuell installieren."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
error "Fehler bei der Installation von $package."
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Funktion zur Erkennung der OpenSSH-Version
|
# ─── Setup prerequisites ─────────────────────────────────────────────────────
|
||||||
detect_openssh_version() {
|
SERVICE_MANAGER=$(detect_service_manager); log "Service-Manager: $SERVICE_MANAGER"
|
||||||
if command -v ssh -V &> /dev/null; then
|
|
||||||
local version_output=$(ssh -V 2>&1)
|
|
||||||
echo "$version_output" | grep -oP 'OpenSSH_\K[0-9]+\.[0-9]+' | head -1
|
|
||||||
else
|
|
||||||
echo "unknown"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Erkennung des Service-Managers
|
command -v sudo &>/dev/null || { warn "sudo fehlt – Installation…"; install_package sudo; }
|
||||||
SERVICE_MANAGER=$(detect_service_manager)
|
command -v sshd &>/dev/null || install_package openssh-server
|
||||||
log "Erkannter Service-Manager: $SERVICE_MANAGER"
|
|
||||||
|
|
||||||
# Prüfe, ob sudo installiert ist
|
[ -d "$SSH_CONFIG_DIR" ] || { warn "Erstelle $SSH_CONFIG_DIR"; sudo mkdir -p "$SSH_CONFIG_DIR"; }
|
||||||
if ! command -v sudo &> /dev/null; then
|
|
||||||
warn "sudo ist nicht installiert. Versuche, sudo zu installieren..."
|
|
||||||
|
|
||||||
# Installiere sudo basierend auf dem erkannten Paketmanager
|
# ─── Host keys (ed25519 & ecdsa-p384) ─────────────────────────────────────────
|
||||||
if command -v apt &> /dev/null; then
|
for key in "$ED25519_KEY ed25519" "$ECDSA_KEY ecdsa -b 384"; do
|
||||||
apt update && apt install -y sudo
|
set -- $key; file=$1; shift; args=$*;
|
||||||
elif command -v dnf &> /dev/null; then
|
[ -f "$file" ] || {
|
||||||
dnf install -y sudo
|
warn "Generiere Host-Schlüssel $(basename $file)…";
|
||||||
elif command -v yum &> /dev/null; then
|
sudo ssh-keygen -t $args -f "$file" -N "" -q && sudo chmod 600 "$file" && log "Schlüssel $(basename $file) erstellt.";
|
||||||
yum install -y sudo
|
}
|
||||||
elif command -v apk &> /dev/null; then
|
done
|
||||||
apk add sudo
|
|
||||||
elif command -v pacman &> /dev/null; then
|
|
||||||
pacman -Sy --noconfirm sudo
|
|
||||||
elif command -v zypper &> /dev/null; then
|
|
||||||
zypper install -y sudo
|
|
||||||
else
|
|
||||||
error "Nicht unterstütztes System. Bitte sudo manuell installieren."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v sudo &> /dev/null; then
|
# ─── Replace main config with include only ────────────────────────────────────
|
||||||
log "sudo wurde erfolgreich installiert."
|
warn "Setze $SSH_MAIN_CONFIG auf Include…"
|
||||||
else
|
echo "Include $SSH_CONFIG_DIR/*.conf" | sudo tee "$SSH_MAIN_CONFIG" >/dev/null
|
||||||
error "Fehler bei der Installation von sudo."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "sudo ist bereits installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prüfe und installiere OpenSSH-Server
|
# ─── Remove legacy configs ────────────────────────────────────────────────────
|
||||||
if ! command -v sshd &> /dev/null; then
|
|
||||||
warn "OpenSSH-Server wird installiert..."
|
|
||||||
|
|
||||||
if command -v apt &> /dev/null; then
|
|
||||||
install_package "openssh-server"
|
|
||||||
elif command -v dnf &> /dev/null || command -v yum &> /dev/null; then
|
|
||||||
install_package "openssh-server"
|
|
||||||
elif command -v apk &> /dev/null; then
|
|
||||||
install_package "openssh"
|
|
||||||
elif command -v pacman &> /dev/null; then
|
|
||||||
install_package "openssh"
|
|
||||||
elif command -v zypper &> /dev/null; then
|
|
||||||
install_package "openssh"
|
|
||||||
else
|
|
||||||
error "Nicht unterstütztes System. Bitte OpenSSH manuell installieren."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "OpenSSH-Server ist bereits installiert."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Erkennen der OpenSSH-Version
|
|
||||||
SSH_VERSION=$(detect_openssh_version)
|
|
||||||
if [ "$SSH_VERSION" != "unknown" ]; then
|
|
||||||
log "Erkannte OpenSSH-Version: $SSH_VERSION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Stelle sicher, dass das Konfigurationsverzeichnis existiert
|
|
||||||
if [ ! -d "$SSH_CONFIG_DIR" ]; then
|
|
||||||
warn "SSH Konfigurationsverzeichnis existiert nicht. Es wird erstellt..."
|
|
||||||
sudo mkdir -p "$SSH_CONFIG_DIR"
|
|
||||||
log "SSH Konfigurationsverzeichnis erstellt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generiere fehlende Schlüssel
|
|
||||||
if [ ! -f "$ED25519_KEY" ]; then
|
|
||||||
warn "Host-Schlüssel fehlt. Generiere ssh_host_ed25519_key..."
|
|
||||||
if sudo ssh-keygen -t ed25519 -f "$ED25519_KEY" -N "" &> /dev/null; then
|
|
||||||
sudo chown root:root "$ED25519_KEY"
|
|
||||||
sudo chmod 600 "$ED25519_KEY"
|
|
||||||
log "Host-Schlüssel ssh_host_ed25519_key wurde erstellt."
|
|
||||||
else
|
|
||||||
error "Fehler beim Erstellen des Host-Schlüssels."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "Host-Schlüssel ssh_host_ed25519_key ist bereits vorhanden."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$ECDSA_KEY" ]; then
|
|
||||||
warn "Host-Schlüssel fehlt. Generiere ssh_host_ecdsa_key..."
|
|
||||||
if sudo ssh-keygen -t ecdsa -b 384 -f "$ECDSA_KEY" -N "" &> /dev/null; then
|
|
||||||
sudo chown root:root "$ECDSA_KEY"
|
|
||||||
sudo chmod 600 "$ECDSA_KEY"
|
|
||||||
log "Host-Schlüssel ssh_host_ecdsa_key wurde erstellt."
|
|
||||||
else
|
|
||||||
error "Fehler beim Erstellen des Host-Schlüssels."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "Host-Schlüssel ssh_host_ecdsa_key ist bereits vorhanden."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prüfe auf RSA-Schlüssel für die Kompatibilität
|
|
||||||
if [ ! -f "$RSA_KEY" ]; then
|
|
||||||
warn "Host-Schlüssel fehlt. Generiere ssh_host_rsa_key für Kompatibilität..."
|
|
||||||
if sudo ssh-keygen -t rsa -b 4096 -f "$RSA_KEY" -N "" &> /dev/null; then
|
|
||||||
sudo chown root:root "$RSA_KEY"
|
|
||||||
sudo chmod 600 "$RSA_KEY"
|
|
||||||
log "Host-Schlüssel ssh_host_rsa_key wurde erstellt."
|
|
||||||
else
|
|
||||||
error "Fehler beim Erstellen des Host-Schlüssels."
|
|
||||||
# Kein Exit hier, da RSA optional ist
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "Host-Schlüssel ssh_host_rsa_key ist bereits vorhanden."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Konfiguriere SSH für die Include-Direktive
|
|
||||||
warn "Setze /etc/ssh/sshd_config zurück auf Include-Direktive..."
|
|
||||||
sudo tee "$SSH_MAIN_CONFIG" > /dev/null <<EOF
|
|
||||||
Include $SSH_CONFIG_DIR/*.conf
|
|
||||||
EOF
|
|
||||||
log "/etc/ssh/sshd_config enthält jetzt nur noch die Include-Direktive."
|
|
||||||
|
|
||||||
# Bereinige alte Konfigurationsdateien im SSH_CONFIG_DIR
|
|
||||||
sudo rm -f $SSH_CONFIG_DIR/*
|
sudo rm -f $SSH_CONFIG_DIR/*
|
||||||
|
|
||||||
echo -en "${BLUE}Welche Benutzer dürfen sich per SSH anmelden? (Benutzer durch ${WHITE}Leerzeichen${BLUE} trennen): ${RESET}"
|
# ─── Collect allowed users + optional creation ───────────────────────────────
|
||||||
read SSH_USERS
|
read -rp $'\e[1;34mWelche Benutzer dürfen sich per SSH anmelden? (Leerzeichen-getrennt): \e[0m' SSH_USERS
|
||||||
|
|
||||||
VALID_USERS=""
|
VALID_USERS=""
|
||||||
|
|
||||||
# Bestimme die sudo-Gruppe abhängig von der Distribution
|
# distribution-specific sudo group
|
||||||
SUDO_GROUP="sudo" # Standard-Wert
|
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"
|
||||||
# Erkennung der korrekten sudo-Gruppe
|
[ -f /etc/os-release ] && { . /etc/os-release; SUDO_GROUP=${distro_sudo[$ID]:-sudo}; }
|
||||||
if [ -f /etc/os-release ]; then
|
|
||||||
. /etc/os-release
|
|
||||||
case "$ID" in
|
|
||||||
ubuntu|debian|raspbian)
|
|
||||||
SUDO_GROUP="sudo"
|
|
||||||
;;
|
|
||||||
centos|fedora|rhel|almalinux|rocky|ol)
|
|
||||||
SUDO_GROUP="wheel"
|
|
||||||
;;
|
|
||||||
arch|manjaro)
|
|
||||||
SUDO_GROUP="wheel"
|
|
||||||
;;
|
|
||||||
alpine)
|
|
||||||
SUDO_GROUP="wheel"
|
|
||||||
;;
|
|
||||||
opensuse*|sles)
|
|
||||||
SUDO_GROUP="wheel"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Prüfe, ob die Gruppe wheel existiert
|
|
||||||
if getent group wheel &>/dev/null; then
|
|
||||||
SUDO_GROUP="wheel"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "Benutze sudo-Gruppe: $SUDO_GROUP"
|
log "Benutze sudo-Gruppe: $SUDO_GROUP"
|
||||||
|
|
||||||
for user in $SSH_USERS; do
|
for user in $SSH_USERS; do
|
||||||
if id "$user" &>/dev/null; then
|
if id "$user" &>/dev/null; then
|
||||||
log "Benutzer $user existiert."
|
log "Benutzer $user existiert."
|
||||||
VALID_USERS+="$user "
|
VALID_USERS+="$user "
|
||||||
else
|
continue
|
||||||
echo -en "${BLUE}Benutzer ${WHITE}$user${BLUE} existiert nicht. Soll dieser erstellt werden? ${YELLOW}[${WHITE}y/n${YELLOW}]${RESET}: "
|
fi
|
||||||
read CREATE_USER
|
read -rp $'\e[1;34mBenutzer '"$user"$' existiert nicht. Anlegen? [y/N]: \e[0m' CREATE
|
||||||
if [ "$CREATE_USER" == "y" ]; then
|
[[ $CREATE =~ ^[Yy]$ ]] || continue
|
||||||
echo -en "${BLUE}Gib ein Passwort für ${WHITE}$user${BLUE} ein: ${RESET}"
|
read -rsp $'\e[1;34mPasswort für '"$user"$': \e[0m' PW; echo
|
||||||
read -s USER_PASSWORD
|
sudo useradd -m -s /bin/bash "$user" && echo "$user:$PW" | sudo chpasswd
|
||||||
echo
|
sudo usermod -aG "$SUDO_GROUP" "$user"
|
||||||
|
sudo install -d -m700 -o "$user" -g "$user" "/home/$user/.ssh"
|
||||||
# Benutzer erstellen basierend auf der Distribution
|
sudo touch "/home/$user/.ssh/authorized_keys" && sudo chmod 600 "/home/$user/.ssh/authorized_keys" && sudo chown "$user":"$user" "/home/$user/.ssh/authorized_keys"
|
||||||
if command -v useradd &> /dev/null; then
|
read -rp $'\e[1;34mSSH Public Key für '"$user"$' hinzufügen? [y/N]: \e[0m' ADDKEY
|
||||||
# Linux-Standard
|
if [[ $ADDKEY =~ ^[Yy]$ ]]; then
|
||||||
if sudo useradd -m -s /bin/bash "$user" &>/dev/null && echo "$user:$USER_PASSWORD" | sudo chpasswd &>/dev/null; then
|
read -rp $'\e[1;34mPublic Key: \e[0m' KEY
|
||||||
log "Benutzer $user wurde erstellt."
|
echo "$KEY" | sudo tee -a "/home/$user/.ssh/authorized_keys" >/dev/null
|
||||||
# Füge Benutzer zur sudo-Gruppe hinzu
|
fi
|
||||||
if getent group "$SUDO_GROUP" &>/dev/null; then
|
VALID_USERS+="$user "
|
||||||
sudo usermod -aG "$SUDO_GROUP" "$user" &>/dev/null
|
|
||||||
log "Benutzer $user wurde zur $SUDO_GROUP-Gruppe hinzugefügt."
|
|
||||||
else
|
|
||||||
warn "Gruppe $SUDO_GROUP existiert nicht. Der Benutzer erhält keine sudo-Rechte."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
error "Fehler bei der Erstellung von Benutzer $user."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
elif command -v adduser &> /dev/null; then
|
|
||||||
# Debian/Ubuntu-spezifisch
|
|
||||||
if sudo adduser --disabled-password --gecos "" "$user" &>/dev/null && echo "$user:$USER_PASSWORD" | sudo chpasswd &>/dev/null; then
|
|
||||||
log "Benutzer $user wurde erstellt."
|
|
||||||
# Füge Benutzer zur sudo-Gruppe hinzu
|
|
||||||
if getent group "$SUDO_GROUP" &>/dev/null; then
|
|
||||||
sudo usermod -aG "$SUDO_GROUP" "$user" &>/dev/null
|
|
||||||
log "Benutzer $user wurde zur $SUDO_GROUP-Gruppe hinzugefügt."
|
|
||||||
else
|
|
||||||
warn "Gruppe $SUDO_GROUP existiert nicht. Der Benutzer erhält keine sudo-Rechte."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
error "Fehler bei der Erstellung von Benutzer $user."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
error "Kein Befehl zum Erstellen von Benutzern gefunden."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# SSH-Verzeichnis und Schlüsseldatei erstellen
|
|
||||||
sudo mkdir -p "/home/$user/.ssh"
|
|
||||||
sudo touch "/home/$user/.ssh/authorized_keys"
|
|
||||||
sudo chown -R "$user":"$user" "/home/$user/.ssh"
|
|
||||||
sudo chmod 700 "/home/$user/.ssh"
|
|
||||||
sudo chmod 600 "/home/$user/.ssh/authorized_keys"
|
|
||||||
|
|
||||||
echo -en "${BLUE}Möchtest du einen SSH Public Key für ${WHITE}$user${BLUE} hinzufügen? ${YELLOW}[${WHITE}y/n${YELLOW}]${RESET}: "
|
|
||||||
read ADD_KEY
|
|
||||||
if [ "$ADD_KEY" == "y" ]; then
|
|
||||||
echo -en "${BLUE}Füge den SSH Public Key hier ein: ${RESET}"
|
|
||||||
read SSH_KEY
|
|
||||||
echo "$SSH_KEY" | sudo tee -a "/home/$user/.ssh/authorized_keys" &> /dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
VALID_USERS+="$user "
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# Erstelle SSH-Konfigurationsdatei
|
# ─── Determine SFTP-server binary ─────────────────────────────────────────────
|
||||||
warn "Erstelle SSH-Konfigurationsdatei..."
|
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
|
||||||
|
SFTP_PATH=${SFTP_PATH:-internal-sftp}
|
||||||
|
log "SFTP-Pfad: $SFTP_PATH"
|
||||||
|
|
||||||
# Bestimme den Pfad zum SFTP-Server basierend auf der Distribution
|
# ─── Check for PAM ────────────────────────────────────────────────────────────
|
||||||
SFTP_PATH="/usr/lib/openssh/sftp-server" # Standard für Debian/Ubuntu
|
|
||||||
|
|
||||||
# Finde den tatsächlichen Pfad des SFTP-Servers
|
|
||||||
if [ -f /usr/lib/openssh/sftp-server ]; then
|
|
||||||
SFTP_PATH="/usr/lib/openssh/sftp-server"
|
|
||||||
elif [ -f /usr/libexec/openssh/sftp-server ]; then
|
|
||||||
SFTP_PATH="/usr/libexec/openssh/sftp-server"
|
|
||||||
elif [ -f /usr/lib/ssh/sftp-server ]; then
|
|
||||||
SFTP_PATH="/usr/lib/ssh/sftp-server"
|
|
||||||
else
|
|
||||||
# Suche nach der sftp-server-Binärdatei im System
|
|
||||||
SFTP_PATH_FIND=$(find /usr -name sftp-server -type f 2>/dev/null | head -1)
|
|
||||||
if [ -n "$SFTP_PATH_FIND" ]; then
|
|
||||||
SFTP_PATH="$SFTP_PATH_FIND"
|
|
||||||
else
|
|
||||||
SFTP_PATH="internal-sftp" # Fallback auf internen SFTP-Server
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "Verwende SFTP-Server-Pfad: $SFTP_PATH"
|
|
||||||
|
|
||||||
# Prüfe, ob PAM verfügbar ist
|
|
||||||
PAM_AVAILABLE=false
|
PAM_AVAILABLE=false
|
||||||
if [ -d "/etc/pam.d" ] && (command -v pam &>/dev/null || ls /lib*/*/libpam.so* &>/dev/null || ls /lib/*/libpam.so* &>/dev/null || ls /usr/lib*/*/libpam.so* &>/dev/null || ls /usr/lib/*/libpam.so* &>/dev/null); then
|
[ -d /etc/pam.d ] && { find / -maxdepth 2 -name 'libpam.so*' -quit | grep -q libpam && PAM_AVAILABLE=true; }
|
||||||
PAM_AVAILABLE=true
|
$PAM_AVAILABLE && log "PAM verfügbar." || warn "PAM nicht gefunden – UsePAM wird deaktiviert."
|
||||||
log "PAM ist auf diesem System verfügbar."
|
|
||||||
else
|
|
||||||
warn "PAM scheint auf diesem System nicht verfügbar zu sein."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prüfe, ob ModuliFile existiert und ob es ersetzt werden soll
|
# ─── Generate modern, hardened secure.conf ────────────────────────────────────
|
||||||
MODULI_FILE="/etc/ssh/moduli"
|
warn "Erstelle $SSH_CONFIG_FILE (modern hardened)…"
|
||||||
if [ -f "$MODULI_FILE" ]; then
|
|
||||||
# Prüfe, ob eine Optimierung der moduli-Datei erwünscht ist
|
|
||||||
echo -en "${BLUE}Möchtest du die Diffie-Hellman-Parameter optimieren? Dies kann die SSH-Sicherheit erhöhen, benötigt aber etwas Zeit. ${YELLOW}[${WHITE}y/n${YELLOW}]${RESET}: "
|
|
||||||
read OPTIMIZE_MODULI
|
|
||||||
if [ "$OPTIMIZE_MODULI" == "y" ]; then
|
|
||||||
warn "Optimiere Diffie-Hellman-Parameter (Dies kann einige Minuten dauern)..."
|
|
||||||
# Sichere die Original-Datei
|
|
||||||
sudo cp "$MODULI_FILE" "${MODULI_FILE}.bak"
|
|
||||||
# Behalte nur starke Moduli (3072 Bit oder mehr)
|
|
||||||
sudo awk '$5 >= 3071' "$MODULI_FILE" | sudo tee "${MODULI_FILE}.tmp" > /dev/null
|
|
||||||
sudo mv "${MODULI_FILE}.tmp" "$MODULI_FILE"
|
|
||||||
log "Diffie-Hellman-Parameter wurden optimiert."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Frage, ob ein nicht-Standard-Port genutzt werden soll (besserer Schutz vor automatisierten Scans)
|
sudo tee "$SSH_CONFIG_FILE" >/dev/null <<EOF
|
||||||
echo -en "${BLUE}Möchtest du einen non-Standard SSH-Port verwenden? (Standard ist 22) ${YELLOW}[${WHITE}y/n${YELLOW}]${RESET}: "
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
read USE_CUSTOM_PORT
|
# Generated $(date -u +'%Y-%m-%dT%H:%MZ')
|
||||||
SSH_PORT="22"
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
if [ "$USE_CUSTOM_PORT" == "y" ]; then
|
|
||||||
echo -en "${BLUE}Gib einen alternativen SSH-Port (1024-65535) ein: ${RESET}"
|
|
||||||
read CUSTOM_PORT
|
|
||||||
if [[ "$CUSTOM_PORT" =~ ^[0-9]+$ ]] && [ "$CUSTOM_PORT" -gt 1023 ] && [ "$CUSTOM_PORT" -lt 65536 ]; then
|
|
||||||
SSH_PORT="$CUSTOM_PORT"
|
|
||||||
log "Verwende benutzerdefinierten SSH-Port: $SSH_PORT"
|
|
||||||
else
|
|
||||||
warn "Ungültiger Port. Verwende Standard-Port 22."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Frage nach Fail2Ban-Installation
|
AddressFamily inet # IPv4 only
|
||||||
echo -en "${BLUE}Möchtest du Fail2Ban installieren, um dich vor Brute-Force-Angriffen zu schützen? ${YELLOW}[${WHITE}y/n${YELLOW}]${RESET}: "
|
Port 22 # default port – change if needed
|
||||||
read INSTALL_FAIL2BAN
|
|
||||||
if [ "$INSTALL_FAIL2BAN" == "y" ]; then
|
|
||||||
warn "Installiere Fail2Ban..."
|
|
||||||
install_package "fail2ban"
|
|
||||||
|
|
||||||
# Konfiguriere Fail2Ban für SSH
|
# Modern host keys only
|
||||||
if [ -d "/etc/fail2ban" ]; then
|
HostKey $ED25519_KEY
|
||||||
# Erstelle eine SSH-Jail-Konfiguration
|
HostKey $ECDSA_KEY
|
||||||
sudo mkdir -p /etc/fail2ban/jail.d
|
|
||||||
sudo tee /etc/fail2ban/jail.d/sshd.conf > /dev/null <<EOF
|
|
||||||
[sshd]
|
|
||||||
enabled = true
|
|
||||||
port = $SSH_PORT
|
|
||||||
filter = sshd
|
|
||||||
logpath = /var/log/auth.log
|
|
||||||
maxretry = 5
|
|
||||||
findtime = 600
|
|
||||||
bantime = 3600
|
|
||||||
EOF
|
|
||||||
log "Fail2Ban wurde für SSH konfiguriert."
|
|
||||||
|
|
||||||
# Starte Fail2Ban neu
|
# Post-quantum hybrid first
|
||||||
case "$SERVICE_MANAGER" in
|
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,diffie-hellman-group14-sha256
|
||||||
systemd)
|
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
|
||||||
sudo systemctl enable fail2ban
|
MACs hmac-sha2-512,hmac-sha2-256
|
||||||
sudo systemctl restart fail2ban
|
|
||||||
;;
|
|
||||||
sysvinit)
|
|
||||||
sudo service fail2ban restart
|
|
||||||
;;
|
|
||||||
openrc)
|
|
||||||
sudo rc-service fail2ban restart
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
else
|
|
||||||
error "Fail2Ban-Konfigurationsverzeichnis wurde nicht gefunden."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Erstelle die Konfigurationsdatei mit dynamischen Einstellungen je nach System
|
# Logging & rate-limits
|
||||||
sudo tee "$SSH_CONFIG_FILE" > /dev/null <<EOL
|
|
||||||
# SSHD Config - maximale Sicherheit (IPv4)
|
|
||||||
|
|
||||||
# Nur IPv4-Verbindungen
|
|
||||||
AddressFamily inet
|
|
||||||
|
|
||||||
# SSH-Port
|
|
||||||
Port $SSH_PORT
|
|
||||||
|
|
||||||
# Nur Protokoll 2 zulassen (Protokoll 1 ist unsicher)
|
|
||||||
Protocol 2
|
|
||||||
|
|
||||||
# Host-Schlüssel (nur moderne Schlüssel)
|
|
||||||
HostKey /etc/ssh/ssh_host_ed25519_key
|
|
||||||
HostKey /etc/ssh/ssh_host_ecdsa_key
|
|
||||||
HostKey /etc/ssh/ssh_host_rsa_key # Für Kompatibilität mit älteren Clients
|
|
||||||
|
|
||||||
# Sichere Key Exchange-Algorithmen (Kex)
|
|
||||||
KexAlgorithms curve25519-sha256@libssh.org,curve25519-sha256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256
|
|
||||||
|
|
||||||
# Sichere Ciphers (Chiffren)
|
|
||||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
|
||||||
|
|
||||||
# Sichere MAC-Algorithmen
|
|
||||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
|
|
||||||
|
|
||||||
# Log-Einstellungen
|
|
||||||
SyslogFacility AUTHPRIV
|
SyslogFacility AUTHPRIV
|
||||||
LogLevel VERBOSE
|
LogLevel VERBOSE
|
||||||
|
MaxStartups 10:30:100
|
||||||
|
|
||||||
# Maximale Login-Wartezeit
|
# Login settings
|
||||||
LoginGraceTime 30s
|
LoginGraceTime 30s
|
||||||
|
|
||||||
# Root-Login verbieten
|
|
||||||
PermitRootLogin no
|
PermitRootLogin no
|
||||||
|
PermitEmptyPasswords no
|
||||||
|
IgnoreRhosts yes
|
||||||
|
StrictModes yes
|
||||||
|
|
||||||
# Nur Schlüssel-Authentifizierung erlauben
|
# Authentication flow – publickey only (FIDO2/U2F etc.)
|
||||||
PubkeyAuthentication yes
|
PubkeyAuthentication yes
|
||||||
PasswordAuthentication no
|
PasswordAuthentication no
|
||||||
ChallengeResponseAuthentication no
|
KbdInteractiveAuthentication no
|
||||||
|
AuthenticationMethods publickey
|
||||||
|
|
||||||
# Benutzer ohne Passwort verbieten
|
# PAM (if available)
|
||||||
PermitEmptyPasswords no
|
UsePAM $($PAM_AVAILABLE && echo yes || echo no)
|
||||||
|
|
||||||
# Maximale Authentifizierungsversuche
|
|
||||||
MaxAuthTries 3
|
|
||||||
|
|
||||||
# Maximale Anzahl simultaner Sessions
|
|
||||||
MaxSessions 5
|
|
||||||
|
|
||||||
# Sicherheitsrelevante Zeitbeschränkungen
|
|
||||||
MaxStartups 10:30:60
|
|
||||||
EOL
|
|
||||||
|
|
||||||
# Füge PAM-Konfiguration hinzu, wenn verfügbar
|
|
||||||
if $PAM_AVAILABLE; then
|
|
||||||
cat <<EOL | sudo tee -a "$SSH_CONFIG_FILE" > /dev/null
|
|
||||||
|
|
||||||
# PAM für zusätzliche Sicherheitsmodule verwenden
|
|
||||||
UsePAM yes
|
|
||||||
|
|
||||||
# Kein MOTD durch SSH (über PAM regeln)
|
|
||||||
PrintMotd no
|
PrintMotd no
|
||||||
EOL
|
|
||||||
else
|
|
||||||
cat <<EOL | sudo tee -a "$SSH_CONFIG_FILE" > /dev/null
|
|
||||||
|
|
||||||
# PAM ist nicht verfügbar auf diesem System
|
# X11 forwarding disabled
|
||||||
# Basic-Auth-Einstellungen
|
|
||||||
StrictModes yes
|
|
||||||
EOL
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Füge den Rest der Konfiguration hinzu
|
|
||||||
cat <<EOL | sudo tee -a "$SSH_CONFIG_FILE" > /dev/null
|
|
||||||
|
|
||||||
# X11-Weiterleitung deaktivieren (falls nicht benötigt)
|
|
||||||
X11Forwarding no
|
X11Forwarding no
|
||||||
|
|
||||||
# TCP-Weiterleitung deaktivieren
|
# SFTP subsystem
|
||||||
AllowTcpForwarding no
|
|
||||||
GatewayPorts no
|
|
||||||
|
|
||||||
# Verhindern von Tunnel-Nutzung
|
|
||||||
PermitTunnel no
|
|
||||||
|
|
||||||
# Verhindern von SSH-Agent-Forwarding (wenn nicht benötigt)
|
|
||||||
AllowAgentForwarding no
|
|
||||||
|
|
||||||
# Banner-Anzeige deaktivieren (keine Systeminfo für potenzielle Angreifer)
|
|
||||||
Banner none
|
|
||||||
|
|
||||||
# Deaktiviere .rhosts Dateien
|
|
||||||
IgnoreRhosts yes
|
|
||||||
HostbasedAuthentication no
|
|
||||||
|
|
||||||
# Pfad zur Authorized Keys-Datei
|
|
||||||
AuthorizedKeysFile .ssh/authorized_keys
|
|
||||||
|
|
||||||
# SFTP-Subsystem
|
|
||||||
Subsystem sftp $SFTP_PATH
|
Subsystem sftp $SFTP_PATH
|
||||||
|
|
||||||
# Inaktive SSH-Sitzungen überwachen und beenden
|
# Keepalive
|
||||||
ClientAliveInterval 300
|
ClientAliveInterval 300
|
||||||
ClientAliveCountMax 2
|
ClientAliveCountMax 2
|
||||||
|
|
||||||
# Kompression deaktivieren (Schutz vor CRIME/BREACH-ähnlichen Angriffen)
|
AllowUsers ${VALID_USERS%% } # strip trailing space
|
||||||
Compression no
|
EOF
|
||||||
|
|
||||||
# Erlaubte Benutzer festlegen
|
log "Secure-Config geschrieben."
|
||||||
AllowUsers $VALID_USERS
|
|
||||||
|
|
||||||
# Trendchecking aktivieren für verdächtige Aktivitäten
|
# ─── Validate & restart SSH ──────────────────────────────────────────────────
|
||||||
DebianBanner no
|
[ -d /run/sshd ] || { sudo mkdir -p /run/sshd; sudo chmod 0755 /run/sshd; }
|
||||||
|
|
||||||
# Anzeige der letzten Anmeldung deaktivieren
|
sudo sshd -t || { error "SSH-Konfiguration ungültig!"; exit 1; }
|
||||||
PrintLastLog no
|
|
||||||
EOL
|
|
||||||
|
|
||||||
log "Prüfe SSH-Konfiguration..."
|
SSH_SERVICE=$(detect_ssh_service "$SERVICE_MANAGER")
|
||||||
|
[ "$SSH_SERVICE" = unknown ] && { error "SSH-Dienst nicht gefunden."; exit 1; }
|
||||||
|
|
||||||
# Stelle sicher, dass das Privilege-Separation-Verzeichnis existiert
|
restart_ssh_service "$SERVICE_MANAGER" "$SSH_SERVICE" || { error "Neustart fehlgeschlagen – manuell prüfen."; exit 1; }
|
||||||
if [ ! -d /run/sshd ]; then
|
|
||||||
warn "/run/sshd fehlt - erstelle Verzeichnis..."
|
|
||||||
sudo mkdir -p /run/sshd
|
|
||||||
sudo chown root:sys /run/sshd || sudo chown root:root /run/sshd
|
|
||||||
sudo chmod 0755 /run/sshd
|
|
||||||
log "/run/sshd wurde erstellt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Erstelle oder modifiziere die Datei /etc/hosts.deny für zusätzliche Sicherheit
|
log "Setup abgeschlossen. Zugelassene Benutzer: ${WHITE}${VALID_USERS}${RESET}"
|
||||||
if [ -f "/etc/hosts.deny" ]; then
|
|
||||||
# Prüfe, ob SSHD bereits in hosts.deny vorhanden ist
|
|
||||||
if ! grep -q "sshd" /etc/hosts.deny; then
|
|
||||||
echo "sshd: ALL" | sudo tee -a /etc/hosts.deny > /dev/null
|
|
||||||
log "SSHD-Eintrag zu /etc/hosts.deny hinzugefügt."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "sshd: ALL" | sudo tee /etc/hosts.deny > /dev/null
|
|
||||||
log "Datei /etc/hosts.deny mit SSHD-Eintrag erstellt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Erstelle oder modifiziere die Datei /etc/hosts.allow für vertrauenswürdige IPs
|
|
||||||
echo -en "${BLUE}Möchtest du bestimmte IP-Adressen explizit für SSH zulassen? ${YELLOW}[${WHITE}y/n${YELLOW}]${RESET}: "
|
|
||||||
read ADD_ALLOWED_IPS
|
|
||||||
if [ "$ADD_ALLOWED_IPS" == "y" ]; then
|
|
||||||
echo -en "${BLUE}Gib vertrauenswürdige IP-Adressen ein (durch Leerzeichen getrennt, z.B. 192.168.1.10 10.0.0.5): ${RESET}"
|
|
||||||
read ALLOWED_IPS
|
|
||||||
|
|
||||||
if [ -n "$ALLOWED_IPS" ]; then
|
|
||||||
FORMATTED_IPS=$(echo "$ALLOWED_IPS" | tr ' ' ',')
|
|
||||||
echo "sshd: $FORMATTED_IPS : allow" | sudo tee -a /etc/hosts.allow > /dev/null
|
|
||||||
|
|||||||
Reference in New Issue
Block a user