-
Notifications
You must be signed in to change notification settings - Fork 4
/
ZFS-root.sh
executable file
·2391 lines (2075 loc) · 96.5 KB
/
ZFS-root.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
# https://www.osso.nl/blog/proxmox-virtio-blk-disk-by-id/
#
# packer with virtio-scsi as disk_interface creates
# /dev/disk/by-id/scsi-0QWMU_QEMU_HARDDISK_drive0
#
# Manual packer run using docker as a non-local/CICD run
# Must do "packer init" first with the whole "init ZFS-root_local.pkr.hcl" to populate .packer.d/plugins
# The volume for /root/.cache/packer is for persistent cache of the ISO download
# Could do this as a local run with a volume pointing to the ISO location
#
# ❯ docker run --rm -it -v "$(pwd)":"${PWD}" -w "${PWD}" --privileged --cap-add=ALL -v "${PWD}/.packer.d":/root/.cache/packer -v /usr/share/OVMF:/usr/share/OVMF -e PACKER_PLUGIN_PATH="${PWD}/.packer.d/plugins" -e PACKER_LOG=1 halfwalker/docker-qemu build ZFS-root_local.pkr.hcl
#
# Running locally
# ❯ packer build -var-file=ZFS-root-packer_local.vars.hcl ZFS-root_local.pkr.hcl
#
# Sample cmd to run a VM with the resulting disk image - needs virtio-scsi-pci to get
# disk to show up in /dev/disk/by-id
# ❯ kvm -no-reboot -m 2048 -drive file=packer-zfsroot-2023-07-23-1754,format=qcow2,cache=none -device virtio-scsi-pci,id=scsi0
# LUKS
# https://fossies.org/linux/cryptsetup/docs/Keyring.txt
# https://hamy.io/post/0009/how-to-install-luks-encrypted-ubuntu-18.04.x-server-and-enable-remote-unlocking/#gsc.tab=0
# https://www.arminpech.de/2019/12/23/debian-unlock-luks-root-partition-remotely-by-ssh-using-dropbear/
# Single script to setup zfsbootmenu root-on-zfs with dracut and dropbear
# https://github.com/Sithuk/ubuntu-server-zfsbootmenu/blob/main/ubuntu_server_encrypted_root_zfs.sh
# >>>>>>>>>> ZFS native encryption <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# https://arstechnica.com/gadgets/2021/06/a-quick-start-guide-to-openzfs-native-encryption/
# https://talldanestale.dk/2020/04/06/zfs-and-homedir-encryption/
### Simple script to unlock home dataset etc. at boot
# https://gbyte.dev/blog/unlock-mount-several-zfs-datasets-boot-single-passphrase
# https://github.com/dynerose/Remote-unlock-native-ZFS
# NOTE: Intesting ideas
# https://blobfolio.com/2018/06/replace-grub2-with-systemd-boot-on-ubuntu-18-04/
# https://github.com/dyindude/ubuntu-zfs
# systemd-boot and systemd-boot-manager
# https://forum.manjaro.org/t/manjaros-grub-probe-compatibility-with-zfs/127134/12
# dropbear remote unlocking
# https://hamy.io/post/0009/how-to-install-luks-encrypted-ubuntu-18.04.x-server-and-enable-remote-unlocking/
# remote key via https https://github.com/stupidpupil/https-keyscript
# Also look into tang keyserver
# AWS keyserver for luks
# https://icicimov.github.io/blog/server/LUKS-with-AWS-SSM-and-KMS-in-Systemd/
#
# This will set up a single-disk system with root-on-zfs, using
# bionic/18.04 or focal/20.04 or jammy/22.04 or noble/24.04.
#
# >>>>>>>>>> NOTE: This will totally overwrite the disk(s) chosen <<<<<<<<<<<<<
#
# 1) Boot an Ubuntu live cd to get a shell. Ubuntu live-server is a good choice.
# 2) Open a shell (ctrl-t) and become root (sudo -i)
# 3) Copy this script onto the box somehow - scp from somewhere
# 4) Make it executable (chmod +x ZFS-root.sh)
# 5) Run it (./ZFS-root.sh)
# 6) Add -d to enable set -x debugging (./ZFS-root.sh -d)
# 7) Add packerci to run in a CI/CD pipeline using ZFS-root-packerci.conf
#
# It will ask a few questions (username, which disk, bionic/focal etc)
# and then fully install a minimal Ubuntu system. Depending on the choices
# several partitions and zfs datasets will be created.
#
# Part Name Use
# ===========================================================================
# 1 BOOT EFI partition, also has syslinux
# 2 SWAP Only created if HIBERNATE is enabled (may be encrypted with LUKS)
# 3 ZFS Main zfs pool (rpool) for full system (rpool/ROOT/bionic)
#
# Datasets created
# ================
# rpool/ROOT/bionic Contains main system
# rpool/ROOT/bionic@base_install Snapshot of install main system
# rpool/home Container for user directories
# rpool/home/<username> Dataset for initial user
# rpool/home/root Dataset for root user
#
# One option is to enable LUKS full disk encryption. If HIBERNATE is enabled
# and a SWAP partition created, then that will be encrypted as well.
#
# NOTE: The HIBERNATE option will be disabled if the appropriate feature is not
# enabled in the power options of the system bios (/sys/power/state)
#
# NOTE: If installing under KVM, then the SCSI disk driver must be used,
# not the virtio one. Otherwise the disks will not be linked into the
# /dev/disk/by-id/ directory.
# Return codes from whiptail
# 0 OK in menu
# 1 Cancel in menu
# 255 Esc key hit
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
# Grab any possible pre-config settings in ZFS-root.conf
if [ -e ZFS-root.conf ] ; then
. ZFS-root.conf
fi
# ZFSBOOTMENU_BINARY_TYPE - use a downloaded binary or build locally
# = EFI (use EFI binary - NOTE: precludes syslinux from working)
# = KERNEL (use vmlinuz/initrd pair from downloaded binary)
# = LOCAL (built locally)
[[ ! -v ZFSBOOTMENU_BINARY_TYPE ]] && ZFSBOOTMENU_BINARY_TYPE=KERNEL
# ZFSBOOTMENU_REPO_TYPE - use the tagged git release or latest git clone
# = TAGGED
# = GIT
[[ ! -v ZFSBOOTMENU_REPO_TYPE ]] && ZFSBOOTMENU_REPO_TYPE=TAGGED
if [ "$1" = "packerci" ] ; then
# Ensure we pick up the packerci-specific config
if [ -e ZFS-root-packerci.conf ] ; then
echo "Setting ZFS-root-packerci variables"
. ZFS-root-packerci.conf
else
echo "ZFS-root-packerci.conf is MISSING - cannot run packer in CI/CD"
exit 1
fi
fi
# For SOF binaries, default to 2024.06
if [[ ! -v $SOF_VERSION ]] ; then
SOF_VERSION=2024.06
fi
# No magenta overrides for whiptail dialogs please
export NEWT_COLORS="none"
# Build location - will be removed prior to build
# NOTE: Can NOT have "zfs" in the name
ZFSBUILD=/mnt/builder
# Partition numbers of each partition
PARTITION_BOOT=1
PARTITION_SWAP=2
PARTITION_DATA=3
# ZFS encryption options
ZFSENC_ROOT_OPTIONS="-o encryption=aes-256-gcm -o keylocation=prompt -o keyformat=passphrase"
# NOTE: for keyfile, put key in local /etc/zfs, then later copy to target /etc/zfs
# to be used for encrypting /home
ZFSENC_HOME_OPTIONS="-o encryption=aes-256-gcm -o keylocation=file:///etc/zfs/zroot.homekey -o keyformat=passphrase"
# Check for a local apt-cacher-ng system - looking for these hosts
# aptcacher.local
# bondi.local
# First see if PROXY is already set in ZFS-root.conf
if [[ ! -v PROXY ]] ; then
echo "Searching for local apt-cacher-ng proxy systems ..."
PROXY=""
for CACHER in bondi.local aptcacher.local ; do
echo -n "... testing ${CACHER}"
CACHER=$(ping -w 2 -c 1 ${CACHER} | fgrep "bytes from" | cut -d' ' -f4)
if [ "${CACHER}" != "" ] ; then
echo " - found !"
PROXY="http://${CACHER}:3142/"
break
else
echo " - not found :("
fi
done
PROXY=$(whiptail --inputbox "Enter an apt proxy. Cancel or hit <esc> for no proxy" --title "APT proxy setup" 8 70 $(echo $PROXY) 3>&1 1>&2 2>&3)
RET=${?}
(( RET )) && PROXY=
fi # Check if PROXY is set already
if [ ${PROXY} ]; then
# export http_proxy=${PROXY}
# export ftp_proxy=${PROXY}
# This is for apt-get
echo "Acquire::http::proxy \"${PROXY}\";" > /etc/apt/apt.conf.d/03proxy
fi # PROXY
apt-get -qq update
apt-get -qq --no-install-recommends --yes install software-properties-common
apt-add-repository -y universe
# Need universe for debconf-utils
apt-get -qq --no-install-recommends --yes install debconf-utils
# Get userid and full name of main user
# First see if USERNAME or UCOMMENT are already set in ZFS-root.conf
if [[ ! -v USERNAME ]] || [[ ! -v UCOMMENT ]] ; then
[[ ! -v USERNAME ]] && USERNAME=deano
[[ ! -v UCOMMENT ]] && UCOMMENT="Dean Carpenter"
USERINFO=$(whiptail --inputbox "Enter username (login id) and full name of user\nAs in <username> <space> <First and Last name>\n\nlogin full name here\n|---| |------------ - - - - -" --title "User information" 11 70 "$(echo $USERNAME $UCOMMENT)" 3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 1 ]] && exit 1
USERNAME=$(echo $USERINFO | cut -d' ' -f1)
UCOMMENT=$(echo $USERINFO | cut -d' ' -f2-)
fi # Check if USERNAME/UCOMMENT set
# Get password, confirm and loop until confirmation OK
if [[ ! -v UPASSWORD ]]; then
DONE=false
until ${DONE} ; do
PW1=$(whiptail --passwordbox "Please enter a password for user $(echo $USERNAME)" 8 70 --title "User password" 3>&1 1>&2 2>&3)
PW2=$(whiptail --passwordbox "Please re-enter the password to confirm" 8 70 --title "User password confirmation" 3>&1 1>&2 2>&3)
[ "$PW1" = "$PW2" ] && DONE=true
done
UPASSWORD="$PW1"
fi # Check if UPASSWORD already set
# Hostname - cancel or blank name will exit
if [[ ! -v MYHOSTNAME ]] ; then
MYHOSTNAME=test
MYHOSTNAME=$(whiptail --inputbox "Enter hostname to be used for new system. This name may also be used for the main ZFS poolname." --title "Hostname for new system." 8 70 $(echo $MYHOSTNAME) 3>&1 1>&2 2>&3)
RET=${?}
(( RET )) && MYHOSTNAME=
if [ ! ${MYHOSTNAME} ]; then
echo "Must have a hostname"
exit 1
fi
fi # Check if MYHOSTNAME already set
if [[ ! -v POOLNAME ]] ; then
POOLNAME=${MYHOSTNAME}
POOLNAME=$(whiptail --inputbox "Enter poolname to use for main system - defaults to hostname" --title "ZFS main poolname" 8 70 $(echo $POOLNAME) 3>&1 1>&2 2>&3)
RET=${?}
(( RET )) && POOLNAME=
if [ ! ${POOLNAME} ]; then
echo "Must have a ZFS poolname"
exit 1
fi
fi # Check if POOLNAME already set
#
# If script was started with one parameter "packerci" then we're running under CI/CD
# and using packer to build an image via qemu. That means a single disk /dev/vda
# We need to create the symlink in /dev/disk/by-id for it
#
if [ "$1" = "packerci" ] ; then
echo "Setting single disk scsi-0QEMU_QEMU_HARDDISK_drive0"
readarray -t zfsdisks < <(echo "scsi-0QEMU_QEMU_HARDDISK_drive0")
else
# Set main disk here - be sure to include the FULL path
# Get list of disks, ask user which one to install to
# Ignore cdrom etc. Limit disk name length to avoid menu uglyness
readarray -t disks < <(ls -l /dev/disk/by-id | egrep -v '(CDROM|CDRW|-ROM|CDDVD|-part|md-|dm-|wwn-)' | sort -t '/' -k3 | tr -s " " | cut -d' ' -f9 | cut -c -58 | sed '/^$/d')
# If no disks available (kvm needs to use scsi, not virtio) then error out
if [ ${#disks[@]} -eq 0 ] ; then
whiptail --title "No disks available in /dev/disk/by-id" --msgbox "No valid disk links were found in /dev/disk/by-id - ensure your target disk has a link in that directory.\n\nKVM/qemu VMs need to use the SCSI storage driver, not the default virtio one (which does not create links in /dev/disk/by-id)" 12 70
exit 1
fi
TMPFILE=$(mktemp)
# Find longest disk name
m=-1
for disk in "${disks[@]}"
do
if [ ${#disk} -gt $m ]
then
m=${#disk}
fi
done
# Set dialog box size to num disks
list_height=$(( ${#disks[@]} + 1 ))
box_height=$(( ${#disks[@]} + 8 ))
box_width=$(( ${m} + 26 ))
DONE=false
until ${DONE} ; do
whiptail --title "List of disks" --separate-output --checklist --noitem \
"Choose disk(s) to install to" ${box_height} ${box_width} ${list_height} \
$( for disk in $(seq 0 $(( ${#disks[@]}-1)) ) ; do echo "${disks[${disk}]}" OFF ; done) 2> "${TMPFILE}"
RET=${?}
[[ ${RET} = 1 ]] && exit 1
readarray -t zfsdisks < <(cat ${TMPFILE})
if [ ${#zfsdisks[@]} != 0 ] ; then
DONE=true
fi
done
fi # Check for packerci
# Single disk can only be "single"
if [ ${#zfsdisks[@]} -eq 1 ] ; then
RAIDLEVEL="single"
fi
# Check if raid level already set in ZFS-root.conf
if [[ ! -v RAIDLEVEL ]] ; then
#_# DISK="/dev/disk/by-id/${DISK}"
if [ ${#zfsdisks[@]} -gt 1 ] ; then
RAIDLEVEL=$(whiptail --title "ZPOOL raid level" --radiolist "Select ZPOOL raid level" 12 60 5 \
single "No raid, just single disks as vdevs" OFF \
mirror "All disks mirrored" OFF \
raidz1 "All disks in raidz1 format" OFF \
raidz2 "All disks in raidz2 format" OFF \
raidz3 "All disks in raidz3 format" OFF 3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 1 ]] && exit 1
fi
fi # Check RAIDLEVEL already set
# We use ${RAIDLEVEL} to set zpool raid level - just vdevs means that should be blank
if [ "${RAIDLEVEL}" = "single" ] ; then RAIDLEVEL= ; fi
if [[ ! -v DISCENC ]] ; then
DISCENC=$(whiptail --title "Select disk encryption" --radiolist "Choose which (if any) disk encryption to use" 11 60 4 \
NOENC "No disk encryption" ON \
ZFSENC "Enable ZFS dataset encryption" OFF \
LUKS "Enable LUKS full disk encryption" OFF \
3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 1 ]] && exit 1
fi # Check DISCENC already set
# If encryption enabled, need a passphrase
if [ "${DISCENC}" != "NOENC" ] ; then
if [[ ! -v PASSPHRASE ]] ; then
DONE=false
until ${DONE} ; do
PW1=$(whiptail --passwordbox "Please enter a good long encryption passphrase" 8 70 --title "Encryption passphrase" 3>&1 1>&2 2>&3)
PW2=$(whiptail --passwordbox "Please re-enter the encryption passphrase" 8 70 --title "Encryption passphrase confirmation" 3>&1 1>&2 2>&3)
[ "$PW1" = "$PW2" ] && DONE=true
done
PASSPHRASE="$PW1"
fi # If PASSPHRASE not already set in ZFS-root.conf
# retcode 0 = YES, 1 = NO
if [[ ! -v DROPBEAR ]] ; then
DROPBEAR=$(whiptail --title "Enable Dropbear ?" --yesno "Should Dropbear be enabled for remote unlocking of encrypted disks ?" 8 60 \
3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 0 ]] && DROPBEAR=y
[[ ${RET} = 1 ]] && DROPBEAR=n
fi
else
# Default Dropbear to NO
DROPBEAR=n
fi
# We check /sys/power/state - if no "disk" in there, then HIBERNATE is disabled
cat /sys/power/state | fgrep disk > /dev/null
HIBERNATE_AVAIL=${?}
# Force Hibernate to n if not available, overriding anything in ZFS-root.conf
[ ${HIBERNATE_AVAIL} -ne 0 ] && HIBERNATE=n
#
# Slightly fugly - have to check if ANY of these are not set
#
if [[ ! -v ZREPL ]] || [[ ! -v RESCUE ]] || [[ ! -v GOOGLE ]] || [[ ! -v HWE ]] || [[ ! -v ZFSPPA ]] || [[ ! -v HIBERNATE ]] || [[ ! -v DELAY ]] || [[ ! -v SOF ]] || [[ ! -v GNOME ]] || [[ ! -v KDE ]] || [[ ! -v NEON ]] || [[ ! -v XFCE ]] ; then
# Hibernate can only resume from a single disk, and currently not available for ZFS encryption
if [ "${DISCENC}" == "ZFSENC" ] || [ ${#zfsdisks[@]} -gt 1 ] || [ ${HIBERNATE_AVAIL} -ne 0 ] ; then
# Set basic options for install - ZFSENC so no Hibernate available (yet)
whiptail --title "Set options to install" --separate-output --checklist "Choose options\n\nNOTE: 18.04 HWE kernel requires pool attribute dnodesize=legacy" 22 89 11 \
RESCUE "Create rescue dataset by cloning initial install" OFF \
GOOGLE "Add google authenticator via pam for ssh logins" OFF \
HWE "Install Hardware Enablement kernel" OFF \
ZFSPPA "Update to latest ZFS 2.1 from PPA" OFF \
ZREPL "Install Zrepl zfs snapshot manager" OFF \
DELAY "Add delay before importing root pool - for many-disk systems" OFF \
SOF "Install Sound Open Firmware binaries ${SOF_VERSION} (for some laptops)" OFF \
GNOME "Install Ubuntu Gnome desktop" OFF \
XFCE "Install Ubuntu xfce4 desktop with goodies" OFF \
KDE "Install Ubuntu KDE Plasma desktop" OFF \
NEON "Install Neon KDE Plasma desktop" OFF 2>"${TMPFILE}"
else
# Set basic options for install - ZFSENC so no Hibernate available (yet)
whiptail --title "Set options to install" --separate-output --checklist "Choose options\n\nNOTE: 18.04 HWE kernel requires pool attribute dnodesize=legacy" 23 89 12 \
RESCUE "Create rescue dataset by cloning initial install" OFF \
GOOGLE "Add google authenticator via pam for ssh logins" OFF \
HWE "Install Hardware Enablement kernel" OFF \
ZFSPPA "Update to latest ZFS 2.1 from PPA" OFF \
ZREPL "Install Zrepl zfs snapshot manager" OFF \
HIBERNATE "Enable swap partition for hibernation" OFF \
DELAY "Add delay before importing root pool - for many-disk systems" OFF \
SOF "Install Sound Open Firmware binaries ${SOF_VERSION} (for some laptops)" OFF \
GNOME "Install Ubuntu Gnome desktop" OFF \
XFCE "Install Ubuntu xfce4 desktop with goodies" OFF \
KDE "Install Ubuntu KDE Plasma desktop" OFF \
NEON "Install Neon KDE Plasma desktop" OFF 2>"${TMPFILE}"
fi
RET=${?}
[[ ${RET} = 1 ]] && exit 1
# Set any selected options to 'y'
while read -r TODO ; do
eval "${TODO}"='y'
done < "${TMPFILE}"
# Any options not enabled in the basic options menu we now set to 'n'
for option in ZREPL RESCUE GNOME XFCE NEON KDE HWE HIBERNATE ZFSPPA DELAY SOF GOOGLE; do
[ ${!option} ] || eval "${option}"='n'
done
fi # Check ALL options from ZFS-root.conf
# See if we need to install Nvidia drivers, notify if so
if [[ ! -v NVIDIA ]] ; then
NVIDIA=none
if [ ${GNOME} = "y" ] || [ ${KDE} = "y" ] || [ ${NEON} = "y" ] || [ ${XFCE} = "y" ] ; then
if [ $(lspci | fgrep -i nvidia | wc -l) -gt 0 ] ; then
# Installing Nvidia PPA here just so we can search for versions
apt-add-repository --yes --update ppa:graphics-drivers/ppa
NVIDIA_LATEST=$(apt-cache search nvidia-driver- | cut -d ' ' -f1 | grep -e "nvidia-driver-...$" | cut -d'-' -f3 | sort | tail -1)
NVIDIA=$(whiptail --title "Nvidia Hardware detected - install latest driver ?" --radiolist "Gnome/KDE/NEON was selected, and Nvidia graphics HW was detected on this system. The ppa:graphics-drivers/ppa repo could be installed in order to get the binary Nvidia driver\n\nNOTE: Be sure to select the correct driver - the latest (${NVIDIA_LATEST}) may not support older legacy HW. See\n\nhttps://www.nvidia.com/en-us/drivers/unix/legacy-gpu/\n\nfor more information on legacy HW. It is safe to select NONE if you are unsure. You can always install the appropriate driver later via Additional Drivers" 22 70 4 \
${NVIDIA_LATEST} "Latest ${NVIDIA_LATEST}" OFF \
470 "Legacy 470 driver" OFF \
390 "Legacy 390 driver" OFF \
none "No Nvidia driver" ON \
3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 1 ]] && exit 1
fi
fi
fi
# Show google authenticator info - file in /root/google_auth.txt is like
# AGNGG2UOIDJXDJNZ
# "RATE_LIMIT 3 30
# " WINDOW_SIZE 3
# " DISALLOW_REUSE
# " TOTP_AUTH
# 75667428
# 93553495
# 65484719
# 23383624
# 28747791
if [ ${GOOGLE} = "y" ] ; then
apt-get -qq --no-install-recommends --yes install python3-qrcode libpam-google-authenticator qrencode
# Generate a google auth config
google-authenticator --time-based --disallow-reuse --label=${MYHOSTNAME} --qr-mode=UTF8 --rate-limit=3 --rate-time=30 --secret=/tmp/google_auth.txt --window-size=3 --force --quiet
# Grab secret to build otpauth line below
GOOGLE_SECRET=$(head -1 /tmp/google_auth.txt)
# Have to tell whiptail library newt to use black/white text, otherwise QR code
# is inverted and Authy can't read it
# Set issuer to Ubuntu so we get a nice Ubuntu logo for the Authy secret
export NEWT_COLORS='white,black'
whiptail --title "Google Authenticator QR code and config" --msgbox "Config for ${USERNAME} is in /home/${USERNAME}/.google_authenticator\n\nBe sure to save the 5 emergency codes below\n\n$(cat /tmp/google_auth.txt)\n\nQR Code for use with OTP application (Authy etc.)\notpauth://totp/${MYHOSTNAME}.local:${USERNAME}?secret=${GOOGLE_SECRET}&Issuer=Ubuntu\n\n$(qrencode -m 3 -t UTF8 otpauth://totp/${MYHOSTNAME}.local:${USERNAME}?secret=${GOOGLE_SECRET}&issuer=Ubuntu)" 45 83
RET=${?}
[[ ${RET} = 1 ]] && exit 1
export NEWT_COLORS="none"
fi
# SSH authorized keys from github for dropbear and ssh
if [[ ! -v AUTHKEYS ]] ; then
AUTHKEYS=$(whiptail --inputbox "Dropbear and ssh need authorized ssh pubkeys to allow access to the server. Please enter any github users to pull ssh pubkeys from. none means no keys to install\n\nDropbear is used for remote unlocking of disk encryption\n\n ssh -p 222 root@<ip addr>" --title "SSH pubkeys for ssh and dropbear" 13 70 $(echo none) 3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 1 ]] && exit 1
(( RET )) && AUTHKEYS=none
fi # Check for github user ssh keys in AUTHKEYS
# If it's NOT a ZFS encryption setup, then clear out the ZFSENC_ROOT_OPTIONS variable
if [ "${DISCENC}" != "ZFSENC" ] ; then
ZFSENC_ROOT_OPTIONS=""
ZFSENC_HOME_OPTIONS=""
fi
# Swap size - if HIBERNATE enabled then this will be an actual disk partition.
# If DISCENC == LUKS then partition will be encrypted. If SIZE_SWAP is not
# defined here, then will be calculated to accomodate memory size (plus fudge factor).
if [[ ! -v SIZE_SWAP ]] ; then
MEMTOTAL=$(cat /proc/meminfo | grep -F MemTotal | tr -s ' ' | cut -d' ' -f2)
SIZE_SWAP=$(( (MEMTOTAL + 20480) / 1024 ))
# We MUST have a swap partition of at least ram size if HIBERNATE is enabled
# So don't even prompt the user for a size. Her own silly fault if it's
# enabled but she doesn't want a swap partition
if [ ${HIBERNATE} = "n" ] ; then
SIZE_SWAP=$(whiptail --inputbox "If HIBERNATE enabled then this will be a disk partition otherwise it will be a regular ZFS dataset. If LUKS enabled then the partition will be encrypted.\nIf SWAP size not set here (left blank), then it will be calculated to accomodate memory size. Set to zero (0) to disable swap.\n\nSize of swap space in megabytes (default is calculated value)\nSet to zero (0) to disable swap" \
--title "SWAP size" 15 70 $(echo $SIZE_SWAP) 3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 1 ]] && exit 1
fi
fi # Check for Swap size in ZFS-root.conf
# Use zswap compressed page cache in front of swap ? https://wiki.archlinux.org/index.php/Zswap
# Only used for swap partition (encrypted or not)
USE_ZSWAP="zswap.enabled=1 zswap.compressor=lz4 zswap.max_pool_percent=25"
# What suite is this script running under ? bionic or focal
# Xenial does not support a couple of zfs feature flags, so have to
# not use them when creating the pools, even if the target system
# is bionic. Pool can be upgraded after booting into the target.
SCRIPT_SUITE=$(lsb_release -cs)
# Suite to install - bionic focal jammy noble
if [[ ! -v SUITE ]] ; then
SUITE=$(whiptail --title "Select Ubuntu distribtion" --radiolist "Choose distro" 12 50 6 \
noble"24.04 noble" ON \
jammy "22.04 jammy" ON \
focal "20.04 focal" OFF \
bionic "18.04 Bionic" OFF \
3>&1 1>&2 2>&3)
RET=${?}
[[ ${RET} = 1 ]] && exit 1
fi # Check for Ubuntu suite to install
#
# TODO: Make use of SUITE_EXTRAS maybe
#
case ${SUITE} in
noble)
SUITE_NUM="24.04"
SUITE_EXTRAS="netplan.io expect"
SUITE_BOOTSTRAP="wget,whois,rsync,gdisk,netplan.io,gpg-agent"
# Install HWE packages - set to blank or to "-hwe-24.04"
# Gets tacked on to various packages below
[ "${HWE}" = "y" ] && HWE="-hwe-${SUITE_NUM}" || HWE=
# Specific zpool features available in jammy
SUITE_ROOT_POOL="-O dnodesize=auto"
;;
jammy)
SUITE_NUM="22.04"
SUITE_EXTRAS="netplan.io expect"
SUITE_BOOTSTRAP="wget,whois,rsync,gdisk,netplan.io,gpg-agent"
# Install HWE packages - set to blank or to "-hwe-22.04"
# Gets tacked on to various packages below
[ "${HWE}" = "y" ] && HWE="-hwe-${SUITE_NUM}" || HWE=
# Specific zpool features available in jammy
SUITE_ROOT_POOL="-O dnodesize=auto"
# If ZFSPPA is off (not using latest zfs) must set dnodesize=legacy
# otherwise cannot set bootfs property on pool
if [ "${ZFSPPA}" = "n" ] ; then
SUITE_ROOT_POOL="-O dnodesize=legacy"
fi
;;
focal)
SUITE_NUM="20.04"
SUITE_EXTRAS="netplan.io expect"
SUITE_BOOTSTRAP="wget,whois,rsync,gdisk,netplan.io"
# Install HWE packages - set to blank or to "-hwe-20.04"
# Gets tacked on to various packages below
[ "${HWE}" = "y" ] && HWE="-hwe-${SUITE_NUM}" || HWE=
# Specific zpool features available in focal
SUITE_ROOT_POOL="-O dnodesize=auto"
# If ZFSPPA is off (not using latest zfs) must set dnodesize=legacy
# otherwise cannot set bootfs property on pool
if [ "${ZFSPPA}" = "n" ] ; then
SUITE_ROOT_POOL="-O dnodesize=legacy"
fi
;;
bionic)
SUITE_NUM="18.04"
SUITE_EXTRAS="netplan.io expect"
SUITE_BOOTSTRAP="wget,whois,rsync,gdisk,netplan.io"
# Install HWE packages - set to blank or to "-hwe-18.04"
# Gets tacked on to various packages below
[ "${HWE}" = "y" ] && HWE="-hwe-${SUITE_NUM}" || HWE=
# Specific zpool features available in bionic
SUITE_ROOT_POOL="-O dnodesize=legacy"
;;
# Default to focal 20.04
*)
SUITE_NUM="20.04"
SUITE_EXTRAS="netplan.io expect"
SUITE_BOOTSTRAP="wget,whois,rsync,gdisk,netplan.io"
# Install HWE packages - set to blank or to "-hwe-20.04"
# Gets tacked on to various packages below
[ "${HWE}" = "y" ] && HWE="-hwe-${SUITE_NUM}" || HWE=
# Specific zpool features available in focal
SUITE_ROOT_POOL="-O dnodesize=auto"
# If ZFSPPA is off (not using latest zfs) must set dnodesize=legacy
# otherwise cannot set bootfs property on pool
if [ "${ZFSPPA}" = "n" ] ; then
SUITE_ROOT_POOL="-O dnodesize=legacy"
fi
;;
esac
#
# If script was started with one parameter "packerci" then we're running under CI/CD
# and using packer to build an image via qemu. That means a single disk /dev/vda was
# selected above and we do not want to pause here for
#
if [ "$1" != "packerci" ] ; then
box_height=$(( ${#zfsdisks[@]} + 28 ))
whiptail --title "Summary of install options" --msgbox "These are the options we're about to install with :\n\n \
Proxy $([ ${PROXY} ] && echo ${PROXY} || echo None)\n \
$(echo $SUITE $SUITE_NUM) $([ ${HWE} ] && echo WITH || echo without) $(echo hwe kernel ${HWE})\n \
Disk $(for disk in $(seq 0 $(( ${#zfsdisks[@]}-1)) ) ; do \
if [ ${disk} -ne 0 ] ; then echo -n " " ; fi ; echo ${zfsdisks[${disk}]} ; done)\n \
Raid $([ ${RAIDLEVEL} ] && echo ${RAIDLEVEL} || echo vdevs)\n \
Hostname $(echo $MYHOSTNAME)\n \
Poolname $(echo $POOLNAME)\n \
User $(echo $USERNAME $UCOMMENT)\n\n \
RESCUE = $(echo $RESCUE) : Create rescue dataset by cloning install\n \
DELAY = $(echo $DELAY) : Enable delay before importing zpool\n \
ZFS ver = $(echo $ZFSPPA) : Update to latest ZFS 2.1 via PPA\n \
ZREPL = $(echo $ZREPL) : Install Zrepl zfs snapshot manager\n \
GOOGLE = $(echo $GOOGLE) : Install google authenticator\n \
GNOME = $(echo $GNOME) : Install Ubuntu Gnome desktop\n \
XFCE = $(echo $XFCE) : Install Ubuntu XFCE4 desktop\n \
KDE = $(echo $KDE) : Install Ubuntu KDE Plasma desktop\n \
NEON = $(echo $NEON) : Install Neon KDE Plasma desktop\n \
NVIDIA = $(echo $NVIDIA) : Install Nvidia drivers\n \
SOF = $(echo $SOF) : Install Sound Open Firmware ${SOF_VERSION} binaries\n \
HIBERNATE = $(echo $HIBERNATE) : Enable SWAP disk partition for hibernation\n \
DISCENC = $(echo $DISCENC) : Enable disk encryption (No, LUKS, ZFS)\n \
DROPBEAR = $(echo $DROPBEAR) : Enable Dropbear unlocking of encrypted disks\n \
Swap size = $(echo $SIZE_SWAP)M $([ ${SIZE_SWAP} -eq 0 ] && echo ': DISABLED')\n" \
${box_height} 76
RET=${?}
[[ ${RET} = 1 ]] && exit 1
fi # Check for packerci
# Log everything we do
rm -f /root/ZFS-setup.log
exec > >(tee -a "/root/ZFS-setup.log") 2>&1
[ "$1" = "-d" ] && set -x
[ "$1" = "packerci" ] && set -x
# Log all the variables used
cat << EOF
==========================================================================
MYHOSTNAME = ${MYHOSTNAME}
RESCUE = ${RESCUE}
BOOTDEVRAW = ${BOOTDEVRAW}
DELAY = ${DELAY}
SUITE = ${SUITE}
POOLNAME = ${POOLNAME}
USERNAME = ${USERNAME}
UCOMMENT = "${UCOMMENT}"
AUTHKEYS = ${AUTHKEYS}
DISCENC = ${DISCENC}
DROPBEAR = ${DROPBEAR}
ZFSPPA = ${ZFSPPA}
ZREPL = ${ZREPL}
GOOGLE = ${GOOGLE}
SOF = ${SOF}
SOF_VERSION = ${SOF_VERSION}
PROXY = ${PROXY}
HWE = ${HWE}
GNOME = ${GNOME}
XFCE = ${XFCE}
NEON = ${NEON}
KDE = ${KDE}
NVIDIA = ${NVIDIA}
HIBERNATE = ${HIBERNATE}
SIZE_SWAP = ${SIZE_SWAP}
PARTITION_BOOT = ${PARTITION_BOOT}
PARTITION_SWAP = ${PARTITION_SWAP}
PARTITION_DATA = ${PARTITION_DATA}
ZFSBOOTMENU_BINARY_TYPE = ${ZFSBOOTMENU_BINARY_TYPE}
ZFSBOOTMENU_REPO_TYPE = ${ZFSBOOTMENU_REPO_TYPE}
==========================================================================
EOF
# Pre-OK the zfs-dkms licenses notification
cat > /tmp/selections <<-EOFPRE
# zfs-dkms license notification
zfs-dkms zfs-dkms/note-incompatible-licenses note
EOFPRE
cat /tmp/selections | debconf-set-selections
# In case ZFS is already installed in this liveCD, check versions to see
# if we need to update/upgrade
# NOTE: Chances are that the kernel module is (eg) 0.8.x and the packages are 0.7.x
# so we may as well just upgrade to latest by PPA. Which means building
# the newest module, which can take a while.
# Update ZFS if module mismatch, ZFS encryption selected or update-zfs selected
# Check if ZFS currently installed in this livecd env
ZFS_LIVECD=
if [ -f /usr/sbin/zfs ] || [ -f /sbin/zfs ] ; then
# Get currently installed version
ZFS_INSTALLED=$(dpkg -s zfsutils-linux | grep -F Version | cut -d' ' -f2)
modprobe zfs
ZFS_MODULE=$(cat /sys/module/zfs/version)
ZFS_LIVECD=y
fi
[ "$ZFS_LIVECD" = "y" ] && echo "ZFS installed with ${ZFS_INSTALLED}, module with ${ZFS_MODULE}"
# Add ZFS ppa if requested
# if [ ${ZFSPPA} = "y" ] ; then
# apt-add-repository --yes --update ppa:jonathonf/zfs
# fi
# NOW, install ZFS, perhaps from ppa above
apt-get -qq update
apt-get --no-install-recommends --yes install zfsutils-linux zfs-zed
# Deprecated - not using dkms version, so new kernel/modules not built
# Have to live with livecd zfs module version
# Logic for restarting ZFS
# If livecd package version != currently running module, OR
# If ppa requested
# Then restart ZFS
# if [[ ("${ZFS_LIVECD}" = "y" && "${ZFS_INSTALLED}" != "${ZFS_MODULE}") || "${ZFSPPA}" = "y" ]] ; then
# echo "ZFS needs an update"
# systemctl stop zfs-zed
# modprobe -r zfs
# modprobe zfs
# systemctl start zfs-zed
# # ensure new system uses updated ZFS
# ZFSPPA="y"
# fi
# Create an encryption key for LUKs partitions
if [ "${DISCENC}" = "LUKS" ] ; then
dd if=/dev/urandom of=/etc/zfs/zroot.rawkey bs=32 count=1
fi
# Put zfs encryption key into place
# We use two keys so the user can change the home dataset to something else if desired
if [ "${DISCENC}" = "ZFSENC" ] ; then
echo "${PASSPHRASE}" > /etc/zfs/zroot.key
echo "${PASSPHRASE}" > /etc/zfs/zroot.homekey
chmod 000 /etc/zfs/zroot.key /etc/zfs/zroot.homekey
fi
apt-get -qq --no-install-recommends --yes install openssh-server debootstrap gdisk dosfstools mdadm
# Unmount any mdadm disks that might have been automounted
# Stop all found mdadm arrays - again, just in case. Sheesh.
find /dev -iname md* -type b -exec bash -c "umount {} > /dev/null 2>&1 ; mdadm --stop --force {} > /dev/null 2>&1 ; mdadm --remove {} > /dev/null 2>&1" \;
for disk in $(seq 0 $(( ${#zfsdisks[@]} - 1))) ; do
zpool labelclear -f /dev/disk/by-id/${zfsdisks[${disk}]}
# Wipe mdadm superblock from all partitions found, even if not md raid partition
mdadm --zero-superblock --force /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_BOOT} > /dev/null 2>&1
wipefs --all --force /dev/disk/by-id/${zfsdisks[${disk}]}
sgdisk --zap-all /dev/disk/by-id/${zfsdisks[${disk}]}
sgdisk --clear /dev/disk/by-id/${zfsdisks[${disk}]}
# Legacy (BIOS) booting
sgdisk -a 1 /dev/disk/by-id/${zfsdisks[${disk}]} # Set sector alignment to 1MiB
sgdisk -n ${PARTITION_BOOT}:1M:+1000M /dev/disk/by-id/${zfsdisks[${disk}]} # Create partition 1/BOOT 1M size
sgdisk -A ${PARTITION_BOOT}:set:2 /dev/disk/by-id/${zfsdisks[${disk}]} # Turn legacy boot attribute on
sgdisk -c ${PARTITION_BOOT}:"BOOT_EFI_${disk}" /dev/disk/by-id/${zfsdisks[${disk}]} # Set partition name to BOOT_EFI_n
sgdisk -t ${PARTITION_BOOT}:EF00 /dev/disk/by-id/${zfsdisks[${disk}]} # Set partition type to EFI
#
# TODO: figure out partitions for both ZFS and LUKS encryption
# both swap and main partitions
#
# For laptop hibernate need swap partition, encrypted or not
if [ "${HIBERNATE}" = "y" ] ; then
if [ "${DISCENC}" != "NOENC" ] ; then
# ZFS or LUKS Encrypted - should be partition type 8309 (Linux LUKS)
sgdisk -n ${PARTITION_SWAP}:0:+${SIZE_SWAP}M -c ${PARTITION_SWAP}:"SWAP_${disk}" -t ${PARTITION_SWAP}:8309 /dev/disk/by-id/${zfsdisks[${disk}]}
else
sgdisk -n ${PARTITION_SWAP}:0:+${SIZE_SWAP}M -c ${PARTITION_SWAP}:"SWAP_${disk}" -t ${PARTITION_SWAP}:8200 /dev/disk/by-id/${zfsdisks[${disk}]}
fi # DISCENC for ZFS or LUKS
fi # HIBERNATE
# Main data partition for root
if [ ${DISCENC} = "LUKS" ] ; then
# LUKS Encrypted - should be partition type 8309 (Linux LUKS)
# wipefs --all --force /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_DATA}
zpool labelclear -f /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_DATA}
sgdisk -n ${PARTITION_DATA}:0:0 -c ${PARTITION_DATA}:"ZFS_${disk}" -t ${PARTITION_DATA}:8300 /dev/disk/by-id/${zfsdisks[${disk}]}
apt-get -qq --no-install-recommends --yes install cryptsetup
else
# Unencrypted or ZFS encrypted
sgdisk -n ${PARTITION_DATA}:0:0 -c ${PARTITION_DATA}:"ZFS_${disk}" -t ${PARTITION_DATA}:BF00 /dev/disk/by-id/${zfsdisks[${disk}]}
fi # DISCENC for LUKS
done
# Refresh partition information
partprobe
# Have to wait a bit for the partitions to actually show up
echo "Wait for partition info to settle out"
sleep 5
# Build list of partitions to use for ...
# Boot partition (mirror across all disks)
PARTSBOOT=
PARTSSWAP=
# ZFS partitions to create zpool with
ZPOOLDISK=
for disk in $(seq 0 $(( ${#zfsdisks[@]} - 1))) ; do
PARTSSWAP="/dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_SWAP} ${PARTSSWAP}"
PARTSBOOT="/dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_BOOT} ${PARTSBOOT}"
if [ "${DISCENC}" = "LUKS" ]; then
ZPOOLDISK="/dev/mapper/root_crypt${disk} ${ZPOOLDISK}"
else
ZPOOLDISK="/dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_DATA} ${ZPOOLDISK}"
fi
done
# Create SWAP volume for HIBERNATE, encrypted maybe
# Just using individual swap partitions - could use mdadm to mirror/raid
# them up, but meh, why ?
# NOTE: Need --disable-keyring so we can pull the derived key from the encrypted partition
# otherwise it's in the kernel keyring
if [ "${HIBERNATE}" = "y" ] ; then
# Hibernate, so we need a real swap partition(s)
for disk in $(seq 0 $(( ${#zfsdisks[@]} - 1))) ; do
case ${DISCENC} in
LUKS)
echo "Encrypting swap partition ${disk} size ${SIZE_SWAP}M"
echo "${PASSPHRASE}" | cryptsetup luksFormat --type luks2 --disable-keyring -c aes-xts-plain64 -s 512 -h sha256 /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_SWAP}
echo "${PASSPHRASE}" | cryptsetup luksOpen --disable-keyring /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_SWAP} swap_crypt${disk}
mkswap -f /dev/mapper/swap_crypt${disk}
if [ ${disk} -eq 0 ] ; then
# Get derived key to insert into other encrypted devices
# To be more secure do this into a small ramdisk
# swap must be opened 1st to enable resume from hibernation
/lib/cryptsetup/scripts/decrypt_derived swap_crypt${disk} > /tmp/key
fi
# Add the derived key to all the other devices
echo "${PASSPHRASE}" | cryptsetup luksAddKey /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_SWAP} /tmp/key
# Add the generated key from /etc/zfs/zroot.rawkey
echo "${PASSPHRASE}" | cryptsetup luksAddKey /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_SWAP} /etc/zfs/zroot.rawkey
;;
ZFSENC)
# ZFS encryption can just use a regular partition
mkswap -f /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_SWAP}
;;
NOENC)
# Not LUKS, so just use a regular partition
mkswap -f /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_SWAP}
;;
esac
done
fi #HIBERNATE
# Encrypt root volume maybe
# NOTE: Need --disable-keyring so we can pull the derived key from the encrypted partition
# otherwise it's in the kernel keyring
if [ "${DISCENC}" = "LUKS" ] ; then
for disk in $(seq 0 $(( ${#zfsdisks[@]} - 1))) ; do
# Encrypted LUKS root
echo "Encrypting root ZFS ${disk}"
echo "${PASSPHRASE}" | cryptsetup luksFormat --type luks2 -c aes-xts-plain64 -s 512 -h sha256 /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_DATA}
echo "${PASSPHRASE}" | cryptsetup luksOpen /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_DATA} root_crypt${disk}
# If no encrypted SWAP then use 1st root device as derived key
# otherwise assume derived key was created above in "Create SWAP volume"
if [ ${disk} -eq 0 ] && [ ${HIBERNATE} = "n" ] ; then
# Get derived key to insert into other encrypted devices
# To be more secure do this into a small ramdisk
/lib/cryptsetup/scripts/decrypt_derived root_crypt${disk} > /tmp/key
fi
# Add the derived key to all the other devices
echo "${PASSPHRASE}" | cryptsetup luksAddKey /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_DATA} /tmp/key
# Add the generated key from /etc/zfs/zroot.rawkey
echo "${PASSPHRASE}" | cryptsetup luksAddKey /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_DATA} /etc/zfs/zroot.rawkey
done
fi
# COMPLETELY clear out build dir
rm -rf ${ZFSBUILD}
mkdir -p ${ZFSBUILD}
# Create root pool
case ${DISCENC} in
LUKS)
echo "Creating root pool ${POOLNAME}"
zpool create -f -o ashift=12 -o autotrim=on ${SUITE_ROOT_POOL} \
-O acltype=posixacl -O canmount=off -O compression=lz4 \
-O atime=off \
-O normalization=formD -O relatime=on -O xattr=sa \
-O mountpoint=/ -R ${ZFSBUILD} \
${POOLNAME} ${RAIDLEVEL} ${ZPOOLDISK}
;;
# With ZFS encryption we don't encrypt the pool, we encrypt individual
# datasets hierarchies
NOENC|ZFSENC)
# Unencrypted
# Certain features must be disabled to boot
# -o feature@project_quota=disabled \
# -o feature@spacemap_v2=disabled \
echo "Creating root pool ${POOLNAME}"
zpool create -f -o ashift=12 -o autotrim=on ${SUITE_ROOT_POOL} \
-O acltype=posixacl -O canmount=off -O compression=lz4 \
-O atime=off \
-O normalization=formD -O relatime=on -O xattr=sa \
-O mountpoint=none -R ${ZFSBUILD} \
${POOLNAME} ${RAIDLEVEL} ${ZPOOLDISK}
;;
*)
# Unknown option
echo "Unknown option DISCENC = ${DISCENC}"
exit 1
;;
esac
# Main filesystem datasets
echo "Creating main zfs datasets"
# Container for root filesystems - possibly zfs native encrypted
if [ "${DISCENC}" = "ZFSENC" ] ; then
echo "${PASSPHRASE}" | zfs create -o canmount=off -o mountpoint=none ${ZFSENC_ROOT_OPTIONS} ${POOLNAME}/ROOT
else
zfs create -o canmount=off -o mountpoint=none ${POOLNAME}/ROOT
fi
# Actual dataset for suite we are installing now
zfs create -o canmount=noauto -o mountpoint=/ \
${POOLNAME}/ROOT/${SUITE}
zpool set bootfs=${POOLNAME}/ROOT/${SUITE} ${POOLNAME}
zfs mount ${POOLNAME}/ROOT/${SUITE}
if [ "${DISCENC}" != "NOENC" ] ; then
# Making sure we have the LUKS raw key available and/or
# Making sure we have the non-root key used for other datasets (/home)
mkdir -p ${ZFSBUILD}/etc/zfs
cp /etc/zfs/zroot.*key ${ZFSBUILD}/etc/zfs
fi
# zfs create pool/home and main user home dataset - possibly zfs native encrypted
if [ "${DISCENC}" = "ZFSENC" ] ; then
echo "${PASSPHRASE}" | zfs create -o canmount=off -o mountpoint=none -o compression=lz4 -o atime=off ${ZFSENC_HOME_OPTIONS} ${POOLNAME}/home
else
zfs create -o canmount=off -o mountpoint=none -o compression=lz4 -o atime=off ${POOLNAME}/home
fi
zfs create -o canmount=on -o mountpoint=/home/${USERNAME} ${POOLNAME}/home/${USERNAME}
zfs create -o canmount=on -o mountpoint=/root ${POOLNAME}/home/root
# If no HIBERNATE partition (not laptop, no resume etc) then just create
# a zvol for swap. Could not create this in the block above for swap because
# the root pool didn't exist yet.
if [ "${HIBERNATE}" = "n" ] && [ ${SIZE_SWAP} -ne 0 ] ; then
# No Hibernate, so just use a zfs volume for swap
echo "Creating swap zfs dataset size ${SIZE_SWAP}M"
# zfs create -V ${SIZE_SWAP}M -b $(getconf PAGESIZE) -o compression=zle \
zfs create -V ${SIZE_SWAP}M -o compression=zle \
-o logbias=throughput -o sync=always \
-o primarycache=metadata -o secondarycache=none \
-o com.sun:auto-snapshot=false ${POOLNAME}/swap
fi #HIBERNATE
# Show what we got before installing
echo "---------- $(tput setaf 1)About to debootstrap into ${ZFSBUILD}$(tput sgr0) -----------"
zfs list -t all
df -h
echo "---------- $(tput setaf 1)About to debootstrap into ${ZFSBUILD}$(tput sgr0) -----------"
read -t 15 QUIT
# Install basic system
echo "debootstrap to build initial system"
debootstrap --include=${SUITE_BOOTSTRAP} ${SUITE} ${ZFSBUILD}
zfs set devices=off ${POOLNAME}
# If this system will use Docker (which manages its own datasets & snapshots):
zfs create -o com.sun:auto-snapshot=false -o mountpoint=/var/lib/docker ${POOLNAME}/docker
# Set up boot partition (UEFI) potentially as mdadm mirror for multi-disk
if [ ${#zfsdisks[@]} -eq 1 ] ; then
BOOTDEVRAW=${PARTSBOOT}
else
# Unmount any mdadm disks that might have been automounted
# Stop all found mdadm arrays - again, just in case. Sheesh.
find /dev -iname md* -type b -exec bash -c "umount {} > /dev/null 2>&1 ; mdadm --stop --force {} > /dev/null 2>&1 ; mdadm --remove {} > /dev/null 2>&1" \;
for disk in $(seq 0 $(( ${#zfsdisks[@]} - 1))) ; do
# Wipe mdadm superblock from all partitions found, even if not md raid partition
mdadm --zero-superblock --force /dev/disk/by-id/${zfsdisks[${disk}]}-part${PARTITION_BOOT} > /dev/null 2>&1
done
BOOTDEVRAW="/dev/md/BOOT_EFI"
echo y | mdadm --create ${BOOTDEVRAW} --metadata=1.0 --force --level=mirror --raid-devices=${#zfsdisks[@]} --homehost=${MYHOSTNAME} --name=efi --assume-clean ${PARTSBOOT}
fi
mkfs.vfat -v -F 32 -s 1 -n "BOOT_EFI" ${BOOTDEVRAW} > /dev/null
echo "UUID=$(blkid -s UUID -o value ${BOOTDEVRAW}) \
/boot/efi vfat nofail,x-systemd.device-timeout=1,x-systemd.after=zfs-mount.service 0 1" >> ${ZFSBUILD}/etc/fstab
mkdir ${ZFSBUILD}/boot/efi
echo ${MYHOSTNAME} > ${ZFSBUILD}/etc/hostname
echo "127.0.1.1 ${MYHOSTNAME}" >> ${ZFSBUILD}/etc/hosts
if [ ${PROXY} ]; then
# This is for apt-get
echo "Acquire::http::proxy \"${PROXY}\";" > ${ZFSBUILD}/etc/apt/apt.conf.d/03proxy
fi # PROXY
# Set up networking for netplan
# renderer: networkd is for text mode only, use NetworkManager for gnome
# We create a bridge here with all found ethernet interfaces as slaves
# Makes it easier to set up multipass or LXD later
# NOTE: tabs as first char to handle indented heredoc
cat > ${ZFSBUILD}/etc/netplan/01_netcfg.yaml <<-EOF
network:
version: 2
renderer: networkd
ethernets:
alleths:
optional: true
match:
name: e*
dhcp4: true
dhcp6: true
wakeonlan: true
# === With the bridge config below, set dhcp to false
# dhcp4: false
# dhcp6: false