From 869a508c0a9bef69530bce119410d632e7937e03 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Fri, 23 Aug 2024 20:35:07 +0100 Subject: [PATCH] add authorizedKeys option to ssh service this has no apparent use as it stands, but opens the door to having the keys managed by an external secrets service --- examples/router-with-l2tp.nix | 6 +++++- modules/ssh/default.nix | 11 +++++++++++ modules/ssh/ssh.nix | 22 +++++++++++++++++++--- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/examples/router-with-l2tp.nix b/examples/router-with-l2tp.nix index 1dbc5ee..36644c6 100644 --- a/examples/router-with-l2tp.nix +++ b/examples/router-with-l2tp.nix @@ -184,7 +184,11 @@ in rec { dependencies = [ config.services.hostname ]; }; - services.sshd = svc.ssh.build { }; + services.sshd = svc.ssh.build { + authorizedKeys = { + root = rsecrets.root.openssh.authorizedKeys.keys; + }; + }; services.lns-address = let ns = "$(output_word ${services.bootstrap-dhcpc} dns 1)"; diff --git a/modules/ssh/default.nix b/modules/ssh/default.nix index 8feff85..5d19975 100644 --- a/modules/ssh/default.nix +++ b/modules/ssh/default.nix @@ -19,6 +19,7 @@ in { type = liminix.lib.types.serviceDefn; }; }; + config.programs.busybox.options.FEATURE_FANCY_ECHO = "y"; config.system.service = { ssh = config.system.callService ./ssh.nix { address = mkOption { @@ -41,6 +42,16 @@ in { type = types.bool; default = false; description = "Allow remote hosts to connect to local forwarded ports (by default they are bound to loopback)"; }; + authorizedKeys = mkOption { + type = types.nullOr (types.attrsOf (types.listOf types.nonEmptyStr)); + example = { + root = ["ssh-rsa AAAAB3N...aZaZ"]; + alice = ["ssh-rsa AAAAB3N...qS4r"]; + bob = []; + }; + default = null; + description = "Authorized SSH public keys for each username. If this optin is provided it overrides any keys found in /home/{username}/.ssh"; + }; extraConfig = mkOption { type = types.separatedString " "; default = ""; diff --git a/modules/ssh/ssh.nix b/modules/ssh/ssh.nix index 94e1162..82eaf64 100644 --- a/modules/ssh/ssh.nix +++ b/modules/ssh/ssh.nix @@ -3,10 +3,11 @@ , dropbear , lib }: -p : +{authorizedKeys, ...} @ p : let + name = "sshd"; inherit (liminix.services) longrun; - inherit (lib) concatStringsSep; + inherit (lib) concatStringsSep mapAttrs mapAttrsToList; options = [ "-e" # pass environment to child @@ -21,19 +22,34 @@ let (lib.optional (! p.allowLocalPortForward) "-j") ++ (lib.optional (! p.allowRemotePortForward) "-k") ++ (lib.optional (! p.allowRemoteConnectionToForwardedPorts) "-a") ++ + (lib.optionals (authorizedKeys != null) + ["-U" "/run/${name}/authorized_keys/%n"]) ++ [(if p.address != null then "-p ${p.address}:${p.port}" else "-p ${builtins.toString p.port}")] ++ [p.extraConfig]; + authKeysConcat = + if authorizedKeys != null + then mapAttrs + (n : v : concatStringsSep "\\n" v) + authorizedKeys + else {}; in longrun { - name = "sshd"; + inherit name; # we need /run/dropbear to point to hostkey storage, as that # pathname is hardcoded into the binary. # env -i clears the environment so we don't pass anything weird to # ssh sessions run = '' ln -s $(mkstate dropbear) /run + mkdir -p /run/${name}/authorized_keys + ${concatStringsSep "\n" + (mapAttrsToList + (n : v : "echo -e '${v}' > /run/${name}/authorized_keys/${n} ") + authKeysConcat + ) + } . /etc/profile # sets PATH but do we need this? it's the same file as ashrc exec env -i ENV=/etc/ashrc PATH=$PATH ${dropbear}/bin/dropbear ${concatStringsSep " " options} '';