├── usr ├── lib │ └── tik │ │ ├── config │ │ ├── modules │ │ ├── pre │ │ │ ├── 10-welcome │ │ │ ├── 15-encrypt │ │ │ └── 20-mig │ │ └── post │ │ │ ├── 20-mig │ │ │ └── 15-encrypt │ │ └── lib │ │ └── tik-functions └── bin │ └── tik ├── etc └── tik │ └── config ├── LICENSE └── README.md /usr/lib/tik/config: -------------------------------------------------------------------------------- 1 | # Directory for users to add custom configuration and modules 2 | # Default: "/etc/tik" 3 | TIK_CUSTOM_DIR="/etc/tik" 4 | 5 | # Directory for OS images to be deployed 6 | # Default: "/usr/lib/tik/img" 7 | TIK_IMG_DIR="/usr/lib/tik/img" 8 | 9 | # To show USB devices in the install device selection dialog, uncomment this variable 10 | # USB devices are filtered out by default 11 | #TIK_ALLOW_USB_INSTALL_DEVICES=1 12 | 13 | # For unattended installations the disk device to deploy the image must be defined 14 | # Default: Undefined 15 | #TIK_INSTALL_DEVICE="" 16 | 17 | # For unattended installations the disk image to deploy must be defined if more than one is present 18 | # Default: Undefined 19 | #TIK_INSTALL_IMAGE="" 20 | 21 | # Display name of the OS to be deployed by tik 22 | # Default: Undefined 23 | #TIK_OS_NAME="" 24 | 25 | # URL for bug reports to go to 26 | # Default: https://aeondesktop.org/reportbug 27 | TIK_BUG_URL="https://aeondesktop.org/reportbug" 28 | -------------------------------------------------------------------------------- /etc/tik/config: -------------------------------------------------------------------------------- 1 | # Directory for users to add custom configuration and modules 2 | # Default: "/etc/tik" 3 | #TIK_CUSTOM_DIR="/etc/tik" 4 | 5 | # Directory for OS images to be deployed 6 | # Default: "/usr/lib/tik/img" 7 | #TIK_IMG_DIR="/usr/lib/tik/img" 8 | 9 | # To show USB devices in the install device selection dialog, uncomment this variable 10 | # USB devices are filtered out by default 11 | #TIK_ALLOW_USB_INSTALL_DEVICES=1 12 | 13 | # For unattended installations the disk device to deploy the image must be defined if more than one is present 14 | # Default: Undefined 15 | #TIK_INSTALL_DEVICE="" 16 | 17 | # For unattended installations the disk image to deploy must be defined if more than one is present 18 | # Default: Undefined 19 | #TIK_INSTALL_IMAGE="" 20 | 21 | # Display name of the OS to be deployed by tik 22 | # Default: Undefined 23 | #TIK_OS_NAME="" 24 | 25 | # URL for bug reports to go to 26 | # Default: https://aeondesktop.org/reportbug 27 | #TIK_BUG_URL="https://aeondesktop.org/reportbug" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 SUSE LLC 4 | Copyright (c) 2023-2024 Richard Brown 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /usr/bin/tik: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: Copyright 2023-2024 SUSE LLC 4 | # SPDX-FileCopyrightText: Copyright 2023-2024 Richard Brown 5 | 6 | # Define variables 7 | # Style notes 8 | # lowercase variables = internal, not expected to be defined by users 9 | # uppercase variables = user facing, expected to be set by config 10 | 11 | tik_log=~/tik.log 12 | tik_dir=/usr/lib/tik 13 | tik_module="tik" 14 | 15 | # Read libraries 16 | . ${tik_dir}/lib/tik-functions 17 | 18 | # Start logging 19 | exec 2> >(exec tee -i -a "${tik_log}" >&2) 20 | log "[START] $0" 21 | 22 | # Check for debug mode 23 | if [[ $1 == "--debug" ]]; then 24 | debug=1 25 | fi 26 | 27 | # Read configuration files, /usr first, then /etc 28 | . ${tik_dir}/config 29 | . ${TIK_CUSTOM_DIR}/config 30 | 31 | # Check essential paths exist 32 | if [ ! -d "${TIK_IMG_DIR}" ]; then 33 | error "${TIK_IMG_DIR} does not exist" 34 | fi 35 | 36 | cleanup() { 37 | retval=$? 38 | log "[STOP][${retval}] $0" 39 | if [ "${debug}" == "1" ]; then 40 | zenity --timeout 5 --info --no-wrap --text="Test Succeeded:\n\nHave a nice day!" 41 | elif [ "${retval}" == "0" ]; then 42 | zenity --timeout 5 --info --no-wrap --title="Installation Complete!" --text="${TIK_OS_NAME} has been installed.\n\nSystem is rebooting" 43 | prun systemctl reboot --force 44 | else 45 | zenity --error --no-wrap --title="Installation Failed" --text="Please file a bug report at ${TIK_BUG_URL}\n\nPlease include the tik.log file\nIt can be found on the IGNITION partition on this USB Stick\n\nSystem is shutting down" 46 | cp -a ${tik_log} /ignition 47 | prun systemctl poweroff --force 48 | fi 49 | } 50 | trap cleanup EXIT 51 | 52 | load_modules "pre" 53 | load_modules "pre" "custom" 54 | 55 | get_disk 56 | get_img 57 | dump_image "${TIK_INSTALL_IMAGE}" "${TIK_INSTALL_DEVICE}" 58 | reread_partitiontable 59 | 60 | load_modules "post" 61 | load_modules "post" "custom" 62 | 63 | wipe_keyfile 64 | set_boot_target -------------------------------------------------------------------------------- /usr/lib/tik/modules/pre/10-welcome: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: Copyright 2024 SUSE LLC 3 | # SPDX-FileCopyrightText: Copyright 2024 Richard Brown 4 | # SPDX-FileCopyrightText: Copyright 2024 Raymond Yip 5 | 6 | proceedInstall() { 7 | d --info --ok-label="Install Now" --no-wrap --width=300 --height=300 --icon=distributor-logo-Aeon-symbolic --title="" --text="Welcome to ${TIK_OS_NAME}\n\nPlease press Install Now to continue" 8 | } 9 | 10 | displayACWarningMsg() { 11 | d --warning --no-wrap --title="AC Power Recommended" --text="Runnning on battery power detected\n\nIt is recommended to connect the system to AC power during the install" 12 | } 13 | 14 | checkLaptop() { 15 | chassis=`cat /sys/class/dmi/id/chassis_type` 16 | #Test for respectively Handheld, Notebook, Laptop, and Portable 17 | #if chassis variable matches 8 9 10 or 11 function continues else it proceeds to test AC power and Battery 18 | [[ "$chassis" =~ ^(8|9|10|11)$ ]] || return 19 | #Tested machine is confirmed mobile 20 | givePowerRecommendation=false 21 | #Only check for AC and Battery power connections with upower 22 | updevices=`/usr/bin/upower -e|grep -E 'AC|BAT'` 23 | for pdev in $updevices; do 24 | #Get detailed info for each AC and BAT device in upower 25 | upinfo=`/usr/bin/upower -i $pdev|grep -E 'online|state'` 26 | #Check for discharging state or AC power offline which is equal to no state 27 | if [[ "$upinfo" =~ (discharging|no) ]]; then 28 | #Give power recommendation only once, so set this to true 29 | givePowerRecommendation=true 30 | fi 31 | done 32 | if [ "$givePowerRecommendation" = true ]; then 33 | log "AC Power disconnected and Battery is not charging" 34 | displayACWarningMsg 35 | fi 36 | } 37 | 38 | verify_efi() { 39 | # Verify that the system was booted with EFI, exit with error if not 40 | if [ ! -d /sys/firmware/efi ]; then 41 | # System was not booted with EFI 42 | local error_msg="${TIK_OS_NAME} requires UEFI mode, which is not found on your system.\nPlease check your BIOS settings to see if UEFI can be enabled." 43 | error "${error_msg}" 44 | fi 45 | } 46 | 47 | proceedInstall 48 | verify_efi 49 | checkLaptop 50 | -------------------------------------------------------------------------------- /usr/lib/tik/modules/pre/15-encrypt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: Copyright 2024 SUSE LLC 3 | # SPDX-FileCopyrightText: Copyright 2024 Richard Brown 4 | 5 | # Based on the guide documented here: https://en.opensuse.org/index.php?title=Portal:Aeon/Encryption 6 | # verify_tpm checks which encryption mode the system is capable of, and stores it in ${tik_encrypt_mode} for use by post module 7 | # "tik_encrypt_mode = 0" - Default Mode 8 | # "tik_encrypt_mode = 1" - Fallback Mode 9 | # check_secureboot checks if secureboot is disabled for Fallback mode. Does not do anything if Default Mode. 10 | # encrypt_notification does nothing if Default Mode. For Fallback mode it provides a single notification containing the following information 11 | # - That Fallback Mode is enabled because [TPM 2.0 is missing | lacking features] 12 | # - That they will be prompted to enter a passphrase to encrypt their system later 13 | # - If Secureboot is disabled they will be encouraged to exit and enable it 14 | # - For more information go to rewrite https://aeondesktop.org/encrypt 15 | 16 | verify_tpm() { 17 | # Verify that the system has a TPM 2.0 18 | if [ -c /dev/tpmrm0 ]; then 19 | # TPM 2.0 found 20 | tpm_found=1 21 | log "[verify_tpm] TPM 2.0 found, checking for PolicyAuthorizeNV" 22 | # Check for command 0x192 PolicyAuthorizeNV 23 | if prun tpm2_getcap commands | grep -q 'commandIndex: 0x192'; then 24 | # PolicyAuthorizeNV found, set encryption mode to default 25 | tik_encrypt_mode=0 26 | log "[verify_tpm] PolicyAuthorizeNV support found, Default Mode set" 27 | else 28 | # PolicyAuthorizeNV not found, set encryption mode to fallback 29 | tik_encrypt_mode=1 30 | log "[verify_tpm] PolicyAuthorizeNV support not found, Fallback Mode set" 31 | fi 32 | else 33 | # TPM 2.0 not found, set encryption mode to fallback 34 | tpm_found=0 35 | tik_encrypt_mode=1 36 | log "[verify_tpm] TPM 2.0 not found, Fallback Mode set" 37 | fi 38 | } 39 | 40 | check_secureboot() { 41 | # We only care about Secureboot when using Fallback mode 42 | if [ "${tik_encrypt_mode}" == 1 ]; then 43 | if ! mokutil --sb-state | grep -q 'enabled'; then 44 | secureboot_disabled=1 45 | log "[check_secureboot] secureboot disabled, will warn user" 46 | else 47 | log "[check_secureboot] secureboot enabled" 48 | fi 49 | fi 50 | } 51 | 52 | encrypt_notification() { 53 | local preamble="This system does not meet the Recommended Hardware requirements for ${TIK_OS_NAME}" 54 | local postamble="Disk Encryption will use Fallback Mode\nYou will be prompted to create a Passphrase later in the installation\nThis Passphrase will be required to unlock ${TIK_OS_NAME} on every boot\n\nFor more information please visit https://aeondesktop.org/encrypt" 55 | local secureboot_warning="It is Strongly Recommended to enable SecureBoot\n\nWithout SecureBoot this system will be at increased risk of attacks which could compromise the security of your data\nPlease Cancel Installation and enable SecureBoot\n\nFor more information please visit https://aeondesktop.org/encrypt" 56 | local reason 57 | # We're only going to show a notification when using Fallback mode 58 | if [ "${tik_encrypt_mode}" == 1 ]; then 59 | [ "${tpm_found}" == 0 ] && reason="No TPM 2.0 chipset found" 60 | [ "${tpm_found}" == 1 ] && reason="TPM 2.0 chipset found, but older than v1.38" 61 | # Secureboot being disabled makes the notification a Yes/No with the preference being to exit 62 | if [ "${secureboot_disabled}" == 1 ]; then 63 | log "[encrypt_notification] secureboot warning shown" 64 | zenity --width=600 --question --icon=security-low-symbolic --title="Warning" --ok-label="Cancel Installation" --cancel-label="I Understand, Proceed Anyway" --text="${preamble}\n\nReason: SecureBoot Disabled and ${reason}\n\n${secureboot_warning}" && exit 1 65 | log "[encrypt_notification] secureboot warning ignored, installation continuing" 66 | d --width=600 --warning --icon=security-low-symbolic --text="${postamble}" 67 | # Secureboot is enabled, so show a warning 68 | else 69 | d --width=600 --warning --icon=security-medium-symbolic --text="${preamble}\n\nReason: ${reason}\n\n${postamble}" 70 | fi 71 | log "[encrypt_notification] user notified that Fallback mode will be used" 72 | fi 73 | } 74 | 75 | verify_tpm 76 | check_secureboot 77 | encrypt_notification -------------------------------------------------------------------------------- /usr/lib/tik/modules/post/20-mig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: Copyright 2024 SUSE LLC 3 | # SPDX-FileCopyrightText: Copyright 2024 Richard Brown 4 | 5 | writemigdesktop() { 6 | prun-opt /usr/bin/tee $1/.config/autostart/aeon-mig-firstboot.desktop << "EOF" 7 | [Desktop Entry] 8 | Name=Aeon Migration FirstBoot Setup 9 | Comment=Sets up Aeon Correctly On FirstBoot after Migration 10 | Exec=/usr/bin/aeon-mig-firstboot 11 | Icon=org.gnome.Terminal 12 | Type=Application 13 | Categories=Utility;System; 14 | Name[en_GB]=startup 15 | EOF 16 | prun-opt /usr/bin/chmod 666 $1/.config/autostart/aeon-mig-firstboot.desktop 17 | } 18 | 19 | 20 | if [ "${migrate}" == 1 ]; then 21 | probe_partitions ${TIK_INSTALL_DEVICE} "crypto_LUKS" 22 | [ -z "${probedpart}" ] || prun /usr/sbin/cryptsetup luksOpen --key-file=${tik_keyfile} ${cryptpart} aeon_root 23 | 24 | probe_partitions $TIK_INSTALL_DEVICE "btrfs" "/usr/lib/os-release" 25 | 26 | [ -n "${probedpart}" ] || error "MIGRATION FAILED: New Installation NOT FOUND" 27 | 28 | prun /usr/bin/mkdir ${mig_dir}/mnt 29 | prun /usr/bin/mount -o compress=zstd:1 ${probedpart} ${mig_dir}/mnt 30 | prun /usr/bin/systemd-repart --pretty 0 --root ${mig_dir}/mnt --dry-run=0 ${probedpart} 31 | prun /usr/bin/mount -o compress=zstd:1,subvol=/@/var ${probedpart} ${mig_dir}/mnt/var 32 | prun /lib/systemd/systemd-growfs ${mig_dir}/mnt/var 33 | etcmountcmd=$(cat ${mig_dir}/mnt/etc/fstab | grep "overlay /etc" | sed 's/\/sysroot\//${mig_dir}\/mnt\//g' | sed 's/\/work-etc.*/\/work-etc ${mig_dir}\/mnt\/etc\//' | sed 's/overlay \/etc overlay/\/usr\/bin\/mount -t overlay overlay -o/') 34 | eval prun "$etcmountcmd" 35 | prun /usr/bin/cat ${mig_dir}/passwd.out | prun tee -a ${mig_dir}/mnt/etc/passwd 36 | prun /usr/bin/cat ${mig_dir}/group.out | prun tee -a ${mig_dir}/mnt/etc/group 37 | prun /usr/bin/cat ${mig_dir}/shadow.out | prun tee -a ${mig_dir}/mnt/etc/shadow 38 | prun /usr/bin/sed -i "/^wheel:/ s/$/$(head -n 1 ${mig_dir}/passwd.out | awk -F'[/:]' '{print $1}')/" ${mig_dir}/mnt/etc/group 39 | prun /usr/bin/cp -a ${mig_dir}/subuid ${mig_dir}/mnt/etc/subuid 40 | prun /usr/bin/cp -a ${mig_dir}/subgid ${mig_dir}/mnt/etc/subgid 41 | # It's not guaranteed that the system will have existing network configs, localtime or AccountsService 42 | prun-opt /usr/bin/cp -a ${mig_dir}/system-connections/* ${mig_dir}/mnt/etc/NetworkManager/system-connections 43 | prun-opt /usr/bin/cp -a ${mig_dir}/localtime ${mig_dir}/mnt/etc/localtime 44 | prun-opt /usr/bin/cp -a ${mig_dir}/users/* ${mig_dir}/mnt/var/lib/AccountsService/users 45 | prun-opt /usr/bin/cp -a ${mig_dir}/icons/* ${mig_dir}/mnt/var/lib/AccountsService/icons 46 | prun-opt /usr/bin/cp -a ${mig_dir}/bluetooth/* ${mig_dir}/mnt/var/lib/bluetooth 47 | prun-opt /usr/bin/cp -a ${mig_dir}/fprint/* ${mig_dir}/mnt/var/lib/fprint 48 | prun-opt /usr/bin/cp -a ${mig_dir}/openvpn/* ${mig_dir}/mnt/etc/openvpn 49 | prun /usr/bin/umount ${mig_dir}/mnt/etc 50 | prun /usr/bin/umount ${mig_dir}/mnt/var 51 | prun /usr/bin/umount ${mig_dir}/mnt 52 | prun /usr/bin/mount -o compress=zstd:1,subvol=/@ ${probedpart} ${mig_dir}/mnt 53 | prun /usr/sbin/btrfs subvolume delete ${mig_dir}/mnt/home 54 | (prun /usr/sbin/btrfs send ${mig_dir}/${snap_dir} | pv -f -F "# %b copied in %t %r" | prun /usr/sbin/btrfs receive ${mig_dir}/mnt) 2>&1 | d --progress --title="Restoring /home" --pulsate --auto-close --no-cancel --width=400 55 | prun /usr/bin/mv ${mig_dir}/mnt/${snap_dir} ${mig_dir}/mnt/home 56 | prun /usr/sbin/btrfs property set -f -ts ${mig_dir}/mnt/home ro false 57 | for subsubvol in $(prun-opt /usr/sbin/btrfs subvolume list -o ${mig_dir}/${snap_dir} --sort=path | rev | cut -f1 -d' ' | rev | sed 's/^@//'); do 58 | subsubvolname=$(basename $subsubvol) 59 | subsubdirname=$(dirname $subsubvol | awk -F "${mig_dir}/${snap_dir}" '{print $2}') 60 | (prun /usr/sbin/btrfs send ${subsubvol} | pv -f -F "# %b copied in %t %r" | prun /usr/sbin/btrfs receive ${mig_dir}/mnt/home/${subsubdirname} ) 2>&1 | d --progress --title="Restoring containers" --pulsate --auto-close --no-cancel --width=400 61 | prun /usr/sbin/btrfs property set -f -ts ${mig_dir}/mnt/home/${subsubdirname}/${subsubvolname} ro false 62 | prun-opt /usr/bin/sed -i 's/driver = "overlay"/driver = "btrfs"/g' ${mig_dir}/mnt/etc/containers/storage.conf 63 | done 64 | for userhome in ${mig_dir}/mnt/home/*/; do 65 | writemigdesktop $userhome 66 | done 67 | prun /usr/bin/umount ${mig_dir}/mnt 68 | prun /usr/bin/rmdir ${mig_dir}/mnt 69 | [ ! -e /dev/mapper/aeon_root ] || prun /usr/sbin/cryptsetup luksClose aeon_root 70 | fi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tik 2 | 3 | Transactional Installation Kit - A toolkit for deploying Operating System images to UEFI hardware from a USB stick. 4 | 5 | ## General Premise 6 | 7 | A simple, lightweight, extensible tool for deploying a premade OS images to UEFI hardware. 8 | 9 | tik was originally inspired by the "SelfInstaller" functionality offered by [kiwi](https://github.com/OSInside/kiwi) OEM images, but is designed to be wholly independent of the toolchain used to create the OS images. 10 | 11 | It's core functionality is very similar to kiwi's SelfInstaller, with the basic workflow for deploying an image being a very simple process: 12 | 13 | - Identify storage devices on the system 14 | - Offer the user a list of available devices 15 | - Deploy image to that device 16 | 17 | In addition to the above workflow, tik supports the following additional features 18 | 19 | - Unattended automation of the deployment of the image 20 | - Optional extensions to be run before or after the deployment of the image (eg to support functionality like checking the network for an updated image). This functionality is inspired by [jeos-firstboot](https://github.com/openSUSE/jeos-firstboot/)'s module support 21 | - Support for multiple images provided on the same installation media (eg. openSUSE Aeon and openSUSE MicroOS) 22 | 23 | ## tik OS Images 24 | 25 | tik is designed to deploy three types of images. 26 | 27 | - systemd-repart "bundles" 28 | - systemd-repart "self deployment" 29 | - Block images 30 | 31 | In all three cases tik should not care about the contents of the disk image, which potentially could be of any Operating System built using any toolset (eg kiwi, mkosi, etc) 32 | 33 | Features like expanding the partitions to fill the disk are expected to be handled by tools like systemd-repart on the booting of the deployed OS, not by tik (though in theory optional extensions could be written to implement this) 34 | 35 | ### systemd-repart "bundles" 36 | 37 | tik can also deploy images bundled with their own systemd-repart configurations. tik expects the following on disk layout for each systemd-repart "bundle": 38 | 39 | - a Directory with a unique name to describe the OS/Version being deployed. Contained within that directory: 40 | - a `repart.d` Directory containing a complete [repart.d](https://www.freedesktop.org/software/systemd/man/latest/repart.d.html) partition layout for the OS being deployed 41 | - 1 (or more) Directories or Block images to be used by `CopyFiles=` or `CopyBlocks=` parameters in the `repart.d` configuration to populate the contents of the defined partitions 42 | 43 | The `repart.d` configuration is then read and applied to the target storage device, being populated automatically based on the configuration. 44 | 45 | By default these files should be located in `/usr/lib/tik/img` but can be relocated by redefining the `TIK_IMG_DIR` parameter in your tik config. 46 | As `CopyBlocks=` and `CopyFiles=` parameters require absolute filesystem paths, any change to the `TIK_IMG_DIR` parameter will require altering your configuration to match that new location. 47 | 48 | This feature was introduced in tik v1.2 49 | 50 | ### systemd-repart "self-deployment" 51 | 52 | If tik is executed without any images in the defined `TIK_IMG_DIR` it will automatically attempt "self deployment", using the currently booted tik USB stick as its 'image' for writing to the target storage device. 53 | This is primarily for using tik to deploy images from a functioning 'live/portable' USB installation. 54 | 55 | For this to work, tik requires 56 | 57 | - a `repart.d` configuration containing a complete [repart.d](https://www.freedesktop.org/software/systemd/man/latest/repart.d.html) partition layout for the OS being deployed. This must be located in the standard `repart.d` paths, eg `/etc/repart.d/` or `/usr/lib/repart.d` 58 | 59 | The `repart.d` configuration is then read and applied to the target storage device, being populated automatically based on the configuration. 60 | It is expected that the `repart.d` configuration will use `CopyBlocks=auto` to automatically map the contents from the booted tik USB stick to the equivalent new partitions on the target storage device. 61 | 62 | This feature was introduced in tik v1.2 63 | 64 | ### Block images 65 | 66 | tik can deploy a block-based disk images. These expected to be raw.xz files containing 67 | 68 | - the full partition table 69 | - a UEFI ESP/EFI partition 70 | - 1 (or more) OS partitions 71 | 72 | By default these files should be located in `/usr/lib/tik/img` but can be relocated by redefining the `TIK_IMG_DIR` parameter in your tik config 73 | 74 | ## tik Installation Media 75 | 76 | tik is designed to be run on a different style of media than many traditional OS installers 77 | 78 | Traditional tooling like YaST, Agama, Windows Installer, etc are all expected to be read-only Installation media that aren't modifiable by the user at all 79 | 80 | tik Installtion Media are expected to be a variant of openSUSE MicroOS, designed to be run from portable media (eg a USB stick) 81 | 82 | while the "Install OS" of the Installation Media will therefore be read-only when in use, the "Install OS" will be possible of being updated and configured to the users needs, directly on the USB stick after it's imaged 83 | 84 | More importantly, this also means that the Installation Media will have various read-write locations, including /var/lib/tik/images, the location of tiks image files, allowing users to add their own custom variants of such images to be offered when the tik installer boots up 85 | 86 | ## tik + ignition + combustion 87 | 88 | because tik installation media are built separately from the Operating System(s) which tik will offer to deploy, this means that tik installation images can also contain a separate 'ignition/combustion partition' which can have your ignition/combustion configurations stored within 89 | 90 | These will then be automatically used by any OS image which uses ignition or combustion (eg openSUSE MicroOS) on their first boot after tik has deployed an image, assuming the tik Installation USB stick is still connected 91 | 92 | This makes ignition and/or combustion the perfect tools for making any automated customisations to any OS image deployed via tik 93 | -------------------------------------------------------------------------------- /usr/lib/tik/modules/pre/20-mig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: Copyright 2024 SUSE LLC 3 | # SPDX-FileCopyrightText: Copyright 2024 Richard Brown 4 | 5 | mig_dir=/var/lib/tik/mig 6 | snap_dir=homebk 7 | if [ ! -d ${mig_dir} ]; then 8 | prun /usr/bin/mkdir -p ${mig_dir} 9 | fi 10 | 11 | if [ ! -z "$(ls -A ${mig_dir})" ]; then 12 | log "existing backup found" 13 | zenity --question --no-wrap --cancel-label="No, Delete Backup" --title="Existing user backup detected" --text="These users can be restored to the new installation\n\nWould you like to use this backup?" 14 | oldbackupyn=$? 15 | log "[oldbackupyn][${oldbackupyn}]" 16 | if [ "${oldbackupyn}" == 0 ]; then 17 | skipbackup=1 18 | migrate=1 19 | log "backup skipped, migration will use existing backup" 20 | else 21 | prun-opt /usr/sbin/btrfs property set -f -ts ${mig_dir}/${snap_dir} ro false 22 | for subsubvol in $(prun-opt /usr/sbin/btrfs subvolume list -o ${mig_dir}/${snap_dir} --sort=path | rev | cut -f1 -d' ' | rev | sed "s/^@//"); do 23 | prun /usr/sbin/btrfs subvolume delete ${subsubvol} 24 | done 25 | prun-opt /usr/sbin/btrfs subvolume delete ${mig_dir}/${snap_dir} 26 | prun-opt /usr/bin/rm ${mig_dir}/*.out 27 | prun-opt /usr/bin/rm ${mig_dir}/system-connections/* 28 | prun-opt /usr/bin/rmdir ${mig_dir}/system-connections 29 | prun-opt /usr/bin/rm ${mig_dir}/users/* 30 | prun-opt /usr/bin/rmdir ${mig_dir}/users 31 | prun-opt /usr/bin/rm ${mig_dir}/icons/* 32 | prun-opt /usr/bin/rmdir ${mig_dir}/icons 33 | prun-opt /usr/bin/rm ${mig_dir}/localtime 34 | prun-opt /usr/bin/rm ${mig_dir}/subgid 35 | prun-opt /usr/bin/rm ${mig_dir}/subuid 36 | prun-opt /usr/bin/rm ${mig_dir}/bluetooth/* 37 | prun-opt /usr/bin/rmdir ${mig_dir}/bluetooth 38 | prun-opt /usr/bin/rm ${mig_dir}/openvpn/* 39 | prun-opt /usr/bin/rmdir ${mig_dir}/openvpn 40 | prun-opt /usr/bin/rm -R ${mig_dir}/fprint/* 41 | prun-opt /usr/bin/rmdir ${mig_dir}/fprint 42 | prun-opt /usr/bin/rmdir ${mig_dir}/mnt 43 | fi 44 | fi 45 | 46 | get_disk 47 | 48 | if [ -z "${skipbackup}" ]; then 49 | # Although Legacy Aeon didn't officially support LUKS encrypted installations, 50 | # some users might have nevertheless enabled encryption anyway. 51 | # Search for existing crypto_LUKS partitions and, if found, prompt the user 52 | # to unlock those so that the migration module can find existing data. 53 | for encrypted_partition in $(lsblk ${TIK_INSTALL_DEVICE} -p -n -r -o ID-LINK,FSTYPE|tr -s ' ' ";"|grep ";crypto_LUKS"|cut -d\; -f1); do 54 | if [ -e /dev/mapper/crypt_${encrypted_partition} ]; then 55 | # Already opened for some reason... do not prompt for the passphrase 56 | # but ensure we will clean up afterwards 57 | crypt_opened="${crypt_opened} crypt_${encrypted_partition}" 58 | else 59 | while [ 1 ]; do 60 | passphrase=$(zenity --password --title="Encrypted partition (${encrypted_partition}) detected" --cancel-label="Skip") || break 61 | if [ -n "${passphrase}" ]; then 62 | echo -n "${passphrase}" | prun /usr/sbin/cryptsetup luksOpen /dev/disk/by-id/${encrypted_partition} crypt_${encrypted_partition} 63 | if [ "${?}" -eq 0 ]; then 64 | crypt_opened="${crypt_opened} crypt_${encrypted_partition}" 65 | # Wait for the mapped device to appear 66 | wait_count=0 67 | while [ ! -e /dev/mapper/crypt_${encrypted_partition} ] && [ ${wait_count} -lt 5 ]; do 68 | sleep 1 69 | wait_count=$((wait_count + 1)) 70 | done 71 | break 72 | fi 73 | fi 74 | done 75 | fi 76 | done 77 | unset passphrase 78 | 79 | # Probe selected disk for a btrfs partition containing /usr/lib/os-release 80 | probe_partitions $TIK_INSTALL_DEVICE "btrfs" "/usr/lib/os-release" 81 | 82 | if [ -n "${probedpart}" ]; then 83 | prun /usr/bin/mkdir ${mig_dir}/mnt 84 | prun-opt /usr/bin/mount -o compress=zstd:1,subvol=/@/home ${probedpart} ${mig_dir}/mnt 85 | if [ ${retval} -eq 0 ]; then 86 | prun /usr/sbin/btrfs quota rescan -w ${mig_dir}/mnt | d --progress --title="Detected existing /home subvolume.." --pulsate --auto-close --no-cancel --width=400 87 | home_size=$(prun /usr/sbin/btrfs qgroup show --raw -f ${mig_dir}/mnt | grep @/home$ | awk '{print $2}') 88 | tik_stick_size=$(prun /usr/sbin/btrfs fi usage --raw ${mig_dir} | grep estimated | awk '{print $3}') 89 | if [ ${home_size} -gt ${tik_stick_size} ]; then 90 | # Not enough space to offer migration 91 | migrate=0 92 | fi 93 | if [ ${home_size} -le 16384 ]; then 94 | # /home subvolume is empty 95 | migrate=0 96 | fi 97 | prun /usr/bin/umount ${mig_dir}/mnt 98 | else 99 | log "no @/home subvolume found on ${probedpart}" 100 | migrate=0 101 | fi 102 | prun /usr/bin/rmdir ${mig_dir}/mnt 103 | # partition found, /home subvolume found, no known reason to not migrate, so ask the user 104 | if [ -z "${migrate}" ]; then 105 | if [ "${legacy_aeon}" == 1 ]; then 106 | d --info --width=300 --height=300 --icon=distributor-logo-Aeon-symbolic --no-wrap --title="Message from the Aeon Team" --text="We'd like to thank you for adopting openSUSE Aeon so early in it's development,\nbefore we fully understood what we were building or how we wanted it to look\n\nWe are sorry that you need to reinstall your system\n\nThank you so much for your support.\nWe hope you enjoy the new look openSUSE Aeon" 107 | fi 108 | zenity --question --no-wrap --title="Backup users from the existing install?" --text="These users will be restored to the new installation." 109 | migrateyn=$? 110 | if [ "${migrateyn}" == 0 ]; then 111 | migrate=1 112 | else 113 | migrate=0 114 | fi 115 | fi 116 | fi 117 | 118 | if [ "${migrate}" == 1 ]; then 119 | # We're migrating, lets go! 120 | prun /usr/bin/mkdir ${mig_dir}/mnt 121 | prun /usr/bin/mount -o compress=zstd:1,subvol=/@/home ${probedpart} ${mig_dir}/mnt 122 | # Check for existing snapshot from interrupted backup and delete if it exists boo#1224824 123 | if [ -d ${mig_dir}/mnt/${snap_dir} ]; then 124 | prun /usr/sbin/btrfs subvolume delete ${mig_dir}/mnt/${snap_dir} 125 | fi 126 | prun /usr/sbin/btrfs subvolume snapshot -r ${mig_dir}/mnt ${mig_dir}/mnt/${snap_dir} 127 | (prun /usr/sbin/btrfs send ${mig_dir}/mnt/${snap_dir} | pv -f -F "# %b copied in %t %r" | prun /usr/sbin/btrfs receive ${mig_dir}) 2>&1 | d --progress --title="Backing up /home" --pulsate --auto-close --no-cancel --width=400 128 | prun /usr/sbin/btrfs subvolume delete ${mig_dir}/mnt/${snap_dir} 129 | # Probe for subvolumes nested beneath /home and back them up also 130 | if (prun-opt /usr/sbin/btrfs subvolume list -o ${mig_dir}/mnt | grep -q "ID "); then 131 | prun /usr/sbin/btrfs property set -f -ts ${mig_dir}/${snap_dir} ro false 132 | for subsubvol in $(prun-opt /usr/sbin/btrfs subvolume list -o ${mig_dir}/mnt --sort=path | rev | cut -f1 -d' ' | rev | sed 's/^@\/home//'); do 133 | subsubvolname=$(basename $subsubvol) 134 | subsubdirname=$(dirname $subsubvol) 135 | prun /usr/sbin/btrfs subvolume snapshot -r ${mig_dir}/mnt/${subsubvol} ${mig_dir}/mnt/${subsubvolname} 136 | (prun /usr/sbin/btrfs send ${mig_dir}/mnt/${subsubvolname} | pv -f -F "# %b copied in %t %r" | prun /usr/sbin/btrfs receive ${mig_dir}/${snap_dir}/${subsubdirname}) 2>&1 | d --progress --title="Backing up containers" --pulsate --auto-close --no-cancel --width=400 137 | prun /usr/sbin/btrfs subvolume delete ${mig_dir}/mnt/${subsubvolname} 138 | done 139 | prun /usr/sbin/btrfs property set -f -ts ${mig_dir}/${snap_dir} ro true 140 | fi 141 | prun /usr/bin/umount ${mig_dir}/mnt 142 | prun /usr/bin/mount -o compress=zstd:1 ${probedpart} ${mig_dir}/mnt 143 | prun /usr/bin/mount -o compress=zstd:1,subvol=/@/var ${probedpart} ${mig_dir}/mnt/var 144 | etcmntcmd=$(cat ${mig_dir}/mnt/etc/fstab | grep "overlay /etc" | sed 's/\/sysroot\//${mig_dir}\/mnt\//g' | sed 's/\/work-etc.*/\/work-etc ${mig_dir}\/mnt\/etc\//' | sed 's/overlay \/etc overlay/\/usr\/bin\/mount -t overlay overlay -o/') 145 | eval prun "$etcmntcmd" 146 | prun /usr/bin/awk -F'[/:]' '($3 >= 1000 && $3 != 65534)' ${mig_dir}/mnt/etc/passwd | prun /usr/bin/awk -F':' '{ $7="/bin/bash"; print };' OFS=':' | prun tee ${mig_dir}/passwd.out 147 | prun /usr/bin/awk -F'[/:]' '($3 >= 1000 && $3 != 65534 && $3 != 65533)' ${mig_dir}/mnt/etc/group | prun tee ${mig_dir}/group.out 148 | prun /usr/bin/awk -F'[/:]' '{if ($3 >= 1000 && $3 != 65534) print $1}' ${mig_dir}/mnt/etc/passwd | prun /usr/bin/grep -f - ${mig_dir}/mnt/etc/shadow | prun tee ${mig_dir}/shadow.out 149 | prun /usr/bin/cp -a ${mig_dir}/mnt/etc/subuid ${mig_dir}/subuid 150 | prun /usr/bin/cp -a ${mig_dir}/mnt/etc/subgid ${mig_dir}/subgid 151 | # It's not guaranteed that the system will have existing network configs, custom localtime or AccountsService 152 | prun-opt /usr/bin/cp -a ${mig_dir}/mnt/etc/NetworkManager/system-connections ${mig_dir}/system-connections 153 | prun-opt /usr/bin/cp -a ${mig_dir}/mnt/etc/localtime ${mig_dir}/localtime 154 | prun-opt /usr/bin/cp -a ${mig_dir}/mnt/var/lib/AccountsService/users ${mig_dir}/users 155 | prun-opt /usr/bin/chmod 744 ${mig_dir}/users 156 | prun-opt /usr/bin/cp -a ${mig_dir}/mnt/var/lib/AccountsService/icons ${mig_dir}/icons 157 | prun-opt /usr/bin/cp -a ${mig_dir}/mnt/var/lib/bluetooth ${mig_dir}/bluetooth 158 | prun-opt /usr/bin/cp -a ${mig_dir}/mnt/var/lib/fprint ${mig_dir}/fprint 159 | prun-opt /usr/bin/cp -a ${mig_dir}/mnt/etc/openvpn ${mig_dir}/openvpn 160 | prun-opt /usr/bin/umount ${mig_dir}/mnt/etc 161 | prun /usr/bin/umount ${mig_dir}/mnt/var 162 | prun /usr/bin/umount ${mig_dir}/mnt 163 | prun /usr/bin/rmdir ${mig_dir}/mnt 164 | fi 165 | 166 | # Close eventual mapped LUKS devices 167 | if [ -n "${crypt_opened}" ]; then 168 | for mapped_partition in ${crypt_opened}; do 169 | if [ -e /dev/mapper/crypt_${encrypted_partition} ]; then 170 | # We are going to replace the encrypted partition anyway, so if 171 | # we're unable to gracefully close the device after 5 seconds, 172 | # just give up (hence the prun-opt usage) 173 | wait_count=0 174 | while ! prun-opt /usr/sbin/cryptsetup luksClose /dev/mapper/${mapped_partition} && [ ${wait_count} -lt 5 ]; do 175 | sleep 1 176 | wait_count=$((wait_count + 1)) 177 | done 178 | fi 179 | done 180 | fi 181 | 182 | fi 183 | -------------------------------------------------------------------------------- /usr/lib/tik/modules/post/15-encrypt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: Copyright 2024 SUSE LLC 3 | # SPDX-FileCopyrightText: Copyright 2024 Richard Brown 4 | 5 | # Module does not actually do any encryption, but is intended to finish installation of an encrypted image, such as one deployed via systemd-repart 6 | # Module expects to find a single ESP partition (find_esp) and a single LUKS2 partition (find_crypt) on $TIK_INSTALL_DEVICE, upon which it will do the following 7 | # - Open the encrypted device, mounting var, etc, boot/efi, tmp, run, sys, dev and proc (open_partition) 8 | # - Against the mounted partition, do the following (configure_encryption) 9 | # - write /etc/kernel/cmdline 10 | # - write /etc/crypttab 11 | # - update any /etc/fstab lines regarding /boot/efi and replace them with the correct ones for the on disk vfat filesystem 12 | # - populate /boot/efi with sdbootutil install & sdbootutil mkinitrd 13 | # - populate /etc/sysconfig/fde-tools (so the measurements can be updated on first boot) 14 | # - Close the partition (close_partition) 15 | # - Generate a recovery key (generate_recoveryKey) 16 | # - Add recovery key to device and identify it as a systemd-recovery key (add_recoveryKey) 17 | # - Display the recovery key to the user (display_recoveryKey) 18 | # - Remove the temporary key-file and replace it either with TPM enrollment or a user-supplied passphrase (add_key) 19 | # It is expected the LUKS2 partition is already encrypted with a key-file in the only populated keyslot. 20 | 21 | encrypt_dir=/var/lib/tik/encrypt 22 | encrypt_pipe=/tmp/encryptpipe 23 | if [ ! -d ${encrypt_dir}/mnt ]; then 24 | prun /usr/bin/mkdir -p ${encrypt_dir}/mnt 25 | fi 26 | if [ ! -p ${encrypt_pipe} ]; then 27 | mkfifo ${encrypt_pipe} 28 | fi 29 | 30 | crypt_progress() { 31 | log "[crypt_progress] Monitoring encryption progress" 32 | (tail -f ${encrypt_pipe}) | d --progress --title="Configuring Encryption" --auto-close --no-cancel --width=400 33 | rm ${encrypt_pipe} 34 | log "[crypt_progress] Encryption progress reached 100%" 35 | } 36 | 37 | find_crypt() { 38 | echo "# Finding encrypted partition" > ${encrypt_pipe} 39 | log "[find_crypt] finding encrypted partition" 40 | probe_partitions ${TIK_INSTALL_DEVICE} "crypto_LUKS" 41 | if [ -z "${probedpart}" ]; then 42 | error "encrypted partition not found" 43 | fi 44 | cryptpart=${probedpart} 45 | log "[find_crypt] found ${cryptpart}" 46 | echo "14" > ${encrypt_pipe} 47 | } 48 | 49 | find_esp() { 50 | echo "# Finding encrypted partition" > ${encrypt_pipe} 51 | log "[find_esp] finding ESP" 52 | probe_partitions ${TIK_INSTALL_DEVICE} "vfat" 53 | if [ -z "${probedpart}" ]; then 54 | error "esp partition not found" 55 | fi 56 | esppart=${probedpart} 57 | log "[find_esp] found ${esppart}" 58 | echo "28" > ${encrypt_pipe} 59 | } 60 | 61 | open_partition() { 62 | echo "# Opening ${cryptpart}" > ${encrypt_pipe} 63 | log "[open_partition] opening ${cryptpart} and mounting for chroot" 64 | prun /usr/sbin/cryptsetup luksOpen --key-file=${tik_keyfile} ${cryptpart} aeon_root 65 | echo "35" > ${encrypt_pipe} 66 | prun /usr/bin/mount -o compress=zstd:1 /dev/mapper/aeon_root ${encrypt_dir}/mnt 67 | for i in proc dev sys tmp 'sys/firmware/efi/efivars' 'sys/fs/cgroup'; do 68 | prun /usr/bin/mount --bind "/$i" "${encrypt_dir}/mnt/$i" 69 | done 70 | prun /usr/bin/mount -o compress=zstd:1,subvol=/@/.snapshots /dev/mapper/aeon_root ${encrypt_dir}/mnt/.snapshots 71 | prun /usr/bin/mount -o compress=zstd:1,subvol=/@/var /dev/mapper/aeon_root ${encrypt_dir}/mnt/var 72 | etcmountcmd=$(cat ${encrypt_dir}/mnt/etc/fstab | grep "overlay /etc" | sed 's/\/sysroot\//${encrypt_dir}\/mnt\//g' | sed 's/\/work-etc.*/\/work-etc ${encrypt_dir}\/mnt\/etc\//' | sed 's/overlay \/etc overlay/\/usr\/bin\/mount -t overlay overlay -o/') 73 | eval prun "$etcmountcmd" 74 | prun /usr/bin/mount ${esppart} ${encrypt_dir}/mnt/boot/efi 75 | prun /usr/bin/mount -t tmpfs tmpfs "${encrypt_dir}/mnt/run" 76 | prun /usr/bin/mount -t securityfs securityfs "${encrypt_dir}/mnt/sys/kernel/security" 77 | echo "42" > ${encrypt_pipe} 78 | } 79 | 80 | configure_encryption() { 81 | # If Default Mode has been detected, configure crypttab for TPM 82 | if [ "${tik_encrypt_mode}" == 0 ]; then 83 | crypttab_opts=',tpm2-device=auto' 84 | fi 85 | echo "# Writing cmdline, crypttab, and fstab" > ${encrypt_pipe} 86 | log "[configure_encryption] configuring cmdline, crypttab, PCR policy, fstab and populating ${esppart}" 87 | espUUID=$(lsblk -n -r -o UUID ${esppart}) 88 | prun /usr/bin/gawk -v espUUID=$espUUID -i inplace '$2 == "/boot/efi" { $1 = "UUID="espUUID } { print $0 }' ${encrypt_dir}/mnt/etc/fstab 89 | # root=UUID= cmdline definition is a hard requirement of sdbootutil for updating predictions 90 | rootUUID=$(lsblk -n -r -o UUID /dev/mapper/aeon_root) 91 | prun /usr/bin/sed -i -e "s,\$, root=UUID=${rootUUID}," ${encrypt_dir}/mnt/etc/kernel/cmdline 92 | # /etc/crypttab is a hard requirement of sdbootutil for updating predictions 93 | cryptUUID=$(lsblk -n -r -d -o UUID ${cryptpart}) 94 | echo "aeon_root UUID=${cryptUUID} none x-initrd.attach${crypttab_opts}" | prun tee ${encrypt_dir}/mnt/etc/crypttab 95 | echo "# Installing boot loader" > ${encrypt_pipe} 96 | # Populate ESP 97 | prun /usr/bin/chroot ${encrypt_dir}/mnt sdbootutil -vv --esp-path /boot/efi --no-variables install 1>&2 98 | echo "56" > ${encrypt_pipe} 99 | echo "# Creating initrd" > ${encrypt_pipe} 100 | # FIXME: Dracut gets confused by previous installations on occasion with the default config, override the problematic option temporarily 101 | /usr/bin/echo 'hostonly_cmdline="no"' | prun tee ${encrypt_dir}/mnt/etc/dracut.conf.d/99-tik.conf 102 | # mkinitrd done by add-all-kernels 103 | prun /usr/bin/chroot ${encrypt_dir}/mnt sdbootutil -vv --esp-path /boot/efi --no-variables add-all-kernels 1>&2 104 | # FIXME: Dracut gets confused by previous installations on occasion with the default config, remove override now initrd done 105 | prun /usr/bin/rm ${encrypt_dir}/mnt/etc/dracut.conf.d/99-tik.conf 106 | echo "70" > ${encrypt_pipe} 107 | # If Default mode has been detected, configure PCR policy 108 | if [ "${tik_encrypt_mode}" == 0 ]; then 109 | # Explaining the chosen PCR list below 110 | # - 0 - UEFI firmware, will require recovery key after firmware update 111 | # - 4 - Bootloader and drivers, should never recovery key as bootloader should only be updated with new PCR measurements 112 | # - 5 - GPT Partition table, should never require recovery key as partition layout shouldn't change 113 | # - 7 - SecureBoot state, will require recovery key if SecureBoot is enabled/disabled 114 | # - 9 - initrd - should never require recovery key as initrd should only be updated with new PCR measurements 115 | echo "FDE_SEAL_PCR_LIST=0,4,5,7,9" | prun tee ${encrypt_dir}/mnt/etc/sysconfig/fde-tools 116 | # Explaining why the following PCRs were not used 117 | # - 1 - Not only changes with CPU/RAM/hardware changes, but also when UEFI config changes are made, which is too common to lockdown 118 | # - 2 - Includes option ROMs on pluggable hardware, such as external GPUs. Attaching a GPU to your laptop shouldn't hinder booting. 119 | # - 3 - Firmware from pluggable hardware. Attaching hardware to your laptop shouldn't hinder booting 120 | prun /usr/bin/tee ${encrypt_dir}/mnt/etc/systemd/system/firstboot-update-predictions.service << EOF 121 | [Unit] 122 | Description=First Boot Update Predictions 123 | ConditionSecurity=tpm2 124 | 125 | [Service] 126 | Type=oneshot 127 | ExecStart=rm /etc/systemd/system/firstboot-update-predictions.service 128 | ExecStart=rm /etc/systemd/system/default.target.wants/firstboot-update-predictions.service 129 | ExecStart=/usr/bin/sdbootutil update-predictions 130 | 131 | [Install] 132 | WantedBy=default.target 133 | EOF 134 | prun /usr/bin/ln -s ${encrypt_dir}/mnt/etc/systemd/system/firstboot-update-predictions.service ${encrypt_dir}/mnt/etc/systemd/system/default.target.wants/firstboot-update-predictions.service 135 | log "[configure_encryption] Generating Predictions" 136 | echo "# Generating TPM Predictions" > ${encrypt_pipe} 137 | prun /usr/bin/chroot ${encrypt_dir}/mnt sdbootutil -vv update-predictions 138 | echo "73" > ${encrypt_pipe} 139 | log "[configure_encryption] Default Mode - Enrolling ${cryptpart} to TPM 2.0" 140 | echo "# Enrolling to TPM" > ${encrypt_pipe} 141 | prun /usr/bin/chroot ${encrypt_dir}/mnt systemd-cryptenroll --unlock-key-file=${tik_keyfile} --tpm2-device=auto ${cryptpart} 142 | echo "76" > ${encrypt_pipe} 143 | fi 144 | } 145 | 146 | close_partition() { 147 | echo "# Closing ${cryptpart}" > ${encrypt_pipe} 148 | log "[close_partition] unmounting and closing ${cryptpart}" 149 | for i in proc dev run tmp 'boot/efi' etc var '.snapshots' 'sys/kernel/security' 'sys/firmware/efi/efivars' 'sys/fs/cgroup' sys; do 150 | prun /usr/bin/umount "${encrypt_dir}/mnt/$i" 151 | done 152 | prun /usr/bin/umount ${encrypt_dir}/mnt 153 | prun /usr/sbin/cryptsetup luksClose aeon_root 154 | echo "77" > ${encrypt_pipe} 155 | } 156 | 157 | generate_recoveryKey() { 158 | echo "# Generating recovery key" > ${encrypt_pipe} 159 | log "[generate_recoveryKey] generating recovery key" 160 | modhex=('c' 'b' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'n' 'r' 't' 'u' 'v') 161 | mapfile -t raw_key < <(hexdump -v --format '1/1 "%u\n"' -n 32 /dev/random) 162 | [ "${#raw_key[@]}" = 32 ] 163 | key="" 164 | for ((i=0;i<"${#raw_key[@]}";++i)); do 165 | [ "$i" -gt 0 ] && [ "$((i%4))" -eq 0 ] && key="${key}-" 166 | c="${raw_key[i]}" 167 | key="${key}${modhex[$((c>>4))]}${modhex[$((c&15))]}" 168 | done 169 | echo "84" > ${encrypt_pipe} 170 | } 171 | 172 | add_recoveryKey() { 173 | echo "# Adding recovery key to ${cryptpart}" > ${encrypt_pipe} 174 | log "[add_recoveryKey] adding recovery key to ${cryptpart}" 175 | prun /usr/sbin/cryptsetup luksAddKey --key-file=${tik_keyfile} --batch-mode --force-password "${cryptpart}" <<<"${key}" 176 | echo '{"type":"systemd-recovery","keyslots":["2"]}' | prun /usr/sbin/cryptsetup token import "${cryptpart}" 177 | echo "100" > ${encrypt_pipe} 178 | } 179 | 180 | display_recoveryKey() { 181 | local defaultmsg="This ${TIK_OS_NAME} system is encrypted and checks its own integrity on every boot\nIn the event of these integrity checks failing, you will need to use the Recovery Key provided below to enter this system\n\nLikely reasons for integrity checks failing include:\n\n• UEFI System Firmware updated\n• Secure Boot changed from enabled or disabled\n• Boot drive was moved to a different computer\n• Disk partitions were changed\n• Boot loader or initrd were altered unexpectedly\n\nIf you are unaware as to why the system is requesting the recovery key, this systems security may have been compromised\nThe best course of action may be to not unlock the disk until you can determine what changed to require the Recovery Key\n\nThis systems Recovery Key is:\n\n ${key}\n\nPlease save this secret Recovery Key in a secure location\n\n" 182 | local fallbackmsg="In addition to your Passphrase a Recovery Key has been generated:\n\n ${key}\n\nPlease save this secret Recovery Key in a secure location\nIt may be used to regain access to this system if the other Passphrase becomes lost or forgotten\n\n" 183 | local message 184 | [ "${tik_encrypt_mode}" == 0 ] && message=${defaultmsg} 185 | [ "${tik_encrypt_mode}" == 1 ] && message=${fallbackmsg} 186 | log "[display_recoveryKey] displaying recovery key" 187 | zenity --width=500 --height=500 --no-wrap --warning --icon=security-high-symbolic --title="Encryption Recovery Key" --text="${message}You may optionally scan the recovery key off screen:\n$(qrencode ${key} -t UTF8i)\nFor more information please visit https://aeondesktop.org/encrypt" 188 | log "[display_recoveryKey] recovery key dialogue dismissed" 189 | } 190 | 191 | add_key() { 192 | if [ "${tik_encrypt_mode}" == 1 ]; then 193 | d --width=500 --height=300 --no-wrap --warning --icon=security-high-symbolic --title="Set Encryption Passphrase" --text="This ${TIK_OS_NAME} system is encrypted and will require a Passphrase on every boot\n\nYou will be prompted to set the Passphrase on the next screen\n\nFor more information please visit https://aeondesktop.org/encrypt" 194 | log "[add_key] Fallback Mode - Prompting user for passphrase for ${cryptpart}" 195 | # Not using 'd' function to avoid logging the password 196 | while true 197 | do 198 | retval=0 199 | key="$(zenity --password --title='Set Encryption Passphrase')" || retval=$? 200 | case $retval in 201 | 0) 202 | prun /usr/sbin/cryptsetup luksAddKey --key-file=${tik_keyfile} --batch-mode --force-password "${cryptpart}" <<<"${key}" 203 | return 0 204 | ;; 205 | 1|255) 206 | zenity --question --text="Do you really want to quit?" && exit 1 207 | ;; 208 | esac 209 | done 210 | fi 211 | } 212 | 213 | crypt_progress & 214 | find_crypt 215 | find_esp 216 | open_partition 217 | configure_encryption 218 | close_partition 219 | add_key 220 | generate_recoveryKey 221 | add_recoveryKey 222 | display_recoveryKey -------------------------------------------------------------------------------- /usr/lib/tik/lib/tik-functions: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: Copyright 2023-2024 SUSE LLC 3 | # SPDX-FileCopyrightText: Copyright 2023-2024 Richard Brown 4 | 5 | log(){ 6 | echo "[${tik_module}][$(date +"%Y%m%d-%T")][LOG] $*" 1>&2 7 | } 8 | 9 | warn() { 10 | echo "[${tik_module}][$(date +"%Y%m%d-%T")][WARN] $*" 1>&2 11 | d --warning --text="$*" 12 | } 13 | 14 | error() { 15 | echo "[${tik_module}][$(date +"%Y%m%d-%T")][ERROR] $*" 1>&2 16 | d --error --text "$*" 17 | exit 1 18 | } 19 | 20 | d(){ 21 | while true 22 | do 23 | retval=0 24 | result="$(zenity "$@")" || retval=$? 25 | log "[zenity][${retval}][${result}] $@" 26 | case $retval in 27 | 0) 28 | return 0 29 | ;; 30 | 1|255) 31 | zenity --question --text="Do you really want to quit?" && exit 1 32 | ;; 33 | esac 34 | done 35 | } 36 | 37 | # variant of privileged run (prun) function that doesn't require the pkexec call to return 0 38 | prun-opt() { 39 | if [ "${debug}" == "1" ]; then 40 | log "[pkexec-noexec] $@" 41 | else 42 | retval=0 43 | pkexec "$@" 44 | retval=$? 45 | log "[pkexec][${retval}] $@" 46 | fi 47 | } 48 | 49 | # Most commonly used prun function, which requires the called command to work 50 | prun() { 51 | prun-opt "$@" 52 | if [ "${retval}" != "0" ]; then 53 | error "Command $@ FAILED" 54 | fi 55 | } 56 | 57 | get_persistent_device_from_unix_node() { 58 | local unix_device=$1 59 | local schema=$2 60 | local node 61 | local persistent_name 62 | node=$(basename "${unix_device}") 63 | for persistent_name in /dev/disk/"${schema}"/*; do 64 | if [ "$(basename "$(readlink "${persistent_name}")")" = "${node}" ];then 65 | if [[ ${persistent_name} =~ ^/dev/disk/"${schema}"/nvme-eui ]]; then 66 | # Filter out nvme-eui nodes as they are not descriptive to the user 67 | continue 68 | fi 69 | echo "${persistent_name}" 70 | return 71 | fi 72 | done 73 | warn "Could not find ${schema} representation of ${node}. Using original device ${unix_device}" 74 | echo "${unix_device}" 75 | } 76 | 77 | probe_partitions() { 78 | local probe_dir=/var/lib/tik/probe 79 | local filesystem_type=$2 80 | local filematch=$3 81 | local device=$1 82 | local mountops 83 | local part 84 | if [[ "${filesystem_type}" == "btrfs" ]]; then 85 | mountops="-o compress=zstd:1" 86 | fi 87 | prun /usr/bin/mkdir -p ${probe_dir}/mnt 88 | probedpart="" 89 | for part in $(lsblk ${device} -p -n -r -o ID-LINK,FSTYPE|tr -s ' ' ";"|grep ";${filesystem_type}"|cut -d\; -f1); do 90 | if [ -z ${filematch} ]; then 91 | log "[probe_partitions] no file match required" 92 | # Fallback to unix device in order to fix issue with USB devices 93 | probedpart="$(/usr/bin/readlink -f "/dev/disk/by-id/""${part}")" 94 | log "[probe_partitions] Partition ${probedpart} found" 95 | else # Check if ${filematch} exists 96 | # Fallback to unix device in order to fix issue with USB devices 97 | part="$(/usr/bin/readlink -f "/dev/disk/by-id/""${part}")" 98 | prun /usr/bin/mount ${mountops} ${part} "${probe_dir}/mnt" 99 | if [ -f ${probe_dir}/mnt/${filematch} ]; then 100 | log "[probe_partitions] File ${filematch} found" 101 | # Fallback to unix device in order to fix issue with USB devices 102 | probedpart="${part}" 103 | log "[probe_partitions] Partition ${probedpart} found" 104 | if grep -q 'PRETTY_NAME="openSUSE MicroOS"' ${probe_dir}/mnt/${filematch} && [ -f ${probe_dir}/mnt/usr/bin/gnome-shell ]; then 105 | # Found legacy Aeon, activate easter egg 106 | log "Legacy Aeon Install FOUND" 107 | legacy_aeon=1 108 | fi 109 | fi 110 | prun-opt /usr/bin/umount ${probe_dir}/mnt 111 | fi 112 | done 113 | prun /usr/bin/rmdir ${probe_dir}/mnt 114 | } 115 | 116 | get_disk() { 117 | # Volume label for the tik install media must be set to "TIKINSTALL" to filter it out from the device list 118 | tik_volid="TIKINSTALL" 119 | local disk_id="by-id" 120 | local disk_size 121 | local disk_device 122 | local disk_device_by_id 123 | local disk_meta 124 | local disk_list 125 | local device_array 126 | local list_items 127 | local blk_opts="-p -n -r -o NAME,SIZE,TYPE" 128 | local message 129 | local blk_opts_plus_label="${blk_opts},LABEL" 130 | local tik_install_disk_part 131 | local part_meta 132 | local part_count 133 | local part_size 134 | local part_info 135 | local part_fs 136 | local blk_opts_part_info="${blk_opts_plus_label},FSTYPE" 137 | local usb_match_1="usb" 138 | local usb_match_2=":0" 139 | 140 | tik_install_disk_part=$( 141 | eval lsblk "${blk_opts_plus_label}" | \ 142 | tr -s ' ' ":" | \ 143 | grep ":${tik_volid}" | \ 144 | cut -f1 -d: 145 | ) 146 | 147 | for disk_meta in $( 148 | eval lsblk "${blk_opts}" | grep -E "disk|raid" | tr ' ' ":" 149 | );do 150 | disk_size=$(echo "${disk_meta}" | cut -f2 -d:) 151 | if [[ "${disk_size}" == "0B" ]]; then 152 | # ignore disks with no size, e.g. empty SD card readers 153 | continue 154 | fi 155 | disk_device="$(echo "${disk_meta}" | cut -f1 -d:)" 156 | # find partitions and info for this disk 157 | part_count=0 158 | part_info="" 159 | for part_meta in $( 160 | eval lsblk "${blk_opts_part_info}" | grep -E "${disk_device}.+part.+" | tr ' ' ":" 161 | );do 162 | part_count=$(expr $part_count + 1) 163 | part_size=$(echo "${part_meta}" | cut -f2 -d:) 164 | part_fs=$(echo "${part_meta}" | cut -f5 -d:) 165 | if [ -n "${part_info}" ]; then 166 | part_info="${part_info}," 167 | fi 168 | if [ -n "${part_fs}" ]; then 169 | part_info="${part_info}${part_fs}(${part_size})" 170 | else 171 | part_info="${part_info}unknown(${part_size})" 172 | fi 173 | done 174 | if [[ ${part_count} -eq 0 ]]; then 175 | part_info="none" 176 | fi 177 | if [[ "${tik_install_disk_part}" == "${disk_device}"* ]]; then 178 | # ignore install source device 179 | continue 180 | fi 181 | if [[ ${disk_device} =~ ^/dev/fd ]];then 182 | # ignore floppy disk devices 183 | continue 184 | fi 185 | if [[ ${disk_device} =~ ^/dev/zram ]];then 186 | # ignore zram devices 187 | continue 188 | fi 189 | disk_device_by_id=$( 190 | get_persistent_device_from_unix_node "${disk_device}" "${disk_id}" 191 | ) 192 | if [[ ( "${TIK_ALLOW_USB_INSTALL_DEVICES}" -ne 1 ) && ( "{$disk_device_by_id}" == *"${usb_match_1}"* || "{$disk_device_by_id}" == *"${usb_match_2}"* ) ]]; then 193 | # ignore USB devices if TIK_ALLOW_USB_INSTALL_DEVICES not set in config 194 | continue 195 | fi 196 | if [ -n "${disk_device_by_id}" ];then 197 | disk_device=${disk_device_by_id} 198 | fi 199 | list_items="${list_items} $(basename ${disk_device}) ${disk_size} ${part_count} ${part_info}" 200 | disk_list="${disk_list} $(basename ${disk_device}) ${disk_size}" 201 | done 202 | if [ -n "${TIK_INSTALL_DEVICE}" ];then 203 | # install device overwritten by config. 204 | local device=${TIK_INSTALL_DEVICE} 205 | local device_meta 206 | local device_size 207 | if [ ! -e "${device}" ];then 208 | local no_dev="Given device ${device} does not exist." 209 | error "${no_dev}" 210 | fi 211 | if [ ! -b "${device}" ];then 212 | local no_block_dev="Given device ${device} is not a block special." 213 | error "${no_block_dev}" 214 | fi 215 | device_meta=$( 216 | eval lsblk "${blk_opts}" "${device}" |\ 217 | grep -E "disk|raid" | tr ' ' ":" 218 | ) 219 | device_size=$(echo "${device_meta}" | cut -f2 -d:) 220 | # this case is not shown in manual selection, threfore we don't need partition info 221 | list_items="$(basename ${device}) ${device_size}" 222 | disk_list="$(basename ${device}) ${device_size}" 223 | message="tik installation device set to to: ${device}" 224 | log "${message}" 225 | fi 226 | if [ -z "${list_items}" ];then 227 | local no_device_text="No device(s) for installation found." 228 | error "${no_device_text}" 229 | fi 230 | if [ -n "${disk_list}" ];then 231 | local count=0 232 | local device_index=0 233 | for entry in ${disk_list};do 234 | if [ $((count % 2)) -eq 0 ];then 235 | device_array[${device_index}]=${entry} 236 | device_index=$((device_index + 1)) 237 | fi 238 | count=$((count + 1)) 239 | done 240 | if [ "${device_index}" -eq 1 ];then 241 | # one single disk device found, use it 242 | # Add back full path to it 243 | TIK_INSTALL_DEVICE="/dev/disk/${disk_id}/${device_array[0]}" 244 | 245 | # Fallback to unix device in case by-id does not exist 246 | # see get_persistent_device_from_unix_node, it does fallback like this. 247 | if [ ! -e "${TIK_INSTALL_DEVICE}" ]; then 248 | TIK_INSTALL_DEVICE="/dev/${device_array[0]}" 249 | fi 250 | else 251 | # manually select from storage list 252 | d --list --column=Disk --column=Size --column=Partitions --column=Filesystems --width=1050 --height=340 --title="Select A Disk" --text="Select the disk to install the operating system to. Make sure any important documents and files have been backed up.\n" ${list_items} 253 | # Add back full path to it 254 | TIK_INSTALL_DEVICE="/dev/disk/${disk_id}/${result}" 255 | 256 | # Fallback to unix device in case by-id does not exist 257 | # see get_persistent_device_from_unix_node, it does fallback like this. 258 | if [ ! -e "${TIK_INSTALL_DEVICE}" ]; then 259 | TIK_INSTALL_DEVICE="/dev/${result}" 260 | fi 261 | fi 262 | fi 263 | } 264 | 265 | get_img() { 266 | local list_items 267 | local message 268 | local img_meta 269 | local img_item 270 | local img_list 271 | local img_array 272 | local file_type 273 | # Images are assumed to be named to the following standard 274 | # $ProductName.$Version.raw.xz for block devices 275 | # $ProductName.$Version.raw for systemd-repart images 276 | # Any extraneous fields may confuse tik's detection, selection and presentation of the image to the user 277 | for file_type in '*.raw.xz' '*.raw';do 278 | for img_meta in $(cd $TIK_IMG_DIR && (stat --printf="%n\t%s\n" ${file_type} | tr ' ' ":"));do 279 | img_filename="$(echo $img_meta | cut -f1 -d:)" 280 | img_size="$(echo $img_meta | cut -f2 -d:)" 281 | list_items="${list_items} ${img_filename} ${img_size}" 282 | done 283 | done 284 | if [ -n "${TIK_INSTALL_IMAGE}" ];then 285 | # install image overwritten by config. 286 | local img=${TIK_INSTALL_IMAGE} 287 | local img_meta 288 | local img_size 289 | if [ ! -e "${img}" ];then 290 | local no_img="Given image ${img} does not exist." 291 | error "${no_img}" 292 | fi 293 | if [ ! -s "${img}" ];then 294 | local empty_img="Given image ${img} is empty." 295 | error "${empty_img}" 296 | fi 297 | img_meta=$( 298 | eval cd $TIK_IMG_DIR && (stat --printf="%n\t%s\n" $img | tr ' ' ":") 299 | ) 300 | img_filename="$(echo $img_meta | cut -f1 -d:)" 301 | img_size="$(echo $img_meta | cut -f2 -d:)" 302 | list_items="${list_items} ${img_filename} ${img_size}" 303 | message="tik installation image set to to: ${img}" 304 | log "${message}" 305 | fi 306 | if [ -z "${list_items}" ];then 307 | TIK_INSTALL_IMAGE='TIK_SELFDEPLOY' 308 | fi 309 | img_list=${list_items} 310 | if [ -n "${img_list}" ];then 311 | local count=0 312 | local img_index=0 313 | for entry in ${img_list};do 314 | if [ $((count % 2)) -eq 0 ];then 315 | img_array[${img_index}]=${entry} 316 | img_index=$((img_index + 1)) 317 | fi 318 | count=$((count + 1)) 319 | done 320 | if [ "${img_index}" -eq 1 ];then 321 | # one single disk image found, use it 322 | TIK_INSTALL_IMAGE="${img_array[0]}" 323 | else 324 | # manually select from storage list 325 | d --list --column=Image --column=Size --title="Select A Image" --text="Select the operating system image to install.\n" ${list_items} 326 | TIK_INSTALL_IMAGE="$result" 327 | fi 328 | fi 329 | } 330 | 331 | reread_partitiontable() { 332 | # We've just done a lot to $TIK_INSTALL_DEVICE and it's probably a good idea to make sure the partition table is clearly read so tools like dracut dont get confused. 333 | log "[reread_partitiontable] Re-reading partition table" 334 | prun /usr/sbin/blockdev --rereadpt ${TIK_INSTALL_DEVICE} 335 | } 336 | 337 | create_keyfile() { 338 | # Even if there's no partitions using encryption, systemd-repart will need a key-file defined for the --key-file parameter. 339 | tik_keyfile=/tmp/tikkeyfile 340 | log "[create_keyfile] Creating keyfile ${tik_keyfile}" 341 | prun /usr/bin/dd bs=512 count=4 if=/dev/urandom of=${tik_keyfile} iflag=fullblock 342 | prun /usr/bin/chmod 400 ${tik_keyfile} 343 | } 344 | 345 | wipe_keyfile() { 346 | # We made a keyfile and need to clean it up at the end of the installation, possibly wiping it from the newly installed device 347 | log "[wipe_keyfile] Deleting keyfile ${tik_keyfile}" 348 | probe_partitions ${TIK_INSTALL_DEVICE} "crypto_LUKS" 349 | if [ -n "${probedpart}" ]; then 350 | # Assumes Slot 0 is always by the key-file at enrolment 351 | prun /usr/bin/systemd-cryptenroll --unlock-key-file=${tik_keyfile} --wipe-slot=0 ${probedpart} 352 | fi 353 | # We're done with the key-file, so remove it 354 | prun /usr/bin/rm ${tik_keyfile} 355 | } 356 | 357 | dump_image() { 358 | local image_source_files=$1 359 | local image_target=$2 360 | 361 | d --question --no-wrap --title="Begin Installation?" --text="Once the installation begins the changes to the selected disk are irreversible.\n\nProceeding will fully erase the disk.\n\nContinue with installation?" 362 | 363 | case "${image_source_files}" in 364 | *.raw.xz) 365 | dump_image_dd ${image_source_files} ${image_target} 366 | ;; 367 | *.raw) 368 | dump_image_repart_image ${image_source_files} ${image_target} 369 | ;; 370 | TIK_SELFDEPLOY) 371 | dump_image_repart_self ${image_target} 372 | ;; 373 | *) 374 | error "invalid image type provided" 375 | esac 376 | } 377 | 378 | dump_image_dd() { 379 | local image_source_files=$1 380 | local image_target=$2 381 | log "[dump_image_dd] deploying ${TIK_IMG_DIR}/${image_source_files}" 382 | (xzcat ${TIK_IMG_DIR}/${image_source_files} | pv -f -F "# %b copied in %t %r" | prun /usr/bin/dd of=${image_target} bs=64k) 2>&1 | d --progress --title="Installing ${TIK_OS_NAME}" --pulsate --auto-close --no-cancel --width=400 383 | prun /usr/bin/sync | d --progress --title="Syncing" --pulsate --auto-close --no-cancel --width=400 384 | } 385 | 386 | dump_image_repart_image() { 387 | local image_source_files=$1 388 | local image_target=$2 389 | local success=0 390 | local max_attempts=5 391 | local attempt_num=1 392 | create_keyfile 393 | log "[dump_image_repart_image] deploying ${TIK_IMG_DIR}/${image_source_files}" 394 | # systemd-repart doesn't always parse the contents of the image perfectly first time, so retry a few times before declaring it a failure 395 | while [ ${success} = 0 ] && [ ${attempt_num} -lt ${max_attempts} ]; do 396 | prun-opt systemd-repart --no-pager --pretty=0 --empty=force --dry-run=no --key-file=${tik_keyfile} --image=${TIK_IMG_DIR}/${image_source_files} --image-policy=root=unprotected ${image_target} > >(d --progress --title="Installing ${TIK_OS_NAME}" --text="Deploying OS Image" --pulsate --auto-close --no-cancel --width=400) 397 | if [ ${retval} -eq 0 ]; then 398 | success=1 399 | else 400 | # repart couldn't find a root partition 401 | log "[dump_image_repart_image] systemd-repart attempt $attempt_num failed. Trying again..." 402 | sleep 1 403 | # Increment the attempt counter 404 | attempt_num=$(( attempt_num + 1 )) 405 | fi 406 | done 407 | if [ ${success} = 1 ]; then 408 | log "[dump_image_repart_image] systemd-repart succeeded after $attempt_num attempts" 409 | else 410 | error "systemd-repart failed" 411 | fi 412 | } 413 | 414 | dump_image_repart_self() { 415 | local image_target=$1 416 | create_keyfile 417 | log "[dump_image_repart_self] self-deploying" 418 | prun systemd-repart --no-pager --pretty=0 --empty=force --dry-run=no --key-file=${tik_keyfile} ${image_target} > >(d --progress --title="Installing ${TIK_OS_NAME}" --text="Deploying OS Image" --pulsate --auto-close --no-cancel --width=400) 419 | } 420 | 421 | set_boot_target() { 422 | local efipartnum 423 | if [ "${debug}" == "1" ]; then 424 | log "[debug] Not setting EFI boot target" 425 | elif [ -n "${efi_already_set}" ]; then 426 | log "[set_boot_target] boot target already set, not setting again" 427 | else 428 | # Cleanup any existing openSUSE boot entries 429 | prun-opt /usr/sbin/efibootmgr -B -L "openSUSE Boot Manager" 430 | prun /usr/sbin/efibootmgr -O 431 | log "[set_boot_target] searching for ESP partition containing /EFI/systemd/shim.efi on ${TIK_INSTALL_DEVICE}" 432 | probe_partitions ${TIK_INSTALL_DEVICE} "vfat" "/EFI/systemd/shim.efi" 433 | if [ -z "${probedpart}" ]; then 434 | error "esp partition not found" 435 | fi 436 | efipartnum=$(lsblk ${probedpart} -p -n -r -o PARTN) 437 | log "[set_boot_target] found ESP on ${probedpart}, partition number ${efipartnum}" 438 | prun /usr/sbin/efibootmgr -c -L "openSUSE Boot Manager" -d ${TIK_INSTALL_DEVICE} -l "\EFI\systemd\shim.efi" -p ${efipartnum} 439 | # Log to show the resulting eficonfig 440 | log "[set_boot_target] $(prun /usr/sbin/efibootmgr)" 441 | efi_already_set=1 442 | fi 443 | } 444 | 445 | load_modules() { 446 | local module_dir 447 | if [[ $2 = "custom" ]]; then 448 | module_dir=$TIK_CUSTOM_DIR/modules/$1 449 | else 450 | module_dir=$tik_dir/modules/$1 451 | fi 452 | if [ -n "$(ls -A $module_dir)" ]; then 453 | for f in $module_dir/* 454 | do 455 | tik_module="$f" 456 | log "[START] $module_dir/$f" 457 | . $f 458 | log "[STOP] $module_dir/$f" 459 | done 460 | fi 461 | tik_module="tik" 462 | } 463 | --------------------------------------------------------------------------------