python312Packages.aiohomeconnect: 0.10.0 -> 0.11.0 (#374011)
[NixPkgs.git] / nixos / modules / services / networking / atticd.nix
blob3984c434c60e1d72cc4b423059b489aca2054f96
2   lib,
3   pkgs,
4   config,
5   ...
6 }:
8 let
9   inherit (lib) types;
11   cfg = config.services.atticd;
13   format = pkgs.formats.toml { };
15   checkedConfigFile =
16     pkgs.runCommand "checked-attic-server.toml"
17       {
18         configFile = format.generate "server.toml" cfg.settings;
19       }
20       ''
21         export ATTIC_SERVER_TOKEN_RS256_SECRET_BASE64="$(${lib.getExe pkgs.openssl} genrsa -traditional 4096 | ${pkgs.coreutils}/bin/base64 -w0)"
22         export ATTIC_SERVER_DATABASE_URL="sqlite://:memory:"
23         ${lib.getExe cfg.package} --mode check-config -f $configFile
24         cat <$configFile >$out
25       '';
27   atticadmShim = pkgs.writeShellScript "atticadm" ''
28     if [ -n "$ATTICADM_PWD" ]; then
29       cd "$ATTICADM_PWD"
30       if [ "$?" != "0" ]; then
31         >&2 echo "Warning: Failed to change directory to $ATTICADM_PWD"
32       fi
33     fi
35     exec ${cfg.package}/bin/atticadm -f ${checkedConfigFile} "$@"
36   '';
38   atticadmWrapper = pkgs.writeShellScriptBin "atticd-atticadm" ''
39     exec systemd-run \
40       --quiet \
41       --pipe \
42       --pty \
43       --same-dir \
44       --wait \
45       --collect \
46       --service-type=exec \
47       --property=EnvironmentFile=${cfg.environmentFile} \
48       --property=DynamicUser=yes \
49       --property=User=${cfg.user} \
50       --property=Environment=ATTICADM_PWD=$(pwd) \
51       --working-directory / \
52       -- \
53       ${atticadmShim} "$@"
54   '';
56   hasLocalPostgresDB =
57     let
58       url = cfg.settings.database.url or "";
59       localStrings = [
60         "localhost"
61         "127.0.0.1"
62         "/run/postgresql"
63       ];
64       hasLocalStrings = lib.any (lib.flip lib.hasInfix url) localStrings;
65     in
66     config.services.postgresql.enable && lib.hasPrefix "postgresql://" url && hasLocalStrings;
69   options = {
70     services.atticd = {
71       enable = lib.mkEnableOption "the atticd, the Nix Binary Cache server";
73       package = lib.mkPackageOption pkgs "attic-server" { };
75       environmentFile = lib.mkOption {
76         description = ''
77           Path to an EnvironmentFile containing required environment
78           variables:
80           - ATTIC_SERVER_TOKEN_RS256_SECRET_BASE64: The base64-encoded RSA PEM PKCS1 of the
81             RS256 JWT secret. Generate it with `openssl genrsa -traditional 4096 | base64 -w0`.
82         '';
83         type = types.nullOr types.path;
84         default = null;
85       };
87       user = lib.mkOption {
88         description = ''
89           The group under which attic runs.
90         '';
91         type = types.str;
92         default = "atticd";
93       };
95       group = lib.mkOption {
96         description = ''
97           The user under which attic runs.
98         '';
99         type = types.str;
100         default = "atticd";
101       };
103       settings = lib.mkOption {
104         description = ''
105           Structured configurations of atticd.
106           See https://github.com/zhaofengli/attic/blob/main/server/src/config-template.toml
107         '';
108         type = format.type;
109         default = { };
110       };
112       mode = lib.mkOption {
113         description = ''
114           Mode in which to run the server.
116           'monolithic' runs all components, and is suitable for single-node deployments.
118           'api-server' runs only the API server, and is suitable for clustering.
120           'garbage-collector' only runs the garbage collector periodically.
122           A simple NixOS-based Attic deployment will typically have one 'monolithic' and any number of 'api-server' nodes.
124           There are several other supported modes that perform one-off operations, but these are the only ones that make sense to run via the NixOS module.
125         '';
126         type = lib.types.enum [
127           "monolithic"
128           "api-server"
129           "garbage-collector"
130         ];
131         default = "monolithic";
132       };
133     };
134   };
136   config = lib.mkIf cfg.enable {
137     assertions = [
138       {
139         assertion = cfg.environmentFile != null;
140         message = ''
141           <option>services.atticd.environmentFile</option> is not set.
143           Run `openssl genrsa -traditional 4096 | base64 -w0` and create a file with the following contents:
145           ATTIC_SERVER_TOKEN_RS256_SECRET="output from command"
147           Then, set `services.atticd.environmentFile` to the quoted absolute path of the file.
148         '';
149       }
150     ];
152     services.atticd.settings = {
153       chunking = lib.mkDefault {
154         nar-size-threshold = 65536;
155         min-size = 16384; # 16 KiB
156         avg-size = 65536; # 64 KiB
157         max-size = 262144; # 256 KiB
158       };
160       database.url = lib.mkDefault "sqlite:///var/lib/atticd/server.db?mode=rwc";
162       # "storage" is internally tagged
163       # if the user sets something the whole thing must be replaced
164       storage = lib.mkDefault {
165         type = "local";
166         path = "/var/lib/atticd/storage";
167       };
168     };
170     systemd.services.atticd = {
171       wantedBy = [ "multi-user.target" ];
172       after = [ "network-online.target" ] ++ lib.optionals hasLocalPostgresDB [ "postgresql.service" ];
173       requires = lib.optionals hasLocalPostgresDB [ "postgresql.service" ];
174       wants = [ "network-online.target" ];
176       serviceConfig = {
177         ExecStart = "${lib.getExe cfg.package} -f ${checkedConfigFile} --mode ${cfg.mode}";
178         EnvironmentFile = cfg.environmentFile;
179         StateDirectory = "atticd"; # for usage with local storage and sqlite
180         DynamicUser = true;
181         User = cfg.user;
182         Group = cfg.group;
183         Restart = "on-failure";
184         RestartSec = 10;
186         CapabilityBoundingSet = [ "" ];
187         DeviceAllow = "";
188         DevicePolicy = "closed";
189         LockPersonality = true;
190         MemoryDenyWriteExecute = true;
191         NoNewPrivileges = true;
192         PrivateDevices = true;
193         PrivateTmp = true;
194         PrivateUsers = true;
195         ProcSubset = "pid";
196         ProtectClock = true;
197         ProtectControlGroups = true;
198         ProtectHome = true;
199         ProtectHostname = true;
200         ProtectKernelLogs = true;
201         ProtectKernelModules = true;
202         ProtectKernelTunables = true;
203         ProtectProc = "invisible";
204         ProtectSystem = "strict";
205         ReadWritePaths =
206           let
207             path = cfg.settings.storage.path;
208             isDefaultStateDirectory = path == "/var/lib/atticd" || lib.hasPrefix "/var/lib/atticd/" path;
209           in
210           lib.optionals (cfg.settings.storage.type or "" == "local" && !isDefaultStateDirectory) [ path ];
211         RemoveIPC = true;
212         RestrictAddressFamilies = [
213           "AF_INET"
214           "AF_INET6"
215           "AF_UNIX"
216         ];
217         RestrictNamespaces = true;
218         RestrictRealtime = true;
219         RestrictSUIDSGID = true;
220         SystemCallArchitectures = "native";
221         SystemCallFilter = [
222           "@system-service"
223           "~@resources"
224           "~@privileged"
225         ];
226         UMask = "0077";
227       };
228     };
230     environment.systemPackages = [
231       atticadmWrapper
232     ];
233   };