diff options
author | Thomas Deutschmann <whissi@gentoo.org> | 2019-07-14 11:11:09 +0200 |
---|---|---|
committer | Thomas Deutschmann <whissi@gentoo.org> | 2019-07-14 13:58:15 +0200 |
commit | 11eff5eb7ad4330c2688ff4795ba33bbf6efcc47 (patch) | |
tree | f50a9947ae3a0ebef542521cdd3de401fe43d03a | |
parent | Add genkernel worker module "dropbear" (diff) | |
download | genkernel-11eff5eb7ad4330c2688ff4795ba33bbf6efcc47.tar.gz genkernel-11eff5eb7ad4330c2688ff4795ba33bbf6efcc47.tar.bz2 genkernel-11eff5eb7ad4330c2688ff4795ba33bbf6efcc47.zip |
Rework --ssh support
- To enable sshd in initramfs user MUST now set "dosshd" kernel
command-line parameter.
- "gk.sshd.wait" kernel command-line parameter was added to interrupt
boot process for X seconds to allow for remote login (can be used like
an remote rescue shell).
- For remote unlock of LUKS-encrypted root or swap device, user can still
send unencrypted keyfile via SSH like
$ cat ~/root.unencrypted.key | ssh root@<remote-host> -C post root
or user can now just SSH into the remote host and call "unlock-luks"
like
remote rescueshell ~ # unlock-luks root
to get a cryptsetup prompt.
NOTE: When manually unlocking the encrypted LUKS device, user must call
"resume-boot" afterwards to resume booting.
- "--ssh-authorized-keys-file" parameter added which can be used to
specify a different file than default "/etc/dropbear/authorized_keys"
file.
- "--ssh-host-keys" parameter added to control if in initramfs embedded
sshd should create its own pair of hosts keys (which will be stored in
"/etc/dropbear for re-use, default), use host keys from host system or
should generate host keys at runtime on each boot.
- "ip" kernel command-line parameter will now default to DHCP usage but
does also support addr/CIDR notation to specify a static address.
- "gk.net.iface" kernel command-line parameter was added to use a
different interface than "eth0". You can either use an interface name
or use a MAC address.
- "gk.net.gw" kernel command-line parameter was added which will allow
user to set specific gateway when DHCP isn't used.
- "gk.net.routes" kernel command-line parameter was added which will allow
user to set additional routes when DHCP isn't used.
Please read manpage for additional parameters and more details.
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
-rw-r--r-- | defaults/busy-config | 8 | ||||
-rw-r--r-- | defaults/initrd.defaults | 22 | ||||
-rw-r--r-- | defaults/initrd.scripts | 302 | ||||
-rw-r--r-- | defaults/linuxrc | 40 | ||||
-rw-r--r-- | defaults/login-remote.sh | 152 | ||||
-rw-r--r-- | defaults/resume-boot.sh | 15 | ||||
-rw-r--r-- | defaults/software.sh | 14 | ||||
-rw-r--r-- | defaults/unlock-luks.sh | 130 | ||||
-rw-r--r-- | doc/genkernel.8.txt | 59 | ||||
-rwxr-xr-x | gen_cmdline.sh | 26 | ||||
-rwxr-xr-x | gen_determineargs.sh | 2 | ||||
-rwxr-xr-x | gen_funcs.sh | 131 | ||||
-rwxr-xr-x | gen_initramfs.sh | 313 | ||||
-rwxr-xr-x | genkernel | 1 | ||||
-rw-r--r-- | gkbuilds/dropbear.gkbuild | 68 |
15 files changed, 1101 insertions, 182 deletions
diff --git a/defaults/busy-config b/defaults/busy-config index fa82f09..1184633 100644 --- a/defaults/busy-config +++ b/defaults/busy-config @@ -46,8 +46,8 @@ CONFIG_FEATURE_INSTALLER=y CONFIG_LONG_OPTS=y CONFIG_FEATURE_DEVPTS=y # CONFIG_FEATURE_CLEAN_UP is not set -# CONFIG_FEATURE_UTMP is not set -# CONFIG_FEATURE_WTMP is not set +CONFIG_FEATURE_UTMP=y +CONFIG_FEATURE_WTMP=y # CONFIG_FEATURE_PIDFILE is not set CONFIG_PID_FILE_PATH="" CONFIG_FEATURE_SUID=y @@ -637,8 +637,8 @@ CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y CONFIG_IONICE=y # CONFIG_IPCRM is not set # CONFIG_IPCS is not set -# CONFIG_LAST is not set -# CONFIG_FEATURE_LAST_FANCY is not set +CONFIG_LAST=y +CONFIG_FEATURE_LAST_FANCY=y CONFIG_LOSETUP=y CONFIG_LSPCI=y CONFIG_LSUSB=y diff --git a/defaults/initrd.defaults b/defaults/initrd.defaults index e1d16b8..6c6abec 100644 --- a/defaults/initrd.defaults +++ b/defaults/initrd.defaults @@ -70,6 +70,28 @@ CDROOT_PATH='/mnt/cdrom' CDROOT_MARKER='/livecd' VERIFY=0 +IP='dhcp' +GK_NET_DHCP_PIDFILE='/var/run/udhcpc.pid' +GK_NET_DHCP_RETRIES=3 +GK_NET_GW= +GK_NET_IFACE='eth0' +GK_NET_LOCKFILE='/tmp/network.started' +GK_NET_ROUTES= +GK_NET_TIMEOUT_DAD=10 +GK_NET_TIMEOUT_DECONFIGURATION=10 +GK_NET_TIMEOUT_DHCP=10 +GK_SHELL_LOCKFILE='/var/run/rescueshell.pid' +GK_SSHD_LOCKFILE='/tmp/remote-rescueshell.lock' +GK_SSHD_PIDFILE='/var/run/dropbear.pid' +GK_SSHD_PORT=22 +GK_SSHD_WAIT= + +CRYPT_ENV_FILE='/etc/CRYPT_ENV.conf' +CRYPT_KEYFILE_ROOT='/tmp/root.key' +CRYPT_KEYFILE_SWAP='/tmp/swap.key' +CRYPT_ROOT_OPENED_LOCKFILE='/tmp/ROOT.opened' +CRYPT_SWAP_OPENED_LOCKFILE='/tmp/SWAP.opened' + # Flag for if ok when using CDROOT got_good_root='0' # if LOOP found on root before mount, trigger Unpacking additional packages diff --git a/defaults/initrd.scripts b/defaults/initrd.scripts index d56cee1..be76a4d 100644 --- a/defaults/initrd.scripts +++ b/defaults/initrd.scripts @@ -685,7 +685,7 @@ setup_overlayfs() { findnfsmount() { - if [ "${IP}" != '' ] || busybox udhcpc -n -T 15 -q + if start_network then [ -e /rootpath ] && NFSROOT=$(cat /rootpath) @@ -1390,7 +1390,7 @@ openLUKS() { eval local LUKS_DEVICE='"${CRYPT_'${TYPE}'}"' LUKS_NAME="$1" LUKS_KEY='"${CRYPT_'${TYPE}'_KEY}"' eval local LUKS_KEYDEV='"${CRYPT_'${TYPE}'_KEYDEV}"' LUKS_TRIM='"${CRYPT_'${TYPE}'_TRIM}"' - local OPENED_LOCKFILE="/${TYPE}.decrypted" + eval local OPENED_LOCKFILE='"${CRYPT_'${TYPE}'_OPENED_LOCKFILE}"' local DEV_ERROR=0 KEY_ERROR=0 KEYDEV_ERROR=0 local mntkey="/mnt/key/" crypt_filter_ret= cryptsetup_options='' @@ -1550,15 +1550,206 @@ openLUKS() { rmdir -p "${mntkey}" >/dev/null 2>&1 } -start_LUKS() { - # if key is set but neither ssh enabled or key device is given, find - # the key device +iface_name() { + local ifname="${1}" + + if echo "${ifname}" | grep -qE ':|-' + then + local interface= + local mac="$(echo "${ifname}" | sed 'y/ABCDEF-/abcdef:/')" + + for interface in /sys/class/net/* + do + if [ $(cat ${interface}/address) = "${mac}" ] + then + echo ${interface##*/} + return + fi + done + else + echo "${ifname}" + fi +} + +start_network() { + # At least gk.net.iface can only be processed after sysfs was + # mounted. + local x= + for x in ${CMDLINE} + do + case "${x}" in + ip=*) + IP=${x#*=} + ;; + gk.net.dhcp.retries=*) + local tmp_n_retries=${x#*=} + if is_int "${tmp_n_retries}" + then + GK_NET_DHCP_RETRIES=${tmp_n_retries} + else + warn_msg "'${x}' does not look like a valid number -- will keep using default value ${GK_NET_DHCP_RETRIES}!" + fi + unset tmp_n_retries + ;; + gk.net.gw=*) + GK_NET_GW=${x#*=} + ;; + gk.net.iface=*) + local tmp_iface=$(iface_name "${x#*=}") + if [ -z "${tmp_iface}" ] + then + warn_msg "Interface specified by '${x}' not found, falling back to genkernel defaults ..." + else + GK_NET_IFACE=${tmp_iface} + fi + ;; + gk.net.routes=*) + GK_NET_ROUTES=${x#*=} + ;; + gk.net.timeout.dad=*) + local tmp_dad_timeout=${x#*=} + if is_int "${tmp_dad_timeout}" + then + GK_NET_TIMEOUT_DAD=${tmp_dad_timeout} + else + warn_msg "'${x}' does not look like a valid number -- will keep using default value ${GK_NET_TIMEOUT_DAD}!" + fi + unset tmp_dad_timeout + ;; + gk.net.timeout.deconfiguration=*) + local tmp_deconfiguration_timeout=${x#*=} + if is_int "${tmp_deconfiguration_timeout}" + then + GK_NET_TIMEOUT_DECONFIGURATION=${tmp_deconfiguration_timeout} + else + warn_msg "'${x}' does not look like a valid number -- will keep using default value ${GK_NET_TIMEOUT_DECONFIGURATION}!" + fi + unset tmp_deconfiguration_timeout + ;; + gk.net.timeout.dhcp=*) + local tmp_dhcp_timeout=${x#*=} + if is_int "${tmp_dhcp_timeout}" + then + GK_NET_TIMEOUT_DHCP=${tmp_dhcp_timeout} + else + warn_msg "'${x}' does not look like a valid number -- will keep using default value ${GK_NET_TIMEOUT_DHCP}!" + fi + unset tmp_dhcp_timeout + ;; + esac + done + + if [ -z "${IP}" -o "${IP}" = 'dhcp' ] + then + good_msg "Bringing up interface ${GK_NET_IFACE} using dhcp ..." ${QUIET} + busybox udhcpc -i "${GK_NET_IFACE}" -n -t ${GK_NET_DHCP_RETRIES} -T ${GK_NET_TIMEOUT_DHCP} -R -p "${GK_NET_DHCP_PIDFILE}" + else + good_msg "Bringing up interface ${GK_NET_IFACE} ..." ${QUIET} + ip link set "${GK_NET_IFACE}" up + + good_msg "Setting address '${IP}' on ${GK_NET_IFACE} ..." ${QUIET} + ip addr add "${IP}" dev "${GK_NET_IFACE}" + + if [ -n "${GK_NET_ROUTES}" ] + then + local route= + for route in ${GK_NET_ROUTES} + do + good_msg "Adding additional route '${route}' on ${GK_NET_IFACE} ..." ${QUIET} + ip route add "${route}" dev "${GK_NET_IFACE}" + done + fi + + if [ -n "${GK_NET_GW}" ] + then + good_msg "Adding default route via '${GK_NET_GW}' on ${GK_NET_IFACE} ..." ${QUIET} + ip route add default via "${GK_NET_GW}" dev "${GK_NET_IFACE}" + fi + fi + + touch "${GK_NET_LOCKFILE}" +} - if [ -e "/usr/sbin/dropbear" ] +kill_network() { + if [ -s "${GK_NET_DHCP_PIDFILE}" ] then - startdropbear + good_msg "Stopping udhcpc ..." + kill $(cat "${GK_NET_DHCP_PIDFILE}") + fi + + # If we are too quick and interface is still configuring IPv6 + # waiting for DAD to complete due to received IPv6 RA, we have to + # wait. + # If we don't wait, ip command will bring down interface for a + # moment but it will return to up state once DAD is completed which + # will cause trouble with real system which is expecting an unused + # interface. + if ipv6_tentative + then + [ -z "${QUIET}" ] && \ + printf "%b" "${WARN}**${NORMAL}${BOLD} Waiting for tentative IPv6 addresses to complete DAD ${NORMAL}..." + + local dad_timeout=10 + while [ ${dad_timeout} -gt 0 ] + do + ipv6_tentative || break + sleep 1 + : $(( dad_timeout -= 1 )) + [ -z "${QUIET}" ] && \ + printf "." + done + + echo "" + + if [ ${dad_timeout} -le 0 ] + then + bad_msg "DAD still not completed after ${GK_NET_TIMEOUT_DAD} seconds!" + fi fi + [ -z "${QUIET}" ] && \ + printf "%b" "${GOOD}>>${NORMAL}${BOLD} Bringing down interface ${GK_NET_IFACE} ${NORMAL}..." + + local deconfiguration_timeout=${GK_NET_TIMEOUT_DECONFIGURATION} + while [ ${deconfiguration_timeout} -gt 0 ] + do + ip addr flush dev "${GK_NET_IFACE}" + ip route flush dev "${GK_NET_IFACE}" + ip link set "${GK_NET_IFACE}" down + if grep -q "down" "/sys/class/net/${GK_NET_IFACE}/operstate" + then + break + fi + sleep 1 + : $(( deconfiguration_timeout -= 1 )) + [ -z "${QUIET}" ] && \ + printf "." + done + + echo "" + + if [ ${deconfiguration_timeout} -le 0 ] + then + bad_msg "Failed to bring down interface ${GK_NET_IFACE} within ${GK_NET_TIMEOUT_DECONFIGURATION} seconds!" + return + fi + + rm "${GK_NET_LOCKFILE}" +} + +ipv6_tentative() { + if ip -family inet6 addr show dev "${GK_NET_IFACE}" | grep -q " tentative " + then + return 0 + else + return 1 + fi +} + +start_LUKS() { + # if key is set but neither ssh enabled or key device is given, find + # the key device + [ -n "${CRYPT_ROOT_KEY}" ] && [ -z "${CRYPT_ROOT_KEYDEV}" ] \ && sleep 6 && bootstrapKey "ROOT" @@ -1589,6 +1780,103 @@ start_LUKS() { fi } +start_sshd() { + if [ -s "${GK_SSHD_PIDFILE}" ] + then + # Already running + return + fi + + if [ ! -x "/usr/sbin/dropbear" ] + then + bad_msg "/usr/sbin/dropbear not found! Did you call genkernel with --ssh parameter?" + return + fi + + # setup environment variables for the ssh login shell + local varname= varvalue= + touch "${CRYPT_ENV_FILE}" + for varname in CRYPT_ROOT CRYPT_ROOT_TRIM CRYPT_SILENT CRYPT_SWAP DEBUG + do + eval varvalue=\$${varname} + echo "${varname}=${varvalue}" >> "${CRYPT_ENV_FILE}" + done + + touch /var/log/lastlog + + good_msg "Starting dropbear sshd ..." ${QUIET} + /usr/sbin/dropbear -p ${GK_SSHD_PORT} -R -P "${GK_SSHD_PIDFILE}" 2>/var/log/dropbear.log + test_success "Failed to start dropbear" +} + +wait_sshd() { + if [ -z "${GK_SSHD_WAIT}" -o "${GK_SSHD_WAIT}" = '0' ] + then + return + fi + + if [ -f "${GK_SSHD_LOCKFILE}" -o ! -s "${GK_SSHD_PIDFILE}" ] + then + return + fi + + printf "%b" "${GOOD}>>${NORMAL}${BOLD} gk.sshd.wait set; Waiting ${GK_SSHD_WAIT} seconds for SSH connection ${NORMAL}..." + + local ssh_timeout=${GK_SSHD_WAIT} + while [ ${ssh_timeout} -gt 0 ] + do + if [ -f "${GK_SSHD_LOCKFILE}" ] + then + echo "" + last -W | head -n 3 2>/dev/null + break + fi + sleep 1 + : $(( ssh_timeout -= 1 )) + printf "." + done + + echo "" +} + +kill_sshd() { + if [ -s "${GK_SSHD_PIDFILE}" ] + then + good_msg "Stopping dropbear sshd ..." ${QUIET} + kill $(cat "${GK_SSHD_PIDFILE}") + fi +} + +cleanup() { + if [ -f "${GK_NET_LOCKFILE}" ] + then + if [ -f "${GK_SSHD_LOCKFILE}" ] + then + warn_msg "The lockfile at '${GK_SSHD_LOCKFILE}' exists." + warn_msg "The boot process will be paused until the lock is removed." + while true + do + if [ -f "${GK_SSHD_LOCKFILE}" ] + then + sleep 1 + else + break + fi + done + fi + fi + + kill_sshd + + # Ensure that we terminated any existing connection + pkill -9 dropbear >/dev/null 2>&1 + + if [ -f "${GK_NET_LOCKFILE}" ] + then + kill_network + fi +} + sdelay() { # Sleep a specific number of seconds if SDELAY is set if [ -n "${SDELAY}" ] diff --git a/defaults/linuxrc b/defaults/linuxrc index 999f05c..e0a4115 100644 --- a/defaults/linuxrc +++ b/defaults/linuxrc @@ -191,10 +191,8 @@ do part|partitionable) MDPART=1 ;; + # For network options see start_network() in initrd.scripts # NFS - ip=*) - IP=${x#*=} - ;; nfsroot=*) NFSROOT=${x#*=} ;; @@ -263,6 +261,29 @@ do crypt_silent) CRYPT_SILENT=1 ;; + dosshd) + USE_SSH=1 + ;; + gk.sshd.port=*) + tmp_port=${x#*=} + if is_int "${tmp_port}" + then + GK_SSHD_PORT=${tmp_port} + else + warn_msg "'${x}' does not look like a valid port -- ignored!" + fi + unset tmp_port + ;; + gk.sshd.wait=*) + tmp_wait=${x#*=} + if is_int "${tmp_wait}" + then + GK_SSHD_WAIT=${tmp_wait} + else + warn_msg "'${x}' does not look like a valid time (second) value -- ignored!" + fi + unset tmp_wait + ;; real_rootflags=*) REAL_ROOTFLAGS=${x#*=} ;; @@ -467,6 +488,12 @@ start_volumes setup_keymap +if [ "${USE_SSH}" = '1' ] +then + start_network + start_sshd +fi + # Initialize LUKS root device except for livecd's if [ "${CDROOT}" != '1' ] then @@ -1113,6 +1140,13 @@ copyKeymap # Setup any user defined environment locales for desktop usage setup_locale +if [ "${USE_SSH}" = '1' ] +then + wait_sshd +fi + +cleanup + good_msg "Booting (initramfs)" cd "${CHROOT}" diff --git a/defaults/login-remote.sh b/defaults/login-remote.sh index c667b5e..7f711da 100644 --- a/defaults/login-remote.sh +++ b/defaults/login-remote.sh @@ -1,11 +1,9 @@ #!/bin/sh # vim: set noexpandtab: -. /etc/login-remote.conf . /etc/initrd.defaults . /etc/initrd.scripts -KEYFILE_ROOT="/tmp/root.key" -KEYFILE_SWAP="/tmp/swap.key" +. "${CRYPT_ENV_FILE}" splash() { return 0 @@ -16,109 +14,23 @@ splash() { receivefile() { case ${1} in root) - file=${KEYFILE_ROOT} + file=${CRYPT_KEYFILE_ROOT} ;; swap) - file=${KEYFILE_SWAP} + file=${CRYPT_KEYFILE_SWAP} + ;; + *) + bad_msg "Unknown '${1}' keyfile received." ${CRYPT_SILENT} + exit 1 ;; esac + # limit maximum stored bytes to 1M to avoid killing the server dd of=${file} count=1k bs=1k 2>/dev/null - exit $? + return $? } -openLUKSremote() { - case $1 in - root) - local TYPE=ROOT - ;; - swap) - local TYPE=SWAP - ;; - esac - - [ ! -d /tmp/key ] && mkdir -p /tmp/key - - eval local LUKS_DEVICE='"${CRYPT_'${TYPE}'}"' LUKS_NAME="$1" LUKS_KEY='"${KEYFILE_'${TYPE}'}"' - local DEV_ERROR=0 KEY_ERROR=0 - local input="" cryptsetup_options="" flag_opened="/${TYPE}.decrypted" - while [ 1 ] - do - local gpg_cmd="" crypt_filter_ret=42 - - if [ -e ${flag_opened} ] - then - good_msg "The LUKS device ${LUKS_DEVICE} meanwhile was opened by someone else." - break - elif [ ${DEV_ERROR} -eq 1 ] - then - prompt_user "LUKS_DEVICE" "${LUKS_NAME}" - DEV_ERROR=0 - else - LUKS_DEVICE=$(find_real_device "${LUKS_DEVICE}") - - setup_md_device ${LUKS_DEVICE} - cryptsetup isLuks ${LUKS_DEVICE} - if [ $? -ne 0 ] - then - bad_msg "The LUKS device ${LUKS_DEVICE} does not contain a LUKS header" ${CRYPT_SILENT} - DEV_ERROR=1 - continue - else - # Handle keys - if [ "x${LUKS_TRIM}" = "xyes" ] - then - good_msg "Enabling TRIM support for ${LUKS_NAME}." ${CRYPT_SILENT} - cryptsetup_options="${cryptsetup_options} --allow-discards" - fi - - if [ ${crypt_filter_ret} -ne 0 ] - then - # 1st try: unencrypted keyfile - crypt_filter "cryptsetup ${cryptsetup_options} --key-file ${LUKS_KEY} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" - crypt_filter_ret=$? - fi - - if [ -f /sbin/gpg ] && [ ${crypt_filter_ret} -ne 0 ] - then - # 2nd try: gpg-encrypted keyfile - [ -e /dev/tty ] && mv /dev/tty /dev/tty.org - mknod /dev/tty c 5 1 - gpg_cmd="/sbin/gpg --logger-file /dev/null --quiet --decrypt ${LUKS_KEY} |" - crypt_filter "${gpg_cmd}cryptsetup ${cryptsetup_options} --key-file ${LUKS_KEY} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" - crypt_filter_ret=$? - [ -e /dev/tty.org ] \ - && rm -f /dev/tty \ - && mv /dev/tty.org /dev/tty - fi - - if [ ${crypt_filter_ret} -ne 0 ] - then - # 3rd try: user-submitted passphrase - crypt_filter "cryptsetup ${cryptsetup_options} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" - crypt_filter_ret=$? - fi - - if [ ${crypt_filter_ret} -eq 0 ] - then - touch ${flag_opened} - good_msg "LUKS device ${LUKS_DEVICE} opened" ${CRYPT_SILENT} - # Kill the cryptsetup process started by init - # so that the boot process can continue - killall cryptsetup - break - else - bad_msg "Failed to open LUKS device ${LUKS_DEVICE}" ${CRYPT_SILENT} - DEV_ERROR=1 - fi - fi - fi - done - rm -f ${LUKS_KEY} - cd / - rmdir -p tmp/key -} if [ "x${1}" = "x-c" ] then @@ -128,9 +40,51 @@ then case ${command} in post) receivefile ${type} + if [ $? -eq 0 ] + then + unlock-luks ${type} + if [ $? -eq 0 ] + then + if [ "${type}" = 'root' ] + then + # this is required to keep scripted unlock working + # without requring an additional command. + resume-boot + fi + + exit 0 + else + exit 1 + fi + else + bad_msg "Keyfile was not properly received!" ${CRYPT_SILENT} + exit 1 + fi ;; + *) + bad_msg "Command '${command}' is not supported!" ${CRYPT_SILENT} + exit 1 esac else - [ -n "${CRYPT_ROOT}" ] && openLUKSremote root - [ -n "${CRYPT_SWAP}" ] && openLUKSremote swap + export PS1='remote rescueshell \w \# ' + touch "${GK_SSHD_LOCKFILE}" + good_msg "The lockfile '${GK_SSHD_LOCKFILE}' was created." + good_msg "In order to resume boot process, run 'resume-boot'." + good_msg "Be aware that it will kill your connection which means" + good_msg "you will no longer be able work in this shell." + + if [ -n "${CRYPT_ROOT}" -a ! -f "${CRYPT_ROOT_OPENED_LOCKFILE}" ] + then + good_msg "To remote unlock LUKS-encrypted root device, run 'unlock-luks root'." + fi + + if [ -n "${CRYPT_SWAP}" -a ! -f "${CRYPT_ROOT_OPENED_LOCKFILE}" ] + then + good_msg "To remote unlock LUKS-encrypted swap device, run 'unlock-luks swap'." + fi + + [ -x /bin/sh ] && SH=/bin/sh || SH=/bin/ash + exec ${SH} --login fi + +exit 0 diff --git a/defaults/resume-boot.sh b/defaults/resume-boot.sh new file mode 100644 index 0000000..cb29831 --- /dev/null +++ b/defaults/resume-boot.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +. /etc/initrd.defaults + +if [ -s "${GK_SHELL_LOCKFILE}" ] +then + kill -9 "$(cat "${GK_SHELL_LOCKFILE}")" +fi + +if [ -f "${GK_SSHD_LOCKFILE}" ] +then + rm "${GK_SSHD_LOCKFILE}" +fi + +exit 0 diff --git a/defaults/software.sh b/defaults/software.sh index 5a0d773..420eb62 100644 --- a/defaults/software.sh +++ b/defaults/software.sh @@ -36,6 +36,13 @@ DMRAID_DIR="${DMRAID_DIR:-dmraid/${DMRAID_VER}/dmraid}" DMRAID_SRCTAR="${DMRAID_SRCTAR:-${DISTDIR}/dmraid-${DMRAID_VER}.tar.bz2}" DMRAID_BINCACHE="${DMRAID_BINCACHE:-%%CACHE%%/dmraid-${DMRAID_VER}-%%ARCH%%.tar.bz2}" +GKPKG_DROPBEAR_PN="dropbear" +GKPKG_DROPBEAR_PV="${GKPKG_DROPBEAR_PV:-VERSION_DROPBEAR}" +GKPKG_DROPBEAR_DEPS="zlib" +GKPKG_DROPBEAR_SRCTAR="${GKPKG_DROPBEAR_SRCTAR:-${DISTDIR}/dropbear-${GKPKG_DROPBEAR_PV}.tar.bz2}" +GKPKG_DROPBEAR_SRCDIR="${GKPKG_DROPBEAR_SRCDIR:-dropbear-${GKPKG_DROPBEAR_PV}}" +GKPKG_DROPBEAR_BINPKG="${GKPKG_DROPBEAR_BINPKG:-%%CACHE%%/dropbear-${GKPKG_DROPBEAR_PV}-%%ARCH%%.tar.xz}" + GKPKG_E2FSPROGS_PN="e2fsprogs" GKPKG_E2FSPROGS_PV="${GKPKG_E2FSPROGS_PV:-VERSION_E2FSPROGS}" GKPKG_E2FSPROGS_DEPS="util-linux" @@ -77,3 +84,10 @@ GKPKG_UTIL_LINUX_DEPS="" GKPKG_UTIL_LINUX_SRCDIR="${GKPKG_UTIL_LINUX_SRCDIR:-util-linux-${GKPKG_UTIL_LINUX_PV}}" GKPKG_UTIL_LINUX_SRCTAR="${GKPKG_UTIL_LINUX_SRCTAR:-${DISTDIR}/util-linux-${GKPKG_UTIL_LINUX_PV}.tar.xz}" GKPKG_UTIL_LINUX_BINPKG="${GKPKG_UTIL_LINUX_BINPKG:-%%CACHE%%/util-linux-${GKPKG_UTIL_LINUX_PV}-%%ARCH%%.tar.xz}" + +GKPKG_ZLIB_PN="zlib" +GKPKG_ZLIB_PV="${GKPKG_ZLIB_PV:-VERSION_ZLIB}" +GKPKG_ZLIB_DEPS="" +GKPKG_ZLIB_SRCDIR="${GKPKG_ZLIB_SRCDIR:-zlib-${GKPKG_ZLIB_PV}}" +GKPKG_ZLIB_SRCTAR="${GKPKG_ZLIB_SRCTAR:-${DISTDIR}/zlib-${GKPKG_ZLIB_PV}.tar.gz}" +GKPKG_ZLIB_BINPKG="${GKPKG_ZLIB_BINPKG:-%%CACHE%%/zlib-${GKPKG_ZLIB_PV}-%%ARCH%%.tar.xz}" diff --git a/defaults/unlock-luks.sh b/defaults/unlock-luks.sh new file mode 100644 index 0000000..ef6b816 --- /dev/null +++ b/defaults/unlock-luks.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +print_usage() { + echo "Usage: $0 root|swap" >&2 +} + +splash() { + return 0 +} + +if [ -z "${1}" ] +then + print_usage + exit 1 +fi + +case "${1}" in + root) + NAME="${1}" + TYPE=ROOT + ;; + swap) + NAME="${1}" + TYPE=SWAP + ;; + *) + echo "ERROR: Unknown type '${1}' specified!" + print_usage + exit 1 + ;; +esac + +. /etc/initrd.defaults +. /etc/initrd.scripts +. "${CRYPT_ENV_FILE}" + +main() { + if [ ! -x /sbin/cryptsetup ] + then + bad_msg "cryptsetup program is missing. Was initramfs built without --luks parameter?" + exit 1 + fi + + eval local LUKS_DEVICE='"${CRYPT_'${TYPE}'}"' LUKS_NAME="${NAME}" LUKS_KEY='"${CRYPT_KEYFILE_'${TYPE}'}"' + eval local LUKS_TRIM='"${CRYPT_'${TYPE}'_TRIM}"' OPENED_LOCKFILE='"${CRYPT_'${TYPE}'_OPENED_LOCKFILE}"' + local cryptsetup_options= + + while true + do + local gpg_cmd= crypt_filter_ret= + + if [ -e "${OPENED_LOCKFILE}" ] + then + good_msg "The LUKS device ${LUKS_DEVICE} was opened by someone else in the meanwhile." + break + else + LUKS_DEVICE=$(find_real_device "${LUKS_DEVICE}") + if [ -z "${LUKS_DEVICE}" ] + then + bad_msg "Looks like CRYPT_${TYPE} kernel cmdline argument is not set." ${CRYPT_SILENT} + exit 1 + fi + + setup_md_device ${LUKS_DEVICE} + cryptsetup isLuks ${LUKS_DEVICE} + if [ $? -ne 0 ] + then + bad_msg "The LUKS device ${LUKS_DEVICE} does not contain a LUKS header" ${CRYPT_SILENT} + + # User has SSH access and is able to call script again or + # able to investigate the problem on its own. + exit 1 + else + if [ "x${LUKS_TRIM}" = "xyes" ] + then + good_msg "Enabling TRIM support for ${LUKS_NAME} ..." ${CRYPT_SILENT} + cryptsetup_options="${cryptsetup_options} --allow-discards" + fi + + # Handle keys + if [ -s "${LUKS_KEY}" ] + then + # we received raw, unencrypted key through SSH -- no GPG possible + cryptsetup_options="${cryptsetup_options} -d ${LUKS_KEY}" + fi + + # At this point, keyfile or not, we're ready! + crypt_filter "${gpg_cmd}cryptsetup ${cryptsetup_options} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" + crypt_filter_ret=$? + + [ -e /dev/tty.org ] \ + && rm -f /dev/tty \ + && mv /dev/tty.org /dev/tty + + if [ ${crypt_filter_ret} -eq 0 ] + then + touch "${OPENED_LOCKFILE}" + good_msg "LUKS device ${LUKS_DEVICE} opened" ${CRYPT_SILENT} + break + else + bad_msg "Failed to open LUKS device ${LUKS_DEVICE}" ${CRYPT_SILENT} + + # We need to stop here with a non-zero exit code to prevent + # a loop when invalid keyfile was sent. + exit 1 + fi + fi + fi + done + + if [ -s "${LUKS_KEY}" ] + then + if [ "${DEBUG}" != 'yes' ] + then + rm -f "${LUKS_KEY}" + else + warn_msg "LUKS key file '${LUKS_KEY}' not deleted because DEBUG mode is enabled!" + fi + fi + + if [ "${crypt_filter_ret}" = '0' ] + then + # Kill any running cryptsetup prompt for this device + pkill -9 -f "luksOpen.*${LUKS_NAME}\$" >/dev/null 2>&1 + fi +} + +main + +exit 0 diff --git a/doc/genkernel.8.txt b/doc/genkernel.8.txt index 2832277..055610b 100644 --- a/doc/genkernel.8.txt +++ b/doc/genkernel.8.txt @@ -317,6 +317,19 @@ INITIALIZATION *--*[*no-*]*ssh*:: Includes or excludes SSH (dropbear) support for remote LUKS keys. +*--ssh-authorized-keys-file*=<file>:: + Specifies a user created authorized_keys file. By default genkernel + will use "/etc/dropbear/authorized_keys". + +*--ssh-host-keys*=<create|create-from-host|runtime>:: + When set to *create*, which is the default value, genkernel will embed + SSH host keys from "/etc/dropbear" and will create those keys when missing. + When set to *create-from-host*, genkernel will embed SSH host keys from + "/etc/dropbear" but will create those keys from "/etc/ssh" when missing + (not recommended for security reasons). + When set to *runtime*, genkernel will not embed any SSH host key in + initramfs and dropbear will generate needed host key at runtime (`dropbear -R`). + *--bootloader*=<grub|grub2>:: Add new kernel to GRUB or GRUB2 configuration. @@ -512,10 +525,50 @@ recognized by the kernel itself. Pauses for up to 3 seconds (or specified number of seconds) while waiting for root device to appear during initramfs root scanning. -*ip*=<...>:: +*ip*=<dhcp,addr/cidr>:: Normally used to tell the kernel that it should start a network - interface. If present, the initrd will try to mount a livecd - over NFS. + interface which can be specified using *gk.net.iface* kernel parameter. + By default, dhcp will be used. You can set a specific IP address using + addr/CIDR notation, i.e. '1.2.3.4/24'. + +*gk.net.iface*=<interface,macaddr>:: + By default we will use the interface named eth0. Use this kernel + parameter to specify another interface. You can also specify a MAC + address (00:00:00:00:00:00 format) instead of an interface name. + +*gk.net.timeout.dhcp*=<...>:: + By default we will send up to 3 DHCP discovery requests. + +*gk.net.gw*=<...>:: + Optional gateway. If *ip* is set to dhcp, this kernel parameter will + be ignored. + +*gk.net.routes*=<...>:: + Optional additional routes. If *ip* is set to dhcp, this kernel + parameter will be ignored. + +*gk.net.timeout.dad*=<...>:: + By default we will wait up to 10 seconds for IPv6's DAD to complete. + At the moment, we only wait for DAD while bringing down an interface + to prevent a race condition. + +*gk.net.timeout.deconfiguration*=<...>:: + By default we will wait up to 10 seconds while bringing down an + interface to prevent a race condition. + +*gk.net.timeout.dhcp*=<...>:: + By default we will wait up to 10 seconds for a DHCP server reply. + +*dosshd*:: + Will start an SSH daemon within initramfs allowing to remotely unlock + encrypted devices or just for debugging purpose. + +*gk.sshd.port*=<...>:: + By default, sshd will listen on port 22. + +*gk.sshd.wait*=<...>:: + Wait X seconds after setting up sshd, useful when you want to login + (and thus pause boot process) before booting real system. *nfsroot*=<...>:: If present, the initrd will try to mount a livecd from that diff --git a/gen_cmdline.sh b/gen_cmdline.sh index a22b607..0a1b1c8 100755 --- a/gen_cmdline.sh +++ b/gen_cmdline.sh @@ -128,6 +128,13 @@ longusage() { echo " --no-iscsi Exclude iSCSI support" echo " --ssh Include SSH (dropbear) support" echo " --no-ssh Exclude SSH (dropbear) support" + echo " --ssh-authorized-keys-file=<file>" + echo " Specifies a user created authorized_keys file" + echo " --ssh-host-keys=(create|create-from-host|runtime)" + echo " Use host keys from /etc/dropbear, but CREATE (default) new host key(s)" + echo " if missing, CREATE host key(s) FROM current HOST running genkernel" + echo " (not recommended) or don't embed any host key in initramfs and" + echo " generate at RUNTIME (dropbear -R)" echo " --bootloader=(grub|grub2)" echo " Add new kernel to GRUB (grub) or GRUB2 (grub2) bootloader" echo " --no-bootloader Skip bootloader update" @@ -445,18 +452,19 @@ parse_cmdline() { --ssh|--no-ssh) CMD_SSH=$(parse_optbool "$*") print_info 2 "CMD_SSH: ${CMD_SSH}" - if isTrue "${CMD_SSH}" && [ ! -e /usr/sbin/dropbear ] - then - echo 'Error: --ssh requires net-misc/dropbear' \ - 'to be installed on the host system.' - exit 1 - fi - if isTrue "${CMD_SSH}" && [ ! -e /etc/dropbear/authorized_keys ] + ;; + --ssh-authorized-keys-file=*) + CMD_SSH_AUTHORIZED_KEYS_FILE="${*#*=}" + print_info 2 "CMD_SSH_AUTHORIZED_KEYS_FILE: ${CMD_SSH_AUTHORIZED_KEYS_FILE}" + ;; + --ssh-host-keys=*) + CMD_SSH_HOST_KEYS="${*#*=}" + if ! isTrue "$(is_valid_ssh_host_keys_parameter_value "${CMD_SSH_HOST_KEYS}")" then - echo 'Error: --ssh requires that dropbear is configured' \ - 'but /etc/dropbear/authorized_keys does not exist!' + echo "Error: --ssh-host-keys value '${CMD_SSH_HOST_KEYS}' is unsupported." exit 1 fi + print_info 2 "CMD_SSH_HOST_KEYS: ${CMD_SSH_HOST_KEYS}" ;; --loglevel=*) CMD_LOGLEVEL="${*#*=}" diff --git a/gen_determineargs.sh b/gen_determineargs.sh index 1e73bc1..a698f77 100755 --- a/gen_determineargs.sh +++ b/gen_determineargs.sh @@ -144,6 +144,8 @@ determine_real_args() { set_config_with_override STRING INSTALL_MOD_PATH CMD_INSTALL_MOD_PATH set_config_with_override BOOL OLDCONFIG CMD_OLDCONFIG "yes" set_config_with_override BOOL SSH CMD_SSH "no" + set_config_with_override STRING SSH_AUTHORIZED_KEYS_FILE CMD_SSH_AUTHORIZED_KEYS_FILE "/etc/dropbear/authorized_keys" + set_config_with_override STRING SSH_HOST_KEYS CMD_SSH_HOST_KEYS "create" set_config_with_override BOOL LVM CMD_LVM "no" set_config_with_override BOOL DMRAID CMD_DMRAID "no" set_config_with_override BOOL ISCSI CMD_ISCSI "no" diff --git a/gen_funcs.sh b/gen_funcs.sh index 79daedf..4136122 100755 --- a/gen_funcs.sh +++ b/gen_funcs.sh @@ -201,6 +201,19 @@ can_run_programs_compiled_by_genkernel() { echo "${can_run_programs}" } +is_valid_ssh_host_keys_parameter_value() { + local parameter_value=${1} + + local is_valid=no + case "${parameter_value}" in + create|create-from-host|runtime) + is_valid=yes + ;; + esac + + echo "${is_valid}" +} + is_valid_triplet() { [[ ${#} -ne 1 ]] \ && gen_die "$(get_useful_function_stack "${FUNCNAME}")Invalid usage of ${FUNCNAME}(): Function takes exactly one argument (${#} given)!" @@ -526,6 +539,124 @@ copy_image_with_preserve() { fi } +dropbear_create_key() { + [[ ${#} -ne 2 ]] \ + && gen_die "$(get_useful_function_stack "${FUNCNAME}")Invalid usage of ${FUNCNAME}(): Function takes exactly two arguments (${#} given)!" + + if ! hash sandbox &>/dev/null + then + gen_die "Sandbox not found. Please install sys-apps/sandbox!" + fi + + local key_file=${1} + local command=${2} + local key_type=$(dropbear_get_key_type_from_filename "${key_file}") + + local -a envvars=( + GK_SHARE="${GK_SHARE}" + LOGLEVEL="${LOGLEVEL}" + LOGFILE="${LOGFILE}" + NOCOLOR="${NOCOLOR}" + TEMP="${TEMP}" + SANDBOX_WRITE="${LOGFILE}:${TEMP}" + ) + + envvars+=( + DROPBEAR_COMMAND="${command}" + DROPBEAR_KEY_FILE="${key_file}" + DROPBEAR_KEY_TYPE="${key_type}" + ) + + # set up worker signal handler + local error_msg_detail="Failed to create dropbear key '${key_file}'!" + local error_msg="gen_worker.sh aborted: ${error_msg_detail}" + trap "gen_die \"${error_msg}\"" SIGABRT SIGHUP SIGQUIT SIGINT SIGTERM + + env -i \ + "${envvars[@]}" \ + sandbox "${GK_SHARE}"/gen_worker.sh dropbear 2>&1 + + local RET=$? + + # restore default trap + set_default_gk_trap + + [ ${RET} -ne 0 ] && gen_die "$(get_useful_function_stack)${error_msg_detail}" +} + +dropbear_get_key_type_from_filename() { + [[ ${#} -ne 1 ]] \ + && gen_die "$(get_useful_function_stack "${FUNCNAME}")Invalid usage of ${FUNCNAME}(): Function takes exactly one argument (${#} given)!" + + local key=${1} + local type= + + case "${key}" in + *_dss_*) + type=dss + ;; + *_ecdsa_*) + type=ecdsa + ;; + *_rsa_*) + type=rsa + ;; + *) + gen_die "Failed to determine key type from '${key}'!" + ;; + esac + + echo "${type}" +} + +dropbear_generate_key_info_file() { + [[ ${#} -ne 3 ]] \ + && gen_die "$(get_useful_function_stack "${FUNCNAME}")Invalid usage of ${FUNCNAME}(): Function takes exactly three arguments (${#} given)!" + + if ! hash sandbox &>/dev/null + then + gen_die "Sandbox not found. Please install sys-apps/sandbox!" + fi + + local command=${1} + local key_info_file=${2} + local initramfs_dropbear_dir=${3} + local key_file="${initramfs_dropbear_dir}/$(basename "${key_info_file/%_key.*/_key}")" + local key_type=$(dropbear_get_key_type_from_filename "${key_file}") + + local -a envvars=( + GK_SHARE="${GK_SHARE}" + LOGLEVEL="${LOGLEVEL}" + LOGFILE="${LOGFILE}" + NOCOLOR="${NOCOLOR}" + TEMP="${TEMP}" + SANDBOX_WRITE="${LOGFILE}:${TEMP}" + ) + + envvars+=( + DROPBEAR_COMMAND="${command}" + DROPBEAR_KEY_FILE="${key_file}" + DROPBEAR_KEY_TYPE="${key_type}" + DROPBEAR_KEY_INFO_FILE="${key_info_file}" + ) + + # set up worker signal handler + local error_msg_detail="Failed to extract dropbear key information from '${key_file}'!" + local error_msg="gen_worker.sh aborted: ${error_msg_detail}" + trap "gen_die \"${error_msg}\"" SIGABRT SIGHUP SIGQUIT SIGINT SIGTERM + + env -i \ + "${envvars[@]}" \ + sandbox "${GK_SHARE}"/gen_worker.sh dropbear 2>&1 + + local RET=$? + + # restore default trap + set_default_gk_trap + + [ ${RET} -ne 0 ] && gen_die "$(get_useful_function_stack)${error_msg_detail}" +} + # @FUNCTION: debug_breakpoint # @USAGE: [<NAME>] # @DESCRIPTION: diff --git a/gen_initramfs.sh b/gen_initramfs.sh index 35ec279..0b660da 100755 --- a/gen_initramfs.sh +++ b/gen_initramfs.sh @@ -754,90 +754,289 @@ append_luks() { rm -r "${TEMP}/initramfs-luks-temp/" } -append_dropbear(){ - if [ -d "${TEMP}"/initramfs-dropbear-temp ] +append_dropbear() { + local PN=dropbear + local TDIR="${TEMP}/initramfs-${PN}-temp" + if [ -d "${TDIR}" ] then - rm -r "${TEMP}"/initramfs-dropbear-temp + rm -r "${TDIR}" || gen_die "Failed to clean out existing '${TDIR}'!" fi - if [ ! -d /etc/dropbear ] + local dropbear_command= + if ! isTrue "$(is_valid_ssh_host_keys_parameter_value "${SSH_HOST_KEYS}")" then - mkdir /etc/dropbear + gen_die "--ssh-host-keys value '${SSH_HOST_KEYS}' is unsupported!" + elif [[ "${SSH_HOST_KEYS}" == 'create' ]] + then + dropbear_command=dropbearkey + else + dropbear_command=dropbearconvert fi - if [ ! -e /etc/dropbear/dropbear_rsa_host_key ] + + local ssh_authorized_keys_file=$(expand_file "${SSH_AUTHORIZED_KEYS_FILE}") + if [ -z "${ssh_authorized_keys_file}" ] then - if [ -e /usr/bin/dropbearconvert -a /etc/ssh/ssh_host_rsa_key ] + gen_die "--ssh-authorized-keys value '${SSH_AUTHORIZED_KEYS_FILE}' is invalid!" + elif [ ! -f "${ssh_authorized_keys_file}" ] + then + gen_die "authorized_keys file '${ssh_authorized_keys_file}' does NOT exist!" + elif [ ! -s "${ssh_authorized_keys_file}" ] + then + gen_die "authorized_keys file '${ssh_authorized_keys_file}' is empty!" + fi + + populate_binpkg ${PN} + + mkdir -p "${TDIR}" || gen_die "Failed to create '${TDIR}'!" + + unpack "$(get_gkpkg_binpkg "${PN}")" "${TDIR}" + + if [[ "${SSH_HOST_KEYS}" == 'runtime' ]] + then + print_info 2 "$(get_indent 2)${PN}: >> No SSH host key embedded due to --ssh-host-key=runtime; Dropbear will generate required host key(s) at runtime!" + else + if ! hash ssh-keygen &>/dev/null then - if /usr/bin/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear/dropbear_rsa_host_key + gen_die "'ssh-keygen' program is required but missing!" + fi + + local initramfs_dropbear_dir="${TDIR}/etc/dropbear" + + if [[ "${SSH_HOST_KEYS}" == 'create-from-host' ]] + then + print_info 3 "$(get_indent 2)${PN}: >> Checking for existence of all SSH host keys ..." + local missing_ssh_host_keys=no + + if [ ! -f "/etc/ssh/ssh_host_rsa_key" ] then - print_info 1 "$(getIndent 2)SSH: >> /etc/ssh/ssh_host_rsa_key converted into /etc/dropbear/dropbear_rsa_host_key" + print_info 3 "$(get_indent 2)${PN}: >> SSH host key '/etc/ssh/ssh_host_rsa_key' is missing!" + missing_ssh_host_keys=yes + fi + + if [ ! -f "/etc/ssh/ssh_host_ecdsa_key" ] + then + print_info 3 "$(get_indent 2)${PN}: >> SSH host key '/etc/ssh/ssh_host_ecdsa_key' is missing!" + missing_ssh_host_keys=yes + fi + + if isTrue "${missing_ssh_host_keys}" + then + # Should only happen when installing a new system ... + print_info 3 "$(get_indent 2)${PN}: >> Creating missing SSH host key(s) ..." + ssh-keygen -A || gen_die "Failed to generate host's SSH host key(s) using 'ssh-keygen -A'!" + fi + fi + + local -a required_dropbear_host_keys=( + /etc/dropbear/dropbear_ecdsa_host_key + /etc/dropbear/dropbear_rsa_host_key + ) + + local i=0 + local n_required_dropbear_keys=${#required_dropbear_host_keys[@]} + local required_key= + while [[ ${i} < ${n_required_dropbear_keys} ]] + do + required_key=${required_dropbear_host_keys[${i}]} + print_info 3 "$(get_indent 2)${PN}: >> Checking for existence of dropbear host key '${required_key}' ..." + if [[ -f "${required_key}" ]] + then + if [[ ! -s "${required_key}" ]] + then + print_info 1 "$(get_indent 2)${PN}: >> Dropbear host key '${required_key}' exists but is empty; Removing ..." + rm "${required_key}" || gen_die "Failed to remove invalid '${required_key}' null byte file!" + elif [[ "${SSH_HOST_KEYS}" == 'create-from-host' ]] \ + && [[ "${required_key}" == *_rsa_* ]] \ + && [[ "${required_key}" -ot "/etc/ssh/ssh_host_rsa_key" ]] + then + print_info 1 "$(get_indent 2)${PN}: >> Dropbear host key '${required_key}' exists but is older than '/etc/ssh/ssh_host_rsa_key'; Removing to force update due to --ssh-host-key=create-from-host ..." + rm "${required_key}" || gen_die "Failed to remove outdated '${required_key}' file!" + elif [[ "${SSH_HOST_KEYS}" == 'create-from-host' ]] \ + && [[ "${required_key}" == *_ecdsa_* ]] \ + && [[ "${required_key}" -ot "/etc/ssh/ssh_host_ecdsa_key" ]] + then + print_info 1 "$(get_indent 2)${PN}: >> Dropbear host key '${required_key}' exists but is older than '/etc/ssh/ssh_host_ecdsa_key'; Removing to force update due to --ssh-host-key=create-from-host ..." + rm "${required_key}" || gen_die "Failed to remove outdated '${required_key}' file!" + else + print_info 3 "$(get_indent 2)${PN}: >> Dropbear host key '${required_key}' exists!" + unset required_dropbear_host_keys[${i}] + fi else - gen_die "RSA host key conversion using dropbearconvert failed" + print_info 3 "$(get_indent 2)${PN}: >> Dropbear host key '${required_key}' is missing! Will create ..." fi + + i=$((i + 1)) + done + + if [[ ${#required_dropbear_host_keys[@]} -gt 0 ]] + then + if isTrue "$(can_run_programs_compiled_by_genkernel)" + then + dropbear_command="${TDIR}/usr/bin/${dropbear_command}" + print_info 3 "$(get_indent 2)${PN}: >> Will use '${dropbear_command}' to create missing keys ..." + elif hash ${dropbear_command} &>/dev/null + then + print_info 3 "$(get_indent 2)${PN}: >> Will use existing '${dropbear_command}' program from path to create missing keys ..." + else + local error_msg="Need to generate '${required_host_keys[*]}' but '${dropbear_command}'" + error_msg=" program is missing. Please install net-misc/dropbear and re-run genkernel!" + gen_die "${error_msg}" + fi + + local missing_key= + for missing_key in ${required_dropbear_host_keys[@]} + do + dropbear_create_key "${missing_key}" "${dropbear_command}" + + # just in case ... + if [ -f "${missing_key}" ] + then + print_info 3 "$(get_indent 2)${PN}: >> Dropbear host key '${missing_key}' successfully created!" + else + gen_die "Sanity check failed: '${missing_key}' should exist at this stage but does NOT." + fi + done else - if /usr/bin/dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key -s 4096 > /dev/null + print_info 2 "$(get_indent 2)${PN}: >> Using existing dropbear host keys from /etc/dropbear ..." + fi + + cp -aL --target-directory "${initramfs_dropbear_dir}" /etc/dropbear/{dropbear_rsa_host_key,dropbear_ecdsa_host_key} \ + || gen_die "Failed to copy '/etc/dropbear/{dropbear_rsa_host_key,dropbear_ecdsa_host_key}'" + + # Try to show embedded dropbear host key details for security reasons. + # We do it that complicated to get common used formats. + local -a key_info_files=() + local -a missing_key_info_files=() + + local host_key_file= host_key_file_checksum= host_key_info_file= + while IFS= read -r -u 3 -d $'\0' host_key_file + do + host_key_file_checksum=$(sha256sum "${host_key_file}" 2>/dev/null | awk '{print $1}') + if [ -z "${host_key_file_checksum}" ] then - print_info 1 "$(getIndent 2)SSH: >> New dropbear RSA host key /etc/dropbear/dropbear_rsa_host_key created" + gen_die "Failed to generate SHA256 checksum of '${host_key_file}'!" + fi + + host_key_info_file="${GK_V_CACHEDIR}/$(basename "${host_key_file}").${host_key_file_checksum:0:10}.info" + + if [ ! -s "${host_key_info_file}" ] + then + missing_key_info_files+=( ${host_key_info_file} ) else - gen_die "RSA host key generation using dropbearkey failed" + key_info_files+=( ${host_key_info_file} ) + fi + done 3< <(find "${initramfs_dropbear_dir}" -type f -name '*_key' -print0 2>/dev/null) + unset host_key_file host_key_file_checksum host_key_info_file + IFS="${GK_DEFAULT_IFS}" + + if [[ ${#missing_key_info_files[@]} -ne 0 ]] + then + dropbear_command= + if isTrue "$(can_run_programs_compiled_by_genkernel)" + then + dropbear_command="${TDIR}/usr/bin/dropbearconvert" + print_info 3 "$(get_indent 2)${PN}: >> Will use '${dropbear_command}' to extract embedded host key information ..." + elif hash dropbearconvert &>/dev/null + then + dropbear_command=dropbearconvert + print_info 3 "$(get_indent 2)${PN}: >> Will use existing '${dropbear_command}' program to extract embedded host key information ..." + else + print_warning 2 "$(get_indent 2)${PN}: >> 'dropbearconvert' program not available; Cannot generate missing key information for ${#missing_key_info_files[@]} key(s)!" + fi + + if [[ -n "${dropbear_command}" ]] + then + # We are missing at least information for one embedded key + # but looks like we are able to generate the missing information ... + local missing_key_info_file= + for missing_key_info_file in "${missing_key_info_files[@]}" + do + dropbear_generate_key_info_file "${dropbear_command}" "${missing_key_info_file}" "${initramfs_dropbear_dir}" + key_info_files+=( ${missing_key_info_file} ) + done + unset missing_key_info_file fi fi - fi - if [ ! -e /etc/dropbear/dropbear_dss_host_key ] - then - if /usr/bin/dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key > /dev/null + if [[ ${#key_info_files[@]} -gt 0 ]] then - print_info 1 "$(getIndent 2)SSH: >> New dropbear DSS host key /etc/dropbear/dropbear_dss_host_key created" + # We have at least information about one embedded key ... + print_info 1 "=================================================================" 1 0 1 + print_info 1 "This initramfs' sshd will use the following host key(s):" 1 0 1 + + local key_info_file= + for key_info_file in "${key_info_files[@]}" + do + print_info 1 "$(cat "${key_info_file}")" 1 0 1 + done + unset key_info_file + + if [ ${LOGLEVEL} -lt 3 ] + then + # Don't clash with output from log_future_cpio_content + print_info 1 "=================================================================" 1 0 1 + fi else - gen_die "DSS host key generation using dropbearkey failed" + print_warning 2 "$(get_indent 2)${PN}: >> No information about embedded SSH host key(s) available." fi fi - cd "${TEMP}" \ - || gen_die "cd '${TEMP}' failed" - mkdir -p ${TEMP}/initramfs-dropbear-temp/var/run - mkdir -p ${TEMP}/initramfs-dropbear-temp/var/log - mkdir -p ${TEMP}/initramfs-dropbear-temp/etc/dropbear - mkdir -p ${TEMP}/initramfs-dropbear-temp/bin - mkdir -p ${TEMP}/initramfs-dropbear-temp/root/.ssh + local libdir=$(get_chost_libdir) + mkdir -p "${TDIR}"/lib || gen_die "Failed to create '${TDIR}/lib'!" + copy_system_binaries "${TDIR}"/lib "${libdir}"/libnss_files.so.2 - cp -L ${GK_SHARE}/defaults/login-remote.sh ${TEMP}/initramfs-dropbear-temp/bin/ || gen_die "failed to copy defaults/login-remote.sh" - cp -L /etc/dropbear/{dropbear_rsa_host_key,dropbear_dss_host_key} ${TEMP}/initramfs-dropbear-temp/etc/dropbear/ || gen_die "failed to copy dropbear host key(s)" - cp -L /etc/dropbear/authorized_keys ${TEMP}/initramfs-dropbear-temp/root/.ssh || gen_die "failed to copy /etc/dropbear/authorized_keys. Did you forget to configure dropbear?" - cp -L /etc/localtime ${TEMP}/initramfs-dropbear-temp/etc/ || gen_die "failed to copy /etc/localtime. Please set system's timezone!" - if [ ${ARCH} = "x86_64" ] - then - mkdir -p ${TEMP}/initramfs-dropbear-temp/lib64 - cp -L /lib64/libnss_files.so.2 ${TEMP}/initramfs-dropbear-temp/lib64/ || gen_die "failed to copy libnss_files.so.2" - else - mkdir -p ${TEMP}/initramfs-dropbear-temp/lib - cp -L /lib/libnss_files.so.2 ${TEMP}/initramfs-dropbear-temp/lib/ || gen_die "failed to libnss_files.so.2" - fi + cd "${TDIR}" || gen_die "Failed to chdir to '${TDIR}'!" + + cp -a "${GK_SHARE}"/defaults/login-remote.sh "${TDIR}"/usr/bin/ \ + || gen_die "Failed to copy '${GK_SHARE}/defaults/login-remote.sh'" + + cp -a "${GK_SHARE}"/defaults/resume-boot.sh "${TDIR}"/usr/sbin/resume-boot \ + || gen_die "Failed to copy '${GK_SHARE}/defaults/resume-boot.sh' to '${TDIR}/usr/sbin/resume-boot'" + + cp -a "${GK_SHARE}"/defaults/unlock-luks.sh "${TDIR}"/usr/sbin/unlock-luks \ + || gen_die "Failed to copy '${GK_SHARE}/defaults/unlock-luks.sh' to '${TDIR}/usr/sbin/unlock-luks'" + + cp -aL "${ssh_authorized_keys_file}" "${TDIR}"/root/.ssh/ \ + || gen_die "Failed to copy '${ssh_authorized_keys_file}'!" + + cp -aL /etc/localtime "${TDIR}"/etc/ \ + || gen_die "Failed to copy '/etc/localtime'. Please set system's timezone!" - sed "s/compat/files/g" /etc/nsswitch.conf > ${TEMP}/initramfs-dropbear-temp/etc/nsswitch.conf || gen_die "failed to modify /etc/nsswitch.conf" - echo "root:x:0:0:root:/root:/bin/login-remote.sh" > ${TEMP}/initramfs-dropbear-temp/etc/passwd - echo "/bin/login-remote.sh" > ${TEMP}/initramfs-dropbear-temp/etc/shells - echo "root:!:0:0:99999:7:::" > ${TEMP}/initramfs-dropbear-temp/etc/shadow - echo "root:x:0:root" > ${TEMP}/initramfs-dropbear-temp/etc/group - echo "" > ${TEMP}/initramfs-dropbear-temp/var/log/lastlog + echo "root:x:0:0:root:/root:/usr/bin/login-remote.sh" > "${TDIR}"/etc/passwd \ + || gen_die "Failed to create '/etc/passwd'!" - chmod 0755 ${TEMP}/initramfs-dropbear-temp/bin/login-remote.sh - chmod 0700 ${TEMP}/initramfs-dropbear-temp/root/.ssh - chmod 0640 ${TEMP}/initramfs-dropbear-temp/etc/shadow - chmod 0644 ${TEMP}/initramfs-dropbear-temp/etc/passwd - chmod 0644 ${TEMP}/initramfs-dropbear-temp/etc/group - mkfifo ${TEMP}/initramfs-dropbear-temp/etc/dropbear/fifo_root - mkfifo ${TEMP}/initramfs-dropbear-temp/etc/dropbear/fifo_swap + echo "/usr/bin/login-remote.sh" > "${TDIR}"/etc/shells \ + || gen_die "Failed to create '/etc/shells'!" - copy_binaries "${TEMP}"/initramfs-dropbear-temp/ /usr/sbin/dropbear \ - /bin/login /usr/bin/passwd + echo "root:!:0:0:99999:7:::" > "${TDIR}"/etc/shadow \ + || gen_die "Failed to create '/etc/shadow'!" + echo "root:x:0:root" > "${TDIR}"/etc/group \ + || gen_die "Failed to create '/etc/group'!" + + chmod 0755 "${TDIR}"/usr/bin/login-remote.sh \ + || gen_die "Failed to chmod of '${TDIR}/usr/bin/login-remote.sh'!" + + chmod 0755 "${TDIR}"/usr/sbin/resume-boot \ + || gen_die "Failed to chmod of '${TDIR}/usr/sbin/resume-boot'!" + + chmod 0755 "${TDIR}"/usr/sbin/unlock-luks \ + || gen_die "Failed to chmod of '${TDIR}/usr/sbin/unlock-luks'!" + + chmod 0640 "${TDIR}"/etc/shadow \ + || gen_die "Failed to chmod of '${TDIR}/etc/shadow'!" + + chmod 0644 "${TDIR}"/etc/passwd \ + || gen_die "Failed to chmod of '${TDIR}/etc/passwd'!" + + chmod 0644 "${TDIR}"/etc/group \ + || gen_die "Failed to chmod of '${TDIR}/etc/group'!" + + cd "${TDIR}" || gen_die "Failed to chdir to '${TDIR}'!" log_future_cpio_content - cd "${TEMP}"/initramfs-dropbear-temp \ - || gen_die "cd '${TEMP}/initramfs-dropbear-temp' failed" - find . -print | cpio ${CPIO_ARGS} --append -F "${CPIO}" - rm -rf "${TEMP}"/initramfs-dropbear-temp > /dev/null + + find . -print | cpio ${CPIO_ARGS} --append -F "${CPIO}" \ + || gen_die "Failed to append ${PN} to cpio!" } append_firmware() { @@ -397,6 +397,7 @@ then isTrue "${DMRAID}" && print_warning 1 '- Add "dodmraid" for dmraid support or "dodmraid=<additional options>"' isTrue "${MDADM}" && print_warning 1 '- Add "domdadm" for MDRAID support' isTrue "${LVM}" && print_warning 1 '- Add "dolvm" for LVM support' + isTrue "${SSH}" && print_warning 1 '- Add "dosshd" to start SSH daemon in initramfs' if isTrue "${ZFS}" then diff --git a/gkbuilds/dropbear.gkbuild b/gkbuilds/dropbear.gkbuild new file mode 100644 index 0000000..e6a43f6 --- /dev/null +++ b/gkbuilds/dropbear.gkbuild @@ -0,0 +1,68 @@ +# Copyright 1999-2019 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +src_prepare() { + default + + # Disable DSS support + echo "#define DROPBEAR_DSS 0" > localoptions.h || die "Failed to disable DSS support" +} + +src_configure() { + local myconf=( + --enable-static + --disable-pam + --disable-syslog + --with-zlib="${BROOT}/usr" + ) + + gkconf "${myconf[@]}" +} + +src_compile() { + local MYMAKEOPTS=( "V=1" ) + MYMAKEOPTS+=( "MULTI=1" ) + MYMAKEOPTS+=( "PROGRAMS='dropbear dropbearkey dropbearconvert scp'" ) + gkmake "${MYMAKEOPTS[@]}" +} + +src_install() { + local mydir= + for mydir in \ + etc/dropbear \ + usr/bin \ + usr/sbin \ + root/.ssh \ + var/log \ + var/run \ + ; do + mkdir -p "${D}"/${mydir} || die "Failed to create '${D}/${mydir}'!" + done + + cp -a dropbearmulti "${D}"/usr/bin/ \ + || die "Failed to copy '${S}/dropbearmulti' to '${D}/usr/bin/'!" + + "${STRIP}" --strip-all "${D}"/usr/bin/dropbearmulti \ + || die "Failed to strip '${D}/usr/bin/dropbearmulti'!" + + ln -s ../bin/dropbearmulti "${D}"/usr/sbin/dropbear \ + || die "Failed to symlink '${D}/usr/sbin/dropbear' to '${D}/usr/bin/dropbearmulti'!" + + ln -s dropbearmulti "${D}"/usr/bin/dropbearconvert \ + || die "Failed to symlink '${D}/usr/bin/dropbearconvert' to '${D}/usr/bin/dropbearmulti'!" + + ln -s dropbearmulti "${D}"/usr/bin/dropbearkey \ + || die "Failed to symlink '${D}/usr/bin/dropbearkey' to '${D}/usr/bin/dropbearmulti'!" + + ln -s dropbearmulti "${D}"/usr/bin/scp \ + || die "Failed to symlink '${D}/usr/bin/scp' to '${D}/usr/bin/dropbearmulti'!" + + chmod 0700 "${D}"/root/.ssh \ + || die "Failed to chmod of '${D}/root/.ssh'!" + + mkfifo "${D}"/etc/dropbear/fifo_root \ + || die "Failed to create '${D}/etc/dropbear/fifo_root'!" + + mkfifo "${D}"/etc/dropbear/fifo_swap \ + || die "Failed to create '${D}/etc/dropbear/fifo_swap'!" +} |