nixpkgs/nixos/modules/programs/rust-motd.nix
Maximilian Bosch d77b59b41d
nixos/rust-motd: use a second attribute (order) for the of sections in TOML
Rather than using `priority` with `sortProperties`, a new option called
`order` defines the ordering of the sections. I.e.

    order = [ "global" "uptime" "banner" ]

means that `uptime` comes before `banner`. Please note that `global` is
for global settings and not a section. I figured that it'd be too much
magic to hide this in the implementation and ask the user to specify the
order of _each_ section in `settings` instead.

OTOH this makes the intent way clearer than priorities. Also, this
remains opt-in, the option defaults to `attrNames cfg.settings`, i.e.
all sections ordered alphabetically.
2023-09-26 23:28:40 +02:00

162 lines
5.3 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.rust-motd;
format = pkgs.formats.toml { };
orderedSections = listToAttrs
(imap0
(i: attr: nameValuePair "env${toString i}" {
${attr} = cfg.settings.${attr};
})
cfg.order);
# Order the sections in the TOML according to the order of sections
# in `cfg.order`.
# This is done by
# * creating an attribute set with keys `env0`/`env1`/.../`envN`
# where `env0` contains the first section and `envN` the last
# (in the form of `{ sectionName = { /* ... */ }}`)
# * the declarations of `env0` to `envN` in ascending order are
# concatenated with `jq`. Now we have a JSON representation of
# the config in the correct order.
# * this is piped to `json2toml` to get the correct format for rust-motd.
motdConf = pkgs.runCommand "motd.conf"
(orderedSections // {
__structuredAttrs = true;
nativeBuildInputs = [ pkgs.remarshal pkgs.jq ];
})
''
cat "$NIX_ATTRS_JSON_FILE" \
| jq '${concatMapStringsSep " + " (key: ''."${key}"'') (attrNames orderedSections)}' \
| json2toml /dev/stdin "$out"
'';
in {
options.programs.rust-motd = {
enable = mkEnableOption (lib.mdDoc "rust-motd");
enableMotdInSSHD = mkOption {
default = true;
type = types.bool;
description = mdDoc ''
Whether to let `openssh` print the
result when entering a new `ssh`-session.
By default either nothing or a static file defined via
[](#opt-users.motd) is printed. Because of that,
the latter option is incompatible with this module.
'';
};
refreshInterval = mkOption {
default = "*:0/5";
type = types.str;
description = mdDoc ''
Interval in which the {manpage}`motd(5)` file is refreshed.
For possible formats, please refer to {manpage}`systemd.time(7)`.
'';
};
order = mkOption {
type = types.listOf types.str;
default = attrNames cfg.settings;
defaultText = literalExpression "attrNames cfg.settings";
description = mdDoc ''
The order of the sections in [](#opt-programs.rust-motd.settings) implies
the order of sections in the motd. Since attribute sets in Nix are always
ordered alphabetically internally this means that
```nix
{
uptime = { /* ... */ };
banner = { /* ... */ };
}
```
will still have `banner` displayed before `uptime`.
To work around that, this option can be used to define the order of all keys,
i.e.
```nix
{
order = [
"uptime"
"banner"
];
}
```
makes sure that `uptime` is placed before `banner` in the motd.
'';
};
settings = mkOption {
type = types.attrsOf (types.submodule {
freeformType = format.type;
});
description = mdDoc ''
Settings on what to generate. Please read the
[upstream documentation](https://github.com/rust-motd/rust-motd/blob/main/README.md#configuration)
for further information.
'';
};
};
config = mkIf cfg.enable {
assertions = [
{ assertion = config.users.motd == null;
message = ''
`programs.rust-motd` is incompatible with `users.motd`!
'';
}
{ assertion = length cfg.order == length (attrNames cfg.settings)
&& all (section: cfg.settings?${section}) cfg.order;
message = ''
Please ensure that every section from `programs.rust-motd.settings` is present in
`programs.rust-motd.order`.
'';
}
];
systemd.services.rust-motd = {
path = with pkgs; [ bash ];
documentation = [ "https://github.com/rust-motd/rust-motd/blob/v${pkgs.rust-motd.version}/README.md" ];
description = "motd generator";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.writeShellScript "update-motd" ''
${pkgs.rust-motd}/bin/rust-motd ${motdConf} > motd
''}";
CapabilityBoundingSet = [ "" ];
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectKernelTunables = true;
ProtectSystem = "full";
StateDirectory = "rust-motd";
RestrictAddressFamilies = [ "AF_UNIX" ];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
WorkingDirectory = "/var/lib/rust-motd";
};
};
systemd.timers.rust-motd = {
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = cfg.refreshInterval;
};
security.pam.services.sshd.text = mkIf cfg.enableMotdInSSHD (mkDefault (mkAfter ''
session optional ${pkgs.pam}/lib/security/pam_motd.so motd=/var/lib/rust-motd/motd
''));
services.openssh.extraConfig = mkIf (cfg.settings ? last_login && cfg.settings.last_login != {}) ''
PrintLastLog no
'';
};
meta.maintainers = with maintainers; [ ma27 ];
}