#!/bin/bash ######################################################### # Function :Initial Server Setup for Debian Server # # Platform :Debian 11, 12 or 13 # # Version :1.7 # # Date :31-10-2024 # # Author :Xiufeng Guo # # Contact :i@m.ac # # Company :Show Corporation # # Usage :curl -sL ba.sh/debian | bash # ######################################################### PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin export PATH set -euo pipefail IFS=$'\n\t' #current_dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) # Check system requirements, if it's not Debian 11, 12 or 13, exit function check_debian_version() { if [[ -f /etc/os-release ]]; then source /etc/os-release if [[ "$ID" == "debian" && ( "$VERSION_CODENAME" == "bullseye" || "$VERSION_CODENAME" == "bookworm" || "$VERSION_CODENAME" == "trixie" ) ]]; then echo "Running on supported Debian version: $PRETTY_NAME" else echo "Unsupported Debian version: $PRETTY_NAME" exit 1 fi else echo "/etc/os-release file not found. Cannot determine OS version." exit 1 fi } check_debian_version # Check if user is root, if not, exit function check_root() { if [ "$(id -u)" != "0" ]; then printf "\E[0;31;40m" echo "### This script must be run as root. Exiting... ###" printf "\E[0m" exit 1 fi } check_root function change_apt_sources() { printf "\E[0;35;40m" echo '### Remove all apt sources files ###' printf "\E[0m" # if /etc/apt/sources.list exists, then backup it if [ -f /etc/apt/sources.list ]; then cp -r /etc/apt/sources.list /etc/apt/sources.list.$(date +"%Y_%m_%d_%I_%M_%p").bak fi # if /etc/apt/sources.list.d exists, then backup it if [ -d /etc/apt/sources.list.d ]; then cp -r /etc/apt/sources.list.d /etc/apt/sources.list.d.$(date +"%Y_%m_%d_%I_%M_%p").bak fi printf "\E[0;35;40m" echo '### Update system, install apt-transport-https and ca-certificates ###' printf "\E[0m" apt update apt install apt-transport-https ca-certificates lsb-release -y printf "\E[0;35;40m" echo '### Replace the default sources.list with the new one ###' printf "\E[0m" codename=$(lsb_release -sc) # If codename = bullseye, use it's sources.list if [ "$codename" == "bullseye" ]; then cat > /etc/apt/sources.list << EOF deb https://mirror-cdn.xtom.com/debian/ $codename main contrib non-free #deb-src https://mirror-cdn.xtom.com/debian/ $codename main contrib non-free deb https://mirror-cdn.xtom.com/debian-security/ $codename-security main contrib non-free #deb-src https://mirror-cdn.xtom.com/debian-security/ $codename-security main contrib non-free deb https://mirror-cdn.xtom.com/debian/ $codename-updates main contrib non-free #deb-src https://mirror-cdn.xtom.com/debian/ $codename-updates main contrib non-free deb https://mirror-cdn.xtom.com/debian/ $codename-backports main contrib non-free EOF # If codename = bookworm, use the new sources.list elif [ "$codename" == "bookworm" ]; then cat > /etc/apt/sources.list << EOF deb https://mirror-cdn.xtom.com/debian/ $codename main contrib non-free non-free-firmware #deb-src https://mirror-cdn.xtom.com/debian/ $codename main contrib non-free non-free-firmware deb https://mirror-cdn.xtom.com/debian-security/ $codename-security main contrib non-free non-free-firmware #deb-src https://mirror-cdn.xtom.com/debian-security/ $codename-security main contrib non-free non-free-firmware deb https://mirror-cdn.xtom.com/debian/ $codename-updates main contrib non-free non-free-firmware #deb-src https://mirror-cdn.xtom.com/debian/ $codename-updates main contrib non-free non-free-firmware deb https://mirror-cdn.xtom.com/debian/ $codename-backports main contrib non-free non-free-firmware EOF # If codename = trixie, use the new sources.list elif [ "$codename" == "trixie" ]; then cat > /etc/apt/sources.list << EOF deb https://mirror-cdn.xtom.com/debian/ $codename main contrib non-free non-free-firmware #deb-src https://mirror-cdn.xtom.com/debian/ $codename main contrib non-free non-free-firmware deb https://mirror-cdn.xtom.com/debian-security/ $codename-security main contrib non-free non-free-firmware #deb-src https://mirror-cdn.xtom.com/debian-security/ $codename-security main contrib non-free non-free-firmware deb https://mirror-cdn.xtom.com/debian/ $codename-updates main contrib non-free non-free-firmware #deb-src https://mirror-cdn.xtom.com/debian/ $codename-updates main contrib non-free non-free-firmware deb https://mirror-cdn.xtom.com/debian/ $codename-backports main contrib non-free non-free-firmware EOF # If codename = others, then exit else printf "\E[0;31;40m" echo "### This script is only for Debian 11, 12 or 13. Exiting... ###" printf "\E[0m" exit 1 fi # Delete all sources.list.d files rm -rf /etc/apt/sources.list.d/* printf "\E[0;35;40m" echo '### Updating system... ###' printf "\E[0m" apt update printf "\E[0;33;40m" echo "### apt sources replaced ###" printf "\E[0m" } function install_packages() { printf "\E[0;35;40m" echo '### Install the necessary packages ###' printf "\E[0m" apt install -y \ vim nano cron sudo \ net-tools dnsutils mtr-tiny traceroute \ wget curl host \ unzip rsync \ bash-completion git whois rsyslog \ fail2ban iptables haveged gnupg vnstat lrzsz jq \ unattended-upgrades apt-listchanges \ htop iftop p7zip-full zstd systemd-timesyncd \ bat python3-systemd byobu screen printf "\E[0;35;40m" echo '### Updating system... ###' printf "\E[0m" apt update apt upgrade -y apt full-upgrade -y apt autoclean apt autoremove -y printf "\E[0;33;40m" echo "### System upgraded ###" printf "\E[0m" } function change_timezone() { printf "\E[0;35;40m" echo '### Change timezone to UTC and environment variables to en_US.UTF-8 ###' printf "\E[0m" rm -rf /etc/localtime ln -s /usr/share/zoneinfo/UTC /etc/localtime localectl set-locale LANG="en_US.UTF-8" LC_TIME="en_US.UTF-8" LANGUAGE="en_US.UTF-8" printf "\E[0;33;40m" echo "### Timezone changed to UTC ###" printf "\E[0m" } function add_ssh_keys(){ printf "\E[0;35;40m" echo '### Add SSH keys... ###' printf "\E[0m" mkdir -p /root/.ssh cat > /root/.ssh/authorized_keys << EOF ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPn3FvKyi2pvUCU943a1FPUKV+rOElgPoOcxORGnEmdS p@m.ac-mobile ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINeLww3zlRtaZ5vSxMRKoUC8s2r3+wEirzGt0bFMCD1R p@m.ac-x1 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHAj4nbOal5A6nPPBFdjLG5a7JjW/BC6jif1yY1rbQS+ y@m.ac-2022 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQvrfn3+7e/qLYFkHt7Jmo+e+7QdD8vD/ECh3PC/gR6 i@m.ac-ed25519 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAlfvpoVibfQ36GwMjJt2PIF77Dup8T9/3frJwVBC33zSwHJiihfPZlfKiUfiOUP1io1kBs3fMhq1r+x708ilxKn/3NxtYFmUWTKpqQq6+t5KBAY+0Cy+WHnJlslg8M0/npf7mDr5HJf09eTn+krSGCUTo+Gd38P9aNp1WamMa6pIRQ27mWMSECDkB93LpPVI4p9t00Z8ArNS6DOaTRcW9+/VJ0S+wYKnTmGz9bvsliHIK1fSBCMtPH0ApZtsQSHmsjh75ACv6qbU2R9d0ur3OA5u3wtFXuCIIzZANJIfQP8ehXnNjmakJTbNtGjye9hErKuaBwy42Ekgw5gRiHRuxvdPM5P5fSy01+jPQb3JqaEQOtrlTaVyjja0Y8+MSA8LqGvgul8FsQ5fRafJo0D+ep2SblrYUCc1mTwWx5+Z7pgaW04dXeVVXXGJVkGvfcE74rOBqfgHe/FAsbW+9rVueDiwuXnkivz/Yeqp2uAQxAqJerpZUPx9mE7ouSTipBT8e2R7zXm65cfhmrHmrrzEo8kJfcxBj9ozzpj2S4RsTm2DvYo05tbzaSo/gC1pv4QIwFhPfz8IvmPOjNI2xdYhMLCZGAf4Rl2g8PCE8lgXTjOFxIUb11H2N1OJgPoXiiAGFBPhdQZardE+fIfjWlh0wW9yCtTk7CojT0l1DPR8F9PE= i@m.ac-rsa-4096 EOF chmod 700 /root/.ssh chmod 600 /root/.ssh/authorized_keys printf "\E[0;33;40m" echo "### SSH keys added ###" printf "\E[0m" } function disable_ssh_password() { printf "\E[0;35;40m" echo '### Disable SSH password ###' printf "\E[0m" cat > /etc/ssh/sshd_config.d/disable_password.conf << EOF PermitRootLogin prohibit-password PasswordAuthentication no EOF cat > /etc/ssh/sshd_config.d/hide_debian_banner.conf << EOF DebianBanner no EOF cat > /etc/ssh/sshd_config.d/enable_rsa_keys.conf << EOF HostKeyAlgorithms +ssh-rsa PubkeyAcceptedKeyTypes +ssh-rsa EOF printf "\E[0;33;40m" echo "### SSH password disabled ###" printf "\E[0m" } function custom_fail2ban() { printf "\E[0;35;40m" echo '### Custom Fail2ban ###' printf "\E[0m" cat > /etc/fail2ban/jail.d/bantime.conf << EOF [DEFAULT] bantime = 1h EOF printf "\E[0;33;40m" echo "### Custom Fail2ban configuration added ###" printf "\E[0m" } function custom_bashrc() { printf "\E[0;35;40m" echo '### Custom .bashrc ###' printf "\E[0m" cp -r /root/.bashrc /root/.bashrc.$(date +"%Y_%m_%d_%I_%M_%p").bak cat > /root/.bashrc << EOF if [ -z "\$PS1" ]; then return fi # Custom alias alias ls='ls --color=auto' alias ll='ls --color=auto -alFh' alias l='ls -A' alias mtr='mtr --aslookup --show-ips' PS1='\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;36m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\\$ \[\033[00m\]' # If this is an xterm set the title to user@host:dir case "\$TERM" in xterm*|rxvt*) PS1="\[\e]0;\${debian_chroot:+(\$debian_chroot)}\u@\h: \w\a\]\$PS1" ;; *) ;; esac EOF printf "\E[0;33;40m" echo "### Custom .bashrc added ###" printf "\E[0m" } function custom_vimrc() { printf "\E[0;35;40m" echo '### Custom .vimrc ###' printf "\E[0m" cat > /root/.vimrc << EOF set nopaste syntax on set nu set tabstop=4 set shiftwidth=4 set softtabstop=4 set expandtab EOF printf "\E[0;33;40m" echo "### Custom .vimrc added ###" printf "\E[0m" } function custom_inputrc() { printf "\E[0;35;40m" echo '### Custom .inputrc ###' printf "\E[0m" cat > /root/.inputrc << EOF set enable-bracketed-paste off EOF printf "\E[0;33;40m" echo "### Custom .inputrc added ###" printf "\E[0m" } function custom_sysctl() { printf "\E[0;35;40m" echo '### Custom sysctl.conf ###' printf "\E[0m" # Backup sysctl.conf if [ -f /etc/sysctl.conf ]; then cp -r /etc/sysctl.conf /etc/sysctl.conf.$(date +"%Y_%m_%d_%I_%M_%p").bak cat /dev/null > /etc/sysctl.conf fi cat > /etc/sysctl.d/local.conf << EOF net.ipv4.ip_forward = 1 net.ipv4.conf.all.proxy_arp = 0 net.ipv6.conf.all.forwarding = 1 net.ipv6.conf.all.autoconf = 0 net.ipv6.conf.default.autoconf = 0 net.ipv6.conf.all.accept_ra = 0 net.ipv6.conf.default.accept_ra = 0 net.ipv6.conf.default.accept_dad = 0 net.ipv6.conf.all.accept_dad = 0 net.core.rmem_default = 1212992 net.ipv4.neigh.default.gc_interval = 3600 net.ipv4.neigh.default.gc_stale_time = 3600 net.ipv4.neigh.default.gc_thresh1 = 2048 net.ipv4.neigh.default.gc_thresh2 = 4096 net.ipv4.neigh.default.gc_thresh3 = 8192 net.ipv6.neigh.default.base_reachable_time_ms = 3600 net.core.wmem_max = 134217728 net.core.rmem_max = 134217728 net.ipv4.tcp_rmem = 10240 87380 134217728 net.ipv4.tcp_wmem = 10240 87380 134217728 net.ipv4.tcp_slow_start_after_idle = 0 fs.inotify.max_user_watches = 819200 net.ipv4.tcp_max_syn_backlog = 32768 net.core.somaxconn = 2048 net.core.netdev_max_backlog = 32768 vm.vfs_cache_pressure = 100 vm.dirty_background_bytes = 52428800 net.core.default_qdisc = fq net.ipv4.tcp_congestion_control = bbr net.ipv6.route.max_size = 1048576 fs.file-max = 1000000 vm.swappiness = 10 vm.dirty_ratio = 60 vm.dirty_background_ratio = 2 net.ipv6.ip6frag_high_thresh = 4194304 net.ipv6.ip6frag_low_thresh = 3145728 net.ipv4.ipfrag_high_thresh = 4194304 net.ipv4.ipfrag_low_thresh = 3145728 net.ipv4.ping_group_range = 0 2147483647 EOF sysctl -p printf "\E[0;33;40m" echo "### Custom sysctl.conf added ###" printf "\E[0m" } function add_nginx_repo() { printf "\E[0;35;40m" echo '### Add n.wtf Nginx repo ###' printf "\E[0m" curl -sS https://n.wtf/public.key | gpg --dearmor > /usr/share/keyrings/n.wtf.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirror-cdn.xtom.com/sb/nginx/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/n.wtf.list printf "\E[0;33;40m" echo "### n.wtf Nginx repo added ###" printf "\E[0m" } function add_docker_repo() { printf "\E[0;35;40m" echo '### Add Docker CE repo ###' printf "\E[0m" curl -sS https://download.docker.com/linux/debian/gpg | gpg --dearmor > /usr/share/keyrings/docker-ce.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/debian $(lsb_release -sc) stable" > /etc/apt/sources.list.d/docker.list printf "\E[0;33;40m" echo "### Docker CE repo added ###" printf "\E[0m" } function add_php_repo() { printf "\E[0;35;40m" echo '### Add Sury PHP repo ###' printf "\E[0m" wget -O /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://mirror-cdn.xtom.com/sury/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list printf "\E[0;33;40m" echo "### Sury PHP repo added ###" printf "\E[0m" } function add_mariadb_repo() { printf "\E[0;35;40m" echo '### Add MariaDB repo ###' printf "\E[0m" curl -sSL https://mariadb.org/mariadb_release_signing_key.asc | gpg --dearmor > /usr/share/keyrings/mariadb.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirror-cdn.xtom.com/mariadb/repo/11.4/debian $(lsb_release -sc) main" > /etc/apt/sources.list.d/mariadb.list printf "\E[0;33;40m" echo "### MariaDB repo added ###" printf "\E[0m" } function add_postgresql_repo() { printf "\E[0;35;40m" echo '### Add PostgreSQL repo ###' printf "\E[0m" curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /usr/share/keyrings/postgresql.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/postgresql.list printf "\E[0;33;40m" echo "### PostgreSQL repo added ###" printf "\E[0m" } function add_tor_repo() { printf "\E[0;35;40m" echo '### Add Tor repo ###' printf "\E[0m" wget -O /usr/share/keyrings/tor.gpg https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/tor.gpg] https://deb.torproject.org/torproject.org $(lsb_release -sc) main" > /etc/apt/sources.list.d/tor.list printf "\E[0;33;40m" echo "### Tor repo added ###" printf "\E[0m" } function add_redis_repo() { printf "\E[0;35;40m" echo '### Add Redis repo ###' printf "\E[0m" curl -sSL https://packages.redis.io/gpg | gpg --dearmor > /usr/share/keyrings/redis.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/redis.gpg] https://packages.redis.io/deb $(lsb_release -sc) main" > /etc/apt/sources.list.d/redis.list printf "\E[0;33;40m" echo "### Redis repo added ###" printf "\E[0m" } function add_unattended_upgrades() { printf "\E[0;35;40m" echo '### Enable Unattended Upgrades ###' printf "\E[0m" cp -r /etc/apt/apt.conf.d/50unattended-upgrades /etc/apt/apt.conf.d/50unattended-upgrades.$(date +"%Y_%m_%d_%I_%M_%p").bak cat > /etc/apt/apt.conf.d/50unattended-upgrades << EOF APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1"; APT::Periodic::Verbose "1"; APT::Periodic::AutocleanInterval "7"; Unattended-Upgrade::Mail "root"; Unattended-Upgrade::Origins-Pattern { "origin=Debian,codename=\${distro_codename},label=Debian-Security"; "origin=Debian,codename=\${distro_codename}-security,label=Debian-Security"; }; Unattended-Upgrade::Package-Blacklist { }; Unattended-Upgrade::Automatic-Reboot "false"; EOF printf "\E[0;33;40m" echo "### Unattended Upgrades enabled ###" printf "\E[0m" } function add_acme_sh() { printf "\E[0;35;40m" echo '### Install acme.sh ###' printf "\E[0m" curl -sS https://get.acme.sh | bash -s email=i@m.ac /root/.acme.sh/acme.sh --upgrade --auto-upgrade /root/.acme.sh/acme.sh --set-default-ca --server letsencrypt printf "\E[0;33;40m" echo "### acme.sh installation done ###" printf "\E[0m" } function add_rclone() { printf "\E[0;35;40m" echo '### Install Rclone ###' printf "\E[0m" VERSION=$(curl -s https://api.github.com/repos/rclone/rclone/releases/latest | grep tag_name | cut -d '"' -f 4) && echo "Latest rclone version is $VERSION" wget -O /tmp/rclone.deb https://github.com/rclone/rclone/releases/download/$VERSION/rclone-$VERSION-linux-$(dpkg --print-architecture).deb dpkg -i /tmp/rclone.deb rm /tmp/rclone.deb printf "\E[0;33;40m" echo "### Rclone installation done ###" printf "\E[0m" } function add_update_sh() { printf "\E[0;35;40m" echo '### Add update.sh ###' printf "\E[0m" cat > /root/update.sh << EOF #!/bin/bash apt update apt upgrade -y apt full-upgrade -y apt autoclean apt autoremove -y EOF chmod +x /root/update.sh /root/update.sh printf "\E[0;33;40m" echo "### update.sh added ###" printf "\E[0m" } # Check if dialog is installed, if not, install it function check_dialog() { if ! command -v dialog &> /dev/null; then echo "dialog is not installed. Installing it now..." apt update && apt install dialog -y fi } check_dialog # Dialog box begins here cmd=(dialog --title "Debian Server Initial Setup" --separate-output --checklist "Select options:" 22 76 16) options=(1 "Install Packages" on # any option can be set to default to "on" 2 "Change APT Sources" on 3 "Change Timezone" on 4 "Add SSH Keys" on 5 "Disable SSH Password" on 6 "Custom Fail2ban" on 7 "Custom .bashrc" on 8 "Custom .vimrc" on 9 "Custom .inputrc" on 10 "Custom sysctl" on 11 "Enable Unattended Upgrades" on 12 "Add n.wtf Nginx Repo" on 13 "Add Docker CE Repo" off 14 "Add Sury PHP Repo" off 15 "Add MariaDB Repo" off 16 "Add PostgreSQL Repo" off 17 "Add Tor Project Repo" off 18 "Add Redis Repo" off 19 "Install acme.sh" off 20 "Install Rclone" off 21 "Add update.sh" on) choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) clear for choice in $choices do case $choice in 1) install_packages ;; 2) change_apt_sources ;; 3) change_timezone ;; 4) add_ssh_keys ;; 5) disable_ssh_password ;; 6) custom_fail2ban ;; 7) custom_bashrc ;; 8) custom_vimrc ;; 9) custom_inputrc ;; 10) custom_sysctl ;; 11) add_unattended_upgrades ;; 12) add_nginx_repo ;; 13) add_docker_repo ;; 14) add_php_repo ;; 15) add_mariadb_repo ;; 16) add_postgresql_repo ;; 17) add_tor_repo ;; 18) add_redis_repo ;; 19) add_acme_sh ;; 20) add_rclone ;; 21) add_update_sh ;; esac done printf "\E[0;36;40m" echo '### Server Initial Setup Completed. ###' echo 'Have a nice day.' echo 'You may need to reboot your system to take affect.' printf "\E[0m"