1{ config, pkgs, lib, ... }:
2let
3 cfg = config.services.harmonia;
4 format = pkgs.formats.toml { };
5in
6{
7 options = {
8 services.harmonia = {
9 enable = lib.mkEnableOption (lib.mdDoc "Harmonia: Nix binary cache written in Rust");
10
11 signKeyPath = lib.mkOption {
12 type = lib.types.nullOr lib.types.path;
13 default = null;
14 description = lib.mdDoc "Path to the signing key that will be used for signing the cache";
15 };
16
17 package = lib.mkPackageOptionMD pkgs "harmonia" { };
18
19 settings = lib.mkOption {
20 inherit (format) type;
21 default = { };
22 description = lib.mdDoc ''
23 Settings to merge with the default configuration.
24 For the list of the default configuration, see <https://github.com/nix-community/harmonia/tree/master#configuration>.
25 '';
26 };
27 };
28 };
29
30 config = lib.mkIf cfg.enable {
31 systemd.services.harmonia = {
32 description = "harmonia binary cache service";
33
34 requires = [ "nix-daemon.socket" ];
35 after = [ "network.target" ];
36 wantedBy = [ "multi-user.target" ];
37
38 environment = {
39 CONFIG_FILE = format.generate "harmonia.toml" cfg.settings;
40 SIGN_KEY_PATH = lib.mkIf (cfg.signKeyPath != null) "%d/sign-key";
41 # Note: it's important to set this for nix-store, because it wants to use
42 # $HOME in order to use a temporary cache dir. bizarre failures will occur
43 # otherwise
44 HOME = "/run/harmonia";
45 };
46
47 serviceConfig = {
48 ExecStart = lib.getExe cfg.package;
49 User = "harmonia";
50 Group = "harmonia";
51 DynamicUser = true;
52 PrivateUsers = true;
53 DeviceAllow = [ "" ];
54 UMask = "0066";
55 RuntimeDirectory = "harmonia";
56 LoadCredential = lib.mkIf (cfg.signKeyPath != null) [ "sign-key:${cfg.signKeyPath}" ];
57 SystemCallFilter = [
58 "@system-service"
59 "~@privileged"
60 "~@resources"
61 ];
62 CapabilityBoundingSet = "";
63 ProtectKernelModules = true;
64 ProtectKernelTunables = true;
65 ProtectControlGroups = true;
66 ProtectKernelLogs = true;
67 ProtectHostname = true;
68 ProtectClock = true;
69 RestrictRealtime = true;
70 MemoryDenyWriteExecute = true;
71 ProcSubset = "pid";
72 ProtectProc = "invisible";
73 RestrictNamespaces = true;
74 SystemCallArchitectures = "native";
75 PrivateNetwork = false;
76 PrivateTmp = true;
77 PrivateDevices = true;
78 PrivateMounts = true;
79 NoNewPrivileges = true;
80 ProtectSystem = "strict";
81 ProtectHome = true;
82 LockPersonality = true;
83 RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
84 LimitNOFILE = 65536;
85 };
86 };
87 };
88}