diff --git a/modules/services/networking/ssh/sshd.nix b/modules/services/networking/ssh/sshd.nix index 5a924361761..3bf4bf642fa 100644 --- a/modules/services/networking/ssh/sshd.nix +++ b/modules/services/networking/ssh/sshd.nix @@ -39,6 +39,7 @@ let ); userOptions = { + openssh.authorizedKeys = { preserveExistingKeys = mkOption { @@ -77,6 +78,7 @@ let }; }; + }; mkAuthkeyScript = @@ -127,19 +129,6 @@ let ${userLoop} ''; - preStart = pkgs.writeScript "openssh-pre-start" - '' - #! ${pkgs.stdenv.shell} - - ${mkAuthkeyScript} - - mkdir -m 0755 -p /etc/ssh - - if ! test -f ${cfg.hostKeyPath}; then - ssh-keygen -t ${hktn} -b ${toString hktb} -f ${cfg.hostKeyPath} -N "" - fi - ''; - in { @@ -317,27 +306,52 @@ in } ]; - boot.systemd.units."sshd.service".text = - '' - [Unit] - Description=SSH daemon + boot.systemd.services."set-ssh-keys.service" = + { description = "Update authorized SSH keys"; - [Service] - Environment=PATH=${pkgs.coreutils}/bin:${pkgs.openssh}/bin - Environment=LD_LIBRARY_PATH=${nssModulesPath} - Environment=LOCALE_ARCHIVE=/var/run/current-system/sw/lib/locale/locale-archive - ExecStartPre=${preStart} - ExecStart=\ - ${pkgs.openssh}/sbin/sshd -h ${cfg.hostKeyPath} \ - -f ${pkgs.writeText "sshd_config" cfg.extraConfig} - Restart=always - Type=forking - KillMode=process - PIDFile=/run/sshd.pid - ''; + wantedBy = [ "multi-user.target" ]; - boot.systemd.units."sshd.service".wantedBy = [ "multi-user.target" ]; + script = mkAuthkeyScript; + + serviceConfig = + '' + Type=oneshot + RemainAfterExit=true + ''; + }; + boot.systemd.services."sshd.service" = + { description = "SSH daemon"; + + wantedBy = [ "multi-user.target" ]; + after = [ "set-ssh-keys.service" ]; + + path = [ pkgs.openssh ]; + + environment.LD_LIBRARY_PATH = nssModulesPath; + environment.LOCALE_ARCHIVE = "/var/run/current-system/sw/lib/locale/locale-archive"; + + preStart = + '' + mkdir -m 0755 -p /etc/ssh + + if ! test -f ${cfg.hostKeyPath}; then + ssh-keygen -t ${hktn} -b ${toString hktb} -f ${cfg.hostKeyPath} -N "" + fi + ''; + + serviceConfig = + '' + ExecStart=\ + ${pkgs.openssh}/sbin/sshd -h ${cfg.hostKeyPath} \ + -f ${pkgs.writeText "sshd_config" cfg.extraConfig} + Restart=always + Type=forking + KillMode=process + PIDFile=/run/sshd.pid + ''; + }; + networking.firewall.allowedTCPPorts = cfg.ports; services.openssh.extraConfig = diff --git a/modules/system/boot/systemd.nix b/modules/system/boot/systemd.nix index d75572b2789..906558bd695 100644 --- a/modules/system/boot/systemd.nix +++ b/modules/system/boot/systemd.nix @@ -4,6 +4,102 @@ with pkgs.lib; let + servicesOptions = { + + description = mkOption { + default = ""; + types = types.uniq types.string; + description = "Description of this unit used in systemd messages and progress indicators."; + }; + + after = mkOption { + default = []; + types = types.listOf types.string; + description = '' + If the specified units are started at the same time as + this unit, delay this unit until they have started. + ''; + }; + + before = mkOption { + default = []; + types = types.listOf types.string; + description = '' + If the specified units are started at the same time as + this unit, delay them until this unit has started. + ''; + }; + + wantedBy = mkOption { + default = []; + types = types.listOf types.string; + description = "Units that want (i.e. depend on) this unit."; + }; + + environment = mkOption { + default = {}; + type = types.attrs; + example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; }; + description = "Environment variables passed to the services's processes."; + }; + + path = mkOption { + default = []; + apply = ps: "${makeSearchPath "bin" ps}:${makeSearchPath "sbin" ps}"; + description = '' + Packages added to the service's PATH + environment variable. Both the bin + and sbin subdirectories of each + package are added. + ''; + }; + + serviceConfig = mkOption { + default = ""; + type = types.string; + description = '' + Contents of the [Service] section of the unit. + See systemd.unit + 5 for details. + ''; + }; + + script = mkOption { + type = types.uniq types.string; + default = ""; + description = "Shell commands executed as the service's main process."; + }; + + preStart = mkOption { + type = types.string; + default = ""; + description = '' + Shell commands executed before the service's main process + is started. + ''; + }; + + }; + + + servicesConfig = { name, config, ... }: { + + config = { + + # Default path for systemd services. Should be quite minimal. + path = + [ pkgs.coreutils + pkgs.findutils + pkgs.gnugrep + pkgs.gnused + systemd + ]; + + }; + + }; + + cfg = config.boot.systemd; systemd = pkgs.systemd; @@ -111,6 +207,103 @@ let "shutdown.target.wants" ]; + rescueService = + '' + [Unit] + Description=Rescue Shell + DefaultDependencies=no + Conflicts=shutdown.target + After=sysinit.target + Before=shutdown.target + + [Service] + Environment=HOME=/root + WorkingDirectory=/root + ExecStartPre=-${pkgs.coreutils}/bin/echo 'Welcome to rescue mode. Use "systemctl default" or ^D to enter default mode.' + #ExecStart=-/sbin/sulogin + ExecStart=-${pkgs.bashInteractive}/bin/bash --login + ExecStopPost=-${systemd}/bin/systemctl --fail --no-block default + Type=idle + StandardInput=tty-force + StandardOutput=inherit + StandardError=inherit + KillMode=process + + # Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash + # terminates cleanly. + KillSignal=SIGHUP + ''; + + gettyService = + '' + [Unit] + Description=Getty on %I + Documentation=man:agetty(8) + After=systemd-user-sessions.service plymouth-quit-wait.service + + # If additional gettys are spawned during boot then we should make + # sure that this is synchronized before getty.target, even though + # getty.target didn't actually pull it in. + Before=getty.target + IgnoreOnIsolate=yes + + [Service] + Environment=TERM=linux + ExecStart=-${pkgs.utillinux}/sbin/agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400 + Type=idle + Restart=always + RestartSec=0 + UtmpIdentifier=%I + TTYPath=/dev/%I + TTYReset=yes + TTYVHangup=yes + TTYVTDisallocate=yes + KillMode=process + IgnoreSIGPIPE=no + + # Unset locale for the console getty since the console has problems + # displaying some internationalized messages. + Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION= + + # Some login implementations ignore SIGTERM, so we send SIGHUP + # instead, to ensure that login terminates cleanly. + KillSignal=SIGHUP + ''; + + serviceToUnit = name: def: + { inherit (def) wantedBy; + + text = + '' + [Unit] + ${optionalString (def.description != "") '' + Description=${def.description} + ''} + Before=${concatStringsSep " " def.before} + After=${concatStringsSep " " def.after} + + [Service] + Environment=PATH=${def.path} + ${concatMapStrings (n: "Environment=${n}=\"${getAttr n def.environment}\"\n") (attrNames def.environment)} + + ${optionalString (def.preStart != "") '' + ExecStartPre=${pkgs.writeScript "${name}-prestart.sh" '' + #! ${pkgs.stdenv.shell} -e + ${def.preStart} + ''} + ''} + + ${optionalString (def.script != "") '' + ExecStart=${pkgs.writeScript "${name}.sh" '' + #! ${pkgs.stdenv.shell} -e + ${def.script} + ''} + ''} + + ${def.serviceConfig} + ''; + }; + nixosUnits = mapAttrsToList makeUnit cfg.units; units = pkgs.runCommand "units" { preferLocalBuild = true; } @@ -160,20 +353,32 @@ in options = { boot.systemd.units = mkOption { + description = "Definition of systemd units."; default = {}; type = types.attrsOf types.optionSet; + options = { + text = mkOption { types = types.uniq types.string; description = "Text of this systemd unit."; }; + wantedBy = mkOption { default = []; types = types.listOf types.string; description = "Units that want (i.e. depend on) this unit."; }; + }; - description = "Definition of systemd units."; + + }; + + boot.systemd.services = mkOption { + description = "Definition of systemd services."; + default = {}; + type = types.attrsOf types.optionSet; + options = [ servicesOptions servicesConfig ]; }; boot.systemd.defaultUnit = mkOption { @@ -201,68 +406,11 @@ in } ]; - boot.systemd.units."getty@.service".text = - '' - [Unit] - Description=Getty on %I - Documentation=man:agetty(8) - After=systemd-user-sessions.service plymouth-quit-wait.service - - # If additional gettys are spawned during boot then we should make - # sure that this is synchronized before getty.target, even though - # getty.target didn't actually pull it in. - Before=getty.target - IgnoreOnIsolate=yes - - [Service] - Environment=TERM=linux - ExecStart=-${pkgs.utillinux}/sbin/agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400 - Type=idle - Restart=always - RestartSec=0 - UtmpIdentifier=%I - TTYPath=/dev/%I - TTYReset=yes - TTYVHangup=yes - TTYVTDisallocate=yes - KillMode=process - IgnoreSIGPIPE=no - - # Unset locale for the console getty since the console has problems - # displaying some internationalized messages. - Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION= - - # Some login implementations ignore SIGTERM, so we send SIGHUP - # instead, to ensure that login terminates cleanly. - KillSignal=SIGHUP - ''; - - boot.systemd.units."rescue.service".text = - '' - [Unit] - Description=Rescue Shell - DefaultDependencies=no - Conflicts=shutdown.target - After=sysinit.target - Before=shutdown.target - - [Service] - Environment=HOME=/root - WorkingDirectory=/root - ExecStartPre=-${pkgs.coreutils}/bin/echo 'Welcome to rescue mode. Use "systemctl default" or ^D to enter default mode.' - #ExecStart=-/sbin/sulogin - ExecStart=-${pkgs.bashInteractive}/bin/bash --login - ExecStopPost=-${systemd}/bin/systemctl --fail --no-block default - Type=idle - StandardInput=tty-force - StandardOutput=inherit - StandardError=inherit - KillMode=process - - # Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash - # terminates cleanly. - KillSignal=SIGHUP - ''; + boot.systemd.units = + { "rescue.service".text = rescueService; + "getty@.service".text = gettyService; + } + // mapAttrs serviceToUnit cfg.services; };