Merge branch 'master' into dbus-switch-to-configuration
Conflicts: nixos/modules/system/activation/switch-to-configuration.pl
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# generate the script used to activate the configuration.
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{pkgs, ...}:
|
||||
{ lib, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
{
|
||||
boot.loader.grub.device = mkOverride 0 "nodev";
|
||||
|
||||
@@ -27,7 +27,10 @@ EOF
|
||||
exit 1;
|
||||
}
|
||||
|
||||
die "This is not a NixOS installation (/etc/NIXOS is missing)!\n" unless -f "/etc/NIXOS";
|
||||
# This is a NixOS installation if it has /etc/NIXOS or a proper
|
||||
# /etc/os-release.
|
||||
die "This is not a NixOS installation!\n" unless
|
||||
-f "/etc/NIXOS" || (read_file("/etc/os-release", err_mode => 'quiet') // "") =~ /ID=nixos/s;
|
||||
|
||||
openlog("nixos", "", LOG_USER);
|
||||
|
||||
@@ -96,12 +99,18 @@ sub parseFstab {
|
||||
sub parseUnit {
|
||||
my ($filename) = @_;
|
||||
my $info = {};
|
||||
foreach my $line (read_file($filename)) {
|
||||
parseKeyValues($info, read_file($filename));
|
||||
parseKeyValues($info, read_file("${filename}.d/overrides.conf")) if -f "${filename}.d/overrides.conf";
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub parseKeyValues {
|
||||
my $info = shift;
|
||||
foreach my $line (@_) {
|
||||
# FIXME: not quite correct.
|
||||
$line =~ /^([^=]+)=(.*)$/ or next;
|
||||
$info->{$1} = $2;
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub boolIsTrue {
|
||||
@@ -109,6 +118,14 @@ sub boolIsTrue {
|
||||
return $s eq "yes" || $s eq "true";
|
||||
}
|
||||
|
||||
# As a fingerprint for determining whether a unit has changed, we use
|
||||
# its absolute path. If it has an override file, we append *its*
|
||||
# absolute path as well.
|
||||
sub fingerprintUnit {
|
||||
my ($s) = @_;
|
||||
return abs_path($s) . (-f "${s}.d/overrides.conf" ? " " . abs_path "${s}.d/overrides.conf" : "");
|
||||
}
|
||||
|
||||
# Stop all services that no longer exist or have changed in the new
|
||||
# configuration.
|
||||
my (@unitsToStop, @unitsToSkip);
|
||||
@@ -125,7 +142,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
|
||||
$baseName =~ s/\.[a-z]*$//;
|
||||
|
||||
if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
|
||||
if (! -e $newUnitFile) {
|
||||
if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
|
||||
push @unitsToStop, $unit;
|
||||
}
|
||||
|
||||
@@ -160,7 +177,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
|
||||
}
|
||||
}
|
||||
|
||||
elsif (abs_path($prevUnitFile) ne abs_path($newUnitFile)) {
|
||||
elsif (fingerprintUnit($prevUnitFile) ne fingerprintUnit($newUnitFile)) {
|
||||
if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target") {
|
||||
# Do nothing. These cannot be restarted directly.
|
||||
} elsif ($unit =~ /\.mount$/) {
|
||||
@@ -170,7 +187,10 @@ while (my ($unit, $state) = each %{$activePrev}) {
|
||||
# FIXME: do something?
|
||||
} else {
|
||||
my $unitInfo = parseUnit($newUnitFile);
|
||||
if (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
|
||||
if (boolIsTrue($unitInfo->{'X-ReloadIfChanged'} // "no")) {
|
||||
write_file($reloadListFile, { append => 1 }, "$unit\n");
|
||||
}
|
||||
elsif (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
|
||||
push @unitsToSkip, $unit;
|
||||
} else {
|
||||
# If this unit is socket-activated, then stop the
|
||||
@@ -319,7 +339,7 @@ if (scalar @restart > 0) {
|
||||
# that are symlinks to other units. We shouldn't start both at the
|
||||
# same time because we'll get a "Failed to add path to set" error from
|
||||
# systemd.
|
||||
my @start = unique("default.target", "timers.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
|
||||
my @start = unique("default.target", "timers.target", "sockets.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
|
||||
print STDERR "starting the following units: ", join(", ", sort(@start)), "\n";
|
||||
$systemdManager->StartUnit($_, "replace") for @start;
|
||||
unlink($startListFile);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, modules, baseModules, ... }:
|
||||
{ config, lib, pkgs, modules, baseModules, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
@@ -11,7 +11,7 @@ let
|
||||
# you can provide an easy way to boot the same configuration
|
||||
# as you use, but with another kernel
|
||||
# !!! fix this
|
||||
cloner = inheritParent: list: with pkgs.lib;
|
||||
cloner = inheritParent: list:
|
||||
map (childConfig:
|
||||
(import ../../../lib/eval-config.nix {
|
||||
inherit baseModules;
|
||||
@@ -68,6 +68,7 @@ let
|
||||
echo -n "$configurationName" > $out/configuration-name
|
||||
echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
|
||||
echo -n "$nixosVersion" > $out/nixos-version
|
||||
echo -n "$system" > $out/system
|
||||
|
||||
mkdir $out/fine-tune
|
||||
childCount=0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
@@ -159,7 +159,7 @@ in
|
||||
|
||||
boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
|
||||
|
||||
boot.kernelModules = [ "loop" ];
|
||||
boot.kernelModules = [ "loop" "configs" ];
|
||||
|
||||
boot.initrd.availableKernelModules =
|
||||
[ # Note: most of these (especially the SATA/PATA modules)
|
||||
@@ -203,6 +203,9 @@ in
|
||||
|
||||
# To wait for SCSI devices to appear.
|
||||
"scsi_wait_scan"
|
||||
|
||||
# Needed by the stage 2 init script.
|
||||
"rtc_cmos"
|
||||
];
|
||||
|
||||
boot.initrd.kernelModules =
|
||||
@@ -215,37 +218,26 @@ in
|
||||
|
||||
# Create /etc/modules-load.d/nixos.conf, which is read by
|
||||
# systemd-modules-load.service to load required kernel modules.
|
||||
# FIXME: ensure that systemd-modules-load.service is restarted if
|
||||
# this file changes.
|
||||
environment.etc = singleton
|
||||
{ target = "modules-load.d/nixos.conf";
|
||||
source = kernelModulesConf;
|
||||
};
|
||||
|
||||
# Sigh. This overrides systemd's systemd-modules-load.service
|
||||
# just so we can set a restart trigger. Also make
|
||||
# multi-user.target pull it in so that it gets started if it
|
||||
# failed earlier.
|
||||
systemd.services."systemd-modules-load" =
|
||||
{ description = "Load Kernel Modules";
|
||||
wantedBy = [ "sysinit.target" "multi-user.target" ];
|
||||
before = [ "sysinit.target" "shutdown.target" ];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
unitConfig =
|
||||
{ DefaultDependencies = false;
|
||||
ConditionCapability = "CAP_SYS_MODULE";
|
||||
};
|
||||
{ wantedBy = [ "multi-user.target" ];
|
||||
restartTriggers = [ kernelModulesConf ];
|
||||
environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules";
|
||||
serviceConfig =
|
||||
{ Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${config.systemd.package}/lib/systemd/systemd-modules-load";
|
||||
# Ignore failed module loads. Typically some of the
|
||||
{ # Ignore failed module loads. Typically some of the
|
||||
# modules in ‘boot.kernelModules’ are "nice to have but
|
||||
# not required" (e.g. acpi-cpufreq), so we don't want to
|
||||
# barf on those.
|
||||
SuccessExitStatus = "0 1";
|
||||
};
|
||||
restartTriggers = [ kernelModulesConf ];
|
||||
};
|
||||
|
||||
systemd.services.kmod-static-nodes =
|
||||
{ environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules";
|
||||
};
|
||||
|
||||
lib.kernelConfig = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ pkgs, ... }:
|
||||
{ lib, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
{
|
||||
options.boot.loader.efi = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
@@ -133,11 +133,8 @@ in
|
||||
chainloader (hd0,1)+1
|
||||
|
||||
# GRUB 2 example
|
||||
menuentry "Windows7" {
|
||||
title Windows7
|
||||
insmod ntfs
|
||||
set root='(hd1,1)'
|
||||
chainloader +1
|
||||
menuentry "Windows 7" {
|
||||
chainloader (hd0,4)+1
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
|
||||
@@ -1,33 +1,87 @@
|
||||
# This module adds Memtest86+ to the GRUB boot menu.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
memtest86 = pkgs.memtest86plus;
|
||||
cfg = config.boot.loader.grub.memtest86;
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
|
||||
boot.loader.grub.memtest86 = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Make Memtest86+, a memory testing program, available from the
|
||||
GRUB boot menu.
|
||||
'';
|
||||
boot.loader.grub.memtest86 = {
|
||||
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Make Memtest86+, a memory testing program, available from the
|
||||
GRUB boot menu.
|
||||
'';
|
||||
};
|
||||
|
||||
params = mkOption {
|
||||
default = [];
|
||||
example = [ "console=ttyS0,115200" ];
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
Parameters added to the Memtest86+ command line. As of memtest86+ 5.01
|
||||
the following list of (apparently undocumented) parameters are
|
||||
accepted:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para><literal>console=...</literal>, set up a serial console.
|
||||
Examples:
|
||||
<literal>console=ttyS0</literal>,
|
||||
<literal>console=ttyS0,9600</literal> or
|
||||
<literal>console=ttyS0,115200n8</literal>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>btrace</literal>, enable boot trace.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>maxcpus=N</literal>, limit number of CPUs.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>onepass</literal>, run one pass and exit if there
|
||||
are no errors.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>tstlist=...</literal>, list of tests to run.
|
||||
Example: <literal>0,1,2</literal>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>cpumask=...</literal>, set a CPU mask, to select CPUs
|
||||
to use for testing.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
This list of command line options was obtained by reading the
|
||||
Memtest86+ source code.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf config.boot.loader.grub.memtest86 {
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
boot.loader.grub.extraEntries =
|
||||
if config.boot.loader.grub.version == 2 then
|
||||
''
|
||||
menuentry "Memtest86+" {
|
||||
linux16 @bootRoot@/memtest.bin
|
||||
linux16 @bootRoot@/memtest.bin ${toString cfg.params}
|
||||
}
|
||||
''
|
||||
else
|
||||
|
||||
@@ -9,7 +9,6 @@ import tempfile
|
||||
import errno
|
||||
|
||||
def copy_if_not_exists(source, dest):
|
||||
known_paths.append(dest)
|
||||
if not os.path.exists(dest):
|
||||
shutil.copyfile(source, dest)
|
||||
|
||||
@@ -38,12 +37,13 @@ def write_loader_conf(generation):
|
||||
print >> f, "default nixos-generation-%d" % (generation)
|
||||
os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
|
||||
|
||||
def copy_from_profile(generation, name):
|
||||
def copy_from_profile(generation, name, dry_run=False):
|
||||
store_file_path = os.readlink("%s/%s" % (system_dir(generation), name))
|
||||
suffix = os.path.basename(store_file_path)
|
||||
store_dir = os.path.basename(os.path.dirname(store_file_path))
|
||||
efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
|
||||
copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
|
||||
if not dry_run:
|
||||
copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
|
||||
return efi_file_path
|
||||
|
||||
def add_entry(generation):
|
||||
@@ -72,6 +72,10 @@ def get_generations(profile):
|
||||
def remove_old_entries(gens):
|
||||
slice_start = len("@efiSysMountPoint@/loader/entries/nixos-generation-")
|
||||
slice_end = -1 * len(".conf")
|
||||
known_paths = []
|
||||
for gen in gens:
|
||||
known_paths.append(copy_from_profile(gen, "kernel", True))
|
||||
known_paths.append(copy_from_profile(gen, "initrd", True))
|
||||
for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf"):
|
||||
try:
|
||||
gen = int(path[slice_start:slice_end])
|
||||
@@ -94,7 +98,6 @@ if os.getenv("NIXOS_INSTALL_GRUB") == "1":
|
||||
else:
|
||||
subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "--no-variables", "install"])
|
||||
|
||||
known_paths = []
|
||||
mkdir_p("@efiSysMountPoint@/efi/nixos")
|
||||
mkdir_p("@efiSysMountPoint@/loader/entries")
|
||||
try:
|
||||
@@ -106,9 +109,8 @@ except IOError as e:
|
||||
machine_id = None
|
||||
|
||||
gens = get_generations("system")
|
||||
remove_old_entries(gens)
|
||||
for gen in gens:
|
||||
add_entry(gen)
|
||||
if os.readlink(system_dir(gen)) == args.default_config:
|
||||
write_loader_conf(gen)
|
||||
|
||||
remove_old_entries(gens)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.boot.loader.gummiboot;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
luks = config.boot.initrd.luks;
|
||||
|
||||
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: ''
|
||||
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, yubikey, ... }: ''
|
||||
# Wait for luksRoot to appear, e.g. if on a usb drive.
|
||||
# XXX: copied and adapted from stage-1-init.sh - should be
|
||||
# available as a function.
|
||||
@@ -31,9 +31,161 @@ let
|
||||
fi
|
||||
''}
|
||||
|
||||
open_normally() {
|
||||
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
|
||||
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
|
||||
}
|
||||
|
||||
${optionalString (luks.yubikeySupport && (yubikey != null)) ''
|
||||
|
||||
rbtohex() {
|
||||
( od -An -vtx1 | tr -d ' \n' )
|
||||
}
|
||||
|
||||
hextorb() {
|
||||
( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
|
||||
}
|
||||
|
||||
open_yubikey() {
|
||||
|
||||
# Make all of these local to this function
|
||||
# to prevent their values being leaked
|
||||
local salt
|
||||
local iterations
|
||||
local k_user
|
||||
local challenge
|
||||
local response
|
||||
local k_luks
|
||||
local opened
|
||||
local new_salt
|
||||
local new_iterations
|
||||
local new_challenge
|
||||
local new_response
|
||||
local new_k_luks
|
||||
|
||||
mkdir -p ${yubikey.storage.mountPoint}
|
||||
mount -t ${yubikey.storage.fsType} ${toString yubikey.storage.device} ${yubikey.storage.mountPoint}
|
||||
|
||||
salt="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
|
||||
iterations="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
|
||||
challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
|
||||
response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
|
||||
|
||||
for try in $(seq 3); do
|
||||
|
||||
${optionalString yubikey.twoFactor ''
|
||||
echo -n "Enter two-factor passphrase: "
|
||||
read -s k_user
|
||||
echo
|
||||
''}
|
||||
|
||||
if [ ! -z "$k_user" ]; then
|
||||
k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
|
||||
else
|
||||
k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
|
||||
fi
|
||||
|
||||
echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
|
||||
|
||||
if [ $? == "0" ]; then
|
||||
opened=true
|
||||
break
|
||||
else
|
||||
opened=false
|
||||
echo "Authentication failed!"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$opened" == false ]; then
|
||||
umount ${yubikey.storage.mountPoint}
|
||||
echo "Maximum authentication errors reached"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
|
||||
for i in $(seq ${toString yubikey.saltLength}); do
|
||||
byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
|
||||
new_salt="$new_salt$byte";
|
||||
echo -n .
|
||||
done;
|
||||
echo "ok"
|
||||
|
||||
new_iterations="$iterations"
|
||||
${optionalString (yubikey.iterationStep > 0) ''
|
||||
new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
|
||||
''}
|
||||
|
||||
new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
|
||||
|
||||
new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
|
||||
|
||||
if [ ! -z "$k_user" ]; then
|
||||
new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
|
||||
else
|
||||
new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
|
||||
fi
|
||||
|
||||
mkdir -p ${yubikey.ramfsMountPoint}
|
||||
# A ramfs is used here to ensure that the file used to update
|
||||
# the key slot with cryptsetup will never get swapped out.
|
||||
# Warning: Do NOT replace with tmpfs!
|
||||
mount -t ramfs none ${yubikey.ramfsMountPoint}
|
||||
|
||||
echo -n "$new_k_luks" | hextorb > ${yubikey.ramfsMountPoint}/new_key
|
||||
echo -n "$k_luks" | hextorb | cryptsetup luksChangeKey ${device} --key-file=- ${yubikey.ramfsMountPoint}/new_key
|
||||
|
||||
if [ $? == "0" ]; then
|
||||
echo -ne "$new_salt\n$new_iterations" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
|
||||
else
|
||||
echo "Warning: Could not update LUKS key, current challenge persists!"
|
||||
fi
|
||||
|
||||
rm -f ${yubikey.ramfsMountPoint}/new_key
|
||||
umount ${yubikey.ramfsMountPoint}
|
||||
rm -rf ${yubikey.ramfsMountPoint}
|
||||
|
||||
umount ${yubikey.storage.mountPoint}
|
||||
}
|
||||
|
||||
${optionalString (yubikey.gracePeriod > 0) ''
|
||||
echo -n "Waiting ${toString yubikey.gracePeriod} seconds as grace..."
|
||||
for i in $(seq ${toString yubikey.gracePeriod}); do
|
||||
sleep 1
|
||||
echo -n .
|
||||
done
|
||||
echo "ok"
|
||||
''}
|
||||
|
||||
yubikey_missing=true
|
||||
ykinfo -v 1>/dev/null 2>&1
|
||||
if [ $? != "0" ]; then
|
||||
echo -n "waiting 10 seconds for yubikey to appear..."
|
||||
for try in $(seq 10); do
|
||||
sleep 1
|
||||
ykinfo -v 1>/dev/null 2>&1
|
||||
if [ $? == "0" ]; then
|
||||
yubikey_missing=false
|
||||
break
|
||||
fi
|
||||
echo -n .
|
||||
done
|
||||
echo "ok"
|
||||
else
|
||||
yubikey_missing=false
|
||||
fi
|
||||
|
||||
if [ "$yubikey_missing" == true ]; then
|
||||
echo "no yubikey found, falling back to non-yubikey open procedure"
|
||||
open_normally
|
||||
else
|
||||
open_yubikey
|
||||
fi
|
||||
''}
|
||||
|
||||
# open luksRoot and scan for logical volumes
|
||||
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
|
||||
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
|
||||
${optionalString ((!luks.yubikeySupport) || (yubikey == null)) ''
|
||||
open_normally
|
||||
''}
|
||||
'';
|
||||
|
||||
isPreLVM = f: f.preLVM;
|
||||
@@ -139,10 +291,108 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
yubikey = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.optionSet;
|
||||
description = ''
|
||||
The options to use for this LUKS device in Yubikey-PBA.
|
||||
If null (the default), Yubikey-PBA will be disabled for this device.
|
||||
'';
|
||||
|
||||
options = {
|
||||
twoFactor = mkOption {
|
||||
default = true;
|
||||
type = types.bool;
|
||||
description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false)";
|
||||
};
|
||||
|
||||
slot = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = "Which slot on the Yubikey to challenge";
|
||||
};
|
||||
|
||||
saltLength = mkOption {
|
||||
default = 16;
|
||||
type = types.int;
|
||||
description = "Length of the new salt in byte (64 is the effective maximum)";
|
||||
};
|
||||
|
||||
keyLength = mkOption {
|
||||
default = 64;
|
||||
type = types.int;
|
||||
description = "Length of the LUKS slot key derived with PBKDF2 in byte";
|
||||
};
|
||||
|
||||
iterationStep = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = "How much the iteration count for PBKDF2 is increased at each successful authentication";
|
||||
};
|
||||
|
||||
gracePeriod = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = "Time in seconds to wait before attempting to find the Yubikey";
|
||||
};
|
||||
|
||||
ramfsMountPoint = mkOption {
|
||||
default = "/crypt-ramfs";
|
||||
type = types.string;
|
||||
description = "Path where the ramfs used to update the LUKS key will be mounted in stage-1";
|
||||
};
|
||||
|
||||
storage = mkOption {
|
||||
type = types.optionSet;
|
||||
description = "Options related to the storing the salt";
|
||||
|
||||
options = {
|
||||
device = mkOption {
|
||||
default = /dev/sda1;
|
||||
type = types.path;
|
||||
description = ''
|
||||
An unencrypted device that will temporarily be mounted in stage-1.
|
||||
Must contain the current salt to create the challenge for this LUKS device.
|
||||
'';
|
||||
};
|
||||
|
||||
fsType = mkOption {
|
||||
default = "vfat";
|
||||
type = types.string;
|
||||
description = "The filesystem of the unencrypted device";
|
||||
};
|
||||
|
||||
mountPoint = mkOption {
|
||||
default = "/crypt-storage";
|
||||
type = types.string;
|
||||
description = "Path where the unencrypted device will be mounted in stage-1";
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
default = "/crypt-storage/default";
|
||||
type = types.string;
|
||||
description = ''
|
||||
Absolute path of the salt on the unencrypted device with
|
||||
that device's root directory as "/".
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
boot.initrd.luks.yubikeySupport = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Enables support for authenticating with a Yubikey on LUKS devices.
|
||||
See the NixOS wiki for information on how to properly setup a LUKS device
|
||||
and a Yubikey to work with this feature.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (luks.devices != []) {
|
||||
@@ -157,15 +407,48 @@ in
|
||||
# copy the cryptsetup binary and it's dependencies
|
||||
boot.initrd.extraUtilsCommands = ''
|
||||
cp -pdv ${pkgs.cryptsetup}/sbin/cryptsetup $out/bin
|
||||
# XXX: do we have a function that does this?
|
||||
for lib in $(ldd $out/bin/cryptsetup |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do
|
||||
cp -pdvn $lib $out/lib
|
||||
cp -pvn $(readlink -f $lib) $out/lib
|
||||
done
|
||||
|
||||
cp -pdv ${pkgs.libgcrypt}/lib/libgcrypt*.so.* $out/lib
|
||||
cp -pdv ${pkgs.libgpgerror}/lib/libgpg-error*.so.* $out/lib
|
||||
cp -pdv ${pkgs.cryptsetup}/lib/libcryptsetup*.so.* $out/lib
|
||||
cp -pdv ${pkgs.popt}/lib/libpopt*.so.* $out/lib
|
||||
|
||||
${optionalString luks.yubikeySupport ''
|
||||
cp -pdv ${pkgs.ykpers}/bin/ykchalresp $out/bin
|
||||
cp -pdv ${pkgs.ykpers}/bin/ykinfo $out/bin
|
||||
cp -pdv ${pkgs.openssl}/bin/openssl $out/bin
|
||||
|
||||
cc -O3 -I${pkgs.openssl}/include -L${pkgs.openssl}/lib ${./pbkdf2-sha512.c} -o $out/bin/pbkdf2-sha512 -lcrypto
|
||||
strip -s $out/bin/pbkdf2-sha512
|
||||
|
||||
cp -pdv ${pkgs.libusb1}/lib/libusb*.so.* $out/lib
|
||||
cp -pdv ${pkgs.ykpers}/lib/libykpers*.so.* $out/lib
|
||||
cp -pdv ${pkgs.libyubikey}/lib/libyubikey*.so.* $out/lib
|
||||
cp -pdv ${pkgs.openssl}/lib/libssl*.so.* $out/lib
|
||||
cp -pdv ${pkgs.openssl}/lib/libcrypto*.so.* $out/lib
|
||||
|
||||
mkdir -p $out/etc/ssl
|
||||
cp -pdv ${pkgs.openssl}/etc/ssl/openssl.cnf $out/etc/ssl
|
||||
|
||||
cat > $out/bin/openssl-wrap <<EOF
|
||||
#!$out/bin/sh
|
||||
EOF
|
||||
chmod +x $out/bin/openssl-wrap
|
||||
''}
|
||||
'';
|
||||
|
||||
boot.initrd.extraUtilsCommandsTest = ''
|
||||
$out/bin/cryptsetup --version
|
||||
${optionalString luks.yubikeySupport ''
|
||||
$out/bin/ykchalresp -V
|
||||
$out/bin/ykinfo -V
|
||||
cat > $out/bin/openssl-wrap <<EOF
|
||||
#!$out/bin/sh
|
||||
export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
|
||||
$out/bin/openssl "\$@"
|
||||
EOF
|
||||
$out/bin/openssl-wrap version
|
||||
''}
|
||||
'';
|
||||
|
||||
boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
{
|
||||
|
||||
@@ -68,7 +68,10 @@ with pkgs.lib;
|
||||
|
||||
config = mkIf (!config.boot.isContainer) {
|
||||
|
||||
environment.etc = singleton
|
||||
environment.etc = [
|
||||
{ source = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
|
||||
target = "modprobe.d/ubuntu.conf";
|
||||
}
|
||||
{ source = pkgs.writeText "modprobe.conf"
|
||||
''
|
||||
${flip concatMapStrings config.boot.blacklistedKernelModules (name: ''
|
||||
@@ -77,26 +80,11 @@ with pkgs.lib;
|
||||
${config.boot.extraModprobeConfig}
|
||||
'';
|
||||
target = "modprobe.d/nixos.conf";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ config.system.sbin.modprobe pkgs.kmod ];
|
||||
|
||||
boot.blacklistedKernelModules =
|
||||
[ # This module is for debugging and generates gigantic amounts
|
||||
# of log output, so it should never be loaded automatically.
|
||||
"evbug"
|
||||
|
||||
# This module causes ALSA to occassionally select the wrong
|
||||
# default sound device, and is little more than an annoyance
|
||||
# on modern machines.
|
||||
"snd_pcsp"
|
||||
|
||||
# The cirrusfb module prevents X11 from starting. FIXME:
|
||||
# Ubuntu blacklists all framebuffer devices because they're
|
||||
# "buggy" and cause suspend problems. Maybe we should too?
|
||||
"cirrusfb"
|
||||
];
|
||||
|
||||
system.activationScripts.modprobe =
|
||||
''
|
||||
# Allow the kernel to find our wrapped modprobe (which searches
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
void hextorb(uint8_t* hex, uint8_t* rb)
|
||||
{
|
||||
while(sscanf(hex, "%2x", rb) == 1)
|
||||
{
|
||||
hex += 2;
|
||||
rb += 1;
|
||||
}
|
||||
*rb = '\0';
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
uint8_t k_user[2048];
|
||||
uint8_t salt[2048];
|
||||
uint8_t key[4096];
|
||||
|
||||
uint32_t key_length = atoi(argv[1]);
|
||||
uint32_t iteration_count = atoi(argv[2]);
|
||||
|
||||
hextorb(argv[3], salt);
|
||||
uint32_t salt_length = strlen(argv[3]) / 2;
|
||||
|
||||
fgets(k_user, 2048, stdin);
|
||||
uint32_t k_user_length = strlen(k_user);
|
||||
if(k_user[k_user_length - 1] == '\n') {
|
||||
k_user[k_user_length - 1] = '\0';
|
||||
}
|
||||
|
||||
PKCS5_PBKDF2_HMAC(k_user, k_user_length, salt, salt_length, iteration_count, EVP_sha512(), key_length, key);
|
||||
fwrite(key, 1, key_length, stdout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
{
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ fail() {
|
||||
# in an interactive shell.
|
||||
cat <<EOF
|
||||
|
||||
An error occured in stage 1 of the boot process, which must mount the
|
||||
An error occurred in stage 1 of the boot process, which must mount the
|
||||
root filesystem on \`$targetRoot' and then start stage 2. Press one
|
||||
of the following keys:
|
||||
|
||||
@@ -139,8 +139,6 @@ mkdir -p /dev/.mdadm
|
||||
systemd-udevd --daemon
|
||||
udevadm trigger --action=add
|
||||
udevadm settle || true
|
||||
modprobe scsi_wait_scan || true
|
||||
udevadm settle || true
|
||||
|
||||
|
||||
# Load boot-time keymap before any LVM/LUKS initialization
|
||||
@@ -320,6 +318,10 @@ while read -u 3 mountPoint; do
|
||||
echo -n "waiting for device $device to appear..."
|
||||
for try in $(seq 1 20); do
|
||||
sleep 1
|
||||
# also re-try lvm activation now that new block devices might have appeared
|
||||
lvm vgchange -ay
|
||||
# and tell udev to create nodes for the new LVs
|
||||
udevadm trigger --action=add
|
||||
if test -e $device; then break; fi
|
||||
echo -n "."
|
||||
done
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
# the modules necessary to mount the root file system, then calls the
|
||||
# init in the root file system to start the second boot stage.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
@@ -74,7 +74,7 @@ let
|
||||
cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup
|
||||
cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm
|
||||
cp -v ${pkgs.lvm2}/lib/libdevmapper.so.*.* $out/lib
|
||||
cp -v ${pkgs.systemd}/lib/libsystemd-daemon.so.* $out/lib
|
||||
cp -v ${pkgs.systemd}/lib/libsystemd.so.* $out/lib
|
||||
|
||||
# Add RAID mdadm tool.
|
||||
cp -v ${pkgs.mdadm}/sbin/mdadm $out/bin/mdadm
|
||||
|
||||
@@ -82,7 +82,7 @@ done
|
||||
|
||||
# More special file systems, initialise required directories.
|
||||
mkdir -m 0755 /dev/shm
|
||||
mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
|
||||
mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" none /dev/shm
|
||||
mkdir -m 0755 -p /dev/pts
|
||||
[ -e /proc/bus/usb ] && mount -t usbfs none /proc/bus/usb # UML doesn't have USB by default
|
||||
mkdir -m 01777 -p /tmp
|
||||
@@ -96,28 +96,14 @@ mkdir -m 0755 -p /etc/nixos
|
||||
|
||||
# Miscellaneous boot time cleanup.
|
||||
rm -rf /var/run /var/lock
|
||||
rm -f /etc/resolv.conf
|
||||
touch /etc/resolv.conf
|
||||
rm -f /etc/{group,passwd,shadow}.lock
|
||||
|
||||
if test -n "@cleanTmpDir@"; then
|
||||
echo -n "cleaning \`/tmp'..."
|
||||
find /tmp -maxdepth 1 -mindepth 1 -print0 | xargs -0r rm -rf --one-file-system
|
||||
echo " done"
|
||||
else
|
||||
# Get rid of ICE locks...
|
||||
rm -rf /tmp/.ICE-unix
|
||||
fi
|
||||
|
||||
# ... and ensure that it's owned by root.
|
||||
mkdir -m 1777 /tmp/.ICE-unix
|
||||
|
||||
# This is a good time to clean up /nix/var/nix/chroots. Doing an `rm
|
||||
# -rf' on it isn't safe in general because it can contain bind mounts
|
||||
# to /nix/store and other places. But after rebooting these are all
|
||||
# gone, of course.
|
||||
rm -rf /nix/var/nix/chroots # recreated in activate-configuration.sh
|
||||
|
||||
|
||||
# Also get rid of temporary GC roots.
|
||||
rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
|
||||
@@ -131,6 +117,15 @@ if ! mountpoint -q /run; then
|
||||
mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
|
||||
fi
|
||||
|
||||
# Create a ramfs on /run/keys to hold secrets that shouldn't be
|
||||
# written to disk (generally used for NixOps, harmless elsewhere).
|
||||
if ! mountpoint -q /run/keys; then
|
||||
rm -rf /run/keys
|
||||
mkdir -m 0750 /run/keys
|
||||
chown 0:96 /run/keys
|
||||
mount -t ramfs none /run/keys
|
||||
fi
|
||||
|
||||
mkdir -m 0755 -p /run/lock
|
||||
|
||||
|
||||
@@ -146,12 +141,32 @@ if test -n "$resumeDevice"; then
|
||||
fi
|
||||
|
||||
|
||||
# Use /etc/resolv.conf supplied by systemd-nspawn, if applicable.
|
||||
if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then
|
||||
cat /etc/resolv.conf | resolvconf -m 1000 -a host
|
||||
else
|
||||
touch /etc/resolv.conf
|
||||
fi
|
||||
|
||||
|
||||
# Create /var/setuid-wrappers as a tmpfs.
|
||||
rm -rf /var/setuid-wrappers
|
||||
mkdir -m 0755 -p /var/setuid-wrappers
|
||||
mount -t tmpfs -o "mode=0755" none /var/setuid-wrappers
|
||||
|
||||
|
||||
# Run the script that performs all configuration activation that does
|
||||
# not have to be done at boot time.
|
||||
echo "running activation script..."
|
||||
$systemConfig/activate
|
||||
|
||||
|
||||
# Restore the system time from the hardware clock. We do this after
|
||||
# running the activation script to be sure that /etc/localtime points
|
||||
# at the current time zone.
|
||||
hwclock --hctosys
|
||||
|
||||
|
||||
# Record the boot configuration.
|
||||
ln -sfn "$systemConfig" /run/booted-system
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
@@ -19,11 +19,13 @@ let
|
||||
isExecutable = true;
|
||||
inherit (config.boot) devShmSize runSize cleanTmpDir;
|
||||
inherit (config.nix) readOnlyStore;
|
||||
inherit (config.networking) useHostResolvConf;
|
||||
ttyGid = config.ids.gids.tty;
|
||||
path =
|
||||
[ pkgs.coreutils
|
||||
pkgs.utillinux
|
||||
pkgs.sysvtools
|
||||
pkgs.openresolv
|
||||
] ++ (optional config.boot.cleanTmpDir pkgs.findutils)
|
||||
++ optional config.nix.readOnlyStore readonlyMountpoint;
|
||||
postBootCommands = pkgs.writeText "local-cmds"
|
||||
@@ -79,6 +81,7 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
# FIXME: should replace this with something that uses systemd-tmpfiles.
|
||||
cleanTmpDir = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ config, pkgs }:
|
||||
{ config, lib }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
@@ -28,7 +28,7 @@ let
|
||||
|
||||
in rec {
|
||||
|
||||
unitOptions = {
|
||||
sharedOptions = {
|
||||
|
||||
enable = mkOption {
|
||||
default = true;
|
||||
@@ -41,6 +41,37 @@ in rec {
|
||||
'';
|
||||
};
|
||||
|
||||
requiredBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.string;
|
||||
description = "Units that require (i.e. depend on and need to go down with) this unit.";
|
||||
};
|
||||
|
||||
wantedBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.string;
|
||||
description = "Units that want (i.e. depend on) this unit.";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
concreteUnitOptions = sharedOptions // {
|
||||
|
||||
text = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Text of this systemd unit.";
|
||||
};
|
||||
|
||||
unit = mkOption {
|
||||
internal = true;
|
||||
description = "The generated unit.";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
commonUnitOptions = sharedOptions // {
|
||||
|
||||
description = mkOption {
|
||||
default = "";
|
||||
type = types.str;
|
||||
@@ -109,18 +140,6 @@ in rec {
|
||||
'';
|
||||
};
|
||||
|
||||
requiredBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.str;
|
||||
description = "Units that require (i.e. depend on and need to go down with) this unit.";
|
||||
};
|
||||
|
||||
wantedBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.str;
|
||||
description = "Units that want (i.e. depend on) this unit.";
|
||||
};
|
||||
|
||||
unitConfig = mkOption {
|
||||
default = {};
|
||||
example = { RequiresMountsFor = "/data"; };
|
||||
@@ -135,6 +154,7 @@ in rec {
|
||||
|
||||
restartTriggers = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.unspecified;
|
||||
description = ''
|
||||
An arbitrary list of items such as derivations. If any item
|
||||
in the list changes between reconfigurations, the service will
|
||||
@@ -145,7 +165,7 @@ in rec {
|
||||
};
|
||||
|
||||
|
||||
serviceOptions = unitOptions // {
|
||||
serviceOptions = commonUnitOptions // {
|
||||
|
||||
environment = mkOption {
|
||||
default = {};
|
||||
@@ -236,6 +256,17 @@ in rec {
|
||||
'';
|
||||
};
|
||||
|
||||
reloadIfChanged = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether the service should be reloaded during a NixOS
|
||||
configuration switch if its definition has changed. If
|
||||
enabled, the value of <option>restartIfChanged</option> is
|
||||
ignored.
|
||||
'';
|
||||
};
|
||||
|
||||
stopIfChanged = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
@@ -268,7 +299,7 @@ in rec {
|
||||
};
|
||||
|
||||
|
||||
socketOptions = unitOptions // {
|
||||
socketOptions = commonUnitOptions // {
|
||||
|
||||
listenStreams = mkOption {
|
||||
default = [];
|
||||
@@ -295,7 +326,7 @@ in rec {
|
||||
};
|
||||
|
||||
|
||||
timerOptions = unitOptions // {
|
||||
timerOptions = commonUnitOptions // {
|
||||
|
||||
timerConfig = mkOption {
|
||||
default = {};
|
||||
@@ -314,7 +345,24 @@ in rec {
|
||||
};
|
||||
|
||||
|
||||
mountOptions = unitOptions // {
|
||||
pathOptions = commonUnitOptions // {
|
||||
|
||||
pathConfig = mkOption {
|
||||
default = {};
|
||||
example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
<literal>[Path]</literal> section of the unit. See
|
||||
<citerefentry><refentrytitle>systemd.path</refentrytitle>
|
||||
<manvolnum>5</manvolnum></citerefentry> for details.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
mountOptions = commonUnitOptions // {
|
||||
|
||||
what = mkOption {
|
||||
example = "/dev/sda1";
|
||||
@@ -358,7 +406,7 @@ in rec {
|
||||
};
|
||||
};
|
||||
|
||||
automountOptions = unitOptions // {
|
||||
automountOptions = commonUnitOptions // {
|
||||
|
||||
where = mkOption {
|
||||
example = "/mnt";
|
||||
@@ -382,4 +430,6 @@ in rec {
|
||||
};
|
||||
};
|
||||
|
||||
targetOptions = commonUnitOptions;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{ config, pkgs, utils, ... }:
|
||||
{ config, lib, pkgs, utils, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
with utils;
|
||||
with import ./systemd-unit-options.nix { inherit config pkgs; };
|
||||
with import ./systemd-unit-options.nix { inherit config lib; };
|
||||
|
||||
let
|
||||
|
||||
@@ -11,29 +11,26 @@ let
|
||||
systemd = cfg.package;
|
||||
|
||||
makeUnit = name: unit:
|
||||
pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
|
||||
((if !unit.enable then ''
|
||||
mkdir -p $out
|
||||
ln -s /dev/null $out/${name}
|
||||
'' else if unit.linkTarget != null then ''
|
||||
mkdir -p $out
|
||||
ln -s ${unit.linkTarget} $out/${name}
|
||||
'' else if unit.text != null then ''
|
||||
mkdir -p $out
|
||||
echo -n "$text" > $out/${name}
|
||||
'' else "") + optionalString (unit.extraConfig != {}) ''
|
||||
mkdir -p $out/${name}.d
|
||||
${concatStringsSep "\n" (mapAttrsToList (n: v: "echo -n \"${v}\" > $out/${name}.d/${n}") unit.extraConfig)}
|
||||
'');
|
||||
if unit.enable then
|
||||
pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
|
||||
''
|
||||
mkdir -p $out
|
||||
echo -n "$text" > $out/${name}
|
||||
''
|
||||
else
|
||||
pkgs.runCommand "unit" { preferLocalBuild = true; }
|
||||
''
|
||||
mkdir -p $out
|
||||
ln -s /dev/null $out/${name}
|
||||
'';
|
||||
|
||||
upstreamUnits =
|
||||
upstreamSystemUnits =
|
||||
[ # Targets.
|
||||
"basic.target"
|
||||
"sysinit.target"
|
||||
"sockets.target"
|
||||
"graphical.target"
|
||||
"multi-user.target"
|
||||
"getty.target"
|
||||
"network.target"
|
||||
"network-online.target"
|
||||
"nss-lookup.target"
|
||||
@@ -43,6 +40,7 @@ let
|
||||
"sigpwr.target"
|
||||
"timers.target"
|
||||
"paths.target"
|
||||
"rpcbind.target"
|
||||
|
||||
# Rescue mode.
|
||||
"rescue.target"
|
||||
@@ -55,6 +53,13 @@ let
|
||||
"systemd-udev-settle.service"
|
||||
"systemd-udev-trigger.service"
|
||||
|
||||
# Consoles.
|
||||
"getty.target"
|
||||
"getty@.service"
|
||||
"serial-getty@.service"
|
||||
"container-getty@.service"
|
||||
"systemd-vconsole-setup.service"
|
||||
|
||||
# Hardware (started by udev when a relevant device is plugged in).
|
||||
"sound.target"
|
||||
"bluetooth.target"
|
||||
@@ -67,12 +72,15 @@ let
|
||||
#"systemd-vconsole-setup.service"
|
||||
"systemd-user-sessions.service"
|
||||
"dbus-org.freedesktop.login1.service"
|
||||
"dbus-org.freedesktop.machine1.service"
|
||||
"user@.service"
|
||||
|
||||
# Journal.
|
||||
"systemd-journald.socket"
|
||||
"systemd-journald.service"
|
||||
"systemd-journal-flush.service"
|
||||
"systemd-journal-gatewayd.socket"
|
||||
"systemd-journal-gatewayd.service"
|
||||
"syslog.socket"
|
||||
|
||||
# SysV init compatibility.
|
||||
@@ -80,7 +88,8 @@ let
|
||||
"systemd-initctl.service"
|
||||
|
||||
# Kernel module loading.
|
||||
#"systemd-modules-load.service"
|
||||
"systemd-modules-load.service"
|
||||
"kmod-static-nodes.service"
|
||||
|
||||
# Filesystems.
|
||||
"systemd-fsck@.service"
|
||||
@@ -93,10 +102,16 @@ let
|
||||
"swap.target"
|
||||
"dev-hugepages.mount"
|
||||
"dev-mqueue.mount"
|
||||
"proc-sys-fs-binfmt_misc.mount"
|
||||
"sys-fs-fuse-connections.mount"
|
||||
"sys-kernel-config.mount"
|
||||
"sys-kernel-debug.mount"
|
||||
|
||||
# Maintaining state across reboots.
|
||||
"systemd-random-seed.service"
|
||||
"systemd-backlight@.service"
|
||||
"systemd-rfkill@.service"
|
||||
|
||||
# Hibernate / suspend.
|
||||
"hibernate.target"
|
||||
"suspend.target"
|
||||
@@ -121,12 +136,30 @@ let
|
||||
"final.target"
|
||||
"kexec.target"
|
||||
"systemd-kexec.service"
|
||||
"systemd-update-utmp.service"
|
||||
|
||||
# Password entry.
|
||||
"systemd-ask-password-console.path"
|
||||
"systemd-ask-password-console.service"
|
||||
"systemd-ask-password-wall.path"
|
||||
"systemd-ask-password-wall.service"
|
||||
|
||||
# Slices / containers.
|
||||
"slices.target"
|
||||
"-.slice"
|
||||
"system.slice"
|
||||
"user.slice"
|
||||
"machine.slice"
|
||||
"systemd-machined.service"
|
||||
|
||||
# Temporary file creation / cleanup.
|
||||
"systemd-tmpfiles-clean.service"
|
||||
"systemd-tmpfiles-clean.timer"
|
||||
"systemd-tmpfiles-setup.service"
|
||||
"systemd-tmpfiles-setup-dev.service"
|
||||
|
||||
# Misc.
|
||||
"systemd-sysctl.service"
|
||||
]
|
||||
|
||||
++ optionals cfg.enableEmergencyMode [
|
||||
@@ -134,16 +167,26 @@ let
|
||||
"emergency.service"
|
||||
];
|
||||
|
||||
upstreamWants =
|
||||
upstreamSystemWants =
|
||||
[ #"basic.target.wants"
|
||||
"sysinit.target.wants"
|
||||
"sockets.target.wants"
|
||||
"local-fs.target.wants"
|
||||
"multi-user.target.wants"
|
||||
"shutdown.target.wants"
|
||||
"timers.target.wants"
|
||||
];
|
||||
|
||||
upstreamUserUnits =
|
||||
[ "basic.target"
|
||||
"default.target"
|
||||
"exit.target"
|
||||
"paths.target"
|
||||
"shutdown.target"
|
||||
"sockets.target"
|
||||
"systemd-exit.service"
|
||||
"timers.target"
|
||||
];
|
||||
|
||||
makeJobScript = name: text:
|
||||
let x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${name}"; inherit text; };
|
||||
in "${x}/bin/${name}";
|
||||
@@ -151,15 +194,23 @@ let
|
||||
unitConfig = { name, config, ... }: {
|
||||
config = {
|
||||
unitConfig =
|
||||
{ Requires = concatStringsSep " " config.requires;
|
||||
Wants = concatStringsSep " " config.wants;
|
||||
After = concatStringsSep " " config.after;
|
||||
Before = concatStringsSep " " config.before;
|
||||
BindsTo = concatStringsSep " " config.bindsTo;
|
||||
PartOf = concatStringsSep " " config.partOf;
|
||||
Conflicts = concatStringsSep " " config.conflicts;
|
||||
"X-Restart-Triggers" = toString config.restartTriggers;
|
||||
} // optionalAttrs (config.description != "") {
|
||||
optionalAttrs (config.requires != [])
|
||||
{ Requires = toString config.requires; }
|
||||
// optionalAttrs (config.wants != [])
|
||||
{ Wants = toString config.wants; }
|
||||
// optionalAttrs (config.after != [])
|
||||
{ After = toString config.after; }
|
||||
// optionalAttrs (config.before != [])
|
||||
{ Before = toString config.before; }
|
||||
// optionalAttrs (config.bindsTo != [])
|
||||
{ BindsTo = toString config.bindsTo; }
|
||||
// optionalAttrs (config.partOf != [])
|
||||
{ PartOf = toString config.partOf; }
|
||||
// optionalAttrs (config.conflicts != [])
|
||||
{ Conflicts = toString config.conflicts; }
|
||||
// optionalAttrs (config.restartTriggers != [])
|
||||
{ X-Restart-Triggers = toString config.restartTriggers; }
|
||||
// optionalAttrs (config.description != "") {
|
||||
Description = config.description;
|
||||
};
|
||||
};
|
||||
@@ -244,6 +295,11 @@ let
|
||||
(if isList value then value else [value]))
|
||||
as));
|
||||
|
||||
commonUnitText = def: ''
|
||||
[Unit]
|
||||
${attrsToSection def.unitConfig}
|
||||
'';
|
||||
|
||||
targetToUnit = name: def:
|
||||
{ inherit (def) wantedBy requiredBy enable;
|
||||
text =
|
||||
@@ -255,15 +311,16 @@ let
|
||||
|
||||
serviceToUnit = name: def:
|
||||
{ inherit (def) wantedBy requiredBy enable;
|
||||
text =
|
||||
text = commonUnitText def +
|
||||
''
|
||||
[Unit]
|
||||
${attrsToSection def.unitConfig}
|
||||
|
||||
[Service]
|
||||
${let env = cfg.globalEnvironment // def.environment;
|
||||
in concatMapStrings (n: "Environment=\"${n}=${getAttr n env}\"\n") (attrNames env)}
|
||||
${optionalString (!def.restartIfChanged) "X-RestartIfChanged=false"}
|
||||
${if def.reloadIfChanged then ''
|
||||
X-ReloadIfChanged=true
|
||||
'' else if !def.restartIfChanged then ''
|
||||
X-RestartIfChanged=false
|
||||
'' else ""}
|
||||
${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
|
||||
${attrsToSection def.serviceConfig}
|
||||
'';
|
||||
@@ -271,11 +328,8 @@ let
|
||||
|
||||
socketToUnit = name: def:
|
||||
{ inherit (def) wantedBy requiredBy enable;
|
||||
text =
|
||||
text = commonUnitText def +
|
||||
''
|
||||
[Unit]
|
||||
${attrsToSection def.unitConfig}
|
||||
|
||||
[Socket]
|
||||
${attrsToSection def.socketConfig}
|
||||
${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
|
||||
@@ -284,23 +338,26 @@ let
|
||||
|
||||
timerToUnit = name: def:
|
||||
{ inherit (def) wantedBy requiredBy enable;
|
||||
text =
|
||||
text = commonUnitText def +
|
||||
''
|
||||
[Unit]
|
||||
${attrsToSection def.unitConfig}
|
||||
|
||||
[Timer]
|
||||
${attrsToSection def.timerConfig}
|
||||
'';
|
||||
};
|
||||
|
||||
pathToUnit = name: def:
|
||||
{ inherit (def) wantedBy requiredBy enable;
|
||||
text = commonUnitText def +
|
||||
''
|
||||
[Path]
|
||||
${attrsToSection def.pathConfig}
|
||||
'';
|
||||
};
|
||||
|
||||
mountToUnit = name: def:
|
||||
{ inherit (def) wantedBy requiredBy enable;
|
||||
text =
|
||||
text = commonUnitText def +
|
||||
''
|
||||
[Unit]
|
||||
${attrsToSection def.unitConfig}
|
||||
|
||||
[Mount]
|
||||
${attrsToSection def.mountConfig}
|
||||
'';
|
||||
@@ -308,70 +365,99 @@ let
|
||||
|
||||
automountToUnit = name: def:
|
||||
{ inherit (def) wantedBy requiredBy enable;
|
||||
text =
|
||||
text = commonUnitText def +
|
||||
''
|
||||
[Unit]
|
||||
${attrsToSection def.unitConfig}
|
||||
|
||||
[Automount]
|
||||
${attrsToSection def.automountConfig}
|
||||
'';
|
||||
};
|
||||
|
||||
units = pkgs.runCommand "units" { preferLocalBuild = true; }
|
||||
''
|
||||
generateUnits = type: units: upstreamUnits: upstreamWants:
|
||||
pkgs.runCommand "${type}-units" { preferLocalBuild = true; } ''
|
||||
mkdir -p $out
|
||||
|
||||
# Copy the upstream systemd units we're interested in.
|
||||
for i in ${toString upstreamUnits}; do
|
||||
fn=${systemd}/example/systemd/system/$i
|
||||
fn=${systemd}/example/systemd/${type}/$i
|
||||
if ! [ -e $fn ]; then echo "missing $fn"; false; fi
|
||||
if [ -L $fn ]; then
|
||||
cp -pd $fn $out/
|
||||
target="$(readlink "$fn")"
|
||||
if [ ''${target:0:3} = ../ ]; then
|
||||
ln -s "$(readlink -f "$fn")" $out/
|
||||
else
|
||||
cp -pd $fn $out/
|
||||
fi
|
||||
else
|
||||
ln -s $fn $out/
|
||||
fi
|
||||
done
|
||||
|
||||
# Copy .wants links, but only those that point to units that
|
||||
# we're interested in.
|
||||
for i in ${toString upstreamWants}; do
|
||||
fn=${systemd}/example/systemd/system/$i
|
||||
fn=${systemd}/example/systemd/${type}/$i
|
||||
if ! [ -e $fn ]; then echo "missing $fn"; false; fi
|
||||
x=$out/$(basename $fn)
|
||||
mkdir $x
|
||||
for i in $fn/*; do
|
||||
y=$x/$(basename $i)
|
||||
cp -pd $i $y
|
||||
if ! [ -e $y ]; then rm -v $y; fi
|
||||
if ! [ -e $y ]; then rm $y; fi
|
||||
done
|
||||
done
|
||||
|
||||
for i in ${toString (mapAttrsToList (n: v: v.unit) cfg.units)}; do
|
||||
ln -fs $i/* $out/
|
||||
done
|
||||
|
||||
# Symlink all units provided listed in systemd.packages.
|
||||
for i in ${toString cfg.packages}; do
|
||||
ln -s $i/etc/systemd/system/* $out/
|
||||
files=$(echo $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*)
|
||||
if [ -n "$files" ]; then
|
||||
ln -s $files $out/
|
||||
fi
|
||||
done
|
||||
|
||||
# Symlink all units defined by systemd.units. If these are also
|
||||
# provided by systemd or systemd.packages, then add them as
|
||||
# <unit-name>.d/overrides.conf, which makes them extend the
|
||||
# upstream unit.
|
||||
for i in ${toString (mapAttrsToList (n: v: v.unit) units)}; do
|
||||
fn=$(basename $i/*)
|
||||
if [ -e $out/$fn ]; then
|
||||
if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
|
||||
ln -sfn /dev/null $out/$fn
|
||||
else
|
||||
mkdir $out/$fn.d
|
||||
ln -s $i/$fn $out/$fn.d/overrides.conf
|
||||
fi
|
||||
else
|
||||
ln -fs $i/$fn $out/
|
||||
fi
|
||||
done
|
||||
|
||||
# Created .wants and .requires symlinks from the wantedBy and
|
||||
# requiredBy options.
|
||||
${concatStrings (mapAttrsToList (name: unit:
|
||||
concatMapStrings (name2: ''
|
||||
mkdir -p $out/'${name2}.wants'
|
||||
ln -sfn '../${name}' $out/'${name2}.wants'/
|
||||
'') unit.wantedBy) cfg.units)}
|
||||
'') unit.wantedBy) units)}
|
||||
|
||||
${concatStrings (mapAttrsToList (name: unit:
|
||||
concatMapStrings (name2: ''
|
||||
mkdir -p $out/'${name2}.requires'
|
||||
ln -sfn '../${name}' $out/'${name2}.requires'/
|
||||
'') unit.requiredBy) cfg.units)}
|
||||
'') unit.requiredBy) units)}
|
||||
|
||||
ln -s ${cfg.defaultUnit} $out/default.target
|
||||
${optionalString (type == "system") ''
|
||||
# Stupid misc. symlinks.
|
||||
ln -s ${cfg.defaultUnit} $out/default.target
|
||||
|
||||
ln -s rescue.target $out/kbrequest.target
|
||||
ln -s rescue.target $out/kbrequest.target
|
||||
|
||||
mkdir -p $out/getty.target.wants/
|
||||
ln -s ../autovt@tty1.service $out/getty.target.wants/
|
||||
mkdir -p $out/getty.target.wants/
|
||||
ln -s ../autovt@tty1.service $out/getty.target.wants/
|
||||
|
||||
ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
|
||||
../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
|
||||
ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
|
||||
../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
|
||||
''}
|
||||
''; # */
|
||||
|
||||
in
|
||||
@@ -393,55 +479,9 @@ in
|
||||
default = {};
|
||||
type = types.attrsOf types.optionSet;
|
||||
options = { name, config, ... }:
|
||||
{ options = {
|
||||
text = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Text of this systemd unit.";
|
||||
};
|
||||
enable = mkOption {
|
||||
default = true;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If set to false, this unit will be a symlink to
|
||||
/dev/null. This is primarily useful to prevent specific
|
||||
template instances (e.g. <literal>serial-getty@ttyS0</literal>)
|
||||
from being started.
|
||||
'';
|
||||
};
|
||||
requiredBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.string;
|
||||
description = "Units that require (i.e. depend on and need to go down with) this unit.";
|
||||
};
|
||||
wantedBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.string;
|
||||
description = "Units that want (i.e. depend on) this unit.";
|
||||
};
|
||||
unit = mkOption {
|
||||
internal = true;
|
||||
description = "The generated unit.";
|
||||
};
|
||||
linkTarget = mkOption {
|
||||
default = null;
|
||||
description = "The file to symlink this target to.";
|
||||
type = types.nullOr types.path;
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
default = {};
|
||||
example = { "foo@1.conf" = "X-RestartIfChanged=false"; };
|
||||
type = types.attrsOf types.lines;
|
||||
description = ''
|
||||
Extra files to be appended to the configuration for the unit.
|
||||
This can be used to override configuration for a unit provided
|
||||
by systemd or another package, or to override only a single instance
|
||||
of a template unit.
|
||||
'';
|
||||
};
|
||||
};
|
||||
{ options = concreteUnitOptions;
|
||||
config = {
|
||||
unit = makeUnit name config;
|
||||
unit = mkDefault (makeUnit name config);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -455,7 +495,7 @@ in
|
||||
systemd.targets = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf types.optionSet;
|
||||
options = [ unitOptions unitConfig ];
|
||||
options = [ targetOptions unitConfig ];
|
||||
description = "Definition of systemd target units.";
|
||||
};
|
||||
|
||||
@@ -480,6 +520,13 @@ in
|
||||
description = "Definition of systemd timer units.";
|
||||
};
|
||||
|
||||
systemd.paths = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf types.optionSet;
|
||||
options = [ pathOptions unitConfig ];
|
||||
description = "Definition of systemd path units.";
|
||||
};
|
||||
|
||||
systemd.mounts = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.optionSet;
|
||||
@@ -567,6 +614,14 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
services.journald.enableHttpGateway = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to enable the HTTP gateway to the journal.
|
||||
'';
|
||||
};
|
||||
|
||||
services.logind.extraConfig = mkOption {
|
||||
default = "";
|
||||
type = types.lines;
|
||||
@@ -590,6 +645,41 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = [ "d /tmp 1777 root root 10d" ];
|
||||
description = ''
|
||||
Rules for creating and cleaning up temporary files
|
||||
automatically. See
|
||||
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for the exact format. You should not use this option to create
|
||||
files required by systemd services, since there is no
|
||||
guarantee that <command>systemd-tmpfiles</command> runs when
|
||||
the system is reconfigured using
|
||||
<command>nixos-rebuild</command>.
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.user.units = mkOption {
|
||||
description = "Definition of systemd per-user units.";
|
||||
default = {};
|
||||
type = types.attrsOf types.optionSet;
|
||||
options = { name, config, ... }:
|
||||
{ options = concreteUnitOptions;
|
||||
config = {
|
||||
unit = mkDefault (makeUnit name config);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.services = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf types.optionSet;
|
||||
options = [ serviceOptions unitConfig serviceConfig ];
|
||||
description = "Definition of systemd per-user service units.";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -597,11 +687,20 @@ in
|
||||
|
||||
config = {
|
||||
|
||||
system.build.units = units;
|
||||
assertions = mapAttrsToList (name: service: {
|
||||
assertion = service.serviceConfig.Type or "" == "oneshot" -> service.serviceConfig.Restart or "no" == "no";
|
||||
message = "${name}: Type=oneshot services must have Restart=no";
|
||||
}) cfg.services;
|
||||
|
||||
system.build.units = cfg.units;
|
||||
|
||||
environment.systemPackages = [ systemd ];
|
||||
|
||||
environment.etc."systemd/system".source = units;
|
||||
environment.etc."systemd/system".source =
|
||||
generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants;
|
||||
|
||||
environment.etc."systemd/user".source =
|
||||
generateUnits "user" cfg.user.units upstreamUserUnits [];
|
||||
|
||||
environment.etc."systemd/system.conf".text =
|
||||
''
|
||||
@@ -645,8 +744,11 @@ in
|
||||
'';
|
||||
|
||||
# Target for ‘charon send-keys’ to hook into.
|
||||
users.extraGroups.keys.gid = config.ids.gids.keys;
|
||||
|
||||
systemd.targets.keys =
|
||||
{ description = "Security Keys";
|
||||
unitConfig.X-StopOnReconfiguration = true;
|
||||
};
|
||||
|
||||
systemd.units =
|
||||
@@ -654,6 +756,7 @@ in
|
||||
// mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
|
||||
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
|
||||
// mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
|
||||
// mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
|
||||
// listToAttrs (map
|
||||
(v: let n = escapeSystemdPath v.where;
|
||||
in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
|
||||
@@ -661,6 +764,9 @@ in
|
||||
(v: let n = escapeSystemdPath v.where;
|
||||
in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
|
||||
|
||||
systemd.user.units =
|
||||
mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services;
|
||||
|
||||
system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled [
|
||||
"CGROUPS" "AUTOFS4_FS" "DEVTMPFS"
|
||||
];
|
||||
@@ -673,6 +779,8 @@ in
|
||||
};
|
||||
|
||||
users.extraGroups.systemd-journal.gid = config.ids.gids.systemd-journal;
|
||||
users.extraUsers.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
|
||||
users.extraGroups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
|
||||
|
||||
# Generate timer units for all services that have a ‘startAt’ value.
|
||||
systemd.timers =
|
||||
@@ -682,43 +790,25 @@ in
|
||||
})
|
||||
(filterAttrs (name: service: service.startAt != "") cfg.services);
|
||||
|
||||
# FIXME: These are borrowed from upstream systemd.
|
||||
systemd.services."systemd-update-utmp" =
|
||||
{ description = "Update UTMP about System Reboot/Shutdown";
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
after = [ "systemd-remount-fs.service" ];
|
||||
before = [ "sysinit.target" "shutdown.target" ];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
unitConfig = {
|
||||
DefaultDependencies = false;
|
||||
RequiresMountsFor = "/var/log";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${systemd}/lib/systemd/systemd-update-utmp reboot";
|
||||
ExecStop = "${systemd}/lib/systemd/systemd-update-utmp shutdown";
|
||||
};
|
||||
restartIfChanged = false;
|
||||
systemd.sockets.systemd-journal-gatewayd.wantedBy =
|
||||
optional config.services.journald.enableHttpGateway "sockets.target";
|
||||
|
||||
# Provide the systemd-user PAM service, required to run systemd
|
||||
# user instances.
|
||||
security.pam.services.systemd-user =
|
||||
{ # Ensure that pam_systemd gets included. This is special-cased
|
||||
# in systemd to provide XDG_RUNTIME_DIR.
|
||||
startSession = true;
|
||||
};
|
||||
|
||||
systemd.services."systemd-random-seed" =
|
||||
{ description = "Load/Save Random Seed";
|
||||
wantedBy = [ "sysinit.target" "multi-user.target" ];
|
||||
after = [ "systemd-remount-fs.service" ];
|
||||
before = [ "sysinit.target" "shutdown.target" ];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
unitConfig = {
|
||||
DefaultDependencies = false;
|
||||
RequiresMountsFor = "/var/lib";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${systemd}/lib/systemd/systemd-random-seed load";
|
||||
ExecStop = "${systemd}/lib/systemd/systemd-random-seed save";
|
||||
};
|
||||
};
|
||||
environment.etc."tmpfiles.d/x11.conf".source = "${systemd}/example/tmpfiles.d/x11.conf";
|
||||
|
||||
environment.etc."tmpfiles.d/nixos.conf".text =
|
||||
''
|
||||
# This file is created automatically and should not be modified.
|
||||
# Please change the option ‘systemd.tmpfiles.rules’ instead.
|
||||
${concatStringsSep "\n" cfg.tmpfiles.rules}
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Management of static files in /etc.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
@@ -19,6 +19,8 @@ let
|
||||
sources = map (x: x.source) etc';
|
||||
targets = map (x: x.target) etc';
|
||||
modes = map (x: x.mode) etc';
|
||||
uids = map (x: x.uid) etc';
|
||||
gids = map (x: x.gid) etc';
|
||||
};
|
||||
|
||||
in
|
||||
@@ -87,6 +89,24 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
uid = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = ''
|
||||
UID of created file. Only takes affect when the file is
|
||||
copied (that is, the mode is not 'symlink').
|
||||
'';
|
||||
};
|
||||
|
||||
gid = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = ''
|
||||
GID of created file. Only takes affect when the file is
|
||||
copied (that is, the mode is not 'symlink').
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
@@ -6,6 +6,8 @@ set -f
|
||||
sources_=($sources)
|
||||
targets_=($targets)
|
||||
modes_=($modes)
|
||||
uids_=($uids)
|
||||
gids_=($gids)
|
||||
set +f
|
||||
|
||||
for ((i = 0; i < ${#targets_[@]}; i++)); do
|
||||
@@ -35,6 +37,8 @@ for ((i = 0; i < ${#targets_[@]}; i++)); do
|
||||
|
||||
if test "${modes_[$i]}" != symlink; then
|
||||
echo "${modes_[$i]}" > $out/etc/$target.mode
|
||||
echo "${uids_[$i]}" > $out/etc/$target.uid
|
||||
echo "${gids_[$i]}" > $out/etc/$target.gid
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
@@ -60,7 +60,15 @@ sub link {
|
||||
if ($mode eq "direct-symlink") {
|
||||
atomicSymlink readlink("$static/$fn"), $target or warn;
|
||||
} else {
|
||||
open UID, "<$_.uid";
|
||||
my $uid = <UID>; chomp $uid;
|
||||
close UID;
|
||||
open GID, "<$_.gid";
|
||||
my $gid = <GID>; chomp $gid;
|
||||
close GID;
|
||||
|
||||
copy "$static/$fn", "$target.tmp" or warn;
|
||||
chown int($uid), int($gid), "$target.tmp" or warn;
|
||||
chmod oct($mode), "$target.tmp" or warn;
|
||||
rename "$target.tmp", $target or warn;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
with import ../boot/systemd-unit-options.nix { inherit config pkgs; };
|
||||
with lib;
|
||||
with import ../boot/systemd-unit-options.nix { inherit config lib; };
|
||||
|
||||
let
|
||||
|
||||
@@ -93,7 +93,7 @@ let
|
||||
if job.daemonType == "fork" || job.daemonType == "daemon" then { Type = "forking"; GuessMainPID = true; } else
|
||||
if job.daemonType == "none" then { } else
|
||||
throw "invalid daemon type `${job.daemonType}'")
|
||||
// optionalAttrs (!job.task && job.respawn)
|
||||
// optionalAttrs (!job.task && !(job.script == "" && job.exec == "") && job.respawn)
|
||||
{ Restart = "always"; }
|
||||
// optionalAttrs job.task
|
||||
{ Type = "oneshot"; RemainAfterExit = false; };
|
||||
|
||||
Reference in New Issue
Block a user