diff --git a/base/base.conf b/base/base.conf index 5e2684c..88d8261 100644 --- a/base/base.conf +++ b/base/base.conf @@ -2,11 +2,15 @@ Distribution=debian Release=trixie +[Build] +PackageCacheDirectory=mkosi.cache +Environment=KERNEL_IMAGE KERNEL_VERSION + [Output] Format=uki +ManifestFormat=json ImageId=tdx-debian OutputDirectory=build -PackageCacheDirectory=mkosi.cache Seed=630b5f72-a36a-4e83-b23d-6ef47c82fd9c [Host] @@ -15,12 +19,10 @@ Seed=630b5f72-a36a-4e83-b23d-6ef47c82fd9c [Content] SourceDateEpoch=0 KernelCommandLine=console=tty0 console=ttyS0,115200n8 mitigations=auto,nosmt spec_store_bypass_disable=on nospectre_v2 -Environment=KERNEL_IMAGE KERNEL_VERSION SkeletonTrees=base/mkosi.skeleton FinalizeScripts=base/debloat.sh PostInstallationScripts=base/debloat-systemd.sh BuildScripts=base/mkosi.build -PrepareScripts=base/export-packages.sh CleanPackageMetadata=true Packages=kmod @@ -28,6 +30,9 @@ Packages=kmod systemd-boot-efi busybox util-linux + procps + ca-certificates + openssl iproute2 udhcpc e2fsprogs @@ -37,4 +42,4 @@ BuildPackages=build-essential cmake pkg-config clang - cargo \ No newline at end of file + cargo diff --git a/base/debloat-systemd.sh b/base/debloat-systemd.sh index adf4900..f0d0a84 100755 --- a/base/debloat-systemd.sh +++ b/base/debloat-systemd.sh @@ -22,6 +22,7 @@ systemd_bin_whitelist=( "systemctl" "journalctl" "systemd" + "systemd-tty-ask-password-agent" ) mkosi-chroot dpkg-query -L systemd | grep -E '^/usr/bin/' | while read -r bin_path; do diff --git a/base/debloat.sh b/base/debloat.sh index 69e33e9..f337fa5 100755 --- a/base/debloat.sh +++ b/base/debloat.sh @@ -27,6 +27,7 @@ debloat_paths=( "/usr/share/mime" "/usr/lib/modules" "/usr/lib/udev/hwdb.d" + "/usr/lib/udev/hwdb.bin" "/usr/lib/systemd/catalog" "/usr/lib/systemd/user" "/usr/lib/systemd/user-generators" @@ -35,7 +36,6 @@ debloat_paths=( "/usr/lib/tmpfiles.d" "/etc/systemd/network" "/etc/credstore" - "/usr/lib/x86_64-linux-gnu/security" ) for p in "${debloat_paths[@]}"; do rm -rf "$BUILDROOT$p"; done \ No newline at end of file diff --git a/base/export-packages.sh b/base/export-packages.sh deleted file mode 100755 index 8413f6e..0000000 --- a/base/export-packages.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -if [ "$1" == "final" ]; then - dpkg-query -W -f='${Package},${Architecture},${Version}\n' > $SRCDIR/build/packages.csv -fi \ No newline at end of file diff --git a/base/mkosi.skeleton/etc/systemd/system/persistent-mount.service b/base/mkosi.skeleton/etc/systemd/system/persistent-mount.service new file mode 100644 index 0000000..1c630ea --- /dev/null +++ b/base/mkosi.skeleton/etc/systemd/system/persistent-mount.service @@ -0,0 +1,14 @@ +[Unit] +Description=Wait for Persistent Directory Mount +DefaultDependencies=no +Conflicts=shutdown.target +Before=minimal.target +After=local-fs-pre.target + +[Service] +Type=oneshot +ExecStart=/bin/bash -c 'until grep -q " /persistent " /proc/mounts; do sleep 1; done' +RemainAfterExit=yes + +[Install] +WantedBy=minimal.target diff --git a/base/mkosi.skeleton/init b/base/mkosi.skeleton/init index 8fe1709..b6f1256 100755 --- a/base/mkosi.skeleton/init +++ b/base/mkosi.skeleton/init @@ -8,5 +8,10 @@ mount -t devtmpfs none /dev mount -t tmpfs none /run mount -t configfs none /sys/kernel/config -# Execute systemd -exec /lib/systemd/systemd systemd.unit=minimal.target +# Workaround to make pivot_root work +# https://aconz2.github.io/2024/07/29/container-from-initramfs.html +exec unshare --mount sh -c ' + mkdir /@ + mount --rbind / /@ + cd /@ && mount --move . / + exec chroot . /lib/systemd/systemd systemd.unit=minimal.target' diff --git a/base/mkosi.skeleton/usr/bin/azure-complete-provisioning b/base/mkosi.skeleton/usr/bin/azure-complete-provisioning new file mode 100755 index 0000000..9bf5f4e --- /dev/null +++ b/base/mkosi.skeleton/usr/bin/azure-complete-provisioning @@ -0,0 +1,73 @@ +#!/bin/sh +# Report VM is ready to Azure API in the absence of the Azure VM Agent +# Adapted from https://learn.microsoft.com/en-us/azure/virtual-machines/linux/no-agent#bash-script + +set -e + +attempts=1 +retrieved_goal_state=false +until [ "$attempts" -gt 5 ] +do + echo "obtaining goal state - attempt $attempts" + goalstate=$(curl --fail -v -X 'GET' -H "x-ms-agent-name: azure-vm-register" \ + -H "Content-Type: text/xml;charset=utf-8" \ + -H "x-ms-version: 2012-11-30" \ + "http://168.63.129.16/machine/?comp=goalstate") + if [ $? -eq 0 ] + then + echo "successfully retrieved goal state" + retrieved_goal_state=true + break + fi + sleep 5 + attempts=$((attempts+1)) +done + +if [ "$retrieved_goal_state" != "true" ] +then + echo "failed to obtain goal state - cannot register this VM" + exit 1 +fi + +container_id=$(echo "$goalstate" | grep ContainerId | sed 's/\s*<\/*ContainerId>//g' | sed 's/\r$//') +instance_id=$(echo "$goalstate" | grep InstanceId | sed 's/\s*<\/*InstanceId>//g' | sed 's/\r$//') + +ready_doc=$(cat << EOF + + + 1 + + $container_id + + + $instance_id + + Ready + + + + + +EOF +) + +attempts=1 +until [ "$attempts" -gt 5 ] +do + echo "registering with Azure - attempt $attempts" + curl --fail -v -X 'POST' -H "x-ms-agent-name: azure-vm-register" \ + -H "Content-Type: text/xml;charset=utf-8" \ + -H "x-ms-version: 2012-11-30" \ + -d "$ready_doc" \ + "http://168.63.129.16/machine?comp=health" + if [ $? -eq 0 ] + then + echo "successfully register with Azure" + exit 0 + fi + sleep 5 # sleep to prevent throttling from wire server + attempts=$((attempts+1)) +done + +echo "failed to register with Azure after $attempts attempts" +exit 1 \ No newline at end of file diff --git a/bob.conf b/bob.conf new file mode 100644 index 0000000..34b6c94 --- /dev/null +++ b/bob.conf @@ -0,0 +1,9 @@ +[Include] +Include=base/base.conf +Include=bob/bob.conf + +[Distribution] +Mirror=https://snapshot.debian.org/archive/debian/20250526T142542Z/ + +[Build] +ToolsTreeMirror=https://snapshot.debian.org/archive/debian/20250526T142542Z/ \ No newline at end of file diff --git a/bob/bob.conf b/bob/bob.conf new file mode 100644 index 0000000..7d6ff3e --- /dev/null +++ b/bob/bob.conf @@ -0,0 +1,41 @@ +[Build] +Environment=LIGHTHOUSE_BINARY +WithNetwork=true + +[Content] +ExtraTrees=bob/mkosi.extra +PostInstallationScripts=bob/mkosi.postinst +BuildScripts=bob/mkosi.build + +Packages=podman + runc + dropbear + socat + iptables + iproute2 + conntrack + netfilter-persistent + openntpd + curl + jq + ncat + logrotate + sudo + uidmap + passt + fuse-overlayfs + cryptsetup + openssh-sftp-server + udev + libsnappy1v5 + +BuildPackages=build-essential + git + gcc + zlib1g-dev + libzstd-dev + libleveldb-dev + libsnappy-dev + libpq-dev + libssl-dev + golang diff --git a/bob/mkosi.build b/bob/mkosi.build new file mode 100755 index 0000000..bd0e646 --- /dev/null +++ b/bob/mkosi.build @@ -0,0 +1,42 @@ +#!/bin/bash +set -euxo pipefail + +source scripts/build_rust_package.sh +source scripts/make_git_package.sh + +# Compile searchersh +mkdir -p "$DESTDIR/usr/bin" +mkosi-chroot gcc -o "$DESTDIR/usr/bin/searchersh" "$SRCDIR/bob/searchersh.c" +chmod 755 "$DESTDIR/usr/bin/searchersh" + +# Compile lighthouse +build_rust_package \ + "lighthouse" \ + "v7.0.1" \ + "https://github.com/sigp/lighthouse.git" \ + "$LIGHTHOUSE_BINARY" \ + "modern" \ + "-l z -l zstd -l snappy" + +# Build tdx-init +make_git_package \ + "tdx-init" \ + "v0.1.1" \ + "https://github.com/flashbots/tdx-init" \ + 'go build -trimpath -ldflags "-s -w -buildid=" -o ./build/tdx-init' \ + "build/tdx-init:/usr/bin/tdx-init" + +# Build ssh-pubkey-server +make_git_package \ + "ssh-pubkey-server" \ + "second-key" \ + "https://github.com/flashbots/ssh-pubkey-server" \ + 'go build -trimpath -ldflags "-s -w -buildid= -X github.com/flashbots/go-template/common.Version=v1.0.0" -o ./build/ssh-pubkey-server cmd/httpserver/main.go' \ + "build/ssh-pubkey-server:/usr/bin/ssh-pubkey-server" + +make_git_package \ + "cvm-reverse-proxy" \ + "v0.1.7" \ + "https://github.com/flashbots/cvm-reverse-proxy" \ + "make build-proxy-server" \ + "build/proxy-server:/usr/bin/cvm-reverse-proxy" diff --git a/bob/mkosi.extra/etc/containers/containers.conf b/bob/mkosi.extra/etc/containers/containers.conf new file mode 100644 index 0000000..f4bafe9 --- /dev/null +++ b/bob/mkosi.extra/etc/containers/containers.conf @@ -0,0 +1,14 @@ +[engine] +cgroup_manager = "cgroupfs" +events_logger = "file" +runtime = "runc" +lock_type = "file" +seccomp_profile = "/etc/containers/seccomp.json" + +[storage] +driver = "vfs" +runroot = "/run/containers/storage" +graphroot = "/var/lib/containers/storage" + +[network] +firewall_driver = "iptables" diff --git a/bob/mkosi.extra/etc/containers/seccomp.json b/bob/mkosi.extra/etc/containers/seccomp.json new file mode 100644 index 0000000..c4d9110 --- /dev/null +++ b/bob/mkosi.extra/etc/containers/seccomp.json @@ -0,0 +1,833 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "defaultErrnoRet": 1, + "archMap": [ + { + "architecture": "SCMP_ARCH_X86_64", + "subArchitectures": [ + "SCMP_ARCH_X86", + "SCMP_ARCH_X32" + ] + }, + { + "architecture": "SCMP_ARCH_AARCH64", + "subArchitectures": [ + "SCMP_ARCH_ARM" + ] + }, + { + "architecture": "SCMP_ARCH_MIPS64", + "subArchitectures": [ + "SCMP_ARCH_MIPS", + "SCMP_ARCH_MIPS64N32" + ] + }, + { + "architecture": "SCMP_ARCH_MIPS64N32", + "subArchitectures": [ + "SCMP_ARCH_MIPS", + "SCMP_ARCH_MIPS64" + ] + }, + { + "architecture": "SCMP_ARCH_MIPSEL64", + "subArchitectures": [ + "SCMP_ARCH_MIPSEL", + "SCMP_ARCH_MIPSEL64N32" + ] + }, + { + "architecture": "SCMP_ARCH_MIPSEL64N32", + "subArchitectures": [ + "SCMP_ARCH_MIPSEL", + "SCMP_ARCH_MIPSEL64" + ] + }, + { + "architecture": "SCMP_ARCH_S390X", + "subArchitectures": [ + "SCMP_ARCH_S390" + ] + }, + { + "architecture": "SCMP_ARCH_RISCV64", + "subArchitectures": null + } + ], + "syscalls": [ + { + "names": [ + "accept", + "accept4", + "access", + "adjtimex", + "alarm", + "bind", + "brk", + "cachestat", + "capget", + "capset", + "chdir", + "chmod", + "chown", + "chown32", + "clock_adjtime", + "clock_adjtime64", + "clock_getres", + "clock_getres_time64", + "clock_gettime", + "clock_gettime64", + "clock_nanosleep", + "clock_nanosleep_time64", + "close", + "close_range", + "connect", + "copy_file_range", + "creat", + "dup", + "dup2", + "dup3", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_ctl_old", + "epoll_pwait", + "epoll_pwait2", + "epoll_wait", + "epoll_wait_old", + "eventfd", + "eventfd2", + "execve", + "execveat", + "exit", + "exit_group", + "faccessat", + "faccessat2", + "fadvise64", + "fadvise64_64", + "fallocate", + "fanotify_mark", + "fchdir", + "fchmod", + "fchmodat", + "fchmodat2", + "fchown", + "fchown32", + "fchownat", + "fcntl", + "fcntl64", + "fdatasync", + "fgetxattr", + "flistxattr", + "flock", + "fork", + "fremovexattr", + "fsetxattr", + "fstat", + "fstat64", + "fstatat64", + "fstatfs", + "fstatfs64", + "fsync", + "ftruncate", + "ftruncate64", + "futex", + "futex_requeue", + "futex_time64", + "futex_wait", + "futex_waitv", + "futex_wake", + "futimesat", + "getcpu", + "getcwd", + "getdents", + "getdents64", + "getegid", + "getegid32", + "geteuid", + "geteuid32", + "getgid", + "getgid32", + "getgroups", + "getgroups32", + "getitimer", + "getpeername", + "getpgid", + "getpgrp", + "getpid", + "getppid", + "getpriority", + "getrandom", + "getresgid", + "getresgid32", + "getresuid", + "getresuid32", + "getrlimit", + "get_robust_list", + "getrusage", + "getsid", + "getsockname", + "getsockopt", + "get_thread_area", + "gettid", + "gettimeofday", + "getuid", + "getuid32", + "getxattr", + "inotify_add_watch", + "inotify_init", + "inotify_init1", + "inotify_rm_watch", + "io_cancel", + "ioctl", + "io_destroy", + "io_getevents", + "io_pgetevents", + "io_pgetevents_time64", + "ioprio_get", + "ioprio_set", + "io_setup", + "io_submit", + "ipc", + "kill", + "landlock_add_rule", + "landlock_create_ruleset", + "landlock_restrict_self", + "lchown", + "lchown32", + "lgetxattr", + "link", + "linkat", + "listen", + "listxattr", + "llistxattr", + "_llseek", + "lremovexattr", + "lseek", + "lsetxattr", + "lstat", + "lstat64", + "madvise", + "map_shadow_stack", + "membarrier", + "memfd_create", + "memfd_secret", + "mincore", + "mkdir", + "mkdirat", + "mknod", + "mknodat", + "mlock", + "mlock2", + "mlockall", + "mmap", + "mmap2", + "mprotect", + "mq_getsetattr", + "mq_notify", + "mq_open", + "mq_timedreceive", + "mq_timedreceive_time64", + "mq_timedsend", + "mq_timedsend_time64", + "mq_unlink", + "mremap", + "msgctl", + "msgget", + "msgrcv", + "msgsnd", + "msync", + "munlock", + "munlockall", + "munmap", + "name_to_handle_at", + "nanosleep", + "newfstatat", + "_newselect", + "open", + "openat", + "openat2", + "pause", + "pidfd_open", + "pidfd_send_signal", + "pipe", + "pipe2", + "pkey_alloc", + "pkey_free", + "pkey_mprotect", + "poll", + "ppoll", + "ppoll_time64", + "prctl", + "pread64", + "preadv", + "preadv2", + "prlimit64", + "process_mrelease", + "pselect6", + "pselect6_time64", + "pwrite64", + "pwritev", + "pwritev2", + "read", + "readahead", + "readlink", + "readlinkat", + "readv", + "recv", + "recvfrom", + "recvmmsg", + "recvmmsg_time64", + "recvmsg", + "remap_file_pages", + "removexattr", + "rename", + "renameat", + "renameat2", + "restart_syscall", + "rmdir", + "rseq", + "rt_sigaction", + "rt_sigpending", + "rt_sigprocmask", + "rt_sigqueueinfo", + "rt_sigreturn", + "rt_sigsuspend", + "rt_sigtimedwait", + "rt_sigtimedwait_time64", + "rt_tgsigqueueinfo", + "sched_getaffinity", + "sched_getattr", + "sched_getparam", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_getscheduler", + "sched_rr_get_interval", + "sched_rr_get_interval_time64", + "sched_setaffinity", + "sched_setattr", + "sched_setparam", + "sched_setscheduler", + "sched_yield", + "seccomp", + "select", + "semctl", + "semget", + "semop", + "semtimedop", + "semtimedop_time64", + "send", + "sendfile", + "sendfile64", + "sendmmsg", + "sendmsg", + "sendto", + "setfsgid", + "setfsgid32", + "setfsuid", + "setfsuid32", + "setgid", + "setgid32", + "setgroups", + "setgroups32", + "setitimer", + "setpgid", + "setpriority", + "setregid", + "setregid32", + "setresgid", + "setresgid32", + "setresuid", + "setresuid32", + "setreuid", + "setreuid32", + "setrlimit", + "set_robust_list", + "setsid", + "setsockopt", + "set_thread_area", + "set_tid_address", + "setuid", + "setuid32", + "setxattr", + "shmat", + "shmctl", + "shmdt", + "shmget", + "shutdown", + "sigaltstack", + "signalfd", + "signalfd4", + "sigprocmask", + "sigreturn", + "socketcall", + "socketpair", + "splice", + "stat", + "stat64", + "statfs", + "statfs64", + "statx", + "symlink", + "symlinkat", + "sync", + "sync_file_range", + "syncfs", + "sysinfo", + "tee", + "tgkill", + "time", + "timer_create", + "timer_delete", + "timer_getoverrun", + "timer_gettime", + "timer_gettime64", + "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", + "times", + "tkill", + "truncate", + "truncate64", + "ugetrlimit", + "umask", + "uname", + "unlink", + "unlinkat", + "utime", + "utimensat", + "utimensat_time64", + "utimes", + "vfork", + "vmsplice", + "wait4", + "waitid", + "waitpid", + "write", + "writev" + ], + "action": "SCMP_ACT_ALLOW" + }, + { + "names": [ + "process_vm_readv", + "process_vm_writev", + "ptrace" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "minKernel": "4.8" + } + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 40, + "op": "SCMP_CMP_NE" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 8, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131072, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131080, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 4294967295, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "sync_file_range2", + "swapcontext" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": [ + "ppc64le" + ] + } + }, + { + "names": [ + "arm_fadvise64_64", + "arm_sync_file_range", + "sync_file_range2", + "breakpoint", + "cacheflush", + "set_tls" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": [ + "arm", + "arm64" + ] + } + }, + { + "names": [ + "arch_prctl" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": [ + "amd64", + "x32" + ] + } + }, + { + "names": [ + "modify_ldt" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": [ + "amd64", + "x32", + "x86" + ] + } + }, + { + "names": [ + "s390_pci_mmio_read", + "s390_pci_mmio_write", + "s390_runtime_instr" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": [ + "s390", + "s390x" + ] + } + }, + { + "names": [ + "riscv_flush_icache" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": [ + "riscv64" + ] + } + }, + { + "names": [ + "open_by_handle_at" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_DAC_READ_SEARCH" + ] + } + }, + { + "names": [ + "bpf", + "clone", + "clone3", + "fanotify_init", + "fsconfig", + "fsmount", + "fsopen", + "fspick", + "lookup_dcookie", + "mount", + "mount_setattr", + "move_mount", + "open_tree", + "perf_event_open", + "quotactl", + "quotactl_fd", + "setdomainname", + "sethostname", + "setns", + "syslog", + "umount", + "umount2", + "unshare" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + } + }, + { + "names": [ + "clone" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2114060288, + "op": "SCMP_CMP_MASKED_EQ" + } + ], + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ], + "arches": [ + "s390", + "s390x" + ] + } + }, + { + "names": [ + "clone" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 1, + "value": 2114060288, + "op": "SCMP_CMP_MASKED_EQ" + } + ], + "comment": "s390 parameter ordering for clone is different", + "includes": { + "arches": [ + "s390", + "s390x" + ] + }, + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + } + }, + { + "names": [ + "clone3" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 38, + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + } + }, + { + "names": [ + "reboot" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_BOOT" + ] + } + }, + { + "names": [ + "chroot" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_CHROOT" + ] + } + }, + { + "names": [ + "delete_module", + "init_module", + "finit_module" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_MODULE" + ] + } + }, + { + "names": [ + "acct" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_PACCT" + ] + } + }, + { + "names": [ + "kcmp", + "pidfd_getfd", + "process_madvise", + "process_vm_readv", + "process_vm_writev", + "ptrace" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_PTRACE" + ] + } + }, + { + "names": [ + "iopl", + "ioperm" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_RAWIO" + ] + } + }, + { + "names": [ + "settimeofday", + "stime", + "clock_settime", + "clock_settime64" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_TIME" + ] + } + }, + { + "names": [ + "vhangup" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_TTY_CONFIG" + ] + } + }, + { + "names": [ + "get_mempolicy", + "mbind", + "set_mempolicy", + "set_mempolicy_home_node" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYS_NICE" + ] + } + }, + { + "names": [ + "syslog" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_SYSLOG" + ] + } + }, + { + "names": [ + "bpf" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_BPF" + ] + } + }, + { + "names": [ + "perf_event_open" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": [ + "CAP_PERFMON" + ] + } + } + ] +} \ No newline at end of file diff --git a/bob/mkosi.extra/etc/default/dropbear b/bob/mkosi.extra/etc/default/dropbear new file mode 100644 index 0000000..e350593 --- /dev/null +++ b/bob/mkosi.extra/etc/default/dropbear @@ -0,0 +1,14 @@ +# -s: Disallow password logins by default +# -w: Disallow root logins +# -g: Disable password logins for root +# -m: Don't display the message of the day on login +# -j: Disable local port forwarding +# -k: Disable remote port forwarding +DROPBEAR_EXTRA_ARGS="-s -w -g -m -j -k" +DROPBEAR_RECEIVE_WINDOW=6291456 + +# Bind only to ipv4 +DROPBEAR_PORT=0.0.0.0:22 + +# SFTP configuration +DROPBEAR_SUBSYSTEM="sftp /usr/lib/openssh/sftp-server" diff --git a/bob/mkosi.extra/etc/logrotate.conf b/bob/mkosi.extra/etc/logrotate.conf new file mode 100644 index 0000000..6fdc6bb --- /dev/null +++ b/bob/mkosi.extra/etc/logrotate.conf @@ -0,0 +1,12 @@ +# Rotate logs in /delayed_logs and /searcher_logs daily, +# keeping 5 old copies. Use copytruncate so tail +# doesn't lose its file handles. + +/persistent/searcher_logs/*.log { + rotate 5 + copytruncate + missingok + notifempty + compress + maxsize 2G +} \ No newline at end of file diff --git a/bob/mkosi.extra/etc/sudoers.d/99-searcher b/bob/mkosi.extra/etc/sudoers.d/99-searcher new file mode 100644 index 0000000..a50d484 --- /dev/null +++ b/bob/mkosi.extra/etc/sudoers.d/99-searcher @@ -0,0 +1 @@ +searcher ALL=(root) NOPASSWD: /usr/bin/toggle, /usr/bin/tdx-init set-passphrase diff --git a/bob/mkosi.extra/etc/sysctl.d/sysctl.conf b/bob/mkosi.extra/etc/sysctl.d/sysctl.conf new file mode 100644 index 0000000..bc8c4b3 --- /dev/null +++ b/bob/mkosi.extra/etc/sysctl.d/sysctl.conf @@ -0,0 +1 @@ +vm.max_map_count=2097152 diff --git a/bob/mkosi.extra/etc/systemd/system/cvm-reverse-proxy.service b/bob/mkosi.extra/etc/systemd/system/cvm-reverse-proxy.service new file mode 100644 index 0000000..0209b0d --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/cvm-reverse-proxy.service @@ -0,0 +1,15 @@ +[Unit] +Description=SSH Public Key Server +After=ssh-pubkey-server.service +Requires=ssh-pubkey-server.service + +[Service] +Type=simple +ExecStart=cvm-reverse-proxy --listen-addr=0.0.0.0:8745 \ + --target-addr=http://localhost:5001 \ + --server-attestation-type=auto +Restart=always +RestartSec=5 + +[Install] +WantedBy=minimal.target diff --git a/bob/mkosi.extra/etc/systemd/system/dropbear.service.d/dropbear-prereq.conf b/bob/mkosi.extra/etc/systemd/system/dropbear.service.d/dropbear-prereq.conf new file mode 100644 index 0000000..deb3611 --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/dropbear.service.d/dropbear-prereq.conf @@ -0,0 +1,10 @@ +[Unit] +After=wait-for-key.service searcher-firewall.service +Requires=wait-for-key.service searcher-firewall.service + +[Service] +ExecStartPre=/usr/bin/chown -R searcher:searcher /home/searcher +ExecStartPre=/bin/sh -c 'test -f /etc/dropbear/dropbear_ed25519_host_key || /usr/bin/dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key' + +[Install] +WantedBy=minimal.target diff --git a/bob/mkosi.extra/etc/systemd/system/lighthouse.service b/bob/mkosi.extra/etc/systemd/system/lighthouse.service new file mode 100644 index 0000000..2a0fa31 --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/lighthouse.service @@ -0,0 +1,26 @@ +[Unit] +Description=Lighthouse Consensus Client +After=network.target network-setup.service persistent-mount.service +Requires=network-setup.service persistent-mount.service + +[Service] +Type=exec +User=lighthouse +Group=eth +ExecStartPre=+/usr/bin/lighthouse-init +ExecStart=/usr/bin/lighthouse bn \ + --network mainnet \ + --execution-endpoint http://localhost:8551 \ + --execution-jwt /tmp/jwt.hex \ + --checkpoint-sync-url https://mainnet.checkpoint.sigp.io \ + --disable-deposit-contract-sync \ + --datadir "/persistent/lighthouse" \ + --disable-optimistic-finalized-sync \ + --disable-quic +Restart=on-failure +RestartSec=10 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=minimal.target \ No newline at end of file diff --git a/bob/mkosi.extra/etc/systemd/system/searcher-container.service b/bob/mkosi.extra/etc/systemd/system/searcher-container.service new file mode 100644 index 0000000..eccf0d0 --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/searcher-container.service @@ -0,0 +1,15 @@ +[Unit] +Description=Searcher SSH Container +After=dropbear.service searcher-firewall.service persistent-mount.service +Requires=dropbear.service searcher-firewall.service persistent-mount.service + +[Service] +Type=oneshot +ExecStartPre=/usr/bin/chown -R searcher:searcher /etc/searcher/ssh_hostkey +ExecStart=/usr/bin/init-container.sh +RemainAfterExit=yes +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=minimal.target diff --git a/bob/mkosi.extra/etc/systemd/system/searcher-firewall.service b/bob/mkosi.extra/etc/systemd/system/searcher-firewall.service new file mode 100644 index 0000000..3812c1e --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/searcher-firewall.service @@ -0,0 +1,14 @@ +[Unit] +Description=Searcher Network and Firewall Rules +After=network.target network-setup.service +Requires=network-setup.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/init-firewall.sh +RemainAfterExit=yes +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=minimal.target diff --git a/bob/mkosi.extra/etc/systemd/system/searcher-log-reader.service b/bob/mkosi.extra/etc/systemd/system/searcher-log-reader.service new file mode 100644 index 0000000..57eccfb --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/searcher-log-reader.service @@ -0,0 +1,14 @@ +[Unit] +Description=Searcher Log Reader +After=persistent-mount.service +Requires=persistent-mount.service + +[Service] +Type=simple +ExecStartPre=/bin/mkdir -p /run/delayed_logs +ExecStartPre=-/bin/rm -f /run/delayed_logs/delay.sock +ExecStart=/bin/sh -c "ncat -U --listen --keep-open --delay 120s /run/delayed_logs/delay.sock > /persistent/delayed_logs/output.log" +Restart=always + +[Install] +WantedBy=minimal.target \ No newline at end of file diff --git a/bob/mkosi.extra/etc/systemd/system/searcher-log-writer.service b/bob/mkosi.extra/etc/systemd/system/searcher-log-writer.service new file mode 100644 index 0000000..531dd5c --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/searcher-log-writer.service @@ -0,0 +1,13 @@ +[Unit] +Description=Delayed Searcher Log Writer +After=searcher-log-reader.service +Requires=searcher-log-reader.service + +[Service] +Type=simple +ExecStartPre=/bin/sudo -u searcher touch /persistent/searcher_logs/bob.log +ExecStart=/bin/sh -c "tail -F -n0 /persistent/searcher_logs/bob.log | ncat -U /run/delayed_logs/delay.sock" +Restart=always + +[Install] +WantedBy=minimal.target \ No newline at end of file diff --git a/bob/mkosi.extra/etc/systemd/system/ssh-pubkey-server.service b/bob/mkosi.extra/etc/systemd/system/ssh-pubkey-server.service new file mode 100644 index 0000000..0c4b156 --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/ssh-pubkey-server.service @@ -0,0 +1,13 @@ +[Unit] +Description=SSH Public Key Server +After=searcher-container.service +Requires=searcher-container.service + +[Service] +Type=simple +ExecStart=/usr/bin/ssh-pubkey-server --listen-addr=127.0.0.1:5001 --container-ssh-pubkey-file /etc/searcher/ssh_hostkey/host_key.pub --host-ssh-pubkey-file /etc/dropbear/dropbear_ed25519_host_key.pub +Restart=always +RestartSec=5 + +[Install] +WantedBy=minimal.target diff --git a/bob/mkosi.extra/etc/systemd/system/wait-for-key.service b/bob/mkosi.extra/etc/systemd/system/wait-for-key.service new file mode 100644 index 0000000..5a4c1ea --- /dev/null +++ b/bob/mkosi.extra/etc/systemd/system/wait-for-key.service @@ -0,0 +1,14 @@ +[Unit] +Description=SSH Public Key Server +After=network.target network-setup.service wait-for-key.service +Requires=network-setup.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/tdx-init wait-for-key +RemainAfterExit=yes +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=minimal.target diff --git a/bob/mkosi.extra/usr/bin/init-container.sh b/bob/mkosi.extra/usr/bin/init-container.sh new file mode 100755 index 0000000..f8fcfec --- /dev/null +++ b/bob/mkosi.extra/usr/bin/init-container.sh @@ -0,0 +1,69 @@ +#!/bin/sh +set -eu -o pipefail + +NAME=searcher-container + +# PORT FORWARDS +SEARCHER_SSH_PORT=10022 +ENGINE_API_PORT=8551 +EL_P2P_PORT=30303 +SEARCHER_INPUT_CHANNEL=27017 + +echo "Starting $NAME..." +su -s /bin/sh searcher -c "cd ~ && podman run -d \ + --name $NAME --replace \ + -p ${SEARCHER_SSH_PORT}:22 \ + -p ${ENGINE_API_PORT}:${ENGINE_API_PORT} \ + -p ${EL_P2P_PORT}:${EL_P2P_PORT} \ + -p ${EL_P2P_PORT}:${EL_P2P_PORT}/udp \ + -p ${SEARCHER_INPUT_CHANNEL}:${SEARCHER_INPUT_CHANNEL}/udp \ + -v /persistent/searcher:/persistent:rw \ + -v /etc/searcher/ssh_hostkey:/etc/searcher/ssh_hostkey:rw \ + -v /persistent/searcher_logs:/var/log/searcher:rw \ + -v /tmp/jwt.hex:/secrets/jwt.hex:ro \ + docker.io/library/ubuntu:24.04 \ + /bin/sh -c ' \ + DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server && \ + mkdir -p /run/sshd && \ + mkdir -p /root/.ssh && \ + echo \"ssh-ed25519 $(cat /etc/searcher_key)\" > /root/.ssh/authorized_keys && \ + chmod 700 /root/.ssh && \ + chmod 600 /root/.ssh/authorized_keys && \ + cp /etc/ssh/ssh_host_ed25519_key.pub /etc/searcher/ssh_hostkey/host_key.pub && \ + /usr/sbin/sshd -D -e'" + +# Attempt a quick check that the container is running +for i in 1 2 3 4 5; do + status=$(su -s /bin/sh - searcher -c "podman inspect --format '{{.State.Status}}' $NAME 2>/dev/null || true") + if [ "$status" = "running" ]; then + break + fi + echo "Waiting for $NAME container to reach 'running' state..." + sleep 1 +done + +if [ "$status" != "running" ]; then + echo "ERROR: $NAME container is not running (status: $status)" + exit 1 +fi + +# Retrieve the PID +pid=$(su -s /bin/sh - searcher -c "podman inspect --format '{{.State.Pid}}' $NAME") +if [ -z "$pid" ] || [ "$pid" = "0" ]; then + echo "ERROR: Could not retrieve PID for container $NAME." + exit 1 +fi + +echo "Applying iptables rules in $NAME (PID: $pid) network namespace..." + +# Enter network namespace and apply DROP rules on port 9000 TCP/UDP +nsenter --target "$pid" --net iptables -A OUTPUT -p tcp --dport 9000 -j DROP +nsenter --target "$pid" --net iptables -A OUTPUT -p udp --dport 9000 -j DROP + +# Enter network namespace and apply DROP rule on port 123 UDP +nsenter --target "$pid" --net iptables -A OUTPUT -p udp --dport 123 -j DROP + +# Drop outbound traffic from SEARCHER_INPUT_CHANNEL +nsenter --target "$pid" --net iptables -A OUTPUT -p udp --sport $SEARCHER_INPUT_CHANNEL -j DROP +nsenter --target "$pid" --net iptables -A OUTPUT -p tcp --sport $SEARCHER_INPUT_CHANNEL -j DROP diff --git a/bob/mkosi.extra/usr/bin/init-firewall.sh b/bob/mkosi.extra/usr/bin/init-firewall.sh new file mode 100755 index 0000000..f6c2bb9 --- /dev/null +++ b/bob/mkosi.extra/usr/bin/init-firewall.sh @@ -0,0 +1,222 @@ +#!/bin/sh +set -eu -o pipefail + +echo "Initializing firewall with separate inbound/outbound chains..." + +TITAN_BUILDER_IP="52.207.17.217" + +############################################################################### +# Ports +############################################################################### +SSH_CONTROL_PORT=22 # Inbound: SSH control plane (always on) +SSH_DATA_PORT=10022 # Inbound: SSH data plane (maintenance mode only) +SSH_REGISTER_PORT=8080 # Inbound: SSH registration + +CL_P2P_PORT=9000 # TCP/UDP inbound/outbound: Consensus client P2P (always on) +EL_P2P_PORT=30303 # TCP/UDP outbound: Execution client P2P (maintenance mode only) + +DNS_PORT=53 # Outbound: DNS (maintenance mode only) +HTTP_PORT=80 # Outbound: HTTP (maintenance mode only) +HTTPS_PORT=443 # Outbound: HTTPS (maintenance mode only) + +SEARCHER_INPUT_CHANNEL=27017 # Inbound: Input Only Channel (always on) + +TITAN_STATE_DIFF_PORT_WSS=42203 # Outbound: Titan builder state diff new:42203 (production only) +TITAN_BUNDLE_PORT_HTTPS=1338 # Outbound: Titan builder bundle submission new:1338 (always on) + +CVM_REVERSE_PROXY_PORT=8745 # Inbound: CVM reverse proxy (always on) +NTP_PORT=123 # Outbound: NTP (always on) + +############################################################################### +# Custom Chains +############################################################################### +CHAIN_ALWAYS_ON_IN="ALWAYS_ON_IN" +CHAIN_ALWAYS_ON_OUT="ALWAYS_ON_OUT" + +CHAIN_MODE_SELECTOR_IN="MODE_SELECTOR_IN" +CHAIN_MODE_SELECTOR_OUT="MODE_SELECTOR_OUT" + +CHAIN_MAINTENANCE_IN="MAINTENANCE_IN" +CHAIN_MAINTENANCE_OUT="MAINTENANCE_OUT" + +CHAIN_PRODUCTION_IN="PRODUCTION_IN" +CHAIN_PRODUCTION_OUT="PRODUCTION_OUT" + + +########################################################################### +# (1) Set default policies to DROP +########################################################################### +iptables -P INPUT DROP +iptables -P FORWARD DROP +iptables -P OUTPUT DROP + +########################################################################### +# (2) Flush any existing rules/chains +########################################################################### +iptables -F +iptables -X + +########################################################################### +# (3) Create custom chains +########################################################################### +for CHAIN in \ + $CHAIN_ALWAYS_ON_IN $CHAIN_ALWAYS_ON_OUT \ + $CHAIN_MODE_SELECTOR_IN $CHAIN_MODE_SELECTOR_OUT \ + $CHAIN_MAINTENANCE_IN $CHAIN_MAINTENANCE_OUT \ + $CHAIN_PRODUCTION_IN $CHAIN_PRODUCTION_OUT +do + iptables -N $CHAIN +done + +########################################################################### +# (4) Allow Established/Related connections (Inbound & Outbound) +# OUTPUT (always): SSH (22), CL P2P (9000), CVM reverse-proxy (8745) +# OUTPUT (maintenance): SSH (10022) +########################################################################### +iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + +########################################################################### +# (5) Main Routing: +# INPUT -> ALWAYS_ON_IN -> MODE_SELECTOR_IN +# OUTPUT -> ALWAYS_ON_OUT -> MODE_SELECTOR_OUT +########################################################################### +iptables -A INPUT -j $CHAIN_ALWAYS_ON_IN +iptables -A INPUT -j $CHAIN_MODE_SELECTOR_IN + +iptables -A OUTPUT -j $CHAIN_ALWAYS_ON_OUT +iptables -A OUTPUT -j $CHAIN_MODE_SELECTOR_OUT + +########################################################################### +# (6) ALWAYS_ON_IN: Inbound rules that never turn off +########################################################################### +# SSH control port (22) +iptables -A $CHAIN_ALWAYS_ON_IN -p tcp --dport $SSH_CONTROL_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# Searcher input channel (UDP on 27017) +iptables -A $CHAIN_ALWAYS_ON_IN -p udp --dport $SEARCHER_INPUT_CHANNEL \ + -m conntrack --ctstate NEW -j ACCEPT + +# Consensus (CL) P2P inbound on port 9000 (TCP + UDP) +iptables -A $CHAIN_ALWAYS_ON_IN -p tcp --dport $CL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT +iptables -A $CHAIN_ALWAYS_ON_IN -p udp --dport $CL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# CVM reverse-proxy inbound on port 8745 (TCP) +# Serves server attestation +# Also forwards request to ssh pubkey server on localhost:5001, +# which serves searcher-container openssh server pubkey +iptables -A $CHAIN_ALWAYS_ON_IN -p tcp --dport $CVM_REVERSE_PROXY_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# Return from ALWAYS_ON_IN back to caller (INPUT chain -> MODE_SELECTOR_IN) +iptables -A $CHAIN_ALWAYS_ON_IN -j RETURN + +########################################################################### +# (7) ALWAYS_ON_OUT: Outbound rules that never turn off +########################################################################### +# CL P2P outbound on port 9000 (TCP + UDP) +# Note: This is the lighthouse CL client run on the host, not the searcher's CL +iptables -A $CHAIN_ALWAYS_ON_OUT -p tcp --dport $CL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT +iptables -A $CHAIN_ALWAYS_ON_OUT -p udp --dport $CL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# NTP outbound on port 123 (UDP) +iptables -A $CHAIN_ALWAYS_ON_OUT -p udp --dport $NTP_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# Titan builder bundle endpoints (always on) +# Security note: This is a side channel. +# While the operator will not be able to see the content of the packets, +# they can observe the presence or absence of packets. +iptables -A $CHAIN_ALWAYS_ON_OUT -p tcp -d $TITAN_BUILDER_IP --dport $TITAN_BUNDLE_PORT_HTTPS \ + -m conntrack --ctstate NEW -j ACCEPT + +# Return from ALWAYS_ON_OUT back to caller (OUTPUT chain -> MODE_SELECTOR_OUT) +iptables -A $CHAIN_ALWAYS_ON_OUT -j RETURN + +########################################################################### +# (8) MAINTENANCE_IN: Inbound rules for Maintenance Mode +########################################################################### +# SSH data plane on port 10022 +iptables -A $CHAIN_MAINTENANCE_IN -p tcp --dport $SSH_DATA_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# SSH Registry port (not used in production mode) +iptables -A $CHAIN_MAINTENANCE_IN -p tcp --dport $SSH_REGISTER_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# EL P2P inbound on port 30303 (TCP + UDP) +iptables -A $CHAIN_MAINTENANCE_IN -p tcp --dport $EL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT +iptables -A $CHAIN_MAINTENANCE_IN -p udp --dport $EL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# Return from MAINTENANCE_IN back to caller (INPUT chain -> END) +iptables -A $CHAIN_MAINTENANCE_IN -j RETURN + +########################################################################### +# (9) MAINTENANCE_OUT: Outbound rules for Maintenance Mode +########################################################################### +# DNS (UDP/TCP 53) +# Note: Searchers will only have DNS in maintenance mode! +iptables -A $CHAIN_MAINTENANCE_OUT -p udp --dport $DNS_PORT \ + -m conntrack --ctstate NEW -j ACCEPT +iptables -A $CHAIN_MAINTENANCE_OUT -p tcp --dport $DNS_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# HTTP / HTTPS +iptables -A $CHAIN_MAINTENANCE_OUT -p tcp --dport $HTTP_PORT \ + -m conntrack --ctstate NEW -j ACCEPT +iptables -A $CHAIN_MAINTENANCE_OUT -p tcp --dport $HTTPS_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# EL P2P (30303) outbound only in Maintenance +iptables -A $CHAIN_MAINTENANCE_OUT -p tcp --dport $EL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT +iptables -A $CHAIN_MAINTENANCE_OUT -p udp --dport $EL_P2P_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# SSH Registry port (not used in production mode) +iptables -A $CHAIN_MAINTENANCE_OUT -p tcp --dport $SSH_REGISTER_PORT \ + -m conntrack --ctstate NEW -j ACCEPT + +# Return from MAINTENANCE_OUT back to caller (OUTPUT chain -> END) +iptables -A $CHAIN_MAINTENANCE_OUT -j RETURN + +########################################################################### +# (10) PRODUCTION_IN: Inbound rules for Production Mode +########################################################################### +# Return from PRODUCTION_IN back to caller (INPUT chain -> END) +iptables -A $CHAIN_PRODUCTION_IN -j RETURN + +########################################################################### +# (11) PRODUCTION_OUT: Outbound rules for Production Mode +# IP whitelisted for builder IPs +########################################################################### +# Titan state diff WSS +iptables -A $CHAIN_PRODUCTION_OUT -p tcp -d $TITAN_BUILDER_IP --dport $TITAN_STATE_DIFF_PORT_WSS \ + -m conntrack --ctstate NEW -j ACCEPT + +# Return from PRODUCTION_OUT back to caller (OUTPUT chain -> END) +iptables -A $CHAIN_PRODUCTION_OUT -j RETURN + +########################################################################### +# (12) Start in Maintenance Mode +########################################################################### +iptables -A $CHAIN_MODE_SELECTOR_IN -j $CHAIN_MAINTENANCE_IN +iptables -A $CHAIN_MODE_SELECTOR_OUT -j $CHAIN_MAINTENANCE_OUT + +# Set initial state +echo "maintenance" > /etc/searcher-network.state + +########################################################################### +# (13) Allow loopback traffic +########################################################################### +iptables -A INPUT -i lo -j ACCEPT +iptables -A OUTPUT -o lo -j ACCEPT + +echo "Firewall initialized in Maintenance Mode." diff --git a/bob/mkosi.extra/usr/bin/toggle b/bob/mkosi.extra/usr/bin/toggle new file mode 100755 index 0000000..257d850 --- /dev/null +++ b/bob/mkosi.extra/usr/bin/toggle @@ -0,0 +1,303 @@ +#!/bin/bash +set -eu -o pipefail + +# -e: script exits on any command error. +# -u: exits if any undefined variable is used. +# -o pipefail: catches errors in any part of a pipeline. + +# +# Mode Switching Script +# +# Switch Paths: +# 1) "production" -> "stopped" +# 2) "stopped" -> "maintenance" (two minute delay) +# 3) "maintenance" -> "production" +# +# Terminating NEW connections: Uses MODE_SELECTOR IN/OUT chain jumps to MAINTENANCE_IN/OUT or PRODUCTION_IN/OUT rule sets +# Terminating ESTABLISHED/RELATED connections: Uses conntrack to kill all current connections by port + +# We must be root to change iptable rules +# Check if we're root, if not, fail +if [ "$(id -u)" != "0" ]; then + echo "Error: This script must be run as root" + exit 1 +fi + +############################################################################### +# Configuration +############################################################################### + +# Delay implementationfiles +LOCK_FILE="/etc/searcher-network.lock" +STATE_FILE="/etc/searcher-network.state" +TIMESTAMP_FILE="/etc/searcher-network-last-stop.timestamp" + +# Tools +IPTABLES="/usr/sbin/iptables" +CONNTRACK="/usr/sbin/conntrack" + +# Chains +CHAIN_MODE_SELECTOR_IN="MODE_SELECTOR_IN" +CHAIN_MODE_SELECTOR_OUT="MODE_SELECTOR_OUT" +CHAIN_MAINTENANCE_IN="MAINTENANCE_IN" +CHAIN_MAINTENANCE_OUT="MAINTENANCE_OUT" +CHAIN_PRODUCTION_IN="PRODUCTION_IN" +CHAIN_PRODUCTION_OUT="PRODUCTION_OUT" + +# Maintenance ports +SSH_DATA_PORT=10022 +DNS_PORT=53 +HTTP_PORT=80 +HTTPS_PORT=443 +EL_P2P_PORT=30303 + +# Production ports +FB_STATE_DIFF_PORT=8547 +TITAN_STATE_DIFF_PORT_WSS=42203 + +############################################################################### +# Delay between stopping and starting maintenance +############################################################################### +with_lock() { + ( + flock -x 200 + "$@" + ) 200>"$LOCK_FILE" +} + +get_state() { + if [ ! -f "$STATE_FILE" ]; then + echo "maintenance" # Default state + else + cat "$STATE_FILE" + fi +} + +set_state() { + echo "$1" > "$STATE_FILE" +} + +# SECURITY: this should only be called from disconnect_from_production() +write_timestamp() { + date +%s > "$TIMESTAMP_FILE" +} + +check_delay() { + if [ ! -f "$TIMESTAMP_FILE" ]; then + echo "Error: No previous stop_production timestamp found" + return 1 + fi + + last_stop=$(cat "$TIMESTAMP_FILE") + # SECURITY: time can be manipulated by the host + current_time=$(date +%s) + delay_seconds=120 # 2 minutes + + if [ $((current_time - last_stop)) -lt $delay_seconds ]; then + remaining=$((delay_seconds - (current_time - last_stop))) + echo "Error: Must wait $remaining more seconds before starting maintenance" + return 1 + fi + return 0 +} + +############################################################################### +# Kill established connections via conntrack +############################################################################### +kill_established_connections() { + local old_mode="$1" + if [ "$old_mode" = "production" ]; then + echo "Killing leftover production flows (port $TITAN_STATE_DIFF_PORT_WSS)..." + $CONNTRACK -D -p tcp --dport $TITAN_STATE_DIFF_PORT_WSS 2>/dev/null + + elif [ "$old_mode" = "maintenance" ]; then + echo "Killing leftover maintenance flows:" + echo " - SSH data port $SSH_DATA_PORT" + echo " - DNS $DNS_PORT, HTTP $HTTP_PORT, HTTPS $HTTPS_PORT, EL P2P $EL_P2P_PORT" + # SSH data plane + $CONNTRACK -D -p tcp --dport $SSH_DATA_PORT 2>/dev/null + # DNS + $CONNTRACK -D -p tcp --dport $DNS_PORT 2>/dev/null + $CONNTRACK -D -p udp --dport $DNS_PORT 2>/dev/null + # HTTP/HTTPS + $CONNTRACK -D -p tcp --dport $HTTP_PORT 2>/dev/null + $CONNTRACK -D -p tcp --dport $HTTPS_PORT 2>/dev/null + # EL P2P + $CONNTRACK -D -p tcp --dport $EL_P2P_PORT 2>/dev/null + $CONNTRACK -D -p udp --dport $EL_P2P_PORT 2>/dev/null + fi + # if old_mode="stopped", nothing to kill +} +############################################################################### +# Toggle the iptables chain jumps +############################################################################### +configure_mode_rules() { + local new_mode="$1" + local old_mode="$2" + + # 1) Flush the mode selector so we remove references to the old sub-chain + $IPTABLES -F $CHAIN_MODE_SELECTOR_IN + $IPTABLES -F $CHAIN_MODE_SELECTOR_OUT + + # 2) Kill all established flows from old mode + kill_established_connections "$old_mode" + + # 3) Depending on new_mode, jump to the matching sub-chains + case "$new_mode" in + maintenance) + $IPTABLES -A $CHAIN_MODE_SELECTOR_IN -j $CHAIN_MAINTENANCE_IN + $IPTABLES -A $CHAIN_MODE_SELECTOR_OUT -j $CHAIN_MAINTENANCE_OUT + ;; + production) + $IPTABLES -A $CHAIN_MODE_SELECTOR_IN -j $CHAIN_PRODUCTION_IN + $IPTABLES -A $CHAIN_MODE_SELECTOR_OUT -j $CHAIN_PRODUCTION_OUT + ;; + stopped) + # No jump => default DROP. + ;; + *) + echo "Unknown mode $new_mode in configure_mode_rules" + return 1 + ;; + esac + return 0 +} + +############################################################################### +# Container Namespace Rule Verification +# Check that the searcher-container-netns iptables rules blocking port 9000 are active +############################################################################### +check_searcher_namespace_rules() { + local cont_name="searcher-container" + local user="searcher" + local daemon="/usr/bin/podman" + + # 1) Check container status + local status + status=$(su -s /bin/sh - $user -c "$daemon inspect --format '{{.State.Status}}' $cont_name 2>/dev/null" || true) + if [ "$status" != "running" ]; then + echo "ERROR: '$cont_name' container is not running; cannot verify rules." + return 1 + fi + + # 2) Retrieve the container PID + local pid + pid=$(su -s /bin/sh - $user -c "$daemon inspect --format '{{.State.Pid}}' $cont_name 2>/dev/null" || true) + if [ -z "$pid" ] || [ "$pid" = "0" ]; then + echo "ERROR: Could not retrieve PID for '$cont_name'." + return 1 + fi + + # 3) Check if the DROP rule on port 9000, 27107, and 123 is present in OUTPUT chain + # -C (check) returns 0 if a matching rule is found, 1 otherwise + if nsenter --target "$pid" --net $IPTABLES -C OUTPUT -p tcp --dport 9000 -j DROP 2>/dev/null \ + && nsenter --target "$pid" --net $IPTABLES -C OUTPUT -p udp --dport 9000 -j DROP 2>/dev/null \ + && nsenter --target "$pid" --net $IPTABLES -C OUTPUT -p tcp --sport 27017 -j DROP 2>/dev/null \ + && nsenter --target "$pid" --net $IPTABLES -C OUTPUT -p udp --sport 27017 -j DROP 2>/dev/null \ + && nsenter --target "$pid" --net $IPTABLES -C OUTPUT -p udp --dport 123 -j DROP 2>/dev/null; then + echo "OK: searcher-container firewall rules (DROP tcp/udp port 9000, tcp/udp port 27017, udp port 123) are ACTIVE." + return 0 + else + echo "WARNING: One or more searcher-container firewall rules (DROP ports 9000/27017/123) are NOT found." + return 1 + fi +} + +############################################################################### +# Mode Switching Functions +############################################################################### +move_to_production() { + if [ "$(get_state)" != "maintenance" ]; then + echo "Error: Can only move to production from maintenance state" + return 1 + fi + + # Check the searcher-container-netns iptables rules are active first: + if ! check_searcher_namespace_rules; then + echo "Error: Required searcher-container-netns iptables rules not active!" + return 1 + fi + + echo "Moving to production mode..." + if configure_mode_rules "production" "maintenance"; then + set_state "production" + echo "Successfully switched to production mode" + return 0 + else + echo "Failed to switch to production mode" + return 1 + fi +} + +disconnect_from_production() { + write_timestamp + if [ "$(get_state)" != "production" ]; then + echo "Error: Can only disconnect from production state" + return 1 + fi + echo "Disconnecting from production mode..." + if configure_mode_rules "stopped" "production"; then + set_state "stopped" + echo "Successfully disconnected from production mode" + return 0 + else + echo "Failed to disconnect from production mode" + return 1 + fi +} + +connect_to_maintenance() { + if [ "$(get_state)" != "stopped" ]; then + echo "Error: Can only connect to maintenance from stopped state" + return 1 + fi + if ! check_delay; then + return 1 + fi + echo "Connecting to maintenance mode..." + if configure_mode_rules "maintenance" "stopped"; then + set_state "maintenance" + rm -f "$TIMESTAMP_FILE" # Clear timestamp after successful use + echo "Successfully connected to maintenance mode" + return 0 + else + echo "Failed to connect to maintenance mode" + return 1 + fi +} + +toggle() { + current_state=$(get_state) + case "$current_state" in + "production") + echo "Switching from production to stopped..." + disconnect_from_production + result=$? + ;; + "stopped") + echo "Switching from stopped to maintenance..." + connect_to_maintenance + result=$? + ;; + "maintenance") + echo "Switching from maintenance to production..." + move_to_production + result=$? + ;; + *) + echo "Error: Unknown state: $current_state" + return 1 + ;; + esac + if [ $result -eq 0 ]; then + echo "Successfully switched state" + return 0 + else + echo "Failed to switch state" + return 1 + fi +} + +# Main execution +with_lock toggle +exit $? diff --git a/bob/mkosi.postinst b/bob/mkosi.postinst new file mode 100755 index 0000000..83a3447 --- /dev/null +++ b/bob/mkosi.postinst @@ -0,0 +1,57 @@ +#!/bin/bash +set -euxo pipefail + +# Create users and groups +# NOTE: Due to a limitation in mkosi, all folders are owned by root by default +# https://github.com/systemd/mkosi/issues/3065 +mkosi-chroot useradd -m -d /home/searcher -s /usr/bin/searchersh -u 1000 searcher +mkosi-chroot groupadd -r eth +mkosi-chroot useradd -r -s /bin/false -G eth lighthouse + + +# Set up sudo permissions for searcher +chmod 440 "$BUILDROOT/etc/sudoers.d/99-searcher" + +# Make sure searchersh is in the list of valid shells +echo "/usr/bin/searchersh" >> "$BUILDROOT/etc/shells" + +# Install lighthouse +install -m 755 services/bin/lighthouse-init "$BUILDROOT/usr/bin/" + +mkdir -p "$BUILDROOT/etc/searcher/ssh_hostkey" + +# Remove autogenerated ssh keys +rm -r "$BUILDROOT/etc/dropbear" +mkdir "$BUILDROOT/etc/dropbear" + +# Enable services +mkdir "$BUILDROOT/etc/systemd/system/minimal.target.wants" +for service in \ + network-setup.service \ + azure-complete-provisioning.service \ + openntpd.service \ + logrotate.service \ + searcher-log-reader.service \ + searcher-log-writer.service \ + wait-for-key.service \ + searcher-firewall.service \ + dropbear.service \ + lighthouse.service \ + searcher-container.service \ + ssh-pubkey-server.service \ + cvm-reverse-proxy.service +do + mkosi-chroot systemctl enable "$service" + ln -sf "/etc/systemd/system/$service" "$BUILDROOT/etc/systemd/system/minimal.target.wants/" +done + +# Don't reserve port 22 +mkosi-chroot systemctl disable ssh.service ssh.socket +mkosi-chroot systemctl mask ssh.service ssh.socket + +# Lock the root account +mkosi-chroot passwd -l root + +# Remove execute permissions from su for non-root users +chmod 700 "$BUILDROOT/bin/su" +mkosi-chroot chown root:root /bin/su diff --git a/bob/readme.md b/bob/readme.md new file mode 100644 index 0000000..8b21796 --- /dev/null +++ b/bob/readme.md @@ -0,0 +1,56 @@ +BOB +=== + +This module implements three key features for bottom of block backrunning: + +1. **Network namespaces and firewall rules** that enforce a searcher cannot SSH into the container while state diffs are being streamed in, and the only way information can leave is through the block builder’s endpoints. +2. A **log delay** **script that enforces a two-minute (~10 block) delay until the searcher can view their bot’s logs. +3. **Mode switching** which allows a searcher to toggle between production and maintenance modes, where the SSH connection is cut and restored respectively. + +Together, they provide the “no-frontrunning” guarantee to order flow providers while balancing searcher bot visibility and maintenance. + +Docs: https://flashbots.notion.site/Bob-V2-Image-Guide-1506b4a0d87680b2979de36288b48d9a?pvs=4 + +![image](https://github.com/user-attachments/assets/aaad8a4e-f640-4a94-b16f-657eb3ff6bdb) + +Additional functionality +------------------------ + +This universal, Mkosi-based version implements additional capabilities which allow searchers to persist data across restarts and version upgrades without sacrificing data privacy and integrity. +When the image boots up, it will open an HTTP server at port 8080 and wait for a ed25519 public key to be submitted. For example, run the qemu image then POST the key to the forwarded port like so: + +``` +ubuntu@ns5018742:~/Angela/bobgela/keys$ curl -X POST -d "AAAAC3NzaC1lZDI1NTE5AAAAIMPdKdQZip5rYQAhuKTbhI09HM9aFSU/erbUWXb4i4nR" http://localhost:8080 +``` + +This step is only necessary if the persistent disk has not been initialized. + +Then, using the dropbear port, you can initialize or decrypt an existing disk by running the "initialize" command (rather than toggle, status, etc). This will prompt you for a password via stdin. This step is necessary on each boot. When you initialize as disk, it will store the previously supplied public key in plaintext inside of the LUKS header so it can be retrieved automatically on subsequent boots. + +Service Order +------------- + +1. Initialize network (**name:** `network-setup.service`) +2. Get searcher key from LUKS partition or wait for key on port 8080 (**name:** `wait-for-key.service`) (**after:** `network-setup.service`) +3. Setup firewall (**name:** `searcher-firewall.service`) (**after:** `network-setup.service`) +4. Start dropbear server for `initialize`, `toggle`, etc. (**name:** `dropbear.service`) (**after:** `wait-for-key.service`, `searcher-firewall.service`) +5. Open a log socket and forward text from it to the delayed log file after 120s (**name:** searcher-log-reader.service) (**after:** `/persistent` is mounted) +6. Write new text in `bob.log` to the log socket (**name:** searcher-log-writer.service) (**after:** searcher-log-reader.service) +7. Lighthouse (**name:** `lighthouse.service`) (**after:** `/persistent` is mounted) +8. Start the podman container (**name:** `searcher-container.service`) (**after:** `dropbear.service`, `lighthouse.service`, `searcher-firewall.service`, `/persistent` is mounted) +9. SSH pubkey server (**name:** `ssh-pubkey-server.service`) (**after:** `searcher-container.service`) +10. CVM reverse proxy for SSH pubkey server (**name:** `cvm-reverse-proxy.service`) (**after:** `ssh-pubkey-server.service`) + +Testing +------- + +```shell +ssh-keygen -t ed25519 +curl -X POST -d "$(cut -d" " -f2 /root/.ssh/id_ed25519.pub)" http://localhost:8080 +sleep 1 +# start here if recovering existing persistent disk (assumes searcher key is in /root/.ssh) +ssh -4 -i /root/.ssh/id_ed25519 searcher@127.0.0.1 initialize +journalctl -fu searcher-container +ssh -4 -i /root/.ssh/id_ed25519 -p 10022 root@127.0.0.1 +ssh -4 -i /root/.ssh/id_ed25519 searcher@127.0.0.1 toggle +``` \ No newline at end of file diff --git a/bob/searchersh.c b/bob/searchersh.c new file mode 100644 index 0000000..bce66b2 --- /dev/null +++ b/bob/searchersh.c @@ -0,0 +1,183 @@ +#include // For fprintf, perror +#include // For exit, malloc/free, strdup, atoi +#include // For strcmp, strtok +#include // For execl +#include // For isdigit +#include // For stat + +#define MAX_LINES 10000000 + +// Check if initialization is complete (persistent is mounted) +int is_initialized() { + struct stat st_mount, st_parent; + + if (stat("/persistent", &st_mount) != 0) { + return 0; + } + + if (stat("/persistent/..", &st_parent) != 0) { + return 0; + } + + // Different device IDs mean it's a mount point + return (st_mount.st_dev != st_parent.st_dev); +} + +// argc is the number of command-line arguments +// argv is an array of C-strings (character pointers) +int main(int argc, char *argv[]) { + + // We expect exactly 3 arguments: + // Example: ssh -i ~/.ssh/yocto-searcher -p 8084 searcher@localhost hello 5 + // argv[0] = 'searchersh' + // argv[1] = '-c' + // argv[2] = 'hello 5' + + if (argc != 3) { + fprintf(stderr, "Invalid number of arguments\n"); + return 1; // return error code 1 + } + + // Verify argv[0] is "searchersh" + if (strcmp(argv[0], "searchersh") != 0) { + fprintf(stderr, "Error: This program must be invoked as 'searchersh'\n"); + return 1; + } + + // Verify argv[1] is "-c" + if (strcmp(argv[1], "-c") != 0) { + fprintf(stderr, "Error: Second argument must be '-c'\n"); + return 1; + } + + // Make a copy of argv[2], because strtok will modify the string + // strdup() allocates memory and copies the entire string. + // We must free() this memory later. + char *arg_copy = strdup(argv[2]); + if (!arg_copy) { + perror("strdup failed"); + return 1; // return error code 1 + } + + // Use strtok() to split the string in arg_copy by spaces (" ") + // strtok modifies the string by inserting '\0' to separate tokens. + // 'command' will point to the first token, if it exists. + char *command = strtok(arg_copy, " "); + if (command == NULL) { + // If there's no token at all (e.g., empty or whitespace-only string), + // we print an error and quit. + fprintf(stderr, "No command provided. Valid commands are: toggle, status, logs, initialize\n"); + free(arg_copy); // free the memory + return 1; // return error code 1 + } + + // If the first token (command) is not NULL, we try to get the next token + // 'arg' is needed when the command is "logs " + // e.g., if argv[2] = "logs 3", then: + // command = "logs" + // arg = "3" + char *arg = strtok(NULL, " "); + + // If command == "initialize", run the tdx-init program with set-passphrase command + if (strcmp(command, "initialize") == 0) { + execl("/usr/bin/sudo", "sudo", "/usr/bin/tdx-init", "set-passphrase", NULL); + + perror("execl failed (initialize)"); + free(arg_copy); + return 1; + } + + // Check if system is initialized before allowing other commands + if (!is_initialized()) { + fprintf(stderr, "System not initialized. Please run 'initialize' command first.\n"); + free(arg_copy); + return 1; + } + + // Compare the first token to see which command we want. + // 1) "toggle" + // 2) "status" + // 3) "logs" + // Anything else -> invalid. + + // If command == "toggle", call /usr/bin/toggle via sudo + if (strcmp(command, "toggle") == 0) { + // execl() replaces the current process with the new program + // Arguments to execl: + // 1) path to executable: "/usr/bin/sudo" + // 2) argv[0] for new program: "sudo" + // 3) "-S" accept password from stdin + // 4) "/usr/bin/toggle" (the program we actually want to run via sudo) + // 5) NULL terminator for argument list + // execl("/usr/bin/sudo", "sudo", "-S", "/usr/bin/toggle", NULL); + execl("/usr/bin/sudo", "sudo", "/usr/bin/toggle", NULL); + + // If execl fails, we reach here. perror prints error details. + perror("execl failed (toggle)"); + + // We must free the copied string before exiting + free(arg_copy); + return 1; + } + + // If command == "status", print the contents of /etc/searcher-network.state + else if (strcmp(command, "status") == 0) { + // runs: cat /etc/searcher-network.state + execl("/bin/cat", "cat", "/etc/searcher-network.state", NULL); + + perror("execl failed (status)"); + free(arg_copy); + return 1; + } + + // If command == "tail-the-logs", print the contents of /persistent/delayed_logs/output.log + else if (strcmp(command, "tail-the-logs") == 0) { + execl("/usr/bin/tail", "tail", "-f", "/persistent/delayed_logs/output.log", NULL); + perror("execl failed (tail-the-logs)"); + free(arg_copy); + return 1; + } + + // If command == "logs", we expect a second token representing number of lines + else if (strcmp(command, "logs") == 0) { + // If no second token, user didn't specify how many lines + if (arg == NULL) { + fprintf(stderr, "Usage: logs \n"); + free(arg_copy); + return 1; // return error code 1 + } + + // 1) Check that 'arg' is strictly numeric + for (int i = 0; arg[i] != '\0'; i++) { + if (!isdigit((unsigned char)arg[i])) { + fprintf(stderr, "Invalid line count (non-digit characters detected): %s\n", arg); + free(arg_copy); + return 1; + } + } + + // 2) Convert to int + int lines = atoi(arg); + + // 3) Check the range + if (lines < 1 || lines > MAX_LINES) { + fprintf(stderr, "Number of lines must be between 1 and %d\n", MAX_LINES); + free(arg_copy); + return 1; + } + + // Call tail with the specified number of lines, e.g.: + // tail -n /persistent/delayed_logs/output.log + // If arg = "3", that's tail -n 3 /persistent/delayed_logs/output.log + execl("/usr/bin/tail", "tail", "-n", arg, "/persistent/delayed_logs/output.log", (char *)NULL); + + perror("execl failed (logs)"); + free(arg_copy); + return 1; // return error code 1 + } + + // If we reach here, the command didn't match any of the valid commands + fprintf(stderr, "Invalid command. Valid commands are: toggle, status, logs, initialize\n"); + free(arg_copy); // Clean up allocated memory + return 1; // Return error code 1 +} diff --git a/buildernet.conf b/buildernet.conf index 9df6566..5aba459 100644 --- a/buildernet.conf +++ b/buildernet.conf @@ -1,4 +1,3 @@ -[Config] +[Include] Include=base/base.conf Include=buildernet/buildernet.conf -Include=devtools/devtools.conf \ No newline at end of file diff --git a/buildernet/buildernet.conf b/buildernet/buildernet.conf index 3fb9498..ac7bfca 100644 --- a/buildernet/buildernet.conf +++ b/buildernet/buildernet.conf @@ -1,20 +1,20 @@ +[Build] +Environment=LIGHTHOUSE_BINARY RETH_BINARY RBUILDER_BINARY +WithNetwork=true + [Content] SkeletonTrees=buildernet/mkosi.skeleton PostInstallationScripts=buildernet/mkosi.postinst PostInstallationScripts=buildernet/render-config.sh BuildScripts=buildernet/mkosi.build -WithNetwork=true -Environment=LIGHTHOUSE_BINARY RETH_BINARY RBUILDER_BINARY + Packages=prometheus prometheus-node-exporter prometheus-process-exporter - fluent-bit rclone openntpd + libsnappy1v5 netcat-openbsd - procps - ca-certificates - openssl bubblewrap BuildPackages=libleveldb-dev libsnappy-dev @@ -22,4 +22,4 @@ BuildPackages=libleveldb-dev libzstd-dev libpq-dev libssl-dev - protobuf-compiler \ No newline at end of file + protobuf-compiler diff --git a/buildernet/mkosi.build b/buildernet/mkosi.build index 3e37760..fbddd27 100755 --- a/buildernet/mkosi.build +++ b/buildernet/mkosi.build @@ -5,11 +5,11 @@ source scripts/build_rust_package.sh build_rust_package \ "lighthouse" \ - "v5.3.0" \ + "v7.0.1" \ "https://github.com/sigp/lighthouse.git" \ "$LIGHTHOUSE_BINARY" \ "modern" \ - "-l z -l zstd" + "-l z -l zstd -l snappy" build_rust_package \ "reth" \ diff --git a/buildernet/mkosi.postinst b/buildernet/mkosi.postinst index a49fc78..3804cd9 100755 --- a/buildernet/mkosi.postinst +++ b/buildernet/mkosi.postinst @@ -18,7 +18,7 @@ mkdir -p "$SERVICE_DIR" # Copy systemd service files for buildernet for service in \ - lighthouse # reth reth-sync \ + persistence-setup # reth reth-sync \ # rbuilder-bidding rbuilder do install -m 644 "services/systemd/$service.service" "$SERVICE_DIR/" diff --git a/services/systemd/lighthouse.service b/buildernet/mkosi.skeleton/etc/systemd/system/lighthouse.service similarity index 81% rename from services/systemd/lighthouse.service rename to buildernet/mkosi.skeleton/etc/systemd/system/lighthouse.service index 97b3d76..dd64add 100644 --- a/services/systemd/lighthouse.service +++ b/buildernet/mkosi.skeleton/etc/systemd/system/lighthouse.service @@ -1,14 +1,13 @@ [Unit] Description=Lighthouse Consensus Client -After=network-setup.service persistence-setup.service -Wants=network-setup.service persistence-setup.service -ConditionPathExists=/persistent +After=network.target network-setup.service persistent-mount.service +Requires=network-setup.service persistent-mount.service [Service] Type=exec User=lighthouse Group=eth -ExecStartPre=/usr/bin/lighthouse-init +ExecStartPre=+/usr/bin/lighthouse-init ExecStart=/usr/bin/lighthouse bn \ --eth1 \ --checkpoint-sync-url https://mainnet.checkpoint.sigp.io \ diff --git a/devtools/devtools.conf b/devtools/devtools.conf deleted file mode 100644 index 1c6931d..0000000 --- a/devtools/devtools.conf +++ /dev/null @@ -1,6 +0,0 @@ -[Content] -SkeletonTrees=devtools/systemd:/etc/systemd/system -Packages=socat - iputils-ping - curl - apt \ No newline at end of file diff --git a/flake.lock b/flake.lock index 0782e05..7dcb42b 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1736012469, - "narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=", + "lastModified": 1746904237, + "narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 5967055..c1bd83b 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,6 @@ system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; }; kernel = import ./kernel.nix { inherit pkgs; }; - mkosi = pkgs.mkosi; reprepro = pkgs.stdenv.mkDerivation rec { name = "reprepro-${version}"; version = "4.16.0"; @@ -25,6 +24,13 @@ wrapProgram "$out/bin/reprepro" --prefix PATH : "${pkgs.gnupg}/bin" ''; }; + mkosi = pkgs.mkosi.override { + extraDeps = with pkgs; [ + apt dpkg gnupg debootstrap + squashfsTools dosfstools e2fsprogs mtools mustache-go + cryptsetup util-linux zstd + ] ++ [ reprepro ]; + }; in { packages.${system} = { kernel = kernel; @@ -32,11 +38,7 @@ }; devShells.${system}.default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - apt dpkg gnupg debootstrap - squashfsTools dosfstools e2fsprogs mtools mustache-go - cryptsetup util-linux zstd qemu - ] ++ [ reprepro mkosi ]; + nativeBuildInputs = [ pkgs.qemu mkosi ]; KERNEL_IMAGE = "${kernel}/bzImage"; KERNEL_VERSION = kernel.version; diff --git a/kernel-yocto.config b/kernel-yocto.config index 67deb8c..455b7e4 100644 --- a/kernel-yocto.config +++ b/kernel-yocto.config @@ -993,7 +993,7 @@ CONFIG_INET_UDP_DIAG=y CONFIG_TCP_CONG_CUBIC=y CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_TCP_MD5SIG is not set -CONFIG_IPV6=y +CONFIG_IPV6=n # CONFIG_IPV6_ROUTER_PREF is not set # CONFIG_IPV6_OPTIMISTIC_DAD is not set # CONFIG_INET6_AH is not set @@ -1024,23 +1024,24 @@ CONFIG_BRIDGE_NETFILTER=y # CONFIG_NETFILTER_EGRESS is not set CONFIG_NETFILTER_FAMILY_BRIDGE=y CONFIG_NETFILTER_BPF_LINK=y +CONFIG_NETFILTER_NETLINK=y # CONFIG_NETFILTER_NETLINK_ACCT is not set # CONFIG_NETFILTER_NETLINK_QUEUE is not set -# CONFIG_NETFILTER_NETLINK_LOG is not set +CONFIG_NETFILTER_NETLINK_LOG=y # CONFIG_NETFILTER_NETLINK_OSF is not set CONFIG_NF_CONNTRACK=y # CONFIG_NF_LOG_SYSLOG is not set -# CONFIG_NF_CONNTRACK_MARK is not set +CONFIG_NF_CONNTRACK_MARK=y # CONFIG_NF_CONNTRACK_ZONES is not set # CONFIG_NF_CONNTRACK_PROCFS is not set -# CONFIG_NF_CONNTRACK_EVENTS is not set +CONFIG_NF_CONNTRACK_EVENTS=y # CONFIG_NF_CONNTRACK_TIMEOUT is not set # CONFIG_NF_CONNTRACK_TIMESTAMP is not set # CONFIG_NF_CONNTRACK_LABELS is not set CONFIG_NF_CONNTRACK_OVS=y # CONFIG_NF_CT_PROTO_DCCP is not set -# CONFIG_NF_CT_PROTO_SCTP is not set -# CONFIG_NF_CT_PROTO_UDPLITE is not set +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y # CONFIG_NF_CONNTRACK_AMANDA is not set # CONFIG_NF_CONNTRACK_FTP is not set # CONFIG_NF_CONNTRACK_H323 is not set @@ -1051,13 +1052,31 @@ CONFIG_NF_CONNTRACK_OVS=y # CONFIG_NF_CONNTRACK_SANE is not set # CONFIG_NF_CONNTRACK_SIP is not set # CONFIG_NF_CONNTRACK_TFTP is not set -# CONFIG_NF_CT_NETLINK is not set +CONFIG_NF_CT_NETLINK=y CONFIG_NF_NAT=y CONFIG_NF_NAT_MASQUERADE=y CONFIG_NF_NAT_OVS=y -# CONFIG_NF_TABLES is not set +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_TABLES=y +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_IPV4=y +CONFIG_NF_TABLES_BRIDGE=y +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_TABLES_NETDEV=y CONFIG_NETFILTER_XTABLES=y -# CONFIG_NETFILTER_XTABLES_COMPAT is not set +CONFIG_NETFILTER_XTABLES_COMPAT=y + +CONFIG_NFT_CT=y +CONFIG_NFT_COUNTER=y +CONFIG_NFT_LOG=y +CONFIG_NFT_LIMIT=y +CONFIG_NFT_MASQ=y +CONFIG_NFT_REJECT=y +CONFIG_NFT_REJECT_INET=y +CONFIG_NFT_COMPAT=y +CONFIG_NFT_NAT=y +CONFIG_NFT_REDIR=y +CONFIG_NFT_OBJREF=y # # Xtables combined modules @@ -1073,7 +1092,7 @@ CONFIG_NETFILTER_XT_MARK=y # CONFIG_NETFILTER_XT_TARGET_HMARK is not set # CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set # CONFIG_NETFILTER_XT_TARGET_LED is not set -# CONFIG_NETFILTER_XT_TARGET_LOG is not set +CONFIG_NETFILTER_XT_TARGET_LOG=y # CONFIG_NETFILTER_XT_TARGET_MARK is not set CONFIG_NETFILTER_XT_NAT=y # CONFIG_NETFILTER_XT_TARGET_NETMAP is not set @@ -1114,7 +1133,7 @@ CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y # CONFIG_NETFILTER_XT_MATCH_LIMIT is not set # CONFIG_NETFILTER_XT_MATCH_MAC is not set # CONFIG_NETFILTER_XT_MATCH_MARK is not set -# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_NFACCT is not set # CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_NETFILTER_XT_MATCH_OWNER is not set @@ -1126,7 +1145,7 @@ CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y # CONFIG_NETFILTER_XT_MATCH_RECENT is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set # CONFIG_NETFILTER_XT_MATCH_SOCKET is not set -# CONFIG_NETFILTER_XT_MATCH_STATE is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y # CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set # CONFIG_NETFILTER_XT_MATCH_STRING is not set # CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set @@ -1152,14 +1171,14 @@ CONFIG_IP_NF_IPTABLES=y # CONFIG_IP_NF_MATCH_ECN is not set # CONFIG_IP_NF_MATCH_TTL is not set CONFIG_IP_NF_FILTER=y -# CONFIG_IP_NF_TARGET_REJECT is not set +CONFIG_IP_NF_TARGET_REJECT=y # CONFIG_IP_NF_TARGET_SYNPROXY is not set CONFIG_IP_NF_NAT=y CONFIG_IP_NF_TARGET_MASQUERADE=y -# CONFIG_IP_NF_TARGET_NETMAP is not set -# CONFIG_IP_NF_TARGET_REDIRECT is not set -# CONFIG_IP_NF_MANGLE is not set -# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y # CONFIG_IP_NF_ARPTABLES is not set # end of IP: Netfilter Configuration @@ -1199,7 +1218,7 @@ CONFIG_LLC=y CONFIG_6LOWPAN=m # CONFIG_6LOWPAN_NHC is not set # CONFIG_IEEE802154 is not set -# CONFIG_NET_SCHED is not set +CONFIG_NET_SCHED=y # CONFIG_DCB is not set CONFIG_DNS_RESOLVER=y # CONFIG_BATMAN_ADV is not set diff --git a/mkosi-docs.md b/mkosi-docs.md new file mode 100644 index 0000000..5a86122 --- /dev/null +++ b/mkosi-docs.md @@ -0,0 +1,3241 @@ +% mkosi(1) +% +% + +# NAME + +mkosi — Build Bespoke OS Images + +# SYNOPSIS + +`mkosi [options…] init` + +`mkosi [options…] summary` + +`mkosi [options…] cat-config` + +`mkosi [options…] build [-- command line…]` + +`mkosi [options…] shell [-- command line…]` + +`mkosi [options…] boot [-- nspawn settings…]` + +`mkosi [options…] vm [-- vmm parameters…]` + +`mkosi [options…] ssh [-- command line…]` + +`mkosi [options…] journalctl [-- command line…]` + +`mkosi [options…] coredumpctl [-- command line…]` + +`mkosi [options…] sysupdate [-- sysupdate settings…]` + +`mkosi [options…] sandbox [-- command line…]` + +`mkosi [options…] dependencies [-- options…]` + +`mkosi [options…] clean` + +`mkosi [options…] serve` + +`mkosi [options…] burn ` + +`mkosi [options…] bump` + +`mkosi [options…] genkey` + +`mkosi [options…] documentation [manual]` + +`mkosi [options…] completion [shell]` + +`mkosi [options…] help` + +# DESCRIPTION + +**mkosi** is a tool for easily building customized OS images. It's a fancy wrapper around **dnf**, **apt**, +**pacman** and **zypper** that may generate disk images with a number of bells and whistles. + +## Command Line Verbs + +The following command line verbs are known: + +`init` +: Initialize **mkosi**. This is a one time operation that sets up various + config files required for an optimal experience. Currently this only + initialized a `tmpfiles.d` dropin for the mkosi package cache + directory to make sure old, unused files are cleaned up + automatically. + +`summary` +: Show a human-readable summary of all options used for building the images. + This will parse the command line and configuration files, but only print + what it is configured for and not actually build or run anything. + +`cat-config` +: Output the names and contents of all loaded configuration files. **mkosi** + loads a bunch of files from different locations and this command makes + it easier to figure out what is configured where. + +`build` +: Build the image-based on the settings passed on the command line and in the + configuration files. This command is the default if no verb is specified. + Arguments may be passed to the build scripts, if some are defined. To pass options to the build + scripts, separate them from regular mkosi options with `--`. + +`shell` +: This builds the image if it is not built yet, and then invokes + **systemd-nspawn** to run an interactive shell in the image. This doesn't + require booting the system, it's like a better chroot. An optional command + line may be specified after the `shell` verb, to be invoked in place of the + shell in the container. To pass extra options to nspawn, separate them + from regular options with `--`. + +`boot` +: Similar to `shell`, but instead of spawning a shell, it boots systemd in the + image using **systemd-nspawn**. Extra arguments may be specified after + the `boot` verb, which are passed as the *kernel command line* to the + init system in the image. To pass extra options to nspawn, separate them + from regular options with `--`. + +`vm` +: Similar to `boot`, but uses the configured virtual machine monitor (by + default `qemu`) to boot up the image, i.e. instead of container + virtualization, virtual machine virtualization is used. How extra + command line arguments are interpreted depends on the configured + virtual machine monitor. See `VirtualMachineMonitor=` for more + information. To pass extra options to the configured virtual machine + monitor, separate them from regular options with `--`. + +`ssh` +: When the image is built with the `Ssh=yes` option, this command + connects to a booted virtual machine via SSH. Make sure to run `mkosi ssh` + with the same config as `mkosi build` so that it has + the necessary information available to connect to the running virtual + machine via SSH. Specifically, the SSH private key from the `SshKey=` + setting is used to connect to the virtual machine. Use `mkosi genkey` + to automatically generate a key and certificate that will be picked up + by **mkosi**. Any arguments passed after the `ssh` verb are passed as + arguments to the **ssh** invocation. To pass extra options, separate + them from regular options with `--`.To connect to a container, use + `machinectl login` or `machinectl shell`. + + The `Machine=` option can be used to give the machine a custom + hostname when booting it which can later be used to **ssh** into the image + (e.g. `mkosi --machine=mymachine vm` followed by + `mkosi --machine=mymachine ssh`). + +`journalctl` +: Uses **journalctl** to inspect the journal inside the image. + All arguments specified after the `journalctl` verb and separated by + `--` from the regular options are appended to the **journalctl** + invocation. + +`coredumpctl` +: Uses **coredumpctl** to look for coredumps inside the image. + All arguments specified after the `coredumpctl` verb and separated by + `--` from the regular options are appended to the **coredumpctl** + invocation. + +`sysupdate` +: Invokes **systemd-sysupdate** with the `--transfer-source=` option set + to the output directory and the `--definitions=` option set to the + directory configured with `SysupdateDirectory=`. All arguments + specified after the `sysupdate` verb and separated from the regular + options with `--` are passed directly to **systemd-sysupdate**. + +`sandbox` +: Run arbitrary commands inside of the same sandbox used to execute + other verbs such as `boot`, `shell`, `vm` and more. This means + `/usr` will be replaced by `/usr` from the tools tree if one is used + while everything else will remain in place. If no command is provided, + `$SHELL` will be executed or **bash** if `$SHELL` is not set. To pass + extra options to the given command, separate them from regular options + with `--`. + +`clean` +: Remove build artifacts generated on a previous build. If combined + with `-f`, also removes incremental build cache images and the tools tree. + If `-f` is specified twice, also removes any package cache. + +`serve` +: This builds the image if it is not built yet, and then serves the + output directory (i.e. usually `mkosi.output/`, see below) via a + small embedded HTTP server, listening on port 8081. Combine with + `-f` in order to rebuild the image unconditionally before serving + it. This command is useful for testing network-based acquisition of + OS images, for example via `machinectl pull-raw …` and `machinectl + pull-tar …`. + +`burn ` +: This builds the image if it is not built yet, and then writes it to the + specified block device. The partition contents are written as-is, but the GPT + partition table is corrected to match sector and disk size of the specified + medium. + +`bump` +: Bumps the image version from `mkosi.version` and writes the resulting + version string to `mkosi.version`. This is useful for implementing a + simple versioning scheme: each time this verb is called the version is + bumped in preparation for the subsequent build. Note that + `--auto-bump`/`-B` may be used to automatically bump the version + as part of a build. The new version is only written to + `mkosi.version` if the build succeeds in that case. + + If `mkosi.bump` exists, it is invoked to generate the new version to + be used instead of using mkosi's own logic. + +`genkey` +: Generate a pair of SecureBoot keys for usage with the + `SecureBootKey=`/`--secure-boot-key=` and + `SecureBootCertificate=`/`--secure-boot-certificate=` options. + +`documentation` +: Show **mkosi**'s documentation. If no argument is given, the **mkosi** man page is shown, but the arguments + `mkosi`, `mkosi-initrd`, `initrd`, `mkosi-sandbox`, `sandbox`, `mkosi.news` and `news` are supported and + respectively show the man pages for **mkosi**, **mkosi-initrd**, **mkosi-sandbox** and **mkosi**'s NEWS file. + + By default this verb will try several ways to output the documentation, but a specific option can be + chosen with the `--doc-format` option. Distro packagers are encouraged to add a file `mkosi.1` into the + `mkosi/resources` directory of the Python package, if it is missing, as well as to install it in the + appropriate search path for man pages. The man page can be generated from the markdown file + `mkosi/resources/man/mkosi.1.md` e.g via `pandoc -t man -s -o mkosi.1 mkosi.1.md`. + +`completion` +: Generate shell completion for the shell given as argument and print it to stdout. The arguments `bash`, + `fish`, and `zsh` are understood. + +`dependencies` +: Output the list of packages required by **mkosi** to build and boot + images. + + This list can be piped directly to a package manager to install the + packages. For example, if the host system uses the **dnf** package + manager, the packages could be installed as follows: + + ```sh + mkosi dependencies | xargs -d '\n' dnf install + ``` + + By default, only the dependencies required to build images with + mkosi are shown. Extra tools tree profiles can be enabled to also + output the packages belonging to those profiles. For example, + running `mkosi dependencies -- --profile runtime` will also output + the packages in the runtime profile on top of the regular packages. + See the documentation for `ToolsTreeProfiles=` for a list of + available profiles. + +`help` +: This verb is equivalent to the `--help` switch documented below: it + shows a brief usage explanation. + +## Commandline-only Options + +Those settings cannot be configured in the configuration files. + +`--force`, `-f` +: Replace the output file if it already exists, when building an + image. By default when building an image and an output artifact + already exists **mkosi** will refuse operation. Specify this option + once to delete all build artifacts from a previous run before + re-building the image. If incremental builds are enabled, + specifying this option twice will ensure the intermediary + cache files are removed, too, before the re-build is initiated. If a + package cache is used (also see the **Files** section below), + specifying this option thrice will ensure the package cache is + removed too, before the re-build is initiated. For the `clean` + operation this option has a slightly different effect: by default + the verb will only remove build artifacts from a previous run, when + specified once the incremental cache files and the tools tree are deleted + too, and when specified twice the package cache is also removed. + +`--directory=`, `-C` +: Takes a path to a directory. **mkosi** switches to this directory before + doing anything. Note that the various configuration files are searched + for in this directory, hence using this option is an effective way to + build a project located in a specific directory. Defaults to the current + working directory. If the empty string is specified, all configuration in + the current working directory will be ignored. + +`--debug` +: Enable additional debugging output. + +`--debug-shell` +: When executing a command in the image fails, **mkosi** will start an interactive + shell in the image allowing further debugging. + +`--debug-workspace` +: When specified, the workspace directory will not be deleted and its + location will be logged when **mkosi** exits. + +`--debug-sandbox` +: Run **mkosi-sandbox** with **strace**. + +`--version` +: Show package version. + +`--help`, `-h` +: Show brief usage information. + +`--genkey-common-name=` +: Common name to be used when generating keys via **mkosi**'s `genkey` command. Defaults to `mkosi of %u`, where + `%u` expands to the username of the user invoking **mkosi**. + +`--genkey-valid-days=` +: Number of days that the keys should remain valid when generating keys via **mkosi**'s `genkey` command. + Defaults to two years (730 days). + +`--auto-bump=`, `-B` +: If specified, the version is bumped and if the build succeeds, the + version is written to `mkosi.version` in a fashion equivalent to the + `bump` verb. This is useful for simple, linear version management: + each build in a series will have a version number one higher then + the previous one. + + If `mkosi.bump` exists, it is invoked to generate the new version to + be used instead of using mkosi's own logic. + +`--doc-format` +: The format to show the documentation in. Supports the values `markdown`, + `man`, `pandoc`, `system` and `auto`. In the case of `markdown` the + documentation is shown in the original Markdown format. `man` shows the + documentation in man page format, if it is available. **pandoc** will generate + the man page format on the fly, if **pandoc** is available. `system` will show + the system-wide man page for **mkosi**, which may or may not correspond to the + version you are using, depending on how you installed **mkosi**. `auto`, which is + the default, will try all methods in the order `man`, `pandoc`, `markdown`, + `system`. + +`--json` +: Show the summary output as JSON-SEQ. + +`--wipe-build-dir`, `-w` +: Wipe the build directory if one is configured before building the image. + +`--rerun-build-scripts`, `-R` +: Rerun build scripts. Requires the `Incremental=` option to be + enabled and the image to have been built once already. If `History=` + is enabled, the history from the previous build will be reused and + no new history will be written. + +## Supported output formats + +The following output formats are supported: + +* Raw *GPT* disk image, created using **systemd-repart** (*disk*) +* Plain directory, containing the OS tree (*directory*) +* Tar archive (*tar*) +* CPIO archive (*cpio*) + +The output format may also be set to *none* to have **mkosi** produce no +image at all. This can be useful if you only want to use the image to +produce another output in the build scripts (e.g. build an RPM). + +When a *GPT* disk image is created, repart partition definition files +may be placed in `mkosi.repart/` to configure the generated disk image. + +It is highly recommended to run **mkosi** on a file system that supports reflinks +such as XFS and btrfs and to keep all related directories on the same file +system. This allows **mkosi** to create images very quickly by using reflinks to +perform copying via copy-on-write operations. + +## Configuration Settings + +The following settings can be set through configuration files (the +syntax with `SomeSetting=value`) and on the command line (the syntax +with `--some-setting=value`). For some command line parameters, a +single-letter shortcut is also allowed. In the configuration files, +the setting must be in the appropriate section, so the settings are +grouped by section below. + +Configuration is parsed in the following order: + +* The command line arguments are parsed. +* `mkosi.local.conf` and `mkosi.local/` are parsed if they exists (in that order). + This file and directory should be in `.gitignore` (or equivalent) + and are intended for local configuration. +* Any default paths (depending on the option) are configured if the + corresponding path exists. +* `mkosi.conf` is parsed if it exists in the directory configured with + `--directory=` or the current working directory if `--directory=` is + not used. If the specified directory does not contain a `mkosi.conf` or + `mkosi.tools.conf` and a `mkosi/mkosi.conf` or `mkosi/mkosi.tools.conf` + exists, the configuration will be parsed from the `mkosi/` + subdirectory of the specified directory instead. +* `mkosi.conf.d/` is parsed in the same directory as `mkosi.conf` if it + exists. Each directory and each file with the `.conf` extension in + `mkosi.conf.d/` is parsed. Any directory in `mkosi.conf.d` is parsed + as if it were a regular top level directory. +* If any profiles are configured, their configuration is parsed from the + `mkosi.profiles/` directory. +* Subimages are parsed from the `mkosi.images/` directory if it exists. + +Note that settings configured via the command line always override +settings configured via configuration files. If the same setting is +configured more than once via configuration files, later assignments +override earlier assignments except for settings that take a collection +of values. Also, settings read from `mkosi.local.conf` or `mkosi.local/` will +override settings from configuration files that are parsed later, but not +settings specified on the CLI. + +For settings that take a single value, the empty assignment (`SomeSetting=` or +`--some-setting=`) can be used to override a previous setting and reset to the +default. + +Settings that take a collection of values are merged by appending the +new values to the previously configured values. Assigning the empty +string to such a setting removes all previously assigned values, and +overrides any configured default values as well. The values specified +on the CLI are appended after all the values from configuration files. + +To conditionally include configuration files, the `[Match]` section can +be used. A `[Match]` section consists of individual conditions. +Conditions can use a pipe symbol (`|`) after the equals sign (`…=|…`), +which causes the condition to become a triggering condition. The config +file will be included if the logical AND of all non-triggering +conditions and the logical OR of all triggering conditions is satisfied. +To negate the result of a condition, prefix the argument with an +exclamation mark. If an argument is prefixed with the pipe symbol and an +exclamation mark, the pipe symbol must be passed first, and the +exclamation second. + +Note that `[Match]` conditions compare against the current values of +specific settings, and do not take into account changes made to the +setting in configuration files that have not been parsed yet (settings +specified on the CLI are taken into account). Also note that matching +against a setting and then changing its value afterwards in a different +config file may lead to unexpected results. + +The `[Match]` section of a `mkosi.conf` file in a directory applies to +the entire directory. If the conditions are not satisfied, the entire +directory is skipped. The `[Match]` sections of files in `mkosi.conf.d/` +and `mkosi.local.conf` only apply to the file itself. + +If there are multiple `[Match]` sections in the same configuration file, +each of them has to be satisfied in order for the configuration file to +be included. Specifically, triggering conditions only apply to the +current `[Match]` section and are reset between multiple `[Match]` +sections. As an example, the following will only match if the output +format is one of `disk` or `directory` and the architecture is one of +`x86-64` or `arm64`: + +```ini +[Match] +Format=|disk +Format=|directory + +[Match] +Architecture=|x86-64 +Architecture=|arm64 +``` + +The `[TriggerMatch]` section can be used to indicate triggering match +sections. These are identical to triggering conditions except they apply +to the entire match section instead of just a single condition. As an +example, the following will match if the distribution is `debian` and +the release is `bookworm` or if the distribution is `ubuntu` and the +release is `noble`. + +```ini +[TriggerMatch] +Distribution=debian +Release=bookworm + +[TriggerMatch] +Distribution=ubuntu +Release=noble +``` + +The semantics of conditions in `[TriggerMatch]` sections is the same as +in `[Match]`, i.e. all normal conditions are joined by a logical AND and +all triggering conditions are joined by a logical OR. When mixing +`[Match]` and `[TriggerMatch]` sections, a match is achieved when all +`[Match]` sections match and at least one `[TriggerMatch]` section +matches. The absence of match sections is valued as true. Logically this means: + +``` +(⋀ᵢ Matchᵢ) ∧ (⋁ᵢ TriggerMatchᵢ) +``` + +Command line options that take no argument are shown without `=` in +their long version. In the config files, they should be specified with a +boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, +`false` to disable. + +### [Distribution] Section + +`Distribution=`, `--distribution=`, `-d` +: The distribution to install in the image. Takes one of the following + arguments: `fedora`, `debian`, `kali`, `ubuntu`, `arch`, `opensuse`, + `mageia`, `centos`, `rhel`, `rhel-ubi`, `openmandriva`, `rocky`, `alma`, + `azure` or `custom`. If not specified, defaults to the distribution of + the host or `custom` if the distribution of the host is not a supported + distribution. + +`Release=`, `--release=`, `-r` +: The release of the distribution to install in the image. The precise + syntax of the argument this takes depends on the distribution used, + and is either a numeric string (in case of Fedora Linux, CentOS, …, + e.g. `29`), or a distribution version name (in case of Debian, Kali, + Ubuntu, …, e.g. `artful`). Defaults to a recent version of the chosen + distribution, or the version of the distribution running on the host + if it matches the configured distribution. + +`Architecture=`, `--architecture=` +: The architecture to build the image for. The architectures that are + actually supported depends on the distribution used and whether a + bootable image is requested or not. When building for a foreign + architecture, you'll also need to install and register a user mode + emulator for that architecture. + + One of the following architectures can be specified per image built: + `alpha`, `arc`, `arm`, `arm64`, `ia64`, `loongarch64`, `mips64-le`, + `mips-le`, `parisc`, `ppc`, `ppc64`, `ppc64-le`, `riscv32`, `riscv64`, + `s390`, `s390x`, `tilegx`, `x86`, `x86-64`. + +`Mirror=`, `--mirror=`, `-m` +: The mirror to use for downloading the distribution packages. Expects + a mirror URL as argument. If not provided, the default mirror for the + distribution is used. + + The default mirrors for each distribution are as follows (unless + specified, the same mirror is used for all architectures): + + | | x86-64 | aarch64 | + |----------------|-----------------------------------|--------------------------------| + | `debian` | http://deb.debian.org/debian | | + | `arch` | https://geo.mirror.pkgbuild.com | http://mirror.archlinuxarm.org | + | `opensuse` | http://download.opensuse.org | | + | `kali` | http://http.kali.org/kali | | + | `ubuntu` | http://archive.ubuntu.com | http://ports.ubuntu.com | + | `centos` | https://mirrors.centos.org | | + | `rocky` | https://mirrors.rockylinux.org | | + | `alma` | https://mirrors.almalinux.org | | + | `fedora` | https://mirrors.fedoraproject.org | | + | `rhel-ubi` | https://cdn-ubi.redhat.com | | + | `mageia` | https://www.mageia.org | | + | `openmandriva` | http://mirrors.openmandriva.org | | + | `azure` | https://packages.microsoft.com/ | | + +`LocalMirror=`, `--local-mirror=` +: The mirror will be used as a local, plain and direct mirror instead + of using it as a prefix for the full set of repositories normally supported + by distributions. Useful for fully offline builds with a single repository. + Supported on **deb**-, **rpm**-, and **pacman**-based distributions. Overrides `--mirror=` but only + for the local **mkosi** build, it will not be configured inside the final image, + `--mirror=` (or the default repository) will be configured inside the final + image instead. + +`RepositoryKeyCheck=`, `--repository-key-check=` +: Controls signature/key checks when using repositories, enabled by default. + Useful to disable checks when combined with `--local-mirror=` and using only + a repository from a local filesystem. + +`RepositoryKeyFetch=`, `--repository-key-fetch=` +: Controls whether **mkosi** will fetch distribution GPG keys remotely. Enabled by + default on Ubuntu when not using a tools tree or when using Ubuntu tools trees to build + Arch Linux or RPM-based distributions. Disabled by default on all other distributions. + When disabled, the distribution GPG keys for the target distribution have to be installed + locally on the host system alongside the package manager for that distribution. + + This setting is only implemented for distributions using **dnf**, **pacman** or **zypper** + as their package manager. For other distributions the distribution GPG keys are always looked + up locally regardless of the value of this setting. To make the distribution GPG keys + for distributions available without enabling this setting, the corresponding package + has to be installed on the host. This is usually one of `archlinux-keyring`, + `debian-keyring`, `kali-archive-keyring`, `ubuntu-keyring` or `distribution-gpg-keys` + (for RPM-based distributions). + +`Repositories=`, `--repositories=` +: Enable package repositories that are disabled by default. This can be used to enable the EPEL repos for + CentOS or different components of the Debian/Kali/Ubuntu repositories. + +### [Output] Section + +`Format=`, `--format=`, `-t` +: The image format type to generate. One of `directory` (for generating + an OS image directly in a local directory), `tar` (similar, but a + tarball of the OS image is generated), `cpio` (similar, but a cpio + archive is generated), `disk` (a block device OS image with a GPT + partition table), `uki` (a unified kernel image with the OS image in + the `.initrd` PE section), `esp` (`uki` but wrapped in a disk image + with only an ESP partition), `oci` (a directory compatible with the + OCI image specification), `sysext`, `confext`, `portable`, + `addon` or `none` (the OS image is solely intended as a build + image to produce another artifact). + + If the `disk` output format is used, the disk image is generated using + **systemd-repart**. The repart partition definition files to use can be + configured using the `RepartDirectories=` setting or via + `mkosi.repart/`. When verity partitions are configured using + **systemd-repart**'s `Verity=` setting, **mkosi** will automatically parse the + verity hash partition's roothash from **systemd-repart**'s JSON output and + include it in the kernel command line of every unified kernel image + built by **mkosi**. + + If the `none` output format is used, the outputs from a previous + build are not removed, but clean scripts (see `CleanScripts=`) are + still executed. This allows rerunning a build script + (see `BuildScripts=`) without removing the results of a previous + build. + +`ManifestFormat=`, `--manifest-format=` +: The manifest format type or types to generate. A comma-delimited + list consisting of `json` (the standard JSON output format that + describes the packages installed), `changelog` (a human-readable + text format designed for diffing). By default no manifest is + generated. + +`Output=`, `--output=`, `-o` +: Name to use for the generated output image file or directory. Defaults + to `image` or, if `ImageId=` is specified, it is used as the default + output name, optionally suffixed with the version set with + `ImageVersion=` or if a specific image is built from `mkosi.images`, the + name of the image is preferred over `ImageId`. Note that this option does + not allow configuring the output directory, use `OutputDirectory=` for that. + + Note that this only specifies the output prefix, depending on the + specific output format, compression and image version used, the full + output name might be `image_7.8.raw.xz`. + +`OutputExtension=`, `--output-extension=` +: Use the specified extension for the output file. Defaults to the appropriate + extension based on the output format. Only includes the file extension, not + any compression extension which will be appended to this extension if compression + is enabled. + +`CompressOutput=`, `--compress-output=` +: Configure compression for the resulting image or archive. The argument can be + either a boolean or a compression algorithm (**xz**, **zstd**). **zstd** + compression is used by default, except CentOS and derivatives up to version + 8, which default to **xz**, and OCI images, which default to **gzip**. + Note that when applied to block device image types, + compression means the image cannot be started directly but needs to be + decompressed first. This also means that the `shell`, `boot`, `vm` verbs + are not available when this option is used. Implied for `tar`, `cpio`, `uki`, + `esp`, `oci` and `addon`. + +`CompressLevel=`, `--compress-level=` +: Configure the compression level to use. Takes an integer. The possible + values depend on the compression being used. + +`OutputDirectory=`, `--output-directory=`, `-O` +: Path to a directory where to place all generated artifacts. If this is + not specified and the directory `mkosi.output/` exists in the local + directory, it is automatically used for this purpose. + +`OutputMode=`, `--output-mode=` +: File system access mode used when creating the output image file. Takes an + access mode in octal notation. If not set, uses the current system defaults. + +`ImageVersion=`, `--image-version=` +: Configure the image version. This accepts any string, but it is + recommended to specify a series of dot separated components. The + version may also be configured by reading a `mkosi.version` file (in + which case it may be conveniently managed via the `bump` verb or the + `--auto-bump` option) or by reading stdout if it is executable (see + the **Scripts** section below). When specified the image version is + included in the default output file name, i.e. instead of `image.raw` + the default will be `image_0.1.raw` for version `0.1` of the image, + and similar. The version is also passed via the `$IMAGE_VERSION` to + any build scripts invoked (which may be useful to patch it into + `/usr/lib/os-release` or similar, in particular the `IMAGE_VERSION=` + field of it). + +`ImageId=`, `--image-id=` +: Configure the image identifier. This accepts a freeform string that + shall be used to identify the image with. If set the default output + file will be named after it (possibly suffixed with the version). The + identifier is also passed via the `$IMAGE_ID` to any build scripts + invoked. The image ID is automatically added to `/usr/lib/os-release`. + +`SplitArtifacts=`, `--split-artifacts=` +: The artifact types to split out of the final image. A comma-delimited + list consisting of `uki`, `kernel`, `initrd`, `os-release`, `prcs`, `partitions`, + `roothash` and `tar`. When building a bootable image `kernel` and `initrd` + correspond to their artifact found in the image (or in the UKI), + while `uki` copies out the entire UKI. If `pcrs` is specified, a JSON + file containing the pre-calculated TPM2 digests is written out, according + to the [UKI specification](https://uapi-group.org/specifications/specs/unified_kernel_image/#json-format-for-pcrsig), + which is useful for offline signing. + + When building a disk image and `partitions` is specified, + pass `--split=yes` to **systemd-repart** to have it write out split partition + files for each configured partition. Read the + [man](https://www.freedesktop.org/software/systemd/man/systemd-repart.html#--split=BOOL) + page for more information. This is useful in A/B update scenarios where + an existing disk image shall be augmented with a new version of a + root or `/usr` partition along with its Verity partition and unified + kernel. + + When `tar` is specified, the rootfs is additionally archived as a + tar archive (compressed according to `CompressOutput=`). + + When `roothash` is specified and a dm-verity disk image is built, the dm-verity + roothash is written out as a separate file, which is useful for offline signing. + + By default `uki`, `kernel` and `initrd` are split out. + +`RepartDirectories=`, `--repart-directory=` +: Paths to directories containing **systemd-repart** partition definition + files that are used when **mkosi** invokes **systemd-repart** when building a + disk image. If `mkosi.repart/` exists in the local directory, it will + be used for this purpose as well. Note that **mkosi** invokes repart with + `--root=` set to the root of the image root, so any `CopyFiles=` + source paths in partition definition files will be relative to the + image root directory. + +`SectorSize=`, `--sector-size=` +: Override the default sector size that **systemd-repart** uses when building a disk + image. + +`Overlay=`, `--overlay=` +: When used together with `BaseTrees=`, the output will consist only out of + changes to the specified base trees. Each base tree is attached as a lower + layer in an overlayfs structure, and the output becomes the upper layer, + initially empty. Thus files that are not modified compared to the base trees + will not be present in the final output. + + This option may be used to create [systemd *system extensions* or + *portable services*](https://uapi-group.org/specifications/specs/extension_image). + +`Seed=`, `--seed=` +: Takes a UUID as argument or the special value `random`. + Overrides the seed that **systemd-repart** + uses when building a disk image. This is useful to achieve reproducible + builds, where deterministic UUIDs and other partition metadata should be + derived on each build. If not specified explicitly and the file `mkosi.seed` + exists in the local directory, the UUID to use is read from it. Otherwise, + a random UUID is used. + +`CleanScripts=`, `--clean-script=` +: Takes a comma-separated list of paths to executables that are used as + the clean scripts for this image. See the **Scripts** section for + more information. + +### [Content] Section + +`Packages=`, `--package=`, `-p` +: Install the specified distribution packages (i.e. RPM, deb, …) in the + image. Takes a comma-separated list of package specifications. This + option may be used multiple times in which case the specified package + lists are combined. Use `BuildPackages=` to specify packages that + shall only be installed in an overlay that is mounted when the prepare + scripts are executed with the `build` argument and when the build scripts + are executed. + + The types and syntax of *package specifications* that are allowed + depend on the package installer (e.g. **dnf** for RPM-based distros or + **apt** for deb-based distros), but may include package names, package + names with version and/or architecture, package name globs, package + groups, and virtual provides, including file paths. + + See `PackageDirectories=` for information on how to make local + packages available for installation with `Packages=`. + + **Example**: when using a distro that uses **dnf**, the following configuration + would install the **meson** package (in the latest version), the 32-bit version + of the `libfdisk-devel` package, all available packages that start with the + `git-` prefix, a **systemd** RPM from the local file system, one of the + packages that provides `/usr/bin/ld`, the packages in the *Development Tools* + group, and the package that contains the `mypy` python module. + + ```ini + Packages=meson + libfdisk-devel.i686 + git-* + /usr/bin/ld + @development-tools + python3dist(mypy) + ``` + +`BuildPackages=`, `--build-package=` +: Similar to `Packages=`, but configures packages to install only in an + overlay that is made available on top of the image to the prepare + scripts when executed with the `build` argument and the build scripts. + This option should be used to list packages containing header files, + compilers, build systems, linkers and other build tools the + `mkosi.build` scripts require to operate. Note that packages listed + here will be absent in the final image. + +`VolatilePackages=`, `--volatile-package=` +: Similar to `Packages=`, but packages configured with this setting are + not cached when `Incremental=` is enabled and are installed after + executing any build scripts. + + Specifically, this setting can be used to install packages that change + often or which are built by a build script. + +`PackageDirectories=`, `--package-directory=` +: Specify directories containing extra packages to be made available during + the build. **mkosi** will create a local repository containing all + packages in these directories and make it available when installing packages or + running scripts. If the `mkosi.packages/` directory is found in the local + directory it is also used for this purpose. + +`VolatilePackageDirectories=`, `--volatile-package-directory=` +: Like `PackageDirectories=`, but any changes to the packages in these + directories will not invalidate the cached images if `Incremental=` + is enabled. + + Additionally, build scripts can add more packages to the local + repository by placing the built packages in `$PACKAGEDIR`. The + packages placed in `$PACKAGEDIR` are shared between all image builds + and thus available for installation in all images using + `VolatilePackages=`. + +`WithRecommends=`, `--with-recommends=` +: Configures whether to install recommended or weak dependencies, + depending on how they are named by the used package manager, or not. + By default, recommended packages are not installed. This is only used + for package managers that support the concept, which are currently + **apt**, **dnf** and **zypper**. + +`WithDocs=`, `--with-docs=` +: Include documentation in the image. Enabled by default. When disabled, + if the underlying distribution package manager supports it + documentation is not included in the image. The `$WITH_DOCS` + environment variable passed to the `mkosi.build` scripts is set to `0` + or `1` depending on whether this option is enabled or disabled. + +`BaseTrees=`, `--base-tree=` +: Takes a comma-separated list of paths to use as base trees. When used, + these base trees are each copied into the OS tree and form the base + distribution instead of installing the distribution from scratch. Only + extra packages are installed on top of the ones already installed in + the base trees. Note that for this to work properly, the base image + still needs to contain the package manager metadata by setting + `CleanPackageMetadata=no` (see `CleanPackageMetadata=`). + + Instead of a directory, a tar file or a disk image may be provided. In + this case it is unpacked into the OS tree. This mode of operation + allows setting permissions and file ownership explicitly, in + particular for projects stored in a version control system such as + **git** which retain full file ownership and access mode metadata for + committed files. + +`SkeletonTrees=`, `--skeleton-tree=` +: Takes a comma-separated list of colon-separated path pairs. The first + path of each pair refers to a directory to copy into the OS tree + before invoking the package manager. The second path of each pair + refers to the target directory inside the image. If the second path is + not provided, the directory is copied on top of the root directory of + the image. The second path is always interpreted as an absolute path. + Use this to insert files and directories into the OS tree before the + package manager installs any packages. If the `mkosi.skeleton/` + directory is found in the local directory it is also used for this + purpose with the root directory as target (also see the **Files** + section below). + + Note that skeleton trees are cached and any changes to skeleton trees + after a cached image has been built (when using `Incremental=`) are + only applied when the cached image is rebuilt (by using `-ff` or + running `mkosi -f clean`). + + As with the base tree logic above, instead of a directory, a tar + file may be provided too. `mkosi.skeleton.tar` will be automatically + used if found in the local directory. + + To add extra package manager configuration files such as extra + repositories, use `SandboxTrees=` as **mkosi** invokes the package + managers from outside the image and not inside so any package + manager configuration files provided via `SkeletonTrees=` won't + take effect when **mkosi** invokes a package manager to install + packages. + +`ExtraTrees=`, `--extra-tree=` +: Takes a comma-separated list of colon-separated path pairs. The first + path of each pair refers to a directory to copy from the host into the + image. The second path of each pair refers to the target directory + inside the image. If the second path is not provided, the directory is + copied on top of the root directory of the image. The second path is + always interpreted as an absolute path. Use this to override any + default configuration files shipped with the distribution. If the + `mkosi.extra/` directory is found in the local directory it is also + used for this purpose with the root directory as target (also see the + **Files** section below). + + As with the base tree logic above, instead of a directory, a tar + file may be provided too. `mkosi.extra.tar` will be automatically + used if found in the local directory. + +`RemovePackages=`, `--remove-package=` +: Takes a comma-separated list of package specifications for removal, in + the same format as `Packages=`. The removal will be performed as one + of the last steps. This step is skipped if `CleanPackageMetadata=no` + is used. + +`RemoveFiles=`, `--remove-files=` +: Takes a comma-separated list of globs. Files in the image matching + the globs will be purged at the end. + +`CleanPackageMetadata=`, `--clean-package-metadata=` +: Enable/disable removal of package manager databases and repository + metadata at the end of installation. Can be specified as `true`, + `false`, or `auto` (the default). With `auto`, package manager + databases and repository metadata will be removed if the respective + package manager executable is *not* present at the end of the + installation. + +`SourceDateEpoch=`, `--source-date-epoch=` +: Takes a timestamp in seconds since the UNIX epoch as argument. + File modification times of all files will be clamped to this value. + The variable is also propagated to **systemd-repart** and + scripts executed by **mkosi**. If not set explicitly, `SOURCE_DATE_EPOCH` from + `--environment=` and from the host environment are tried in that order. + This is useful to make builds reproducible. See + [SOURCE_DATE_EPOCH](https://reproducible-builds.org/specs/source-date-epoch/) + for more information. + +`SyncScripts=`, `--sync-script=` +: Takes a comma-separated list of paths to executables that are used as + the sync scripts for this image. See the **Scripts** section for + more information. + +`PrepareScripts=`, `--prepare-script=` +: Takes a comma-separated list of paths to executables that are used as + the prepare scripts for this image. See the **Scripts** section for + more information. + +`BuildScripts=`, `--build-script=` +: Takes a comma-separated list of paths to executables that are used as + the build scripts for this image. See the **Scripts** section for more + information. + +`PostInstallationScripts=`, `--postinst-script=` +: Takes a comma-separated list of paths to executables that are used as + the post-installation scripts for this image. See the **Scripts** section + for more information. + +`FinalizeScripts=`, `--finalize-script=` +: Takes a comma-separated list of paths to executables that are used as + the finalize scripts for this image. See the **Scripts** section for more + information. + +`PostOutputScripts=`, `--postoutput-script=` +: Takes a comma-separated list of paths to executables that are used as + the post output scripts for this image. See the **Scripts** section for more + information. + +`Bootable=`, `--bootable=` +: Takes a boolean or `auto`. Enables or disables generation of a + bootable image. If enabled, **mkosi** will install an EFI bootloader, and + add an ESP partition when the disk image output is used. If the + selected EFI bootloader (see `Bootloader=`) is not installed or no + kernel images can be found, the build will fail. `auto` behaves as if + the option was enabled, but the build won't fail if either no kernel + images or the selected EFI bootloader can't be found. If disabled, no + bootloader will be installed even if found inside the image, no + unified kernel images will be generated and no ESP partition will be + added to the image if the disk output format is used. + +`Bootloader=`, `--bootloader=` +: Takes one of `none`, `systemd-boot`, `uki`, `grub`, + `systemd-boot-signed`, `uki-signed` or `grub-signed`. Defaults to + `systemd-boot`. If set to `none`, no EFI bootloader will be installed + into the image. If set to `systemd-boot`, **systemd-boot** will be + installed and for each installed kernel, a UKI will be generated and + stored in `EFI/Linux` in the ESP. If set to `uki`, a single UKI will + be generated for the latest installed kernel (the one with the highest + version) which is installed to `EFI/BOOT/BOOTX64.EFI` in the ESP. If + set to `grub`, for each installed kernel, a UKI will be generated and + stored in `EFI/Linux` in the ESP. For each generated UKI, a menu entry + is appended to the grub configuration in `grub/grub.cfg` in the ESP + which chainloads into the UKI. A shim grub.cfg is also written to + `EFI//grub.cfg` in the ESP which loads `grub/grub.cfg` + in the ESP for compatibility with signed versions of grub which load + the grub configuration from this location. + + The `signed` variants will only install pre-signed EFI binaries + shipped by the distribution. + + Kernels need to be placed into the root filesystem (for example using + `ExtraTrees=`) under `/usr/lib/modules/$version`, named `vmlinux` or + `vmlinuz`. The `$version` is as produced by Kbuild's `kernelversion` make + target. + + Note: When using `systemd-boot` or `systemd-boot-signed`, `mkosi` expects + the `systemd-boot` EFI binaries to be present in the image. Depending on + your distribution, these may be packaged separately. For example, Debian- + based images will need `systemd-boot-efi`. + +`BiosBootloader=`, `--bios-bootloader=` +: Takes one of `none` or `grub`. Defaults to `none`. If set to `none`, + no BIOS bootloader will be installed. If set to `grub`, grub is + installed as the BIOS boot loader if a bootable image is requested + with the `Bootable=` option. If no repart partition definition files + are configured, **mkosi** will add a grub BIOS boot partition and an EFI + system partition to the default partition definition files. + + Note that this option is not mutually exclusive with `Bootloader=`. It + is possible to have an image that is both bootable on UEFI and BIOS by + configuring both `Bootloader=` and `BiosBootloader=`. + + The grub BIOS boot partition should have UUID + `21686148-6449-6e6f-744e-656564454649` and should be at least 1MB. + + Even if no EFI bootloader is installed, we still need an ESP for BIOS + boot as that's where we store the kernel, initrd and grub modules. + +`ShimBootloader=`, `--shim-bootloader=` +: Takes one of `none`, `unsigned`, or `signed`. Defaults to `none`. If + set to `none`, shim and MokManager will not be installed to the ESP. + If set to `unsigned`, **mkosi** will search for unsigned shim and + MokManager EFI binaries and install them. If `SecureBoot=` is enabled, + **mkosi** will sign the unsigned EFI binaries before installing them. If + set to `signed`, **mkosi** will search for signed EFI binaries and install + those. Even if `SecureBoot=` is enabled, **mkosi** won't sign these + binaries again. + + Note that this option only takes effect when an image that is bootable + on UEFI firmware is requested using other options + (`Bootable=`, `Bootloader=`). + + Note that when this option is enabled, **mkosi** will only install already + signed bootloader binaries, kernel image files and unified kernel + images as self-signed binaries would not be accepted by the signed + version of shim. + +`UnifiedKernelImages=`, `--unified-kernel-images=` +: Specifies whether to use unified kernel images or not when + `Bootloader=` is set to `systemd-boot` or `grub`. Takes a boolean + value or `auto`. Defaults to `auto`. If enabled, unified kernel images + are always used and the build will fail if any components required to + build unified kernel images are missing. If set to `auto`, unified + kernel images will be used if all necessary components are available. + Otherwise Type 1 entries as defined by the Boot Loader Specification + will be used instead. If disabled, Type 1 entries will always be used. + +`UnifiedKernelImageFormat=`, `--unified-kernel-image-format=` +: Takes a filename without any path components to specify the format that + unified kernel images should be installed as. This may include both the + regular specifiers (see **Specifiers**) and special delayed specifiers, that + are expanded during the installation of the files, which are described below. + The default format for this parameter is `&e-&k` with `-&h` being appended + if `roothash=` or `usrhash=` is found on the kernel command line and `+&c` + if `/etc/kernel/tries` is found in the image. + + The following specifiers may be used: + + | Specifier | Value | + |-----------|----------------------------------------------------| + | `&&` | `&` character | + | `&e` | Entry Token | + | `&k` | Kernel version | + | `&h` | `roothash=` or `usrhash=` value of kernel argument | + +`UnifiedKernelImageProfiles=`, `--uki-profile=` +: Build additional UKI profiles. Takes a comma-separated list of paths + to UKI profile config files. This option may be used multiple times in + which case each config gets built into a corresponding UKI profile. + Config files in the `mkosi.uki-profiles/` directory are + automatically picked up. All configured UKI profiles are added as + additional UKI profiles to each UKI built by **mkosi**. + + See the documentation for the `UKIProfile` section for information + on which settings can be configured in UKI profile config files. + +`Initrds=`, `--initrd=` +: Use user-provided initrd(s). Takes a comma-separated list of paths to initrd + files. This option may be used multiple times in which case the initrd lists + are combined. If no initrds are specified and a bootable image is requested, + **mkosi** will look for initrds in a subdirectory `io.mkosi.initrd` of the + artifact directory (see `$ARTIFACTDIR` in the section **ENVIRONMENT + VARIABLES**), if none are found there **mkosi** will automatically build a + default initrd. + +`InitrdProfiles=`, `--initrd-profile=` +: Set the profiles to enable for the default initrd. Takes a + comma-delimited list of profiles. By default, all profiles are + disabled. + + The `lvm` profile enables support for LVM. + The `pkcs11` profile enables support for PKCS#11. + The `plymouth` profile provides a graphical interface at boot (animation and + password prompt). + The `raid` profile enables support for RAID arrays. + +`InitrdPackages=`, `--initrd-package=` +: Extra packages to install into the default initrd. Takes a comma + separated list of package specifications. This option may be used + multiple times in which case the specified package lists are combined. + +`InitrdVolatilePackages=`, `--initrd-volatile-package=` +: Similar to `VolatilePackages=`, except it applies to the default + initrd. + +`Devicetree=`, `--devicetree=` +: When set, specifies a Devicetree blob to be used by the booting system, + instead of the one provided by firmware. **mkosi** will search for the + specified file relative to common paths where Linux distributions install + Devicetree files. It should typically have the format `/.dtb`. + +`Splash=`, `--splash=` +: When set, the boot splash for any unified kernel image built by **mkosi** will + be picked up from the given path inside the image. + +`MicrocodeHost=`, `--microcode-host=` +: When set to true only include microcode for the host's CPU in the image. + +`KernelCommandLine=`, `--kernel-command-line=` +: Use the specified kernel command line when building images. + + If the root or usr partition are created with verity enabled, + `roothash=` or `usrhash=` respectively are automatically added to the + kernel command line and `root=` or `mount.usr=` should not be added. + Otherwise, if the value of this setting contains the literals + `root=PARTUUID` or `mount.usr=PARTUUID`, these are replaced with the + partition UUID of the root or usr partition respectively. For + example, `root=PARTUUID` would be replaced with + `root=PARTUUID=58c7d0b2-d224-4834-a16f-e036322e88f7` where + `58c7d0b2-d224-4834-a16f-e036322e88f7` is the partition UUID of the + root partition. + +`KernelModules=`, `--kernel-modules=` +: Takes a list of glob patterns that specify which kernel modules to include in the image. + Each argument may be prefixed with a dash (`-`), to *exclude* matching modules. + The arguments are evaluated in order, + the last positive or negative matching pattern determines the result. + The modules that were last matched by a positive pattern are included in the image, + as well as their module and firmware dependencies. + + The module paths are taken relative to the `/usr/lib/modules///kernel/` directory, + and the `.ko` suffix and compression suffix are ignored during matching. + The patterns may include just the basename (e.g. `loop`), + which must match the basename of the module, + the relative path (e.g. `block/loop`), + which must match the final components of the module path up to the basename, + or an absolute path (e.g. `/drivers/block/loop`), + which must match the full path to the module. + When suffixed with `/`, the pattern will match all modules underneath that directory. + The patterns may include shell-style globs (`*`, `?`, `[…-…]`). + + If the special value `default` is used, the default kernel modules + defined in the **mkosi-initrd** configuration are included as well. + + If the special value `host` is used, the currently loaded modules on + the host system are included as well. + +`KernelModulesInitrd=`, `--kernel-modules-initrd=` +: Boolean value, enabled (true) by default. If enabled, when building a bootable image, **mkosi** will generate + an extra initrd for each unified kernel image it assembles. This initrd contains only modules for + the specific kernel version, and will be appended to the prebuilt initrd. This allows generating kernel + independent initrds which are augmented with the necessary modules when the UKI is assembled. + +`KernelInitrdModules=`, `--kernel-modules-initrd-include=` +: Like `KernelModules=`, but specifies the kernel modules to include in the initrd. + +`FirmwareFiles=`, `--firmware-files=` +: Takes a list of glob patterns that specify which firmware files to include in the image. + The patterns are interpreted in the same way as in the `KernelModules=` settings, + except that the paths are relative to `/usr/lib/firmware/`. + The compression suffix is ignored and must not be included in the pattern. + + Firmware files that listed by modules that are included in the image are + automatically included. + + Example: `FirmwareFiles=cxgb4/bcm8483.bin` or `FirmwareFiles=bcm8483.*` would both cause + `/usr/lib/firmware/cxgb4/bcm8483.bin.xz` to be included, + even if not listed by a module. + +`Locale=`, `--locale=`, `LocaleMessages=`, `--locale-messages=`, `Keymap=`, `--keymap=`, `Timezone=`, `--timezone=`, `Hostname=`, `--hostname=`, `RootShell=`, `--root-shell=` +: The settings `Locale=`, `--locale=`, `LocaleMessages=`, `--locale-messages=`, + `Keymap=`, `--keymap=`, `Timezone=`, `--timezone=`, `Hostname=`, + `--hostname=`, `RootShell=`, `--root-shell=` correspond to the identically + named systemd-firstboot options. See **systemd-firstboot**(1) + for more information. Additionally, where applicable, the corresponding + systemd credentials for these settings are written to `/usr/lib/credstore`, + so that they apply even if only `/usr` is shipped in the image. + +`RootPassword=`, `--root-password=`, +: Set the system root password. If this option is not used, but a `mkosi.rootpw` file is found in the local + directory, the password is automatically read from it or if the file is executable it is run as a script + and stdout is read instead (see the **Scripts** section below). If the password starts with `hashed:`, it is + treated as an already hashed root password. The root password is also stored in `/usr/lib/credstore` under + the appropriate systemd credential so that it applies even if only `/usr` is shipped in the image. To create + an unlocked account without any password use `hashed:` without a hash. + +`Autologin=`, `--autologin=`, `-a` +: Enable autologin for the `root` user on `/dev/pts/0` (nspawn), + `/dev/tty1` and `/dev/hvc0`. + +`MakeInitrd=`, `--make-initrd=` +: Add `/etc/initrd-release` and `/init` to the image so that it can be + used as an initramfs. + +`Ssh=`, `--ssh=` +: Specifies whether to install an **sshd** socket unit and matching service + in the final image. Takes one of `always`, `never`, `auto` or `runtime`. + Defaults to `auto`. + + If set to `auto` and `sshd` is present in the image and the generator binary + `systemd-ssh-generator` is not present, or if set to `always`, + mkosi will install **sshd** units in the final image that expose SSH over VSock. + If set to `never`, mkosi will not install these units. If the `runtime` value is used, + mkosi will also not install any units but abort starting `mkosi vm` if no + SSH credentials are configured. When building with this + option and running the image using `mkosi vm`, the `mkosi ssh` + command can be used to connect to the container/VM via SSH. Note that + you still have to make sure openssh is installed in the image to make + `mkosi ssh` behave correctly. Run `mkosi genkey` to automatically + generate an X.509 certificate and private key to be used by **mkosi** to + enable SSH access to any virtual machines via `mkosi ssh`. To access + images booted using `mkosi boot`, use **machinectl**. + +`SELinuxRelabel=`, `--selinux-relabel=` +: Specifies whether to relabel files to match the image's SELinux + policy. Takes a boolean value or `auto`. Defaults to `auto`. If + disabled, files will not relabeled. If enabled, an SELinux policy has + to be installed in the image and **setfiles** has to be available to + relabel files. If any errors occur during **setfiles**, the build will + fail. If set to `auto`, files will be relabeled if mkosi is not + building a directory image, an SELinux policy is installed in the + image and if **setfiles** is available. Any errors occurred during + **setfiles** will be ignored. + + Note that when running unprivileged, **setfiles** will fail to set any + labels that are not in the host's SELinux policy. To ensure **setfiles** + succeeds without errors, make sure to run **mkosi** as root or build from + a host system with the same SELinux policy as the image you're + building. + +`MachineId=`, `--machine-id=` + +: Takes a UUID or the special value `random`. Sets the machine ID of the + image to the specified UUID. If set to `random`, a random UUID will be + written to `/etc/machine-id`. If not specified explicitly and the file + `mkosi.machine-id` exists in the local directory, the UUID to use is + read from it. Otherwise, `uninitialized` will be written to `/etc/machine-id`. + +### [Validation] Section + +`SecureBoot=`, `--secure-boot=` +: Sign **systemd-boot** (if it is not signed yet) and any generated + unified kernel images for UEFI SecureBoot. + +`SecureBootAutoEnroll=`, `--secure-boot-auto-enroll=` +: Set up automatic enrollment of the secure boot keys in virtual machines as + documented in **systemd-boot**(7) if `SecureBoot=` is used. + Note that **systemd-boot** will only do automatic secure boot key + enrollment in virtual machines starting from systemd v253. To do auto + enrollment on systemd v252 or on bare metal machines, write a + **systemd-boot** configuration file to `/efi/loader/loader.conf` using an + extra tree with `secure-boot-enroll force` or + `secure-boot-enroll manual` in it. Auto enrollment is not supported on + systemd versions older than v252. Defaults to `yes`. + +`SecureBootKey=`, `--secure-boot-key=` +: Path to the PEM file containing the secret key for signing the + UEFI kernel image if `SecureBoot=` is used and PCR signatures when + `SignExpectedPcr=` is also used. When `SecureBootKeySource=` is specified, + the input type depends on the source. + +`SecureBootCertificate=`, `--secure-boot-certificate=` +: Path to the X.509 file containing the certificate for the signed + UEFI kernel image, if `SecureBoot=` is used. + +`SecureBootSignTool=`, `--secure-boot-sign-tool=` +: Tool to use to sign secure boot PE binaries. Takes one of `systemd-sbsign`, `sbsign` or `auto`. + Defaults to `auto`. If set to `auto`, either **systemd-sbsign** or **sbsign** are used if + available, with **systemd-sbsign** being preferred. + +`Verity=`, `--verity=` +: Whether to enforce or disable verity for extension images. Takes one of + `signed`, `hash`, `defer`, `auto` or a boolean value. If set to `signed`, + a verity key and certificate must be present and the build will fail if + we don't detect any verity partitions in the disk image produced by + **systemd-repart**. If disabled, verity partitions will be excluded + from the extension images produced by **systemd-repart**. If set to + `hash`, **mkosi** configures **systemd-repart** to create a verity hash + partition, but no signature partition. If set to `defer`, space for the verity + sig partition will be allocated but it will not be populated yet. If set to + `auto` and a verity key and certificate are present, **mkosi** will pass them + to **systemd-repart** and expects the generated disk image to contain verity + partitions, but the build won't fail if no verity partitions are found in the + disk image produced by **systemd-repart**. + + Note that explicitly disabling verity signature and/or hash is not yet + implemented for the `disk` output and only works for extension images at the + moment. + +`VerityKey=`, `--verity-key=` +: Path to the PEM file containing the secret key for signing the verity signature, if a verity signature + partition is added with **systemd-repart**. When `VerityKeySource=` is specified, the input type depends on + the source. + +`VerityCertificate=`, `--verity-certificate=` +: Path to the X.509 file containing the certificate for signing the verity signature, if a verity signature + partition is added with **systemd-repart**. + +`SignExpectedPcr=`, `--sign-expected-pcr=` +: Measure the components of the unified kernel image (UKI) using + **systemd-measure** and embed the PCR signature into the unified kernel + image. This option takes a boolean value or the special value `auto`, + which is the default, which is equal to a true value if the + **systemd-measure** binary is in `PATH`. Depends on `SecureBoot=` + being enabled and key from `SecureBootKey=`. + +`SignExpectedPcrKey=`, `--sign-expected-pcr-key=` +: Path to the PEM file containing the secret key for signing the expected PCR signatures. + When `SignExpectedPcrKeySource=` is specified, the input type depends on + the source. + +`SignExpectedPcrCertificate=`, `--sign-expected-pcr-certificate=` +: Path to the X.509 file containing the certificate for signing the expected PCR signatures. + +`SecureBootKeySource=`, `--secure-boot-key-source=`, `VerityKeySource=`, `--verity-key-source=`, `SignExpectedPcrKeySource=`, `--sign-expected-key-source=` +: The source of the corresponding private key, to support OpenSSL engines and providers, + e.g. `--secure-boot-key-source=engine:pkcs11` or `--secure-boot-key-source=provider:pkcs11`. + +`SecureBootCertificateSource=`, `--secure-boot-certificate-source=`, `VerityCertificateSource=`, `--verity-certificate-source=`, `SignExpectedPcrCertificateSource=`, `--sign-expected-certificate-source=` +: The source of the corresponding certificate, to support OpenSSL providers, + e.g. `--secure-boot-certificate-source=provider:pkcs11`. Note that engines are not supported. + +`Passphrase=`, `--passphrase=` +: Specify the path to a file containing the passphrase to use for LUKS + encryption. It should contain the passphrase literally, and not end in + a newline character (i.e. in the same format as **cryptsetup** and + `/etc/crypttab` expect the passphrase files). The file must have an + access mode of 0600 or less. + +`Checksum=`, `--checksum=` +: Generate a `.SHA256SUMS` file of all generated artifacts + after the build is complete. + +`Sign=`, `--sign=` +: Sign the generated `SHA256SUMS` using **gpg** after completion. + +`OpenPGPTool=`, `--openpgp-tool=` +: OpenPGP implementation to use for signing. `gpg` is the default. + Selecting a value different than the default will use the given Stateless + OpenPGP (SOP) tool for signing the `SHA256SUMS` file. + + Exemplary choices are `sqop` and `rsop`, but any implementation from + https://www.openpgp.org/about/sop/ that can be installed locally will work. + +`Key=`, `--key=` +: Select the **gpg** key to use for signing `SHA256SUMS`. This key must + be already present in the **gpg** keyring. + +### [Build] Section + +`ToolsTree=`, `--tools-tree=` +: If specified, programs executed by **mkosi** to build and boot an image + are looked up inside the given tree instead of in the host system. Use + this option to make image builds more reproducible by always using the + same versions of programs to build the final image instead of whatever + version is installed on the host system. If this option is not used, + but the `mkosi.tools/` directory is found in the local directory it is + automatically used for this purpose with the root directory as target. + +: The tools tree directory is kept between repeated image builds unless + cleaned by calling `mkosi clean -f`. + + Note that binaries found in any of the paths configured with + `ExtraSearchPaths=` will be executed with `/usr/` from the tools + tree instead of from the host. If the host distribution or release + does not match the tools tree distribution or release respectively, + this might result in failures when trying to execute binaries from + any of the extra search paths. + + If set to `default`, **mkosi** will automatically add an extra tools tree + image and use it as the tools tree. This image can be further configured + using the settings below or with `mkosi.tools.conf` which can either be a + file or directory containing extra configuration for the default tools tree. + + The following table shows for which distributions default tools tree + packages are defined and which packages are included in those default + tools trees: + + | | Fedora | CentOS | Debian | Kali | Ubuntu | Arch | openSUSE | + |-------------------------|:------:|:------:|:------:|:----:|:------:|:----:|:--------:| + | `acl` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `apt` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | + | `archlinux-keyring` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | + | `attr` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `bash` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `btrfs-progs` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `ca-certificates` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `coreutils` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `cpio` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `createrepo_c` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `curl` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `debian-keyring` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | + | `diffutils` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `distribution-gpg-keys` | ✓ | ✓ | ✓ | ✓ | | ✓ | ✓ | + | `dnf` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `dosfstools` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `e2fsprogs` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `edk2-ovmf` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `erofs-utils` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `findutils` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `git` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `grep` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `grub-tools` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | + | `jq` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `kali-archive-keyring` | | | | ✓ | | | | + | `kmod` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `less` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `mtools` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `nano` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `opensc` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `openssh` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `openssl` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `pkcs11-provider` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `perf` | ✓ | ✓ | ✓ | ✓ | | ✓ | ✓ | + | `sed` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `pacman` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | + | `policycoreutils` | ✓ | ✓ | ✓ | ✓ | ✓ | | ✓ | + | `qemu` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `sbsigntools` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `socat` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `squashfs-tools` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `strace` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `swtpm` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `systemd` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `ukify` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `tar` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `ubuntu-keyring` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | + | `util-linux` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `virtiofsd` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `virt-firmware` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `xfsprogs` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `xz` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `zstd` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + | `zypper` | ✓ | | ✓ | ✓ | ✓ | ✓ | ✓ | + +`ToolsTreeDistribution=`, `--tools-tree-distribution=` +: Set the distribution to use for the default tools tree. Defaults to the host distribution except for + Ubuntu, which defaults to Debian, and RHEL, CentOS, Alma and Rocky, which default to Fedora, or `custom` + if the distribution of the host is not a supported distribution. + +`ToolsTreeRelease=`, `--tools-tree-release=` +: Set the distribution release to use for the default tools tree. By + default, the hardcoded default release in **mkosi** for the distribution + is used. + +`ToolsTreeProfiles=`, `--tools-tree-profile=` +: Set the profiles to enable for the default tools tree. Takes a + comma-delimited list consisting of `devel`, `misc`, + `package-manager` and `runtime`. By default, all profiles except + `devel` are enabled. + + The `devel` profile contains tools required to build (C/C++) + projects. The `misc` profile contains various useful tools that are + handy to have available in scripts. The package manager profile + contains package managers and related tools other than those native + to the tools tree distribution. The `runtime` profile contains the + tools required to boot images in a systemd-nspawn container or in a + virtual machine. + +`ToolsTreeMirror=`, `--tools-tree-mirror=` +: Set the mirror to use for the default tools tree. By default, the + default mirror for the tools tree distribution is used. + +`ToolsTreeRepositories=`, `--tools-tree-repository=` +: Same as `Repositories=` but for the default tools tree. + +`ToolsTreeSandboxTrees=`, `--tools-tree-sandbox-tree=` +: Same as `SandboxTrees=` but for the default tools tree. + +`ToolsTreePackages=`, `--tools-tree-package=` +: Extra packages to install into the default tools tree. Takes a comma + separated list of package specifications. This option may be used + multiple times in which case the specified package lists are combined. + +`ToolsTreePackageDirectories=`, `--tools-tree-package-directory=` +: Same as `PackageDirectories=`, but for the default tools tree. + +`ToolsTreeCertificates=`, `--tools-tree-certificates=` +: Specify whether to use certificates and keys from the tools tree. + Enabled by default. If enabled, `/etc/pki`, `/etc/ssl`, + `/etc/ca-certificates`, and `/var/lib/ca-certificates` from the + tools tree are used. Otherwise, these directories are picked up from + the host. + +`ExtraSearchPaths=`, `--extra-search-path=` +: List of colon-separated paths to look for tools in, before using the + regular `$PATH` search path. + +`Incremental=`, `--incremental=`, `-i` +: Takes either `strict` or a boolean value as its argument. Enables + incremental build mode. In this mode, a copy of the OS image is created + immediately after all OS packages are installed and the prepare scripts + have executed but before the `mkosi.build` scripts are invoked (or + anything that happens after it). On subsequent invocations of **mkosi** + with the `-i` switch this cached image may be used to skip the OS package + installation, thus drastically speeding up repetitive build times. Note + that while there is some rudimentary cache invalidation, it is definitely + not perfect. In order to force a rebuild of the cached image, combine + `-i` with `-ff` to ensure the cached image is first removed and then + re-created. + + If set to `strict`, the build fails if previously built cached image does + not exist. + +`CacheOnly=`, `--cache-only=` +: Takes one of `auto`, `metadata`, `always` or `never`. Defaults to + `auto`. If `always`, the package manager is instructed not to contact + the network. This provides a minimal level of reproducibility, as long + as the package cache is already fully populated. If set to `metadata`, + the package manager can still download packages, but we won't sync the + repository metadata. If set to `auto`, the repository metadata is + synced unless we have a cached image (see `Incremental=`) and packages + can be downloaded during the build. If set to `never`, repository + metadata is always synced and packages can be downloaded during + the build. + +`SandboxTrees=`, `--sandbox-tree=` +: Takes a comma-separated list of colon-separated path pairs. The first + path of each pair refers to a directory to copy into the mkosi + sandbox before executing a tool. If the `mkosi.sandbox/` directory + is found in the local directory it is used for this purpose with the + root directory as target (also see the **Files** section below). + + **mkosi** will look for the package manager configuration and related + files in the configured sandbox trees. Unless specified otherwise, + it will use the configuration files from their canonical locations + in `/usr` or `/etc` in the sandbox trees. For example, it will look + for `/etc/dnf/dnf.conf` in the sandbox trees if **dnf** is used to + install packages. + +`WorkspaceDirectory=`, `--workspace-directory=` +: Path to a directory where to store data required temporarily while + building the image. This directory should have enough space to store + the full OS image, though in most modes the actually used disk space + is smaller. If not specified, a subdirectory of `$XDG_CACHE_HOME` (if + set), `$HOME/.cache` (if set) or `/var/tmp` is used. + + The data in this directory is removed automatically after each + build. It's safe to manually remove the contents of this directory + should an **mkosi** invocation be aborted abnormally (for example, due + to reboot/power failure). + +`CacheDirectory=`, `--cache-directory=` +: Takes a path to a directory to use as the incremental cache directory + for the incremental images produced when the `Incremental=` option is + enabled. If this option is not used, but a `mkosi.cache/` directory is + found in the local directory it is automatically used for this + purpose. + +`CacheKey=`, `--cache-key=` +: Specifies the subdirectory within the cache directory where to store + the cached image. This may include both the regular specifiers (see + **Specifiers**) and special delayed specifiers, that are expanded + after config parsing has finished, instead of during config parsing, + which are described below. The default format for this parameter is + `&d~&r~&a~&I`. + + The following specifiers may be used: + + | Specifier | Value | + |-----------|----------------------------------------------------| + | `&&` | `&` character | + | `&d` | `Distribution=` | + | `&r` | `Release=` | + | `&a` | `Architecture=` | + | `&i` | `ImageId=` | + | `&v` | `ImageVersion=` | + | `&I` | Subimage name within mkosi.images/ or `main` | + + Note that all images within a build must have a unique cache key. + +`PackageCacheDirectory=`, `--package-cache-dir=` +: Takes a path to a directory to use as the package cache directory for the distribution package manager + used. If unset, but a `mkosi.pkgcache/` directory is found in the local directory it is automatically + used for this purpose, otherwise a suitable directory in the user's home directory or system is used. + +`BuildDirectory=`, `--build-directory=` +: Takes a path to a directory to use as the build directory for build + systems that support out-of-tree builds (such as Meson). The directory + used this way is shared between repeated builds, and allows the build + system to reuse artifacts (such as object files, executable, …) + generated on previous invocations. The build scripts can find the path + to this directory in the `$BUILDDIR` environment variable. This + directory is mounted into the image's root directory when + **mkosi-chroot** is invoked during execution of the build scripts. If + this option is not specified, but a directory `mkosi.builddir/` exists + in the local directory it is automatically used for this purpose (also + see the **Files** section below). + +`BuildKey=`, `--build-key=` +: Specifies the subdirectory within the build directory where to store + incremental build artifacts. This may include both the regular + specifiers (see **Specifiers**) and special delayed specifiers, that + are expanded after config parsing has finished, instead of during + config parsing, which are the same delayed specifiers that are + supported by `CacheKey=`. The default format for this parameter is + `&d~&r~&a`. + + To disable usage of a build subdirectory completely, assign a + literal `-` to this setting. + +`UseSubvolumes=`, `--use-subvolumes=` +: Takes a boolean or `auto`. Enables or disables use of btrfs subvolumes for + directory tree outputs. If enabled, **mkosi** will create the root directory as + a btrfs subvolume and use btrfs subvolume snapshots where possible to copy + base or cached trees which is much faster than doing a recursive copy. If + explicitly enabled and `btrfs` is not installed or subvolumes cannot be + created, an error is raised. If `auto`, missing **btrfs** or failures to + create subvolumes are ignored. + +`RepartOffline=`, `--repart-offline=` +: Specifies whether to build disk images using loopback devices. Enabled + by default. When enabled, **systemd-repart** will not use loopback + devices to build disk images. When disabled, **systemd-repart** will + always use loopback devices to build disk images. + + Note that when using `RepartOffline=no`**mkosi** cannot run unprivileged and + the image build has to be done as the root user outside of any + containers and with loopback devices available on the host system. + + There are currently two known scenarios where `RepartOffline=no` has to be + used. The first is when using `Subvolumes=` in a repart partition + definition file, as subvolumes cannot be created without using + loopback devices. The second is when creating a system with SELinux + and an XFS root partition. Because **mkfs.xfs** does not support + populating an XFS filesystem with extended attributes, loopback + devices have to be used to ensure the SELinux extended attributes end + up in the generated XFS filesystem. + +`History=`, `--history=` +: Takes a boolean. If enabled, **mkosi** will write the configuration + provided via the CLI for the latest build to the `.mkosi-private` + subdirectory in the directory from which it was invoked. These + arguments are then reused as long as the image is not rebuilt to + avoid having to specify them over and over again. + + To give an example of why this is useful, if you run + `mkosi -O my-custom-output-dir -f` followed by `mkosi vm`, **mkosi** + will fail saying the image hasn't been built yet. If you run + `mkosi -O my-custom-output-dir --history=yes -f` followed by + `mkosi vm`, it will boot the image built in the previous step as + expected. + +`BuildSources=`, `--build-sources=` +: Takes a comma-separated list of colon-separated path pairs. The first + path of each pair refers to a directory to mount from the host. The + second path of each pair refers to the directory where the source + directory should be mounted when running scripts. Every target path is + prefixed with `/work/src` and all build sources are sorted + lexicographically by their target before mounting, so that top level + paths are mounted first. If not configured explicitly, the current + working directory is mounted to `/work/src`. + +`BuildSourcesEphemeral=`, `--build-sources-ephemeral=` +: Takes a boolean or the special value `buildcache`. Disabled by default. Configures whether changes to + source directories, the working directory and configured using `BuildSources=`, are persisted. If + enabled, all source directories will be reset to their original state every time after running all + scripts of a specific type (except sync scripts). + + 💥💣💥 If set to `buildcache` the overlay is not discarded when running build scripts, but saved to the + build directory, configured via `BuildDirectory=`, and will be reused on subsequent runs. The overlay is + still discarded for all other scripts. This option can be used to implement more advanced caching of + builds, but can lead to unexpected states of the source directory. When using this option, a build + directory must be configured. 💥💣💥 + +`Environment=`, `--environment=` +: Adds variables to the environment that package managers and the + prepare/build/postinstall/finalize scripts are executed with. Takes + a space-separated list of variable assignments or just variable + names. In the latter case, the values of those variables will be + passed through from the environment in which **mkosi** was invoked. + This option may be specified more than once, in which case all + listed variables will be set. If the same variable is set twice, the + later setting overrides the earlier one. + +`EnvironmentFiles=`, `--env-file=` +: Takes a comma-separated list of paths to files that contain environment + variable definitions to be added to the scripting environment. Uses + `mkosi.env` if it is found in the local directory. The variables are + first read from `mkosi.env` if it exists, then from the given list of + files and then from the `Environment=` settings. + +`WithTests=`, `--with-tests=`, `-T` +: If set to false (or when the command-line option is used), the + `$WITH_TESTS` environment variable is set to `0` when the + `mkosi.build` scripts are invoked. This is supposed to be used by the + build scripts to bypass any unit or integration tests that are + normally run during the source build process. Note that this option + has no effect unless the `mkosi.build` build scripts honor it. + +`WithNetwork=`, `--with-network=` +: When true, enables network connectivity while the build scripts + `mkosi.build` are invoked. By default, the build scripts run with + networking turned off. The `$WITH_NETWORK` environment variable is + passed to the `mkosi.build` build scripts indicating whether the + build is done with or without network. + +`ProxyUrl=`, `--proxy-url=` +: Configure a proxy to be used for all outgoing network connections. + Various tools that **mkosi** invokes and for which the proxy can be + configured are configured to use this proxy. **mkosi** also sets various + well-known environment variables to specify the proxy to use for any + programs it invokes that may need internet access. + +`ProxyExclude=`, `--proxy-exclude=` +: Configure hostnames for which requests should not go through the + proxy. Takes a comma-separated list of hostnames. + +`ProxyPeerCertificate=`, `--proxy-peer-certificate=` +: Configure a file containing certificates used to verify the proxy. + Defaults to the system-wide certificate store. + + Currently, setting a proxy peer certificate is only supported when + **dnf** or **dnf5** is used to build the image. + +`ProxyClientCertificate=`, `--proxy-client-certificate=` +: Configure a file containing the certificate used to authenticate the + client with the proxy. + + Currently, setting a proxy client certificate is only supported when + **dnf** or **dnf5** is used to build the image. + +`ProxyClientKey=`, `--proxy-client-key=` +: Configure a file containing the private key used to authenticate the + client with the proxy. Defaults to the proxy client certificate if one + is provided. + + Currently, setting a proxy client key is only supported when **dnf** or + **dnf5** is used to build the image. + +### [Runtime] Section (previously known as the [Host] section) + +`NSpawnSettings=`, `--settings=` +: Specifies a `.nspawn` settings file for **systemd-nspawn** to use in + the `boot` and `shell` verbs, and to place next to the generated + image file. This is useful to configure the **systemd-nspawn** + environment when the image is run. If this setting is not used but + an `mkosi.nspawn` file found in the local directory it is + automatically used for this purpose. + +`VirtualMachineMonitor=`, `--vmm=` +: Configures the virtual machine monitor to use. Takes one of `qemu` or + `vmspawn`. Defaults to `qemu`. + + When set to `qemu`, the image is booted with **qemu**. Most output + formats can be booted in **qemu**. Any arguments specified after the + verb are appended to the **qemu** invocation and are interpreted as + extra **qemu** command line arguments. + + When set to `vmspawn`, **systemd-vmspawn** is used to boot up the image, + `vmspawn` only supports disk and directory type images. Any arguments + specified after the verb are appended to the **systemd-vmspawn** + invocation and are interpreted as extra vmspawn options and extra + kernel command line arguments. + +`Console=`, `--console=` +: Configures how to set up the console of the VM. Takes one of `interactive`, `read-only`, `native`, or + `gui`. Defaults to `interactive`. `interactive` provides an interactive terminal interface to the VM. + `read-only` is similar, but is strictly read-only, i.e. does not accept any input from the user. + `native` also provides a TTY-based interface, but uses **qemu**'s native implementation (which means the **qemu** + monitor is available). `gui` shows the **qemu** graphical UI. + +`CPUs=`, `--cpus=` +: Configures the number of CPU cores to assign to the guest when booting a virtual machine. + Defaults to `2`. + + When set to `0`, the number of CPUs available to the **mkosi** process + will be used. + +`RAM=`, `--ram=` +: Configures the amount of RAM assigned to the guest when booting a virtual machine. Defaults to `2G`. + +`KVM=`, `--kvm=` +: Configures whether KVM acceleration should be used when booting a virtual machine. Takes a + boolean value or `auto`. Defaults to `auto`. + +`VSock=`, `--vsock=` +: Configures whether to provision a vsock when booting a virtual machine. Takes + a boolean value or `auto`. Defaults to `auto`. + +`VSockCID=`, `--vsock-cid=` +: Configures the vsock connection ID to use when booting a virtual machine. + Takes a number in the interval `[3, 0xFFFFFFFF)` or `hash` or `auto`. + Defaults to `auto`. When set to `hash`, the connection ID will be derived + from the full path to the image. When set to `auto`, **mkosi** will try to + find a free connection ID automatically. Otherwise, the provided number will + be used as is. + +`TPM=`, `--tpm=` +: Configure whether to use a virtual TPM when booting a virtual machine. + Takes a boolean value or `auto`. Defaults to `auto`. + +`CDROM=`, `--cdrom=` +: Configures whether to attach the image as a CD-ROM device when booting a + virtual machine. Takes a boolean. Defaults to `no`. + +`Removable=`, `--removable=` +: Configures whether to attach the image as a removable device when booting + a virtual machine. Takes a boolean. Defaults to `no`. + +`Firmware=`, `--firmware=` +: Configures the virtual machine firmware to use. Takes one of `uefi`, + `uefi-secure-boot`, `bios`, `linux`, `linux-noinitrd` or `auto`. + Defaults to `auto`. When set to `uefi`, the OVMF firmware without + secure boot support is used. When set to `uefi-secure-boot`, the + OVMF firmware with secure boot support is used. When set to `bios`, + the default SeaBIOS firmware is used. When set to `linux`, direct + kernel boot is used. See the `Linux=` option for more details on + which kernel image is used with direct kernel boot. + `linux-noinitrd` is identical to `linux` except that no initrd is + used. When set to `auto`, `uefi-secure-boot` is used if possible and + `linux` otherwise. + +`FirmwareVariables=`, `--firmware-variables=` +: Configures the path to the the virtual machine firmware variables file + to use. Currently, this option is only taken into account when the `uefi` + or `uefi-secure-boot` firmware is used. If not specified, **mkosi** will search + for the default variables file and use that instead. + + When set to `microsoft`, a firmware variables file with the Microsoft + secure boot certificates already enrolled will be used. + + When set to `microsoft-mok`, a firmware variables file with the + Microsoft secure boot certificates already enrolled will be extended + with a `MokList` variable containing the secure boot certificate + from `SecureBootCertificate=`. This is intended to be used together + with shim binaries signed by the distribution and locally signed EFI + binaries. + + When set to `custom`, the secure boot certificate from + `SecureBootCertificate=` will be enrolled into the default firmware + variables file. + + `virt-fw-vars` from the + [virt-firmware](https://gitlab.com/kraxel/virt-firmware) project can + be used to customize OVMF variable files. + +`Linux=`, `--linux=` +: Set the kernel image to use for **qemu** direct kernel boot. If not + specified, **mkosi** will use the kernel provided via the command line + (`-kernel` option) or the latest kernel that was installed into + the image (or fail if no kernel was installed into the image). + + Note that when the `cpio` output format is used, direct kernel boot is + used regardless of the configured firmware. Depending on the + configured firmware, **qemu** might boot the kernel itself or using the + configured firmware. + + This setting may include both the regular specifiers (see + **Specifiers**) and special delayed specifiers, that are expanded + after config parsing has finished, instead of during config parsing, + which are described below. + + The following specifiers may be used: + + | Specifier | Value | + |-----------|----------------------------------------------------| + | `&&` | `&` character | + | `&b` | The final build directory (including subdirectory) | + +`Drives=`, `--drive=` +: Add a drive. Takes a colon-delimited string of format + `:[:[:[:[:]]]]`. `id` specifies + the ID assigned to the drive. This can be used as the `drive=` + property in various **qemu** devices. `size` specifies the size of the + drive. This takes a size in bytes. Additionally, the suffixes `K`, `M` + and `G` can be used to specify a size in kilobytes, megabytes and + gigabytes respectively. `directory` optionally specifies the directory + in which to create the file backing the drive. If unset, the file will be created under `/var/tmp`. + `options` optionally specifies extra comma-delimited properties which are passed verbatim + to **qemu**'s `-blockdev` option. `file-id` specifies the ID of the file + backing the drive. If unset, this defaults to the drive ID. + Drives with the same file ID will share the backing file. + The directory and size of the file will be determined from the first drive with a given file ID. + `flags` takes a comma-separated list of drive flags which currently only supports `persist`. + `persist` determines whether the drive will be persisted across **qemu** invocations. + The files backing the drives will be created with the schema + `//mkosi-drive--`. + You can skip values by setting them to the empty string, specifying e.g. `myfs:1G::::persist` + will create a persistent drive under `/var/tmp/mkosi-drive-main-myfs`. + + **Example usage:** + + ```ini + [Runtime] + Drives=btrfs:10G + ext4:20G + QemuArgs=-device nvme,serial=btrfs,drive=btrfs + -device nvme,serial=ext4,drive=ext4 + ``` + +`QemuArgs=` +: Space-delimited list of additional arguments to pass when invoking + **qemu**. + +`Ephemeral=`, `--ephemeral=` +: When used with the `shell`, `boot`, or `vm` verbs, this option runs the specified verb on a temporary + snapshot of the output image that is removed immediately when the container terminates. Taking the + temporary snapshot is more efficient on file systems that support reflinks natively (**btrfs** or **xfs**) + than on more traditional file systems that do not (ext4). + +`Credentials=`, `--credential=` +: Set credentials to be passed to **systemd-nspawn** or the virtual machine respectively + when `mkosi shell/boot` or `mkosi vm` are used. This option takes a + space separated list of values which can be either key=value pairs or + paths. If a path is provided, if it is a file, the credential name + will be the name of the file. If the file is executable, the + credential value will be the output of executing the file. Otherwise, + the credential value will be the contents of the file. If the path is + a directory, the same logic applies to each file in the directory. + + Note that values will only be treated as paths if they do not contain + the delimiter (`=`). + +`KernelCommandLineExtra=`, `--kernel-command-line-extra=` +: Set extra kernel command line entries that are appended to the kernel command + line at runtime when booting the image. When booting in a container, these are + passed as extra arguments to systemd. When booting in a VM, these are appended + to the kernel command line via the SMBIOS io.systemd.stub.kernel-cmdline-extra + OEM string. This will only be picked up by **systemd-boot** and **systemd-stub** versions + newer than or equal to v254. + +`RuntimeTrees=`, `--runtime-tree=` +: Takes a colon-separated pair of paths. The first path refers to a + directory to mount into any machine (container or VM) started by + mkosi. The second path refers to the target directory inside the + machine. If the second path is not provided, the directory is mounted + at `/root/src` in the machine. If the second path is relative, it + is interpreted relative to `/root/src` in the machine. + + For each mounted directory, the uid and gid of the user running mkosi + are mapped to the root user in the machine. This means that all the + files and directories will appear as if they're owned by root in the + machine, and all new files and directories created by root in the + machine in these directories will be owned by the user running mkosi + on the host. + + Note that when using `mkosi vm` with this feature systemd v254 or + newer has to be installed in the image. + +`RuntimeSize=`, `--runtime-size=` +: If specified, disk images are grown to the specified size when + they're booted with `mkosi boot` or `mkosi vm`. Takes a size in + bytes. Additionally, the suffixes `K`, `M` and `G` can be used to + specify a size in kilobytes, megabytes and gigabytes respectively. + +`RuntimeScratch=`, `--runtime-scratch=` +: Takes a boolean value or `auto`. Specifies whether to mount extra + scratch space to `/var/tmp`. If enabled, practically unlimited scratch + space is made available under `/var/tmp` when booting the image with + `mkosi vm`, `mkosi boot` or `mkosi shell`. + + Note that using this feature with `mkosi vm` requires systemd v254 + or newer in the guest. + +`RuntimeNetwork=`, `--runtime-network=` +: Takes one of `user`, `interface` or `none`. Defaults to `user`. + Specifies the networking to set up when booting the image. `user` sets + up usermode networking. `interface` sets up a virtual network + connection between the host and the image. This translates to a veth + interface for `mkosi shell` and `mkosi boot` and a tap interface for + `mkosi vm` and `mkosi vmspawn`. + + Note that when using `interface`, **mkosi** does not automatically + configure the host interface. It is expected that a recent version of + **systemd-networkd** is running on the host which will automatically + configure the host interface of the link. + +`RuntimeBuildSources=`, `--runtime-build-sources=` +: Mount the build sources configured with `BuildSources=` and the build + directory (if one is configured) to the same locations in `/work` that + they were mounted to when running the build script when using `mkosi + boot` or `mkosi vm`. + +`RuntimeHome=`, `--runtime-home=` +: Mount the current home directory from which **mkosi** is running to + `/root` when using `mkosi boot` or `mkosi vm`. + +`UnitProperties=`, `--unit-property=` +: Configure systemd unit properties to add to the systemd scopes + allocated when using `mkosi boot` or `mkosi vm`. These are passed + directly to the `--property=` options of **systemd-nspawn** and + **systemd-run** respectively. + +`SshKey=`, `--ssh-key=` +: Path to the X.509 private key in PEM format to use to connect to a + virtual machine started with `mkosi vm` and built with the `Ssh=` + option enabled (or with **systemd-ssh-generator** installed) via the `mkosi ssh` command. + If not configured and `mkosi.key` exists in the working directory, + it will automatically be used for this purpose. + Run `mkosi genkey` to automatically generate a key in `mkosi.key`. + +`SshCertificate=`, `--ssh-certificate=` +: Path to the X.509 certificate in PEM format to provision as the SSH + public key in virtual machines started with `mkosi vm`. If not + configured and `mkosi.crt` exists in the working directory, it will + automatically be used for this purpose. Run `mkosi genkey` to + automatically generate a certificate in `mkosi.crt`. + +`Machine=`, `--machine=` +: Specify the machine name to use when booting the image. Can also be + used to refer to a specific image when SSH-ing into an image (e.g. + `mkosi --image=myimage ssh`). + + Note that `Ephemeral=` has to be enabled to start multiple instances + of the same image. + +`Register=`, `--register=` +: Takes a boolean value or `auto`. Specifies whether to register the + vm/container with systemd-machined. If enabled, mkosi will fail if + it can't register the vm/container with systemd-machined. If + disabled, mkosi will not register the vm/container with + systemd-machined. If `auto`, mkosi will register the vm/container + with systemd-machined if it is available. Defaults to `auto`. + +`ForwardJournal=`, `--forward-journal=` +: Specify the path to which journal logs from containers and virtual + machines should be forwarded. If the path has the `.journal` + extension, it is interpreted as a file to which the journal should be + written. Otherwise, the path is interpreted as a directory to which + the journal should be written. + + Note that systemd v256 or newer is required in the virtual machine for + log forwarding to work. + + Note that if a path with the `.journal` extension is given, the + journal size is limited to `4G`. Configure an output directory instead + of file if your workload produces more than `4G` worth of journal + data. + +`StorageTargetMode=`, `--storage-target-mode=` +: Specifies whether the `serve` verb should start + **systemd-storagetm** to serve disk images over NVME-TCP. Takes a + boolean value or `auto`. If enabled, systemd-storagetm is always + started and mkosi will fail if it cannot start systemd-storagetm. If + disabled, systemd-storagetm is never started. If `auto`, + systemd-storagetm will be started if a disk image is being built, + the systemd-storagetm binary is found and `mkosi serve` is being + invoked as the root user. + +`SysupdateDirectory=`, `--sysupdate-directory=` +: Path to a directory containing systemd-sysupdate transfer definition + files that are used by `mkosi sysupdate`. If `mkosi.sysupdate/` + exists in the local directory, it will be used for this purpose as + well. + + Note that `mkosi sysupdate` invokes `systemd-sysupdate` with + `--transfer-source=` set to the **mkosi** output directory. To make use + of this in a transfer definition file, set `PathRelativeTo=explicit` + to have the `Path=` setting for the transfer source be interpreted + relative to the **mkosi** output directory. Generally, configuring + `PathRelativeTo=explicit` and `Path=/` for the transfer source is + sufficient for the match pattern to be interpreted relative to the + **mkosi** output directory. + +### [Match] Section + +`Profiles=` +: Matches against the configured profiles. + +`Distribution=` +: Matches against the configured distribution. + +`Release=` +: Matches against the configured distribution release. If this condition is used and no distribution has been + explicitly configured yet, the host distribution and release are used. + +`Architecture=` +: Matches against the configured architecture. If this condition is used + and no architecture has been explicitly configured yet, the host + architecture is used. + +`Repositories=` +: Matches against repositories enabled with the `Repositories=` setting. + Takes a single repository name. + +`PathExists=` +: This condition is satisfied if the given path exists. Relative paths are interpreted relative to the parent + directory of the config file that the condition is read from. + +`ImageId=` +: Matches against the configured image ID, supporting globs. If this condition is used and no image ID has + been explicitly configured yet, this condition fails. + +`ImageVersion=` +: Matches against the configured image version. Image versions can be prepended by the operators `==`, `!=`, + `>=`, `<=`, `<`, `>` for rich version comparisons according to the UAPI group version format specification. + If no operator is prepended, the equality operator is assumed by default. If this condition is used and no + image version has been explicitly configured yet, this condition fails. + +`Bootable=` +: Matches against the configured value for the `Bootable=` feature. Takes a boolean value or `auto`. + +`Format=` +: Matches against the configured value for the `Format=` option. Takes + an output format (see the `Format=` option). + +`SystemdVersion=` +: Matches against the systemd version on the host (as reported by + `systemctl --version`). Values can be prepended by the operators `==`, + `!=`, `>=`, `<=`, `<`, `>` for rich version comparisons according to + the UAPI group version format specification. If no operator is + prepended, the equality operator is assumed by default. + +`BuildSources=` +: Takes a build source target path (see `BuildSources=`). This match is + satisfied if any of the configured build sources uses this target + path. For example, if we have a `mkosi.conf` file containing: + + ```ini + [Build] + BuildSources=../abc/qed:kernel + ``` + + and a drop-in containing: + + ```ini + [Match] + BuildSources=kernel + ``` + + The drop-in will be included. + + Any absolute paths passed to this setting are interpreted relative to + the current working directory. + +`HostArchitecture=` +: Matches against the host's native architecture. See the + `Architecture=` setting for a list of possible values. + +`ToolsTreeDistribution=` +: Matches against the configured tools tree distribution. + +`ToolsTreeRelease=` +: Matches against the configured tools tree release. + +`Environment=` +: Matches against a specific key/value pair configured with + `Environment=`. If no value is provided, check if the given key is in + the environment regardless of which value it has. + +`Image=` +: Match against the current (sub)image name. The name of a subimage is + its name in `mkosi.images/` (without any `.conf` suffix). The name + of the top level image is `main`. The main use case is to allow + having a shared config that can be included by both the top level + image and subimages by gating the universal settings behind a + `Image=main` match. + +This table shows which matchers support globs, rich comparisons and the default +value that is matched against if no value has been configured at the time the +config file is read: + +| Matcher | Globs | Rich Comparisons | Default | +|--------------------------|-------|------------------|----------------------------------------------------------------------------------------| +| `Profiles=` | no | no | match fails | +| `Distribution=` | no | no | match host distribution | +| `Release=` | no | no | match host release | +| `Architecture=` | no | no | match host architecture | +| `PathExists=` | no | no | n/a | +| `ImageId=` | yes | no | match fails | +| `ImageVersion=` | no | yes | match fails | +| `Bootable=` | no | no | match auto feature | +| `Format=` | no | no | match default format | +| `SystemdVersion=` | no | yes | n/a | +| `BuildSources=` | no | no | match fails | +| `HostArchitecture=` | no | no | n/a | +| `ToolsTreeDistribution=` | no | no | match the fallback tools tree distribution (see `ToolsTreeDistribution=` in `[Build]`) | +| `ToolsTreeRelease=` | no | no | match default tools tree release | +| `Environment=` | no | no | n/a | +| `Image=` | no | no | n/a | + +### [Include] + +`Include=`, `--include=`, `-I` +: Include extra configuration from the given file or directory. The + extra configuration is included immediately after parsing the setting, + except when used on the command line, in which case the extra + configuration is included after parsing all command line arguments. + + Note that each path containing extra configuration is only parsed + once, even if included more than once with `Include=`. + + The builtin configs for the **mkosi** default initrd, default tools tree, + default virtual machine image and default UKI addon can be included by + including the literal value `mkosi-initrd`, `mkosi-tools`, `mkosi-vm` or + `mkosi-addon` respectively. + + Note: Include names starting with either of the literals `mkosi-` or + `contrib-` are reserved for use by **mkosi** itself. + +### [Config] Section + +`Profiles=`, `--profile=` +: Select the given profiles. A profile is a configuration file or + directory in the `mkosi.profiles/` directory. The configuration files + and directories of each profile are included after parsing the + `mkosi.conf.d/*.conf` drop in configuration. + +`Dependencies=`, `--dependency=` +: The images that this image depends on specified as a comma-separated + list. All images configured in this option will be built before this + image. + + When this setting is specified for the "main" image, it specifies + which subimages should be built. See the + **Building multiple images** section for more information. + +`MinimumVersion=`, `--minimum-version=` +: The minimum **mkosi** version required to build this configuration. If + specified multiple times, the highest specified version is used. + + The minimum version can also be specified as a git commit hash when + prefixed with `commit:`, in which case mkosi must be executed from a + git checkout and the specified git commit hash must be an ancestor + of the currently checked out git commit in the repository that mkosi + is being executed from. + +`ConfigureScripts=`, `--configure-script=` +: Takes a comma-separated list of paths to executables that are used as + the configure scripts for this image. See the **Scripts** section for + more information. + +`PassEnvironment=`, `--pass-environment=` +: Takes a list of environment variable names separated by spaces. When + building multiple images, pass the listed environment variables to + each individual subimage as if they were "universal" settings. See + the **Building multiple images** section for more information. + +### [UKIProfile] Section + +The `UKIProfile` section can be used in UKI profile config files which +are passed to the `UnifiedKernelImageProfiles=` setting. The following +settings can be specified in the `UKIProfile` section: + +`Profile=` +: The contents of the `.profile` section of the UKI profile. Takes a + list of key/value pairs separated by `=`. The `ID=` key must be + specified. See the UKI [specification](https://uapi-group.org/specifications/specs/unified_kernel_image/#multi-profile-ukis) + for a full list of possible keys. + +`Cmdline=` +: Extra kernel command line options for the UKI profile. Takes a space + delimited list of extra kernel command line arguments. Note that + the final `.cmdline` section will the combination of the base + `.cmdline` section and the extra kernel command line arguments + specified with this setting. + +`SignExpectedPcr=` +: Sign expected PCR measurements for this UKI profile. Takes a boolean. + Enabled by default. + +## Specifiers + +The current value of various settings can be accessed when parsing +configuration files by using specifiers. To write a literal `%` +character in a configuration file without treating it as a specifier, +use `%%`. The following specifiers are understood: + +| Setting | Specifier | +|--------------------|-----------| +| `Distribution=` | `%d` | +| `Release=` | `%r` | +| `Architecture=` | `%a` | +| `Format=` | `%t` | +| `Output=` | `%o` | +| `OutputDirectory=` | `%O` | +| `ImageId=` | `%i` | +| `ImageVersion=` | `%v` | + +There are also specifiers that are independent of settings: + +| Specifier | Value | +|-----------|------------------------------------------------| +| `%C` | Parent directory of current config file | +| `%P` | Current working directory | +| `%D` | Directory that **mkosi** was invoked in | +| `%I` | Name of the current subimage in `mkosi.images` | + +Finally, there are specifiers that are derived from a setting: + +| Specifier | Value | +|-----------|-------------------------------------------------------| +| `%F` | The default filesystem of the configured distribution | + +Note that the current working directory changes as **mkosi** parses its +configuration. Specifically, each time **mkosi** parses a directory +containing a `mkosi.conf` file, **mkosi** changes its working directory to +that directory. + +Note that the directory that **mkosi** was invoked in is influenced by the +`--directory=` command line argument. + +The following table shows example values for the directory specifiers +listed above: + +| | `$D/mkosi.conf` | `$D/mkosi.conf.d/abc/abc.conf` | `$D/mkosi.conf.d/abc/mkosi.conf` | +|------|-----------------|--------------------------------|----------------------------------| +| `%C` | `$D` | `$D/mkosi.conf.d` | `$D/mkosi.conf.d/abc` | +| `%P` | `$D` | `$D` | `$D/mkosi.conf.d/abc` | +| `%D` | `$D` | `$D` | `$D` | + +## Supported distributions + +Images may be created containing installations of the following +distributions: + +* *Fedora Linux* + +* *Debian* + +* *Kali Linux* + +* *Ubuntu* + +* *Arch Linux* + +* *openSUSE* + +* *Mageia* + +* *CentOS* + +* *RHEL* + +* *RHEL UBI* + +* *OpenMandriva* + +* *Rocky Linux* + +* *Alma Linux* + +* *Azure Linux* + +* *None* (**Requires the user to provide a pre-built rootfs**) + +In theory, any distribution may be used on the host for building images +containing any other distribution, as long as the necessary tools are +available. +Specifically, +any distribution that packages **apt** may be used to build *Debian*, *Kali* or *Ubuntu* images. +Any distribution that packages **dnf** may be used to build images for any of the RPM-based distributions. +Any distro that packages **pacman** may be used to build *Arch Linux* images. +Any distribution that packages **zypper** may be used to build *openSUSE* images. +Other distributions and build automation tools for embedded Linux +systems such as Buildroot, OpenEmbedded and Yocto Project may be used by +selecting the `custom` distribution, and populating the rootfs via a +combination of base trees, skeleton trees, and prepare scripts. + +Currently, *Fedora Linux* packages all relevant tools as of Fedora 28. + +Note that when not using a custom mirror, `RHEL` images can only be +built from a host system with a `RHEL` subscription (established using +e.g. `subscription-manager`). + +# Execution Flow + +Execution flow for `mkosi build`. Default values/calls are shown in parentheses. +When building with `--incremental=yes` **mkosi** creates a cache of the distribution +installation if not already existing and replaces the distribution installation +in consecutive runs with data from the cached one. + +1. Parse CLI options +1. Parse configuration files +1. Run configure scripts (`mkosi.configure`) +1. If we're not running as root, unshare the user namespace and map the + subuid range configured in `/etc/subuid` and `/etc/subgid` into it. +1. Unshare the mount namespace +1. Remount the following directories read-only if they exist: + - `/usr` + - `/etc` + - `/opt` + - `/srv` + - `/boot` + - `/efi` + - `/media` + - `/mnt` + +Then, for each image, we execute the following steps: + +1. Copy sandbox trees into the workspace +1. Sync the package manager repository metadata +1. Run sync scripts (`mkosi.sync`) +1. Copy base trees (`--base-tree=`) into the image +1. Reuse a cached image if one is available +1. Copy a snapshot of the package manager repository metadata into the + image +1. Copy skeleton trees (`mkosi.skeleton`) into image +1. Install distribution and packages into image +1. Run prepare scripts on image with the `final` argument (`mkosi.prepare`) +1. Install build packages in overlay if any build scripts are configured +1. Run prepare scripts on overlay with the `build` argument if any build + scripts are configured (`mkosi.prepare`) +1. Cache the image if configured (`--incremental=yes`) +1. Run build scripts on image + overlay if any build scripts are configured (`mkosi.build`) +1. Finalize the build if the output format `none` is configured +1. Copy the build scripts outputs into the image +1. Copy the extra trees into the image (`mkosi.extra`) +1. Run post-install scripts (`mkosi.postinst`) +1. Write config files required for `Ssh=`, `Autologin=` and `MakeInitrd=` +1. Install systemd-boot and configure secure boot if configured (`--secure-boot=yes`) +1. Run **systemd-sysusers** +1. Run **systemd-tmpfiles** +1. Run `systemctl preset-all` +1. Run **depmod** +1. Run **systemd-firstboot** +1. Run **systemd-hwdb** +1. Remove packages and files (`RemovePackages=`, `RemoveFiles=`) +1. Run SELinux relabel is a SELinux policy is installed +1. Run finalize scripts (`mkosi.finalize`) +1. Generate unified kernel image if configured to do so +1. Generate final output format +1. Run post-output scripts (`mkosi.postoutput`) + +# Scripts + +To allow for image customization that cannot be implemented using +**mkosi**'s builtin features, **mkosi** supports running scripts at various +points during the image build process that can customize the image as +needed. Scripts are executed on the host system as root (either real +root or root within the user namespace that **mkosi** created when running +unprivileged) with a customized environment to simplify modifying the +image. For each script, the configured build sources (`BuildSources=`) +are mounted into the current working directory before running the script +in the current working directory. `$SRCDIR` is set to point to the +current working directory. The following scripts are supported: + +* If **`mkosi.configure`** (`ConfigureScripts=`) exists, it is executed + before building the image. This script may be used to dynamically + modify the configuration. It receives the configuration serialized as + JSON on stdin and should output the modified configuration serialized + as JSON on stdout. Note that this script only runs when building or + booting the image (`build`, `vm`, `boot` and `shell` verbs). If a + default tools tree is configured, it will be built before running the + configure scripts and the configure scripts will run with the tools + tree available. This also means that the modifications made by + configure scripts will not be visible in the `summary` output. + +* If **`mkosi.sync`** (`SyncScripts=`) exists, it is executed before the + image is built. This script may be used to update various sources that + are used to build the image. One use case is to run `git pull` on + various source repositories before building the image. Specifically, + the `BuildSourcesEphemeral=` setting does not apply to sync scripts, + which means sync scripts can be used to update build sources even if + `BuildSourcesEphemeral=` is enabled. + +* If **`mkosi.prepare`** (`PrepareScripts=`) exists, it is first called + with the `final` argument, right after the software packages are + installed. It is called a second time with the `build` command line + parameter, right after the build packages are installed and the build + overlay mounted on top of the image's root directory . This script has + network access and may be used to install packages from other sources + than the distro's package manager (e.g. **pip**, **npm**, ...), after all + software packages are installed but before the image is cached (if + incremental mode is enabled). In contrast to a general purpose + installation, it is safe to install packages to the system + (`pip install`, `npm install -g`) instead of in `$SRCDIR` itself + because the build image is only used for a single project and can + easily be thrown away and rebuilt so there's no risk of conflicting + dependencies and no risk of polluting the host system. + +* If **`mkosi.build`** (`BuildScripts=`) exists, it is executed with the + build overlay mounted on top of the image's root directory. When + running the build script, `$DESTDIR` points to a directory where the + script should place any files generated it would like to end up in the + image. Note that **make**-, **automake**-, and **meson**-based build systems + generally honor `$DESTDIR`, thus making it very natural to build + *source* trees from the build script. After running the build script, + the contents of `$DESTDIR` are copied into the image. + +* If **`mkosi.postinst`** (`PostInstallationScripts=`) exists, it is + executed after the (optional) build tree and extra trees have been + installed. This script may be used to alter the images without any + restrictions, after all software packages and built sources have been + installed. + +* If **`mkosi.finalize`** (`FinalizeScripts=`) exists, it is executed as + the last step of preparing an image. + +* If **`mkosi.postoutput`** (`PostOutputScripts=`) exists, it is executed right + after all the output files have been generated, before they are finally + moved into the output directory. This can be used to generate additional or + alternative outputs, e.g. `SHA256FILES` or SBOM manifests. + +* If **`mkosi.clean`** (`CleanScripts=`) exists, it is executed right + after the outputs of a previous build have been cleaned up. A clean + script can clean up any outputs that **mkosi** does not know about (e.g. + artifacts from `SplitArtifacts=partitions` or RPMs built in a build script). + Note that this script does not use the tools tree even if one is configured. + +* If **`mkosi.version`** exists and is executable, it is run during + configuration parsing and populates `ImageVersion=` with the output on stdout. + This can be used for external version tracking, e.g. with `git describe` or + `date '+%Y-%m-%d'`. Note that this script is executed on the host system + without any sandboxing. + +* If **`mkosi.rootpw`** exists and is executable, it is run during + configuration parsing and populates `RootPassword=` with the output + on stdout. This can be used to randomly generate a password and can + be remembered by outputting it to stderr or by reading `$MKOSI_CONFIG` + in another script (e.g. `mkosi.postoutput`). Note that this script is + executed on the host system without any sandboxing. + +If a script uses the `.chroot` extension, **mkosi** will chroot into the +image using **mkosi-chroot** (see below) before executing the script. For +example, if `mkosi.postinst.chroot` exists, **mkosi** will chroot into the +image and execute it as the post-installation script. + +Instead of a single file script, **mkosi** will also read all files in lexicographical order from appropriately +named `.d` directories, e.g. all files in a `mkosi.build.d` would be used as build scripts. This is supported +by + +* `mkosi.sync.d`, +* `mkosi.prepare.d`, +* `mkosi.build.d`, +* `mkosi.postinst.d`, +* `mkosi.finalize.d`, +* `mkosi.postoutput.d`, and +* `mkosi.clean.d`. + +This can be combined with the `.chroot` extension, e.g. `mkosi.build.d/01-foo.sh` would be run without +chrooting into the image and `mkosi.build.d/02-bar.sh.chroot` would be run after chrooting into the image +first. + +Scripts executed by **mkosi** receive the following environment variables: + +* `$ARCHITECTURE` contains the architecture from the `Architecture=` + setting. If `Architecture=` is not set, it will contain the native + architecture of the host machine. See the documentation of + `Architecture=` for possible values for this variable. + +* `$QEMU_ARCHITECTURE` contains the architecture from `$ARCHITECTURE` in + the format used by **qemu**. Useful for finding the qemu binary ( + `qemu-system-$QEMU_ARCHITECTURE`). + +* `$DISTRIBUTION` contains the distribution from the `Distribution=` setting. + +* `$RELEASE` contains the release from the `Release=` setting. + +* `$DISTRIBUTION_ARCHITECTURE` contains the architecture from + `$ARCHITECTURE` in the format used by the configured distribution. + +* `$PROFILES` contains the profiles from the `Profiles=` setting as a + comma-delimited string. + +* `$CACHED` is set to `1` if a cached image is available, `0` otherwise. + +* `$CHROOT_SCRIPT` contains the path to the running script relative to + the image root directory. The primary usecase for this variable is in + combination with the **mkosi-chroot** script. See the description of + **mkosi-chroot** below for more information. + +* `$SRCDIR` contains the path to the directory **mkosi** was invoked from, + with any configured build sources mounted on top. `$CHROOT_SRCDIR` + contains the value that `$SRCDIR` will have after invoking + **mkosi-chroot**. + +* `$BUILDDIR` is only defined if `mkosi.builddir` exists and points to + the build directory to use. This is useful for all build systems that + support out-of-tree builds to reuse already built artifacts from + previous runs. `$CHROOT_BUILDDIR` contains the value that `$BUILDDIR` + will have after invoking **mkosi-chroot**. + +* `$DESTDIR` is a directory into which any installed software generated + by a build script may be placed. This variable is only set when + executing a build script. `$CHROOT_DESTDIR` contains the value that + `$DESTDIR` will have after invoking **mkosi-chroot**. + +* `$OUTPUTDIR` points to the staging directory used to store build + artifacts generated during the build. `$CHROOT_OUTPUTDIR` contains the + value that `$OUTPUTDIR` will have after invoking **mkosi-chroot**. + +* `$PACKAGEDIR` points to the directory containing the local package + repository. Build scripts can add more packages to the local + repository by writing the packages to `$PACKAGEDIR`. + +* `$ARTIFACTDIR` points to the directory that is used to pass around build + artifacts generated during the build and make them available for use by + mkosi. This is similar to `PACKAGEDIR`, but is meant for artifacts that may + not be packages understood by the package manager, e.g. initrds created by + other initrd generators than mkosi. Build scripts can add more artifacts to + the directory by placing them in `$ARTIFACTDIR`. Files in this directory are + only available for the current build and are not copied out like the contents + of `$OUTPUTDIR`. + + **mkosi** will also use certain subdirectories of an artifacts directory to + automatically use their contents at certain steps. Currently the following + two subdirectories in the artifact directory are used by mkosi: + - `io.mkosi.microcode`: All files in this directory are used as microcode + files, i.e. they are prepended to the initrds in lexicographical order. + - `io.mkosi.initrd`: All files in this directory are used as initrds and + joined in lexicographical order. + + It is recommended, that users of `$ARTIFACTDIR` put things for their own use in a + similar namespaced directory, e.g. `local.my.namespace`. + +* `$BUILDROOT` is the root directory of the image being built, + optionally with the build overlay mounted on top depending on the + script that's being executed. + +* `$WITH_DOCS` is either `0` or `1` depending on whether a build + without or with installed documentation was requested + (`WithDocs=yes`). A build script should suppress installation of + any package documentation to `$DESTDIR` in case `$WITH_DOCS` is set + to `0`. + +* `$WITH_TESTS` is either `0` or `1` depending on whether a build + without or with running the test suite was requested + (`WithTests=no`). A build script should avoid running any unit or + integration tests in case `$WITH_TESTS` is `0`. + +* `$WITH_NETWORK` is either `0` or `1` depending on whether a build + without or with networking is being executed (`WithNetwork=no`). + A build script should avoid any network communication in case + `$WITH_NETWORK` is `0`. + +* `$SOURCE_DATE_EPOCH` is defined if requested (`SourceDateEpoch=TIMESTAMP`, + `Environment=SOURCE_DATE_EPOCH=TIMESTAMP` or the host environment variable + `$SOURCE_DATE_EPOCH`). This is useful to make builds reproducible. See + [SOURCE_DATE_EPOCH](https://reproducible-builds.org/specs/source-date-epoch/) + for more information. + +* `$MKOSI_UID` and `$MKOSI_GID` respectively are the uid, gid of the + user that invoked mkosi. + +* `$MKOSI_CONFIG` is a file containing a json summary of the settings of the + current image. This file can be parsed inside scripts to gain access to all + settings for the current image. + +* `$IMAGE_ID` contains the identifier from the `ImageId=` or `--image-id=` setting. + +* `$IMAGE_VERSION` contains the version from the `ImageVersion=` or `--image-version=` setting. + +* `$MKOSI_DEBUG` is either `0` or `1` depending on whether debugging output is + enabled. + +Consult this table for which script receives which environment variables: + +| Variable | `configure` | `sync` | `prepare` | `build` | `postinst` | `finalize` | `postoutput` | `clean` | +|-----------------------------|:-----------:|:------:|:---------:|:-------:|:----------:|:----------:|:------------:|:-------:| +| `ARCHITECTURE` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `ARTIFACTDIR` | | | ✓ | ✓ | ✓ | ✓ | | | +| `BUILDDIR` | | | | ✓ | ✓ | ✓ | | | +| `BUILDROOT` | | | ✓ | ✓ | ✓ | ✓ | | | +| `CACHED` | | ✓ | | | | | | | +| `CHROOT_BUILDDIR` | | | | ✓ | | | | | +| `CHROOT_DESTDIR` | | | | ✓ | | | | | +| `CHROOT_OUTPUTDIR` | | | | | ✓ | ✓ | | | +| `CHROOT_SCRIPT` | | | ✓ | ✓ | ✓ | ✓ | | | +| `CHROOT_SRCDIR` | | | ✓ | ✓ | ✓ | ✓ | | | +| `MKOSI_DEBUG` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `DESTDIR` | | | | ✓ | | | | | +| `DISTRIBUTION` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `DISTRIBUTION_ARCHITECTURE` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `IMAGE_ID` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `IMAGE_VERSION` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `MKOSI_CONFIG` | | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `MKOSI_GID` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `MKOSI_UID` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `OUTPUTDIR` | | | | | ✓ | ✓ | ✓ | ✓ | +| `PACKAGEDIR` | | | ✓ | ✓ | ✓ | ✓ | | | +| `PROFILES` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | ✓ | +| `QEMU_ARCHITECTURE` | ✓ | | | | | | | | +| `RELEASE` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `SOURCE_DATE_EPOCH` | | | ✓ | ✓ | ✓ | ✓ | | ✓ | +| `SRCDIR` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `WITH_DOCS` | | | ✓ | ✓ | | | | | +| `WITH_NETWORK` | | | ✓ | ✓ | ✓ | ✓ | | | +| `WITH_TESTS` | | | ✓ | ✓ | | | | | + +Additionally, when a script is executed, a few scripts are made +available via `$PATH` to simplify common usecases. + +* **mkosi-chroot**: This script will chroot into the image and execute the + given command. On top of chrooting into the image, it will also mount + various files and directories (`$SRCDIR`, `$DESTDIR`, `$BUILDDIR`, + `$OUTPUTDIR`, `$CHROOT_SCRIPT`) into the image and modify the + corresponding environment variables to point to the locations inside + the image. It will also mount APIVFS filesystems (`/proc`, `/dev`, + ...) to make sure scripts and tools executed inside the chroot work + properly. It also propagates `/etc/resolv.conf` from the host into the + chroot if requested so that DNS resolution works inside the chroot. + After the mkosi-chroot command exits, various mount points are cleaned + up. + + For example, to invoke **ls** inside of the image, use the following: + + ```sh + mkosi-chroot ls ... + ``` + + To execute the entire script inside the image, add a `.chroot` suffix + to the name (`mkosi.build.chroot` instead of `mkosi.build`, etc.). + +* For all of the supported package managers (**dnf**, **rpm**, **apt**, **dpkg**, + **pacman**, **zypper**), scripts of the same name are put into `$PATH` + that make sure these commands operate on the image's root directory + with the configuration supplied by the user instead of on the host + system. This means that from a script, you can do e.g. + `dnf install vim` to install vim into the image. + + Additionally, `mkosi-install`, `mkosi-reinstall`, `mkosi-upgrade` and + `mkosi-remove` will invoke the corresponding operation of the package + manager being used to built the image. + +* **git** is automatically invoked with `safe.directory=*` to avoid + permissions errors when running as the root user in a user namespace. + +* **useradd** and **groupadd** are automatically invoked with + `--root=$BUILDROOT` when executed outside of the image. + +When scripts are executed, any directories that are still writable are +also made read-only (`/home`, `/var`, `/root`, ...) and only the minimal +set of directories that need to be writable remain writable. This is to +ensure that scripts can't mess with the host system when **mkosi** is +running as root. + +Note that when executing scripts, all source directories are made +ephemeral which means all changes made to source directories while +running scripts are thrown away after the scripts finish executing. Use +the output, build or cache directories if you need to persist data +between builds. + +# Files + +To make it easy to build images for development versions of your +projects, **mkosi** can read configuration data from the local directory, +under the assumption that it is invoked from a *source* +tree. Specifically, the following files are used if they exist in the +local directory: + +* The **`mkosi.skeleton/`** directory or **`mkosi.skeleton.tar`** + archive may be used to insert files into the image. The files are + copied *before* the distribution packages are installed into the + image. This allows creation of files that need to be provided + early, for example to configure the package manager or set systemd + presets. + + When using the directory, file ownership is not preserved: all files + copied will be owned by root. To preserve ownership, use a tar + archive. + +* The **`mkosi.extra/`** directory or **`mkosi.extra.tar`** archive + may be used to insert additional files into the image, on top of + what the distribution includes in its packages. They are similar to + `mkosi.skeleton/` and `mkosi.skeleton.tar`, but the files are copied + into the directory tree of the image *after* the OS was installed. + + When using the directory, file ownership is not preserved: all files + copied will be owned by root. To preserve ownership, use a tar + archive. + +* The **`mkosi.sandbox/`** directory or **`mkosi.sandbox.tar`** archive + may be used to configure the package manager without the files being + inserted into the image. If the files should be included in the image + `mkosi.skeleton/` and `mkosi.skeleton.tar` should be used instead. + + When using the directory, file ownership is not preserved: all files + copied will be owned by root. To preserve ownership, use a tar + archive. + +* The **`mkosi.nspawn`** nspawn settings file will be copied into the same place as the output image file, if + it exists. This is useful since nspawn looks for settings files next to image files it boots, for + additional container runtime settings. + +* The **`mkosi.cache/`** directory, if it exists, is automatically used as package download cache, in order + to speed repeated runs of the tool. + +* The **`mkosi.builddir/`** directory, if it exists, is automatically used as out-of-tree build directory, if + the build commands in the `mkosi.build` scripts support it. Specifically, this directory will be mounted + into the build container, and the `$BUILDDIR` environment variable will be set to it when the build scripts + are invoked. A build script may then use this directory as build directory, for **automake**-style or + **ninja**-style out-of-tree builds. This speeds up builds considerably, in particular when **mkosi** is used in + incremental mode (`-i`): not only the image and build overlay, but also the build tree is reused between + subsequent invocations. Note that if this directory does not exist the `$BUILDDIR` environment variable is + not set, and it is up to the build scripts to decide whether to do an in-tree or an out-of-tree build, and + which build directory to use. + +* The **`mkosi.rootpw`** file can be used to provide the password for the root user of the image. If the + password is prefixed with `hashed:` it is treated as an already hashed root password. The password may + optionally be followed by a newline character which is implicitly removed. The file must have an access + mode of 0600 or less. If this file does not exist, the distribution's default root password is set (which + usually means access to the root user is blocked). + +* The **`mkosi.passphrase`** file provides the passphrase to use when + LUKS encryption is selected. It should contain the passphrase + literally, and not end in a newline character (i.e. in the same + format as **cryptsetup** and `/etc/crypttab` expect the passphrase + files). The file must have an access mode of 0600 or less. + +* The **`mkosi.crt`** and **`mkosi.key`** files contain an X.509 certificate and PEM private key to use when + signing is required (UEFI SecureBoot, verity, ...). + +* The **`mkosi.output/`** directory is used to store all build + artifacts. + +* The **`mkosi.credentials/`** directory is used as a + source of extra credentials similar to the `Credentials=` option. For + each file in the directory, the filename will be used as the credential + name and the file contents become the credential value, or, if the file is + executable, **mkosi** will execute the file and the command's + output to stdout will be used as the credential value. Output to stderr will be ignored. + Credentials configured with `Credentials=` take precedence over files in `mkosi.credentials`. + +* The **`mkosi.repart/`** directory is used as the source for + **systemd-repart** partition definition files which are passed to + **systemd-repart** when building a disk image. If it does not exist and + the `RepartDirectories=` setting is not configured, **mkosi** will default + to the following partition definition files: + + `00-esp.conf` (if we're building a bootable image): + + ```ini + [Partition] + Type=esp + Format=vfat + CopyFiles=/boot:/ + CopyFiles=/efi:/ + SizeMinBytes=512M + SizeMaxBytes=512M + ``` + + `05-bios.conf` (if we're building a BIOS bootable image): + + ```ini + [Partition] + # UUID of the grub BIOS boot partition which grubs needs on GPT to + # embed itself into. + Type=21686148-6449-6e6f-744e-656564454649 + SizeMinBytes=1M + SizeMaxBytes=1M + ``` + + `10-root.conf` + + ```ini + [Partition] + Type=root + Format= + CopyFiles=/ + Minimize=guess + ``` + + Note that if either `mkosi.repart/` is found or `RepartDirectories=` + is used, we will not use any of the default partition definitions. + +All these files are optional. + +Note that the location of all these files may also be configured +during invocation via command line switches, and as settings in +`mkosi.conf`, in case the default settings are not acceptable for a +project. + +# CACHING + +**mkosi** supports three different caches for speeding up repetitive +re-building of images. Specifically: + +1. The package cache of the distribution package manager may be cached + between builds. This is configured with the `--cache-directory=` option + or the `mkosi.cache/` directory. This form of caching relies on the + distribution's package manager, and caches distribution packages + (RPM, deb, …) after they are downloaded, but before they are + unpacked. + +2. If the incremental build mode is enabled with `--incremental=yes`, cached + copies of the final image and build overlay are made immediately + before the build sources are copied in (for the build overlay) or the + artifacts generated by `mkosi.build` are copied in (in case of the + final image). This form of caching allows bypassing the time-consuming + package unpacking step of the distribution package managers, but is only + effective if the list of packages to use remains stable, but the build + sources and its scripts change regularly. Note that this cache requires + manual flushing: whenever the package list is modified the cached + images need to be explicitly removed before the next re-build, + using the `-f` switch. + +3. Finally, between multiple builds the build artifact directory may + be shared, using the `mkosi.builddir/` directory. This directory + allows build systems such as Meson to reuse already compiled + sources from a previous built, thus speeding up the build process + of a `mkosi.build` build script. + +The package cache and incremental mode are unconditionally useful. The +final cache only apply to uses of **mkosi** with a source tree and build +script. When all three are enabled together turn-around times for +complete image builds are minimal, as only changed source files need to +be recompiled. + +# Building multiple images + +If the `mkosi.images/` directory exists, **mkosi** will load individual +subimage configurations from it and build each of them. Image +configurations can be either directories containing **mkosi** configuration +files or regular files with the `.conf` extension. + +When image configurations are found in `mkosi.images/`, **mkosi** will build +the images specified in the `Dependencies=` setting of the main image +and all of their dependencies (or all of them if no images were +explicitly configured using `Dependencies=` in the main image +configuration). To add dependencies between subimages, the +`Dependencies=` setting can be used as well. Subimages are always built +before the main image. + +When images are defined, **mkosi** will first read the main image +configuration (configuration outside of the `mkosi.images/` directory), +followed by the image specific configuration. + +Several "multiversal" settings apply to the default tools tree and to +the main image and cannot be configured separately outside of the main +image: + +- `RepositoryKeyCheck=` +- `RepositoryKeyFetch=` +- `SourceDateEpoch=` +- `CacheOnly=` +- `WorkspaceDirectory=` +- `PackageCacheDirectory=` +- `BuildSources=` +- `BuildSourcesEphemeral=` +- `ProxyClientCertificate=` +- `ProxyClientKey=` +- `ProxyExclude=` +- `ProxyPeerCertificate=` +- `ProxyUrl=` + +Several "universal" settings apply to the main image and all its +subimages and cannot be configured separately in subimages. The +following settings are universal and cannot be configured in subimages: + +- `Architecture=` +- `BuildDirectory=` +- `CacheDirectory=` +- `Distribution=` +- `ExtraSearchPaths=` +- `Incremental=` +- `LocalMirror=` +- `Mirror=` +- `OutputDirectory=` +- `OutputMode=` +- `PackageDirectories=` +- `Release=` +- `RepartOffline=` +- `Repositories=` +- `SandboxTrees=` +- `ToolsTree=` +- `ToolsTreeCertificates=` +- `UseSubvolumes=` +- `SecureBootCertificate=` +- `SecureBootCertificateSource=` +- `SecureBootKey=` +- `SecureBootKeySource=` +- `VerityCertificate=` +- `VerityCertificateSource=` +- `VerityKey=` +- `VerityKeySource=` +- `VolatilePackageDirectories=` +- `WithNetwork=` +- `WithTests` + +There are also settings which are passed down to subimages but can +be overridden. For these settings, values configured explicitly in +the subimage will take priority over values configured on the CLI or +in the main image config. Currently the following settings are passed +down to subimages but can be overridden: + +- `Profiles=` +- `ImageId=` +- `ImageVersion=` +- `SectorSize=` +- `CacheKey=` +- `BuildKey=` +- `CompressLevel=` +- `SignExpectedPcrKey=` +- `SignExpectedPcrKeySource=` +- `SignExpectedPcrCertificate=` +- `SignExpectedPcrCertificateSource=` + +Additionally, there are various settings that can only be configured in +the main image but which are not passed down to subimages: + +- `MinimumVersion=` +- `PassEnvironment=` +- `ToolsTreeDistribution=` +- `ToolsTreeRelease=` +- `ToolsTreeProfiles=` +- `ToolsTreeMirror=` +- `ToolsTreeRepositories=` +- `ToolsTreeSandboxTrees=` +- `ToolsTreePackages=` +- `ToolsTreePackageDirectories=` +- `History=` +- Every setting in the `[Runtime]` section + +Images can refer to outputs of images they depend on. Specifically, +for the following options, **mkosi** will only check whether the inputs +exist just before building the image: + +- `BaseTrees=` +- `ExtraTrees=` +- `Initrds=` + +To refer to outputs of a image's dependencies, simply configure any of +these options with a relative path to the output to use in the output +directory of the dependency. Or use the `%O` specifier to refer to the +output directory. + +A good example on how to build multiple images can be found in the +[systemd](https://github.com/systemd/systemd/tree/main/mkosi.images) +repository. + +# ENVIRONMENT VARIABLES + +* `$MKOSI_LESS` overrides options for **less** when it is invoked by + **mkosi** to page output. + +* `$MKOSI_DNF` can be used to override the executable used as **dnf**. + This is particularly useful to select between **dnf** and **dnf5**. + +* `$EPEL_MIRROR` can be used to override the default mirror location + used for the epel repositories when `Mirror=` is used. By default + **mkosi** looks for the epel repositories in the `fedora` subdirectory of + the parent directory of the mirror specified in `Mirror=`. For example + if the mirror is set to `https://mirror.net/centos-stream` **mkosi** will + look for the epel repositories in `https://mirror.net/fedora/epel`. + +* `SYSEXT_SCOPE` and `CONFEXT_SCOPE` can be used to override the default + value of the respective `extension-release` file when building a sysext + or confext. By default the value is set to `initrd system portable`. + +# EXAMPLES + +Create and run a raw *GPT* image with *ext4*, as `image.raw`: + +```console +# mkosi -p systemd -i boot +``` + +Create and run a bootable *GPT* image, as `foobar.raw`: + +```console +$ mkosi -d fedora -p kernel-core -p systemd -p systemd-boot -p udev -o foobar.raw +# mkosi --output foobar.raw boot +$ mkosi --output foobar.raw vm +``` + +Create and run a *Fedora Linux* image in a plain directory: + +```console +# mkosi --distribution fedora --format directory boot +``` + +Create a compressed image `image.raw.xz` with SSH installed and add a checksum file: + +```console +$ mkosi --distribution fedora --format disk --checksum=yes --compress-output=yes --package=openssh-clients +``` + +Inside the source directory of an **automake**-based project, configure +**mkosi** so that simply invoking **mkosi** without any parameters builds +an OS image containing a built version of the project in its current +state: + +```console +$ cat >mkosi.conf <mkosi.build <, + +include + +/path/to/mkosi flags=(default_allow) { + userns, +} +``` + +# Frequently Asked Questions (FAQ) + +- Why does `mkosi vm` with KVM not work on Debian/Kali/Ubuntu? + + While other distributions are OK with allowing access to `/dev/kvm`, on + Debian/Kali/Ubuntu this is only allowed for users in the `kvm` group. Because + **mkosi** unshares a user namespace when running unprivileged, even if the + calling user was in the kvm group, when **mkosi** unshares the user + namespace to run unprivileged, it loses access to the `kvm` group and by + the time we start **qemu** we don't have access to `/dev/kvm` anymore. As + a workaround, you can change the permissions of the device nodes to + `0666` which is sufficient to make KVM work unprivileged. To persist + these settings across reboots, copy + `/usr/lib/tmpfiles.d/static-nodes-permissions.conf` to + `/etc/tmpfiles.d/static-nodes-permissions.conf` and change the mode of + `/dev/kvm` from `0660` to `0666`. + +- How do I add a regular user to an image? + + You can use the following snippet in a post-installation script: + + ```sh + useradd --create-home --user-group $USER --password "$(openssl passwd -stdin -6 <$USER_PASSWORD_FILE)" + ``` + + Note that from systemd v256 onwards, if enabled, + **systemd-homed-firstboot.service** will prompt to create a regular user + on first boot if there are no regular users. + +- Why do I see failures to chown files when building images? + + When not running as root, your user is not able to change ownership of + files to arbitrary owners. Various distributions still ship files in their + packages that are not owned by the root user. When not running as root, mkosi + maps the current user to root when invoking package managers, which means that + changing ownership to root will work but changing ownership to any other user + or group will fail. + + Note that chown calls are only suppressed when running package managers, but + not when running scripts. If this is required, e.g. for a build script, you + can set the `MKOSI_CHROOT_SUPPRESS_CHOWN` variable to a true value (`1`, + `yes`, `true`) to suppress chown calls in **mkosi-chroot** and `.chroot` scripts. + + If this behavior causes applications running in your image to misbehave, you + can consider running **mkosi** as root which avoids this problem. Alternatively, + if running **mkosi** as root is not desired, you can use + `unshare --map-auto --map-current-user --setuid 0 --setgid 0` to become root in + a user namespace with more than one user assuming the UID/GID mappings in + `/etc/subuid` and `/etc/subgid` are configured correctly. Note that running mkosi + as root or with `unshare` means that all output files produced by **mkosi** will not + be owned by your current user anymore. + + Note that for systemd services that need directories in `/var` owned by the service + user and group, an alternative to shipping these directories in packages or + creating them via systemd-tmpfiles is to use `StateDirectory=`, `CacheDirectory=` or + `LogsDirectory=` in the service file which instructs systemd to create the directory + when it first starts the service. + + Alternatively, the `z` or `Z` directives for `systemd-tmpfiles` can be used to chown + various directories and files to their owning user when the system first boots up. + +- Why does `portablectl inspect `/`systemd-dissect ` say my portable service isn't one? + + `systemd-dissect` and`portablectl inspect` check for `PORTABLE_PREFIXES=` in `os-release` and if the key is + missing, will fail to recognise a portable service as one, showing ✗ under *Use as* for in the case of + `systemd-dissect` or `n/a` under *Portable Service* for `portablectl`. + + Since there is no good default to set for this key and the generated portable service images will still + attach properly, even when the key is not set, **mkosi** doesn't set one. + + You can set `PORTABLE_PREFIXES=` in the `os-release` file yourself in a postinst script. + +# REFERENCES +* [Primary mkosi git repository on GitHub](https://github.com/systemd/mkosi/) +* [mkosi — A Tool for Generating OS Images](https://0pointer.net/blog/mkosi-a-tool-for-generating-os-images.html) introductory blog post by Lennart Poettering +* [The mkosi OS generation tool](https://lwn.net/Articles/726655/) story on LWN + +# SEE ALSO +**systemd-nspawn**(1), **systemd-repart**(8), **dnf**(8) diff --git a/mkosi.profiles/azure/azure-complete-provisioning.service b/mkosi.profiles/azure/azure-complete-provisioning.service new file mode 100644 index 0000000..4982b86 --- /dev/null +++ b/mkosi.profiles/azure/azure-complete-provisioning.service @@ -0,0 +1,14 @@ +[Unit] +Description=Report VM is ready to Azure API +After=network.target network-setup.service +Requires=network-setup.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/azure-complete-provisioning +RemainAfterExit=yes +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=minimal.target diff --git a/mkosi.profiles/azure/mkosi.conf b/mkosi.profiles/azure/mkosi.conf new file mode 100644 index 0000000..f068bc6 --- /dev/null +++ b/mkosi.profiles/azure/mkosi.conf @@ -0,0 +1,5 @@ +[Output] +ImageId=tdx-debian-azure + +[Content] +SkeletonTrees=azure-complete-provisioning.service:/etc/systemd/system/azure-complete-provisioning.service diff --git a/mkosi.profiles/azure/mkosi.postinst b/mkosi.profiles/azure/mkosi.postinst new file mode 100755 index 0000000..2807f15 --- /dev/null +++ b/mkosi.profiles/azure/mkosi.postinst @@ -0,0 +1,4 @@ +#!/bin/sh + +mkosi-chroot systemctl enable "azure-complete-provisioning.service" +ln -sf "/etc/systemd/system/azure-complete-provisioning.service" "$BUILDROOT/etc/systemd/system/minimal.target.wants/" diff --git a/mkosi.profiles/azure/mkosi.postoutput b/mkosi.profiles/azure/mkosi.postoutput new file mode 100755 index 0000000..47bb3ae --- /dev/null +++ b/mkosi.profiles/azure/mkosi.postoutput @@ -0,0 +1,57 @@ +#!/bin/bash +set -euxo pipefail + +OUTPUT="tdx-debian-azure" +EFI_FILE="${OUTPUTDIR}/${OUTPUT}.efi" +VHD_FILE="${OUTPUTDIR}/${OUTPUT}.vhd" +WORK_DIR="${OUTPUTDIR}/azure-tmp" + +if [ ! -f "$EFI_FILE" ]; then + echo "Error: EFI file not found at $EFI_FILE" + exit 1 +fi + +echo "Converting $EFI_FILE to VHD format..." + +# Create working directory +mkdir -p "$WORK_DIR" + +# Create ESP filesystem image (500MB should be plenty) +ESP_SIZE_MB=500 +ESP_IMAGE="$WORK_DIR/esp.img" + +# Create empty ESP image and format it as FAT32 +dd if=/dev/zero of="$ESP_IMAGE" bs=1M count=$ESP_SIZE_MB +mformat -i "$ESP_IMAGE" -F -v "ESP" :: + +# Create EFI directory structure and copy the UKI file using mtools +mmd -i "$ESP_IMAGE" ::EFI +mmd -i "$ESP_IMAGE" ::EFI/BOOT +mcopy -i "$ESP_IMAGE" "$EFI_FILE" ::EFI/BOOT/BOOTX64.EFI + +# Create the final disk image with GPT +DISK_SIZE_MB=$((ESP_SIZE_MB + 2)) # ESP + 1MB for GPT headers +DISK_IMAGE="$WORK_DIR/azure_image.raw" + +# Create empty disk +dd if=/dev/zero of="$DISK_IMAGE" bs=1M count=$DISK_SIZE_MB + +# Create GPT partition table and ESP partition +# Use sector size of 512 bytes, so 1MB = 2048 sectors +parted "$DISK_IMAGE" --script -- \ + mklabel gpt \ + mkpart ESP fat32 2048s $(($ESP_SIZE_MB * 2048 + 2047))s \ + set 1 boot on + +# Copy the ESP filesystem into the partition +# Skip first 1MB (2048 sectors) to account for GPT header +dd if="$ESP_IMAGE" of="$DISK_IMAGE" bs=512 seek=2048 conv=notrunc + +# Convert to VHD +truncate -s %1MiB "$DISK_IMAGE" +qemu-img convert -O vpc -o subformat=fixed,force_size "$DISK_IMAGE" "$VHD_FILE" + +# Clean up +rm -rf "$WORK_DIR" + +echo "Successfully created VHD: $VHD_FILE" diff --git a/mkosi.profiles/devtools/mkosi.conf b/mkosi.profiles/devtools/mkosi.conf new file mode 100644 index 0000000..da74e61 --- /dev/null +++ b/mkosi.profiles/devtools/mkosi.conf @@ -0,0 +1,12 @@ +[Content] +SkeletonTrees=serial-console.service:/etc/systemd/system/serial-console.service +Packages=socat + iputils-ping + dnsutils + strace + netcat-openbsd + tcpdump + net-tools + curl + apt + vim diff --git a/mkosi.profiles/devtools/mkosi.postinst b/mkosi.profiles/devtools/mkosi.postinst new file mode 100755 index 0000000..d13b0be --- /dev/null +++ b/mkosi.profiles/devtools/mkosi.postinst @@ -0,0 +1,3 @@ +#!/bin/sh +mkosi-chroot passwd -u root +echo "root:dqSPjo4p" | mkosi-chroot chpasswd diff --git a/devtools/systemd/serial-console.service b/mkosi.profiles/devtools/serial-console.service similarity index 90% rename from devtools/systemd/serial-console.service rename to mkosi.profiles/devtools/serial-console.service index 5d69d7f..0435263 100644 --- a/devtools/systemd/serial-console.service +++ b/mkosi.profiles/devtools/serial-console.service @@ -14,4 +14,4 @@ Restart=always [Install] WantedBy=minimal.target WantedBy=rescue.target -WantedBy=emergency.target \ No newline at end of file +WantedBy=emergency.target diff --git a/readme.md b/readme.md index f9327fc..e20073e 100644 --- a/readme.md +++ b/readme.md @@ -40,7 +40,11 @@ sudo qemu-system-x86_64 \ -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.secboot.4m.fd \ -drive file=/usr/share/edk2/x64/OVMF_VARS.4m.fd,if=pflash,format=raw \ -kernel build/tdx-debian \ - -drive file=persistent.qcow2,format=qcow2,if=virtio,cache=writeback + -netdev user,id=net0 \ + -device virtio-net-pci,netdev=net0 \ + -device virtio-scsi-pci,id=scsi0 \ + -drive file=persistent.qcow2,format=qcow2,if=none,id=disk0 \ + -device scsi-hd,drive=disk0,bus=scsi0.0,channel=0,scsi-id=0,lun=10 ``` Developing @@ -52,7 +56,7 @@ Just running `mkosi` itself will not trigger a kernel build. To rebuild the kern ```shell exit # if you're currently in the nix develop shell -nix build --rebuild flake.nix # not needed if you only modified kernel.nix +nix-build kernel.nix # not needed if you only modified kernel.nix nix develop -c $SHELL ``` diff --git a/scripts/build_rust_package.sh b/scripts/build_rust_package.sh index 3aae53b..58f313f 100755 --- a/scripts/build_rust_package.sh +++ b/scripts/build_rust_package.sh @@ -54,6 +54,6 @@ build_rust_package() { " # Cache and install the built binary - cp "$build_dir/target/release/$package" "$cached_binary" - cp "$cached_binary" "$dest_path" + install -m 755 "$build_dir/target/release/$package" "$cached_binary" + install -m 755 "$cached_binary" "$dest_path" } \ No newline at end of file diff --git a/scripts/make_git_package.sh b/scripts/make_git_package.sh index f81afe8..d04853d 100644 --- a/scripts/make_git_package.sh +++ b/scripts/make_git_package.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Note env variables: DESTDIR, BUILDROOT, GOCACHE +# Note env variables: DESTDIR, BUILDROOT, GOCACHE, BUILDDIR make_git_package() { local package="$1" @@ -10,29 +10,36 @@ make_git_package() { # All remaining arguments are artifact mappings in src:dest format mkdir -p "$DESTDIR/usr/bin" + local cache_dir="$BUILDDIR/${package}-${version}" - # Clone the repository + # Use cached artifacts if available + if [ -n "$cache_dir" ] && [ -d "$cache_dir" ] && [ "$(ls -A "$cache_dir" 2>/dev/null)" ]; then + echo "Using cached artifacts for $package version $version" + for artifact_map in "${@:5}"; do + local src="${artifact_map%%:*}" + local dest="${artifact_map#*:}" + mkdir -p "$(dirname "$DESTDIR$dest")" + cp "$cache_dir/$(echo "$src" | tr '/' '_')" "$DESTDIR$dest" + done + return 0 + fi + + # Build from source local build_dir="$BUILDROOT/build/$package" git clone --depth 1 --branch "$version" "$git_url" "$build_dir" - - # Build inside mkosi chroot with custom build command mkosi-chroot bash -c "cd '/build/$package' && $build_cmd" - - # Process each artifact mapping + + # Copy artifacts to image and cache for artifact_map in "${@:5}"; do - # Split the mapping into source and destination - local src=$(echo "$artifact_map" | cut -d':' -f1) - local dest=$(echo "$artifact_map" | cut -d':' -f2) - - # Create destination directory if needed + local src="${artifact_map%%:*}" + local dest="${artifact_map#*:}" + + # Copy the built artifact to the destination mkdir -p "$(dirname "$DESTDIR$dest")" - - # Copy the artifact cp "$build_dir/$src" "$DESTDIR$dest" + + # Cache artifact + mkdir -p "$cache_dir" + cp "$build_dir/$src" "$cache_dir/$(echo "$src" | tr '/' '_')" done -} - -# Example usage: -# make_git_package "myapp" "v1.0.0" "https://github.com/user/myapp.git" "make build" \ -# "bin/myapp:/usr/bin/myapp" \ -# "config/myapp.conf:/etc/myapp/myapp.conf" +} \ No newline at end of file diff --git a/scripts/verify.py b/scripts/verify.py deleted file mode 100644 index 5631007..0000000 --- a/scripts/verify.py +++ /dev/null @@ -1,114 +0,0 @@ -import csv -import requests -import json -from typing import Dict, List, Tuple -from datetime import datetime - -def fetch_package_data(url: str) -> Dict[str, dict]: - """Fetch package data from the API and index it by package name and architecture.""" - response = requests.get(url) - response.raise_for_status() - data = response.json() - - # Create a nested dictionary: package_name -> arch -> package_info - indexed_data = {} - for pkg in data: - if pkg['name'] not in indexed_data: - indexed_data[pkg['name']] = {} - indexed_data[pkg['name']][pkg['architecture']] = pkg - - return indexed_data - -def analyze_packages(csv_path: str) -> Tuple[List[dict], List[dict], List[dict]]: - """ - Analyze packages listed in the CSV file against the API data. - Returns: (bad_packages, version_mismatches, all_packages) - """ - # Fetch data from both APIs - print("Fetching package data...") - amd64_data = fetch_package_data('https://amd64.reproduce.debian.net/api/v0/pkgs/list') - all_data = fetch_package_data('https://all.reproduce.debian.net/api/v0/pkgs/list') - - bad_packages = [] - version_mismatches = [] - all_packages = [] - - print("Analyzing packages...") - with open(csv_path, 'r') as f: - reader = csv.reader(f) - for row in reader: - if len(row) != 3: - print(f"Warning: Skipping malformed row: {row}") - continue - - name, arch, expected_version = row - - # Choose the appropriate API data based on architecture - api_data = amd64_data if arch == 'amd64' else all_data - - package_info = None - if name in api_data and arch in api_data[name]: - package_info = api_data[name][arch] - - # Add to all packages list - all_packages.append({ - 'name': name, - 'architecture': arch, - 'version': package_info['version'], - 'status': package_info['status'], - 'built_at': package_info['built_at'] - }) - - # Check for bad status - if package_info['status'] == 'BAD': - bad_packages.append(package_info) - - # Check version mismatch - if package_info['version'] != expected_version: - version_mismatches.append({ - 'name': name, - 'architecture': arch, - 'expected_version': expected_version, - 'actual_version': package_info['version'] - }) - else: - print(f"Warning: Package not found in API data: {name} ({arch})") - - return bad_packages, version_mismatches, all_packages - -def print_report(bad_packages: List[dict], version_mismatches: List[dict], all_packages: List[dict]): - """Print a formatted report of the analysis results.""" - print("\n=== Package Analysis Report ===\n") - - # Overall statistics - total_packages = len(all_packages) - bad_count = len(bad_packages) - good_count = total_packages - bad_count - - print(f"Total packages analyzed: {total_packages}") - print(f"Good packages: {good_count} ({(good_count/total_packages*100):.1f}%)") - print(f"Bad packages: {bad_count} ({(bad_count/total_packages*100):.1f}%)") - - # List bad packages - if bad_packages: - print("\nBad Packages:") - for pkg in bad_packages: - built_at = datetime.fromisoformat(pkg['built_at'].replace('Z', '+00:00')) - print(f"- {pkg['name']} ({pkg['architecture']}) version {pkg['version']}") - print(f" Built at: {built_at.strftime('%Y-%m-%d %H:%M:%S UTC')}") - - # List version mismatches - if version_mismatches: - print("\nVersion Mismatches:") - for mismatch in version_mismatches: - print(f"- {mismatch['name']} ({mismatch['architecture']})") - print(f" Expected: {mismatch['expected_version']}") - print(f" Actual: {mismatch['actual_version']}") - -def main(): - csv_path = 'build/packages.csv' - bad_packages, version_mismatches, all_packages = analyze_packages(csv_path) - print_report(bad_packages, version_mismatches, all_packages) - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/services/bin/lighthouse-init b/services/bin/lighthouse-init index c39fd98..7e14d1c 100755 --- a/services/bin/lighthouse-init +++ b/services/bin/lighthouse-init @@ -4,8 +4,10 @@ set -e # Generate JWT token if it doesn't exist if [ ! -f /tmp/jwt.hex ]; then openssl rand -hex 32 | tr -d "\n" | tee /tmp/jwt.hex - chmod 640 /tmp/jwt.hex + chown lighthouse:eth /tmp/jwt.hex + chmod 644 /tmp/jwt.hex fi # Create and setup persistent directory -mkdir -p /persistent/lighthouse \ No newline at end of file +mkdir -p /persistent/lighthouse +chown lighthouse:eth /persistent/lighthouse \ No newline at end of file diff --git a/services/bin/rbuilder-init b/services/bin/rbuilder-init index 8a3612d..94e4ff2 100755 --- a/services/bin/rbuilder-init +++ b/services/bin/rbuilder-init @@ -3,7 +3,7 @@ set -e # Create necessary directories mkdir -p /var/run/rbuilder /persistent/rbuilder -chown -R rbuilder:eth /var/run/rbuilder /persistent/rbuilder +chown -R rbuilder:eth /var/run/rbuilder /persistent/rbuilder /etc/rbuilder.config chmod 640 /etc/rbuilder.config chmod 770 /var/run/rbuilder @@ -11,5 +11,5 @@ chmod 770 /var/run/rbuilder if [ ! -f /persistent/rbuilder/rbuilder.blocklist.json ]; then echo '{}' > /persistent/rbuilder/rbuilder.blocklist.json chmod 640 /persistent/rbuilder/rbuilder.blocklist.json - chown rbuilder:rbuilder /persistent/rbuilder/rbuilder.blocklist.json + chown rbuilder:eth /persistent/rbuilder/rbuilder.blocklist.json fi \ No newline at end of file diff --git a/base/mkosi.skeleton/etc/systemd/system/persistence-setup.service b/services/systemd/persistence-setup.service similarity index 100% rename from base/mkosi.skeleton/etc/systemd/system/persistence-setup.service rename to services/systemd/persistence-setup.service diff --git a/services/systemd/rbuilder-bidding.service b/services/systemd/rbuilder-bidding.service index d0c2131..8d03924 100644 --- a/services/systemd/rbuilder-bidding.service +++ b/services/systemd/rbuilder-bidding.service @@ -1,14 +1,13 @@ [Unit] Description=RBuilder Bidding Service -After=network-setup.service persistence-setup.service -ConditionPathExists=/persistent +After=network.target network-setup.service persistent-mount.service rbuilder.service +Requires=network-setup.service persistent-mount.service rbuilder.service [Service] Type=exec User=rbuilder Group=eth WorkingDirectory=/var/run/rbuilder -ExecStartPre=/usr/bin/rbuilder-init ExecStart=/usr/bin/bwrap \ --ro-bind /usr /usr \ --ro-bind /lib /lib \ diff --git a/services/systemd/rbuilder.service b/services/systemd/rbuilder.service index 45d5612..764d82b 100644 --- a/services/systemd/rbuilder.service +++ b/services/systemd/rbuilder.service @@ -1,13 +1,13 @@ [Unit] Description=RBuilder Bidding Service -After=network-setup.service persistence-setup.service -ConditionPathExists=/persistent +After=network.target network-setup.service persistent-mount.service +Requires=network-setup.service persistent-mount.service [Service] Type=exec User=rbuilder Group=eth -ExecStartPre=/bin/bash /usr/bin/rbuilder-init.sh +ExecStartPre=+/usr/bin/rbuilder-init ExecStart=/usr/bin/bwrap \ --ro-bind /usr /usr \ --ro-bind /lib /lib \ diff --git a/services/systemd/reth-sync.service b/services/systemd/reth-sync.service index 84e06c4..e47c9c8 100644 --- a/services/systemd/reth-sync.service +++ b/services/systemd/reth-sync.service @@ -1,7 +1,7 @@ [Unit] Description=Reth Chain Data Sync -After=network-setup.service persistence-setup.service -Before=reth.service +After=network.target network-setup.service persistent-mount.service +Requires=network-setup.service persistent-mount.service [Service] User=reth diff --git a/services/systemd/reth.service b/services/systemd/reth.service index b4e38b7..9d41f01 100644 --- a/services/systemd/reth.service +++ b/services/systemd/reth.service @@ -1,9 +1,7 @@ [Unit] Description=Reth Execution Client -After=network-setup.service persistence-setup.service reth-sync.service -Requires=persistence-setup.service -Wants=network-setup.service reth-sync.service -ConditionPathExists=/persistent +After=network-setup.service reth-sync.service persistent-mount.service +Requires=network-setup.service reth-sync.service persistent-mount.service [Service] User=reth diff --git a/tdx-dummy.conf b/tdx-dummy.conf index 462dc4b..c57fa98 100644 --- a/tdx-dummy.conf +++ b/tdx-dummy.conf @@ -1,4 +1,3 @@ -[Config] +[Include] Include=base/base.conf Include=tdx-dummy/tdx-dummy.conf -Include=devtools/devtools.conf