improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
2022-11-30 13:41:21 +01:00
## Creates all of the system's ZFS pools that are »createDuringInstallation«, plus their datasets.
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
function create-zpools { # 1: mnt
2022-11-30 13:41:21 +01:00
local poolName ; for poolName in "@{!config.wip.fs.zfs.pools[@]}" ; do
if [ [ ! @{ config.wip.fs.zfs.pools!catAttrSets.createDuringInstallation[ $poolName ] } ] ] ; then continue ; fi
create-zpool " $1 " " $poolName "
done
}
## Creates a single of the system's ZFS pools and its datasets.
function create-zpool { # 1: mnt, 2: poolName
2023-01-29 15:55:56 +01:00
local mnt = $1 ; local poolName = $2
eval 'local -A pool=' " @{config.wip.fs.zfs.pools[ $poolName ]} "
eval 'local -a vdevs=' " ${ pool [vdevArgs] } "
eval 'local -A poolProps=' " ${ pool [props] } "
eval 'local -A dataset=' " @{config.wip.fs.zfs.datasets[ ${ pool [name] } ]} "
eval 'local -A dataProps=' " ${ dataset [props] } "
local dummy ; get-zfs-crypt-props " ${ dataset [name] } " dataProps dummy dummy
local -a zpoolCreate = ( ) ; keySrc = /dev/null
if [ [ ${ dataProps [keyformat] :- } = = ephemeral ] ] ; then
dataProps[ encryption] = aes-256-gcm ; dataProps[ keyformat] = hex ; dataProps[ keylocation] = file:///dev/stdin ; keySrc = /dev/urandom
fi
local name ; for name in " ${ !poolProps[@] } " ; do zpoolCreate += ( -o " ${ name } = ${ poolProps [ $name ] } " ) ; done
local name ; for name in " ${ !dataProps[@] } " ; do zpoolCreate += ( -O " ${ name } = ${ dataProps [ $name ] } " ) ; done
local index ; for index in " ${ !vdevs[@] } " ; do
part = ${ vdevs [ $index ] } ; if [ [ $part = ~ ^( mirror| raidz[ 123] ?| draid[ 123] ?.*| spare| log| dedup| special| cache) $ ] ] ; then continue ; fi
if [ [ @{ config.boot.initrd.luks.devices!catAttrSets.device[ $part ] :-} ] ] ; then
vdevs[ $index ] = /dev/mapper/$part
else
part = /dev/disk/by-partlabel/$part ; vdevs[ $index ] = $part
if ! is-partition-on-disks " $part " " ${ blockDevs [@] } " ; then echo " Partition alias $part used by zpool ${ pool [name] } does not point at one of the target disks ${ blockDevs [@] } " 1>& 2 ; \r eturn 1 ; fi
2022-11-30 13:41:21 +01:00
fi
2023-01-29 15:55:56 +01:00
done
@{ native.kmod} /bin/modprobe zfs || true
<$keySrc @{ native.xxd} /bin/xxd -l 32 -c 64 -p | ( PATH = @{ native.zfs} /bin ; ${ _set_x :- : } ; zpool create " ${ zpoolCreate [@] } " -R " $mnt " " ${ pool [name] } " " ${ vdevs [@] } " ) || return
if [ [ $keySrc = = /dev/urandom ] ] ; then @{ native.zfs} /bin/zfs unload-key " $poolName " & >/dev/null ; fi
2022-11-30 13:41:21 +01:00
prepend_trap " @{native.zfs}/bin/zpool export ' $poolName ' " EXIT || return
ensure-datasets $mnt '^' " $poolName " '($|[/])' || return
2022-12-27 15:37:27 +01:00
if [ [ ${ args [debug] :- } ] ] ; then @{ native.zfs} /bin/zfs list -o name,canmount,mounted,mountpoint,keystatus,encryptionroot -r " $poolName " ; fi
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
}
## Ensures that the system's datasets exist and have the defined properties (but not that they don't have properties that aren't defined).
2022-06-04 20:54:09 +02:00
# The pool(s) must exist, be imported with root prefix »$mnt«, and (if datasets are to be created or encryption roots to be inherited) the system's keystore must be open (see »mount-keystore-luks«) or the keys be loaded.
# »keystatus« and »mounted« of existing datasets should remain unchanged, newly crated datasets will not be mounted but have their keys loaded.
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
function ensure-datasets { ( set -eu # 1: mnt, 2?: filterExp
2023-01-29 15:55:56 +01:00
if ( ( @{ #config.wip.fs.zfs.datasets[@]} == 0 )) ; then \return ; fi
local mnt = $1 ; while [ [ " $mnt " = = */ ] ] ; do mnt = ${ mnt : 0 : (-1) } ; done # (remove any tailing slashes)
local filterExp = ${ 2 :- '^' }
local tmpMnt = $( mktemp -d) ; trap " rmdir $tmpMnt " EXIT
local zfs = @{ native.zfs} /bin/zfs
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
: 'Step-through is very verbose and breaks the loop, disabling it for this function' ; trap - debug
2023-01-29 15:55:56 +01:00
local name ; while IFS = read -u3 -r -d $'\0' name ; do
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
if [ [ ! $name = ~ $filterExp ] ] ; then printf 'Skipping dataset »%s« since it does not match »%s«\n' " $name " " $filterExp " >& 2 ; continue ; fi
2023-01-29 15:55:56 +01:00
eval 'local -A dataset=' " @{config.wip.fs.zfs.datasets[ $name ]} "
eval 'local -A props=' " ${ dataset [props] } "
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
2023-01-29 15:55:56 +01:00
local explicitKeylocation = ${ props [keylocation] :- } cryptKey cryptRoot
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
get-zfs-crypt-props " ${ dataset [name] } " props cryptKey cryptRoot
2022-07-29 12:49:55 +02:00
if $zfs get -o value -H name " ${ dataset [name] } " & >/dev/null ; then # dataset exists: check its properties
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
if [ [ ${ props [mountpoint] :- } ] ] ; then # don't set the current mount point again (no-op), cuz that fails if the dataset is mounted
2023-01-29 15:55:56 +01:00
local current = $( $zfs get -o value -H mountpoint " ${ dataset [name] } " ) ; current = ${ current / $mnt / }
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
if [ [ ${ props [mountpoint] } = = " ${ current :- / } " ] ] ; then unset props[ mountpoint] ; fi
fi
if [ [ ${ props [keyformat] :- } = = ephemeral ] ] ; then
cryptRoot = ${ dataset [name] } ; unset props[ keyformat] ; props[ keylocation] = file:///dev/null
fi
2022-06-04 20:54:09 +02:00
if [ [ $explicitKeylocation ] ] ; then props[ keylocation] = $explicitKeylocation ; fi
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
unset props[ encryption] ; unset props[ keyformat] # can't change these anyway
2023-01-29 15:55:56 +01:00
local propNames = $( IFS = , ; echo " ${ !props[*] } " )
local propValues = $( IFS = $'\n' ; echo " ${ props [*] } " )
if [ [ $propValues != " $( $zfs get -o value -H " $propNames " " ${ dataset [name] } " ) " ] ] ; then
local -a zfsSet = ( ) ; local propName ; for propName in " ${ !props[@] } " ; do zfsSet += ( " ${ propName } = ${ props [ $propName ] } " ) ; done
( PATH = @{ native.zfs} /bin ; ${ _set_x :- : } ; zfs set " ${ zfsSet [@] } " " ${ dataset [name] } " ) || return
fi
2022-07-29 12:49:55 +02:00
if [ [ $cryptRoot && $( $zfs get -o value -H encryptionroot " ${ dataset [name] } " ) != " $cryptRoot " ] ] ; then ( # inherit key from parent (which the parent would also already have done if necessary)
if [ [ $( $zfs get -o value -H keystatus " $cryptRoot " ) != available ] ] ; then
2023-01-29 15:55:56 +01:00
$zfs load-key -L file://" $cryptKey " " $cryptRoot " || exit ; trap " $zfs unload-key $cryptRoot || true " EXIT
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
fi
2022-07-29 12:49:55 +02:00
if [ [ $( $zfs get -o value -H keystatus " ${ dataset [name] } " ) != available ] ] ; then
2023-01-29 15:55:56 +01:00
$zfs load-key -L file://" $cryptKey " " ${ dataset [name] } " || exit # will unload with cryptRoot
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
fi
2023-01-29 15:55:56 +01:00
( PATH = @{ native.zfs} /bin ; ${ _set_x :- : } ; zfs change-key -i " ${ dataset [name] } " ) || exit
) || return ; fi
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
2022-06-04 20:54:09 +02:00
else ( # create dataset
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
if [ [ ${ props [keyformat] :- } = = ephemeral ] ] ; then
props[ encryption] = aes-256-gcm ; props[ keyformat] = hex ; props[ keylocation] = file:///dev/stdin ; explicitKeylocation = file:///dev/null
2023-01-29 15:55:56 +01:00
declare -a zfsCreate = ( ) ; for name in " ${ !props[@] } " ; do zfsCreate += ( -o " ${ name } = ${ props [ $name ] } " ) ; done
{ </dev/urandom tr -dc 0-9a-f || true ; } | head -c 64 | ( PATH = @{ native.zfs} /bin ; ${ _set_x :- : } ; zfs create " ${ zfsCreate [@] } " " ${ dataset [name] } " ) || exit
$zfs unload-key " ${ dataset [name] } " || exit
2022-06-04 20:54:09 +02:00
else
2022-07-29 12:49:55 +02:00
if [ [ $cryptRoot && $cryptRoot != ${ dataset [name] } && $( $zfs get -o value -H keystatus " $cryptRoot " ) != available ] ] ; then
2023-01-29 15:55:56 +01:00
$zfs load-key -L file://" $cryptKey " " $cryptRoot " || exit
trap " $zfs unload-key $cryptRoot || true " EXIT
2022-06-04 20:54:09 +02:00
fi
2023-01-29 15:55:56 +01:00
declare -a zfsCreate = ( ) ; for name in " ${ !props[@] } " ; do zfsCreate += ( -o " ${ name } = ${ props [ $name ] } " ) ; done
( PATH = @{ native.zfs} /bin ; ${ _set_x :- : } ; zfs create " ${ zfsCreate [@] } " " ${ dataset [name] } " )
2022-06-04 20:54:09 +02:00
fi
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
if [ [ ${ props [canmount] } != off ] ] ; then (
2023-01-29 15:55:56 +01:00
mount -t zfs -o zfsutil " ${ dataset [name] } " $tmpMnt && trap " umount ' ${ dataset [name] } ' " EXIT &&
chmod 000 " $tmpMnt " && chown " ${ dataset [uid] } : ${ dataset [gid] } " -- " $tmpMnt " && chmod " ${ dataset [mode] } " -- " $tmpMnt "
) || exit ; fi
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
if [ [ $explicitKeylocation && $explicitKeylocation != " ${ props [keylocation] :- } " ] ] ; then
2023-01-29 15:55:56 +01:00
( PATH = @{ native.zfs} /bin ; ${ _set_x :- : } ; zfs set keylocation = " $explicitKeylocation " " ${ dataset [name] } " ) || exit
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
fi
2023-01-29 15:55:56 +01:00
$zfs snapshot -r " ${ dataset [name] } " @empty || exit
) || return ; fi
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
2023-01-29 15:55:56 +01:00
eval 'local -A allows=' " ${ dataset [permissions] } "
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
for who in " ${ !allows[@] } " ; do
# »zfs allow $dataset« seems to be the only way to view permissions, and that is not very parsable -.-
2023-01-29 15:55:56 +01:00
( PATH = @{ native.zfs} /bin ; ${ _set_x :- : } ; zfs allow -$who " ${ allows [ $who ] } " " ${ dataset [name] } " >& 2 ) || return
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
done
2023-01-29 15:55:56 +01:00
done 3< <( printf '%s\0' "@{!config.wip.fs.zfs.datasets[@]}" | LC_ALL = C sort -z )
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
) }
2022-07-29 12:49:55 +02:00
## Given the name (»datasetPath«) of a ZFS dataset, this deducts crypto-related options from the declared keys (»config.wip.fs.keystore.keys."zfs/..."«).
2022-11-30 13:41:21 +01:00
function get-zfs-crypt-props { # 1: datasetPath, 2?: name_cryptProps, 3?: name_cryptKey, 4?: name_cryptRoot
improve installation, add support for:
ZFS, encryption (keys, keystore, LUKS), bootFS, ephemeral root (tmpfs, ZFS, F2FS, ...), testing in qemu, options & debugging, ... and many small things
2022-05-31 03:41:28 +02:00
local hash = @{ config.networking.hostName!hashString.sha256:0:8}
local keystore = /run/keystore-$hash
local -n __cryptProps = ${ 2 :- props } ; local -n __cryptKey = ${ 3 :- cryptKey } ; local -n __cryptRoot = ${ 4 :- cryptRoot }
local name = $1 ; {
if [ [ $name = = */* ] ] ; then local pool = ${ name / \/ */ } / ; local path = /${ name / $pool / } ; else local pool = $name / ; local path = ; fi
} ; local key = ${ pool /- $hash '/' / } $path # strip hash from pool name
__cryptKey = '' ; __cryptRoot = ''
if [ [ @{ config.wip.fs.keystore.keys[ zfs/$name ] :-} ] ] ; then
if [ [ @{ config.wip.fs.keystore.keys[ zfs/$name ] } = = unencrypted ] ] ; then
__cryptProps[ encryption] = off # empty key to disable encryption
else
__cryptProps[ encryption] = aes-256-gcm ; __cryptProps[ keyformat] = hex ; __cryptProps[ keylocation] = file://" $keystore " /zfs/" $name " .key
__cryptKey = $keystore /zfs/$name .key ; __cryptRoot = $name
fi
else
while true ; do
name = $( dirname $name ) ; if [ [ $name = = . ] ] ; then break ; fi
if [ [ @{ config.wip.fs.keystore.keys[ zfs/$name ] :-} ] ] ; then
if [ [ @{ config.wip.fs.keystore.keys[ zfs/$name ] } != unencrypted ] ] ; then
__cryptKey = $keystore /zfs/$name .key ; __cryptRoot = $name
fi ; break
fi
done
fi
}