Kieran's opinionated (and probably slightly dumb) nix config
1{
2 inputs,
3 lib,
4 config,
5 pkgs,
6 ...
7}:
8{
9 imports = [
10 ./disk-config.nix
11 ./home-manager.nix
12
13 (inputs.import-tree ../../modules/nixos)
14 inputs.tangled.nixosModules.knot
15 inputs.tangled.nixosModules.spindle
16 ];
17
18 nixpkgs = {
19 hostPlatform = "aarch64-linux";
20 config = {
21 allowUnfree = true;
22 };
23 };
24
25 nix =
26 let
27 flakeInputs = lib.filterAttrs (_: lib.isType "flake") inputs;
28 in
29 {
30 settings = {
31 experimental-features = "nix-command flakes";
32 flake-registry = "";
33 nix-path = config.nix.nixPath;
34 trusted-users = [
35 "kierank"
36 ];
37 };
38 channel.enable = false;
39 optimise.automatic = true;
40 registry = lib.mapAttrs (_: flake: { inherit flake; }) flakeInputs;
41 nixPath = lib.mapAttrsToList (n: _: "${n}=flake:${n}") flakeInputs;
42 };
43
44 time.timeZone = "America/New_York";
45
46 environment.systemPackages = with pkgs; [
47 # core
48 coreutils
49 screen
50 bc
51 jq
52 psmisc
53 # cli_utils
54 direnv
55 zsh
56 gum
57 vim
58 # networking
59 xh
60 curl
61 wget
62 dogdns
63 inetutils
64 mosh
65 # nix_tools
66 inputs.nixvim.packages.aarch64-linux.default
67 nixd
68 nil
69 nixfmt-rfc-style
70 inputs.agenix.packages.aarch64-linux.default
71 # security
72 openssl
73 gpgme
74 gnupg
75 # dev_langs
76 nodejs_22
77 unstable.bun
78 python3
79 go
80 gopls
81 gotools
82 go-tools
83 gcc
84 # misc
85 neofetch
86 git
87 ];
88
89 programs.nh = {
90 enable = true;
91 clean.enable = true;
92 clean.extraArgs = "--keep-since 4d --keep 3";
93 flake = "/home/kierank/dots";
94 };
95
96 age.identityPaths = [
97 "/home/kierank/.ssh/id_rsa"
98 "/etc/ssh/id_rsa"
99 ];
100 age.secrets = {
101 wakatime = {
102 file = ../../secrets/wakatime.age;
103 path = "/home/kierank/.wakatime.cfg";
104 owner = "kierank";
105 };
106 cachet = {
107 file = ../../secrets/cachet.age;
108 owner = "cachet";
109 };
110 hn-alerts = {
111 file = ../../secrets/hn-alerts.age;
112 owner = "hn-alerts";
113 };
114 emojibot = {
115 file = ../../secrets/emojibot.age;
116 owner = "emojibot";
117 };
118 cloudflare = {
119 file = ../../secrets/cloudflare.age;
120 owner = "caddy";
121 };
122 github-knot-sync = {
123 file = ../../secrets/github-knot-sync.age;
124 owner = "git";
125 };
126 battleship-arena = {
127 file = ../../secrets/battleship-arena.age;
128 owner = "battleship-arena";
129 };
130 };
131
132 environment.sessionVariables = {
133 XDG_CACHE_HOME = "$HOME/.cache";
134 XDG_CONFIG_HOME = "$HOME/.config";
135 XDG_DATA_HOME = "$HOME/.local/share";
136 XDG_STATE_HOME = "$HOME/.local/state";
137 EDITOR = "nvim";
138 SYSTEMD_EDITOR = "nvim";
139 VISUAL = "nvim";
140 };
141
142 atelier = {
143 authentication.enable = true;
144 };
145
146 networking = {
147 hostName = "terebithia";
148 networkmanager.enable = true;
149 };
150
151 programs.zsh.enable = true;
152 programs.direnv.enable = true;
153
154 users.users = {
155 kierank = {
156 initialPassword = "changeme";
157 isNormalUser = true;
158 shell = pkgs.zsh;
159 openssh.authorizedKeys.keys = [
160 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCzEEjvbL/ttqmYoDjxYQmDIq36BabROJoXgQKeh9liBxApwp+2PmgxROzTg42UrRc9pyrkq5kVfxG5hvkqCinhL1fMiowCSEs2L2/Cwi40g5ZU+QwdcwI8a4969kkI46PyB19RHkxg54OUORiIiso/WHGmqQsP+5wbV0+4riSnxwn/JXN4pmnE//stnyAyoiEZkPvBtwJjKb3Ni9n3eNLNs6gnaXrCtaygEZdebikr9kS2g9mM696HvIFgM6cdR/wZ7DcLbG3IdTXuHN7PC3xxL+Y4ek5iMreQIPmuvs4qslbthPGYoYbYLUQiRa9XO5s/ksIj5Z14f7anHE6cuTQVpvNWdGDOigyIVS5qU+4ZF7j+rifzOXVL48gmcAvw/uV68m5Wl/p0qsC/d8vI3GYwEsWG/EzpAlc07l8BU2LxWgN+d7uwBFaJV9VtmUDs5dcslsh8IbzmtC9gq3OLGjklxTfIl6qPiL8U33oc/UwqzvZUrI2BlbagvIZYy6rP+q0= kierank@mockingjay"
161 ];
162 extraGroups = [
163 "wheel"
164 "networkmanager"
165 "services"
166 ];
167 };
168 root.openssh.authorizedKeys.keys = [
169 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCzEEjvbL/ttqmYoDjxYQmDIq36BabROJoXgQKeh9liBxApwp+2PmgxROzTg42UrRc9pyrkq5kVfxG5hvkqCinhL1fMiowCSEs2L2/Cwi40g5ZU+QwdcwI8a4969kkI46PyB19RHkxg54OUORiIiso/WHGmqQsP+5wbV0+4riSnxwn/JXN4pmnE//stnyAyoiEZkPvBtwJjKb3Ni9n3eNLNs6gnaXrCtaygEZdebikr9kS2g9mM696HvIFgM6cdR/wZ7DcLbG3IdTXuHN7PC3xxL+Y4ek5iMreQIPmuvs4qslbthPGYoYbYLUQiRa9XO5s/ksIj5Z14f7anHE6cuTQVpvNWdGDOigyIVS5qU+4ZF7j+rifzOXVL48gmcAvw/uV68m5Wl/p0qsC/d8vI3GYwEsWG/EzpAlc07l8BU2LxWgN+d7uwBFaJV9VtmUDs5dcslsh8IbzmtC9gq3OLGjklxTfIl6qPiL8U33oc/UwqzvZUrI2BlbagvIZYy6rP+q0= kierank@mockingjay"
170 ];
171 };
172
173 # Allow passwordless sudo for wheel group (needed for deploy-rs)
174 security.sudo.wheelNeedsPassword = false;
175
176 services.openssh = {
177 enable = true;
178 openFirewall = true;
179 settings = {
180 PermitRootLogin = "no";
181 PasswordAuthentication = false;
182 };
183 };
184
185 networking.firewall = {
186 enable = true;
187 allowedTCPPorts = [
188 22
189 80
190 443
191 ];
192 logRefusedConnections = false;
193 rejectPackets = true;
194 };
195
196 services.tailscale = {
197 enable = true;
198 useRoutingFeatures = "client";
199 };
200
201 services.caddy = {
202 enable = true;
203 package = pkgs.caddy.withPlugins {
204 plugins = [ "github.com/caddy-dns/cloudflare@v0.2.2" ];
205 hash = "sha256-ea8PC/+SlPRdEVVF/I3c1CBprlVp1nrumKM5cMwJJ3U=";
206 };
207 email = "me@dunkirk.sh";
208 globalConfig = ''
209 acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
210 '';
211 virtualHosts."knot.dunkirk.sh" = {
212 extraConfig = ''
213 tls {
214 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
215 }
216 header {
217 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
218 }
219 reverse_proxy localhost:5555 {
220 header_up X-Forwarded-Proto {scheme}
221 header_up X-Forwarded-For {remote}
222 }
223 '';
224 };
225 virtualHosts."spindle.dunkirk.sh" = {
226 extraConfig = ''
227 tls {
228 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
229 }
230 header {
231 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
232 }
233 reverse_proxy localhost:6555 {
234 header_up X-Forwarded-Proto {scheme}
235 header_up X-Forwarded-For {remote}
236 }
237 '';
238 };
239 virtualHosts."emojibot.dunkirk.sh" = {
240 extraConfig = ''
241 tls {
242 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
243 }
244 header {
245 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
246 }
247 reverse_proxy localhost:3002 {
248 header_up X-Forwarded-Proto {scheme}
249 header_up X-Forwarded-For {remote}
250 }
251 '';
252 };
253 virtualHosts."battleship.dunkirk.sh" = {
254 extraConfig = ''
255 tls {
256 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
257 }
258 header {
259 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
260 }
261 reverse_proxy localhost:8081 {
262 header_up X-Forwarded-Proto {scheme}
263 header_up X-Forwarded-For {remote}
264 }
265 '';
266 };
267 extraConfig = ''
268 # Default response for unhandled domains
269 :80 {
270 respond "404 - Looks like this bridge doesn't have an end" 404
271 }
272 :443 {
273 respond "404 - Looks like this bridge doesn't have an end" 404
274 }
275 '';
276 };
277
278 systemd.services.caddy.serviceConfig = {
279 EnvironmentFile = config.age.secrets.cloudflare.path;
280 };
281
282 atelier.services.cachet = {
283 enable = true;
284 domain = "cachet.dunkirk.sh";
285 secretsFile = config.age.secrets.cachet.path;
286 };
287
288 atelier.services.hn-alerts = {
289 enable = true;
290 domain = "hn.dunkirk.sh";
291 secretsFile = config.age.secrets.hn-alerts.path;
292 };
293
294 atelier.services.emojibot = {
295 enable = true;
296 domain = "emojibot.dunkirk.sh";
297 secretsFile = config.age.secrets.emojibot.path;
298 };
299
300 atelier.services.battleship-arena = {
301 enable = true;
302 domain = "battleship.dunkirk.sh";
303 sshPort = 2222;
304 package = inputs.battleship-arena.packages.aarch64-linux.default;
305 secretsFile = config.age.secrets.battleship-arena.path;
306 };
307
308 services.tangled.knot = {
309 enable = true;
310 package = inputs.tangled.packages.aarch64-linux.knot;
311 appviewEndpoint = "https://tangled.org";
312 server = {
313 owner = "did:plc:krxbvxvis5skq7jj6eot23ul";
314 hostname = "knot.dunkirk.sh";
315 listenAddr = "127.0.0.1:5555";
316 };
317 };
318
319 services.tangled.spindle = {
320 enable = true;
321 package = inputs.tangled.packages.aarch64-linux.spindle;
322 server = {
323 owner = "did:plc:krxbvxvis5skq7jj6eot23ul";
324 hostname = "spindle.dunkirk.sh";
325 listenAddr = "127.0.0.1:6555";
326 };
327 };
328
329 atelier.services.knot-sync = {
330 enable = true;
331 secretsFile = config.age.secrets.github-knot-sync.path;
332 };
333
334 services.n8n = {
335 enable = true;
336 environment = {
337 N8N_HOST = "n8n.dunkirk.sh";
338 N8N_PROTOCOL = "https";
339 WEBHOOK_URL = "https://n8n.dunkirk.sh";
340 };
341 };
342
343 services.caddy.virtualHosts."n8n.dunkirk.sh" = {
344 extraConfig = ''
345 tls {
346 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
347 }
348 header {
349 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
350 }
351 reverse_proxy localhost:5678 {
352 header_up X-Forwarded-Proto {scheme}
353 header_up X-Forwarded-For {remote}
354 }
355 '';
356 };
357
358 boot.loader.systemd-boot.enable = true;
359 boot.loader.efi.canTouchEfiVariables = true;
360 boot.kernelParams = [ "console=ttyS0" ];
361
362 system.stateVersion = "23.05";
363}