#!/bin/bash # Debian 12.1 post install script for new servers # Author: Martijn de Boer if [[ $EUID -ne 0 ]]; then echo "You must be a root user" 2>&1 exit 1 fi # Check if we are running Debian if [[ ! -f /etc/debian_version ]]; then echo "This script only works on Debian" exit 1 fi # Configuration options AUTHORIZED_SSH_KEYS="https://change.my.host.tld/authorized_keys" SSH_PORT="2323" set -e echo echo "Install and configure new Debian 12.1 (bookworm) servers" echo echo "This script will perform the following actions:" echo " - Update all packages to latest" echo " - Harden the system" echo " - Install common packages" echo " - Configure SSH to listen on port ${SSH_PORT} and disable passwords if desired" echo " - Configure firewall" echo " - Install crowdsec + firewall bouncer" echo "" # Ask for confirmation echo -e "\e[1;32mPlease review the actions above and confirm to continue...\e[0m" echo -e "\e[1;31mWARNING: Make sure you understand the prompt(s) before continuing!\e[0m" read -p "Continue? " -n 1 -r REPLY_CONTINUE if [[ ! $REPLY_CONTINUE =~ ^[Yy]$ ]] then exit 1 fi echo read -p "Disable SSH passwords and load public keys from ${AUTHORIZED_SSH_KEYS}? " -n 1 -r REPLY_SSH_KEYS case "$REPLY_SSH_KEYS" in y|Y ) echo "yes";; n|N ) echo "no";; * ) echo "invalid";; esac echo read -p "Configure for http+https? " -n 1 -r REPLY_HTTPHTTPS case "$REPLY_HTTPHTTPS" in y|Y ) echo "yes";; n|N ) echo "no";; * ) echo "invalid";; esac # Make a sane environment echo -e "\e[1;32mSetting up environment...\e[0m" echo "deb https://deb.debian.org/debian/ bookworm main contrib non-free-firmware non-free" > /etc/apt/sources.list # Update everything to latest echo -e "\e[1;32mUpdating packages...\e[0m" apt update apt -y upgrade # System hardening echo -e "\e[1;32mHardening system...\e[0m" ## Disable info packets echo "net.ipv4.icmp_echo_ignore_broadcasts = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.icmp_ignore_bogus_error_responses = 1" >> /etc/sysctl.d/99-custom.conf ## Track bad attempts echo "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.log_martians = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.log_martians = 1" >> /etc/sysctl.d/99-custom.conf ## Harden ip options echo "net.ipv4.conf.all.accept_source_route = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.accept_source_route = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.rp_filter = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.rp_filter = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.accept_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.accept_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.secure_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.secure_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.send_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.send_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.accept_source_route = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.accept_source_route = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.rp_filter = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.rp_filter = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.log_martians = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.log_martians = 1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.secure_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.secure_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_synack_retries=2" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_fin_timeout=15" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_keepalive_time=300" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_keepalive_probes=5" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_keepalive_intvl=15" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_max_tw_buckets=1440000" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_tw_recycle=1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_tw_reuse=1" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_fastopen=3" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.autoconf = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.dad_transmits = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.max_addresses = 1" >> /etc/sysctl.d/99-custom.conf ## Disable routing functionality echo "net.ipv4.ip_forward = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.all.send_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.conf.default.send_redirects = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.router_solicitations = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.accept_ra_rtr_pref = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.accept_ra_pinfo = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.accept_ra_defrtr = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.all.accept_ra = 0" >> /etc/sysctl.d/99-custom.conf echo "net.ipv6.conf.default.accept_ra = 0" >> /etc/sysctl.d/99-custom.conf ## Network optimisation echo -e "\e[1;32mOptimising network settings...\e[0m" echo "net.ipv4.ip_local_port_range = 2000 65000" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_rmem = 4096 87380 8388608" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_wmem = 4096 87380 8388608" >> /etc/sysctl.d/99-custom.conf echo "net.core.rmem_max = 8388608" >> /etc/sysctl.d/99-custom.conf echo "net.core.wmem_max = 8388608" >> /etc/sysctl.d/99-custom.conf echo "net.core.netdev_max_backlog = 5000" >> /etc/sysctl.d/99-custom.conf echo "net.ipv4.tcp_window_scaling = 1" >> /etc/sysctl.d/99-custom.conf ## Kernel hardening echo "kernel.randomize_va_space = 2" >> /etc/sysctl.d/99-custom.conf echo "kernel.kptr_restrict = 1" >> /etc/sysctl.d/99-custom.conf echo "kernel.dmesg_restrict = 1" >> /etc/sysctl.d/99-custom.conf echo "kernel.kexec_load_disabled = 1" >> /etc/sysctl.d/99-custom.conf echo "kernel.yama.ptrace_scope = 1" >> /etc/sysctl.d/99-custom.conf echo "kernel.unprivileged_userns_clone = 0" >> /etc/sysctl.d/99-custom.conf echo "kernel.unprivileged_bpf_disabled = 1" >> /etc/sysctl.d/99-custom.conf echo "net.core.bpf_jit_harden = 2" >> /etc/sysctl.d/99-custom.conf echo "kernel.panic=10" >> /etc/sysctl.d/99-custom.conf ## Kernel optimisation echo "kernel.pid_max = 65536" >> /etc/sysctl.d/99-custom.conf ## Filesystem protection echo -e "\e[1;32mHardening filesystem...\e[0m" echo "fs.protected_hardlinks=1" >> /etc/sysctl.d/99-custom.conf echo "fs.protected_symlinks=1" >> /etc/sysctl.d/99-custom.conf echo "fs.file-max=2097152" >> /etc/sysctl.d/99-custom.conf ## Swap echo -e "\e[1;32mTuning swap...\e[0m" echo "vm.swappiness=10" >> /etc/sysctl.d/99-custom.conf echo "vm.dirty_ratio=60" >> /etc/sysctl.d/99-custom.conf echo "vm.dirty_background_ratio=2" >> /etc/sysctl.d/99-custom.conf # Write sysctl values sysctl -p /etc/sysctl.d/99-custom.conf # Set limits echo -e "\e[1;32mSetting security limits...\e[0m" touch /etc/security/limits.d/99-custom.conf echo "* hard nofile 94000" >> /etc/security/limits.d/99-custom.conf echo "* soft nofile 94000" >> /etc/security/limits.d/99-custom.conf echo "* hard nproc 64000" >> /etc/security/limits.d/99-custom.conf echo "* soft nproc 64000" >> /etc/security/limits.d/99-custom.conf # Install common packages echo -e "\e[1;32mInstalling common packages...\e[0m" apt install -y debian-archive-keyring apt-transport-https curl vim git ufw openssh-server auditd &> /dev/null # Configure SSH echo -e "\e[1;32mConfiguring SSH...\e[0m" sed -i 's/#Port 22/Port ${SSH_PORT}/g' /etc/ssh/sshd_config sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/g' /etc/ssh/sshd_config sed -i 's/#MaxAuthTries 6/MaxAuthTries 3/g' /etc/ssh/sshd_config sed -i 's/#MaxSessions 10/MaxSessions 3/g' /etc/ssh/sshd_config sed -i 's/#ClientAliveInterval 0/ClientAliveInterval 300/g' /etc/ssh/sshd_config sed -i 's/#ClientAliveCountMax 3/ClientAliveCountMax 0/g' /etc/ssh/sshd_config sed -i 's/#LoginGraceTime 2m/LoginGraceTime 30/g' /etc/ssh/sshd_config sed -i 's/#ChallengeResponseAuthentication yes/ChallengeResponseAuthentication no/g' /etc/ssh/sshd_config sed -i 's/#GSSAPIAuthentication yes/GSSAPIAuthentication no/g' /etc/ssh/sshd_config sed -i 's/#X11Forwarding yes/X11Forwarding no/g' /etc/ssh/sshd_config sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding no/g' /etc/ssh/sshd_config sed -i 's/#AllowTcpForwarding yes/AllowTcpForwarding no/g' /etc/ssh/sshd_config printf ' DebianBanner no KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 Protocol 2 Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr ' >> /etc/ssh/sshd_config if [[ $REPLY_SSH_KEYS =~ ^[Yy]$ ]] then echo -e "\e[1;32mConfiguring SSH keys...\e[0m" sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' /etc/ssh/sshd_config mkdir -p /root/.ssh curl -s ${AUTHORIZED_SSH_KEYS} > /root/.ssh/authorized_keys chmod 700 /root/.ssh chmod 400 /root/.ssh/authorized_keys fi # Configure audit logging echo -e "\e[1;32mConfiguring Audit logging...\e[0m" echo "ForwardToSyslog=yes" >> /etc/systemd/journald.conf printf ' -a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale -a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale -w /etc/issue -p wa -k system-locale -w /etc/issue.net -p wa -k system-locale -w /etc/hosts -p wa -k system-locale -w /var/run/utmp -p wa -k session -w /var/log/wtmp -p wa -k session -w /var/log/btmp -p wa -k session -w /etc/sudoers -p wa -k scope -w /etc/sudoers.d/ -p wa -k scope -w /etc/group -p wa -k identity -w /etc/passwd -p wa -k identity -w /etc/gshadow -p wa -k identity -w /etc/shadow -p wa -k identity -w /etc/security/opasswd -p wa -k identity -w /var/log/faillog -p wa -k logins -w /var/log/lastlog -p wa -k logins -w /var/log/tallylog -p wa -k logins -a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change -a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change -a always,exit -F arch=b64 -S clock_settime -k time-change -a always,exit -F arch=b32 -S clock_settime -k time-change -w /etc/localtime -p wa -k time-change ' >> /etc/audit/rules.d/50-system_local.rules printf ' Defaults logfile="/var/log/sudo.log" ' >> /etc/sudoers.d/log augenrules --load echo -e "\e[1;32mConfiguring permissions...\e[0m" chown root:root /etc/ssh/sshd_config chmod og-rwx /etc/ssh/sshd_config chown root:root /etc/passwd- chmod 600 /etc/passwd- chown root:root /etc/group- chmod 600 /etc/group- # Configure firewall echo -e "\e[1;32mConfiguring firewall...\e[0m" ufw default deny incoming ufw default allow outgoing ufw allow ${SSH_PORT}/tcp if [[ $REPLY_HTTPHTTPS =~ ^[Yy]$ ]] then ufw allow http ufw allow https fi ufw enable # Install crowdsec echo -e "\e[1;32mInstalling crowdsec...\e[0m" curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | bash apt install crowdsec crowdsec-firewall-bouncer crowdsec-firewall-bouncer-iptables -y &> /dev/null echo -e "\e[1;32mDone configuring!\e[0m" echo -e "\e[1;31mWARNING: Make sure to remember the SSH port is set to: ${SSH_PORT}\e[0m" # Reboot read -p "Reboot the system now? " -n 1 -r REPLY_REBOOT case "$REPLY_REBOOT" in y|Y ) echo "yes";; n|N ) echo "no";; * ) echo "invalid";; esac if [[ $REPLY_REBOOT =~ ^[Yy]$ ]] then reboot fi