-
Notifications
You must be signed in to change notification settings - Fork 0
/
solidcore-install.sh
1032 lines (843 loc) · 29.6 KB
/
solidcore-install.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
## Solidcore Hardening Scripts for Fedora's rpm-ostree Operating Systems
## Version 0.2.7
##
## Copyright (C) 2023 solidc0re (https://github.com/solidc0re)
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see https://www.gnu.org/licenses/.
# Install script
# Running order
# - Display functions
# - Flags
# - Initial checks (sudo, immutable variant)
# - Welcome
# - Define new sysctl settings
# - Create backups and restore files
# - Apply new sysctl settings
# - Bootloader settings
# - Block kernel modules
# - Disable services
# - Hidepid and add noexec to /tmp and /dev/shm
# - Umask 0077
# - Disable core dumps
# - Improve password policies
# - Lock root
# - Firewalld zone to drop
# - MAC randomization
# - Update Chrony conf
# - Install automatic update services (rpm-ostree, flatpak and replace Fedora flatpaks with Flathub)
# - Mute microphone on boot
# - Install minisign and flatseal
# - Set up for first boot
# - Install uninstall script
# - Reboot
# === DISPLAY FUNCTIONS ===
# Declare bold and normal
bold=$(tput bold)
green=$(tput setaf 2)
italics=$(tput sitm)
normal=$(tput sgr0)
# Interruptable version for long texts
long_msg() {
local main_output="$1"
local idx=0
local char
while [ $idx -lt ${#main_output} ]; do
char="${main_output:$idx:1}"
echo -n "$char"
# Check if a key was pressed
if read -r -s -n 1 -t 0.01 key; then
# Output the remaining portion of the main_output
echo -n "${main_output:idx+1}"
break
fi
sleep 0.015
idx=$((idx + 1))
done
}
# Non-interruptable version for short messages
short_msg() {
local main_output="> $1"
echo
local idx=0
local char
while [ $idx -lt ${#main_output} ]; do
char="${main_output:$idx:1}"
echo -n "$char"
sleep 0.015
idx=$((idx + 1))
done
}
# Non-interruptable version for confirmation messages
conf_msg() {
short_msg "$1"
echo -ne " ${bold}${green}✓${normal}"
}
# Create two line gap
space_2() {
long_msg "
>
> "
}
# Create one line gap
space_1() {
long_msg "
> "
}
# === FLAGS ===
# Server mode
# Check if the -server flag is provided
if [[ "$1" == "-server" ]]; then
server_mode=true
short_msg "Server mode: Some commands will not be executed."
else
server_mode=false
fi
# Test mode
# Check if the -test flag is provided
if [[ "$1" == "-test" ]]; then
test_mode=true
short_msg "Test mode."
else
test_mode=false
fi
# === INITIAL CHECKS ===
# Enable error tracing on -server flag
if [[ "$server_mode" == true ]]; then
set -ouex pipefail
fi
# Sudo check
# Check if the script is being run with sudo privileges
if [ "$EUID" -ne 0 ]; then
short_msg "This script requires sudo privileges. Please run it with 'sudo' using 'sudo <path-to-script>./solidcore-install.sh'"
sleep 1
exit 1
fi
# Variant check
declare -a fedora_variants=("silverblue" "kinoite" "sericea" "vauxite" "onyx")
detected_variant=""
# Run rpm-ostree status -b and capture the output
ostree_status=$(rpm-ostree status -b)
# Iterate through the array to check for a match
for variant in "${fedora_variants[@]}"; do
if [[ "$ostree_status" == *"$variant"* ]]; then
detected_variant="$variant"
break # Exit the loop after the first match
fi
done
# Use the detected_variant variable later in your script
if [ -n "$detected_variant" ]; then
:
else
short_msg "No supported immutable Fedora variant detected."
exit 1
fi
# === WELCOME ===
RELEASE="$(rpm -E %fedora)"
if [[ "$server_mode" == true ]]; then
solidcore_response="Y"
else
clear
long_msg ">
>
>
> Welcome to solidcore!
>
> The hardening script for immutable Fedora variants.
>
> You are currently running: ${detected_variant^} $RELEASE."
sleep 1
long_msg "
>
> This script will carry out various hardening measures, including:
>
> 1. Kernel and physical hardening to reduce attack surface
> 2. Drop all incoming connections, prevent IP spoofing and protect against various forms of attack
> 3. More secure DNS lookups and added blocklists
> 4. Stengthen password policies
> 5. Enable automatic updates for rpm-ostree and flatpaks"
sleep 1
long_msg "
>
>
> This script is open source (GPLv3) and has been tested on Silverblue 38 by the author.
>
> Hardening MAY reduce your experience of your device and is not suitable for everyone."
sleep 2
space_2
while true; do
read -p "${bold}Do you want to continue?${normal} (y/n): " solidcore_response
case $solidcore_response in
[Yy] ) solidcore_response="Y";
break;;
[Nn] ) short_msg "Aborting."
space_2
sleep
clear;
exit 1;;
* ) short_msg "Invalid response. Please retry with 'y' or 'n'."
echo ">";
esac
done
fi
if [[ "$solidcore_response" =~ ^[Yy]$ ]]; then
long_msg ">
>
> Your system need to reboot once the first round of hardening is completed.
> You will be presented with another script on first boot.
> Be sure to complete all the stages to finish the hardening process.
>
> Starting...
>
>"
sleep 3
clear
space_2
# === SYSCTL PARAMETERS ===
# Array of sysctl commands and their new settings
declare -A sysctl_settings
# KERNEL
sysctl_settings["kernel.kptr_restrict"]="2" # Mitigate kernel pointer leaks
sysctl_settings["kernel.dmesg_restrict"]="1" # Restrict kernel log
sysctl_settings["kernel.printk"]="3 3 3 3" # Stop printing kernel log on boot
sysctl_settings["kernel.unprivileged_bpf_disabled"]="1" # Restrict eBPF
sysctl_settings["net.core.bpf_jit_harden"]="2"
sysctl_settings["dev.tty.ldisc_autoload"]="0" # Restrict loading TTY line disciplines
sysctl_settings["kernel.kexec_load_disabled"]="1" # Disable kexec
sysctl_settings["kernel.sysrq"]="0" # Disable SysRq
sysctl_settings["kernel.perf_event_paranoid"]="3" # Restrict usage of performance events
# NETWORK
sysctl_settings["net.ipv4.tcp_syncookies"]="1" # Protect against SYN flood attacks
sysctl_settings["net.ipv4.tcp_rfc1337"]="1" # Protect against time-wait assassination
sysctl_settings["net.ipv4.conf.all.rp_filter"]="1" # Protect against IP spoofing
sysctl_settings["net.ipv4.conf.default.rp_filter"]="1"
sysctl_settings["net.ipv4.conf.all.accept_redirects"]="0" # Disable ICMP redirect acceptance
sysctl_settings["net.ipv4.conf.default.accept_redirects"]="0"
sysctl_settings["net.ipv4.conf.all.secure_redirects"]="0"
sysctl_settings["net.ipv4.conf.default.secure_redirects"]="0"
sysctl_settings["net.ipv6.conf.all.accept_redirects"]="0"
sysctl_settings["net.ipv6.conf.default.accept_redirects"]="0"
sysctl_settings["net.ipv4.conf.all.send_redirects"]="0"
sysctl_settings["net.ipv4.conf.default.send_redirects"]="0"
sysctl_settings["net.ipv4.icmp_echo_ignore_all"]="1" # Prevent smurf attacks and clock fingerprinting
sysctl_settings["net.ipv6.conf.all.accept_ra"]="0" # Disable IPv6 router advertisements
sysctl_settings["net.ipv6.conf.default.accept_ra"]="0"
sysctl_settings["net.ipv4.tcp_sack"]="0" # Disable TCP SACK
sysctl_settings["net.ipv4.tcp_dsack"]="0"
sysctl_settings["net.ipv4.tcp_fack"]="0"
sysctl_settings["net.ipv4.tcp_timestamps"]="0" # Disable TCP timestamps
sysctl_settings["net.ipv6.conf.all.use_tempaddr"]="2" # Generate random IPv6 addresses
sysctl_settings["net.ipv6.conf.default.use_tempaddr"]="2"
# USERSPACE
sysctl_settings["kernel.yama.ptrace_scope"]="2" # Restrict ptrace
sysctl_settings["vm.mmap_rnd_bits"]="32" # Increase mmap ALSR entropy
sysctl_settings["vm.mmap_rnd_compat_bits"]="16"
sysctl_settings["fs.protected_fifos"]="2" # Prevent creating files in potential attacker-controlled directories
sysctl_settings["fs.protected_regular"]="2"
# === BACKUPS & RESTORE FILE ===
# Create the directory if it doesn't exist
mkdir -p /etc/solidcore
# Only do the backing up and creation of default scripts if server mode is not flagged
if [[ "$server_mode" == false ]]; then
# Output default settings to the new script
echo "#!/bin/bash" > /etc/solidcore/defaults.sh
echo "# Previous sysctl values. Created by solidcore script." >> /etc/solidcore/defaults.sh
for key in "${!sysctl_settings[@]}"; do
# Get the existing sysctl value
existing_value=$(sysctl -n "$key")
echo "sysctl -w $key=$existing_value" >> /etc/solidcore/defaults.sh
done
chmod +x /etc/solidcore/defaults.sh
# Define an array of files to be backed up
files_to_backup=(
"/etc/chrony.conf"
"/etc/default/grub"
"/etc/fstab"
"/etc/machine-id"
"/etc/resolv.conf"
"/etc/rpm-ostreed.conf"
"/etc/security/access.conf"
"/etc/security/faillock.conf"
"/etc/security/limits.conf"
"/etc/security/pwquality.conf"
"/etc/ssh/sshd_config"
"/etc/sysconfig/chronyd"
"/etc/systemd/coredump.conf"
"/etc/systemd/system/rpm-ostreed-automatic.timer.d/override.conf"
"/etc/xdg/autostart/org.gnome.Software.desktop"
"/var/lib/dbus/machine-id"
)
# Loop through the array and create backup copies
for source_file in "${files_to_backup[@]}"; do
# Check if the source file exists
if [ -e "$source_file" ]; then
# Construct the backup filename
backup_file="${source_file}_sc.bak"
# Copy the source file to the backup file
cp "$source_file" "$backup_file"
fi
done
# Remove Gnome Software update service after backup created
rm -f /etc/xdg/autostart/org.gnome.Software.desktop
conf_msg "All backups created"
fi # End of -server flag if statement
# === APPLY SYSCTL SETTINGS ===
# Apply new sysctl settings
for key in "${!sysctl_settings[@]}"; do
if [ "$test_mode" == true ]; then
sysctl -w "$key=${sysctl_settings[$key]}"
else
sysctl -w "$key=${sysctl_settings[$key]}" > /dev/null
fi
done
conf_msg "Hardened sysctl settings applied"
sleep 1
short_msg "Changing the bootloader parameters. This may take several minutes..."
# === BOOTLOADER SETTINGS ===
# Boot parameters to be added
boot_parameters=(
"slab_nomerge" # Disables slab merging
"init_on_alloc=1" # Enables zeroing of memory to mitigate use-after-free vulnerabilities
"init_on_free=1"
"page_alloc.shuffle=1" # Improve security by making page allocation less predictable
"pti=on" # Mitigate Meltdown and prevents some KASLR bypasses
"randomize_kstack_offset=on" # Randomises kernel stack offset on each syscall
"vsyscall=none" # Disables obsolete vsyscalls
"debugfs=off" # Disables debugfs to stop sensitive information being exposed
#"lockdown=confidentiality" # Makes it harder to load malicious kernel modules; implies module.sig_enforce=1 so could break unsigned drivers (NVIDIA, etc.)
"quiet loglevel=0" # Prevents information leaks on boot; must be used in conjuction with kernel.printk sysctl
#"ipv6.disable=1" # Not disabling IPv6 in solidcore
"random.trust_cpu=off" # Do not trust proprietary code on CPU for random number generation
"random.trust_bootloader=off" # Recommended by privsec.dev
"efi=disable_early_pci_dma" # Fixes hole in IOMMU
"mitigations=auto" # Ensures mitigations against known CPU vulnerabilities
"iommu.passthrough=0" # Recommended by privsec.dev
"iommu.strict=1" # Recommended by privsec.dev
"extra_latent_entropy" # Recommended by privsec.dev
)
# Check CPU vendor using lscpu
cpu_vendor=$(lscpu | awk '/Vendor/ {print $3}')
# Add IOMMU parameter based on CPU vendor
case "$cpu_vendor" in
GenuineIntel*) boot_parameters+=("intel_iommu=on") ;;
AuthenticAMD*) boot_parameters+=("amd_iommu=on") ;;
*) short_msg "${bold}[Notice]${normal} CPU vendor doesn't match GenuineIntel or AuthenticAMD. CPU Vendor currently recorded as: $cpu_vendor" ;;
esac
# Backup existing kargs and keep new kargs for uninstall process
rpm-ostree kargs > /etc/solidcore/kargs-orig_sc.bak
printf "%s\n" "${boot_parameters[@]}" > /etc/solidcore/kargs-added_sc.bak
# Construct a single string with all the parameters
param_string=""
for param in "${boot_parameters[@]}"; do
param_string+="--append-if-missing=$param "
done
# Remove the trailing space
param_string="${param_string%" "}"
# Cancel any current rpm-ostree operations and append boot parameters
rpm-ostree cancel -q
rpm-ostree kargs -q "$param_string" > /dev/null
# === BLOCK KERNEL MODULES ===
block_file="/etc/modprobe.d/solidcore-blocklist.conf"
# List of module names to be blocked
modules_to_block=(
"af_802154"
"appletalk" # Already blacklisted in Fedora, adding install <module> /bin/true to block re-loading
"atm" # Already backlisted in Fedora, adding install <module> /bin/true to block re-loading
"ax25" # Already blacklisted in Fedora, adding install <module> /bin/true to block re-loading
"can"
"cdrom"
"cifs"
"cramfs"
"decnet"
"dccp"
"econet"
"freevxfs"
"gfs2"
"hfs"
"hfsplus"
"ipx"
"jffs2"
"ksmbd"
"mei" # Intel Management Engine
"mei-me" # Intel Management Engine
"msr"
"n-hdlc"
"netrom" # Already blacklisted in Fedora, adding install <module> /bin/true to block re-loading
"nfs"
"nfsv3"
"nfsv4"
"p8022"
"p8023"
"psnap"
"rds" # Already blacklisted in Fedora, adding install <module> /bin/true to block re-loading
"rose" # Already blacklisted in Fedora, adding install <module> /bin/true to block re-loading
"sctp" # Already blacklisted in Fedora, adding install <module> /bin/true to block re-loading
"sr_mod"
"squashfs"
"tipc"
"udf"
"vivid"
"x25"
)
# Add module names to the blacklist configuration file
echo "# Blocked kernel modules to prevent loading. Created by solidcore script." | tee "$block_file" > /dev/null
for module in "${modules_to_block[@]}"; do
echo "install $module /bin/true" | tee -a "$block_file" > /dev/null
done
# Additional modules to blacklist, thanks to Kicksecure & Ubuntu
modules_to_blacklist=(
"amd76x_edac"
"asus_acpi"
"ath_pci"
"aty128fb"
"atyfb"
"bcm43xx"
"cirrusfb"
"cyber2000fb"
"cyblafb"
"de4x5"
"eepro100"
"eth1394"
"evbug"
"garmin_gps"
"gx1fb"
"hgafb"
"i810fb"
"intelfb"
"kyrofb"
"lxfb"
"matroxfb_bases"
"neofb"
"nvidiafb"
"pcspkr"
"pm2fb"
"prism54"
"radeonfb"
"rivafb"
"s1d13xxxfb"
"savagefb"
"sisfb"
"snd_aw2"
"snd_intel8x0m"
"snd_pcsp"
"sstfb"
"tdfxfb"
"tridentfb"
"udlfb"
"usbkbd"
"usbmouse"
"vesafb"
"vfb"
"viafb"
"vt8623fb"
)
# Add module names to the blacklist configuration file
for module in "${modules_to_blacklist[@]}"; do
echo "blacklist $module" | tee -a "$block_file" > /dev/null
done
conf_msg "Unsafe and legacy kernel modules blocked"
# === DISABLE SERVICES ===
# High risk and unused services/sockets
services=(
#abrt-journal-core.service # Not found in Silverblue 38
#abrt-oops.service # Fedora crash reporting, not in Silverblue 38
#abrtd.service # Fedora crash reporting, not in Silverblue 38
avahi-daemon # Recommended by CIS
geoclue.service # Location service
httpd # Recommended by CIS
network-online.target # Remote file mounting, most likely unncessary
nfs-server # Recommended by CIS
remote-fs.target # Remote file mounting, most likely unnecessary
rpcbind # Recommended by CIS
rpm-ostree-countme.service # Potential to leak information about OS to someone monitoring network
sshd # Not needed on desktop
)
# Loop through the array and stop and disable each service/socket
for service in "${services[@]}"; do
# Check if the service exists
if systemctl list-units --all | grep -q "^$service"; then
# Stop the service/socket
systemctl stop "$service"
# Disable the service/socket
systemctl disable "$service" > /dev/null
# Mask service/socket
systemctl --now mask "$service" > /dev/null
# Reload systemd after masking
systemctl daemon-reload
fi
done
conf_msg "High risk and unnecessary services disabled"
# === HIDEPID ===
# Create service to remount /proc, /dev/shm and /tmp
cat > /etc/systemd/system/solidcore-remount.service << EOF
# Inspired by Kicksecure's proc-hidepid.service
# https://raw.githubusercontent.com/Kicksecure/security-misc/master/lib/systemd/system/proc-hidepid.service
[Unit]
Description=Remounts existing /proc, /dev/shm and /tmp
DefaultDependencies=no
Before=sysinit.target
Requires=local-fs.target
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/bin/mount -o remount,hidepid=2 /proc
ExecStart=/bin/mount -o remount,noexec /dev/shm
ExecStart=/bin/mount -o remount,noexec /tmp
RemainAfterExit=yes
[Install]
WantedBy=sysinit.target
EOF
systemctl daemon-reload
systemctl enable solidcore-remount.service > /dev/null 2>&1
conf_msg "hidepid enabled for /proc"
# === FILE PERMISSIONS ===
# Ensure new files are only rwx by the user who created them
umask_script="/etc/profile.d/solidcore_umask.sh"
# Create the umask script
echo '#!/bin/bash' | tee "$umask_script" > /dev/null
echo 'umask 0077' | tee -a "$umask_script" > /dev/null
# Make the script executable
chmod +x "$umask_script"
conf_msg "Newly created files now only readable by user that created them"
# === DISABLE CORE DUMPS ===
# Temporarily disable core dumps until next reboot
ulimit -c 0
# Purge old core dumps
systemd-tmpfiles --clean 2> /dev/null
# Add a line to disable core dumps in limits.conf
echo "* hard core 0" | tee -a /etc/security/limits.conf > /dev/null
# Update the coredump.conf file
echo "[Coredump]" | tee /etc/systemd/coredump.conf > /dev/null
echo "Storage=none" | tee -a /etc/systemd/coredump.conf > /dev/null
echo "ProcessSizeMax=0" | tee -a /etc/systemd/coredump.conf > /dev/null
echo "ExternalSizeMax=0" | tee -a /etc/systemd/coredump.conf > /dev/null
# Reload systemctl configs
systemctl daemon-reload
conf_msg "Core dumps disabled"
# === PASSWORD POLICIES ===
# Change defaults
uncomment_and_modify() {
local file="$1"
local pattern="$2"
local replacement="$3"
# Use sed to uncomment and modify the line
sed -i "s/^# \(.*$pattern.*\)/\1/g" "$file"
sed -i "s/$pattern.*/$pattern $replacement/g" "$file"
}
# Modify pwquality.conf
pwquality_file="/etc/security/pwquality.conf"
# Uncomment and modify the specified lines
uncomment_and_modify "$pwquality_file" "minlen" "12"
uncomment_and_modify "$pwquality_file" "ucredit" "-1"
uncomment_and_modify "$pwquality_file" "lcredit" "-1"
uncomment_and_modify "$pwquality_file" "maxrepeat" "3"
uncomment_and_modify "$pwquality_file" "dictcheck" "1"
uncomment_and_modify "$pwquality_file" "enforcing" "1"
uncomment_and_modify "$pwquality_file" "retry" "5"
uncomment_and_modify "$pwquality_file" "enforce_for_root" ""
# Modify faillock.conf
faillock_file="/etc/security/faillock.conf"
# Uncomment and modify the specified lines
uncomment_and_modify "$faillock_file" "deny" "10" # increase lock limit to 10 attempts, as root account is locked and no one can unlock you
uncomment_and_modify "$faillock_file" "even_deny_root" "" # in case someone unlocks root
conf_msg "Applied stronger password requirements"
# Tighten access to console
echo "-:ALL EXCEPT (wheel):LOCAL" | tee -a /etc/security/access.conf > /dev/null
conf_msg "Only non-remote 'wheel' group members can access the console"
# Create a custom authselect profile called "solidcore"
if [ "$test_mode" == true ]; then
authselect create-profile solidcore -b sssd
else
authselect create-profile solidcore -b sssd > /dev/null 2>&1
fi
# Increase password delay from 2 second default to 5 seconds
new_delay="5000000"
# Define the files to modify
pwd_files=(
"/etc/authselect/custom/solidcore/password-auth"
"/etc/authselect/custom/solidcore/system-auth"
)
# Loop through the files and update the line
for file in "${pwd_files[@]}"; do
# Check if the file exists
if [ -f "$file" ]; then
# Use sed to replace the line with the new value
sed -i "s/\(auth\s*required\s*pam_faildelay.so\s*delay=\).*$/\1$new_delay/" "$file"
else
echo "File not found: $file"
fi
done
# Apply the custom profile
authselect select custom/solidcore with-pamaccess with-faillock without-nullok --quiet
conf_msg "Custom password profile 'solidcore' created and applied"
# === LOCK ROOT ===
# Uncomment the PermitRootLogin line in sshd_config, should someone ever enable it on their desktop
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PermitEmptyPasswords no/PermitEmptyPasswords no/' /etc/ssh/sshd_config
# Lock root account
if [ "$test_mode" == true ]; then
passwd -l root
else
passwd -l root > /dev/null
fi
conf_msg "Root account locked"
# === FIREWALL D ===
# Drop all incoming connections
if [ "$test_mode" == true ]; then
firewall-cmd --set-default-zone drop
firewall-cmd --reload
else
firewall-cmd --set-default-zone drop > /dev/null 2>&1
firewall-cmd --reload > /dev/null
fi
conf_msg "Firewalld updated so only outgoing connections are permitted"
# === MAC RANDOMIZATION ===
cat > /etc/NetworkManager/conf.d/00-solidcore.conf << EOF
[device]
wifi.scan-rand-mac-address=yes
[connection]
wifi.cloned-mac-address=random
ethernet.cloned-mac-address=random
EOF
# Restart NetworkManager
systemctl restart NetworkManager
# === CHRONY CONF ===
# Borrowed from GrapheneOS, keeping license intact
license_url="https://raw.githubusercontent.com/GrapheneOS/infrastructure/main/LICENSE"
chrony_url="https://raw.githubusercontent.com/GrapheneOS/infrastructure/main/chrony.conf"
mkdir -p ./tmp
wget -q -O ./tmp/LICENSE "$license_url"
sed 's/^/# /' ./tmp/LICENSE > ./tmp/LICENSE_temp
wget -q -O ./tmp/chrony.conf "$chrony_url"
systemctl stop chronyd.service
rm -rf /etc/chrony.conf
# Build new chrony.conf
cat ./tmp/LICENSE_temp >> /etc/chrony.conf
cat ./tmp/chrony.conf >> /etc/chrony.conf
# Update chronyd
sed -i 's/^OPTIONS=.*$/OPTIONS='"-F 1"'/' /etc/sysconfig/chronyd
# Clean up
systemctl start chronyd.service
rm -rf ./tmp
conf_msg "Chrony configuration updated (thanks GrapheneOS!)"
# === AUTOMATIC UPDATES ===
# RPM-OSTREE
# Update the rpm-ostree timer to trigger updates 10 minutes after boot and every 3 hours
timer_dir="/etc/systemd/system/rpm-ostreed-automatic.timer.d"
override_file="$timer_dir/override.conf"
# Check if the override.conf file exists
if [ -f "$override_file" ]; then
# Remove the original file
rm "$override_file"
fi
mkdir -p "$timer_dir"
cat > "$override_file" << EOF
[Unit]
Description=Run rpm-ostree updates 10 minutes after boot and every 3 hours
[Timer]
Persistent=True
OnBootSec=10min
OnCalendar=*-*-* *:0/3
[Install]
WantedBy=timers.target
EOF
# Update AutomaticUpdatePolicy to automatically stage updates
sed -i 's/^AutomaticUpdatePolicy=.*/AutomaticUpdatePolicy=stage/' /etc/rpm-ostreed.conf
# Reload systemd configuration after modifying the timer
systemctl daemon-reload
conf_msg "Automatic rpm-ostree update timer installed"
# FLATPAK
# Enable Flathub
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-modify --no-filter --enable flathub
# Change remotes of existing flathub apps
short_msg "Replacing Fedora flatpaks with Flathub versions. This will take a while..."
# Create undo script only if -server flag absent
if [[ "$server_mode" == false ]]; then
echo "#!/bin/bash" > /etc/solidcore/fedora_flatpak.sh
echo "flatpak remote-modify --enable fedora" >> /etc/solidcore/fedora_flatpak.sh
# Get a list of Fedora flatpaks and output install commands
flatpak list --app-runtime=org.fedoraproject.Platform --columns=application | tail -n +2 | while read -r flatpak_name; do
echo "flatpak install -y --noninteractive --reinstall fedora $flatpak_name" >> /etc/solidcore/fedora_flatpak.sh
done
# Make executable
chmod +x /etc/solidcore/fedora_flatpak.sh
fi # End of -server flag if statement
# Replace Fedora flatpaks and install flathub versions
flatpak list --app-runtime=org.fedoraproject.Platform --columns=application | tail -n +2 | while read -r flatpak_name; do
if [ "$test_mode" == true ]; then
flatpak install -y --noninteractive --reinstall flathub "$flatpak_name"
else
flatpak install -y --noninteractive --reinstall flathub "$flatpak_name" > /dev/null 2>&1
fi
done
conf_msg "Done"
# Disable Fedora flatpak repo
flatpak remote-modify --disable fedora
# Create the service file for Flatpak update
cat > /etc/systemd/system/flatpak-update.service << EOF
[Unit]
Description=Automatically update Flatpak applications
[Service]
Type=oneshot
ExecStart=/usr/bin/flatpak uninstall --unused -y --noninteractive && \
/usr/bin/flatpak update -y --noninteractive && \
/usr/bin/flatpak repair
EOF
# Create the timer file for Flatpak update
cat > /etc/systemd/system/flatpak-update.timer << EOF
[Unit]
Description=Run Flatpak updates 20 minutes after boot and every 3 hours and 10 minute
[Timer]
Persistent=True
OnBootSec=20min
OnCalendar=*-*-* *:10/3
[Install]
WantedBy=timers.target
EOF
# Reload systemd configuration after creating the files
systemctl daemon-reload
conf_msg "Automatic Flatpak update timer installed"
# === MISC ===
# Create a xdg autostart file to mute microphone on boot
cat > /etc/xdg/autostart/solidcore-mute-mic.desktop << EOF
[Desktop Entry]
Type=Application
Name=Solidcore Script to Mute Microphone on Boot
Exec=/usr/bin/amixer set Capture nocap
Icon=utilities-terminal
EOF
chmod 644 /etc/xdg/autostart/solidcore-mute-mic.desktop
# === INSTALLS ===
short_msg "Installing minisign (for dnscrypt-proxy installation & updates). This will also take a while..."
# Minisign
if [ "$test_mode" == true ]; then
rpm-ostree cancel -q
rpm-ostree install -q minisign
else
rpm-ostree cancel -q
rpm-ostree install -q minisign > /dev/null 2>&1
fi
conf_msg "Done"
# Flatseal
if [ "$test_mode" == true ]; then
flatpak install -y com.github.tchx84.Flatseal
else
flatpak install -y com.github.tchx84.Flatseal > /dev/null 2>&1
fi
conf_msg "Flatseal installed (for managing Flatpak permissions)"
# === SETUP FIRSTBOOT ===
# Check if solidcore-firstboot.sh exists in the working directory
if [ -e "$PWD/solidcore-firstboot.sh" ]; then
:
else
# Download solidcore-firstboot.sh
wget -q https://raw.githubusercontent.com/solidc0re/solidcore-scripts/main/solidcore-firstboot.sh
fi
# Make solidcore-firstboot.sh executable
chmod +x solidcore-firstboot.sh
# Create the directory if it doesn't exist
mkdir -p /etc/solidcore
# Move the file to /etc/solidcore/
mv -f "solidcore-firstboot.sh" "/etc/solidcore/"
# Create first boot loader
cat > /etc/solidcore/solidcore-welcome.sh << EOF
#!/bin/bash
## Solidcore Hardening Scripts for Fedora's rpm-ostree Operating Systems
## Version 0.2.7
##
## Copyright (C) 2023 solidc0re (https://github.com/solidc0re)
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see https://www.gnu.org/licenses/.
# Welcome script
# === RUN FIRSTBOOT ===
clear
echo ">"
echo ">"
echo "> Please enter your sudo password to continue with the solidcore process."
sudo bash /etc/solidcore/solidcore-firstboot.sh
EOF
# Make executable
chmod +x /etc/solidcore/solidcore-welcome.sh
# Create a xdg autostart file
cat > /etc/xdg/autostart/solidcore-welcome.desktop << EOF
[Desktop Entry]
Type=Application
Name=Solidcore Script to Run on First Boot
Exec=bash /etc/solidcore/solidcore-welcome.sh
Terminal=true
Icon=utilities-terminal
EOF
conf_msg "Set up first boot script"
# === INSTALL UNINSTALL SCRIPT ===
# Check if the -server flag is provided
if [[ "$server_mode" == false ]]; then
if [ -e "$PWD/solidcore-uninstall.sh" ]; then
:
else
# Download solidcore-uninstall.sh
wget -q https://raw.githubusercontent.com/solidc0re/solidcore-scripts/main/solidcore-uninstall.sh
fi
# Make solidcore-uninstall.sh executable
chmod u+x solidcore-uninstall.sh
# Move the file to /etc/solidcore/
mv -f "solidcore-uninstall.sh" "/etc/solidcore/"
# Create uninstall alias
cat > /etc/profile.d/solidcore.sh << EOF
solidcore() {
if [ "$1"= "uninstall" ]; then
shift
sudo bash /etc/solidcore/uninstall.sh
else
echo "solidcore: error - use 'solidcore uninstall' to uninstall."