Compare commits

..

1 commit

Author SHA1 Message Date
4a82f52920
WIP: Just a quick hack overlaying systemd from staging-next
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
2025-01-13 17:25:34 +01:00
35 changed files with 1185 additions and 3396 deletions

View file

@ -1,3 +1 @@
nix_direnv_manual_reload
use flake
dotenv_if_exists

2
.gitignore vendored
View file

@ -4,8 +4,6 @@
.task
/result
/target
/out
/initrd.gz
.*.swp
.*.swo
.nixos-test-history

View file

@ -1,165 +0,0 @@
{
lib,
stdenv,
fetchFromGitHub,
pkgs,
...
}:
let
meta = {
maintainers = with lib.maintainers; [ peterhoeg ];
platforms = lib.platforms.linux;
};
dep =
{
pname,
version,
hash,
rev ? "v${version}",
buildInputs ? [ ],
}:
stdenv.mkDerivation {
inherit pname version;
src = fetchFromGitHub {
owner = "c-util";
repo = pname;
inherit hash rev;
};
nativeBuildInputs = with pkgs; [
meson
ninja
pkg-config
];
inherit buildInputs;
meta = meta // {
description = "The C-Util Project is a collection of utility libraries for the C11 language.";
homepage = "https://c-util.github.io/";
license = [
lib.licenses.asl20
lib.licenses.lgpl21Plus
];
};
};
# These libraries are not used outside of dbus-broker.
#
# If that changes, we can always break them out, but they are essentially
# part of the dbus-broker project, just in separate repositories.
c-dvar = dep {
pname = "c-dvar";
version = "1.1.0";
hash = "sha256-p/C+BktclVseCtZJ1Q/YK03vP2ClnYRLB1Vmj2OQJD4=";
buildInputs = [
c-stdaux
c-utf8
];
};
c-ini = dep {
pname = "c-ini";
version = "1.1.0";
hash = "sha256-wa7aNl20hkb/83c4AkQ/0YFDdmBs4XGW+WLUtBWIC98=";
buildInputs = [
c-list
c-rbtree
c-stdaux
c-utf8
];
};
c-list = dep {
pname = "c-list";
version = "3.1.0";
hash = "sha256-fp3EAqcbFCLaT2EstLSzwP2X13pi2EFpFAullhoCtpw=";
};
c-rbtree = dep {
pname = "c-rbtree";
version = "3.2.0";
hash = "sha256-dTMeawhPLRtHvMXfXCrT5iCdoh7qS3v+raC6c+t+X38=";
buildInputs = [ c-stdaux ];
};
c-shquote = dep {
pname = "c-shquote";
version = "1.1.0";
hash = "sha256-z6hpQ/kpCYAngMNfxLkfsxaGtvP4yBMigX1lGpIIzMQ=";
buildInputs = [ c-stdaux ];
};
c-stdaux = dep {
pname = "c-stdaux";
version = "1.5.0";
hash = "sha256-MsnuEyVCmOIr/q6I1qyPsNXp48jxIEcXoYLHbOAZtW0=";
};
c-utf8 = dep {
pname = "c-utf8";
version = "1.1.0";
hash = "sha256-9vBYylbt1ypJwIAQJd/oiAueh+4VYcn/KzofQuhUea0=";
buildInputs = [ c-stdaux ];
};
in
stdenv.mkDerivation (finalAttrs: {
pname = "dbus-broker";
version = "36";
src = fetchFromGitHub {
owner = "bus1";
repo = "dbus-broker";
rev = "v${finalAttrs.version}";
hash = "sha256-5dAMKjybqrHG57vArbtWEPR/svSj2ION75JrjvnnpVM=";
};
nativeBuildInputs = with pkgs; [
docutils
meson
ninja
pkg-config
];
buildInputs = [
c-dvar
c-ini
c-list
c-rbtree
c-shquote
c-stdaux
c-utf8
pkgs.dbus
pkgs.linuxHeaders
pkgs.systemd
];
mesonFlags = [
# while we technically support 4.9 and 4.14, the NixOS module will throw an
# error when using a kernel that's too old
"--prefix=/"
"--bindir=/usr/bin"
"-D=linux-4-17=true"
"-D=system-console-users=gdm,sddm,lightdm"
];
PKG_CONFIG_SYSTEMD_SYSTEMDSYSTEMUNITDIR = "/usr/lib/systemd/system";
PKG_CONFIG_SYSTEMD_SYSTEMDUSERUNITDIR = "/usr/lib/systemd/user";
PKG_CONFIG_SYSTEMD_CATALOGDIR = "/usr/lib/systemd/catalog";
preInstall = ''
export DESTDIR=${placeholder "out"}
'';
postInstall = ''
mkdir -p $out/usr/share
cp -Pr ${pkgs.dbus.out}/share/* $out/usr/share/
cp ${pkgs.dbus.out}/etc/systemd/system/dbus.socket $out/usr/lib/systemd/system/
find $out/usr/share/ -type d -exec chmod 755 {} \;
sed -i 's#/nix/store.*/share#/usr/share#' $out/usr/share/xml/dbus-1/catalog.xml
sed -i 's#/nix/store.*/libexec#/usr/bin#' $out/usr/share/dbus-1/system.conf
'';
doCheck = false;
meta = meta // {
description = "Linux D-Bus Message Broker";
homepage = "https://github.com/bus1/dbus-broker/wiki";
license = lib.licenses.asl20;
};
})

53
flake.lock generated
View file

@ -1,30 +1,12 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1739020877,
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
"lastModified": 1731139594,
"narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
"rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
"type": "github"
},
"original": {
@ -34,26 +16,27 @@
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"nixpkgs-staging-next": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"lastModified": 1736775562,
"narHash": "sha256-4qqVIwZXGezX1X6Hyo+MdU/xhQv4IMKCE4qqa1oXYw4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "71c2fcc94d888c994a0962af2d1d78ae73181c79",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"owner": "NixOS",
"ref": "staging-next",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"nixpkgs-staging-next": "nixpkgs-staging-next"
}
}
},
"root": "root",

132
flake.nix
View file

@ -2,68 +2,96 @@
description = "PatOS is a minimal, immutable Linux distribution specialized for the Patagia Platform.";
inputs = {
flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-staging-next.url = "github:NixOS/nixpkgs/staging-next";
};
outputs =
{ self, nixpkgs, nixpkgs-staging-next }:
let
releaseVersion = "0.0.1";
system = "x86_64-linux";
updateUrl = "https://images.dl.patagia.dev/patos/";
pkgs = import nixpkgs { inherit system; };
in
{
self,
flake-utils,
nixpkgs,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
patosPkgs = self.packages.${system};
in
{
packages = {
default = self.packages.${system}.image;
image = pkgs.writeShellScriptBin "image" ''
set -ex
echo "make UKI..."
nixosModules.devel.imports = [
./modules/profiles/devel.nix
];
mkdir -p patos/efi/boot
${self.packages.${system}.systemd.out}/usr/bin/ukify build \
--linux ${self.packages.${system}.kernel.kernel}/bzImage \
--initrd ./initrd.gz \
--cmdline "console=ttyS0" \
-o patos/efi/boot/bootx64.efi
'';
nixosModules.server.imports = [
./modules/profiles/server.nix
];
kernel = pkgs.callPackage ./kernel { };
glibc = pkgs.callPackage ./glibc { };
systemd = pkgs.callPackage ./systemd { };
dbus-broker = pkgs.callPackage ./dbus-broker { };
rootfs = pkgs.callPackage ./rootfs { inherit patosPkgs; };
nixosModules.image.imports = [
./modules
./modules/profiles/base.nix
./modules/image
];
mkinitrd = pkgs.callPackage ./utils/mkinitrd.nix { inherit patosPkgs; };
qemu-uefi-tpm = pkgs.callPackage ./utils/qemu-uefi-tpm.nix { };
};
packages.${system} = {
devel =
(nixpkgs.lib.nixosSystem {
modules = [
(
{ lib, ... }:
{
nixpkgs.hostPlatform = system;
system.stateVersion = "24.05";
}
)
{
system.image.updates.url = "${updateUrl}";
system.image.id = "patos";
system.image.version = releaseVersion;
image.compress = false;
nixpkgs.overlays = [
(final: prev: {
systemd = nixpkgs-staging-next.legacyPackages.${final.system}.systemd;
})
];
}
self.nixosModules.image
self.nixosModules.devel
];
}).config.system.build.updatePackage;
checks = {
simple-test = pkgs.runCommand "simple-test" { } ''
${self.packages.${system}.default}/bin/my-program
touch $out
'';
};
patos =
(nixpkgs.lib.nixosSystem {
modules = [
(
{ lib, ... }:
{
nixpkgs.hostPlatform = system;
system.stateVersion = "24.05";
}
)
{
system.image.updates.url = "${updateUrl}";
system.image.id = "patos";
system.image.version = releaseVersion;
}
self.nixosModules.image
self.nixosModules.server
];
}).config.system.build.updatePackage;
formatter = pkgs.nixpkgs-fmt;
qemu-uefi-tpm = pkgs.callPackage ./utils/qemu-uefi-tpm.nix { inherit pkgs; };
};
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
erofs-utils
just
nixd
nixfmt-rfc-style
squashfs-tools-ng
self.packages.${system}.qemu-uefi-tpm
self.packages.${system}.mkinitrd
];
};
checks.${system} = {
podman = import ./tests/podman.nix { inherit pkgs self; };
system-update = import ./tests/system-update.nix { inherit pkgs self; };
};
}
);
devShells.${system}.default = pkgs.mkShell {
buildInputs = with pkgs; [
erofs-utils
just
self.packages.${system}.qemu-uefi-tpm
squashfs-tools-ng
];
};
};
}

View file

@ -1,53 +0,0 @@
{
pkgs,
stdenv,
...
}:
let
version = pkgs.glibc.version;
src = pkgs.glibc.src;
pname = "glibcPatos";
in
stdenv.mkDerivation (finalAttrs: {
inherit version;
inherit src;
inherit pname;
enableParallelBuilding = true;
dontPatchShebangs = true;
configureFlags = [
"--prefix=/"
"--libdir=/lib"
"--bindir=/bin"
"--sysconfdir=/etc"
];
preConfigure =
''
export PWD_P=$(type -tP pwd)
for i in configure io/ftwtest-sh; do
sed -i "$i" -e "s^/bin/pwd^$PWD_P^g"
done
mkdir ../build
cd ../build
configureScript="`pwd`/../$sourceRoot/configure"
'';
nativeBuildInputs = with pkgs; [
bison
python3Minimal
];
outputs = [
"out"
];
preInstall = ''
export DESTDIR=${placeholder "out"}
'';
})

View file

@ -1,16 +0,0 @@
{ pkgs, ... }:
let
version = "6.13.2";
in
pkgs.linuxPackagesFor (
pkgs.linuxManualConfig {
version = "${version}-patos1";
modDirVersion = version;
src = pkgs.fetchurl {
url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${version}.tar.xz";
hash = "sha256-zfYpgZBru+lwGutzxPn8yAegmEbCiHMWY9YnF+0a5wU=";
};
configfile = ./generic.config;
allowImportFromDerivation = true;
}
)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
{ config, ... }:
{
boot = {
bootspec.enable = false;
initrd.kernelModules = config.boot.kernelModules;
kernel.enable = false; # No kernel or modules in the rootfs
modprobeConfig.enable = false;
};
system.build = {
inherit (config.boot.kernelPackages) kernel;
};
system.modulesTree = [ config.boot.kernelPackages.kernel ] ++ config.boot.extraModulePackages;
}

View file

@ -0,0 +1,26 @@
{ ... }:
{
nixpkgs.overlays = [
(final: prev: {
composefs = final.callPackage ../../pkgs/composefs.nix { inherit prev; };
qemu_tiny = final.callPackage ../../pkgs/qemu.nix { inherit prev; };
systemdUkify = final.callPackage ../../pkgs/systemd-ukify.nix { inherit prev; };
# # FIXME: Revisit + refine these below in a future image minimization effort
#
# util-linux = prev.util-linux.override {
# ncursesSupport = false;
# nlsSupport = false;
# };
#
# dbus = prev.dbus.override {
# enableSystemd = false;
# x11Support = false;
# };
})
];
}

6
modules/default.nix Normal file
View file

@ -0,0 +1,6 @@
{
imports = [
./config/minimal-modules.nix
./config/minimal-system.nix
];
}

196
modules/image/builder.nix Normal file
View file

@ -0,0 +1,196 @@
{
config,
lib,
options,
pkgs,
...
}:
let
inherit (pkgs.stdenv.hostPlatform) efiArch;
initialPartitions = {
"10-root" = {
storePaths = [ config.system.build.toplevel ];
repartConfig = {
Type = "root";
Minimize = "best";
Format = "erofs";
MakeDirectories = "/home /root /etc /dev /sys /bin /var /proc /run /usr /srv /tmp /mnt /lib /efi";
Verity = "data";
VerityMatchKey = "root";
SplitName = "root";
};
};
"20-root-verity" = {
repartConfig = {
Type = "root-verity";
Minimize = "best";
Verity = "hash";
VerityMatchKey = "root";
SplitName = "verity";
};
};
};
# TODO: We don't need a combined image here - add dry-run flag to repart invocation
verityRepart = import (pkgs.path + "/nixos/lib/eval-config.nix") {
inherit lib pkgs;
system = null;
modules = [
(
{ modulesPath, ... }:
{
imports = [ (modulesPath + "/image/repart.nix") ];
image.repart = {
name = "verity";
split = true;
mkfsOptions = lib.mkIf config.image.compress {
erofs = [
"-zlz4hc,level=12"
"-Efragments,dedupe,ztailpacking"
];
};
partitions = initialPartitions;
};
}
)
];
};
rootPart = "${verityRepart.config.system.build.image}/${verityRepart.config.image.repart.imageFileBasename}.root.raw";
verityPart = "${verityRepart.config.system.build.image}/${verityRepart.config.image.repart.imageFileBasename}.verity.raw";
verityImgAttrs = builtins.fromJSON (
builtins.readFile "${verityRepart.config.system.build.image}/repart-output.json"
);
rootAttrs = builtins.elemAt verityImgAttrs 0;
verityAttrs = builtins.elemAt verityImgAttrs 1;
rootUuid = rootAttrs.uuid;
verityUuid = verityAttrs.uuid;
verityRootHash = rootAttrs.roothash;
finalPartitions = {
"10-esp" = {
contents = {
"/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source = "${pkgs.systemdUkify}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
"/EFI/Linux/${config.system.boot.loader.ukiFile}".source = "${config.system.build.uki}/${config.system.boot.loader.ukiFile}";
"/EFI/memtest86/memtest86.efi".source = "${pkgs.memtest86plus}/memtest.efi";
"/loader/entries/patos-factory-reset.conf".source = pkgs.writeText "patos-factory-reset.conf" ''
title Patos Factory Reset
efi /EFI/Linux/${config.system.boot.loader.ukiFile}
options ${toString config.boot.kernelParams} systemd.factory_reset=yes
sort-key z_factory_reset
'';
"/loader/entries/memtest86.conf".source = pkgs.writeText "memtest86.conf" ''
title Memtest86+
efi /EFI/memtest86/memtest86.efi
options console=ttyS0
sort-key z_memtest
'';
"/loader/loader.conf".source = pkgs.writeText "loader.conf" ''
timeout 2
'';
};
repartConfig = {
Type = "esp";
Format = "vfat";
SizeMinBytes = "96M";
SizeMaxBytes = "96M";
SplitName = "-";
};
};
"20-root-verity-a" = {
repartConfig = {
Type = "root-verity";
Label = "verity-${config.system.image.version}";
CopyBlocks = "${verityPart}";
SplitName = "-";
SizeMinBytes = "64M";
SizeMaxBytes = "64M";
UUID = "${verityUuid}";
ReadOnly = 1;
};
};
# TODO: Add signature partition for systemd-nspawn
"22-root-a" = {
repartConfig = {
Type = "root";
Label = "root-${config.system.image.version}";
CopyBlocks = "${rootPart}";
SplitName = "-";
UUID = "${rootUuid}";
ReadOnly = 1;
};
};
};
finalRepart = import (pkgs.path + "/nixos/lib/eval-config.nix") {
inherit lib pkgs;
system = null;
modules = [
(
{ modulesPath, ... }:
{
imports = [ (modulesPath + "/image/repart.nix") ];
image.repart = {
name = "${config.system.image.id}";
partitions = finalPartitions;
};
}
)
];
};
in
{
# This fields is immutable by default, but can be overridden.
options.system.nixos.codeName = lib.mkOption { readOnly = false; };
options.system.nixos.release = lib.mkOption { readOnly = false; };
# FIXME: Should be configured somehow
config.system.nixos = {
codeName = "Finn";
distroId = "patos";
distroName = "PatOS";
release = "2024-11";
variant_id = "server";
variantName = "Server";
vendorName = "PatOS";
};
options.image.compress = lib.mkEnableOption "image compression" // {
default = true;
};
config.system.build = {
inherit verityRootHash;
image =
(pkgs.linkFarm "image-release" [
{
name = "${config.system.image.id}_${config.system.image.version}.efi";
path = "${config.system.build.uki}/${config.system.boot.loader.ukiFile}";
}
{
name = "${config.system.image.id}_${config.system.image.version}_${verityUuid}.verity";
path = "${verityRepart.config.system.build.image}/${verityRepart.config.image.repart.imageFileBasename}.verity.raw";
}
{
name = "${config.system.image.id}_${config.system.image.version}_${rootUuid}.root";
path = "${verityRepart.config.system.build.image}/${verityRepart.config.image.repart.imageFileBasename}.root.raw";
}
{
name = "${config.system.image.id}_${config.system.image.version}.img";
path = "${finalRepart.config.system.build.image}/${finalRepart.config.image.repart.imageFileBasename}.raw";
}
])
// {
imageFile = "${config.system.image.id}_${config.system.image.version}.img";
};
};
}

139
modules/image/default.nix Normal file
View file

@ -0,0 +1,139 @@
{
config,
lib,
pkgs,
...
}:
{
imports = [
./updater.nix
./builder.nix
./veritysetup.nix
];
system.build.updatePackage = pkgs.runCommand "update-package" { } ''
mkdir "$out"
cd "$out"
cp "${config.system.build.image}"/* .
${pkgs.coreutils}/bin/sha256sum * > SHA256SUMS
'';
systemd.repart.partitions = {
"10-esp" = {
Type = "esp";
Format = "vfat";
SizeMinBytes = "96M";
SizeMaxBytes = "96M";
};
"20-root-verity-a" = {
Type = "root-verity";
SizeMinBytes = "64M";
SizeMaxBytes = "64M";
};
"22-root-a" = {
Type = "root";
SizeMinBytes = "512M";
SizeMaxBytes = "512M";
};
"30-root-verity-b" = {
Type = "root-verity";
SizeMinBytes = "64M";
SizeMaxBytes = "64M";
Label = "_empty";
ReadOnly = 1;
};
"32-root-b" = {
Type = "root";
SizeMinBytes = "512M";
SizeMaxBytes = "512M";
Label = "_empty";
ReadOnly = 1;
};
"40-var" = {
Type = "var";
UUID = "4d21b016-b534-45c2-a9fb-5c16e091fd2d"; # Well known
Format = "btrfs";
Label = "patos-state";
Minimize = "off";
FactoryReset = "yes";
# FIXME: borked w/systemd 257?
# Encrypt = "tpm2";
SizeMinBytes = "2G";
SplitName = "-";
};
};
boot.loader.grub.enable = false;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.enable = true;
boot.uki.name = "patos";
boot.initrd = {
compressor = "zstd";
compressorArgs = [ "-8" ];
luks.forceLuksSupportInInitrd = true;
kernelModules = [
"dm_mod"
"dm_crypt"
] ++ config.boot.initrd.luks.cryptoModules;
supportedFilesystems = {
btrfs = true;
erofs = true;
};
systemd.enable = true;
systemd.repart.enable = true;
systemd.services.systemd-repart = {
after = lib.mkForce [ "sysroot.mount" ];
requires = [ "sysroot.mount" ];
serviceConfig.Environment = [
"SYSTEMD_REPART_MKFS_OPTIONS_BTRFS=--nodiscard"
];
};
};
system.etc.overlay.mutable = false;
users.mutableUsers = false;
boot.kernelParams = [
"rootfstype=erofs"
"rootflags=ro"
"roothash=${config.system.build.verityRootHash}"
];
fileSystems =
let
parts = config.systemd.repart.partitions;
in
{
"/var" = {
fsType = parts."40-var".Format;
device = "/dev/disk/by-partuuid/${parts."40-var".UUID}";
# FIXME: borked w/systemd 257?
# device = "/dev/mapper/var";
# encrypted = {
# enable = true;
# blkDev = "/dev/disk/by-partuuid/${parts."40-var".UUID}";
# label = "var";
# };
};
};
# Required to mount the efi partition
boot.kernelModules = [
"vfat"
"nls_cp437"
"nls_iso8859-1"
];
environment.etc."machine-id" = {
text = "";
mode = "0755";
};
# Refuse to boot on mount failure
systemd.targets."sysinit".requires = [ "local-fs.target" ];
}

87
modules/image/updater.nix Normal file
View file

@ -0,0 +1,87 @@
{ config, lib, ... }:
{
options.system.image.updates = {
enable = lib.mkEnableOption "system updates via systemd-sysupdate" // {
default = config.system.image.updates.url != null;
};
url = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
};
config = lib.mkIf config.system.image.updates.enable {
assertions = [
{ assertion = config.system.image.updates.url != null; }
];
systemd.sysupdate.enable = true;
systemd.sysupdate.reboot.enable = lib.mkDefault true;
systemd.sysupdate.transfers = {
"10-uki" = {
Transfer = {
Verify = "no";
};
Source = {
Type = "url-file";
Path = "${config.system.image.updates.url}";
MatchPattern = "${config.boot.uki.name}_@v.efi";
};
Target = {
Type = "regular-file";
Path = "/EFI/Linux";
PathRelativeTo = "esp";
MatchPattern = "${config.boot.uki.name}_@v+@l-@d.efi ${config.boot.uki.name}_@v+@l.efi ${config.boot.uki.name}_@v.efi";
Mode = "0444";
TriesLeft = 3;
TriesDone = 0;
InstancesMax = 2;
};
};
"20-root-verity" = {
Transfer = {
Verify = "no";
};
Source = {
Type = "url-file";
Path = "${config.system.image.updates.url}";
MatchPattern = "${config.system.image.id}_@v_@u.verity";
};
Target = {
Type = "partition";
Path = "auto";
MatchPattern = "verity-@v";
MatchPartitionType = "root-verity";
ReadOnly = 1;
};
};
"22-root" = {
Transfer = {
Verify = "no";
};
Source = {
Type = "url-file";
Path = "${config.system.image.updates.url}";
MatchPattern = "${config.system.image.id}_@v_@u.root";
};
Target = {
Type = "partition";
Path = "auto";
MatchPattern = "root-@v";
MatchPartitionType = "root";
ReadOnly = 1;
};
};
};
systemd.additionalUpstreamSystemUnits = [
"systemd-bless-boot.service"
"boot-complete.target"
];
};
}

View file

@ -0,0 +1,39 @@
{ config, lib, ... }:
{
options.boot.initrd.systemd.root = lib.mkOption {
type = lib.types.enum [
"fstab"
"gpt-auto"
""
];
};
config.boot.initrd = {
kernelModules = [
"dm_mod"
"dm_verity"
];
systemd = {
# Required to activate systemd-fstab-generator
root = "";
additionalUpstreamUnits = [
"veritysetup-pre.target"
"veritysetup.target"
"remote-veritysetup.target"
];
storePaths = [
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-veritysetup"
"${config.boot.initrd.systemd.package}/lib/systemd/system-generators/systemd-veritysetup-generator"
];
};
};
}

95
modules/profiles/base.nix Normal file
View file

@ -0,0 +1,95 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/profiles/image-based-appliance.nix")
(modulesPath + "/profiles/perlless.nix")
(modulesPath + "/profiles/qemu-guest.nix")
];
# system.forbiddenDependenciesRegexes = lib.mkForce [ ];
nixpkgs.flake.setNixPath = false;
nixpkgs.flake.setFlakeRegistry = false;
boot.enableContainers = false;
boot.kernelModules = [
"zram"
"usb_storage"
"uas"
"sd_mod"
"r8169"
"ehci-hcd"
"ehci-pci"
"xhci-hcd"
"xhci-pci"
"xhci-pci-renesas"
"nvme"
"virtio_net"
];
system.etc.overlay.mutable = lib.mkDefault false;
systemd.watchdog = lib.mkDefault {
runtimeTime = "10s";
rebootTime = "30s";
};
zramSwap.enable = true;
# FIXME: fstrim should only be enabled for virtual machine images?
services.fstrim.enable = true;
users.allowNoPasswordLogin = true;
users.users.root.home = lib.mkForce "/";
security.sudo.enable = lib.mkDefault false;
security.polkit = {
enable = true;
extraConfig = ''
polkit.addRule(function(action, subject) {
if (subject.isInGroup("wheel")) {
return polkit.Result.YES;
}
});
'';
};
i18n.supportedLocales = [ "en_US.UTF-8/UTF-8" ];
# Console
systemd.enableEmergencyMode = false;
boot.consoleLogLevel = lib.mkDefault 1;
boot.kernelParams = [
"panic=1"
"boot.panic_on_fail"
# "nomodeset"
"console=ttyS0,115200n8"
"earlyprintk=ttyS0,115200n8"
"systemd.mask=systemd-vconsole-setup.service" # FIXME: Figure out why vconsole-setup fails when loading keymap
];
# This is vi country
programs.nano.enable = false;
programs.vim.enable = true;
programs.vim.defaultEditor = lib.mkDefault true;
# Temporary file
boot.tmp.useTmpfs = true;
# Logging
services.journald = {
storage = "volatile";
extraConfig = ''
SystemMaxUse=10M
'';
};
}

View file

@ -0,0 +1,39 @@
{
modulesPath,
...
}:
{
imports = [ ./server.nix ];
boot.kernel.sysctl = {
"net.ipv4.ip_unprivileged_port_start" = 0;
};
boot.kernelParams = [
"systemd.log_level=info"
"systemd.log_target=console"
"systemd.journald.forward_to_console"
];
users.users."admin" = {
isNormalUser = true;
linger = true;
extraGroups = [ "wheel" ];
home = "/var/home/admin";
};
environment.etc = {
subuid = {
text = "admin:100000:65536";
mode = "0644";
};
subgid = {
text = "admin:100000:65536";
mode = "0644";
};
};
services.getty.autologinUser = "admin";
}

View file

@ -0,0 +1,65 @@
{ lib, ... }:
{
# Use networkd
networking.useNetworkd = true;
systemd.network.wait-online.enable = true;
# Firewall
networking.firewall.enable = false;
networking.nftables.enable = lib.mkDefault true;
# DNS
services.resolved = {
fallbackDns = [ ]; # Disable fallback DNS. DNS will fail if resolvers are unconfigured
extraConfig = ''
DNSStubListener=no
'';
};
# Configuration
networking.hostName = "";
# Kernel
boot.kernel.sysctl = {
"net.core.default_qdisc" = "fq"; # FIXME: manage these with networkd?
"net.ipv4.tcp_congestion_control" = "bbr";
};
# Modules
boot.kernelModules = [
"ip_tables"
"x_tables"
"nf_tables"
"nft_ct"
"nft_log"
"nf_log_syslog"
"nft_fib"
"nft_fib_inet"
"nft_compat"
"nft_nat"
"nft_chain_nat"
"nft_masq"
"nfnetlink"
"xt_conntrack"
"nf_conntrack"
"nf_log_syslog"
"nf_nat"
"af_packet"
"bridge"
"veth"
"tcp_bbr"
"sch_fq_codel"
"ipt_rpfilter"
"ip6t_rpfilter"
"sch_fq"
"tun"
"tap"
"xt_MASQUERADE"
"xt_mark"
"xt_comment"
"xt_multiport"
"xt_addrtype"
];
}

View file

@ -0,0 +1,18 @@
{
modulesPath,
...
}:
{
imports = [
(modulesPath + "/profiles/minimal.nix")
./network.nix
./sysext.nix
];
boot.kernelParams = [
"quiet"
];
virtualisation.podman.enable = true;
}

View file

@ -0,0 +1,23 @@
{ ... }:
{
system.activationScripts.sysext = ''
mkdir -p /var/lib/confexts
mkdir -p /var/lib/extensions
mkdir -p /etc/systemd/extensions
'';
systemd.additionalUpstreamSystemUnits = [
"systemd-confext.service"
"systemd-sysext.service"
];
# systemd.services."systemd-confext" = {
# enable = true;
# wantedBy = [ "multi-user.target" ];
# };
# systemd.services."systemd-sysext.service" = {
# enable = true;
# wantedBy = [ "multi-user.target" ];
# };
}

5
pkgs/composefs.nix Normal file
View file

@ -0,0 +1,5 @@
{ prev, ... }:
prev.composefs.overrideAttrs (final: prev: {
doCheck = false;
})

12
pkgs/linux-firmware.nix Normal file
View file

@ -0,0 +1,12 @@
{ stdenv, lib
, linux-firmware
, fwDirs
}: stdenv.mkDerivation {
pname = "linux-firmware-minimal";
version = linux-firmware.version;
buildCommand = lib.concatStringsSep "\n" (
[''mkdir -p "$out/lib/firmware"'']
++ (map (name: ''
cp -r "${linux-firmware}/lib/firmware/${name}" "$out/lib/firmware/${name}"
'') fwDirs));
}

30
pkgs/qemu.nix Normal file
View file

@ -0,0 +1,30 @@
{ prev, pkgs, ... }:
(prev.qemu_test.override {
enableDocs = false;
capstoneSupport = false;
guestAgentSupport = false;
tpmSupport = false;
libiscsiSupport = false;
usbredirSupport = false;
canokeySupport = false;
hostCpuTargets = [ "x86_64-softmmu" ];
}).overrideDerivation (old: {
postFixup = ''
rm -r "$out/share/icons"
cp "${pkgs.OVMF.fd + "/FV/OVMF.fd"}" "$out/share/qemu/"
'';
configureFlags = old.configureFlags ++ [
"--disable-tcg"
"--disable-tcg-interpreter"
"--disable-docs"
"--disable-install-blobs"
"--disable-slirp"
"--disable-virtfs"
"--disable-virtfs-proxy-helper"
"--disable-vhost-user-blk-server"
"--without-default-features"
"--enable-kvm"
"--disable-tools"
];
})

48
pkgs/systemd-ukify.nix Normal file
View file

@ -0,0 +1,48 @@
{ prev, ... }:
prev.systemd.override {
withAcl = false;
withAnalyze = false;
withApparmor = false;
withAudit = false;
withEfi = true;
withCompression = false;
withCoredump = false;
withCryptsetup = false;
withRepart = false;
withDocumentation = false;
withFido2 = false;
withFirstboot = false;
withHomed = false;
withHostnamed = false;
withHwdb = false;
withImportd = false;
withIptables = false;
withKmod = false;
withLibBPF = false;
withLibidn2 = false;
withLocaled = false;
withLogind = false;
withMachined = false;
withNetworkd = false;
withNss = false;
withOomd = false;
withPam = false;
withPasswordQuality = false;
withPCRE2 = false;
withPolkit = false;
withPortabled = false;
withQrencode = false;
withRemote = false;
withResolved = false;
withShellCompletions = false;
withSysusers = false;
withSysupdate = false;
withTimedated = false;
withTimesyncd = false;
withTpm2Tss = false;
withUkify = true;
withUserDb = false;
withUtmp = false;
withVmspawn = false;
}

10
pkgs/systemd.nix Normal file
View file

@ -0,0 +1,10 @@
{ prev, ... }:
prev.systemd.override {
withAcl = false;
withApparmor = false;
withDocumentation = false;
withRemote = false;
withShellCompletions = false;
withVmspawn = false;
}

View file

@ -1,30 +0,0 @@
{
pkgs,
stdenvNoCC,
patosPkgs,
...
}:
let
version = "0.0.1";
pname = "patos-rootfs";
in
stdenvNoCC.mkDerivation (finalAttrs: {
inherit version;
inherit pname;
buildInputs = with pkgs; [
glibc
binutils
];
glibcPatos = patosPkgs.glibc.out;
systemd = patosPkgs.systemd.out;
dbusBroker = patosPkgs.dbus-broker.out;
kernel = patosPkgs.kernel.kernel;
busybox = pkgs.busybox.out;
kmodLibs = pkgs.kmod.lib;
kmodBin = pkgs.kmod.out;
libbpf = pkgs.libbpf.out;
builder = ./mkrootfs.sh;
})

View file

@ -1,54 +0,0 @@
set -ex -o pipefail
mkdir -p $out
mkdir -p $out/etc $out/dev $out/proc $out/sys $out/tmp $out/root
ln -sf ../usr/bin $out/bin
ln -sf ../usr/bin $out/sbin
ln -sf ../usr/lib $out/lib
ln -sf ../usr/lib $out/lib64
ln -sf ../proc/self/mounts $out/etc/mtab
### install systemd
echo "Installing systemd"
cp -Pr $systemd/* $out/
find $out -type d -exec chmod 755 {} \;
rm -rf $out/usr/include
rm -rf $out/usr/sbin
rm -f $out/usr/lib/systemd/system/sysinit.target.wants/systemd-firstboot.service
# remove vconsole setup
rm -f $out/usr/lib/udev/rules.d/90-vconsole.rules
### install PatOS glibc
cp -P $glibcPatos/lib/*.so* $out/usr/lib/
### install kernel modules
cp -r $kernel/lib/modules $out/usr/lib/
find $out/usr/lib/modules -type d -exec chmod 755 {} \;
### install busybox
cp $busybox/bin/busybox $out/usr/bin/
$out/usr/bin/busybox --list | xargs -I {} ln -sf busybox $out/usr/bin/{}
### install dbus broker
cp -r $dbusBroker/* $out/
### install lib kmod
cp -P $kmodLibs/lib/* $out/usr/lib
cp -P $kmodBin/bin/* $out/usr/bin
### install libbpf
cp -P $libbpf/lib/libbpf* $out/usr/lib
### Find and install all shared libs
find $out -type f -executable -exec ldd {} \; | awk '{print $3}' | grep -v systemd | grep -v glibc | sort -u | xargs cp -t $out/usr/lib
find $out -type f -executable -exec chmod 755 {} \;
# FIXME: ELF patching. Is there a better way?
find $out -type f -executable -exec patchelf --set-rpath /lib:/usr/lib:/usr/lib/systemd {} \;
find $out -type f -executable -exec patchelf --set-interpreter /lib/ld-linux-x86-64.so.2 {} \;
patchelf --remove-rpath $out/usr/lib/ld-linux-x86-64.so.2
# strip binaries
find $out -type f -executable -exec strip {} \;
find $out -type d -exec chmod 755 {} \;

View file

@ -1,21 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nikstur <nikstur@outlook.com>
Date: Mon, 6 Nov 2023 22:51:38 +0100
Subject: [PATCH] meson.build: do not create systemdstatedir
---
meson.build | 1 -
1 file changed, 1 deletion(-)
diff --git a/meson.build b/meson.build
index bffda86845..cb5dcec0f9 100644
--- a/meson.build
+++ b/meson.build
@@ -2781,7 +2781,6 @@ install_data('LICENSE.GPL2',
install_subdir('LICENSES',
install_dir : docdir)
-install_emptydir(systemdstatedir)
#####################################################################

View file

@ -1,324 +0,0 @@
{
fetchFromGitHub,
lib,
pkgs,
stdenv,
targetPackages,
...
}:
let
version = "257.3";
# Use the command below to update `releaseTimestamp` on every (major) version
# change. More details in the commentary at mesonFlags.
# command:
# $ curl -s https://api.github.com/repos/systemd/systemd/releases/latest | \
# jq '.created_at|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime'
releaseTimestamp = "1734643670";
pname = "systemd";
in
stdenv.mkDerivation (finalAttrs: {
inherit version;
pname = pname;
src = fetchFromGitHub {
owner = "systemd";
repo = "systemd";
rev = "v${version}";
hash = "sha256-GvRn55grHWR6M+tA86RMzqinuXNpPZzRB4ApuGN/ZvU=";
};
dontCheckForBrokenSymlinks = true;
patches = [
./0017-meson.build-do-not-create-systemdstatedir.patch
];
nativeBuildInputs = with pkgs; [
bash
pkg-config
makeBinaryWrapper
gperf
ninja
meson
glibcLocales
getent
m4
autoPatchelfHook
intltool
gettext
libxslt
docbook_xsl
docbook_xml_dtd_42
docbook_xml_dtd_45
bash
(buildPackages.python3Packages.python.withPackages (
ps: with ps; [
lxml
jinja2
ps.pyelftools
]
))
bpftools
buildPackages.llvmPackages.clang
buildPackages.llvmPackages.libllvm
];
outputs = [
"out"
"dev"
];
separateDebugInfo = true;
autoPatchelfFlags = [ "--keep-libc" ];
hardeningDisable = [
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111523
"trivialautovarinit"
# breaks clang -target bpf; should be fixed to filter target?
"zerocallusedregs"
"shadowstack"
];
buildInputs = with pkgs; [
libxcrypt
libcap
libuuid
linuxHeaders
bashInteractive # for patch shebangs
libgcrypt
libgpg-error
openssl
acl
libapparmor
audit
zlib
bzip2
lz4
xz
zstd
elfutils
kexec-tools
kmod
libidn2
libseccomp
libselinux
iptables
p11-kit
libfido2
pam
pcre2
libbpf
tpm2-tss
qrencode
libarchive
(lib.getDev curl)
(lib.getDev cryptsetup.dev)
(python3Packages.python.withPackages (ps: with ps; [ pefile ]))
(llvmPackages.compiler-rt.override {
doFakeLibgcc = true;
})
];
mesonBuildType = "release";
doCheck = false; # fails a bunch of tests
preConfigure = ''
mesonFlagsArray+=(-Dntp-servers="0.europe.pool.ntp.org 1.europe.pool.ntp.org 2.europe.pool.ntp.org 3.europe.pool.ntp.org")
export LC_ALL="en_US.UTF-8";
'';
postPatch =
''
substituteInPlace meson.build \
--replace "find_program('clang'" "find_program('${stdenv.cc.targetPrefix}clang'"
''
+ ''
substituteInPlace src/ukify/ukify.py \
--replace \
"'readelf'" \
"'${targetPackages.stdenv.cc.bintools.targetPrefix}readelf'" \
--replace \
"/usr/lib/systemd/boot/efi" \
"$out/usr/lib/systemd/boot/efi"
''
# Finally, patch shebangs in scripts used at build time. This must not patch
# scripts that will end up in the output, to avoid build platform references
# when cross-compiling.
+ ''
shopt -s extglob
patchShebangs tools test src/!(rpm|kernel-install|ukify) src/kernel-install/test-kernel-install.sh
'';
# trigger the test -n "$DESTDIR" || mutate in upstreams build system
preInstall = ''
export DESTDIR=${placeholder "out"}
'';
mesonFlags = [
"--prefix=/usr"
"--sysconfdir=/etc"
"--localstatedir=/var"
"--libdir=/usr/lib"
"--bindir=/usr/bin"
"--includedir=/usr/include"
"--localedir=/usr/share/locale"
# Options
# We bump this attribute on every (major) version change to ensure that we
# have known-good value for a timestamp that is in the (not so distant)
# past. This serves as a lower bound for valid system timestamps during
# startup. Systemd will reset the system timestamp if this date is +- 15
# years from the system time.
# See the systemd v250 release notes for further details:
# https://github.com/systemd/systemd/blob/60e930fc3e6eb8a36fbc184773119eb8d2f30364/NEWS#L258-L266
(lib.mesonOption "time-epoch" releaseTimestamp)
(lib.mesonOption "version-tag" version)
(lib.mesonOption "mode" "release")
(lib.mesonOption "tty-gid" "3") # tty in NixOS has gid 3
(lib.mesonOption "kmod-path" "/usr/bin/kmod")
(lib.mesonOption "debug-shell" "/usr/bin/sh")
(lib.mesonOption "pamconfdir" "/etc/pam.d")
(lib.mesonOption "shellprofiledir" "/etc/profile.d")
(lib.mesonOption "dbuspolicydir" "/usr/share/dbus-1/system.d")
(lib.mesonOption "dbussessionservicedir" "/usr/share/dbus-1/services")
(lib.mesonOption "dbussystemservicedir" "/usr/share/dbus-1/system-services")
(lib.mesonOption "setfont-path" "/usr/bin/setfont")
(lib.mesonOption "loadkeys-path" "/usr/bin/loadkeys")
(lib.mesonOption "sulogin-path" "/usr/bin/sulogin")
(lib.mesonOption "nologin-path" "/usr/bin/nologin")
(lib.mesonOption "mount-path" "/usr/bin/mount")
(lib.mesonOption "umount-path" "/usr/bin/umount")
# SBAT
(lib.mesonOption "sbat-distro" "patos")
(lib.mesonOption "sbat-distro-summary" "PatOS")
(lib.mesonOption "sbat-distro-url" "https://patagia.io/")
(lib.mesonOption "sbat-distro-pkgname" pname)
(lib.mesonOption "sbat-distro-version" version)
# Users
(lib.mesonOption "system-uid-max" "999")
(lib.mesonOption "system-gid-max" "999")
# SysVinit
(lib.mesonOption "sysvinit-path" "")
(lib.mesonOption "sysvrcnd-path" "")
# SSH
# Disabled for now until someone makes this work.
(lib.mesonOption "sshconfdir" "no")
(lib.mesonOption "sshdconfdir" "no")
# Features
# Tests
(lib.mesonBool "tests" false)
(lib.mesonEnable "glib" false)
(lib.mesonEnable "dbus" false)
# Compression
(lib.mesonEnable "bzip2" true)
(lib.mesonEnable "lz4" true)
(lib.mesonEnable "xz" true)
(lib.mesonEnable "zstd" true)
(lib.mesonEnable "zlib" true)
# NSS
(lib.mesonEnable "nss-resolve" true)
(lib.mesonBool "nss-myhostname" true)
(lib.mesonBool "nss-systemd" true)
# Cryptsetup
(lib.mesonEnable "libcryptsetup" true)
(lib.mesonEnable "libcryptsetup-plugins" true)
(lib.mesonEnable "p11kit" true)
# FIDO2
(lib.mesonEnable "libfido2" true)
(lib.mesonEnable "openssl" true)
# Password Quality
(lib.mesonEnable "pwquality" false)
(lib.mesonEnable "passwdqc" false)
# Remote
(lib.mesonEnable "remote" false)
(lib.mesonEnable "microhttpd" false)
(lib.mesonEnable "pam" false)
(lib.mesonEnable "acl" true)
(lib.mesonEnable "audit" true)
(lib.mesonEnable "apparmor" true)
(lib.mesonEnable "gcrypt" true)
(lib.mesonEnable "importd" true)
(lib.mesonEnable "homed" false)
(lib.mesonEnable "polkit" true)
(lib.mesonEnable "elfutils" true)
(lib.mesonEnable "libcurl" true)
(lib.mesonEnable "libidn" false)
(lib.mesonEnable "libidn2" true)
(lib.mesonEnable "libiptc" true)
(lib.mesonEnable "repart" true)
(lib.mesonEnable "sysupdate" true)
(lib.mesonEnable "sysupdated" true)
(lib.mesonEnable "seccomp" true)
(lib.mesonEnable "selinux" true)
(lib.mesonEnable "tpm2" true)
(lib.mesonEnable "pcre2" true)
(lib.mesonEnable "bpf-framework" true)
(lib.mesonEnable "bootloader" true)
(lib.mesonEnable "ukify" true)
(lib.mesonEnable "kmod" true)
(lib.mesonEnable "qrencode" true)
(lib.mesonEnable "vmspawn" false)
(lib.mesonEnable "libarchive" true)
(lib.mesonEnable "xenctrl" false)
(lib.mesonEnable "gnutls" false)
(lib.mesonEnable "xkbcommon" false)
(lib.mesonEnable "man" false)
(lib.mesonBool "analyze" true)
(lib.mesonBool "logind" true)
(lib.mesonBool "localed" false)
(lib.mesonBool "hostnamed" true)
(lib.mesonBool "machined" true)
(lib.mesonBool "networkd" true)
(lib.mesonBool "oomd" true)
(lib.mesonBool "portabled" true)
(lib.mesonBool "hwdb" true)
(lib.mesonBool "timedated" true)
(lib.mesonBool "timesyncd" true)
(lib.mesonBool "userdb" false)
(lib.mesonBool "coredump" true)
(lib.mesonBool "firstboot" true)
(lib.mesonBool "resolve" true)
(lib.mesonBool "sysusers" true)
(lib.mesonBool "efi" true)
(lib.mesonBool "utmp" true)
(lib.mesonBool "log-trace" true)
(lib.mesonBool "kernel-install" true)
(lib.mesonBool "quotacheck" false)
(lib.mesonBool "ldconfig" false)
(lib.mesonBool "install-sysconfdir" true)
(lib.mesonBool "create-log-dirs" true)
(lib.mesonBool "smack" true)
(lib.mesonBool "b_pie" true)
(lib.mesonOption "bashcompletiondir" "no")
(lib.mesonOption "zshcompletiondir" "no")
];
})

155
tests/common.nix Normal file
View file

@ -0,0 +1,155 @@
{
self,
lib,
pkgs,
...
}:
with import (pkgs.path + "/nixos/lib/testing-python.nix") {
inherit pkgs;
inherit (pkgs.hostPlatform) system;
};
let
qemu-common = import (pkgs.path + "/nixos/lib/qemu-common.nix") { inherit lib pkgs; };
in
rec {
makeSystem =
extraConfig:
(import (pkgs.path + "/nixos/lib/eval-config.nix")) {
inherit pkgs lib;
system = null;
modules = [
{
nixpkgs.hostPlatform = pkgs.hostPlatform;
}
{
users.allowNoPasswordLogin = true;
system.stateVersion = lib.versions.majorMinor lib.version;
system.image.id = lib.mkDefault "test";
system.image.version = lib.mkDefault "1";
networking.hosts."10.0.2.1" = [ "server.test" ];
}
{
boot.kernelParams = [
"console=ttyS0,115200n8"
"systemd.journald.forward_to_console=1"
];
image.compress = false;
boot.uki.name = lib.mkForce "test";
boot.initrd.compressor = lib.mkForce "zstd";
boot.initrd.compressorArgs = lib.mkForce [ "-8" ];
}
(pkgs.path + "/nixos/modules/testing/test-instrumentation.nix")
self.nixosModules.devel
self.nixosModules.image
extraConfig
];
};
makeImage =
extraConfig:
let
system = makeSystem extraConfig;
in
"${system.config.system.build.image}/${system.config.system.build.image.imageFile}";
makeUpdatePackage =
extraConfig:
let
system = makeSystem extraConfig;
in
"${system.config.system.build.updatePackage}";
makeImageTest =
{
name,
image,
script,
httpRoot ? null,
}:
let
qemu = qemu-common.qemuBinary pkgs.qemu_test;
flags = [
"-m"
"512M"
"-drive"
"if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}"
"-drive"
"if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}"
"-drive"
"if=virtio,file=${mutableImage}"
"-chardev"
"socket,id=chrtpm,path=${tpmFolder}/swtpm-sock"
"-tpmdev"
"emulator,id=tpm0,chardev=chrtpm"
"-device"
"tpm-tis,tpmdev=tpm0"
"-netdev"
(
"'user,id=net0"
+ (lib.optionalString (
httpRoot != null
) ",guestfwd=tcp:10.0.2.1:80-cmd:${pkgs.micro-httpd}/bin/micro_httpd ${httpRoot}")
+ "'"
)
"-device"
"virtio-net-pci,netdev=net0"
];
flagsStr = lib.concatStringsSep " " flags;
startCommand = "${qemu} ${flagsStr}";
mutableImage = "/tmp/linked-image.qcow2";
tpmFolder = "/tmp/emulated_tpm";
indentLines = str: lib.concatLines (map (s: " " + s) (lib.splitString "\n" str));
in
makeTest {
inherit name;
nodes = { };
testScript =
''
import os
import subprocess
subprocess.check_call(
[
"qemu-img",
"create",
"-f",
"qcow2",
"-F",
"raw",
"-b",
"${image}",
"${mutableImage}",
]
)
subprocess.check_call(["qemu-img", "resize", "${mutableImage}", "4G"])
os.mkdir("${tpmFolder}")
os.mkdir("${tpmFolder}/swtpm")
def start_tpm():
subprocess.Popen(
[
"${pkgs.swtpm}/bin/swtpm",
"socket",
"--tpmstate", "dir=${tpmFolder}/swtpm",
"--ctrl", "type=unixio,path=${tpmFolder}/swtpm-sock",
"--tpm2"
]
)
machine = create_machine("${startCommand}")
try:
''
+ indentLines script
+ ''
finally:
machine.shutdown()
'';
};
}

9
tests/lib.nix Normal file
View file

@ -0,0 +1,9 @@
test:
{ pkgs, self }:
let nixos-lib = import (pkgs.path + "/nixos/lib") {};
in (nixos-lib.runTest {
hostPkgs = pkgs;
defaults.documentation.enable = false;
node.specialArgs = { inherit self; };
imports = [ test ];
}).config.result

22
tests/podman.nix Normal file
View file

@ -0,0 +1,22 @@
{ pkgs, self }: let
lib = pkgs.lib;
test-common = import ./common.nix { inherit self lib pkgs; };
image = test-common.makeImage { };
in test-common.makeImageTest {
name = "podman";
inherit image;
script = ''
start_tpm()
machine.start()
machine.wait_for_unit("multi-user.target")
machine.wait_for_unit("network-online.target")
machine.succeed("tar cv --files-from /dev/null | su admin -l -c 'podman import - scratchimg'")
machine.succeed("su admin -l -c 'podman run --rm -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg true'")
'';
}

45
tests/system-update.nix Normal file
View file

@ -0,0 +1,45 @@
{ pkgs, self }: let
lib = pkgs.lib;
test-common = import ./common.nix { inherit self lib pkgs; };
initialImage = test-common.makeImage {
system.image.version = "1";
system.image.updates.url = "http://server.test/";
# The default root-b is too small for uncompressed test images
systemd.repart.partitions."32-root-b" = {
SizeMinBytes = lib.mkForce "1G";
SizeMaxBytes = lib.mkForce "1G";
};
};
updatePackage = test-common.makeUpdatePackage {
system.image.version = "2";
system.image.updates.url = "http://server.test/";
};
in test-common.makeImageTest {
name = "system-update";
image = initialImage;
httpRoot = updatePackage;
script = ''
start_tpm()
machine.start()
machine.wait_for_unit("multi-user.target")
machine.wait_for_unit("network-online.target")
machine.succeed("/run/current-system/sw/lib/systemd/systemd-sysupdate update")
machine.shutdown()
start_tpm()
machine.start()
machine.wait_for_unit("multi-user.target")
machine.succeed('. /etc/os-release; [ "$IMAGE_VERSION" == "2" ]')
machine.wait_for_unit("systemd-bless-boot.service")
'';
}

View file

@ -1,125 +0,0 @@
{
pkgs,
patosPkgs,
...
}:
pkgs.writeShellApplication {
name = "mkinitrd";
runtimeInputs = with pkgs; [
cpio
gzip
];
text = ''
echo "Building initram disk"
mkdir -p root
pushd root
### copy rootfs
cp -prP ${patosPkgs.rootfs}/* .
find . -type d -exec chmod 755 {} \;
### create directories
ln -sf ../usr/lib/systemd/systemd init
# set default target to basic
mkdir usr/lib/systemd/system/basic.target.wants
ln -sf basic.target usr/lib/systemd/system/default.target
# enable dbus broker
ln -sf ../dbus-broker.service usr/lib/systemd/system/basic.target.wants/dbus.service
ln -sf ../dbus.socket usr/lib/systemd/system/sockets.target.wants/dbus.socket
### Create needed files
echo patos > ./etc/hostname
cat <<EOF > ./etc/os-release
NAME="PatOS"
PRETTY_NAME="PatOS Platform"
ID=patos
EOF
cat <<EOF > ./etc/passwd
root::0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/usr/bin/nologin
daemon:x:2:2:daemon:/:/usr/bin/nologin
mail:x:8:12:mail:/var/spool/mail:/usr/bin/nologin
ftp:x:14:11:ftp:/srv/ftp:/usr/bin/nologin
http:x:33:33:http:/srv/http:/usr/bin/nologin
uuidd:x:68:68:uuidd:/:/usr/bin/nologin
messagebus:x:81:81:messagebus:/:/usr/bin/nologin
nobody:x:99:99:nobody:/:/usr/bin/nologin
EOF
chmod 644 ./etc/passwd
cat <<EOF > ./etc/group
root:x:0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
sys:x:3:root,bin
adm:x:4:root,daemon
tty:x:5:
disk:x:6:root
lp:x:7:daemon
mem:x:8:
kmem:x:9:
wheel:x:10:root
ftp:x:11:
mail:x:12:
uucp:x:14:
log:x:19:root
utmp:x:20:
locate:x:21:
rfkill:x:24:
smmsp:x:25:
proc:x:26:
http:x:33:
games:x:50:
lock:x:54:
uuidd:x:68:
messagebus:x:81:
network:x:90:
video:x:91:
audio:x:92:
optical:x:93:
floppy:x:94:
storage:x:95:
scanner:x:96:
input:x:97:
power:x:98:
nobody:x:99:
EOF
chmod 644 ./etc/group
# FIXME: remove this later (just to get a shell in the initramfs)
cat <<EOF > usr/lib/systemd/system/demo.service
[Unit]
Description=Debug Shell (/bin/sulogin)
Conflicts=shutdown.target
Before=shutdown.target
[Service]
Environment=HOME=/root
WorkingDirectory=/root
ExecStart=/bin/sulogin
Type=idle
StandardInput=tty-force
StandardOutput=inherit
StandardError=inherit
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
Restart=always
[Install]
WantedBy=basic.target
EOF
ln -sf ../demo.service usr/lib/systemd/system/basic.target.wants/demo.service
# gen initrd
find . -print0 | cpio --null --owner=root:root -o --format=newc | gzip -9 > ../initrd.gz
popd
rm -rf root
'';
}

View file

@ -1,4 +1,5 @@
{
config,
pkgs,
...
}:
@ -19,7 +20,7 @@ pkgs.writeShellApplication {
state="/tmp/patos-qemu-$USER"
rm -rf "$state"
mkdir -m 700 "$state"
truncate -s 1G "$state/disk.raw"
qemu-img create -f qcow2 -F raw -b "$(readlink -e "$1")" "$state/disk.qcow2" 10G
swtpm socket -d --tpmstate dir="$state" \
--ctrl type=unixio,path="$state/swtpm-sock" \
@ -33,7 +34,6 @@ pkgs.writeShellApplication {
-smp 8 \
-m 4G \
-display none \
-nographic \
-chardev "stdio,id=char0,mux=on,logfile=$state/console.log,signal=off" \
-serial chardev:char0 \
-mon chardev=char0 \
@ -44,7 +44,6 @@ pkgs.writeShellApplication {
-device tpm-tis,tpmdev=tpm0 \
-netdev id=net00,type=user,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=net00 \
-drive "file=fat:rw:patos/,format=raw" \
-drive "format=raw,file=$state/disk.raw"
-drive "format=qcow2,file=$state/disk.qcow2"
'';
}