This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / configs / services / git / knot.nix
6.5 kB 216 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 11 ownerDid = "did:plc:6nk6tk76up7254wawocoekjr"; # lass.pds.lassul.us 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 [::]:2222 (it binds a single address, and 66 # sshd keeps 22). Port 22 on the public addresses is redirected to it, so 67 # git@knot.lassul.us:<owner>/<repo> works as the appview shows it. Admin 68 # ssh stays on 22 via retiolum/wiregrill and on public 45621 69 # (ssh-redirect.nix). 70 71 clan.core.vars.generators.tangled-knot = { 72 files."knot.env" = { }; 73 runtimeInputs = [ pkgs.openssl ]; 74 script = '' 75 echo "KNOT_MASTER_KEY=$(openssl rand -base64 32)" > "$out/knot.env" 76 ''; 77 }; 78 79 environment.systemPackages = [ package ]; 80 environment.etc."knot/config.toml".source = configFile; 81 82 users.users.${user} = { 83 isSystemUser = true; 84 home = stateDir; 85 group = user; 86 }; 87 users.groups.${user} = { }; 88 89 systemd.tmpfiles.settings."10-knot-rs" = { 90 ${stateDir}.d = { 91 mode = "0750"; 92 inherit user; 93 group = user; 94 }; 95 "${stateDir}/repos".d = { 96 mode = "0750"; 97 inherit user; 98 group = user; 99 }; 100 }; 101 102 systemd.services.knot-rs = { 103 description = "knot git server"; 104 after = [ "network-online.target" ]; 105 wants = [ "network-online.target" ]; 106 wantedBy = [ "multi-user.target" ]; 107 restartTriggers = [ configFile ]; 108 startLimitIntervalSec = 60; 109 startLimitBurst = 5; 110 serviceConfig = { 111 User = user; 112 Group = user; 113 UMask = "0077"; 114 WorkingDirectory = stateDir; 115 EnvironmentFile = config.clan.core.vars.generators.tangled-knot.files."knot.env".path; 116 ExecStart = "${lib.getExe package} /etc/knot/config.toml"; 117 Restart = "on-failure"; 118 RestartSec = 5; 119 TimeoutStopSec = 120; 120 LimitNOFILE = 65536; 121 # without a cgroup limit the knot budgets for MemTotal and refuses 122 # pushes whose base cache (40% of it) exceeds MemAvailable 123 MemoryMax = "4G"; 124 CapabilityBoundingSet = [ ]; 125 ReadWritePaths = [ 126 stateDir 127 "${stateDir}/repos" 128 ]; 129 NoNewPrivileges = true; 130 ProtectProc = "invisible"; 131 ProtectSystem = "strict"; 132 ProtectHome = true; 133 PrivateTmp = true; 134 PrivateDevices = true; 135 PrivateUsers = true; 136 ProtectHostname = true; 137 ProtectClock = true; 138 ProtectKernelTunables = true; 139 ProtectKernelModules = true; 140 ProtectKernelLogs = true; 141 ProtectControlGroups = true; 142 RestrictAddressFamilies = [ 143 "AF_INET" 144 "AF_INET6" 145 "AF_NETLINK" 146 "AF_UNIX" 147 ]; 148 RestrictNamespaces = true; 149 LockPersonality = true; 150 MemoryDenyWriteExecute = true; 151 RestrictRealtime = true; 152 RestrictSUIDSGID = true; 153 RemoveIPC = true; 154 PrivateMounts = true; 155 SystemCallFilter = [ 156 "@system-service" 157 "~@privileged @resources" 158 ]; 159 SystemCallArchitectures = "native"; 160 }; 161 }; 162 163 networking.firewall.allowedTCPPorts = [ sshPort ]; 164 165 # knot 2 doesn't request crawls (no KNOT_MIRRORS), so subscribe our 166 # mirror (knotmirror.nix) to the knot's event stream whenever either 167 # starts; the mirror marks a knot offline after it was unreachable for a 168 # while 169 systemd.services.knotmirror-request-crawl = { 170 wantedBy = [ 171 "knot-rs.service" 172 "knotmirror.service" 173 ]; 174 after = [ 175 "knot-rs.service" 176 "knotmirror.service" 177 ]; 178 serviceConfig.Type = "oneshot"; 179 script = '' 180 ${pkgs.curl}/bin/curl -fsS --retry 10 --retry-connrefused \ 181 -H 'Content-Type: application/json' -d '{"hostname":"${domain}"}' \ 182 http://127.0.0.1:7000/xrpc/sh.tangled.sync.requestCrawl 183 ''; 184 }; 185 186 networking.nftables.tables.knot-ssh = { 187 family = "inet"; 188 content = '' 189 chain prerouting { 190 type nat hook prerouting priority dstnat; policy accept; 191 ip daddr 95.217.192.59 tcp dport 22 redirect to :${toString sshPort} 192 ip6 daddr 2a01:4f9:4a:4f1a::2 tcp dport 22 redirect to :${toString sshPort} 193 } 194 ''; 195 }; 196 197 # repos (with the ACL data), sealed keys and ssh host key; the master key 198 # is in vars 199 lass.backup.state.tangled-knot.folders = [ stateDir ]; 200 201 services.nginx.virtualHosts.${domain} = { 202 enableACME = true; 203 forceSSL = true; 204 locations."/" = { 205 proxyPass = "http://${listenAddr}"; 206 recommendedProxySettings = true; 207 # /events 208 proxyWebsockets = true; 209 extraConfig = '' 210 proxy_read_timeout 1d; 211 client_max_body_size 0; 212 proxy_request_buffering off; 213 ''; 214 }; 215 }; 216}