Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support network in initrd and entering LUKS passphrase via SSH #10460

Merged
merged 8 commits into from Nov 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Expand Up @@ -436,6 +436,7 @@
./system/activation/top-level.nix
./system/boot/coredump.nix
./system/boot/emergency-mode.nix
./system/boot/initrd-network.nix
./system/boot/kernel.nix
./system/boot/kexec.nix
./system/boot/loader/efi.nix
Expand Down
149 changes: 149 additions & 0 deletions nixos/modules/system/boot/initrd-network.nix
@@ -0,0 +1,149 @@
{ config, lib, pkgs, ... }:

with lib;

let
cfg = config.boot.initrd.network;

in
{

options = {

boot.initrd.network.enable = mkOption {
type = types.bool;
default = false;
description = ''
Add network connectivity support to initrd.

Network options are configured via <literal>ip</literal> kernel
option, according to the kernel documentation.
'';
};

boot.initrd.network.ssh.enable = mkOption {
type = types.bool;
default = false;
description = ''
Start SSH service during initrd boot. It can be used to debug failing
boot on a remote server, enter pasphrase for an encrypted partition etc.
Service is killed when stage-1 boot is finished.
'';
};

boot.initrd.network.ssh.port = mkOption {
type = types.int;
default = 22;
description = ''
Port on which SSH initrd service should listen.
'';
};

boot.initrd.network.ssh.shell = mkOption {
type = types.str;
default = "/bin/ash";
description = ''
Login shell of the remote user. Can be used to limit actions user can do.
'';
};

boot.initrd.network.ssh.hostRSAKey = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
RSA SSH private key file in the Dropbear format.

WARNING: This key is contained insecurely in the global Nix store. Do NOT
use your regular SSH host private keys for this purpose or you'll expose
them to regular users!
'';
};

boot.initrd.network.ssh.hostDSSKey = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
DSS SSH private key file in the Dropbear format.

WARNING: This key is contained insecurely in the global Nix store. Do NOT
use your regular SSH host private keys for this purpose or you'll expose
them to regular users!
'';
};

boot.initrd.network.ssh.hostECDSAKey = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
ECDSA SSH private key file in the Dropbear format.

WARNING: This key is contained insecurely in the global Nix store. Do NOT
use your regular SSH host private keys for this purpose or you'll expose
them to regular users!
'';
};

boot.initrd.network.ssh.authorizedKeys = mkOption {
type = types.listOf types.str;
default = config.users.extraUsers.root.openssh.authorizedKeys.keys;
description = ''
Authorized keys for the root user on initrd.
'';
};

};

config = mkIf cfg.enable {

boot.initrd.kernelModules = [ "af_packet" ];

boot.initrd.extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.mkinitcpio-nfs-utils}/bin/ipconfig
'' + optionalString cfg.ssh.enable ''
copy_bin_and_libs ${pkgs.dropbear}/bin/dropbear

cp -pv ${pkgs.glibc}/lib/libnss_files.so.* $out/lib
'';

boot.initrd.extraUtilsCommandsTest = optionalString cfg.ssh.enable ''
$out/bin/dropbear -V
'';

boot.initrd.postEarlyDeviceCommands = ''
# Search for interface definitions in command line
for o in $(cat /proc/cmdline); do
case $o in
ip=*)
ipconfig $o && hasNetwork=1
;;
esac
done
'' + optionalString cfg.ssh.enable ''
if [ -n "$hasNetwork" ]; then
mkdir /dev/pts
mount -t devpts devpts /dev/pts

mkdir -p /etc
echo 'root:x:0:0:root:/root:${cfg.ssh.shell}' > /etc/passwd
echo '${cfg.ssh.shell}' > /etc/shells
echo 'passwd: files' > /etc/nsswitch.conf

mkdir -p /var/log
touch /var/log/lastlog

mkdir -p /etc/dropbear
${optionalString (cfg.ssh.hostRSAKey != null) "ln -s ${cfg.ssh.hostRSAKey} /etc/dropbear/dropbear_rsa_host_key"}
${optionalString (cfg.ssh.hostDSSKey != null) "ln -s ${cfg.ssh.hostDSSKey} /etc/dropbear/dropbear_dss_host_key"}
${optionalString (cfg.ssh.hostECDSAKey != null) "ln -s ${cfg.ssh.hostECDSAKey} /etc/dropbear/dropbear_ecdsa_host_key"}

mkdir -p /root/.ssh
${concatStrings (map (key: ''
echo -n ${escapeShellArg key} >> /root/.ssh/authorized_keys
'') cfg.ssh.authorizedKeys)}

dropbear -s -j -k -E -m -p ${toString cfg.ssh.port}
fi
'';

};
}
26 changes: 19 additions & 7 deletions nixos/modules/system/boot/luksroot.nix
Expand Up @@ -32,9 +32,12 @@ let
''}

open_normally() {
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
echo luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
${optionalString (header != null) "--header=${header}"} \
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"} \
> /.luksopen_args
cryptsetup-askpass
rm /.luksopen_args
}

${optionalString (luks.yubikeySupport && (yubikey != null)) ''
Expand Down Expand Up @@ -418,6 +421,18 @@ in
boot.initrd.extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup

cat > $out/bin/cryptsetup-askpass <<EOF
#!$out/bin/sh -e
if [ -e /.luksopen_args ]; then
cryptsetup \$(cat /.luksopen_args)
killall cryptsetup
else
echo "Passphrase is not requested now"
exit 1
fi
EOF
chmod +x $out/bin/cryptsetup-askpass

${optionalString luks.yubikeySupport ''
copy_bin_and_libs ${pkgs.ykpers}/bin/ykchalresp
copy_bin_and_libs ${pkgs.ykpers}/bin/ykinfo
Expand All @@ -432,6 +447,8 @@ in

cat > $out/bin/openssl-wrap <<EOF
#!$out/bin/sh
export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
$out/bin/openssl "\$@"
EOF
chmod +x $out/bin/openssl-wrap
''}
Expand All @@ -442,11 +459,6 @@ in
${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
''}
'';
Expand Down
4 changes: 4 additions & 0 deletions nixos/modules/system/boot/stage-1-init.sh
Expand Up @@ -149,6 +149,10 @@ udevadm trigger --action=add
udevadm settle


# Additional devices initialization.
@postEarlyDeviceCommands@


# Load boot-time keymap before any LVM/LUKS initialization
@extraUtils@/bin/busybox loadkmap < "@busyboxKeymap@"

Expand Down
12 changes: 10 additions & 2 deletions nixos/modules/system/boot/stage-1.nix
Expand Up @@ -104,7 +104,7 @@ let
stripDirs "lib bin" "-s"

# Run patchelf to make the programs refer to the copied libraries.
for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs -e $out $i; fi; done

for i in $out/bin/*; do
if ! test -L $i; then
Expand Down Expand Up @@ -203,7 +203,7 @@ let
inherit (config.boot) resumeDevice devSize runSize;

inherit (config.boot.initrd) checkJournalingFS
preLVMCommands postDeviceCommands postMountCommands kernelModules;
postEarlyDeviceCommands preLVMCommands postDeviceCommands postMountCommands kernelModules;

resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}")
(filter (sd: sd ? label || hasPrefix "/dev/" sd.device) config.swapDevices);
Expand Down Expand Up @@ -313,6 +313,14 @@ in
'';
};

boot.initrd.postEarlyDeviceCommands = mkOption {
default = "";
type = types.lines;
description = ''
Shell commands to be executed early after creation of device nodes.
'';
};

boot.initrd.postMountCommands = mkOption {
default = "";
type = types.lines;
Expand Down
25 changes: 20 additions & 5 deletions pkgs/build-support/nuke-references/builder.sh
Expand Up @@ -3,11 +3,26 @@ source $stdenv/setup
mkdir -p $out/bin
cat > $out/bin/nuke-refs <<EOF
#! $SHELL -e
for i in \$*; do
if test ! -L \$i -a -f \$i; then
cat \$i | sed "s|$NIX_STORE/[a-z0-9]*-|$NIX_STORE/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-|g" > \$i.tmp
if test -x \$i; then chmod +x \$i.tmp; fi
mv \$i.tmp \$i

excludes=""
while getopts e: o; do
case "\$o" in
e) storeId=\$(echo "\$OPTARG" | sed -n "s|^$NIX_STORE/\\([a-z0-9]\{32\}\\)-.*|\1|p")
if [ -z "\$storeId" ]; then
echo "-e argument must be a Nix store path"
exit 1
fi
excludes="\$excludes(?!\$storeId)"
;;
esac
done
shift \$((\$OPTIND-1))

for i in "\$@"; do
if test ! -L "\$i" -a -f "\$i"; then
cat "\$i" | $perl/bin/perl -pe "s|$NIX_STORE/\$excludes[a-z0-9]{32}-|$NIX_STORE/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-|g" > "\$i.tmp"
if test -x "\$i"; then chmod +x "\$i.tmp"; fi
mv "\$i.tmp" "\$i"
fi
done
EOF
Expand Down
5 changes: 3 additions & 2 deletions pkgs/build-support/nuke-references/default.nix
Expand Up @@ -3,9 +3,10 @@
# path (/nix/store/eeee...). This is useful for getting rid of
# dependencies that you know are not actually needed at runtime.

{stdenv}:
{ stdenv, perl }:

stdenv.mkDerivation {
name = "nuke-references";
builder = ./builder.sh;
}
inherit perl;
}
26 changes: 26 additions & 0 deletions pkgs/os-specific/linux/mkinitcpio-nfs-utils/default.nix
@@ -0,0 +1,26 @@
{ stdenv, fetchurl, xz }:

stdenv.mkDerivation rec {
name = "mkinitcpio-nfs-utils-0.3";

src = fetchurl {
url = "https://sources.archlinux.org/other/mkinitcpio/${name}.tar.xz";
sha256 = "0fc93sfk41ycpa33083kyd7i4y00ykpbhj5qlw611bjghj4x946j";
# ugh, upstream...
name = "${name}.tar.gz";
};

makeFlags = [ "DESTDIR=$(out)" "bindir=/bin" ];

postInstall = ''
rm -rf $out/usr
'';

meta = with stdenv.lib; {
homepage = https://archlinux.org/;
description = "ipconfig and nfsmount tools for root on NFS, ported from klibc";
license = licenses.gpl2;
platforms = platforms.linux;
maintainers = with maintainers; [ abbradar ];
};
}
6 changes: 4 additions & 2 deletions pkgs/tools/networking/dropbear/default.nix
Expand Up @@ -35,9 +35,11 @@ stdenv.mkDerivation rec {

buildInputs = [ zlib ];

meta = {
meta = with stdenv.lib; {
homepage = http://matt.ucc.asn.au/dropbear/dropbear.html;
description = "An small footprint implementation of the SSH 2 protocol";
license = stdenv.lib.licenses.mit;
license = licenses.mit;
maintainers = with maintainers; [ abbradar ];
platforms = platforms.unix;
};
}
23 changes: 14 additions & 9 deletions pkgs/tools/networking/dropbear/pass-path.patch
@@ -1,31 +1,36 @@
diff --git a/svr-chansession.c b/svr-chansession.c
index 23dad8c..32cac13 100644
index e44299e..7ef750a 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -823,6 +823,7 @@ static void addchildpid(struct ChanSess *chansess, pid_t pid) {
@@ -893,6 +893,8 @@ static void addchildpid(struct ChanSess *chansess, pid_t pid) {
static void execchild(void *user_data) {
struct ChanSess *chansess = user_data;
char *usershell = NULL;
+ const char *path = DEFAULT_PATH;
+ const char *ldpath = NULL;

/* with uClinux we'll have vfork()ed, so don't want to overwrite the
* hostkey. can't think of a workaround to clear it */
@@ -835,6 +836,9 @@ static void execchild(void *user_data) {
reseedrandom();
/* with uClinux we'll have vfork()ed, so don't want to overwrite the
* hostkey. can't think of a workaround to clear it */
@@ -905,6 +907,10 @@ static void execchild(void *user_data) {
seedrandom();
#endif

+ if (getenv("PATH"))
+ path = getenv("PATH");
+ if (getenv("PATH"))
+ path = getenv("PATH");
+ ldpath = getenv("LD_LIBRARY_PATH");
+
/* clear environment */
/* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
* etc. This is hazardous, so should only be used for debugging. */
@@ -878,7 +882,7 @@ static void execchild(void *user_data) {
@@ -948,7 +954,10 @@ static void execchild(void *user_data) {
addnewvar("LOGNAME", ses.authstate.pw_name);
addnewvar("HOME", ses.authstate.pw_dir);
addnewvar("SHELL", get_user_shell());
- addnewvar("PATH", DEFAULT_PATH);
+ addnewvar("PATH", path);
+ if (ldpath != NULL) {
+ addnewvar("LD_LIBRARY_PATH", ldpath);
+ }
if (chansess->term != NULL) {
addnewvar("TERM", chansess->term);
}
2 changes: 2 additions & 0 deletions pkgs/top-level/all-packages.nix
Expand Up @@ -10063,6 +10063,8 @@ let
systemd = systemd.override { enableKDbus = true; };
};

mkinitcpio-nfs-utils = callPackage ../os-specific/linux/mkinitcpio-nfs-utils { };

module_init_tools = callPackage ../os-specific/linux/module-init-tools { };

aggregateModules = modules:
Expand Down