+14
-85
flake.lock
+14
-85
flake.lock
···
58
58
"type": "gitlab"
59
59
}
60
60
},
61
-
"colour-guesser": {
62
-
"inputs": {
63
-
"flake-utils": "flake-utils_2",
64
-
"nix-filter": "nix-filter",
65
-
"nixpkgs": [
66
-
"nixpkgs"
67
-
]
68
-
},
69
-
"locked": {
70
-
"lastModified": 1713180220,
71
-
"narHash": "sha256-hTdD8t3/hvaexQXDFL2lsXgyxg93IrTFszXeCrBcmEY=",
72
-
"ref": "develop",
73
-
"rev": "6ecf853ca91af2e6ddcecd64e6eaa1214aaf87fa",
74
-
"revCount": 11,
75
-
"type": "git",
76
-
"url": "ssh://git@github.com/ryangibb/colour-guesser.git"
77
-
},
78
-
"original": {
79
-
"ref": "develop",
80
-
"type": "git",
81
-
"url": "ssh://git@github.com/ryangibb/colour-guesser.git"
82
-
}
83
-
},
84
61
"darwin": {
85
62
"inputs": {
86
63
"nixpkgs": [
···
152
129
},
153
130
"eon": {
154
131
"inputs": {
155
-
"flake-utils": "flake-utils_3",
132
+
"flake-utils": "flake-utils_2",
156
133
"nixpkgs": [
157
134
"nixpkgs"
158
135
],
···
287
264
},
288
265
"flake-utils_2": {
289
266
"inputs": {
290
-
"systems": "systems_2"
291
-
},
292
-
"locked": {
293
-
"lastModified": 1681202837,
294
-
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
295
-
"owner": "numtide",
296
-
"repo": "flake-utils",
297
-
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
298
-
"type": "github"
299
-
},
300
-
"original": {
301
-
"id": "flake-utils",
302
-
"type": "indirect"
303
-
}
304
-
},
305
-
"flake-utils_3": {
306
-
"inputs": {
307
-
"systems": "systems_5"
267
+
"systems": "systems_4"
308
268
},
309
269
"locked": {
310
270
"lastModified": 1731533236,
···
320
280
"type": "github"
321
281
}
322
282
},
323
-
"flake-utils_4": {
283
+
"flake-utils_3": {
324
284
"locked": {
325
285
"lastModified": 1676283394,
326
286
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
···
334
294
"type": "indirect"
335
295
}
336
296
},
337
-
"flake-utils_5": {
297
+
"flake-utils_4": {
338
298
"inputs": {
339
-
"systems": "systems_6"
299
+
"systems": "systems_5"
340
300
},
341
301
"locked": {
342
302
"lastModified": 1694529238,
···
352
312
"type": "github"
353
313
}
354
314
},
355
-
"flake-utils_6": {
315
+
"flake-utils_5": {
356
316
"locked": {
357
317
"lastModified": 1638122382,
358
318
"narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=",
···
367
327
"type": "github"
368
328
}
369
329
},
370
-
"flake-utils_7": {
330
+
"flake-utils_6": {
371
331
"inputs": {
372
-
"systems": "systems_7"
332
+
"systems": "systems_6"
373
333
},
374
334
"locked": {
375
335
"lastModified": 1692799911,
···
387
347
},
388
348
"fn06-website": {
389
349
"inputs": {
390
-
"flake-utils": "flake-utils_4",
350
+
"flake-utils": "flake-utils_3",
391
351
"nixpkgs": [
392
352
"nixpkgs"
393
353
]
···
505
465
},
506
466
"hyperbib-eeg": {
507
467
"inputs": {
508
-
"flake-utils": "flake-utils_5",
468
+
"flake-utils": "flake-utils_4",
509
469
"nixpkgs": [
510
470
"nixpkgs"
511
471
],
···
529
489
},
530
490
"i3-workspace-history": {
531
491
"inputs": {
532
-
"flake-utils": "flake-utils_7",
492
+
"flake-utils": "flake-utils_6",
533
493
"gomod2nix": "gomod2nix",
534
494
"nixpkgs": [
535
495
"nixpkgs"
···
625
585
"type": "github"
626
586
}
627
587
},
628
-
"nix-filter": {
629
-
"locked": {
630
-
"lastModified": 1681154353,
631
-
"narHash": "sha256-MCJ5FHOlbfQRFwN0brqPbCunLEVw05D/3sRVoNVt2tI=",
632
-
"owner": "numtide",
633
-
"repo": "nix-filter",
634
-
"rev": "f529f42792ade8e32c4be274af6b6d60857fbee7",
635
-
"type": "github"
636
-
},
637
-
"original": {
638
-
"owner": "numtide",
639
-
"repo": "nix-filter",
640
-
"type": "github"
641
-
}
642
-
},
643
588
"nix-formatter-pack": {
644
589
"inputs": {
645
590
"nixpkgs": [
···
1023
968
"opam-nix_2": {
1024
969
"inputs": {
1025
970
"flake-compat": "flake-compat_4",
1026
-
"flake-utils": "flake-utils_6",
971
+
"flake-utils": "flake-utils_5",
1027
972
"mirage-opam-overlays": "mirage-opam-overlays_2",
1028
973
"nixpkgs": [
1029
974
"hyperbib-eeg",
···
1162
1107
"inputs": {
1163
1108
"agenix": "agenix",
1164
1109
"alec-website": "alec-website",
1165
-
"colour-guesser": "colour-guesser",
1166
1110
"deploy-rs": "deploy-rs",
1167
1111
"eilean": "eilean",
1168
1112
"eon": "eon",
···
1311
1255
"type": "github"
1312
1256
}
1313
1257
},
1314
-
"systems_7": {
1315
-
"locked": {
1316
-
"lastModified": 1681028828,
1317
-
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
1318
-
"owner": "nix-systems",
1319
-
"repo": "default",
1320
-
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
1321
-
"type": "github"
1322
-
},
1323
-
"original": {
1324
-
"owner": "nix-systems",
1325
-
"repo": "default",
1326
-
"type": "github"
1327
-
}
1328
-
},
1329
1258
"tangled": {
1330
1259
"inputs": {
1331
1260
"gitignore": "gitignore",
···
1395
1324
},
1396
1325
"utils": {
1397
1326
"inputs": {
1398
-
"systems": "systems_3"
1327
+
"systems": "systems_2"
1399
1328
},
1400
1329
"locked": {
1401
1330
"lastModified": 1701680307,
···
1413
1342
},
1414
1343
"utils_2": {
1415
1344
"inputs": {
1416
-
"systems": "systems_4"
1345
+
"systems": "systems_3"
1417
1346
},
1418
1347
"locked": {
1419
1348
"lastModified": 1709126324,
-2
flake.nix
-2
flake.nix
···
15
15
eilean.url = "github:RyanGibb/eilean-nix/main";
16
16
alec-website.url = "github:alexanderhthompson/website";
17
17
fn06-website.url = "github:RyanGibb/fn06";
18
-
colour-guesser.url = "git+ssh://git@github.com/ryangibb/colour-guesser.git?ref=develop";
19
18
i3-workspace-history.url = "github:RyanGibb/i3-workspace-history";
20
19
hyperbib-eeg.url = "github:RyanGibb/hyperbib?ref=nixify";
21
20
nix-rpi5.url = "gitlab:vriska/nix-rpi5?ref=main";
···
33
32
alec-website.inputs.nixpkgs.follows = "nixpkgs";
34
33
fn06-website.inputs.nixpkgs.follows = "nixpkgs";
35
34
eon.inputs.nixpkgs.follows = "nixpkgs";
36
-
colour-guesser.inputs.nixpkgs.follows = "nixpkgs";
37
35
i3-workspace-history.inputs.nixpkgs.follows = "nixpkgs";
38
36
hyperbib-eeg.inputs.nixpkgs.follows = "nixpkgs";
39
37
nix-rpi5.inputs.nixpkgs.follows = "nixpkgs";
+1
-513
hosts/owl/default.nix
+1
-513
hosts/owl/default.nix
···
1
1
{
2
2
pkgs,
3
-
config,
4
3
lib,
5
-
eon,
6
4
...
7
5
}@inputs:
8
6
9
-
let
10
-
vpnRecords = [
11
-
{
12
-
name = "nix-cache.vpn.${config.networking.domain}.";
13
-
type = "A";
14
-
value = "100.64.0.9";
15
-
}
16
-
{
17
-
name = "jellyfin.vpn.${config.networking.domain}.";
18
-
type = "A";
19
-
value = "100.64.0.9";
20
-
}
21
-
{
22
-
name = "nextcloud.vpn.${config.networking.domain}.";
23
-
type = "A";
24
-
value = "100.64.0.9";
25
-
}
26
-
{
27
-
name = "transmission.vpn.${config.networking.domain}.";
28
-
type = "A";
29
-
value = "100.64.0.9";
30
-
}
31
-
{
32
-
name = "owntracks.vpn.${config.networking.domain}.";
33
-
type = "A";
34
-
value = "100.64.0.9";
35
-
}
36
-
{
37
-
name = "immich.vpn.${config.networking.domain}.";
38
-
type = "A";
39
-
value = "100.64.0.9";
40
-
}
41
-
{
42
-
name = "audiobookshelf.vpn.${config.networking.domain}.";
43
-
type = "A";
44
-
value = "100.64.0.9";
45
-
}
46
-
];
47
-
in
48
7
{
49
8
imports = [
50
9
./hardware-configuration.nix
51
10
./minimal.nix
52
-
../../modules/colour-guesser.nix
11
+
./services.nix
53
12
../../modules/ryan-website.nix
54
13
../../modules/alec-website.nix
55
14
../../modules/fn06-website.nix
···
57
16
];
58
17
59
18
environment.systemPackages = with pkgs; [
60
-
# nix
61
19
nixd
62
20
nixfmt-rfc-style
63
21
];
64
22
65
-
age.secrets.eon-capnp = {
66
-
file = ../../secrets/eon-capnp.age;
67
-
mode = "770";
68
-
owner = "eon";
69
-
group = "eon";
70
-
};
71
-
age.secrets.eon-sirref-primary = {
72
-
file = ../../secrets/eon-sirref-primary.cap.age;
73
-
mode = "770";
74
-
owner = "eon";
75
-
group = "eon";
76
-
};
77
-
services.eon = {
78
-
capnpSecretKeyFile = config.age.secrets.eon-capnp.path;
79
-
primaries = [ config.age.secrets.eon-sirref-primary.path ];
80
-
prod = true;
81
-
capnpAddress = "135.181.100.27";
82
-
logLevel = 0;
83
-
};
84
-
85
-
security.acme-eon = {
86
-
acceptTerms = true;
87
-
package = eon.defaultPackage.${config.nixpkgs.hostPlatform.system};
88
-
defaults.email = "${config.custom.username}@${config.networking.domain}";
89
-
defaults.capFile = "/var/lib/eon/caps/domain/freumh.org.cap";
90
-
certs = {
91
-
"fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap";
92
-
"capybara.fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap";
93
-
};
94
-
};
95
-
96
-
eilean = {
97
-
username = config.custom.username;
98
-
serverIpv4 = "135.181.100.27";
99
-
serverIpv6 = "2a01:4f9:c011:87ad:0:0:0:0";
100
-
acme-eon = true;
101
-
fail2ban.enable = true;
102
-
};
103
-
networking.domain = lib.mkDefault "freumh.org";
104
-
eilean.publicInterface = "enp1s0";
105
-
eilean.mailserver.enable = true;
106
-
eilean.radicale = {
107
-
enable = true;
108
-
users = null;
109
-
};
110
-
age.secrets.matrix-shared-secret = {
111
-
file = ../../secrets/matrix-shared-secret.age;
112
-
mode = "770";
113
-
owner = "${config.systemd.services.matrix-synapse.serviceConfig.User}";
114
-
group = "${config.systemd.services.matrix-synapse.serviceConfig.Group}";
115
-
};
116
-
eilean.matrix = {
117
-
enable = true;
118
-
registrationSecretFile = config.age.secrets.matrix-shared-secret.path;
119
-
bridges.whatsapp = true;
120
-
bridges.signal = true;
121
-
bridges.instagram = true;
122
-
bridges.messenger = true;
123
-
};
124
-
eilean.turn.enable = true;
125
-
eilean.mastodon.enable = true;
126
-
eilean.headscale.enable = true;
127
-
#eilean.dns.enable = lib.mkForce false;
128
-
129
-
systemd.services.matrix-as-meta = {
130
-
# voice messages need `ffmpeg`
131
-
path = [ pkgs.ffmpeg ];
132
-
};
133
-
134
-
custom = {
135
-
freumh.enable = true;
136
-
rmfakecloud.enable = true;
137
-
website = {
138
-
ryan = {
139
-
enable = true;
140
-
cname = "vps";
141
-
};
142
-
alec = {
143
-
enable = true;
144
-
cname = "vps";
145
-
};
146
-
fn06 = {
147
-
enable = true;
148
-
cname = "vps";
149
-
domain = "fn06.org";
150
-
};
151
-
colour-guesser = {
152
-
# enable = true;
153
-
cname = "vps";
154
-
};
155
-
};
156
-
};
157
-
158
-
eilean.dns.nameservers = [ "ns1" ];
159
-
eilean.services.dns.zones = {
160
-
${config.networking.domain} = {
161
-
ttl = 300;
162
-
soa = {
163
-
serial = 2018011660;
164
-
refresh = 300;
165
-
};
166
-
records = [
167
-
{
168
-
name = "@";
169
-
type = "TXT";
170
-
value = "google-site-verification=rEvwSqf7RYKRQltY412qMtTuoxPp64O3L7jMotj9Jnc";
171
-
}
172
-
{
173
-
name = "_atproto.ryan";
174
-
type = "TXT";
175
-
value = "did=did:plc:3lfhu6ehlynzjgehef6alnvg";
176
-
}
177
-
178
-
{
179
-
name = "teapot";
180
-
type = "CNAME";
181
-
value = "vps";
182
-
}
183
-
184
-
{
185
-
name = "@";
186
-
type = "NS";
187
-
value = "ns1.sirref.org.";
188
-
}
189
-
190
-
{
191
-
name = "@";
192
-
type = "A";
193
-
value = config.eilean.serverIpv4;
194
-
}
195
-
{
196
-
name = "@";
197
-
type = "AAAA";
198
-
value = config.eilean.serverIpv6;
199
-
}
200
-
{
201
-
name = "vps";
202
-
type = "A";
203
-
value = config.eilean.serverIpv4;
204
-
}
205
-
{
206
-
name = "vps";
207
-
type = "AAAA";
208
-
value = config.eilean.serverIpv6;
209
-
}
210
-
211
-
{
212
-
name = "@";
213
-
type = "LOC";
214
-
value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m";
215
-
}
216
-
217
-
{
218
-
name = "ns.cl";
219
-
type = "A";
220
-
value = "128.232.113.136";
221
-
}
222
-
{
223
-
name = "cl";
224
-
type = "NS";
225
-
value = "ns.cl";
226
-
}
227
-
228
-
{
229
-
name = "ns1.eilean";
230
-
type = "A";
231
-
value = "65.109.10.223";
232
-
}
233
-
{
234
-
name = "eilean";
235
-
type = "NS";
236
-
value = "ns1.eilean";
237
-
}
238
-
239
-
{
240
-
name = "shrew";
241
-
type = "CNAME";
242
-
value = "vps";
243
-
}
244
-
245
-
{
246
-
name = "knot";
247
-
type = "CNAME";
248
-
value = "vps";
249
-
}
250
-
251
-
# generate with
252
-
# sudo openssl x509 -in /var/lib/acme/mail.freumh.org/fullchain.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "3 1 1", $1}'
253
-
{
254
-
name = "_25._tcp.mail";
255
-
type = "TLSA";
256
-
value = "3 1 1 2f0fd413f063c75141937dd196a9f4ab66139d599e0dcf2a7ce6d557647e26a6";
257
-
}
258
-
# generate with
259
-
# for i in r3 e1 r4-cross-signed e2
260
-
# openssl x509 -in ~/downloads/lets-encrypt-$i.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "2 1 1", $1}'
261
-
# LE R3
262
-
{
263
-
name = "_25._tcp.mail";
264
-
type = "TLSA";
265
-
value = "2 1 1 8d02536c887482bc34ff54e41d2ba659bf85b341a0a20afadb5813dcfbcf286d";
266
-
}
267
-
# LE E1
268
-
{
269
-
name = "_25._tcp.mail";
270
-
type = "TLSA";
271
-
value = "2 1 1 276fe8a8c4ec7611565bf9fce6dcace9be320c1b5bea27596b2204071ed04f10";
272
-
}
273
-
# LE R4
274
-
{
275
-
name = "_25._tcp.mail";
276
-
type = "TLSA";
277
-
value = "2 1 1 e5545e211347241891c554a03934cde9b749664a59d26d615fe58f77990f2d03";
278
-
}
279
-
# LE E2
280
-
{
281
-
name = "_25._tcp.mail";
282
-
type = "TLSA";
283
-
value = "2 1 1 bd936e72b212ef6f773102c6b77d38f94297322efc25396bc3279422e0c89270";
284
-
}
285
-
] ++ vpnRecords;
286
-
};
287
-
"fn06.org" = {
288
-
soa.serial = 1706745602;
289
-
records = [
290
-
{
291
-
name = "@";
292
-
type = "NS";
293
-
value = "ns1";
294
-
}
295
-
{
296
-
name = "@";
297
-
type = "NS";
298
-
value = "ns2";
299
-
}
300
-
301
-
{
302
-
name = "ns1";
303
-
type = "A";
304
-
value = config.eilean.serverIpv4;
305
-
}
306
-
{
307
-
name = "ns1";
308
-
type = "AAAA";
309
-
value = config.eilean.serverIpv6;
310
-
}
311
-
{
312
-
name = "ns2";
313
-
type = "A";
314
-
value = config.eilean.serverIpv4;
315
-
}
316
-
{
317
-
name = "ns2";
318
-
type = "AAAA";
319
-
value = config.eilean.serverIpv6;
320
-
}
321
-
322
-
{
323
-
name = "@";
324
-
type = "A";
325
-
value = config.eilean.serverIpv4;
326
-
}
327
-
{
328
-
name = "@";
329
-
type = "AAAA";
330
-
value = config.eilean.serverIpv6;
331
-
}
332
-
333
-
{
334
-
name = "www.fn06.org.";
335
-
type = "CNAME";
336
-
value = "fn06.org.";
337
-
}
338
-
339
-
{
340
-
name = "@";
341
-
type = "LOC";
342
-
value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m";
343
-
}
344
-
345
-
{
346
-
name = "capybara.fn06.org.";
347
-
type = "CNAME";
348
-
value = "fn06.org.";
349
-
}
350
-
351
-
{
352
-
name = "jellyfin.${config.networking.domain}.";
353
-
type = "AAAA";
354
-
value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb";
355
-
}
356
-
{
357
-
name = "jellyseerr.${config.networking.domain}.";
358
-
type = "AAAA";
359
-
value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb";
360
-
}
361
-
{
362
-
name = "calibre.${config.networking.domain}.";
363
-
type = "AAAA";
364
-
value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb";
365
-
}
366
-
];
367
-
};
368
-
};
369
-
services.bind.zones.${config.networking.domain}.extraConfig =
370
-
''
371
-
dnssec-policy default;
372
-
inline-signing yes;
373
-
journal "${config.services.bind.directory}/${config.networking.domain}.signed.jnl";
374
-
''
375
-
+
376
-
# dig ns org +short | xargs dig +short
377
-
# replace with `checkds true;` in bind 9.20
378
-
''
379
-
parental-agents {
380
-
199.19.56.1;
381
-
199.249.112.1;
382
-
199.19.54.1;
383
-
199.249.120.1;
384
-
199.19.53.1;
385
-
199.19.57.1;
386
-
};
387
-
'';
388
-
389
-
services.nginx.commonHttpConfig = ''
390
-
add_header Strict-Transport-Security max-age=31536000 always;
391
-
add_header X-Frame-Options SAMEORIGIN always;
392
-
add_header X-Content-Type-Options nosniff always;
393
-
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always;
394
-
add_header Referrer-Policy 'same-origin';
395
-
'';
396
-
services.nginx.virtualHosts."teapot.${config.networking.domain}" = {
397
-
extraConfig = ''
398
-
return 418;
399
-
'';
400
-
};
401
-
age.secrets.website-phd = {
402
-
file = ../../secrets/website-phd.age;
403
-
mode = "770";
404
-
owner = "${config.systemd.services.nginx.serviceConfig.User}";
405
-
group = "${config.systemd.services.nginx.serviceConfig.Group}";
406
-
};
407
-
services.nginx.virtualHosts."${config.custom.website.ryan.domain}" = {
408
-
locations."/phd/" = {
409
-
basicAuthFile = config.age.secrets.website-phd.path;
410
-
};
411
-
};
412
-
413
-
security.acme-eon.nginxCerts = [
414
-
"capybara.fn06.org"
415
-
"shrew.freumh.org"
416
-
"knot.freumh.org"
417
-
];
418
-
services.nginx.virtualHosts."capybara.fn06.org" = {
419
-
forceSSL = true;
420
-
locations."/" = {
421
-
proxyPass = ''
422
-
http://100.64.0.10:8123
423
-
'';
424
-
proxyWebsockets = true;
425
-
};
426
-
};
427
-
services.nginx.virtualHosts."shrew.freumh.org" = {
428
-
forceSSL = true;
429
-
locations."/" = {
430
-
# need to specify ip or there's a bootstrap problem with headscale
431
-
proxyPass = ''
432
-
http://100.64.0.6:8123
433
-
'';
434
-
proxyWebsockets = true;
435
-
};
436
-
};
437
-
438
-
services.mastodon = {
439
-
webProcesses = lib.mkForce 1;
440
-
webThreads = lib.mkForce 1;
441
-
sidekiqThreads = lib.mkForce 1;
442
-
streamingProcesses = lib.mkForce 1;
443
-
};
444
-
445
-
boot.kernel.sysctl = {
446
-
"net.ipv4.ip_forward" = 1;
447
-
"net.ipv6.conf.all.forwarding" = 1;
448
-
};
449
-
450
-
services.headscale.settings.dns = {
451
-
extra_records = vpnRecords;
452
-
base_domain = "vpn.freumh.org";
453
-
nameservers.global = config.networking.nameservers;
454
-
};
455
-
456
-
age.secrets.restic-owl.file = ../../secrets/restic-owl.age;
457
-
services.restic.backups.${config.networking.hostName} = {
458
-
repository = "rest:http://100.64.0.9:8000/${config.networking.hostName}/";
459
-
passwordFile = config.age.secrets.restic-owl.path;
460
-
initialize = true;
461
-
paths = [
462
-
"/var/"
463
-
"/run/"
464
-
"/etc/"
465
-
];
466
-
timerConfig = {
467
-
OnCalendar = "03:00";
468
-
randomizedDelaySec = "1hr";
469
-
};
470
-
};
471
-
472
23
nix = {
473
24
gc = {
474
25
automatic = true;
···
478
29
};
479
30
};
480
31
481
-
age.secrets.email-ryan.file = ../../secrets/email-ryan.age;
482
-
age.secrets.email-system.file = ../../secrets/email-system.age;
483
-
eilean.mailserver.systemAccountPasswordFile = config.age.secrets.email-system.path;
484
-
mailserver.loginAccounts = {
485
-
"${config.eilean.username}@${config.networking.domain}" = {
486
-
passwordFile = config.age.secrets.email-ryan.path;
487
-
aliases = [
488
-
"dns@${config.networking.domain}"
489
-
"postmaster@${config.networking.domain}"
490
-
];
491
-
sieveScript = ''
492
-
require ["fileinto", "mailbox"];
493
-
494
-
if header :contains ["to", "cc"] ["ai-control@ietf.org"] {
495
-
fileinto :create "lists.aietf";
496
-
stop;
497
-
}
498
-
'';
499
-
};
500
-
"misc@${config.networking.domain}" = {
501
-
passwordFile = config.age.secrets.email-ryan.path;
502
-
catchAll = [ "${config.networking.domain}" ];
503
-
};
504
-
"system@${config.networking.domain}" = {
505
-
aliases = [ "nas@${config.networking.domain}" ];
506
-
};
507
-
};
508
-
509
-
services.minecraft-server = {
510
-
enable = true;
511
-
package = pkgs.overlay-unstable.minecraft-server;
512
-
eula = true;
513
-
openFirewall = true;
514
-
};
515
-
516
-
networking.firewall.allowedTCPPorts = [ 7001 ];
517
-
518
32
services.openssh.openFirewall = true;
519
-
520
-
age.secrets.tangled = {
521
-
file = ../../secrets/tangled.age;
522
-
mode = "660";
523
-
owner = "git";
524
-
group = "git";
525
-
};
526
-
services.tangled-knotserver = {
527
-
enable = true;
528
-
repo.mainBranch = "master";
529
-
server.hostname = "knot.freumh.org";
530
-
server = {
531
-
secretFile = config.age.secrets.tangled.path;
532
-
listenAddr = "127.0.0.1:5555";
533
-
internalListenAddr = "127.0.0.1:5444";
534
-
};
535
-
};
536
-
services.nginx.virtualHosts."knot.freumh.org" = {
537
-
forceSSL = true;
538
-
locations."/" = {
539
-
proxyPass = ''
540
-
http://${config.services.tangled-knotserver.server.listenAddr}
541
-
'';
542
-
proxyWebsockets = true;
543
-
};
544
-
};
545
33
}
+482
hosts/owl/services.nix
+482
hosts/owl/services.nix
···
1
+
{
2
+
pkgs,
3
+
config,
4
+
lib,
5
+
eon,
6
+
...
7
+
}:
8
+
9
+
let
10
+
vpnRecords = [
11
+
{
12
+
name = "nix-cache.vpn.${config.networking.domain}.";
13
+
type = "A";
14
+
value = "100.64.0.9";
15
+
}
16
+
{
17
+
name = "jellyfin.vpn.${config.networking.domain}.";
18
+
type = "A";
19
+
value = "100.64.0.9";
20
+
}
21
+
{
22
+
name = "nextcloud.vpn.${config.networking.domain}.";
23
+
type = "A";
24
+
value = "100.64.0.9";
25
+
}
26
+
{
27
+
name = "transmission.vpn.${config.networking.domain}.";
28
+
type = "A";
29
+
value = "100.64.0.9";
30
+
}
31
+
{
32
+
name = "owntracks.vpn.${config.networking.domain}.";
33
+
type = "A";
34
+
value = "100.64.0.9";
35
+
}
36
+
{
37
+
name = "immich.vpn.${config.networking.domain}.";
38
+
type = "A";
39
+
value = "100.64.0.9";
40
+
}
41
+
{
42
+
name = "audiobookshelf.vpn.${config.networking.domain}.";
43
+
type = "A";
44
+
value = "100.64.0.9";
45
+
}
46
+
];
47
+
in
48
+
{
49
+
# eilean
50
+
networking.domain = lib.mkDefault "freumh.org";
51
+
eilean = {
52
+
username = config.custom.username;
53
+
serverIpv4 = "135.181.100.27";
54
+
serverIpv6 = "2a01:4f9:c011:87ad:0:0:0:0";
55
+
publicInterface = "enp1s0";
56
+
fail2ban.enable = true;
57
+
};
58
+
59
+
# eon
60
+
age.secrets.eon-capnp = {
61
+
file = ../../secrets/eon-capnp.age;
62
+
mode = "660";
63
+
owner = "eon";
64
+
group = "eon";
65
+
};
66
+
age.secrets.eon-sirref-primary = {
67
+
file = ../../secrets/eon-sirref-primary.cap.age;
68
+
mode = "660";
69
+
owner = "eon";
70
+
group = "eon";
71
+
};
72
+
services.eon = {
73
+
capnpSecretKeyFile = config.age.secrets.eon-capnp.path;
74
+
primaries = [ config.age.secrets.eon-sirref-primary.path ];
75
+
prod = true;
76
+
capnpAddress = "135.181.100.27";
77
+
logLevel = 0;
78
+
};
79
+
80
+
# certificates
81
+
eilean.acme-eon = true;
82
+
security.acme-eon = {
83
+
acceptTerms = true;
84
+
package = eon.defaultPackage.${config.nixpkgs.hostPlatform.system};
85
+
defaults.email = "${config.custom.username}@${config.networking.domain}";
86
+
defaults.capFile = "/var/lib/eon/caps/domain/freumh.org.cap";
87
+
certs = {
88
+
"fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap";
89
+
"capybara.fn06.org".capFile = "/var/lib/eon/caps/domain/fn06.org.cap";
90
+
};
91
+
};
92
+
security.acme-eon.nginxCerts = [
93
+
"capybara.fn06.org"
94
+
"shrew.freumh.org"
95
+
"knot.freumh.org"
96
+
];
97
+
98
+
# VPN
99
+
eilean.headscale.enable = true;
100
+
services.headscale.settings.dns = {
101
+
extra_records = vpnRecords;
102
+
base_domain = "vpn.freumh.org";
103
+
nameservers.global = config.networking.nameservers;
104
+
};
105
+
boot.kernel.sysctl = {
106
+
"net.ipv4.ip_forward" = 1;
107
+
"net.ipv6.conf.all.forwarding" = 1;
108
+
};
109
+
110
+
# websites
111
+
custom = {
112
+
freumh.enable = true;
113
+
rmfakecloud.enable = true;
114
+
website = {
115
+
ryan = {
116
+
enable = true;
117
+
cname = "vps";
118
+
};
119
+
alec = {
120
+
enable = true;
121
+
cname = "vps";
122
+
};
123
+
fn06 = {
124
+
enable = true;
125
+
cname = "vps";
126
+
domain = "fn06.org";
127
+
};
128
+
};
129
+
};
130
+
services.nginx.commonHttpConfig = ''
131
+
add_header Strict-Transport-Security max-age=31536000 always;
132
+
add_header X-Frame-Options SAMEORIGIN always;
133
+
add_header X-Content-Type-Options nosniff always;
134
+
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always;
135
+
add_header Referrer-Policy 'same-origin';
136
+
'';
137
+
services.nginx.virtualHosts."teapot.${config.networking.domain}" = {
138
+
extraConfig = ''
139
+
return 418;
140
+
'';
141
+
};
142
+
143
+
# mailserver
144
+
eilean.mailserver.enable = true;
145
+
age.secrets.email-ryan.file = ../../secrets/email-ryan.age;
146
+
age.secrets.email-system.file = ../../secrets/email-system.age;
147
+
eilean.mailserver.systemAccountPasswordFile = config.age.secrets.email-system.path;
148
+
mailserver.loginAccounts = {
149
+
"${config.eilean.username}@${config.networking.domain}" = {
150
+
passwordFile = config.age.secrets.email-ryan.path;
151
+
aliases = [
152
+
"dns@${config.networking.domain}"
153
+
"postmaster@${config.networking.domain}"
154
+
];
155
+
sieveScript = ''
156
+
require ["fileinto", "mailbox"];
157
+
158
+
if header :contains ["to", "cc"] ["ai-control@ietf.org"] {
159
+
fileinto :create "lists.aietf";
160
+
stop;
161
+
}
162
+
'';
163
+
};
164
+
"misc@${config.networking.domain}" = {
165
+
passwordFile = config.age.secrets.email-ryan.path;
166
+
catchAll = [ "${config.networking.domain}" ];
167
+
};
168
+
"system@${config.networking.domain}" = {
169
+
aliases = [ "nas@${config.networking.domain}" ];
170
+
};
171
+
};
172
+
173
+
# CalDAV calendar server
174
+
eilean.radicale = {
175
+
enable = true;
176
+
users = null;
177
+
};
178
+
179
+
# matrix
180
+
age.secrets.matrix-shared-secret = {
181
+
file = ../../secrets/matrix-shared-secret.age;
182
+
mode = "770";
183
+
owner = "${config.systemd.services.matrix-synapse.serviceConfig.User}";
184
+
group = "${config.systemd.services.matrix-synapse.serviceConfig.Group}";
185
+
};
186
+
eilean.matrix = {
187
+
enable = true;
188
+
registrationSecretFile = config.age.secrets.matrix-shared-secret.path;
189
+
bridges.whatsapp = true;
190
+
bridges.signal = true;
191
+
bridges.instagram = true;
192
+
bridges.messenger = true;
193
+
};
194
+
eilean.turn.enable = true;
195
+
systemd.services.matrix-as-meta = {
196
+
path = [ pkgs.ffmpeg ];
197
+
};
198
+
199
+
# mastodon
200
+
eilean.mastodon.enable = true;
201
+
services.mastodon = {
202
+
webProcesses = lib.mkForce 1;
203
+
webThreads = lib.mkForce 1;
204
+
sidekiqThreads = lib.mkForce 1;
205
+
streamingProcesses = lib.mkForce 1;
206
+
};
207
+
208
+
# restic
209
+
age.secrets.restic-owl.file = ../../secrets/restic-owl.age;
210
+
services.restic.backups.${config.networking.hostName} = {
211
+
repository = "rest:http://100.64.0.9:8000/${config.networking.hostName}/";
212
+
passwordFile = config.age.secrets.restic-owl.path;
213
+
initialize = true;
214
+
paths = [
215
+
"/var/"
216
+
"/run/"
217
+
"/etc/"
218
+
];
219
+
timerConfig = {
220
+
OnCalendar = "03:00";
221
+
randomizedDelaySec = "1hr";
222
+
};
223
+
};
224
+
225
+
# reverse proxy
226
+
services.nginx.virtualHosts."capybara.fn06.org" = {
227
+
forceSSL = true;
228
+
locations."/" = {
229
+
proxyPass = ''
230
+
http://100.64.0.10:8123
231
+
'';
232
+
proxyWebsockets = true;
233
+
};
234
+
};
235
+
services.nginx.virtualHosts."shrew.freumh.org" = {
236
+
forceSSL = true;
237
+
locations."/" = {
238
+
proxyPass = ''
239
+
http://100.64.0.6:8123
240
+
'';
241
+
proxyWebsockets = true;
242
+
};
243
+
};
244
+
245
+
# tangled
246
+
age.secrets.tangled = {
247
+
file = ../../secrets/tangled.age;
248
+
mode = "660";
249
+
owner = "git";
250
+
group = "git";
251
+
};
252
+
services.tangled-knotserver = {
253
+
enable = true;
254
+
repo.mainBranch = "master";
255
+
server.hostname = "knot.freumh.org";
256
+
server = {
257
+
secretFile = config.age.secrets.tangled.path;
258
+
listenAddr = "127.0.0.1:5555";
259
+
internalListenAddr = "127.0.0.1:5444";
260
+
};
261
+
};
262
+
services.nginx.virtualHosts."knot.freumh.org" = {
263
+
forceSSL = true;
264
+
locations."/" = {
265
+
proxyPass = ''
266
+
http://${config.services.tangled-knotserver.server.listenAddr}
267
+
'';
268
+
proxyWebsockets = true;
269
+
};
270
+
};
271
+
272
+
# minecraft server
273
+
services.minecraft-server = {
274
+
enable = true;
275
+
package = pkgs.overlay-unstable.minecraft-server;
276
+
eula = true;
277
+
openFirewall = true;
278
+
};
279
+
280
+
# DNS records
281
+
eilean.dns.nameservers = [ "ns1" ];
282
+
eilean.services.dns.zones = {
283
+
${config.networking.domain} = {
284
+
ttl = 300;
285
+
soa = {
286
+
serial = 2018011660;
287
+
refresh = 300;
288
+
};
289
+
records = [
290
+
{
291
+
name = "@";
292
+
type = "TXT";
293
+
value = "google-site-verification=rEvwSqf7RYKRQltY412qMtTuoxPp64O3L7jMotj9Jnc";
294
+
}
295
+
{
296
+
name = "_atproto.ryan";
297
+
type = "TXT";
298
+
value = "did=did:plc:3lfhu6ehlynzjgehef6alnvg";
299
+
}
300
+
301
+
{
302
+
name = "teapot";
303
+
type = "CNAME";
304
+
value = "vps";
305
+
}
306
+
307
+
{
308
+
name = "@";
309
+
type = "ns";
310
+
value = "ns1.sirref.org.";
311
+
}
312
+
313
+
{
314
+
name = "vps";
315
+
type = "A";
316
+
value = config.eilean.serverIpv4;
317
+
}
318
+
{
319
+
name = "vps";
320
+
type = "AAAA";
321
+
value = config.eilean.serverIpv6;
322
+
}
323
+
324
+
{
325
+
name = "@";
326
+
type = "LOC";
327
+
value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m";
328
+
}
329
+
330
+
{
331
+
name = "ns.cl";
332
+
type = "A";
333
+
value = "128.232.113.136";
334
+
}
335
+
{
336
+
name = "cl";
337
+
type = "NS";
338
+
value = "ns.cl";
339
+
}
340
+
341
+
{
342
+
name = "ns1.eilean";
343
+
type = "A";
344
+
value = "65.109.10.223";
345
+
}
346
+
{
347
+
name = "eilean";
348
+
type = "NS";
349
+
value = "ns1.eilean";
350
+
}
351
+
352
+
{
353
+
name = "shrew";
354
+
type = "CNAME";
355
+
value = "vps";
356
+
}
357
+
358
+
{
359
+
name = "knot";
360
+
type = "CNAME";
361
+
value = "vps";
362
+
}
363
+
364
+
# generate with
365
+
# sudo openssl x509 -in /var/lib/acme/mail.freumh.org/fullchain.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "3 1 1", $1}'
366
+
{
367
+
name = "_25._tcp.mail";
368
+
type = "TLSA";
369
+
value = "3 1 1 2f0fd413f063c75141937dd196a9f4ab66139d599e0dcf2a7ce6d557647e26a6";
370
+
}
371
+
# generate with
372
+
# for i in r3 e1 r4-cross-signed e2
373
+
# openssl x509 -in ~/downloads/lets-encrypt-$i.pem -pubkey -noout | openssl pkey -pubin -outform der | sha256sum | awk '{print "2 1 1", $1}'
374
+
# LE R3
375
+
{
376
+
name = "_25._tcp.mail";
377
+
type = "TLSA";
378
+
value = "2 1 1 8d02536c887482bc34ff54e41d2ba659bf85b341a0a20afadb5813dcfbcf286d";
379
+
}
380
+
# LE E1
381
+
{
382
+
name = "_25._tcp.mail";
383
+
type = "TLSA";
384
+
value = "2 1 1 276fe8a8c4ec7611565bf9fce6dcace9be320c1b5bea27596b2204071ed04f10";
385
+
}
386
+
# LE R4
387
+
{
388
+
name = "_25._tcp.mail";
389
+
type = "TLSA";
390
+
value = "2 1 1 e5545e211347241891c554a03934cde9b749664a59d26d615fe58f77990f2d03";
391
+
}
392
+
# LE E2
393
+
{
394
+
name = "_25._tcp.mail";
395
+
type = "TLSA";
396
+
value = "2 1 1 bd936e72b212ef6f773102c6b77d38f94297322efc25396bc3279422e0c89270";
397
+
}
398
+
399
+
{
400
+
name = "jellyfin";
401
+
type = "AAAA";
402
+
value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb";
403
+
}
404
+
{
405
+
name = "jellyseerr";
406
+
type = "AAAA";
407
+
value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb";
408
+
}
409
+
{
410
+
name = "calibre";
411
+
type = "AAAA";
412
+
value = "2a00:23c6:aa22:e401:8dff:9b9a:cb3c:3fcb";
413
+
}
414
+
] ++ vpnRecords;
415
+
};
416
+
"fn06.org" = {
417
+
soa.serial = 1706745602;
418
+
records = [
419
+
{
420
+
name = "@";
421
+
type = "NS";
422
+
value = "ns1";
423
+
}
424
+
{
425
+
name = "@";
426
+
type = "NS";
427
+
value = "ns2";
428
+
}
429
+
430
+
{
431
+
name = "ns1";
432
+
type = "A";
433
+
value = config.eilean.serverIpv4;
434
+
}
435
+
{
436
+
name = "ns1";
437
+
type = "AAAA";
438
+
value = config.eilean.serverIpv6;
439
+
}
440
+
{
441
+
name = "ns2";
442
+
type = "A";
443
+
value = config.eilean.serverIpv4;
444
+
}
445
+
{
446
+
name = "ns2";
447
+
type = "AAAA";
448
+
value = config.eilean.serverIpv6;
449
+
}
450
+
451
+
{
452
+
name = "@";
453
+
type = "A";
454
+
value = config.eilean.serverIpv4;
455
+
}
456
+
{
457
+
name = "@";
458
+
type = "AAAA";
459
+
value = config.eilean.serverIpv6;
460
+
}
461
+
462
+
{
463
+
name = "www.fn06.org.";
464
+
type = "CNAME";
465
+
value = "fn06.org.";
466
+
}
467
+
468
+
{
469
+
name = "@";
470
+
type = "LOC";
471
+
value = "52 12 40.4 N 0 5 31.9 E 22m 10m 10m 10m";
472
+
}
473
+
474
+
{
475
+
name = "capybara.fn06.org.";
476
+
type = "CNAME";
477
+
value = "fn06.org.";
478
+
}
479
+
];
480
+
};
481
+
};
482
+
}
-57
modules/colour-guesser.nix
-57
modules/colour-guesser.nix
···
1
-
{
2
-
pkgs,
3
-
config,
4
-
lib,
5
-
options,
6
-
colour-guesser,
7
-
...
8
-
}:
9
-
10
-
let
11
-
cfg = config.custom.website.colour-guesser;
12
-
in
13
-
{
14
-
options = {
15
-
custom.website.colour-guesser = {
16
-
enable = lib.mkEnableOption "Colour Guesser";
17
-
domain = lib.mkOption {
18
-
type = lib.types.str;
19
-
default = "colour-guesser.${config.networking.domain}";
20
-
};
21
-
cname = lib.mkOption {
22
-
type = lib.types.str;
23
-
default = null;
24
-
description = ''
25
-
CNAME to create DNS records for.
26
-
Ignored if null
27
-
'';
28
-
};
29
-
};
30
-
};
31
-
32
-
config = lib.mkIf cfg.enable {
33
-
security.acme-eon.nginxCerts = [ cfg.domain ];
34
-
services.nginx = {
35
-
enable = true;
36
-
recommendedProxySettings = true;
37
-
virtualHosts."${cfg.domain}" = {
38
-
forceSSL = true;
39
-
root = "${colour-guesser.packages.${pkgs.stdenv.hostPlatform.system}.default}";
40
-
};
41
-
};
42
-
43
-
# requires dns module
44
-
eilean.services.dns.zones.${config.networking.domain}.records = [
45
-
{
46
-
name = "${cfg.domain}.";
47
-
type = "CNAME";
48
-
value = cfg.cname;
49
-
}
50
-
{
51
-
name = "www.${cfg.domain}.";
52
-
type = "CNAME";
53
-
value = cfg.cname;
54
-
}
55
-
];
56
-
};
57
-
}