1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.soft-serve;
7 configFile = format.generate "config.yaml" cfg.settings;
8 format = pkgs.formats.yaml { };
9 docUrl = "https://charm.sh/blog/self-hosted-soft-serve/";
10 stateDir = "/var/lib/soft-serve";
11in
12{
13 options = {
14 services.soft-serve = {
15 enable = mkEnableOption "soft-serve";
16
17 package = mkPackageOption pkgs "soft-serve" { };
18
19 settings = mkOption {
20 type = format.type;
21 default = { };
22 description = mdDoc ''
23 The contents of the configuration file for soft-serve.
24
25 See <${docUrl}>.
26 '';
27 example = literalExpression ''
28 {
29 name = "dadada's repos";
30 log_format = "text";
31 ssh = {
32 listen_addr = ":23231";
33 public_url = "ssh://localhost:23231";
34 max_timeout = 30;
35 idle_timeout = 120;
36 };
37 stats.listen_addr = ":23233";
38 }
39 '';
40 };
41 };
42 };
43
44 config = mkIf cfg.enable {
45
46 systemd.tmpfiles.rules = [
47 # The config file has to be inside the state dir
48 "L+ ${stateDir}/config.yaml - - - - ${configFile}"
49 ];
50
51 systemd.services.soft-serve = {
52 description = "Soft Serve git server";
53 documentation = [ docUrl ];
54 requires = [ "network-online.target" ];
55 after = [ "network-online.target" ];
56 wantedBy = [ "multi-user.target" ];
57
58 environment.SOFT_SERVE_DATA_PATH = stateDir;
59
60 serviceConfig = {
61 Type = "simple";
62 DynamicUser = true;
63 Restart = "always";
64 ExecStart = "${getExe cfg.package} serve";
65 StateDirectory = "soft-serve";
66 WorkingDirectory = stateDir;
67 RuntimeDirectory = "soft-serve";
68 RuntimeDirectoryMode = "0750";
69 ProcSubset = "pid";
70 ProtectProc = "invisible";
71 UMask = "0027";
72 CapabilityBoundingSet = "";
73 ProtectHome = true;
74 PrivateDevices = true;
75 PrivateUsers = true;
76 ProtectHostname = true;
77 ProtectClock = true;
78 ProtectKernelTunables = true;
79 ProtectKernelModules = true;
80 ProtectKernelLogs = true;
81 ProtectControlGroups = true;
82 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
83 RestrictNamespaces = true;
84 LockPersonality = true;
85 MemoryDenyWriteExecute = true;
86 RestrictRealtime = true;
87 RemoveIPC = true;
88 PrivateMounts = true;
89 SystemCallArchitectures = "native";
90 SystemCallFilter = [
91 "@system-service"
92 "~@cpu-emulation @debug @keyring @module @mount @obsolete @privileged @raw-io @reboot @setuid @swap"
93 ];
94 };
95 };
96 };
97
98 meta.maintainers = [ maintainers.dadada ];
99}