diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/abs-podcast-autoplaylist.nix | 55 | ||||
-rw-r--r-- | modules/backup-utils.nix | 3 | ||||
-rw-r--r-- | modules/borgcopy/default.nix | 1 | ||||
-rw-r--r-- | modules/envfs.nix | 77 | ||||
-rw-r--r-- | modules/i18n.nix | 156 | ||||
-rw-r--r-- | modules/installer.nix | 56 | ||||
-rw-r--r-- | modules/niri.nix | 6 | ||||
-rw-r--r-- | modules/nix-access-tokens/default.nix | 24 | ||||
-rw-r--r-- | modules/nix-access-tokens/nix.conf | 32 | ||||
-rw-r--r-- | modules/pgbackrest.nix | 3 | ||||
-rw-r--r-- | modules/postsrsd.nix | 157 | ||||
-rw-r--r-- | modules/systemd-run0.nix | 4 | ||||
-rw-r--r-- | modules/tzupdate.nix | 81 | ||||
-rw-r--r-- | modules/uucp.nix | 398 |
14 files changed, 578 insertions, 475 deletions
diff --git a/modules/abs-podcast-autoplaylist.nix b/modules/abs-podcast-autoplaylist.nix new file mode 100644 index 00000000..f526a434 --- /dev/null +++ b/modules/abs-podcast-autoplaylist.nix | |||
@@ -0,0 +1,55 @@ | |||
1 | { config, pkgs, lib, utils, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.services.abs-podcast-autoplaylist; | ||
5 | |||
6 | enabledAttrs = lib.filterAttrs (_name: { enable, ... }: enable) cfg; | ||
7 | in { | ||
8 | options = { | ||
9 | services.abs-podcast-autoplaylist = lib.mkOption { | ||
10 | default = {}; | ||
11 | type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { | ||
12 | options = { | ||
13 | enable = lib.mkEnableOption "this instance of abs-podcast-autoplaylist" // { | ||
14 | default = true; | ||
15 | }; | ||
16 | cron = lib.mkOption { | ||
17 | type = lib.types.str; | ||
18 | default = "*-*-* *:00/30:00"; | ||
19 | }; | ||
20 | configSecret = lib.mkOption { | ||
21 | type = lib.types.str; | ||
22 | default = "abs-podcast-autoplaylist-${name}.toml"; | ||
23 | }; | ||
24 | }; | ||
25 | })); | ||
26 | }; | ||
27 | }; | ||
28 | |||
29 | config = lib.mkIf (enabledAttrs != {}) { | ||
30 | systemd.services = { | ||
31 | "abs-podcast-autoplaylist@" = { | ||
32 | serviceConfig = { | ||
33 | WorkingDirectory = "%d"; | ||
34 | DynamicUser = true; | ||
35 | ProtectHome = true; | ||
36 | PrivateTmp = true; | ||
37 | PrivateDevices = true; | ||
38 | Type = "oneshot"; | ||
39 | ExecStart = "${lib.getExe pkgs.abs-podcast-autoplaylist} %I.toml"; | ||
40 | TimeoutSec = "5min"; | ||
41 | }; | ||
42 | }; | ||
43 | } // lib.mapAttrs' (name: { configSecret, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
44 | overrideStrategy = "asDropin"; | ||
45 | serviceConfig = { | ||
46 | LoadCredential = "${name}.toml:${config.sops.secrets.${configSecret}.path}"; | ||
47 | }; | ||
48 | }) enabledAttrs; | ||
49 | |||
50 | systemd.timers = lib.mapAttrs' (name: { cron, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
51 | wantedBy = [ "timers.target" ]; | ||
52 | timerConfig.OnCalendar = cron; | ||
53 | }) enabledAttrs; | ||
54 | }; | ||
55 | } | ||
diff --git a/modules/backup-utils.nix b/modules/backup-utils.nix index 82a42ecd..698140da 100644 --- a/modules/backup-utils.nix +++ b/modules/backup-utils.nix | |||
@@ -9,5 +9,8 @@ with lib; | |||
9 | 9 | ||
10 | config = { | 10 | config = { |
11 | services.borgsnap.archive-prefix = mkDefault "yggdrasil.${hostName}."; | 11 | services.borgsnap.archive-prefix = mkDefault "yggdrasil.${hostName}."; |
12 | |||
13 | systemd.services."zfssnap-prune".restartIfChanged = false; | ||
14 | systemd.services."zfssnap".restartIfChanged = false; | ||
12 | }; | 15 | }; |
13 | } | 16 | } |
diff --git a/modules/borgcopy/default.nix b/modules/borgcopy/default.nix index 475edbd9..8e1afc27 100644 --- a/modules/borgcopy/default.nix +++ b/modules/borgcopy/default.nix | |||
@@ -22,6 +22,7 @@ let | |||
22 | }; | 22 | }; |
23 | 23 | ||
24 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { | 24 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { |
25 | restartIfChanged = false; | ||
25 | serviceConfig = { | 26 | serviceConfig = { |
26 | Type = "oneshot"; | 27 | Type = "oneshot"; |
27 | ExecStart = "${copyBorg}/bin/copy_borg --verbosity ${toString opts.verbosity} ${utils.escapeSystemdExecArgs [opts.from opts.to]}"; | 28 | ExecStart = "${copyBorg}/bin/copy_borg --verbosity ${toString opts.verbosity} ${utils.escapeSystemdExecArgs [opts.from opts.to]}"; |
diff --git a/modules/envfs.nix b/modules/envfs.nix deleted file mode 100644 index b5b453a5..00000000 --- a/modules/envfs.nix +++ /dev/null | |||
@@ -1,77 +0,0 @@ | |||
1 | { pkgs, config, lib, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.services.envfs; | ||
5 | mounts = { | ||
6 | "/usr/bin" = { | ||
7 | device = "none"; | ||
8 | fsType = "envfs"; | ||
9 | options = [ | ||
10 | "bind-mount=/bin" | ||
11 | "fallback-path=${pkgs.symlinkJoin { | ||
12 | name = "fallback-path"; | ||
13 | inherit (cfg) paths; | ||
14 | }}" | ||
15 | "nofail" | ||
16 | ]; | ||
17 | }; | ||
18 | "/bin" = { | ||
19 | device = "/usr/bin"; | ||
20 | fsType = "none"; | ||
21 | options = [ "bind" "nofail" ]; | ||
22 | }; | ||
23 | }; | ||
24 | in { | ||
25 | disabledModules = [ "tasks/filesystems/envfs.nix" ]; | ||
26 | |||
27 | options = { | ||
28 | services.envfs = { | ||
29 | enable = lib.mkEnableOption "Envfs filesystem" // { | ||
30 | default = true; | ||
31 | description = '' | ||
32 | Fuse filesystem that returns symlinks to executables based on the PATH | ||
33 | of the requesting process. This is useful to execute shebangs on NixOS | ||
34 | that assume hard coded locations in locations like /bin or /usr/bin | ||
35 | etc. | ||
36 | ''; | ||
37 | }; | ||
38 | |||
39 | package = lib.mkOption { | ||
40 | type = lib.types.package; | ||
41 | default = pkgs.envfs; | ||
42 | defaultText = lib.literalExpression "pkgs.envfs"; | ||
43 | description = "Which package to use for the envfs."; | ||
44 | }; | ||
45 | |||
46 | paths = lib.mkOption { | ||
47 | type = lib.types.listOf lib.types.package; | ||
48 | default = [ | ||
49 | (pkgs.runCommand "fallback-path-environment" {} '' | ||
50 | mkdir -p $out | ||
51 | ln -s ${config.environment.usrbinenv} $out/env | ||
52 | ln -s ${config.environment.binsh} $out/sh | ||
53 | '') | ||
54 | ]; | ||
55 | defaultText = lib.literalExpression '' | ||
56 | [ (pkgs.runCommand "fallback-path-environment" {} ''' | ||
57 | mkdir -p $out | ||
58 | ln -s ''${config.environment.usrbinenv} $out/env | ||
59 | ln -s ''${config.environment.binsh} $out/sh | ||
60 | ''') | ||
61 | ] | ||
62 | ''; | ||
63 | description = "Extra packages to join into collection of fallback executables in case not other executable is found"; | ||
64 | }; | ||
65 | }; | ||
66 | }; | ||
67 | |||
68 | config = lib.mkIf (cfg.enable) { | ||
69 | environment.systemPackages = [ cfg.package ]; | ||
70 | # we also want these mounts in virtual machines. | ||
71 | fileSystems = if config.virtualisation ? qemu then lib.mkVMOverride mounts else mounts; | ||
72 | |||
73 | # We no longer need those when using envfs | ||
74 | system.activationScripts.usrbinenv = lib.mkForce ""; | ||
75 | system.activationScripts.binsh = lib.mkForce ""; | ||
76 | }; | ||
77 | } | ||
diff --git a/modules/i18n.nix b/modules/i18n.nix new file mode 100644 index 00000000..f84e8b64 --- /dev/null +++ b/modules/i18n.nix | |||
@@ -0,0 +1,156 @@ | |||
1 | { | ||
2 | config, | ||
3 | lib, | ||
4 | pkgs, | ||
5 | ... | ||
6 | }: | ||
7 | let | ||
8 | aggregatedLocales = | ||
9 | (builtins.map | ||
10 | (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") | ||
11 | ( | ||
12 | [ config.i18n.defaultLocale ] | ||
13 | ++ (lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales) | ||
14 | ++ (lib.attrValues (lib.filterAttrs (n: _v: lib.hasPrefix "LC_" n) config.i18n.extraLocaleSettings)) | ||
15 | ) | ||
16 | ) | ||
17 | ++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales); | ||
18 | in | ||
19 | { | ||
20 | disabledModules = [ "config/i18n.nix" ]; | ||
21 | |||
22 | ###### interface | ||
23 | |||
24 | options = { | ||
25 | |||
26 | i18n = { | ||
27 | glibcLocales = lib.mkOption { | ||
28 | type = lib.types.path; | ||
29 | default = pkgs.glibcLocales.override { | ||
30 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
31 | locales = config.i18n.supportedLocales; | ||
32 | }; | ||
33 | defaultText = lib.literalExpression '' | ||
34 | pkgs.glibcLocales.override { | ||
35 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
36 | locales = config.i18n.supportedLocales; | ||
37 | } | ||
38 | ''; | ||
39 | example = lib.literalExpression "pkgs.glibcLocales"; | ||
40 | description = '' | ||
41 | Customized pkg.glibcLocales package. | ||
42 | |||
43 | Changing this option can disable handling of i18n.defaultLocale | ||
44 | and supportedLocale. | ||
45 | ''; | ||
46 | }; | ||
47 | |||
48 | defaultLocale = lib.mkOption { | ||
49 | type = lib.types.str; | ||
50 | default = "en_US.UTF-8"; | ||
51 | example = "nl_NL.UTF-8"; | ||
52 | description = '' | ||
53 | The default locale. It determines the language for program | ||
54 | messages, the format for dates and times, sort order, and so on. | ||
55 | It also determines the character set, such as UTF-8. | ||
56 | ''; | ||
57 | }; | ||
58 | |||
59 | extraLocales = lib.mkOption { | ||
60 | type = lib.types.either (lib.types.listOf lib.types.str) (lib.types.enum [ "all" ]); | ||
61 | default = [ ]; | ||
62 | example = [ "nl_NL.UTF-8" ]; | ||
63 | description = '' | ||
64 | Additional locales that the system should support, besides the ones | ||
65 | configured with {option}`i18n.defaultLocale` and | ||
66 | {option}`i18n.extraLocaleSettings`. | ||
67 | Set this to `"all"` to install all available locales. | ||
68 | ''; | ||
69 | }; | ||
70 | |||
71 | extraLocaleSettings = lib.mkOption { | ||
72 | type = lib.types.attrsOf lib.types.str; | ||
73 | default = { }; | ||
74 | example = { | ||
75 | LC_MESSAGES = "en_US.UTF-8"; | ||
76 | LC_TIME = "de_DE.UTF-8"; | ||
77 | }; | ||
78 | description = '' | ||
79 | A set of additional system-wide locale settings other than | ||
80 | `LANG` which can be configured with | ||
81 | {option}`i18n.defaultLocale`. | ||
82 | ''; | ||
83 | }; | ||
84 | |||
85 | supportedLocales = lib.mkOption { | ||
86 | type = lib.types.listOf lib.types.str; | ||
87 | visible = false; | ||
88 | default = lib.unique ( | ||
89 | [ | ||
90 | "C.UTF-8/UTF-8" | ||
91 | "en_US.UTF-8/UTF-8" | ||
92 | ] | ||
93 | ++ aggregatedLocales | ||
94 | ); | ||
95 | example = [ | ||
96 | "en_US.UTF-8/UTF-8" | ||
97 | "nl_NL.UTF-8/UTF-8" | ||
98 | "nl_NL/ISO-8859-1" | ||
99 | ]; | ||
100 | description = '' | ||
101 | List of locales that the system should support. The value | ||
102 | `"all"` means that all locales supported by | ||
103 | Glibc will be installed. A full list of supported locales | ||
104 | can be found at <https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED>. | ||
105 | ''; | ||
106 | }; | ||
107 | |||
108 | }; | ||
109 | |||
110 | }; | ||
111 | |||
112 | ###### implementation | ||
113 | |||
114 | config = { | ||
115 | warnings = | ||
116 | lib.optional | ||
117 | ( | ||
118 | !( | ||
119 | (lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ] | ||
120 | || lib.any (x: x == "all") config.i18n.supportedLocales | ||
121 | ) | ||
122 | ) | ||
123 | '' | ||
124 | `i18n.supportedLocales` is deprecated in favor of `i18n.extraLocales`, | ||
125 | and it seems you are using `i18n.supportedLocales` and forgot to | ||
126 | include some locales specified in `i18n.defaultLocale`, | ||
127 | `i18n.extraLocales` or `i18n.extraLocaleSettings`. | ||
128 | |||
129 | If you're trying to install additional locales not specified in | ||
130 | `i18n.defaultLocale` or `i18n.extraLocaleSettings`, consider adding | ||
131 | only those locales to `i18n.extraLocales`. | ||
132 | ''; | ||
133 | |||
134 | environment.systemPackages = | ||
135 | # We increase the priority a little, so that plain glibc in systemPackages can't win. | ||
136 | lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales); | ||
137 | |||
138 | environment.sessionVariables = { | ||
139 | LANG = config.i18n.defaultLocale; | ||
140 | LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; | ||
141 | } // config.i18n.extraLocaleSettings; | ||
142 | |||
143 | systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) { | ||
144 | LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive"; | ||
145 | }; | ||
146 | |||
147 | # ‘/etc/locale.conf’ is used by systemd. | ||
148 | environment.etc."locale.conf".source = pkgs.writeText "locale.conf" '' | ||
149 | LANG=${config.i18n.defaultLocale} | ||
150 | ${lib.concatStringsSep "\n" ( | ||
151 | lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings | ||
152 | )} | ||
153 | ''; | ||
154 | |||
155 | }; | ||
156 | } | ||
diff --git a/modules/installer.nix b/modules/installer.nix new file mode 100644 index 00000000..3e5c6d5b --- /dev/null +++ b/modules/installer.nix | |||
@@ -0,0 +1,56 @@ | |||
1 | { flake, config, lib, pkgs, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.installer.links; | ||
5 | |||
6 | installerOutPath = { | ||
7 | "cd-dvd" = _: installerBuild: "${installerBuild.config.system.build.isoImage}/iso"; | ||
8 | "netboot" = {system, variant}: installerBuild: pkgs.runCommandLocal "${system}-${variant}" {} '' | ||
9 | mkdir $out | ||
10 | install -m 0444 -t $out \ | ||
11 | ${installerBuild.config.system.build.netbootRamdisk}/initrd \ | ||
12 | ${installerBuild.config.system.build.kernel}/${config.system.boot.loader.kernelFile} \ | ||
13 | ${installerBuild.config.system.build.netbootIpxeScript}/netboot.ipxe \ | ||
14 | ${pkgs.ipxe.override { | ||
15 | additionalTargets = { | ||
16 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
17 | }; | ||
18 | additionalOptions = [ | ||
19 | "NSLOOKUP_CMD" | ||
20 | "PING_CMD" | ||
21 | "CONSOLE_CMD" | ||
22 | ]; | ||
23 | embedScript = pkgs.writeText "netboot.ipxe" '' | ||
24 | #!ipxe | ||
25 | |||
26 | chain netboot.ipxe | ||
27 | ''; | ||
28 | }}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} | ||
29 | ''; | ||
30 | }; | ||
31 | in { | ||
32 | options = { | ||
33 | installer.links = lib.mkOption { | ||
34 | type = lib.types.listOf (lib.types.submodule { | ||
35 | options = { | ||
36 | system = lib.mkOption { | ||
37 | type = lib.types.str; | ||
38 | }; | ||
39 | variant = lib.mkOption { | ||
40 | type = lib.types.str; | ||
41 | }; | ||
42 | }; | ||
43 | }); | ||
44 | default = []; | ||
45 | }; | ||
46 | }; | ||
47 | |||
48 | config = lib.mkIf (cfg != []) { | ||
49 | systemd.tmpfiles.rules = map (installer'@{system, variant}: | ||
50 | let | ||
51 | installer = "${system}-${variant}"; | ||
52 | installerBuild = builtins.addErrorContext "while evaluating installer-${installer}" flake.nixosConfigurations.${"installer-${installer}"}; | ||
53 | in "L+ /run/installer-${installer} - - - - ${installerOutPath.${variant} installer' installerBuild}" | ||
54 | ) cfg; | ||
55 | }; | ||
56 | } | ||
diff --git a/modules/niri.nix b/modules/niri.nix new file mode 100644 index 00000000..4e2ddf8b --- /dev/null +++ b/modules/niri.nix | |||
@@ -0,0 +1,6 @@ | |||
1 | { flakeInputs, ... }: | ||
2 | { | ||
3 | imports = [ | ||
4 | flakeInputs.niri-flake.nixosModules.niri | ||
5 | ]; | ||
6 | } | ||
diff --git a/modules/nix-access-tokens/default.nix b/modules/nix-access-tokens/default.nix new file mode 100644 index 00000000..a3b7abfa --- /dev/null +++ b/modules/nix-access-tokens/default.nix | |||
@@ -0,0 +1,24 @@ | |||
1 | { lib, config, hostName ,... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.nix.includeAccessTokens; | ||
5 | in { | ||
6 | options = { | ||
7 | nix.includeAccessTokens.enable = lib.mkEnableOption "including access tokens in nix.conf" // { default = lib.elem hostName ["sif" "surtr" "vidhar"]; }; | ||
8 | }; | ||
9 | |||
10 | config = lib.mkIf cfg.enable { | ||
11 | nix = { | ||
12 | extraOptions = '' | ||
13 | !include ${config.sops.secrets.nixAccessTokens.path} | ||
14 | ''; | ||
15 | }; | ||
16 | |||
17 | sops.secrets.nixAccessTokens = { | ||
18 | format = "binary"; | ||
19 | sopsFile = ./nix.conf; | ||
20 | mode = "0440"; | ||
21 | group = "wheel"; | ||
22 | }; | ||
23 | }; | ||
24 | } | ||
diff --git a/modules/nix-access-tokens/nix.conf b/modules/nix-access-tokens/nix.conf new file mode 100644 index 00000000..f0b394ef --- /dev/null +++ b/modules/nix-access-tokens/nix.conf | |||
@@ -0,0 +1,32 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:/cdBpvCAFpgm0YWhy1WYlA09KlU6PzVfBYVLBD0boqGqvP+8wuyDzj5KWbcKsdGhoiklODiKR0ODXNU+fA35y862PFXvSb4xVyfbdKRndYdIA4W6vyobtoC9h7B1yR9pkq9L+1tqlU30Dgy2Gndg9rWHlIo+1lO/1A==,iv:B1Px2+cxCaopHZThkEG5saOib+PNvurPIS6aeAv2uPo=,tag:K3JqRaX3/iIqD3c//YdqSQ==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5NkZUUGI3M2pQYWVXeFV6\na2h2czRTeUJFekJCS012YlBkL1FDdTd3ekZ3ClJsTVh0R2JQM0Jua1JjL285RVA1\nRHhlbjlLdmNBUXVLelFGY2NGYWpLejQKLS0tIDBUWUhJNm8zWGoyQ0pBYnV1ZjBh\ndktNRkNPS1lpWXFITC81aEZJbXlONk0Km2c1xVKwSankaVs7O/utGJwRRX395upz\ndPbsOElTnbGmkb0esGtvGSPboTvK+gjn9w/GhaPyTnNDoos7GaIfyg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age1fj65apkhfkrwyv5tx6zcs9nkjg8267fy733qph30sc7zfn7vapjqkd5kne", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bS9iY2lua3U4U3lJa1pK\nSlZNMmFZMEU5M1V2bWRjaXIwajZJVDJPMlM4Cmd3TTNFWjVuSGdtbC9iODltTS91\nOE5XOEVEQkh0SFpVVW5jc3IzbzNpTmMKLS0tIEtrSU54QUVPa2tBZDhLYlRFWitR\nc2x6MFlxL0tobDJTek42dEcyZXpoWDgKXzQfU+o6FkbJBwmm6oaHu4sDPi822uUR\n5VY6gY/h3g2kM4cuS03Q4NJmeRxuh7cx0UqGU3j5Mf8muE1LHpYEPw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | }, | ||
17 | { | ||
18 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
19 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaOVpNZ2lVT0VwbHVZNzFl\nenJsMGpnbkRvU0xOSU5obk5yT2p5ZVNzdXhNCnVlQzZtRjZNVmJLSUpKc3UwVXZs\nWi9EZ3kxZkJNeFJDSjl1L1IweTFNMXcKLS0tIDJUOTBwTldCUmlnU0tWVkZkNzJL\nejM4ajJVbVhvSm1YM2Vxa2JldllYN0UKAzxy2wkzRvCSiTy417AulpCu41z668HG\nto92eGF2ZRFfEG5LGlCKWeDcP3gM8QwKiVlm6wndbOkhMMfc4Sp3wA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
20 | }, | ||
21 | { | ||
22 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
23 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2ejRHcGttNUxYZnFzTU5J\nMTFvY3daQ1VMM2xxYTgvLzZwT1owazVNenhzCktaWFF6K2s5UjI2b20rSHFNSS9E\nMVlJSmZhQm15eUs3U0hGTGpSRndmSDgKLS0tIDVrcjl4eDhwak1pRithbnRWWEZy\nVE9EOEpKdEJoRTFrTXpQVDc1cmsrU1kK/goTdUmpZPeMRbY1QzLXAa6Qpg4YYYYo\n3v3GK1bzdey8szfgIr1dHTtQEzqE2WX1swzZizDXj/RiUWx01Ky3GA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
24 | } | ||
25 | ], | ||
26 | "lastmodified": "2025-01-25T19:58:58Z", | ||
27 | "mac": "ENC[AES256_GCM,data:Oza4XgnTX3vly89nGluLbEytk1dUYAiOhIYewQyDLLLSSlUIpXmWhV+X0HUQ9AX5kUrEhNbVzRdvUG/9YwoWjTJfvd7tw41IYeTqgykMNXJUfGssoutXfeij9YR+t5aJaRhlTkIWcBhUjXSUNyJCl6Z3XmzWstTPZXEU9VmAvuE=,iv:LqVwIiit+WqI5NWSboexWsmPzg7e63nWJYsNFEK1Uog=,tag:ClR6oI62WXEfIYYAY6vL0A==,type:str]", | ||
28 | "pgp": null, | ||
29 | "unencrypted_suffix": "_unencrypted", | ||
30 | "version": "3.9.3" | ||
31 | } | ||
32 | } \ No newline at end of file | ||
diff --git a/modules/pgbackrest.nix b/modules/pgbackrest.nix index 886840b9..550e970b 100644 --- a/modules/pgbackrest.nix +++ b/modules/pgbackrest.nix | |||
@@ -43,6 +43,8 @@ let | |||
43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; | 43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; |
44 | inherit (utils.systemdUtils.unitOptions) unitOption; | 44 | inherit (utils.systemdUtils.unitOptions) unitOption; |
45 | in { | 45 | in { |
46 | disabledModules = ["services/backup/pgbackrest.nix"]; | ||
47 | |||
46 | options = { | 48 | options = { |
47 | services.pgbackrest = { | 49 | services.pgbackrest = { |
48 | enable = mkEnableOption "pgBackRest"; | 50 | enable = mkEnableOption "pgBackRest"; |
@@ -216,6 +218,7 @@ in { | |||
216 | }; | 218 | }; |
217 | }; | 219 | }; |
218 | } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { | 220 | } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { |
221 | restartIfChanged = false; | ||
219 | description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})"; | 222 | description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})"; |
220 | serviceConfig = { | 223 | serviceConfig = { |
221 | Type = "oneshot"; | 224 | Type = "oneshot"; |
diff --git a/modules/postsrsd.nix b/modules/postsrsd.nix new file mode 100644 index 00000000..205e669d --- /dev/null +++ b/modules/postsrsd.nix | |||
@@ -0,0 +1,157 @@ | |||
1 | { | ||
2 | config, | ||
3 | lib, | ||
4 | pkgs, | ||
5 | ... | ||
6 | }: | ||
7 | let | ||
8 | |||
9 | cfg = config.services.postsrsd; | ||
10 | runtimeDirectoryName = "postsrsd"; | ||
11 | runtimeDirectory = "/run/${runtimeDirectoryName}"; | ||
12 | # TODO: follow RFC 42, but we need a libconfuse format first: | ||
13 | # https://github.com/NixOS/nixpkgs/issues/401565 | ||
14 | # Arrays in `libconfuse` look like this: {"Life", "Universe", "Everything"} | ||
15 | # See https://www.nongnu.org/confuse/tutorial-html/ar01s03.html. | ||
16 | # | ||
17 | # Note: We're using `builtins.toJSON` to escape strings, but JSON strings | ||
18 | # don't have exactly the same semantics as libconfuse strings. For example, | ||
19 | # "${F}" gets treated as an env var reference, see above issue for details. | ||
20 | libconfuseDomains = "{ " + lib.concatMapStringsSep ", " builtins.toJSON cfg.domains + " }"; | ||
21 | configFile = pkgs.writeText "postsrsd.conf" '' | ||
22 | secrets-file = "''${CREDENTIALS_DIRECTORY}/secrets-file" | ||
23 | domains = ${libconfuseDomains} | ||
24 | separator = "${cfg.separator}" | ||
25 | |||
26 | # Disable postsrsd's jailing in favor of confinement with systemd. | ||
27 | unprivileged-user = "" | ||
28 | chroot-dir = "" | ||
29 | |||
30 | ${cfg.extraConfig} | ||
31 | ''; | ||
32 | |||
33 | in | ||
34 | { | ||
35 | imports = | ||
36 | map | ||
37 | ( | ||
38 | name: | ||
39 | lib.mkRemovedOptionModule [ "services" "postsrsd" name ] '' | ||
40 | `postsrsd` was upgraded to `>= 2.0.0`, with some different behaviors and configuration settings: | ||
41 | - NixOS Release Notes: https://nixos.org/manual/nixos/unstable/release-notes#sec-nixpkgs-release-25.05-incompatibilities | ||
42 | - NixOS Options Reference: https://nixos.org/manual/nixos/unstable/options#opt-services.postsrsd.enable | ||
43 | - Migration instructions: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#migrating-from-version-1x | ||
44 | - Postfix Setup: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#postfix-setup | ||
45 | '' | ||
46 | ) | ||
47 | [ | ||
48 | "domain" | ||
49 | "forwardPort" | ||
50 | "reversePort" | ||
51 | "timeout" | ||
52 | "excludeDomains" | ||
53 | ]; | ||
54 | |||
55 | disabledModules = [ "services/mail/postsrsd.nix" ]; | ||
56 | |||
57 | options = { | ||
58 | services.postsrsd = { | ||
59 | enable = lib.mkOption { | ||
60 | type = lib.types.bool; | ||
61 | default = false; | ||
62 | description = "Whether to enable the postsrsd SRS server for Postfix."; | ||
63 | }; | ||
64 | |||
65 | secretsFile = lib.mkOption { | ||
66 | type = lib.types.path; | ||
67 | default = "/var/lib/postsrsd/postsrsd.secret"; | ||
68 | description = "Secret keys used for signing and verification"; | ||
69 | }; | ||
70 | |||
71 | domains = lib.mkOption { | ||
72 | type = lib.types.listOf lib.types.str; | ||
73 | description = "Domain names for rewrite"; | ||
74 | default = [ config.networking.hostName ]; | ||
75 | defaultText = lib.literalExpression "[ config.networking.hostName ]"; | ||
76 | }; | ||
77 | |||
78 | separator = lib.mkOption { | ||
79 | type = lib.types.enum [ | ||
80 | "-" | ||
81 | "=" | ||
82 | "+" | ||
83 | ]; | ||
84 | default = "="; | ||
85 | description = "First separator character in generated addresses"; | ||
86 | }; | ||
87 | |||
88 | user = lib.mkOption { | ||
89 | type = lib.types.str; | ||
90 | default = "postsrsd"; | ||
91 | description = "User for the daemon"; | ||
92 | }; | ||
93 | |||
94 | group = lib.mkOption { | ||
95 | type = lib.types.str; | ||
96 | default = "postsrsd"; | ||
97 | description = "Group for the daemon"; | ||
98 | }; | ||
99 | |||
100 | extraConfig = lib.mkOption { | ||
101 | type = lib.types.lines; | ||
102 | default = ""; | ||
103 | }; | ||
104 | }; | ||
105 | }; | ||
106 | |||
107 | config = lib.mkIf cfg.enable { | ||
108 | users.users = lib.optionalAttrs (cfg.user == "postsrsd") { | ||
109 | postsrsd = { | ||
110 | group = cfg.group; | ||
111 | uid = config.ids.uids.postsrsd; | ||
112 | }; | ||
113 | }; | ||
114 | |||
115 | users.groups = lib.optionalAttrs (cfg.group == "postsrsd") { | ||
116 | postsrsd.gid = config.ids.gids.postsrsd; | ||
117 | }; | ||
118 | |||
119 | systemd.services.postsrsd-generate-secrets = { | ||
120 | path = [ pkgs.coreutils ]; | ||
121 | script = '' | ||
122 | if [ -e "${cfg.secretsFile}" ]; then | ||
123 | echo "Secrets file exists. Nothing to do!" | ||
124 | else | ||
125 | echo "WARNING: secrets file not found, autogenerating!" | ||
126 | DIR="$(dirname "${cfg.secretsFile}")" | ||
127 | install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR" | ||
128 | install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}" | ||
129 | fi | ||
130 | ''; | ||
131 | serviceConfig = { | ||
132 | Type = "oneshot"; | ||
133 | }; | ||
134 | }; | ||
135 | |||
136 | systemd.services.postsrsd = { | ||
137 | description = "PostSRSd SRS rewriting server"; | ||
138 | after = [ | ||
139 | "network.target" | ||
140 | "postsrsd-generate-secrets.service" | ||
141 | ]; | ||
142 | before = [ "postfix.service" ]; | ||
143 | wantedBy = [ "multi-user.target" ]; | ||
144 | requires = [ "postsrsd-generate-secrets.service" ]; | ||
145 | confinement.enable = true; | ||
146 | |||
147 | serviceConfig = { | ||
148 | ExecStart = "${lib.getExe pkgs.postsrsd} -C ${configFile}"; | ||
149 | User = cfg.user; | ||
150 | Group = cfg.group; | ||
151 | PermissionsStartOnly = true; | ||
152 | RuntimeDirectory = runtimeDirectoryName; | ||
153 | LoadCredential = "secrets-file:${cfg.secretsFile}"; | ||
154 | }; | ||
155 | }; | ||
156 | }; | ||
157 | } | ||
diff --git a/modules/systemd-run0.nix b/modules/systemd-run0.nix new file mode 100644 index 00000000..8575ec7c --- /dev/null +++ b/modules/systemd-run0.nix | |||
@@ -0,0 +1,4 @@ | |||
1 | { config, lib, ... }: | ||
2 | { | ||
3 | config.security.pam.services.systemd-run0 = lib.mkIf (lib.versionAtLeast config.systemd.package.version "256") {}; | ||
4 | } | ||
diff --git a/modules/tzupdate.nix b/modules/tzupdate.nix new file mode 100644 index 00000000..6465d33f --- /dev/null +++ b/modules/tzupdate.nix | |||
@@ -0,0 +1,81 @@ | |||
1 | { | ||
2 | config, | ||
3 | lib, | ||
4 | pkgs, | ||
5 | ... | ||
6 | }: | ||
7 | let | ||
8 | cfg = config.services.tzupdate; | ||
9 | in | ||
10 | { | ||
11 | disabledModules = [ "services/misc/tzupdate.nix" ]; | ||
12 | |||
13 | options.services.tzupdate = { | ||
14 | enable = lib.mkOption { | ||
15 | type = lib.types.bool; | ||
16 | default = false; | ||
17 | description = '' | ||
18 | Enable the tzupdate timezone updating service. This provides | ||
19 | a one-shot service which can be activated with systemctl to | ||
20 | update the timezone. | ||
21 | ''; | ||
22 | }; | ||
23 | |||
24 | package = lib.mkPackageOption pkgs "tzupdate" { }; | ||
25 | |||
26 | timer.enable = lib.mkOption { | ||
27 | type = lib.types.bool; | ||
28 | default = true; | ||
29 | description = '' | ||
30 | Enable the tzupdate timer to update the timezone automatically. | ||
31 | ''; | ||
32 | }; | ||
33 | |||
34 | timer.interval = lib.mkOption { | ||
35 | type = lib.types.str; | ||
36 | default = "hourly"; | ||
37 | description = '' | ||
38 | The interval at which the tzupdate timer should run. See | ||
39 | {manpage}`systemd.time(7)` to understand the format. | ||
40 | ''; | ||
41 | }; | ||
42 | }; | ||
43 | |||
44 | config = lib.mkIf cfg.enable { | ||
45 | # We need to have imperative time zone management for this to work. | ||
46 | # This will give users an error if they have set an explicit time | ||
47 | # zone, which is better than silently overriding it. | ||
48 | time.timeZone = null; | ||
49 | |||
50 | # We provide a one-shot service that runs at startup once network | ||
51 | # interfaces are up, but we can’t ensure we actually have Internet access | ||
52 | # at that point. It can also be run manually with `systemctl start tzupdate`. | ||
53 | systemd.services.tzupdate = { | ||
54 | description = "tzupdate timezone update service"; | ||
55 | wantedBy = [ "multi-user.target" ]; | ||
56 | wants = [ "network-online.target" ]; | ||
57 | after = [ "network-online.target" ]; | ||
58 | script = '' | ||
59 | timezone="$(${lib.getExe cfg.package} --print-only)" | ||
60 | if [[ -n "$timezone" ]]; then | ||
61 | echo "Setting timezone to '$timezone'" | ||
62 | ${lib.getExe' config.systemd.package "timedatectl"} set-timezone "$timezone" | ||
63 | fi | ||
64 | ''; | ||
65 | |||
66 | serviceConfig = { | ||
67 | Type = "oneshot"; | ||
68 | }; | ||
69 | }; | ||
70 | |||
71 | systemd.timers.tzupdate = { | ||
72 | enable = cfg.timer.enable; | ||
73 | timerConfig = { | ||
74 | OnStartupSec = "30s"; | ||
75 | OnCalendar = cfg.timer.interval; | ||
76 | Persistent = true; | ||
77 | }; | ||
78 | wantedBy = [ "timers.target" ]; | ||
79 | }; | ||
80 | }; | ||
81 | } | ||
diff --git a/modules/uucp.nix b/modules/uucp.nix deleted file mode 100644 index abca2acb..00000000 --- a/modules/uucp.nix +++ /dev/null | |||
@@ -1,398 +0,0 @@ | |||
1 | { flake, config, lib, pkgs, ... }: | ||
2 | |||
3 | with lib; | ||
4 | |||
5 | let | ||
6 | portSpec = name: node: concatStringsSep "\n" (map (port: '' | ||
7 | port ${name}.${port} | ||
8 | type pipe | ||
9 | protocol ${node.protocols} | ||
10 | reliable true | ||
11 | command ${pkgs.openssh}/bin/ssh -x -o batchmode=yes ${name}.${port} | ||
12 | '') node.hostnames); | ||
13 | sysSpec = name: node: '' | ||
14 | system ${name} | ||
15 | time any | ||
16 | chat-seven-bit false | ||
17 | chat . "" | ||
18 | protocol ${node.protocols} | ||
19 | command-path ${concatStringsSep " " cfg.commandPath} | ||
20 | commands ${concatStringsSep " " node.commands} | ||
21 | ${concatStringsSep "\nalternate\n" (map (port: '' | ||
22 | port ${name}.${port} | ||
23 | '') node.hostnames)} | ||
24 | ''; | ||
25 | sshConfig = name: node: concatStringsSep "\n" (map (port: '' | ||
26 | Host ${name}.${port} | ||
27 | Hostname ${port} | ||
28 | IdentitiesOnly Yes | ||
29 | IdentityFile ${cfg.sshKeyDir}/${name} | ||
30 | '') node.hostnames); | ||
31 | sshKeyGen = name: node: '' | ||
32 | if [[ ! -e ${cfg.sshKeyDir}/${name} ]]; then | ||
33 | ${pkgs.openssh}/bin/ssh-keygen ${escapeShellArgs node.generateKey} -f ${cfg.sshKeyDir}/${name} | ||
34 | fi | ||
35 | ''; | ||
36 | restrictKey = key: '' | ||
37 | restrict,command="${chat}" ${key} | ||
38 | ''; | ||
39 | chat = pkgs.writeScript "chat" '' | ||
40 | #!${pkgs.stdenv.shell} | ||
41 | |||
42 | echo . | ||
43 | exec ${config.security.wrapperDir}/uucico | ||
44 | ''; | ||
45 | |||
46 | nodeCfg = { | ||
47 | options = { | ||
48 | commands = mkOption { | ||
49 | type = types.listOf types.str; | ||
50 | default = cfg.defaultCommands; | ||
51 | defaultText = literalExpression "config.services.uucp.defaultCommands"; | ||
52 | description = "Commands to allow for this remote"; | ||
53 | }; | ||
54 | |||
55 | protocols = mkOption { | ||
56 | type = types.separatedString ""; | ||
57 | default = cfg.defaultProtocols; | ||
58 | defaultText = literalExpression "config.services.uucp.defaultProtocols"; | ||
59 | description = "UUCP protocols to use for this remote"; | ||
60 | }; | ||
61 | |||
62 | publicKeys = mkOption { | ||
63 | type = types.listOf types.str; | ||
64 | default = []; | ||
65 | description = "SSH client public keys for this node"; | ||
66 | }; | ||
67 | |||
68 | generateKey = mkOption { | ||
69 | type = types.listOf types.str; | ||
70 | default = [ "-t" "ed25519" "-N" "" ]; | ||
71 | description = "Arguments to pass to `ssh-keygen` to generate a keypair for communication with this host"; | ||
72 | }; | ||
73 | |||
74 | hostnames = mkOption { | ||
75 | type = types.listOf types.str; | ||
76 | default = []; | ||
77 | description = "Hostnames to try in order when connecting"; | ||
78 | }; | ||
79 | }; | ||
80 | }; | ||
81 | |||
82 | cfg = config.services.uucp; | ||
83 | in { | ||
84 | options = { | ||
85 | services.uucp = { | ||
86 | enable = mkOption { | ||
87 | type = types.bool; | ||
88 | default = false; | ||
89 | description = '' | ||
90 | If enabled we set up an account accesible via uucp over ssh | ||
91 | ''; | ||
92 | }; | ||
93 | |||
94 | nodeName = mkOption { | ||
95 | type = types.str; | ||
96 | default = "nixos"; | ||
97 | description = "uucp node name"; | ||
98 | }; | ||
99 | |||
100 | sshUser = mkOption { | ||
101 | type = types.attrs; | ||
102 | default = {}; | ||
103 | description = "Overrides for the local uucp linux-user"; | ||
104 | }; | ||
105 | |||
106 | extraSSHConfig = mkOption { | ||
107 | type = types.str; | ||
108 | default = ""; | ||
109 | description = "Extra SSH config"; | ||
110 | }; | ||
111 | |||
112 | remoteNodes = mkOption { | ||
113 | type = types.attrsOf (types.submodule nodeCfg); | ||
114 | default = {}; | ||
115 | description = '' | ||
116 | Ports to set up | ||
117 | Names will probably need to be configured in sshConfig | ||
118 | ''; | ||
119 | }; | ||
120 | |||
121 | commandPath = mkOption { | ||
122 | type = types.listOf types.path; | ||
123 | default = [ "${pkgs.rmail}/bin" ]; | ||
124 | defaultText = literalExpression ''[ "''${pkgs.rmail}/bin" ]''; | ||
125 | description = '' | ||
126 | Command search path for all systems | ||
127 | ''; | ||
128 | }; | ||
129 | |||
130 | defaultCommands = mkOption { | ||
131 | type = types.listOf types.str; | ||
132 | default = ["rmail"]; | ||
133 | description = "Commands allowed for remotes without explicit override"; | ||
134 | }; | ||
135 | |||
136 | defaultProtocols = mkOption { | ||
137 | type = types.separatedString ""; | ||
138 | default = "te"; | ||
139 | description = "UUCP protocol to use within ssh unless overriden"; | ||
140 | }; | ||
141 | |||
142 | incomingProtocols = mkOption { | ||
143 | type = types.separatedString ""; | ||
144 | default = "te"; | ||
145 | description = "UUCP protocols to use when called"; | ||
146 | }; | ||
147 | |||
148 | homeDir = mkOption { | ||
149 | type = types.path; | ||
150 | default = "/var/uucp"; | ||
151 | description = "Home of the uucp user"; | ||
152 | }; | ||
153 | |||
154 | sshKeyDir = mkOption { | ||
155 | type = types.path; | ||
156 | default = "${cfg.homeDir}/.ssh/"; | ||
157 | defaultText = literalExpression ''''${config.services.uucp.homeDir}/.ssh/''; | ||
158 | description = "Directory to store ssh keypairs"; | ||
159 | }; | ||
160 | |||
161 | spoolDir = mkOption { | ||
162 | type = types.path; | ||
163 | default = "/var/spool/uucp"; | ||
164 | description = "Spool directory"; | ||
165 | }; | ||
166 | |||
167 | lockDir = mkOption { | ||
168 | type = types.path; | ||
169 | default = "/var/spool/uucp"; | ||
170 | description = "Lock directory"; | ||
171 | }; | ||
172 | |||
173 | pubDir = mkOption { | ||
174 | type = types.path; | ||
175 | default = "/var/spool/uucppublic"; | ||
176 | description = "Public directory"; | ||
177 | }; | ||
178 | |||
179 | logFile = mkOption { | ||
180 | type = types.path; | ||
181 | default = "/var/log/uucp"; | ||
182 | description = "Log file"; | ||
183 | }; | ||
184 | |||
185 | statFile = mkOption { | ||
186 | type = types.path; | ||
187 | default = "/var/log/uucp.stat"; | ||
188 | description = "Statistics file"; | ||
189 | }; | ||
190 | |||
191 | debugFile = mkOption { | ||
192 | type = types.path; | ||
193 | default = "/var/log/uucp.debug"; | ||
194 | description = "Debug file"; | ||
195 | }; | ||
196 | |||
197 | interval = mkOption { | ||
198 | type = types.nullOr types.str; | ||
199 | default = "1h"; | ||
200 | description = '' | ||
201 | Specification of when to run `uucico' in format used by systemd timers | ||
202 | The default is to do so every hour | ||
203 | ''; | ||
204 | }; | ||
205 | |||
206 | nmDispatch = mkOption { | ||
207 | type = types.bool; | ||
208 | default = config.networking.networkmanager.enable; | ||
209 | defaultText = literalExpression "config.networking.networkmanager.enable"; | ||
210 | description = '' | ||
211 | Install a network-manager dispatcher script to automatically | ||
212 | call all remotes when networking is available | ||
213 | ''; | ||
214 | }; | ||
215 | |||
216 | extraConfig = mkOption { | ||
217 | type = types.lines; | ||
218 | default = '' | ||
219 | run-uuxqt 1 | ||
220 | ''; | ||
221 | description = "Extra configuration to append verbatim to `/etc/uucp/config'"; | ||
222 | }; | ||
223 | |||
224 | extraSys = mkOption { | ||
225 | type = types.lines; | ||
226 | default = '' | ||
227 | protocol-parameter g packet-size 4096 | ||
228 | ''; | ||
229 | description = "Extra configuration to prepend verbatim to `/etc/uucp/sys`"; | ||
230 | }; | ||
231 | }; | ||
232 | }; | ||
233 | |||
234 | config = mkIf cfg.enable { | ||
235 | environment.etc."uucp/config" = { | ||
236 | text = '' | ||
237 | hostname ${cfg.nodeName} | ||
238 | |||
239 | spool ${cfg.spoolDir} | ||
240 | lockdir ${cfg.lockDir} | ||
241 | pubdir ${cfg.pubDir} | ||
242 | logfile ${cfg.logFile} | ||
243 | statfile ${cfg.statFile} | ||
244 | debugfile ${cfg.debugFile} | ||
245 | |||
246 | ${cfg.extraConfig} | ||
247 | ''; | ||
248 | }; | ||
249 | |||
250 | users.groups."uucp" = {}; | ||
251 | users.users."uucp" = { | ||
252 | name = "uucp"; | ||
253 | group = "uucp"; | ||
254 | isSystemUser = true; | ||
255 | isNormalUser = false; | ||
256 | createHome = true; | ||
257 | home = cfg.homeDir; | ||
258 | description = "User for uucp over ssh"; | ||
259 | useDefaultShell = true; | ||
260 | openssh.authorizedKeys.keys = map restrictKey (concatLists (mapAttrsToList (name: node: node.publicKeys) cfg.remoteNodes)); | ||
261 | } // cfg.sshUser; | ||
262 | |||
263 | system.activationScripts."uucp-sshconfig" = '' | ||
264 | mkdir -p ${config.users.users."uucp".home}/.ssh | ||
265 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${config.users.users."uucp".home}/.ssh | ||
266 | chmod 700 ${config.users.users."uucp".home}/.ssh | ||
267 | ln -fs ${builtins.toFile "ssh-config" '' | ||
268 | ${concatStringsSep "\n" (mapAttrsToList sshConfig cfg.remoteNodes)} | ||
269 | |||
270 | ${cfg.extraSSHConfig} | ||
271 | ''} ${config.users.users."uucp".home}/.ssh/config | ||
272 | |||
273 | mkdir -p ${cfg.sshKeyDir} | ||
274 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.sshKeyDir} | ||
275 | chmod 700 ${cfg.sshKeyDir} | ||
276 | |||
277 | ${concatStringsSep "\n" (mapAttrsToList sshKeyGen cfg.remoteNodes)} | ||
278 | ''; | ||
279 | |||
280 | system.activationScripts."uucp-logs" = '' | ||
281 | touch ${cfg.logFile} | ||
282 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.logFile} | ||
283 | chmod 644 ${cfg.logFile} | ||
284 | touch ${cfg.statFile} | ||
285 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.statFile} | ||
286 | chmod 644 ${cfg.statFile} | ||
287 | touch ${cfg.debugFile} | ||
288 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.debugFile} | ||
289 | chmod 644 ${cfg.debugFile} | ||
290 | ''; | ||
291 | |||
292 | environment.etc."uucp/port" = { | ||
293 | text = '' | ||
294 | port ssh | ||
295 | type stdin | ||
296 | protocol ${cfg.incomingProtocols} | ||
297 | '' + concatStringsSep "\n" (mapAttrsToList portSpec cfg.remoteNodes); | ||
298 | }; | ||
299 | environment.etc."uucp/sys" = { | ||
300 | text = cfg.extraSys + "\n" + concatStringsSep "\n" (mapAttrsToList sysSpec cfg.remoteNodes); | ||
301 | }; | ||
302 | |||
303 | security.wrappers = let | ||
304 | wrapper = p: { | ||
305 | name = p; | ||
306 | value = { | ||
307 | source = "${pkgs.uucp}/bin/${p}"; | ||
308 | owner = "root"; | ||
309 | group = "root"; | ||
310 | setuid = true; | ||
311 | setgid = false; | ||
312 | }; | ||
313 | }; | ||
314 | in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]); | ||
315 | |||
316 | nixpkgs.overlays = [(self: super: { | ||
317 | uucp = super.lib.overrideDerivation super.uucp (oldAttrs: { | ||
318 | configureFlags = "--with-newconfigdir=/etc/uucp"; | ||
319 | patches = [ | ||
320 | (super.writeText "mailprogram" '' | ||
321 | policy.h | 2 +- | ||
322 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
323 | |||
324 | diff --git a/policy.h b/policy.h | ||
325 | index 5afe34b..8e92c8b 100644 | ||
326 | --- a/policy.h | ||
327 | +++ b/policy.h | ||
328 | @@ -240,7 +240,7 @@ | ||
329 | the sendmail choice below. Otherwise, select one of the other | ||
330 | choices as appropriate. */ | ||
331 | #if 1 | ||
332 | -#define MAIL_PROGRAM "/usr/lib/sendmail -t" | ||
333 | +#define MAIL_PROGRAM "${config.security.wrapperDir}/sendmail -t" | ||
334 | /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */ | ||
335 | #define MAIL_PROGRAM_TO_BODY 1 | ||
336 | #define MAIL_PROGRAM_SUBJECT_BODY 1 | ||
337 | '') | ||
338 | ]; | ||
339 | }); | ||
340 | rmail = super.writeScriptBin "rmail" '' | ||
341 | #!${super.stdenv.shell} | ||
342 | |||
343 | # Dummy UUCP rmail command for postfix/qmail systems | ||
344 | |||
345 | IFS=" " read junk from junk junk junk junk junk junk junk relay | ||
346 | |||
347 | case "$from" in | ||
348 | *[@!]*) ;; | ||
349 | *) from="$from@$relay";; | ||
350 | esac | ||
351 | |||
352 | exec ${config.security.wrapperDir}/sendmail -G -i -f "$from" -- "$@" | ||
353 | ''; | ||
354 | })]; | ||
355 | |||
356 | environment.systemPackages = with pkgs; [ | ||
357 | uucp | ||
358 | ]; | ||
359 | |||
360 | systemd.services."uucico@" = { | ||
361 | serviceConfig = { | ||
362 | User = "uucp"; | ||
363 | Type = "oneshot"; | ||
364 | ExecStart = "${config.security.wrapperDir}/uucico -D -S %i"; | ||
365 | }; | ||
366 | }; | ||
367 | |||
368 | systemd.timers."uucico@" = { | ||
369 | timerConfig.OnActiveSec = cfg.interval; | ||
370 | timerConfig.OnUnitActiveSec = cfg.interval; | ||
371 | }; | ||
372 | |||
373 | systemd.targets."multi-user" = { | ||
374 | wants = mapAttrsToList (name: node: "uucico@${name}.timer") cfg.remoteNodes; | ||
375 | }; | ||
376 | |||
377 | systemd.kill-user.enable = true; | ||
378 | systemd.targets."sleep" = { | ||
379 | after = [ "kill-user@uucp.service" ]; | ||
380 | wants = [ "kill-user@uucp.service" ]; | ||
381 | }; | ||
382 | |||
383 | networking.networkmanager.dispatcherScripts = optional cfg.nmDispatch { | ||
384 | type = "basic"; | ||
385 | source = pkgs.writeScript "callRemotes.sh" '' | ||
386 | #!${pkgs.stdenv.shell} | ||
387 | |||
388 | shopt -s extglob | ||
389 | |||
390 | case "''${2}" in | ||
391 | (?(vpn-)up) | ||
392 | ${concatStringsSep "\n " (mapAttrsToList (name: node: "${pkgs.systemd}/bin/systemctl start uucico@${name}.service") cfg.remoteNodes)} | ||
393 | ;; | ||
394 | esac | ||
395 | ''; | ||
396 | }; | ||
397 | }; | ||
398 | } | ||