From ae8fbf86065a3e5bd010caa34fe69ea72070828b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82a=C5=BCej=20Sowa?= Date: Sun, 27 Oct 2024 22:02:11 +0100 Subject: [PATCH 1/4] maintainers: add bjsowa --- maintainers/maintainer-list.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 86b6ac1a048b8..0d89154baf33a 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -2857,6 +2857,12 @@ githubId = 133602; name = "Bjørn Forsman"; }; + bjsowa = { + email = "bsowa123@gmail.com"; + github = "bjsowa"; + githubId = 23124539; + name = "Błażej Sowa"; + }; bkchr = { email = "nixos@kchr.de"; github = "bkchr"; From d07617f885675adf668b9422e0a2b37ddea90ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82a=C5=BCej=20Sowa?= Date: Mon, 28 Oct 2024 01:23:56 +0100 Subject: [PATCH 2/4] schroot: init at 1.6.13-5 --- pkgs/by-name/sc/schroot/debian-patches.nix | 70 +++++++++++++++ pkgs/by-name/sc/schroot/debian-patches.txt | 17 ++++ .../sc/schroot/fix-absolute-paths.patch | 43 +++++++++ .../sc/schroot/no-default-config.patch | 33 +++++++ pkgs/by-name/sc/schroot/no-pam-service.patch | 10 +++ pkgs/by-name/sc/schroot/no-setuid.patch | 12 +++ pkgs/by-name/sc/schroot/package.nix | 89 +++++++++++++++++++ 7 files changed, 274 insertions(+) create mode 100644 pkgs/by-name/sc/schroot/debian-patches.nix create mode 100644 pkgs/by-name/sc/schroot/debian-patches.txt create mode 100644 pkgs/by-name/sc/schroot/fix-absolute-paths.patch create mode 100644 pkgs/by-name/sc/schroot/no-default-config.patch create mode 100644 pkgs/by-name/sc/schroot/no-pam-service.patch create mode 100644 pkgs/by-name/sc/schroot/no-setuid.patch create mode 100644 pkgs/by-name/sc/schroot/package.nix diff --git a/pkgs/by-name/sc/schroot/debian-patches.nix b/pkgs/by-name/sc/schroot/debian-patches.nix new file mode 100644 index 0000000000000..bab32e6ff731b --- /dev/null +++ b/pkgs/by-name/sc/schroot/debian-patches.nix @@ -0,0 +1,70 @@ +# Generated by debian-patches.sh from debian-patches.txt +let + prefix = "https://sources.debian.org/data/main/s/schroot/1.6.13-5/debian/patches"; +in +[ + { + url = "${prefix}/1539621689.revert.schroot-1.6.10-48-g2600bcab.revert-environment-preserve-empty-values.patch"; + sha256 = "0kvp47qkhq4s7bqil1pcq7c0a63hiw7xgmrxv460rfb9irvlp2wn"; + } + { + url = "${prefix}/1448890714.schroot-1.7.2-72-gbf30a928.setup-d-20copyfiles-canonicalize-destination-path.patch"; + sha256 = "09vliqpmqn4yvh3mxsa9iqlwxkfrhfjraizm945ir6v23m622i6k"; + } + { + url = "${prefix}/1453505583.schroot-1.7.2-72-g11587fd8.etc-setup-d-20copyfiles-replace-dangling-symlink-during-cp.patch"; + sha256 = "036g3br7251ykhmgq48zbjdgyy06m0rhrv1hf540zb7n6ildygkz"; + } + { + url = "${prefix}/1496783678.schroot-1.7.2-127-ga5e5d8d9.fix-bash-completion.patch"; + sha256 = "0adqpyqkw5kyg1843ygificm2hxawl4pi1lbyn2dvzw11yawvv9k"; + } + { + url = "${prefix}/1530433671.schroot-1.7.2-129-g00c0a972.cmake-use-soelim-r-option.patch"; + sha256 = "0csn6j6xsi8bd16r7bvd33albwz6v7fv7wawakkiimbj59zbinhn"; + } + { + url = "${prefix}/1487872945.schroot-1.7.2-137-g5c36362b.support-copyfiles-installation-into-non-existent-directories.patch"; + sha256 = "1iizprcdnisdjgp091y0zjsayq5qqfwh9d2d9zns1d0414jafp7b"; + } + { + url = "${prefix}/1487872999.schroot-1.7.2-138-g5a611c49.support-copyfiles-source-destination-specifications.patch"; + sha256 = "03f735zyzj4zmrrmynb2xl49i8s8580d9dyp0kxq5aq555sl7hx2"; + } + { + url = "${prefix}/1662655911.reschroot-1.6.13-2-g779349dc.replace-usage-of-egrep-and-which.patch"; + sha256 = "0f2wziwchfg3v193i8x7s60vr9yx72z3f7ddgsfkshay4m5mrprs"; + } + { + url = "${prefix}/1662656169.reschroot-1.6.13-3-ga9e100e5.clean-up-mess-created-in-the-portuguese-translations.patch"; + sha256 = "1yq05pc38rkbb0njwllfwjc8dl73i7g7z89spsa7ls3amkyccm7w"; + } + { + url = "${prefix}/1664011392.reschroot-1.6.13-4-g93017cff.update-french-translation.patch"; + sha256 = "0qhcifjxgipxh67fjf0ydclvxn9yys7hfcwxir9jnw572y9igx1k"; + } + { + url = "${prefix}/1665995770.reschroot-1.6.13-5-g81b88b45.document-a-login-shell-might-be-switched-to-a-regular-shell.patch"; + sha256 = "1lwslyqrfz1xmi28j1s80pq763k63f9g4qgx9vr3qm247gb7v55l"; + } + { + url = "${prefix}/1692468301.reschroot-1.6.13-6-g271acf6e.subject-mount-a-new-instance-of-dev-pts-in-the-chroot.patch"; + sha256 = "1j04bxcbg2bigss7sjqyg7cn8547wwmp5zj8406v3vk5g71iaijf"; + } + { + url = "${prefix}/1664222056.reschroot-1.6.13-9-g55af32cf.fix-localename-type.patch"; + sha256 = "15kjl3g6c79jfqh45mr3h3qbl5gx2pmxqc2pmsjplxdkksx9i6yn"; + } + { + url = "${prefix}/1658716738.reschroot-1.6.12-2-g2045008e.fix-variable-usage-in-copyfiles-copy-file-function.patch"; + sha256 = "02c6zsmprdvkgf3krl6z1qwvx144arng0s34ry1dq8qvwl5fd66c"; + } + { + url = "${prefix}/fix-dupes-in-buildd-configuration.patch"; + sha256 = "0s8racsl2s4mix6n7xb09dmncs5w0jmnb0vrjxpwx9c1yhz46dwl"; + } + { + url = "${prefix}/fix-example-configuration.patch"; + sha256 = "1r6kffc7a4aanksjv4658vs4xs31gmrhpa2gmpqkr21s7zk45yav"; + } +] diff --git a/pkgs/by-name/sc/schroot/debian-patches.txt b/pkgs/by-name/sc/schroot/debian-patches.txt new file mode 100644 index 0000000000000..b4f3d8461a82c --- /dev/null +++ b/pkgs/by-name/sc/schroot/debian-patches.txt @@ -0,0 +1,17 @@ +schroot/1.6.13-5 +1539621689.revert.schroot-1.6.10-48-g2600bcab.revert-environment-preserve-empty-values.patch +1448890714.schroot-1.7.2-72-gbf30a928.setup-d-20copyfiles-canonicalize-destination-path.patch +1453505583.schroot-1.7.2-72-g11587fd8.etc-setup-d-20copyfiles-replace-dangling-symlink-during-cp.patch +1496783678.schroot-1.7.2-127-ga5e5d8d9.fix-bash-completion.patch +1530433671.schroot-1.7.2-129-g00c0a972.cmake-use-soelim-r-option.patch +1487872945.schroot-1.7.2-137-g5c36362b.support-copyfiles-installation-into-non-existent-directories.patch +1487872999.schroot-1.7.2-138-g5a611c49.support-copyfiles-source-destination-specifications.patch +1662655911.reschroot-1.6.13-2-g779349dc.replace-usage-of-egrep-and-which.patch +1662656169.reschroot-1.6.13-3-ga9e100e5.clean-up-mess-created-in-the-portuguese-translations.patch +1664011392.reschroot-1.6.13-4-g93017cff.update-french-translation.patch +1665995770.reschroot-1.6.13-5-g81b88b45.document-a-login-shell-might-be-switched-to-a-regular-shell.patch +1692468301.reschroot-1.6.13-6-g271acf6e.subject-mount-a-new-instance-of-dev-pts-in-the-chroot.patch +1664222056.reschroot-1.6.13-9-g55af32cf.fix-localename-type.patch +1658716738.reschroot-1.6.12-2-g2045008e.fix-variable-usage-in-copyfiles-copy-file-function.patch +fix-dupes-in-buildd-configuration.patch +fix-example-configuration.patch diff --git a/pkgs/by-name/sc/schroot/fix-absolute-paths.patch b/pkgs/by-name/sc/schroot/fix-absolute-paths.patch new file mode 100644 index 0000000000000..e2db2fdb58fcf --- /dev/null +++ b/pkgs/by-name/sc/schroot/fix-absolute-paths.patch @@ -0,0 +1,43 @@ +diff --git a/etc/setup.d/20copyfiles b/etc/setup.d/20copyfiles +index 3247ae2a..eed9fa46 100755 +--- a/etc/setup.d/20copyfiles ++++ b/etc/setup.d/20copyfiles +@@ -39,9 +39,9 @@ copy_file() + if [ -e "$2" ]; then + + # Device and inode +- da=$(/usr/bin/stat --format="%d %i" "$1") ++ da=$(stat --format="%d %i" "$1") + # This one can fail since it might not exist yet +- db=$(/usr/bin/stat --format="%d %i" "$2" 2>/dev/null || :) ++ db=$(stat --format="%d %i" "$2" 2>/dev/null || :) + + if [ "$da" = "$db" ]; then + COPY="false" +@@ -50,8 +50,8 @@ copy_file() + : + elif [ -f "$1" ] && [ -f "$2" ]; then + # Content +- ca=$(/usr/bin/md5sum "$1" | sed -e 's/\(^[0-9a-f][0-9a-f]*\).*$/\1/') +- cb=$(/usr/bin/md5sum "$2" 2>/dev/null || :) ++ ca=$(md5sum "$1" | sed -e 's/\(^[0-9a-f][0-9a-f]*\).*$/\1/') ++ cb=$(md5sum "$2" 2>/dev/null || :) + cb=$(echo "$cb" | sed -e 's/\(^[0-9a-f][0-9a-f]*\).*$/\1/') + # Copy only if file contents differ + if [ "$ca" = "$cb" ]; then +diff --git a/etc/setup.d/20nssdatabases b/etc/setup.d/20nssdatabases +index ac7206b7..00645362 100755 +--- a/etc/setup.d/20nssdatabases ++++ b/etc/setup.d/20nssdatabases +@@ -42,9 +42,9 @@ if [ $STAGE = "setup-start" ] || [ $STAGE = "setup-recover" ]; then + fi + + # Device and inode +- dr=$(/usr/bin/stat --format="%d %i" "/etc/$db") ++ dr=$(stat --format="%d %i" "/etc/$db") + # This one can fail since it might not exist yet +- dc=$(/usr/bin/stat --format="%d %i" "${CHROOT_PATH}/etc/$db" 2>/dev/null || :) ++ dc=$(stat --format="%d %i" "${CHROOT_PATH}/etc/$db" 2>/dev/null || :) + + # If the database inside and outside the chroot is the + # same, it's very likely that dup_nss would blank the diff --git a/pkgs/by-name/sc/schroot/no-default-config.patch b/pkgs/by-name/sc/schroot/no-default-config.patch new file mode 100644 index 0000000000000..3714c49827801 --- /dev/null +++ b/pkgs/by-name/sc/schroot/no-default-config.patch @@ -0,0 +1,33 @@ +diff --git a/etc/CMakeLists.txt b/etc/CMakeLists.txt +index dd31fd3d..65521010 100644 +--- a/etc/CMakeLists.txt ++++ b/etc/CMakeLists.txt +@@ -19,20 +19,20 @@ + set(schroot_sysconf_data + schroot.conf) + +-install(FILES ${schroot_sysconf_data} +- DESTINATION ${SCHROOT_SYSCONF_DIR}) ++# install(FILES ${schroot_sysconf_data} ++# DESTINATION ${SCHROOT_SYSCONF_DIR}) + + set(files + copyfiles + fstab + nssdatabases) + +-set(profiles +- buildd +- default +- desktop +- minimal +- sbuild) ++# set(profiles ++# buildd ++# default ++# desktop ++# minimal ++# sbuild) + + set(arches + ${SBUILD_PLATFORM}) diff --git a/pkgs/by-name/sc/schroot/no-pam-service.patch b/pkgs/by-name/sc/schroot/no-pam-service.patch new file mode 100644 index 0000000000000..7cbf4c9054dac --- /dev/null +++ b/pkgs/by-name/sc/schroot/no-pam-service.patch @@ -0,0 +1,10 @@ +--- a/etc/CMakeLists.txt ++++ b/etc/CMakeLists.txt +@@ -68,6 +68,6 @@ foreach(profile ${profiles}) + endforeach(file) + endforeach(profile) + +-add_subdirectory(pam) ++# add_subdirectory(pam) + add_subdirectory(bash_completion) + add_subdirectory(setup.d) diff --git a/pkgs/by-name/sc/schroot/no-setuid.patch b/pkgs/by-name/sc/schroot/no-setuid.patch new file mode 100644 index 0000000000000..7f1382bab7fda --- /dev/null +++ b/pkgs/by-name/sc/schroot/no-setuid.patch @@ -0,0 +1,12 @@ +--- a/bin/schroot/CMakeLists.txt ++++ b/bin/schroot/CMakeLists.txt +@@ -40,8 +40,7 @@ install(TARGETS schroot RUNTIME + DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE +- WORLD_READ WORLD_EXECUTE +- SETUID) ++ WORLD_READ WORLD_EXECUTE) + + set(installdirs + ${SCHROOT_CONF_CHROOT_D} diff --git a/pkgs/by-name/sc/schroot/package.nix b/pkgs/by-name/sc/schroot/package.nix new file mode 100644 index 0000000000000..5f3aa5facfd3d --- /dev/null +++ b/pkgs/by-name/sc/schroot/package.nix @@ -0,0 +1,89 @@ +{ + lib, + stdenv, + fetchurl, + pkgs, + buildPackages, +}: + +let + scripts-bin-path = + with pkgs; + lib.makeBinPath [ + coreutils + getent + gnugrep + gnused + gnutar + util-linux + ]; + upstream-version = "1.6.13"; +in +stdenv.mkDerivation { + pname = "schroot"; + version = "${upstream-version}-5"; + + src = fetchurl { + url = "https://codeberg.org/shelter/reschroot/archive/release/reschroot-${upstream-version}.tar.gz"; + hash = "sha256-wF1qG7AhDUAeZSLu4sRl4LQ8bJj3EB1nH56e+Is6zPU="; + }; + + patches = map fetchurl (import ./debian-patches.nix) ++ [ + ./no-setuid.patch + ./no-pam-service.patch + ./no-default-config.patch + ./fix-absolute-paths.patch + ]; + + nativeBuildInputs = with buildPackages; [ + cmake + findutils + gettext + mandoc + makeWrapper + perlPackages.Po4a + ]; + + buildInputs = with pkgs; [ + boost + ]; + + cmakeFlags = [ + "-DCMAKE_INSTALL_LOCALSTATEDIR=/var" + "-DSCHROOT_SYSCONF_DIR=/etc/schroot" + "-DSCHROOT_CONF_SETUP_D=${placeholder "out"}/etc/schroot/setup.d" + ]; + + postPatch = '' + # Substitute the path to the mount binary + substituteInPlace bin/schroot-mount/schroot-mount-main.cc \ + --replace-fail "/bin/mount" "${pkgs.util-linux}/bin/mount" + ''; + + postFixup = '' + # Make wrappers for all shell scripts used by schroot + # The wrapped script are put into a separate directory to not be run by schroot during setup + mkdir $out/etc/schroot/setup.d.wrapped + cd $out/etc/schroot/setup.d + find * -type f | while read -r file; do + mv "$file" $out/etc/schroot/setup.d.wrapped + makeWrapper "$out/etc/schroot/setup.d.wrapped/$file" "$file" --set PATH ${scripts-bin-path} + done + + # Get rid of stuff that's (probably) not needed + rm -vrf $out/lib $out/include + ''; + + meta = with lib; { + description = "Lightweight virtualisation tool"; + longDescription = '' + Schroot is a program that allows the user to run a command or a login shell in a chroot environment. + ''; + homepage = "https://codeberg.org/shelter/reschroot"; + changelog = "https://codeberg.org/shelter/reschroot/raw/tag/release/reschroot-${upstream-version}/NEWS"; + mainProgram = "schroot"; + maintainers = with lib.maintainers; [ bjsowa ]; + license = with licenses; [ gpl3 ]; + platforms = lib.platforms.linux; + }; +} From ce2bcc793b59a9450df3a06d8e71c7dc17ae1247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82a=C5=BCej=20Sowa?= Date: Sun, 27 Oct 2024 21:42:21 +0100 Subject: [PATCH 3/4] nixos/schroot: init module --- .../manual/release-notes/rl-2411.section.md | 2 + nixos/modules/module-list.nix | 1 + nixos/modules/programs/schroot.nix | 137 ++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 nixos/modules/programs/schroot.nix diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 1874a3b336f47..c9ce5f5fb9d5b 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -75,6 +75,8 @@ - [Cyrus IMAP](https://github.com/cyrusimap/cyrus-imapd), an email, contacts and calendar server. Available as [services.cyrus-imap](#opt-services.cyrus-imap.enable) service. +- [Schroot](), a lightweight virtualisation tool. Securely enter a chroot and run a command or login shell. Available as [programs.schroot](#opt-programs.schroot.enable). + - [TaskChampion Sync-Server](https://github.com/GothenburgBitFactory/taskchampion-sync-server), a [Taskwarrior 3](https://taskwarrior.org/docs/upgrade-3/) sync server, replacing Taskwarrior 2's sync server named [`taskserver`](https://github.com/GothenburgBitFactory/taskserver). - [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr), proxy server to bypass Cloudflare protection. Available as [services.flaresolverr](#opt-services.flaresolverr.enable) service. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 98340036f7b64..e096b6b89b38e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -277,6 +277,7 @@ ./programs/rust-motd.nix ./programs/ryzen-monitor-ng.nix ./programs/screen.nix + ./programs/schroot.nix ./programs/seahorse.nix ./programs/sedutil.nix ./programs/shadow.nix diff --git a/nixos/modules/programs/schroot.nix b/nixos/modules/programs/schroot.nix new file mode 100644 index 0000000000000..a185cff1248aa --- /dev/null +++ b/nixos/modules/programs/schroot.nix @@ -0,0 +1,137 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.programs.schroot; + iniFmt = pkgs.formats.ini { }; +in +{ + options = { + programs.schroot = { + enable = lib.mkEnableOption "schroot, a lightweight virtualisation tool"; + package = lib.mkPackageOption pkgs "schroot" { }; + + settings = lib.mkOption { + type = iniFmt.type; + default = { }; + example = { + "noble" = { + type = "directory"; + description = "Ubuntu 24.04 Noble"; + directory = "/srv/chroot/noble"; + users = "my-user"; + root-users = "my-user"; + personality = "linux"; + preserve-environment = false; + profile = "my-profile"; + shell = "/bin/bash"; + }; + }; + description = '' + Schroot configuration settings. + For more details, see {manpage}`schroot.conf(5)`. + ''; + }; + + profiles = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options = { + copyfiles = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = [ "/etc/resolv.conf" ]; + description = "A list of files to copy into the chroot from the host system."; + }; + fstab = lib.mkOption { + type = lib.types.path; + example = lib.literalExpression '' + pkgs.writeText "my-schroot-fstab" ''' + /proc /proc none rw,bind 0 0 + /sys /sys none rw,bind 0 0 + /dev /dev none rw,bind 0 0 + /dev/pts /dev/pts none rw,bind 0 0 + /home /home none rw,rbind 0 0 + /tmp /tmp none rw,bind 0 0 + /dev/shm /dev/shm none rw,bind 0 0 + /nix /nix none ro,bind 0 0 + /run/current-system /run/current-system none rw,bind 0 0 + /run/wrappers /run/wrappers none rw,bind 0 0 + ''' + ''; + description = '' + A file in the format described in {manpage}`fstab(5)`, used to mount filesystems inside the chroot. + The mount location is relative to the root of the chroot. + ''; + }; + nssdatabases = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = [ + "passwd" + "shadow" + "group" + "gshadow" + "services" + "protocols" + "networks" + "hosts" + ]; + description = '' + System databases (as described in /etc/nsswitch.conf on GNU/Linux systems) to copy into the chroot from the host. + ''; + }; + }; + } + ); + default = { }; + description = "Custom configuration profiles for schroot."; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment = { + systemPackages = [ cfg.package ]; + + etc = + { + # schroot requires this directory to exist + "schroot/chroot.d/.keep".text = ""; + + "schroot/schroot.conf".source = iniFmt.generate "schroot.conf" cfg.settings; + } + // (lib.attrsets.concatMapAttrs ( + name: + { + copyfiles, + fstab, + nssdatabases, + }: + { + "schroot/${name}/copyfiles".text = lib.strings.concatStringsSep "\n" copyfiles; + "schroot/${name}/fstab".source = fstab; + "schroot/${name}/nssdatabases".text = lib.strings.concatStringsSep "\n" nssdatabases; + } + ) cfg.profiles); + }; + + security.wrappers.schroot = { + source = "${cfg.package}/bin/schroot"; + owner = "root"; + group = "root"; + setuid = true; + }; + + # Schroot requires these directories to exist + systemd.tmpfiles.rules = [ + "d /var/lib/schroot/session - root root - -" + "d /var/lib/schroot/unpack - root root - -" + "d /var/lib/schroot/union - root root - -" + "d /var/lib/schroot/union/overlay - root root - -" + "d /var/lib/schroot/union/underlay - root root - -" + ]; + }; +} From 42369ee4b4741b61eb7bac700e91e3322f5fc8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82a=C5=BCej=20Sowa?= Date: Thu, 31 Oct 2024 14:52:02 +0100 Subject: [PATCH 4/4] nixos/schroot: fix missing newline characters --- nixos/modules/programs/schroot.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/modules/programs/schroot.nix b/nixos/modules/programs/schroot.nix index a185cff1248aa..bce8e4709ddb5 100644 --- a/nixos/modules/programs/schroot.nix +++ b/nixos/modules/programs/schroot.nix @@ -111,9 +111,9 @@ in nssdatabases, }: { - "schroot/${name}/copyfiles".text = lib.strings.concatStringsSep "\n" copyfiles; + "schroot/${name}/copyfiles".text = (lib.strings.concatStringsSep "\n" copyfiles) + "\n"; "schroot/${name}/fstab".source = fstab; - "schroot/${name}/nssdatabases".text = lib.strings.concatStringsSep "\n" nssdatabases; + "schroot/${name}/nssdatabases".text = (lib.strings.concatStringsSep "\n" nssdatabases) + "\n"; } ) cfg.profiles); };