at 24.11-pre 4.0 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.restic.server; 7in 8{ 9 meta.maintainers = [ maintainers.bachp ]; 10 11 options.services.restic.server = { 12 enable = mkEnableOption "Restic REST Server"; 13 14 listenAddress = mkOption { 15 default = "8000"; 16 example = "127.0.0.1:8080"; 17 type = types.str; 18 description = "Listen on a specific IP address and port."; 19 }; 20 21 dataDir = mkOption { 22 default = "/var/lib/restic"; 23 type = types.path; 24 description = "The directory for storing the restic repository."; 25 }; 26 27 appendOnly = mkOption { 28 default = false; 29 type = types.bool; 30 description = '' 31 Enable append only mode. 32 This mode allows creation of new backups but prevents deletion and modification of existing backups. 33 This can be useful when backing up systems that have a potential of being hacked. 34 ''; 35 }; 36 37 privateRepos = mkOption { 38 default = false; 39 type = types.bool; 40 description = '' 41 Enable private repos. 42 Grants access only when a subdirectory with the same name as the user is specified in the repository URL. 43 ''; 44 }; 45 46 prometheus = mkOption { 47 default = false; 48 type = types.bool; 49 description = "Enable Prometheus metrics at /metrics."; 50 }; 51 52 extraFlags = mkOption { 53 type = types.listOf types.str; 54 default = []; 55 description = '' 56 Extra commandline options to pass to Restic REST server. 57 ''; 58 }; 59 60 package = mkPackageOption pkgs "restic-rest-server" { }; 61 }; 62 63 config = mkIf cfg.enable { 64 assertions = [{ 65 assertion = lib.substring 0 1 cfg.listenAddress != ":"; 66 message = "The restic-rest-server now uses systemd socket activation, which expects only the Port number: services.restic.server.listenAddress = \"${lib.substring 1 6 cfg.listenAddress}\";"; 67 }]; 68 69 systemd.services.restic-rest-server = { 70 description = "Restic REST Server"; 71 after = [ "network.target" "restic-rest-server.socket" ]; 72 requires = [ "restic-rest-server.socket" ]; 73 wantedBy = [ "multi-user.target" ]; 74 serviceConfig = { 75 ExecStart = '' 76 ${cfg.package}/bin/rest-server \ 77 --path ${cfg.dataDir} \ 78 ${optionalString cfg.appendOnly "--append-only"} \ 79 ${optionalString cfg.privateRepos "--private-repos"} \ 80 ${optionalString cfg.prometheus "--prometheus"} \ 81 ${escapeShellArgs cfg.extraFlags} \ 82 ''; 83 Type = "simple"; 84 User = "restic"; 85 Group = "restic"; 86 87 # Security hardening 88 CapabilityBoundingSet = ""; 89 LockPersonality = true; 90 MemoryDenyWriteExecute = true; 91 NoNewPrivileges = true; 92 PrivateNetwork = true; 93 PrivateTmp = true; 94 PrivateUsers = true; 95 ProtectClock = true; 96 ProtectHome = true; 97 ProtectHostname = true; 98 ProtectKernelLogs = true; 99 ProtectProc = "invisible"; 100 ProtectSystem = "strict"; 101 ProtectKernelTunables = true; 102 ProtectKernelModules = true; 103 ProtectControlGroups = true; 104 PrivateDevices = true; 105 ReadWritePaths = [ cfg.dataDir ]; 106 RemoveIPC = true; 107 RestrictAddressFamilies = "none"; 108 RestrictNamespaces = true; 109 RestrictRealtime = true; 110 RestrictSUIDSGID = true; 111 SystemCallArchitectures = "native"; 112 SystemCallFilter = "@system-service"; 113 UMask = 027; 114 }; 115 }; 116 117 systemd.sockets.restic-rest-server = { 118 listenStreams = [ cfg.listenAddress ]; 119 wantedBy = [ "sockets.target" ]; 120 }; 121 122 systemd.tmpfiles.rules = mkIf cfg.privateRepos [ 123 "f ${cfg.dataDir}/.htpasswd 0700 restic restic -" 124 ]; 125 126 users.users.restic = { 127 group = "restic"; 128 home = cfg.dataDir; 129 createHome = true; 130 uid = config.ids.uids.restic; 131 }; 132 133 users.groups.restic.gid = config.ids.uids.restic; 134 }; 135}