forked from
lass.pds.lassul.us/superconfig
This repository has no description
6.1 kB
201 lines
1{
2 self,
3 config,
4 lib,
5 pkgs,
6 ...
7}:
8let
9 domain = "knot.lassul.us";
10 # DID of the owner's account on pds.lassul.us; the knot stays off while null
11 ownerDid = null;
12 user = "tangled-knot";
13 stateDir = "/var/lib/tangled-knot";
14 listenAddr = "127.0.0.1:5555";
15 sshPort = 2222;
16 package = self.packages.${pkgs.stdenv.hostPlatform.system}.tangled.knot-rs;
17
18 # `knot-server config-template` lists the keys; upstream's defaults, except
19 # where commented
20 rendered = (pkgs.formats.toml { }).generate "knot.toml" {
21 server = {
22 hostname = domain;
23 admins = [ ownerDid ];
24 appview_endpoint = "https://git.lassul.us";
25 listen_addr = listenAddr;
26 internal_listen_addr = "[::1]:5444";
27 ssh_listen_addr = "[::]:${toString sshPort}";
28 ssh_host_key_file = "${stateDir}/ssh_host_key";
29 };
30 atproto.plc_directory = "https://plc.directory";
31 acl.admission = "closed";
32 repo.scan_path = "${stateDir}/repos";
33 # sha256 repos can't exchange objects with sha1 ones (github, cgit)
34 git.object_format = "sha1";
35 secrets = {
36 sealed_key_file = "${stateDir}/sealed-keys";
37 master_key_env = "KNOT_MASTER_KEY";
38 };
39 tls = {
40 http3 = true;
41 acme_enabled = false;
42 acme_cache_dir = "${stateDir}/acme";
43 acme_staging = false;
44 mtls_enabled = false;
45 };
46 xrpc = {
47 trusted_proxy_header = "x-forwarded-for";
48 trusted_proxies = [
49 "127.0.0.1"
50 "::1"
51 ];
52 };
53 };
54 configFile = pkgs.runCommandLocal "knot-config.toml" { nativeBuildInputs = [ package ]; } ''
55 knot-server validate --config-only ${rendered}
56 ln -s ${rendered} $out
57 '';
58in
59{
60 # Tangled knot 2 (knot-rs): git hosting for git.lassul.us, following
61 # tangled's nix/modules/knot-rs.nix without its flake (the module files are
62 # in a fetched source, importing them would be IFD): plain HTTP behind
63 # nginx, unprivileged ports, keys in stateDir. The Go knot's unit and user
64 # are both named knot, which the DNS server already uses.
65 # Knot 2 serves ssh itself, on port 2222 since sshd keeps 22, so clone URLs
66 # need ssh://git@knot.lassul.us:2222/... or a Port line in ~/.ssh/config.
67
68 config = lib.mkIf (ownerDid != null) {
69 clan.core.vars.generators.tangled-knot = {
70 files."knot.env" = { };
71 runtimeInputs = [ pkgs.openssl ];
72 script = ''
73 echo "KNOT_MASTER_KEY=$(openssl rand -base64 32)" > "$out/knot.env"
74 '';
75 };
76
77 environment.systemPackages = [ package ];
78 environment.etc."knot/config.toml".source = configFile;
79
80 users.users.${user} = {
81 isSystemUser = true;
82 home = stateDir;
83 group = user;
84 };
85 users.groups.${user} = { };
86
87 systemd.tmpfiles.settings."10-knot-rs" = {
88 ${stateDir}.d = {
89 mode = "0750";
90 inherit user;
91 group = user;
92 };
93 "${stateDir}/repos".d = {
94 mode = "0750";
95 inherit user;
96 group = user;
97 };
98 };
99
100 systemd.services.knot-rs = {
101 description = "knot git server";
102 after = [ "network-online.target" ];
103 wants = [ "network-online.target" ];
104 wantedBy = [ "multi-user.target" ];
105 restartTriggers = [ configFile ];
106 startLimitIntervalSec = 60;
107 startLimitBurst = 5;
108 serviceConfig = {
109 User = user;
110 Group = user;
111 UMask = "0077";
112 WorkingDirectory = stateDir;
113 EnvironmentFile = config.clan.core.vars.generators.tangled-knot.files."knot.env".path;
114 ExecStart = "${lib.getExe package} /etc/knot/config.toml";
115 Restart = "on-failure";
116 RestartSec = 5;
117 TimeoutStopSec = 120;
118 LimitNOFILE = 65536;
119 CapabilityBoundingSet = [ ];
120 ReadWritePaths = [
121 stateDir
122 "${stateDir}/repos"
123 ];
124 NoNewPrivileges = true;
125 ProtectProc = "invisible";
126 ProtectSystem = "strict";
127 ProtectHome = true;
128 PrivateTmp = true;
129 PrivateDevices = true;
130 PrivateUsers = true;
131 ProtectHostname = true;
132 ProtectClock = true;
133 ProtectKernelTunables = true;
134 ProtectKernelModules = true;
135 ProtectKernelLogs = true;
136 ProtectControlGroups = true;
137 RestrictAddressFamilies = [
138 "AF_INET"
139 "AF_INET6"
140 "AF_NETLINK"
141 "AF_UNIX"
142 ];
143 RestrictNamespaces = true;
144 LockPersonality = true;
145 MemoryDenyWriteExecute = true;
146 RestrictRealtime = true;
147 RestrictSUIDSGID = true;
148 RemoveIPC = true;
149 PrivateMounts = true;
150 SystemCallFilter = [
151 "@system-service"
152 "~@privileged @resources"
153 ];
154 SystemCallArchitectures = "native";
155 };
156 };
157
158 networking.firewall.allowedTCPPorts = [ sshPort ];
159
160 # knot 2 doesn't request crawls (no KNOT_MIRRORS), so subscribe our
161 # mirror (knotmirror.nix) to the knot's event stream whenever either
162 # starts; the mirror marks a knot offline after it was unreachable for a
163 # while
164 systemd.services.knotmirror-request-crawl = {
165 wantedBy = [
166 "knot-rs.service"
167 "knotmirror.service"
168 ];
169 after = [
170 "knot-rs.service"
171 "knotmirror.service"
172 ];
173 serviceConfig.Type = "oneshot";
174 script = ''
175 ${pkgs.curl}/bin/curl -fsS --retry 10 --retry-connrefused \
176 -H 'Content-Type: application/json' -d '{"hostname":"${domain}"}' \
177 http://127.0.0.1:7000/xrpc/sh.tangled.sync.requestCrawl
178 '';
179 };
180
181 # repos (with the ACL data), sealed keys and ssh host key; the master key
182 # is in vars
183 lass.backup.state.tangled-knot.folders = [ stateDir ];
184
185 services.nginx.virtualHosts.${domain} = {
186 enableACME = true;
187 forceSSL = true;
188 locations."/" = {
189 proxyPass = "http://${listenAddr}";
190 recommendedProxySettings = true;
191 # /events
192 proxyWebsockets = true;
193 extraConfig = ''
194 proxy_read_timeout 1d;
195 client_max_body_size 0;
196 proxy_request_buffering off;
197 '';
198 };
199 };
200 };
201}