Last active 3 weeks ago

Revision 4d5c406b13c761799768603d2157c2eb62694132

debian-13-initial-setup.sh Raw
1#!/usr/bin/env bash
2set -euo pipefail
3
4# ============================================================
5# Debian 13 VPS Initial Setup Script
6#
7# Includes:
8# 1. Base update + useful tools
9# 2. Chrony time sync
10# 3. Fail2ban
11# 4. Automatic security updates
12# 5. Kernel/network tuning
13# 6. Journald log limits
14# 7. Locale
15# 8. Reboot-required check
16# ============================================================
17
18export DEBIAN_FRONTEND=noninteractive
19
20# -----------------------------
21# Config
22# -----------------------------
23
24TIMEZONE="UTC"
25
26# Change this if you prefer en_AU.UTF-8 or en_NZ.UTF-8
27LOCALE_NAME="en_US.UTF-8"
28
29# Journald limits
30JOURNAL_SYSTEM_MAX_USE="500M"
31JOURNAL_RUNTIME_MAX_USE="100M"
32JOURNAL_MAX_RETENTION="1month"
33
34# Fail2ban SSH settings
35FAIL2BAN_MAXRETRY="5"
36FAIL2BAN_FINDTIME="10m"
37FAIL2BAN_BANTIME="1h"
38
39# -----------------------------
40# Helpers
41# -----------------------------
42
43log() {
44 echo
45 echo "============================================================"
46 echo "$1"
47 echo "============================================================"
48}
49
50warn() {
51 echo
52 echo "WARNING: $1"
53}
54
55require_root() {
56 if [ "$(id -u)" -ne 0 ]; then
57 echo "This script must be run as root."
58 exit 1
59 fi
60}
61
62detect_debian() {
63 if [ ! -f /etc/os-release ]; then
64 warn "Cannot detect OS because /etc/os-release is missing."
65 return
66 fi
67
68 . /etc/os-release
69
70 echo "Detected OS: ${PRETTY_NAME:-unknown}"
71
72 if [ "${ID:-}" != "debian" ]; then
73 warn "This script is intended for Debian. Continuing anyway."
74 fi
75
76 if [ "${VERSION_ID:-}" != "13" ]; then
77 warn "This script is intended for Debian 13. Detected VERSION_ID=${VERSION_ID:-unknown}. Continuing anyway."
78 fi
79}
80
81safe_systemctl_enable_now() {
82 local service="$1"
83
84 if systemctl list-unit-files "$service" >/dev/null 2>&1; then
85 systemctl enable --now "$service"
86 else
87 warn "Service $service not found, skipping enable/start."
88 fi
89}
90
91# -----------------------------
92# Start
93# -----------------------------
94
95require_root
96detect_debian
97
98log "Step 1/8: Updating APT package lists and upgrading system"
99
100apt-get update
101apt-get upgrade -y
102
103log "Step 1/8: Installing base useful packages"
104
105apt-get install -y \
106 apt-transport-https \
107 bash-completion \
108 btop \
109 ca-certificates \
110 curl \
111 dnsutils \
112 git \
113 gnupg \
114 htop \
115 iftop \
116 iotop \
117 iproute2 \
118 jq \
119 less \
120 lsb-release \
121 lsof \
122 nano \
123 ncdu \
124 net-tools \
125 openssh-client \
126 openssh-server \
127 rsync \
128 screen \
129 strace \
130 sudo \
131 tar \
132 tcpdump \
133 tmux \
134 tree \
135 unzip \
136 vim \
137 wget \
138 zip
139
140log "Step 2/8: Installing and enabling Chrony time sync"
141
142apt-get install -y chrony
143
144timedatectl set-timezone "$TIMEZONE" || warn "Could not set timezone to $TIMEZONE"
145
146safe_systemctl_enable_now chrony.service
147
148echo
149echo "Current time status:"
150timedatectl || true
151
152log "Step 3/8: Installing and configuring Fail2ban"
153
154apt-get install -y fail2ban
155
156mkdir -p /etc/fail2ban/jail.d
157
158cat >/etc/fail2ban/jail.d/sshd.local <<EOF
159[sshd]
160enabled = true
161port = ssh
162filter = sshd
163backend = systemd
164maxretry = ${FAIL2BAN_MAXRETRY}
165findtime = ${FAIL2BAN_FINDTIME}
166bantime = ${FAIL2BAN_BANTIME}
167EOF
168
169safe_systemctl_enable_now fail2ban.service
170
171systemctl restart fail2ban || warn "Could not restart fail2ban."
172
173echo
174echo "Fail2ban SSH jail status:"
175fail2ban-client status sshd || true
176
177log "Step 4/8: Installing and configuring automatic security updates"
178
179apt-get install -y unattended-upgrades apt-listchanges needrestart
180
181cat >/etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
182APT::Periodic::Update-Package-Lists "1";
183APT::Periodic::Unattended-Upgrade "1";
184APT::Periodic::AutocleanInterval "7";
185APT::Periodic::Verbose "1";
186EOF
187
188cat >/etc/apt/apt.conf.d/51unattended-upgrades-local <<'EOF'
189Unattended-Upgrade::Origins-Pattern {
190 "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
191 "origin=Debian,codename=${distro_codename},label=Debian";
192};
193
194Unattended-Upgrade::Package-Blacklist {
195};
196
197Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
198Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
199Unattended-Upgrade::Remove-Unused-Dependencies "false";
200
201Unattended-Upgrade::Automatic-Reboot "false";
202Unattended-Upgrade::SyslogEnable "true";
203EOF
204
205systemctl restart unattended-upgrades || true
206safe_systemctl_enable_now unattended-upgrades.service
207
208echo
209echo "Testing unattended-upgrades config:"
210unattended-upgrade --dry-run --debug || warn "unattended-upgrades dry run returned a warning/error. Review output above."
211
212log "Step 5/8: Applying kernel and network tuning"
213
214cat >/etc/sysctl.d/99-vps-tuning.conf <<'EOF'
215# ============================================================
216# VPS kernel/network tuning
217# ============================================================
218
219# Use fair queueing. Recommended with BBR.
220net.core.default_qdisc = fq
221
222# Enable BBR congestion control.
223net.ipv4.tcp_congestion_control = bbr
224
225# Enable TCP Fast Open.
226net.ipv4.tcp_fastopen = 3
227
228# Do not act as a router by default.
229net.ipv4.ip_forward = 0
230net.ipv6.conf.all.forwarding = 0
231
232# Basic anti-spoofing / safer IPv4 behaviour.
233net.ipv4.conf.all.rp_filter = 1
234net.ipv4.conf.default.rp_filter = 1
235
236# Ignore ICMP redirects.
237net.ipv4.conf.all.accept_redirects = 0
238net.ipv4.conf.default.accept_redirects = 0
239net.ipv6.conf.all.accept_redirects = 0
240net.ipv6.conf.default.accept_redirects = 0
241
242# Do not send ICMP redirects.
243net.ipv4.conf.all.send_redirects = 0
244net.ipv4.conf.default.send_redirects = 0
245
246# Ignore source-routed packets.
247net.ipv4.conf.all.accept_source_route = 0
248net.ipv4.conf.default.accept_source_route = 0
249net.ipv6.conf.all.accept_source_route = 0
250net.ipv6.conf.default.accept_source_route = 0
251
252# Log suspicious martian packets.
253net.ipv4.conf.all.log_martians = 1
254net.ipv4.conf.default.log_martians = 1
255
256# Reasonable file handle limit.
257fs.file-max = 2097152
258
259# Reasonable memory behaviour for small VPSes.
260vm.swappiness = 10
261vm.vfs_cache_pressure = 50
262EOF
263
264sysctl --system
265
266echo
267echo "Current TCP congestion control:"
268sysctl net.ipv4.tcp_congestion_control || true
269
270echo
271echo "Available TCP congestion controls:"
272sysctl net.ipv4.tcp_available_congestion_control || true
273
274log "Step 6/8: Setting journald log size limits"
275
276mkdir -p /etc/systemd/journald.conf.d
277
278cat >/etc/systemd/journald.conf.d/99-vps-limits.conf <<EOF
279[Journal]
280SystemMaxUse=${JOURNAL_SYSTEM_MAX_USE}
281RuntimeMaxUse=${JOURNAL_RUNTIME_MAX_USE}
282MaxRetentionSec=${JOURNAL_MAX_RETENTION}
283Compress=yes
284EOF
285
286systemctl restart systemd-journald
287
288echo
289echo "Current journal disk usage:"
290journalctl --disk-usage || true
291
292log "Step 7/8: Configuring locale"
293
294apt-get install -y locales
295
296if grep -q "^# *${LOCALE_NAME} UTF-8" /etc/locale.gen; then
297 sed -i "s/^# *${LOCALE_NAME} UTF-8/${LOCALE_NAME} UTF-8/" /etc/locale.gen
298elif ! grep -q "^${LOCALE_NAME} UTF-8" /etc/locale.gen; then
299 echo "${LOCALE_NAME} UTF-8" >> /etc/locale.gen
300fi
301
302locale-gen
303update-locale LANG="$LOCALE_NAME"
304
305echo
306echo "Configured locale:"
307localectl status || true
308
309log "Cleaning up packages"
310
311apt-get autoremove -y
312apt-get autoclean -y
313
314log "Step 8/8: Reboot-required check"
315
316REBOOT_REQUIRED="no"
317
318if [ -f /var/run/reboot-required ]; then
319 REBOOT_REQUIRED="yes"
320fi
321
322if command -v needrestart >/dev/null 2>&1; then
323 echo
324 echo "needrestart summary:"
325 needrestart -b || true
326fi
327
328echo
329echo "============================================================"
330echo "Debian 13 VPS initial setup complete."
331echo "============================================================"
332echo "Timezone: $TIMEZONE"
333echo "Locale: $LOCALE_NAME"
334echo "Reboot required: $REBOOT_REQUIRED"
335echo
336
337if [ "$REBOOT_REQUIRED" = "yes" ]; then
338 echo "A reboot is recommended:"
339 echo " reboot"
340else
341 echo "No reboot-required flag detected."
342fi
343
344echo
345echo "Useful checks:"
346echo " systemctl status chrony"
347echo " systemctl status fail2ban"
348echo " fail2ban-client status sshd"
349echo " systemctl status unattended-upgrades"
350echo " journalctl --disk-usage"
351echo " sysctl net.ipv4.tcp_congestion_control"
352echo
readme.md Raw

wget -qO- 'https://beanman.net/gist/beanman109/debian-13-initial-setup/raw/HEAD/debian-13-initial-setup.sh' | bash