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 extraSshdConfig = mkOption {
117 type = types.lines;
118 default = "";
119 example = ''
120 Banner none
121 PasswordAuthentication no
122 KbdInteractiveAuthentication no
123 '';
124 description = "Additional sshd_config options to set for the git user.";
125 };
126
127 environmentFile = mkOption {
128 type = types.nullOr types.path;
129 default = null;
130 example = "/etc/tangled/knotserver.env";
131 description = ''
132 Environment file to set additional configuration and secrets for the knotserver.
133
134 `KNOT_SERVER_SECRET` must be set for the knotserver to work, and can be obtained from
135 [this page](https://tangled.sh/knots).
136 '';
137 };
138 };
139 };
140
141 config = mkIf config.enable {
142 warnings = optional cfg.server.dev ''
143 tangled-knotserver: development mode is enabled. This is not recommended in production as signature checks are disabled.
144 '';
145
146 environment.systemPackages = with pkgs; [ git ];
147
148 users.users.${cfg.gitUser} = {
149 home = cfg.repo.scanPath;
150 group = cfg.gitUser;
151 isSystemUser = true;
152 useDefaultShell = true;
153 };
154
155 users.groups.${cfg.gitUser} = { };
156
157 services.openssh = {
158 enable = true;
159 extraConfig = ''
160 Match User ${cfg.gitUser}
161 AuthorizedKeysCommand ${lib.getExe' wrapped-packages "keyfetch"}
162 AuthorizedKeysCommandUser nobody
163 ${cfg.extraSshdConfig}
164 '';
165 };
166
167 systemd.services.knotserver = {
168 description = "knotserver service";
169 after = [
170 "network-online.target"
171 "sshd.service"
172 ];
173 wants = [
174 "network-online.target"
175 "sshd.service"
176 ];
177 wantedBy = [ "multi-user.target" ];
178 serviceConfig = {
179 User = cfg.gitUser;
180 WorkingDirectory = cfg.repo.scanPath;
181 ExecStart = lib.getExe tangledPkgs.knotserver;
182 Restart = "always";
183
184 StateDirectory = mkIf (lib.hasPrefix "/var/lib/tangled-knotserver" cfg.repo.scanPath) "tangled-knotserver";
185 EnvironmentFile = cfg.environmentFile;
186 # TODO: hardening
187 };
188
189 environment = {
190 KNOT_REPO_SCANPATH = cfg.repo.scanPath;
191 APPVIEW_ENDPOINT = cfg.appviewEndpoint;
192 KNOT_SERVER_INTERNAL_LISTEN_ADDR = cfg.server.internalListenAddr;
193 KNOT_SERVER_LISTEN_ADDR = cfg.server.listenAddr;
194 KNOT_SERVER_HOSTNAME = cfg.server.hostname;
195 } // cfg.extraConfig;
196 };
197 };
198}