|
1 | | -#!/bin/sh |
2 | | -# |
3 | | -# Applies system hardening: sysctl kernel parameters, AppArmor, and SELinux. |
4 | | -# Safe to run multiple times (idempotent). |
5 | | - |
6 | | -if [ "$(id -u)" -ne 0 ]; then |
7 | | - echo "[!] This script must be run as root." |
8 | | - exit 1 |
9 | | -fi |
10 | | - |
11 | | -log() { echo "[*] $1"; } |
12 | | -ok() { echo "[+] $1"; } |
13 | | -warn() { echo "[~] $1"; } |
14 | | -err() { echo "[!] $1"; } |
15 | | - |
16 | | -####################################################################### |
17 | | -# Sysctl hardening |
18 | | -####################################################################### |
19 | | - |
20 | | -SYSCTL_CONF="/etc/sysctl.d/99-hardening.conf" |
21 | | - |
22 | | -log "Writing sysctl hardening rules to $SYSCTL_CONF..." |
23 | | - |
24 | | -cat > "$SYSCTL_CONF" << 'EOF' |
25 | | -# --------------------------------------------------------------- |
26 | | -# Network hardening |
27 | | -# --------------------------------------------------------------- |
28 | | -
|
29 | | -# Disable IP forwarding (not a router) |
30 | | -net.ipv4.ip_forward = 0 |
31 | | -net.ipv6.conf.all.forwarding = 0 |
32 | | -
|
33 | | -# Disable source routing |
34 | | -net.ipv4.conf.all.accept_source_route = 0 |
35 | | -net.ipv4.conf.default.accept_source_route = 0 |
36 | | -net.ipv6.conf.all.accept_source_route = 0 |
37 | | -
|
38 | | -# Ignore ICMP redirects |
39 | | -net.ipv4.conf.all.accept_redirects = 0 |
40 | | -net.ipv4.conf.default.accept_redirects = 0 |
41 | | -net.ipv4.conf.all.secure_redirects = 0 |
42 | | -net.ipv6.conf.all.accept_redirects = 0 |
43 | | -
|
44 | | -# Do not send redirects |
45 | | -net.ipv4.conf.all.send_redirects = 0 |
46 | | -
|
47 | | -# Enable reverse path filter (anti-spoofing) |
48 | | -net.ipv4.conf.all.rp_filter = 1 |
49 | | -net.ipv4.conf.default.rp_filter = 1 |
50 | | -
|
51 | | -# Ignore broadcast ICMP (Smurf attack protection) |
52 | | -net.ipv4.icmp_echo_ignore_broadcasts = 1 |
53 | | -
|
54 | | -# Ignore bogus ICMP error responses |
55 | | -net.ipv4.icmp_ignore_bogus_error_responses = 1 |
56 | | -
|
57 | | -# Enable SYN cookies (SYN flood protection) |
58 | | -net.ipv4.tcp_syncookies = 1 |
59 | | -
|
60 | | -# Log martian packets |
61 | | -net.ipv4.conf.all.log_martians = 1 |
62 | | -net.ipv4.conf.default.log_martians = 1 |
63 | | -
|
64 | | -# Disable IPv6 if not needed |
65 | | -net.ipv6.conf.all.disable_ipv6 = 1 |
66 | | -net.ipv6.conf.default.disable_ipv6 = 1 |
67 | | -
|
68 | | -# --------------------------------------------------------------- |
69 | | -# Kernel hardening |
70 | | -# --------------------------------------------------------------- |
71 | | -
|
72 | | -# Restrict dmesg to root |
73 | | -kernel.dmesg_restrict = 1 |
74 | | -
|
75 | | -# Restrict ptrace to root |
76 | | -kernel.yama.ptrace_scope = 2 |
77 | | -
|
78 | | -# Restrict kernel pointers in /proc |
79 | | -kernel.kptr_restrict = 2 |
80 | | -
|
81 | | -# Disable magic SysRq key |
82 | | -kernel.sysrq = 0 |
83 | | -
|
84 | | -# Restrict unprivileged access to kernel logs |
85 | | -kernel.perf_event_paranoid = 3 |
86 | | -
|
87 | | -# Randomise virtual address space (ASLR) |
88 | | -kernel.randomize_va_space = 2 |
89 | | -
|
90 | | -# Restrict unprivileged BPF |
91 | | -kernel.unprivileged_bpf_disabled = 1 |
92 | | -net.core.bpf_jit_harden = 2 |
93 | | -
|
94 | | -# --------------------------------------------------------------- |
95 | | -# Filesystem hardening |
96 | | -# --------------------------------------------------------------- |
97 | | -
|
98 | | -# Prevent hard links to files not owned by the user |
99 | | -fs.protected_hardlinks = 1 |
100 | | -
|
101 | | -# Prevent symlink following in world-writable sticky dirs |
102 | | -fs.protected_symlinks = 1 |
103 | | -
|
104 | | -# Restrict FIFO creation in world-writable sticky dirs |
105 | | -fs.protected_fifos = 2 |
106 | | -
|
107 | | -# Restrict regular file creation in world-writable sticky dirs |
108 | | -fs.protected_regular = 2 |
109 | | -
|
110 | | -# Restrict core dumps |
111 | | -fs.suid_dumpable = 0 |
112 | | -EOF |
113 | | - |
114 | | -sysctl -p "$SYSCTL_CONF" >/dev/null 2>&1 && ok "sysctl hardening applied." || err "Failed to apply some sysctl settings." |
115 | | - |
116 | | -####################################################################### |
117 | | -# AppArmor |
118 | | -####################################################################### |
119 | | - |
120 | | -log "Configuring AppArmor..." |
121 | | -if command -v aa-enforce >/dev/null 2>&1; then |
122 | | - if ! aa-status --enabled >/dev/null 2>&1; then |
123 | | - warn "AppArmor is not enabled in the kernel. Enable it by adding 'apparmor=1 security=apparmor' to GRUB_CMDLINE_LINUX in /etc/default/grub and running update-grub." |
124 | | - else |
125 | | - aa-enforce /etc/apparmor.d/* >/dev/null 2>&1 |
126 | | - ok "AppArmor profiles set to enforce mode." |
127 | | - fi |
128 | | -else |
129 | | - warn "AppArmor tools not found. Run ./install.sh first." |
130 | | -fi |
131 | | - |
132 | | -####################################################################### |
133 | | -# SELinux |
134 | | -####################################################################### |
135 | | - |
136 | | -log "Configuring SELinux..." |
137 | | -if command -v selinux-activate >/dev/null 2>&1; then |
138 | | - if sestatus 2>/dev/null | grep -q "enabled"; then |
139 | | - warn "SELinux is already active." |
140 | | - else |
141 | | - warn "SELinux requires a reboot to activate. Run 'selinux-activate' to enable, then reboot." |
142 | | - warn "Note: AppArmor and SELinux are mutually exclusive — use one or the other." |
143 | | - fi |
144 | | -else |
145 | | - warn "SELinux tools not found. Run ./install.sh first." |
146 | | -fi |
147 | | - |
148 | | -####################################################################### |
149 | | -# Password policy |
150 | | -####################################################################### |
151 | | - |
152 | | -log "Applying password policy..." |
153 | | - |
154 | | -# Install required packages |
155 | | -for PKG in libpam-pwquality passwd; do |
156 | | - dpkg -s "$PKG" >/dev/null 2>&1 || apt-get install -y "$PKG" >/dev/null 2>&1 |
157 | | -done |
158 | | - |
159 | | -# /etc/login.defs — password ageing |
160 | | -sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs |
161 | | -sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 1/' /etc/login.defs |
162 | | -sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 14/' /etc/login.defs |
163 | | - |
164 | | -# SHA-512 password hashing |
165 | | -sed -i 's/^ENCRYPT_METHOD.*/ENCRYPT_METHOD SHA512/' /etc/login.defs |
166 | | - |
167 | | -# /etc/security/pwquality.conf — password complexity |
168 | | -PWQUALITY_CONF="/etc/security/pwquality.conf" |
169 | | -cat > "$PWQUALITY_CONF" << 'EOF' |
170 | | -minlen = 14 |
171 | | -dcredit = -1 |
172 | | -ucredit = -1 |
173 | | -lcredit = -1 |
174 | | -ocredit = -1 |
175 | | -maxrepeat = 3 |
176 | | -maxsequence = 3 |
177 | | -gecoscheck = 1 |
178 | | -dictcheck = 1 |
179 | | -EOF |
180 | | - |
181 | | -# PAM: enforce pwquality and account lockout |
182 | | -PAM_PASSWD="/etc/pam.d/common-password" |
183 | | -if [ -f "$PAM_PASSWD" ] && ! grep -q "pam_pwquality" "$PAM_PASSWD"; then |
184 | | - sed -i '/pam_unix.so/i password requisite pam_pwquality.so retry=3 enforce_for_root' "$PAM_PASSWD" |
185 | | -fi |
186 | | - |
187 | | -PAM_AUTH="/etc/pam.d/common-auth" |
188 | | -if [ -f "$PAM_AUTH" ] && ! grep -q "pam_faillock" "$PAM_AUTH"; then |
189 | | - sed -i '1s/^/auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900\n/' "$PAM_AUTH" |
190 | | - echo "auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900" >> "$PAM_AUTH" |
191 | | -fi |
192 | | - |
193 | | -ok "Password policy applied." |
194 | | - |
195 | | -####################################################################### |
196 | | -# SSH hardening |
197 | | -####################################################################### |
198 | | - |
199 | | -log "Hardening SSH..." |
200 | | - |
201 | | -SSHD_CONF="/etc/ssh/sshd_config" |
202 | | -SSHD_HARDENING="/etc/ssh/sshd_config.d/99-hardening.conf" |
203 | | - |
204 | | -if [ ! -f "$SSHD_CONF" ]; then |
205 | | - warn "sshd_config not found, skipping SSH hardening." |
206 | | -else |
207 | | - # Use drop-in file if supported (OpenSSH 8.2+), else patch sshd_config directly |
208 | | - if grep -q "^Include /etc/ssh/sshd_config.d" "$SSHD_CONF" 2>/dev/null; then |
209 | | - mkdir -p /etc/ssh/sshd_config.d |
210 | | - cat > "$SSHD_HARDENING" << 'EOF' |
211 | | -# Disable root login |
212 | | -PermitRootLogin no |
213 | | -
|
214 | | -# Disable password authentication — use keys only |
215 | | -PasswordAuthentication no |
216 | | -PermitEmptyPasswords no |
217 | | -
|
218 | | -# Disable challenge-response (e.g. keyboard-interactive) |
219 | | -ChallengeResponseAuthentication no |
220 | | -KbdInteractiveAuthentication no |
221 | | -
|
222 | | -# Disable unused authentication methods |
223 | | -UsePAM yes |
224 | | -GSSAPIAuthentication no |
225 | | -HostbasedAuthentication no |
226 | | -
|
227 | | -# Limit authentication attempts |
228 | | -MaxAuthTries 3 |
229 | | -MaxSessions 4 |
230 | | -
|
231 | | -# Idle session timeout (seconds) |
232 | | -ClientAliveInterval 300 |
233 | | -ClientAliveCountMax 2 |
234 | | -
|
235 | | -# Disable X11 and agent forwarding |
236 | | -X11Forwarding no |
237 | | -AllowAgentForwarding no |
238 | | -AllowTcpForwarding no |
239 | | -
|
240 | | -# Restrict to strong ciphers, MACs, and key exchange algorithms |
241 | | -Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr |
242 | | -MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com |
243 | | -KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512 |
244 | | -
|
245 | | -# Use privilege separation |
246 | | -UsePrivilegeSeparation sandbox |
247 | | -
|
248 | | -# Log level for auditing |
249 | | -LogLevel VERBOSE |
250 | | -
|
251 | | -# Disable .rhosts and /etc/hosts.equiv |
252 | | -IgnoreRhosts yes |
253 | | -
|
254 | | -# Banner |
255 | | -Banner /etc/issue.net |
256 | | -EOF |
257 | | - ok "SSH hardening written to $SSHD_HARDENING." |
258 | | - else |
259 | | - # Direct patching for older OpenSSH |
260 | | - apply_sshd() { |
261 | | - KEY=$1; VAL=$2 |
262 | | - if grep -q "^#*\s*${KEY}" "$SSHD_CONF"; then |
263 | | - sed -i "s|^#*\s*${KEY}.*|${KEY} ${VAL}|" "$SSHD_CONF" |
264 | | - else |
265 | | - echo "${KEY} ${VAL}" >> "$SSHD_CONF" |
266 | | - fi |
267 | | - } |
268 | | - apply_sshd PermitRootLogin no |
269 | | - apply_sshd PasswordAuthentication no |
270 | | - apply_sshd PermitEmptyPasswords no |
271 | | - apply_sshd MaxAuthTries 3 |
272 | | - apply_sshd X11Forwarding no |
273 | | - apply_sshd ClientAliveInterval 300 |
274 | | - apply_sshd ClientAliveCountMax 2 |
275 | | - apply_sshd AllowAgentForwarding no |
276 | | - apply_sshd AllowTcpForwarding no |
277 | | - apply_sshd LogLevel VERBOSE |
278 | | - apply_sshd IgnoreRhosts yes |
279 | | - ok "SSH hardening applied to $SSHD_CONF." |
280 | | - fi |
281 | | - |
282 | | - # Set a login banner |
283 | | - cat > /etc/issue.net << 'EOF' |
284 | | -Authorized access only. All activity is monitored and logged. |
285 | | -Unauthorized access is prohibited and may be prosecuted. |
286 | | -EOF |
287 | | - |
288 | | - # Validate and reload sshd |
289 | | - if sshd -t >/dev/null 2>&1; then |
290 | | - systemctl reload sshd 2>/dev/null || service ssh reload 2>/dev/null || true |
291 | | - ok "sshd reloaded." |
292 | | - else |
293 | | - err "sshd config test failed — review $SSHD_CONF manually." |
294 | | - fi |
295 | | -fi |
296 | | - |
297 | | -####################################################################### |
298 | | -# Kernel module hardening |
299 | | -####################################################################### |
300 | | - |
301 | | -log "Applying kernel module hardening..." |
302 | | - |
303 | | -MODULES_CONF="/etc/modprobe.d/99-hardening.conf" |
304 | | - |
305 | | -cat > "$MODULES_CONF" << 'EOF' |
306 | | -# Disable uncommon filesystems |
307 | | -install cramfs /bin/true |
308 | | -install freevxfs /bin/true |
309 | | -install hfs /bin/true |
310 | | -install hfsplus /bin/true |
311 | | -install jffs2 /bin/true |
312 | | -install squashfs /bin/true |
313 | | -install udf /bin/true |
314 | | -
|
315 | | -# Disable uncommon network protocols |
316 | | -install dccp /bin/true |
317 | | -install sctp /bin/true |
318 | | -install rds /bin/true |
319 | | -install tipc /bin/true |
320 | | -install n-hdlc /bin/true |
321 | | -install ax25 /bin/true |
322 | | -install netrom /bin/true |
323 | | -install x25 /bin/true |
324 | | -install rose /bin/true |
325 | | -install decnet /bin/true |
326 | | -install econet /bin/true |
327 | | -install af_802154 /bin/true |
328 | | -install ipx /bin/true |
329 | | -install appletalk /bin/true |
330 | | -install psnap /bin/true |
331 | | -install p8023 /bin/true |
332 | | -install p8022 /bin/true |
333 | | -install can /bin/true |
334 | | -install atm /bin/true |
335 | | -
|
336 | | -# Disable Bluetooth |
337 | | -install bluetooth /bin/true |
338 | | -install btusb /bin/true |
339 | | -
|
340 | | -# Disable FireWire (DMA-based attack surface) |
341 | | -install firewire-core /bin/true |
342 | | -install firewire-ohci /bin/true |
343 | | -install firewire-sbp2 /bin/true |
344 | | -
|
345 | | -# Disable Thunderbolt (DMA attacks) |
346 | | -install thunderbolt /bin/true |
347 | | -EOF |
348 | | - |
349 | | -ok "Kernel module restrictions written to $MODULES_CONF." |
350 | | - |
351 | | -# Rebuild initramfs to apply module blacklist at boot |
352 | | -if command -v update-initramfs >/dev/null 2>&1; then |
353 | | - update-initramfs -u >/dev/null 2>&1 && ok "initramfs updated." || warn "initramfs update failed." |
354 | | -fi |
355 | | - |
356 | | -####################################################################### |
357 | | -# USB restrictions |
358 | | -####################################################################### |
359 | | - |
360 | | -log "Applying USB restrictions..." |
361 | | - |
362 | | -USB_CONF="/etc/modprobe.d/99-usb-hardening.conf" |
363 | | - |
364 | | -cat > "$USB_CONF" << 'EOF' |
365 | | -# Disable USB storage (block mass storage devices) |
366 | | -install usb-storage /bin/true |
367 | | -
|
368 | | -# Disable uncommon USB device classes |
369 | | -install usbip-core /bin/true |
370 | | -install usbip-host /bin/true |
371 | | -EOF |
372 | | - |
373 | | -ok "USB storage disabled via $USB_CONF." |
374 | | - |
375 | | -# USBGuard — policy-based USB device authorisation |
376 | | -if command -v usbguard >/dev/null 2>&1; then |
377 | | - if ! usbguard list-rules >/dev/null 2>&1; then |
378 | | - usbguard generate-policy > /etc/usbguard/rules.conf 2>/dev/null \ |
379 | | - && ok "USBGuard policy generated from currently connected devices." \ |
380 | | - || warn "Could not generate USBGuard policy." |
381 | | - else |
382 | | - ok "USBGuard policy already exists." |
383 | | - fi |
384 | | - systemctl enable --now usbguard 2>/dev/null && ok "USBGuard service enabled." || warn "Could not enable USBGuard." |
385 | | -else |
386 | | - warn "usbguard not installed. Install it with: apt-get install -y usbguard" |
387 | | -fi |
388 | | - |
389 | | -# Rebuild initramfs |
390 | | -if command -v update-initramfs >/dev/null 2>&1; then |
391 | | - update-initramfs -u >/dev/null 2>&1 && ok "initramfs updated." || warn "initramfs update failed." |
392 | | -fi |
393 | | - |
394 | | -log "Hardening complete." |
| 1 | +NO BE DONE |
0 commit comments