From bfd189b7bc2a64c3028448ff33e77f3a2d6a9df7 Mon Sep 17 00:00:00 2001 From: Sergey Popovich Date: Mon, 5 Nov 2018 19:06:59 +0200 Subject: [PATCH] installer: Do not ship precreated ssh host keys with image Host keys are individual for each machine running sshd(8) and should not be used by multiple hosts for security reasons. Unfortunately for openssh-server package in Debian ssh host keys precreated in postinst script forcing all debootstrap(8) installations to take care about this problem. On the other hand one might want to keep previous host keys during reinstall or upgrade. For that purpose introduce onl-ssh helper by the analogy with onl-pki, call it using chroot(1) from main rootfs where ssh-keygen(1) and /dev entries available. It will store host keys in /mnt/onl/config/ssh directory pointed by /etc/ssh/ssh_host_*key* symlinks for sshd(8). Signed-off-by: Sergey Popovich --- .../loader-initrd-files/src/bin/switchroot | 3 + .../all/vendor-config-onl/src/bin/onl-ssh | 3 + .../src/python/onl/ssh/__init__.py | 106 ++++++++++++++++++ tools/onlrfs.py | 7 ++ 4 files changed, 119 insertions(+) create mode 100755 packages/base/all/vendor-config-onl/src/bin/onl-ssh create mode 100755 packages/base/all/vendor-config-onl/src/python/onl/ssh/__init__.py diff --git a/packages/base/all/initrds/loader-initrd-files/src/bin/switchroot b/packages/base/all/initrds/loader-initrd-files/src/bin/switchroot index 97709a4aeb..ff31e42edb 100644 --- a/packages/base/all/initrds/loader-initrd-files/src/bin/switchroot +++ b/packages/base/all/initrds/loader-initrd-files/src/bin/switchroot @@ -59,6 +59,9 @@ if [ -d /newroot/sys/firmware/efi/efivars ]; then fi mount --move /dev /newroot/dev +# Generate ssh host keys if needed +chroot /newroot onl-ssh --init + # Switch to /newroot if possible, else re-execute /init if [ -x /newroot/sbin/init ]; then exec switch_root -c /dev/console /newroot /sbin/init diff --git a/packages/base/all/vendor-config-onl/src/bin/onl-ssh b/packages/base/all/vendor-config-onl/src/bin/onl-ssh new file mode 100755 index 0000000000..cd86c79e83 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/bin/onl-ssh @@ -0,0 +1,3 @@ +#!/usr/bin/python +from onl.ssh import OnlSSH +OnlSSH.main() diff --git a/packages/base/all/vendor-config-onl/src/python/onl/ssh/__init__.py b/packages/base/all/vendor-config-onl/src/python/onl/ssh/__init__.py new file mode 100755 index 0000000000..1b18651962 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/ssh/__init__.py @@ -0,0 +1,106 @@ +#!/usr/bin/python +############################################################ +# +# ONL SSH Management +# +############################################################ +import sys +import os +import re +import argparse +import logging +import shutil +import tempfile +from onl.mounts import OnlMountManager, OnlMountContextReadOnly, OnlMountContextReadWrite +from onl.util import * + + + +class OnlSSHContextReadWrite(OnlMountContextReadWrite): + def __init__(self, logger): + OnlMountContextReadWrite.__init__(self, "ONL-CONFIG", logger) + +class OnlSSHContextReadOnly(OnlMountContextReadOnly): + def __init__(self, logger): + OnlMountContextReadOnly.__init__(self, "ONL-CONFIG", logger) + + +class OnlSSH(OnlServiceMixin): + """Initialize the ONL-CONFIG::SSH credentials.""" + + CONFIG_SSH_DIR="/mnt/onl/config/ssh" + + def __init__(self, logger=None): + self.logger = logger + + if self.logger is None: + self.logger = logging.getLogger("ONL:SSH") + + def init(self, force=False): + hostkeys = {} + + with OnlSSHContextReadOnly(self.logger): + for fname in os.listdir("/etc/ssh"): + fname = os.path.join("/etc/ssh", fname) + + if not os.path.islink(fname): + continue + fname = os.path.realpath(fname) + + pattern = '^' + self.CONFIG_SSH_DIR + '/ssh_host_((\w+)_)?key$' + m = re.match(pattern, fname) + if not m: + continue + + keytype = m.group(2) + if not keytype: + keytype = "rsa1" + + hostkeys[keytype] = fname + + if not hostkeys: + return + + with OnlSSHContextReadWrite(self.logger): + if not os.path.isdir(self.CONFIG_SSH_DIR): + os.makedirs(self.CONFIG_SSH_DIR) + + for keytype in hostkeys: + fname = hostkeys[keytype] + + if not force and os.path.exists(fname): + self.logger.info("Using existing SSH host key type %s.", keytype) + continue + + self.logger.info("Generating SSH host key type %s", keytype) + + # yes(1) is needed for "Overwrite (y/n)?" + cmd = "yes 2>/dev/null | ssh-keygen -q -t '%s' -N '' -f '%s'" \ + % (keytype, fname) + + self._execute(cmd, logLevel=logging.INFO) + + @staticmethod + def main(): + ap = argparse.ArgumentParser(description="ONL SSH Management") + ap.add_argument("--init", action='store_true', help="Initialize SSH files (if necessary)") + ap.add_argument("--force", "-f", action='store_true', help="Force regeneration of the host keys during initialization (--init)") + ap.add_argument("--quiet", "-q", action='store_true', help="Quiet output.") + ap.add_argument("--verbose", "-v", action='store_true', help="Verbose output.") + + ops = ap.parse_args() + + logging.basicConfig() + logger = logging.getLogger("SSH") + + if ops.verbose: + logger.setLevel(logging.DEBUG) + elif ops.quiet: + logger.setLevel(logging.WARNING) + else: + logger.setLevel(logging.INFO) + + ssh = OnlSSH(logger) + + if ops.init: + ssh.init(force=ops.force) diff --git a/tools/onlrfs.py b/tools/onlrfs.py index 42172e8b2e..4cf2411d89 100755 --- a/tools/onlrfs.py +++ b/tools/onlrfs.py @@ -410,6 +410,13 @@ def dpkg_configure(self, dir_): dpkg --configure -a # configure any packages that failed the first time and abort on failure. rm -f /usr/sbin/policy-rc.d + +# Replace generated host keys with symlinks to ONL-CONFIG. +# Host keys are individual for each host, generated at boot +# when missing and preserved on reinstall and upgrade. +for f in /etc/ssh/ssh_host_*key*; do + ln -snf "/mnt/onl/config/ssh/${f##*/}" "$f" +done """) logger.info("dpkg-configure filesystem...")