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;
};