diff --git a/examples/rotuer.nix b/examples/rotuer.nix index 293546c..9351931 100644 --- a/examples/rotuer.nix +++ b/examples/rotuer.nix @@ -50,6 +50,7 @@ in rec { ../modules/hostapd ../modules/bridge ../modules/ntp + ../modules/ssh ]; rootfsType = "jffs2"; hostname = "rotuer"; @@ -95,25 +96,7 @@ in rec { makestep = { threshold = 1.0; limit = 3; }; }; - services.sshd = longrun { - name = "sshd"; - # env -i clears the environment so we don't pass anything weird to - # ssh sessions. Dropbear params are - # -e pass environment to child - # -E log to stderr - # -R create hostkeys if needed - # -P pid-file - # -F don't fork into background - - run = '' - if test -d /persist; then - mkdir -p /persist/secrets/dropbear - ln -s /persist/secrets/dropbear /run - fi - PATH=${lib.makeBinPath config.defaultProfile.packages}:/bin - exec env -i ENV=/etc/ashrc PATH=$PATH ${dropbear}/bin/dropbear -e -E -R -P /run/dropbear.pid -F - ''; - }; + services.sshd = svc.ssh.build { }; users.root = secrets.root; diff --git a/modules/ssh/default.nix b/modules/ssh/default.nix new file mode 100644 index 0000000..cb1305f --- /dev/null +++ b/modules/ssh/default.nix @@ -0,0 +1,50 @@ +## Secure Shell +## ============ +## +## Provide SSH service using Dropbear + +{ lib, pkgs, config, ...}: +let + inherit (lib) mkOption types; + inherit (pkgs) liminix; + mkBoolOption = description : mkOption { + type = types.bool; + inherit description; + default = true; + }; + +in { + options = { + system.service.ssh = mkOption { + type = liminix.lib.types.serviceDefn; + }; + }; + config.system.service = { + ssh = liminix.callService ./ssh.nix { + address = mkOption { + type = types.nullOr types.str; + default = null; + description = "Listen on specified address"; + example = "127.0.0.1"; + }; + port = mkOption { + type = types.port; + default = 22; + description = "Listen on specified TCP port"; + }; + allowRoot = mkBoolOption "Allow root to login"; + allowPasswordLogin = mkBoolOption "Allow login using password (disable for public key auth only)"; + allowPasswordLoginForRoot = mkBoolOption "Allow root to login using password (disable for public key auth only)"; + allowLocalPortForward = mkBoolOption "Enable local port forwarding"; + allowRemotePortForward = mkBoolOption "Enable remote port forwarding"; + allowRemoteConnectionToForwardedPorts = mkOption { + type = types.bool; default = false; + description = "Allow remote hosts to connect to local forwarded ports (by default they are bound to loopback)"; + }; + extraConfig = mkOption { + type = types.separatedString " "; + default = ""; + }; + }; + }; +} diff --git a/modules/ssh/ssh.nix b/modules/ssh/ssh.nix new file mode 100644 index 0000000..ef5942e --- /dev/null +++ b/modules/ssh/ssh.nix @@ -0,0 +1,42 @@ +{ + liminix +, dropbear +, serviceFns +, lib +}: +p : +let + inherit (liminix.services) longrun; + inherit (lib) concatStringsSep; + options = + [ + "-e" # pass environment to child + "-E" # log to stderr + "-R" # create hostkeys if needed + "-P /run/dropbear.pid" + "-F" # don't fork into background + ] ++ + (lib.optional (! p.allowRoot) "-w") ++ + (lib.optional (! p.allowPasswordLogin) "-s") ++ + (lib.optional (! p.allowPasswordLoginForRoot) "-g") ++ + (lib.optional (! p.allowLocalPortForward) "-j") ++ + (lib.optional (! p.allowRemotePortForward) "-k") ++ + (lib.optional (! p.allowRemoteConnectionToForwardedPorts) "-a") ++ + [(if p.address != null + then "-p ${p.address}:${p.port}" + else "-p ${builtins.toString p.port}")] ++ + [p.extraConfig]; +in +longrun { + name = "sshd"; + # env -i clears the environment so we don't pass anything weird to + # ssh sessions + run = '' + if test -d /persist; then + mkdir -p /persist/secrets/dropbear + ln -s /persist/secrets/dropbear /run + fi + . /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} + ''; +}