This repository has no description
1# modified from https://tangled.sh/@tangled.sh/core/blob/master/flake.nix
2tangledFlake:
3{
4 config,
5 pkgs,
6 lib,
7 ...
8}:
9let
10 inherit (lib)
11 mkOption
12 types
13 mkIf
14 optional
15 ;
16 cfg = config.services.tangled-knotserver;
17 tangledPkgs = tangledFlake.packages.${pkgs.system};
18
19 wrapped-packages =
20 pkgs.runCommandCC "tangled-packages-wrapped" { nativeBuildInputs = [ pkgs.makeBinaryWrapper ]; }
21 ''
22 mkdir -p $out/bin
23
24 makeBinaryWrapper ${lib.getExe tangledPkgs.repoguard} $out/bin/repoguard \
25 --add-flags -internal-api=http://${cfg.server.internalListenAddr}
26 # other flags are set by keyfetch
27
28 makeBinaryWrapper ${lib.getExe tangledPkgs.keyfetch} $out/bin/keyfetch \
29 --add-flags "-repoguard-path=$out/bin/repoguard" \
30 --add-flags "-internal-api=http://${cfg.server.internalListenAddr}" \
31 --add-flags "-git-dir=${cfg.repo.scanPath}" \
32 --add-flags "-log-path=/tmp/repoguard.log"
33 '';
34
35in
36{
37 options = {
38 services.tangled-knotserver = {
39 enable = mkOption {
40 type = types.bool;
41 default = false;
42 description = "Enable a tangled knotserver";
43 };
44
45 appviewEndpoint = mkOption {
46 type = types.str;
47 default = "https://tangled.sh";
48 description = "Appview endpoint";
49 };
50
51 gitUser = mkOption {
52 type = types.str;
53 default = "git";
54 description = "User that hosts git repos and performs git operations";
55 };
56
57 repo = {
58 scanPath = mkOption {
59 type = types.path;
60 default = "/var/lib/tangled-knot";
61 description = "Path where repositories are scanned from";
62 };
63
64 mainBranch = mkOption {
65 type = types.str;
66 default = "main";
67 description = "Default branch name for repositories";
68 };
69 };
70
71 server = {
72 listenAddr = mkOption {
73 type = types.str;
74 default = "0.0.0.0:5555";
75 description = "Address to listen on";
76 };
77
78 internalListenAddr = mkOption {
79 type = types.str;
80 default = "127.0.0.1:5444";
81 description = "Internal address for inter-service communication";
82 };
83
84 dbPath = mkOption {
85 type = types.path;
86 default = "knotserver.db";
87 description = "Path to the database file";
88 };
89
90 hostname = mkOption {
91 type = types.str;
92 example = "knot.tangled.sh";
93 description = "Hostname for the server (required)";
94 };
95
96 dev = mkOption {
97 type = types.bool;
98 default = false;
99 description = "Enable development mode (disables signature verification)";
100 internal = true;
101 };
102 };
103
104 extraConfig = mkOption {
105 type = types.attrsOf types.str;
106 default = { };
107 example = lib.literalExpression ''
108 {
109 # this is only an example, do NOT do this! your secret will end up readable by *everyone*!
110 KNOT_SERVER_SECRET = "verysecuresecret";
111 }
112 '';
113 description = "Additional environment variables. Use `environmentFile` for secrets.";
114 };
115
116 environmentFile = mkOption {
117 type = types.nullOr types.path;
118 default = null;
119 example = "/etc/tangled/knotserver.env";
120 description = ''
121 Environment file to set additional configuration and secrets for the knotserver.
122
123 `KNOT_SERVER_SECRET` must be set for the knotserver to work, and can be obtained from
124 [this page](https://tangled.sh/knots).
125 '';
126 };
127 };
128 };
129
130 config = mkIf config.enable {
131 warnings = optional cfg.server.dev ''
132 tangled-knotserver: development mode is enabled. This is not recommended in production as signature checks are disabled.
133 '';
134
135 environment.systemPackages = with pkgs; [ git ];
136
137 users.users.${cfg.gitUser} = {
138 home = cfg.repo.scanPath;
139 group = cfg.gitUser;
140 isSystemUser = true;
141 useDefaultShell = true;
142 };
143
144 users.groups.${cfg.gitUser} = { };
145
146 services.openssh = {
147 enable = true;
148 extraConfig = ''
149 Match User ${cfg.gitUser}
150 AuthorizedKeysCommand ${lib.getExe' wrapped-packages "keyfetch"}
151 AuthorizedKeysCommandUser nobody
152 '';
153 };
154
155 systemd.services.knotserver = {
156 description = "knotserver service";
157 after = [
158 "network-online.target"
159 "sshd.service"
160 ];
161 wants = [
162 "network-online.target"
163 "sshd.service"
164 ];
165 wantedBy = [ "multi-user.target" ];
166 serviceConfig = {
167 User = cfg.gitUser;
168 WorkingDirectory = cfg.repo.scanPath;
169 ExecStart = lib.getExe tangledPkgs.knotserver;
170 Restart = "always";
171
172 StateDirectory = mkIf (lib.hasPrefix "/var/lib/tangled-knotserver" cfg.repo.scanPath) "tangled-knotserver";
173 EnvironmentFile = cfg.environmentFile;
174 # TODO: hardening
175 };
176
177 environment = {
178 KNOT_REPO_SCANPATH = cfg.repo.scanPath;
179 APPVIEW_ENDPOINT = cfg.appviewEndpoint;
180 KNOT_SERVER_INTERNAL_LISTEN_ADDR = cfg.server.internalListenAddr;
181 KNOT_SERVER_LISTEN_ADDR = cfg.server.listenAddr;
182 KNOT_SERVER_HOSTNAME = cfg.server.hostname;
183 } // cfg.extraConfig;
184 };
185 };
186}