Compare commits
533 Commits
staging/mo
...
staging/ni
Author | SHA1 | Date | |
---|---|---|---|
3cc0d7ec40 | |||
42c2d93306 | |||
f6f8d0e19d | |||
8ad4d8a4f9 | |||
d419e10f10 | |||
c56734e00e | |||
e095149797 | |||
6006b84f9e | |||
b3b914bc2e | |||
d6f87c7ee7 | |||
028d78437b | |||
0539bb8fd5 | |||
95cc42f057 | |||
54cfde0e30 | |||
b358cbe02a | |||
64cd562824 | |||
749f59a4af | |||
77d5ff623e | |||
6386abb8c6 | |||
28f8434c53 | |||
30fafb4837 | |||
c7205ed668 | |||
c69c6612fe | |||
6951b277ad | |||
1167c9bd0c | |||
8537fe411b | |||
1fb2ddbecc | |||
27608cb8c7 | |||
33ee59e80d | |||
edacc28e4a | |||
99780e30bc | |||
140dd05bd1 | |||
c35bc92ac9 | |||
16b92f98e7 | |||
576b63da9c | |||
e434add20d | |||
d288086aa2 | |||
e9c0f692d8 | |||
5a75d0f56b | |||
a222cf18a3 | |||
45b9ee763b | |||
50a2196495 | |||
e4419ffad4 | |||
ae3ba64fc3 | |||
948b2489d3 | |||
abe491b563 | |||
9911593a63 | |||
798ba7f7e3 | |||
f72e901f57 | |||
2047222233 | |||
5d33cb66d6 | |||
6fded1f256 | |||
1d1c528abc | |||
35fdd2788f | |||
77cb951545 | |||
1909e0fbe8 | |||
33d7819619 | |||
0846abb6bf | |||
f3568462c2 | |||
ed9291a443 | |||
9eac7f7c02 | |||
f5eaa635a5 | |||
47db6f0dd4 | |||
bba7efc535 | |||
358b673344 | |||
d9c101689d | |||
ccbb573681 | |||
f5c270233f | |||
bf92bb48be | |||
2037b425d6 | |||
026746a76f | |||
de74c4e7d5 | |||
34a5f3f49b | |||
ee93141c07 | |||
f2d22231a3 | |||
d5334e65ad | |||
17cdfff286 | |||
8d4ff6d4e6 | |||
5d78bc6704 | |||
4da19a6d34 | |||
2f75925678 | |||
55a1856e87 | |||
2ee0f4efe2 | |||
b0c5a5907f | |||
9af157b294 | |||
c36fed8547 | |||
a653311f04 | |||
f4d6ecb1cf | |||
c2e5a0a2fc | |||
c316e51344 | |||
f4f0c1bdd6 | |||
6a2374e046 | |||
708cb841fe | |||
094b7223c7 | |||
f6dfc9cf29 | |||
7c2ab92302 | |||
7c18d77046 | |||
02f316f7f8 | |||
df848b3262 | |||
1e67b5c97e | |||
a3a7b6c563 | |||
e527beb9d0 | |||
2e942e2dd4 | |||
f46e3fdf01 | |||
038a9034d7 | |||
5a232eb832 | |||
9301b95dbb | |||
d13bcc49ab | |||
35e28041cd | |||
58a5a8b56d | |||
e6d4ff3c6a | |||
be29ad8bd8 | |||
0fb8e2c867 | |||
580c1b74cb | |||
f8595f1ed6 | |||
1deda148bb | |||
5bbef18130 | |||
6967c331e2 | |||
9202345beb | |||
17a8cabc09 | |||
bc190f90bd | |||
bb983a5328 | |||
0e8fc29b01 | |||
4e14f063fc | |||
10d69fb0a4 | |||
7aac965e32 | |||
98ae1a8513 | |||
72a2ab78f3 | |||
18c98feb34 | |||
487af9b492 | |||
472d25c056 | |||
9eafacad12 | |||
0eb46a3179 | |||
ddb184b5ff | |||
194a6b6cf4 | |||
016384aa2b | |||
b4e19c037e | |||
bd504f6c83 | |||
bdd309eb15 | |||
eedc1170ec | |||
5a586c6e3c | |||
371bcad650 | |||
926decbea5 | |||
c0f76ea8d8 | |||
40fc37930f | |||
30e7eb9ab6 | |||
2e03f47edc | |||
4d552e3f0f | |||
176a98879d | |||
fc70889c34 | |||
49b4c57826 | |||
5111d095ac | |||
fe15cdd705 | |||
638420ea0d | |||
d55dd5ace6 | |||
45695aed6b | |||
d6e79c4d07 | |||
380ceaf625 | |||
942c581107 | |||
b6d94c2e08 | |||
fd7acc8fc8 | |||
db670fc172 | |||
6438971c8c | |||
e439d398b6 | |||
0f25cba331 | |||
39959e912d | |||
62e649743d | |||
b1741a18e1 | |||
a829a8e027 | |||
d742ae83bd | |||
110ab1a794 | |||
7d5a81e542 | |||
1af2a3f329 | |||
3fa9e910a9 | |||
6befc40700 | |||
29db2d8dc5 | |||
36d8052982 | |||
48115231a3 | |||
8b56ddd1ca | |||
c1457f5bfb | |||
7dfaf77a71 | |||
72dc7029e6 | |||
95f3215b00 | |||
baac8df8c2 | |||
dc6a08a12b | |||
2413e2eb5f | |||
7327128493 | |||
ed8059f4c4 | |||
3a72295610 | |||
e6d9edf27d | |||
78782d5f7e | |||
91275f3723 | |||
8115edea8d | |||
4c475bbf9c | |||
7040e1f07c | |||
aafa64942c | |||
a44a99e371 | |||
a7ff90c843 | |||
d4996d6f31 | |||
bd5209c655 | |||
9588108fd5 | |||
942e302afb | |||
2bd98e6764 | |||
7b9910f287 | |||
917afe209e | |||
cc5cf9b6f4 | |||
57d95dd298 | |||
0b78df53be | |||
c8dcb4ac59 | |||
241f4ae58f | |||
965d7eedbb | |||
cdc881e887 | |||
33967554a5 | |||
5af55ecdbf | |||
6ca3e7086e | |||
ca62f1b62f | |||
eef66df36d | |||
9ca6a1c907 | |||
dbb78088f4 | |||
f17ae1ca7b | |||
b2774a4004 | |||
0ae548d47c | |||
760505db20 | |||
71fc1a2fd7 | |||
a457fc1416 | |||
2c0b0f6947 | |||
f10de6c2c4 | |||
a6be200a82 | |||
fb57e9aa5b | |||
f5acbbd830 | |||
af77417531 | |||
eea80b575d | |||
6a209d27fd | |||
e8f778fecd | |||
488036beb3 | |||
00b681eca5 | |||
72d589cb2d | |||
ea5552daa7 | |||
fb7d94209c | |||
8f5b92685b | |||
32a4cb19fd | |||
031cfa2bcd | |||
e93fbea1e6 | |||
85a2fbc38a | |||
9e902c8eb2 | |||
dc15091ea7 | |||
c063ecd047 | |||
70a43c770d | |||
cc9e2d8e15 | |||
bb41fb95fe | |||
d852adf806 | |||
5443542cba | |||
81effb01a3 | |||
83f416999f | |||
dd34883246 | |||
e47f9e38ce | |||
0f0b728911 | |||
1839f87a4e | |||
53edf4e6af | |||
fb6e0ddb34 | |||
0a48d79174 | |||
b6208e1a19 | |||
e46ab4ec14 | |||
19c254c266 | |||
1d0cadce85 | |||
e8342b8044 | |||
40e642bfc3 | |||
f008565e22 | |||
4ea2835d9d | |||
493d317bb1 | |||
e446bfba58 | |||
a7bac5de18 | |||
b0950e90f4 | |||
d8cd0e1f57 | |||
fd7d67ee05 | |||
1a712b4d47 | |||
4520e1d1f5 | |||
841a2a3bcb | |||
fe816e9110 | |||
426e0c3ae2 | |||
a95b91a556 | |||
837e5438c3 | |||
8217b22c86 | |||
0b35ce4dec | |||
413f9a171b | |||
43a46af43b | |||
1a0f05bfd6 | |||
c18dd9636d | |||
0977721af5 | |||
122d3cd7e4 | |||
cd5f8054c0 | |||
3db388b105 | |||
2ba6116f10 | |||
592d17b725 | |||
4d9c15f9b8 | |||
abced7dd0d | |||
5c42365912 | |||
247ad326b2 | |||
170008f345 | |||
2c48e61854 | |||
f89f756489 | |||
c0da19951b | |||
5fb67306e4 | |||
5533b586d7 | |||
68c2eb7363 | |||
fd79026366 | |||
a76471cb1f | |||
c94b8299a6 | |||
175bc0709f | |||
7b02477486 | |||
d7c8638fea | |||
9d7d1acc80 | |||
787857d27f | |||
9c248a8a31 | |||
829680fb00 | |||
a9ee26388c | |||
2960b895b6 | |||
933063115b | |||
afe684ca2c | |||
93f1411522 | |||
01e44c1f7f | |||
618e9bd2fa | |||
fbc39d0584 | |||
2d7b3750cd | |||
e6ccd2e4f7 | |||
d4bf491e9c | |||
5a2bbcce3b | |||
327e6b536f | |||
bace7403e7 | |||
57f5521ef3 | |||
9e32211c12 | |||
edf6bd4455 | |||
a9a14786f9 | |||
eade5fe16e | |||
be222c1d70 | |||
88a33dd5de | |||
875e923197 | |||
54dd643cf0 | |||
3c726f148b | |||
e225e2e704 | |||
cf0bf8190e | |||
b8f7f68d4c | |||
7a3aae8c97 | |||
89e519810d | |||
0e920230ba | |||
6ffae00e17 | |||
be19985440 | |||
f7e3e7294a | |||
d745e3c1ee | |||
c1890ce82b | |||
53a0b621d8 | |||
aeb2f63d65 | |||
528ffdb58e | |||
b6887b305e | |||
08dfc80c98 | |||
5a273213f6 | |||
0a6d88dfc1 | |||
50dfd482cf | |||
9743aee79d | |||
0819899102 | |||
d3ff68217e | |||
1a96859994 | |||
af92a2250e | |||
d00f9b15d7 | |||
aa1c1f40cb | |||
530b2d6385 | |||
e6919dd16f | |||
760f2ac66d | |||
8e5ca11259 | |||
121936620a | |||
f5b49e014c | |||
4bdb34775d | |||
f5fbc206f5 | |||
a9096f3312 | |||
67cddecab4 | |||
9a002c99eb | |||
a0ac7fa98d | |||
b03043e513 | |||
0713e3bad1 | |||
d3a3f39756 | |||
a7d9e5cc54 | |||
13f3b322b0 | |||
5c25330891 | |||
dc6dc2e475 | |||
c4352fa9bb | |||
2c6629a658 | |||
c0496b25b5 | |||
9e0346c329 | |||
364a598324 | |||
c6850aff23 | |||
730ef272d1 | |||
16fa1e0eda | |||
51a96525d9 | |||
7b01822ee7 | |||
f9aa36a620 | |||
9b75d8705b | |||
217ecec250 | |||
6c7ca7630a | |||
1f99d44288 | |||
f1aa685a03 | |||
2b31fc8776 | |||
0c35e2b3c1 | |||
77b8d0ddc0 | |||
84f23c602e | |||
ea5fbc63cf | |||
69361ee9a2 | |||
1808d153b2 | |||
b3ad0f8f1f | |||
c745612cfd | |||
278cc98c6d | |||
fac661af15 | |||
65777c70ad | |||
09c524a5b1 | |||
0db7f0857a | |||
38befe502c | |||
55e09c2dbf | |||
bd699c887c | |||
2de6f7d364 | |||
d60e5264f3 | |||
c66699b697 | |||
97044bf70e | |||
3122334a41 | |||
0b2faef989 | |||
8acd6ca4f1 | |||
8169f7c6b2 | |||
cd1aa0b376 | |||
72b627100c | |||
567c08460a | |||
9b66aecf1b | |||
16cb3b83a2 | |||
970438be8a | |||
51da29555e | |||
8a745a9b8a | |||
3505f3b9f3 | |||
444595e847 | |||
3e1407c30b | |||
0a744117a4 | |||
a2935cedaa | |||
22e46d52c2 | |||
1e0c213adf | |||
3e1340ed61 | |||
341dd3f2b2 | |||
1c9caa40bd | |||
3be15c6d05 | |||
8e8168ec28 | |||
28397807fc | |||
42ebb9a155 | |||
a8a4b8e739 | |||
2550601179 | |||
199a49755a | |||
8c7700688f | |||
8fe304d6c1 | |||
700fef7df3 | |||
01db7e1f23 | |||
df6e8f1562 | |||
1f0a40c81f | |||
995b41d1e8 | |||
7674735d42 | |||
329693c9ce | |||
5ae3bb2f6c | |||
e0b1aef127 | |||
9b8363dfb4 | |||
58ad87df8e | |||
5fc894cda9 | |||
07e6ec2533 | |||
005a79e680 | |||
0f5279bbca | |||
e9b3b7ebab | |||
7a83c1d6df | |||
46788fe565 | |||
a473ef6db3 | |||
3627d47f12 | |||
115f8d7054 | |||
ac44b04d99 | |||
afff0aff19 | |||
f0086dc5bd | |||
acabd34f28 | |||
d0e6b82739 | |||
dc09b7b9b2 | |||
38c5b82a08 | |||
89def1a073 | |||
ad2ed370d9 | |||
3e8f7a9ba2 | |||
028ecfe93f | |||
c5ac792c13 | |||
bd1624bef9 | |||
3ae53d7f32 | |||
e7f2d41b1f | |||
3394a79e2b | |||
b01501663d | |||
cbd5ccd1c8 | |||
cf857eaf9f | |||
3a7eb294c7 | |||
2ccb470adc | |||
0a2a929507 | |||
2014d5ce77 | |||
041adb7092 | |||
a979521a98 | |||
77881be955 | |||
0450b4d9a6 | |||
edea64a41c | |||
90e479592f | |||
62d83d94f2 | |||
52bbe4e9f4 | |||
ab176b8d4b | |||
62df4492a3 | |||
f4ed194abc | |||
6420c9fd16 | |||
86245b460b | |||
bf1ba786b3 | |||
35a896a3e2 | |||
b4314bd919 | |||
4696209822 | |||
c3957d81c2 | |||
8a5be00c93 | |||
c2db9fe28e | |||
ccaac901f7 | |||
7f285a8254 | |||
b0b82a3d88 | |||
b0664d81ab | |||
8ba52bb9cd | |||
20f0a19e25 | |||
9dc17a3874 | |||
2992644901 | |||
d5d89a10b9 | |||
f7d9fdfe04 | |||
c42aa2847b | |||
768c5c910f | |||
8790a7d9fd | |||
7c36a0d522 | |||
977a80d59e | |||
63c92a44ed | |||
bf838ea203 |
118
flake.lock
generated
118
flake.lock
generated
@@ -36,29 +36,14 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"impermanence": {
|
||||
"locked": {
|
||||
"lastModified": 1668668915,
|
||||
"narHash": "sha256-QjY4ZZbs9shwO4LaLpvlU2bO9J1juYhO9NtV3nrbnYQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "impermanence",
|
||||
"rev": "5df9108b346f8a42021bf99e50de89c9caa251c3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "impermanence",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mobile-nixos": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1668897543,
|
||||
"narHash": "sha256-1bjvy5zi/6KDzhN3ihOUEA6y5FFEOf5xvIbf65RWIh0=",
|
||||
"lastModified": 1674779092,
|
||||
"narHash": "sha256-mFBD0Dvjf8tuxWtJhsCQ+8VYqI4fQeWjd/vfWsZiRRo=",
|
||||
"owner": "nixos",
|
||||
"repo": "mobile-nixos",
|
||||
"rev": "25eec596116553112681d72ee4880107fc3957fa",
|
||||
"rev": "80ece5a61738fbf3b96fdda402ab2dfc74ee5cee",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -68,60 +53,79 @@
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unpatched"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1669542132,
|
||||
"narHash": "sha256-DRlg++NJAwPh8io3ExBJdNW7Djs3plVI5jgYQ+iXAZQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a115bb9bd56831941be3776c8a94005867f316a7",
|
||||
"type": "github"
|
||||
"lastModified": 1,
|
||||
"narHash": "sha256-5pNu9Ph1LIBj5q9RWLV3r7daANjmd4u5y+MVq8vlfS4=",
|
||||
"path": "/nix/store/bjzsgw8zn4av0dv4sqyj7vxhi43na16y-source/nixpatches",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs-22_05": {
|
||||
"locked": {
|
||||
"lastModified": 1669513802,
|
||||
"narHash": "sha256-AmTRNi8bHgJlmaNe3r5k+IMFbbXERM/KarqveMAZmsY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6649e08812f579581bfb4cada3ba01e30485c891",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-22.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
"path": "/nix/store/bjzsgw8zn4av0dv4sqyj7vxhi43na16y-source/nixpatches",
|
||||
"type": "path"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1669546925,
|
||||
"narHash": "sha256-Gvtk9agz88tBgqmCdHl5U7gYttTkiuEd8/Rq1Im0pTg=",
|
||||
"owner": "NixOS",
|
||||
"lastModified": 1674692158,
|
||||
"narHash": "sha256-oqGpwVg4D+eMSgF7Th5Ve1ysCiH3H3g85vGJ3nvJsZQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "fecf05d4861f3985e8dee73f08bc82668ef75125",
|
||||
"rev": "def9e420d27c951026d57dc96ce0218c3131f412",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-22.05",
|
||||
"type": "indirect"
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-22.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable_2": {
|
||||
"locked": {
|
||||
"lastModified": 1674352297,
|
||||
"narHash": "sha256-OkAnJPrauEcUCrst4/3DKoQfUn2gXKuU6CFvhtMrLgg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "918b760070bb8f48cb511300fcd7e02e13058a2e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-22.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unpatched": {
|
||||
"locked": {
|
||||
"lastModified": 1674641431,
|
||||
"narHash": "sha256-qfo19qVZBP4qn5M5gXc/h1MDgAtPA5VxJm9s8RUAkVk=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9b97ad7b4330aacda9b2343396eb3df8a853b4fc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager",
|
||||
"impermanence": "impermanence",
|
||||
"mobile-nixos": "mobile-nixos",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-stable": "nixpkgs-stable",
|
||||
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||
"sops-nix": "sops-nix",
|
||||
"uninsane": "uninsane"
|
||||
"uninsane-dot-org": "uninsane-dot-org"
|
||||
}
|
||||
},
|
||||
"sops-nix": {
|
||||
@@ -129,14 +133,14 @@
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-22_05": "nixpkgs-22_05"
|
||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1669714206,
|
||||
"narHash": "sha256-9aiMbzRL8REsyi9U0eZ+lT4s7HaILA1gh9n2apKzLxU=",
|
||||
"lastModified": 1674546403,
|
||||
"narHash": "sha256-vkyNv0xzXuEnu9v52TUtRugNmQWIti8c2RhYnbLG71w=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "8295b8139ef7baadeb90c5cad7a40c4c9297ebf7",
|
||||
"rev": "b6ab3c61e2ca5e07d1f4eb1b67304e2670ea230c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -145,7 +149,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"uninsane": {
|
||||
"uninsane-dot-org": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
|
228
flake.nix
228
flake.nix
@@ -1,25 +1,48 @@
|
||||
# docs:
|
||||
# - <https://nixos.wiki/wiki/Flakes>
|
||||
# FLAKE FEEDBACK:
|
||||
# - if flake inputs are meant to be human-readable, a human should be able to easily track them down given the URL.
|
||||
# - this is not the case with registry URLs, like `nixpkgs/nixos-22.11`.
|
||||
# - this is marginally the case with schemes like `github:nixos/nixpkgs`.
|
||||
# - given the *existing* `git+https://` scheme, i propose expressing github URLs similarly:
|
||||
# - `github+https://github.com/nixos/nixpkgs/tree/nixos-22.11`
|
||||
# - need some way to apply local patches to inputs.
|
||||
#
|
||||
#
|
||||
# DEVELOPMENT DOCS:
|
||||
# - Flake docs: <https://nixos.wiki/wiki/Flakes>
|
||||
# - Flake RFC: <https://github.com/tweag/rfcs/blob/flakes/rfcs/0049-flakes.md>
|
||||
# - Discussion: <https://github.com/NixOS/rfcs/pull/49>
|
||||
# - <https://serokell.io/blog/practical-nix-flakes>
|
||||
|
||||
{
|
||||
# XXX: use the `github:` scheme instead of the more readable git+https: because it's *way* more efficient
|
||||
# preferably, i would rewrite the human-readable https URLs to nix-specific github: URLs with a helper,
|
||||
# but `inputs` is required to be a strict attrset: not an expression.
|
||||
inputs = {
|
||||
nixpkgs-stable.url = "nixpkgs/nixos-22.05";
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
# <https://github.com/nixos/nixpkgs/tree/nixos-22.11>
|
||||
nixpkgs-stable.url = "github:nixos/nixpkgs?ref=nixos-22.11";
|
||||
|
||||
# <https://github.com/nixos/nixpkgs/tree/nixos-unstable>
|
||||
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
nixpkgs = {
|
||||
url = "./nixpatches";
|
||||
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||
};
|
||||
mobile-nixos = {
|
||||
# <https://github.com/nixos/mobile-nixos>
|
||||
url = "github:nixos/mobile-nixos";
|
||||
flake = false;
|
||||
};
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-22.05";
|
||||
# <https://github.com/nix-community/home-manager/tree/release-22.05>
|
||||
url = "github:nix-community/home-manager?ref=release-22.05";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
sops-nix = {
|
||||
# <https://github.com/Mic92/sops-nix>
|
||||
url = "github:Mic92/sops-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
impermanence.url = "github:nix-community/impermanence";
|
||||
uninsane = {
|
||||
uninsane-dot-org = {
|
||||
url = "git+https://git.uninsane.org/colin/uninsane";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
@@ -29,56 +52,56 @@
|
||||
self,
|
||||
nixpkgs,
|
||||
nixpkgs-stable,
|
||||
nixpkgs-unpatched,
|
||||
mobile-nixos,
|
||||
home-manager,
|
||||
sops-nix,
|
||||
impermanence,
|
||||
uninsane
|
||||
}: let
|
||||
patchedPkgs = system: nixpkgs.legacyPackages.${system}.applyPatches {
|
||||
name = "nixpkgs-patched-uninsane";
|
||||
src = nixpkgs;
|
||||
patches = import ./nixpatches/list.nix nixpkgs.legacyPackages.${system}.fetchpatch;
|
||||
};
|
||||
# return something which behaves like `pkgs`, for the provided system
|
||||
# `local` = architecture of builder. `target` = architecture of the system beying deployed to
|
||||
nixpkgsFor = local: target: import (patchedPkgs target) { crossSystem = target; localSystem = local; };
|
||||
# evaluate ONLY our overlay, for the provided system
|
||||
customPackagesFor = local: target: import ./pkgs/overlay.nix (nixpkgsFor local target) (nixpkgsFor local target);
|
||||
decl-host = { name, local, target }:
|
||||
uninsane-dot-org
|
||||
}:
|
||||
let
|
||||
nixosSystem = import ((patchedPkgs target) + "/nixos/lib/eval-config.nix");
|
||||
in (nixosSystem {
|
||||
# by default the local system is the same as the target, employing emulation when they differ
|
||||
system = target;
|
||||
specialArgs = { inherit mobile-nixos home-manager impermanence; };
|
||||
modules = [
|
||||
./modules
|
||||
(import ./hosts/instantiate.nix name)
|
||||
home-manager.nixosModule
|
||||
impermanence.nixosModule
|
||||
sops-nix.nixosModules.sops
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(import "${mobile-nixos}/overlay/overlay.nix")
|
||||
uninsane.overlay
|
||||
(import ./pkgs/overlay.nix)
|
||||
(next: prev: rec {
|
||||
# non-emulated packages build *from* local *for* target.
|
||||
# for large packages like the linux kernel which are expensive to build under emulation,
|
||||
# the config can explicitly pull such packages from `pkgs.cross` to do more efficient cross-compilation.
|
||||
cross = (nixpkgsFor local target) // (customPackagesFor local target);
|
||||
stable = import nixpkgs-stable { system = target; };
|
||||
# cross-compatible packages
|
||||
# gocryptfs = cross.gocryptfs;
|
||||
})
|
||||
];
|
||||
}
|
||||
];
|
||||
});
|
||||
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
|
||||
|
||||
decl-bootable-host = { name, local, target }: rec {
|
||||
nixosConfiguration = decl-host { inherit name local target; };
|
||||
evalHost = { name, local, target }:
|
||||
let
|
||||
# XXX: we'd prefer to use `nixosSystem = (nixpkgsCompiledBy target).nixos`
|
||||
# but it doesn't propagate config to the underlying pkgs, meaning it doesn't let you use
|
||||
# non-free packages even after setting nixpkgs.allowUnfree.
|
||||
# XXX: patch using the target -- not local -- otherwise the target will
|
||||
# need to emulate the host in order to rebuild!
|
||||
nixosSystem = import ((nixpkgsCompiledBy target).path + "/nixos/lib/eval-config.nix");
|
||||
in
|
||||
(nixosSystem {
|
||||
# we use pkgs built for and *by* the target, i.e. emulation, by default.
|
||||
# cross compilation only happens on explicit access to `pkgs.cross`
|
||||
system = target;
|
||||
modules = [
|
||||
(import ./hosts/instantiate.nix { localSystem = local; hostName = name; })
|
||||
self.nixosModules.default
|
||||
self.nixosModules.passthru
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
self.overlays.default
|
||||
self.overlays.passthru
|
||||
self.overlays.pins
|
||||
];
|
||||
}
|
||||
];
|
||||
});
|
||||
in {
|
||||
nixosConfigurations = {
|
||||
servo = evalHost { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
desko = evalHost { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
lappy = evalHost { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
moby = evalHost { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
|
||||
# special cross-compiled variant, to speed up deploys from an x86 box to the arm target
|
||||
# note that these *do* produce different store paths, because the closure for the tools used to cross compile
|
||||
# v.s. emulate differ.
|
||||
# so deploying foo-cross and then foo incurs some rebuilding.
|
||||
moby-cross = evalHost { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
||||
rescue = evalHost { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
};
|
||||
|
||||
# unofficial output
|
||||
# this produces a EFI-bootable .img file (GPT with a /boot partition and a system (/ or /nix) partition).
|
||||
# after building this:
|
||||
# - flash it to a bootable medium (SD card, flash drive, HDD)
|
||||
@@ -92,40 +115,77 @@
|
||||
# - if fs wasn't resized automatically, then `sudo btrfs filesystem resize max /`
|
||||
# - checkout this flake into /etc/nixos AND UPDATE THE FS UUIDS.
|
||||
# - `nixos-rebuild --flake './#<host>' switch`
|
||||
img = nixosConfiguration.config.system.build.img;
|
||||
};
|
||||
hosts.servo = decl-bootable-host { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
hosts.desko = decl-bootable-host { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
hosts.lappy = decl-bootable-host { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
hosts.moby = decl-bootable-host { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
|
||||
# special cross-compiled variant, to speed up deploys from an x86 box to the arm target
|
||||
# note that these *do* produce different store paths, because the closure for the tools used to cross compile
|
||||
# v.s. emulate differ.
|
||||
# so deploying foo-cross and then foo incurs some rebuilding.
|
||||
hosts.moby-cross = decl-bootable-host { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
||||
hosts.rescue = decl-bootable-host { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
in {
|
||||
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) hosts;
|
||||
imgs = builtins.mapAttrs (name: value: value.img) hosts;
|
||||
packages = let
|
||||
allPkgsFor = sys: (customPackagesFor sys sys) // {
|
||||
nixpkgs = nixpkgsFor sys sys;
|
||||
uninsane = uninsane.packages."${sys}";
|
||||
imgs = builtins.mapAttrs (_: host-dfn: host-dfn.config.system.build.img) self.nixosConfigurations;
|
||||
|
||||
overlays = rec {
|
||||
default = pkgs;
|
||||
pkgs = import ./overlays/pkgs.nix;
|
||||
pins = import ./overlays/pins.nix; # TODO: move to `nixpatches/` input
|
||||
passthru =
|
||||
let
|
||||
stable = next: prev: {
|
||||
stable = nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform.system}";
|
||||
};
|
||||
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||
uninsane = uninsane-dot-org.overlay;
|
||||
in
|
||||
next: prev:
|
||||
(stable next prev) // (mobile next prev) // (uninsane next prev);
|
||||
};
|
||||
in {
|
||||
x86_64-linux = allPkgsFor "x86_64-linux";
|
||||
aarch64-linux = allPkgsFor "aarch64-linux";
|
||||
};
|
||||
templates = {
|
||||
python-data = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#python-data'`
|
||||
# then enter with:
|
||||
# - `nix develop`
|
||||
path = ./templates/python-data;
|
||||
description = "python environment for data processing";
|
||||
|
||||
nixosModules = rec {
|
||||
default = sane;
|
||||
sane = import ./modules;
|
||||
passthru = { ... }: {
|
||||
imports = [
|
||||
home-manager.nixosModule
|
||||
sops-nix.nixosModules.sops
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# this includes both our native packages and all the nixpkgs packages.
|
||||
legacyPackages =
|
||||
let
|
||||
allPkgsFor = sys: (nixpkgsCompiledBy sys).appendOverlays [
|
||||
self.overlays.passthru self.overlays.pkgs
|
||||
];
|
||||
in {
|
||||
x86_64-linux = allPkgsFor "x86_64-linux";
|
||||
aarch64-linux = allPkgsFor "aarch64-linux";
|
||||
};
|
||||
|
||||
# extract only our own packages from the full set
|
||||
packages = builtins.mapAttrs
|
||||
(_: full: full.sane // { inherit (full) sane uninsane-dot-org; })
|
||||
self.legacyPackages;
|
||||
|
||||
apps."x86_64-linux" =
|
||||
let
|
||||
pkgs = self.legacyPackages."x86_64-linux";
|
||||
in {
|
||||
update-feeds = {
|
||||
type = "app";
|
||||
program = "${pkgs.feeds.passthru.updateScript}";
|
||||
};
|
||||
|
||||
init-feed = {
|
||||
# use like `nix run '.#init-feed' uninsane.org`
|
||||
type = "app";
|
||||
program = "${pkgs.feeds.passthru.initFeedScript}";
|
||||
};
|
||||
};
|
||||
|
||||
templates = {
|
||||
python-data = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#python-data'`
|
||||
# then enter with:
|
||||
# - `nix develop`
|
||||
path = ./templates/python-data;
|
||||
description = "python environment for data processing";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -6,25 +6,30 @@
|
||||
|
||||
# sane.packages.enableDevPkgs = true;
|
||||
|
||||
sane.gui.sway.enable = true;
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
|
||||
sane.services.duplicity.enable = true;
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../secrets/desko.yaml;
|
||||
sane.impermanence.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../../secrets/desko.yaml;
|
||||
sane.persist.enable = true;
|
||||
|
||||
sane.gui.sway.enable = true;
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
# needed to use libimobiledevice/ifuse, for iphone sync
|
||||
services.usbmuxd.enable = true;
|
||||
users.users.usbmux.uid = config.sane.allocations.usbmux-uid;
|
||||
users.groups.usbmux.gid = config.sane.allocations.usbmux-gid;
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/desko.yaml;
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
||||
# don't enable wifi by default: it messes with connectivity.
|
||||
systemd.services.iwd.enable = false;
|
||||
|
||||
# default config: https://man.archlinux.org/man/snapper-configs.5
|
||||
# defaults to something like:
|
||||
# - hourly snapshots
|
||||
@@ -40,7 +45,7 @@
|
||||
};
|
||||
|
||||
sops.secrets.duplicity_passphrase = {
|
||||
sopsFile = ../../secrets/desko.yaml;
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
};
|
||||
|
||||
programs.steam = {
|
||||
@@ -49,7 +54,7 @@
|
||||
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
||||
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
||||
};
|
||||
sane.impermanence.home-dirs = [
|
||||
sane.persist.home.plaintext = [
|
||||
".steam"
|
||||
".local/share/Steam"
|
||||
];
|
@@ -1,16 +1,7 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
||||
fileSystems."/" = {
|
||||
device = "none";
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"mode=755"
|
||||
"size=1G"
|
||||
"defaults"
|
||||
];
|
||||
};
|
||||
sane.persist.root-on-tmpfs = true;
|
||||
# we need a /tmp for building large nix things.
|
||||
# a cross-compiled kernel, particularly, will easily use 30+GB of tmp
|
||||
fileSystems."/tmp" = {
|
@@ -1,20 +1,24 @@
|
||||
{ pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
|
||||
|
||||
# sane.packages.enableDevPkgs = true;
|
||||
|
||||
# sane.users.guest.enable = true;
|
||||
sane.gui.sway.enable = true;
|
||||
sane.impermanence.enable = true;
|
||||
sane.persist.enable = true;
|
||||
sane.nixcache.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/lappy.yaml;
|
||||
sopsFile = ../../../secrets/lappy.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
@@ -1,16 +1,7 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
||||
fileSystems."/" = {
|
||||
device = "none";
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"mode=755"
|
||||
"size=1G"
|
||||
"defaults"
|
||||
];
|
||||
};
|
||||
sane.persist.root-on-tmpfs = true;
|
||||
# we need a /tmp of default size (half RAM) for building large nix things
|
||||
fileSystems."/tmp" = {
|
||||
device = "none";
|
@@ -1,4 +1,4 @@
|
||||
{ config, pkgs, lib, mobile-nixos, ... }:
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
imports = [
|
||||
./firmware.nix
|
||||
@@ -6,6 +6,10 @@
|
||||
./kernel.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
|
||||
|
||||
# cross-compiled documentation is *slow*.
|
||||
# no obvious way to natively compile docs (2022/09/29).
|
||||
# entrypoint is nixos/modules/misc/documentation.nix
|
||||
@@ -19,13 +23,21 @@
|
||||
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/moby.yaml;
|
||||
sopsFile = ../../../secrets/moby.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
||||
# usability compromises
|
||||
sane.impermanence.home-dirs = [
|
||||
config.sane.web-browser.dotDir
|
||||
sane.web-browser = {
|
||||
# compromise impermanence for the sake of usability
|
||||
persistCache = "private";
|
||||
persistData = "private";
|
||||
|
||||
# i don't do crypto stuff on moby
|
||||
addons.ether-metamask.enable = false;
|
||||
# addons.sideberry.enable = false;
|
||||
};
|
||||
|
||||
sane.persist.home.plaintext = [
|
||||
".config/pulse" # persist pulseaudio volume
|
||||
];
|
||||
|
||||
@@ -35,12 +47,14 @@
|
||||
];
|
||||
|
||||
sane.nixcache.enable = true;
|
||||
sane.impermanence.enable = true;
|
||||
sane.persist.enable = true;
|
||||
sane.gui.phosh.enable = true;
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
# /boot space is at a premium. default was 20.
|
||||
boot.loader.generic-extlinux-compatible.configurationLimit = 10;
|
||||
# even 10 can be too much
|
||||
# TODO: compress moby kernels!
|
||||
boot.loader.generic-extlinux-compatible.configurationLimit = 8;
|
||||
# mobile.bootloader.enable = false;
|
||||
# mobile.boot.stage-1.enable = false;
|
||||
# boot.initrd.systemd.enable = false;
|
||||
@@ -51,9 +65,10 @@
|
||||
|
||||
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
|
||||
# this is because they can't allocate enough video ram.
|
||||
# the default CMA seems to be 32M. we could probably get by with as little as 64M, and safely with 128M.
|
||||
# the default CMA seems to be 32M.
|
||||
# i was running fine with 256MB from 2022/07-ish through 2022/12-ish, but then the phone quit reliably coming back from sleep: maybe a memory leak?
|
||||
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
||||
boot.kernelParams = [ "cma=256M" ];
|
||||
boot.kernelParams = [ "cma=512M" ];
|
||||
|
||||
# mobile-nixos' /lib/firmware includes:
|
||||
# rtl_bt (bluetooth)
|
@@ -1,17 +1,7 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
||||
fileSystems."/" = {
|
||||
device = "none";
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"mode=755"
|
||||
"size=1G"
|
||||
"defaults"
|
||||
];
|
||||
};
|
||||
|
||||
sane.persist.root-on-tmpfs = true;
|
||||
fileSystems."/nix" = {
|
||||
device = "/dev/disk/by-uuid/1f1271f8-53ce-4081-8a29-60a4a6b5d6f9";
|
||||
fsType = "btrfs";
|
@@ -125,6 +125,9 @@ in
|
||||
# aarch64-unknown-linux-gnu-gcc: error: unrecognized command line option '-mfpu=neon'
|
||||
# make[3]: *** [../scripts/Makefile.build:289: drivers/video/fbdev/sun5i-eink-neon.o] Error 1
|
||||
FB_SUN5I_EINK = no;
|
||||
# used by the pinephone pro, but fails to compile with:
|
||||
# ../drivers/media/i2c/ov8858.c:1834:27: error: implicit declaration of function 'compat_ptr'
|
||||
VIDEO_OV8858 = no;
|
||||
})
|
||||
))
|
||||
];
|
@@ -8,9 +8,6 @@
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
users.users.dhcpcd.uid = config.sane.allocations.dhcpcd-uid;
|
||||
users.groups.dhcpcd.gid = config.sane.allocations.dhcpcd-gid;
|
||||
|
||||
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
||||
system.stateVersion = "21.05";
|
||||
}
|
@@ -1,30 +1,32 @@
|
||||
{ pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
./net.nix
|
||||
./users.nix
|
||||
./secrets.nix
|
||||
./services
|
||||
];
|
||||
|
||||
sane.packages.extraUserPkgs = [
|
||||
sane.packages.extraUserPkgs = with pkgs; [
|
||||
# for administering services
|
||||
pkgs.matrix-synapse
|
||||
pkgs.freshrss
|
||||
freshrss
|
||||
matrix-synapse
|
||||
signaldctl
|
||||
];
|
||||
sane.impermanence.enable = true;
|
||||
sane.persist.enable = true;
|
||||
sane.services.dyn-dns.enable = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../secrets/servo.yaml;
|
||||
|
||||
# automatically log in at the virtual consoles.
|
||||
# using root here makes sure we always have an escape hatch
|
||||
services.getty.autologinUser = "root";
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
sops.secrets.duplicity_passphrase = {
|
||||
sopsFile = ../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
# both transmission and ipfs try to set different net defaults.
|
||||
# we just use the most aggressive of the two here:
|
||||
boot.kernel.sysctl = {
|
@@ -1,16 +1,7 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
||||
fileSystems."/" = {
|
||||
device = "none";
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"mode=755"
|
||||
"size=1G"
|
||||
"defaults"
|
||||
];
|
||||
};
|
||||
sane.persist.root-on-tmpfs = true;
|
||||
# we need a /tmp for building large nix things
|
||||
fileSystems."/tmp" = {
|
||||
device = "none";
|
||||
@@ -36,7 +27,7 @@
|
||||
};
|
||||
|
||||
# slow, external storage (for archiving, etc)
|
||||
fileSystems."/nix/persist/ext" = {
|
||||
fileSystems."/mnt/persist/ext" = {
|
||||
device = "/dev/disk/by-uuid/aa272cff-0fcc-498e-a4cb-0d95fb60631b";
|
||||
fsType = "btrfs";
|
||||
options = [
|
||||
@@ -45,27 +36,31 @@
|
||||
];
|
||||
};
|
||||
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.stores."ext" = {
|
||||
origin = "/mnt/persist/ext/persist";
|
||||
storeDescription = "external HDD storage";
|
||||
};
|
||||
sane.fs."/mnt/persist/ext".mount = {};
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: this is overly broad; only need media and share directories to be persisted
|
||||
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
|
||||
];
|
||||
# direct these media directories to external storage
|
||||
environment.persistence."/nix/persist/ext/persist" = {
|
||||
directories = [
|
||||
({
|
||||
user = "colin";
|
||||
group = "users";
|
||||
mode = "0777";
|
||||
directory = "/var/lib/uninsane/media/Videos";
|
||||
})
|
||||
({
|
||||
user = "colin";
|
||||
group = "users";
|
||||
mode = "0777";
|
||||
directory = "/var/lib/uninsane/media/freeleech";
|
||||
})
|
||||
];
|
||||
};
|
||||
# make sure large media is stored to the HDD
|
||||
sane.persist.sys.ext = [
|
||||
{
|
||||
user = "colin";
|
||||
group = "users";
|
||||
mode = "0777";
|
||||
directory = "/var/lib/uninsane/media/Videos";
|
||||
}
|
||||
{
|
||||
user = "colin";
|
||||
group = "users";
|
||||
mode = "0777";
|
||||
directory = "/var/lib/uninsane/media/freeleech";
|
||||
}
|
||||
];
|
||||
|
||||
# in-memory compressed RAM (seems to be dynamically sized)
|
||||
# zramSwap = {
|
209
hosts/by-name/servo/net.nix
Normal file
209
hosts/by-name/servo/net.nix
Normal file
@@ -0,0 +1,209 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
networking.domain = "uninsane.org";
|
||||
|
||||
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
|
||||
# Per-interface useDHCP will be mandatory in the future, so this generated config
|
||||
# replicates the default behaviour.
|
||||
networking.useDHCP = false;
|
||||
networking.interfaces.eth0.useDHCP = true;
|
||||
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
|
||||
networking.wireless.enable = false;
|
||||
|
||||
# networking.firewall.enable = false;
|
||||
networking.firewall.enable = true;
|
||||
|
||||
# this is needed to forward packets from the VPN to the host
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||
|
||||
# unless we add interface-specific settings for each VPN, we have to define nameservers globally.
|
||||
# networking.nameservers = [
|
||||
# "1.1.1.1"
|
||||
# "9.9.9.9"
|
||||
# ];
|
||||
|
||||
# use systemd's stub resolver.
|
||||
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
|
||||
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
|
||||
# in the ovnps namespace to use the provider's DNS resolvers.
|
||||
# a weakness is we can only query 1 NS at a time (unless we were to clone the packets?)
|
||||
# there also seems to be some cache somewhere that's shared between the two namespaces.
|
||||
# i think this is a libc thing. might need to leverage proper cgroups to _really_ kill it.
|
||||
# - getent ahostsv4 www.google.com
|
||||
# - try fix: <https://serverfault.com/questions/765989/connect-to-3rd-party-vpn-server-but-dont-use-it-as-the-default-route/766290#766290>
|
||||
services.resolved.enable = true;
|
||||
networking.nameservers = [
|
||||
# use systemd-resolved resolver
|
||||
# full resolver (which understands /etc/hosts) lives on 127.0.0.53
|
||||
# stub resolver (just forwards upstream) lives on 127.0.0.54
|
||||
"127.0.0.53"
|
||||
];
|
||||
|
||||
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
|
||||
# in a way that's unaware of my VPN routing, so routes are frequently poor against
|
||||
# services which advertise different IPs based on geolocation.
|
||||
# nscd claims to be usable without a cache, but in practice i can't get it to not cache!
|
||||
# nsncd is the Name Service NON-Caching Daemon. it's a drop-in that doesn't cache;
|
||||
# this is OK on the host -- because systemd-resolved caches. it's probably sub-optimal
|
||||
# in the netns and we query upstream DNS more often than needed. hm.
|
||||
# TODO: run a separate recursive resolver in each namespace.
|
||||
services.nscd.enableNsncd = true;
|
||||
|
||||
# services.resolved.extraConfig = ''
|
||||
# # docs: `man resolved.conf`
|
||||
# # DNS servers to use via the `wg-ovpns` interface.
|
||||
# # i hope that from the root ns, these aren't visible.
|
||||
# DNS=46.227.67.134%wg-ovpns 192.165.9.158%wg-ovpns
|
||||
# FallbackDNS=1.1.1.1 9.9.9.9
|
||||
# '';
|
||||
|
||||
# OVPN CONFIG (https://www.ovpn.com):
|
||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
||||
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
||||
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
||||
networking.wireguard.enable = true;
|
||||
networking.wireguard.interfaces.wg-ovpns = let
|
||||
ip = "${pkgs.iproute2}/bin/ip";
|
||||
in-ns = "${ip} netns exec ovpns";
|
||||
iptables = "${pkgs.iptables}/bin/iptables";
|
||||
veth-host-ip = "10.0.1.5";
|
||||
veth-local-ip = "10.0.1.6";
|
||||
vpn-ip = "185.157.162.178";
|
||||
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
|
||||
vpn-dns = "46.227.67.134";
|
||||
in {
|
||||
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||
# wg is active only in this namespace.
|
||||
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
|
||||
# sudo ip netns exec ovpns ping www.google.com
|
||||
interfaceNamespace = "ovpns";
|
||||
ips = [
|
||||
"185.157.162.178/32"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
||||
endpoint = "185.157.162.10:9930";
|
||||
# alternatively: use hostname, but that presents bootstrapping issues (e.g. if host net flakes)
|
||||
# endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
|
||||
allowedIPs = [ "0.0.0.0/0" ];
|
||||
# nixOS says this is important for keeping NATs active
|
||||
persistentKeepalive = 25;
|
||||
# re-executes wg this often. docs hint that this might help wg notice DNS/hostname changes.
|
||||
# so, maybe that helps if we specify endpoint as a domain name
|
||||
# dynamicEndpointRefreshSeconds = 30;
|
||||
# when refresh fails, try it again after this period instead.
|
||||
# TODO: not avail until nixpkgs upgrade
|
||||
# dynamicEndpointRefreshRestartSeconds = 5;
|
||||
}
|
||||
];
|
||||
preSetup = "" + ''
|
||||
${ip} netns add ovpns || echo "ovpns already exists"
|
||||
'';
|
||||
postShutdown = "" + ''
|
||||
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||
# restore rules/routes
|
||||
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
|
||||
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
|
||||
${ip} rule add from all lookup local pref 0
|
||||
${ip} rule del from all lookup local pref 100
|
||||
'';
|
||||
postSetup = "" + ''
|
||||
# DOCS:
|
||||
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
||||
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
||||
# create veth pair
|
||||
${ip} link add ovpns-veth-a type veth peer name ovpns-veth-b
|
||||
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
|
||||
${ip} link set ovpns-veth-a up
|
||||
|
||||
# mv veth-b into the ovpns namespace
|
||||
${ip} link set ovpns-veth-b netns ovpns
|
||||
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
|
||||
${in-ns} ip link set ovpns-veth-b up
|
||||
|
||||
# make it so traffic originating from the host side of the veth
|
||||
# is sent over the veth no matter its destination.
|
||||
${ip} rule add from ${veth-host-ip} lookup ovpns pref 50
|
||||
# for traffic originating at the host veth to the WAN, use the veth as our gateway
|
||||
# not sure if the metric 1002 matters.
|
||||
${ip} route add default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
|
||||
# give the default route lower priority
|
||||
${ip} rule add from all lookup local pref 100
|
||||
${ip} rule del from all lookup local pref 0
|
||||
|
||||
# bridge HTTP traffic:
|
||||
# any external port-80 request sent to the VPN addr will be forwarded to the rootns.
|
||||
# this exists so LetsEncrypt can procure a cert for the MX over http.
|
||||
# TODO: we could use _acme_challence.mx.uninsane.org CNAME to avoid this forwarding
|
||||
# - <https://community.letsencrypt.org/t/where-does-letsencrypt-resolve-dns-from/37607/8>
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 80 -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}:80
|
||||
|
||||
# we also bridge DNS traffic
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p udp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}:53
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}:53
|
||||
|
||||
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
||||
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
||||
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
|
||||
-j DNAT --to-destination ${vpn-dns}:53
|
||||
'';
|
||||
};
|
||||
|
||||
# create a new routing table that we can use to proxy traffic out of the root namespace
|
||||
# through the ovpns namespace, and to the WAN via VPN.
|
||||
networking.iproute2.rttablesExtraConfig = ''
|
||||
5 ovpns
|
||||
'';
|
||||
networking.iproute2.enable = true;
|
||||
|
||||
|
||||
# HURRICANE ELECTRIC CONFIG:
|
||||
# networking.sits = {
|
||||
# hurricane = {
|
||||
# remote = "216.218.226.238";
|
||||
# local = "192.168.0.5";
|
||||
# # local = "10.0.0.5";
|
||||
# # remote = "10.0.0.1";
|
||||
# # local = "10.0.0.22";
|
||||
# dev = "eth0";
|
||||
# ttl = 255;
|
||||
# };
|
||||
# };
|
||||
# networking.interfaces."hurricane".ipv6 = {
|
||||
# addresses = [
|
||||
# # mx.uninsane.org (publically routed /64)
|
||||
# {
|
||||
# address = "2001:470:b:465::1";
|
||||
# prefixLength = 128;
|
||||
# }
|
||||
# # client addr
|
||||
# # {
|
||||
# # address = "2001:470:a:466::2";
|
||||
# # prefixLength = 64;
|
||||
# # }
|
||||
# ];
|
||||
# routes = [
|
||||
# {
|
||||
# address = "::";
|
||||
# prefixLength = 0;
|
||||
# # via = "2001:470:a:466::1";
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
|
||||
# # after configuration, we want the hurricane device to look like this:
|
||||
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
|
||||
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
|
||||
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
|
||||
# # sit txqueuelen 1000 (IPv6-in-IPv4)
|
||||
# # test with:
|
||||
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
||||
# # ping 2607:f8b0:400a:80b::2004
|
||||
}
|
41
hosts/by-name/servo/secrets.nix
Normal file
41
hosts/by-name/servo/secrets.nix
Normal file
@@ -0,0 +1,41 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sops.secrets."ddns_afraid" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."ddns_he" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."dovecot_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."duplicity_passphrase" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."mautrix_signal_env" = {
|
||||
sopsFile = ../../../secrets/servo/mautrix_signal_env.bin;
|
||||
};
|
||||
|
||||
sops.secrets."mediawiki_pw" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."wg_ovpns_privkey" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
27
hosts/by-name/servo/services/ddns-afraid.nix
Normal file
27
hosts/by-name/servo/services/ddns-afraid.nix
Normal file
@@ -0,0 +1,27 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
# using manual ddns now
|
||||
lib.mkIf false
|
||||
{
|
||||
systemd.services.ddns-afraid = {
|
||||
description = "update dynamic DNS entries for freedns.afraid.org";
|
||||
serviceConfig = {
|
||||
EnvironmentFile = config.sops.secrets.ddns_afraid.path;
|
||||
# TODO: ProtectSystem = "strict";
|
||||
# TODO: ProtectHome = "full";
|
||||
# TODO: PrivateTmp = true;
|
||||
};
|
||||
script = let
|
||||
curl = "${pkgs.curl}/bin/curl -4";
|
||||
in ''
|
||||
${curl} "https://freedns.afraid.org/dynamic/update.php?$AFRAID_KEY"
|
||||
'';
|
||||
};
|
||||
systemd.timers.ddns-afraid = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
timerConfig = {
|
||||
OnStartupSec = "2min";
|
||||
OnUnitActiveSec = "10min";
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
# we use manual DDNS now
|
||||
lib.mkIf false
|
||||
{
|
||||
systemd.services.ddns-he = {
|
||||
description = "update dynamic DNS entries for HurricaneElectric";
|
||||
@@ -25,8 +27,4 @@
|
||||
OnUnitActiveSec = "10min";
|
||||
};
|
||||
};
|
||||
|
||||
sops.secrets."ddns_he" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./ddns-afraid.nix
|
||||
./ddns-he.nix
|
||||
./ejabberd.nix
|
||||
./freshrss.nix
|
||||
@@ -9,13 +10,17 @@
|
||||
./ipfs.nix
|
||||
./jackett.nix
|
||||
./jellyfin.nix
|
||||
./kiwix-serve.nix
|
||||
./matrix
|
||||
./navidrome.nix
|
||||
./nixserve.nix
|
||||
./nginx.nix
|
||||
./pleroma.nix
|
||||
./postfix.nix
|
||||
./postgres.nix
|
||||
./prosody.nix
|
||||
./transmission.nix
|
||||
./trust-dns.nix
|
||||
./wikipedia.nix
|
||||
];
|
||||
}
|
395
hosts/by-name/servo/services/ejabberd.nix
Normal file
395
hosts/by-name/servo/services/ejabberd.nix
Normal file
@@ -0,0 +1,395 @@
|
||||
# docs:
|
||||
# - <https://docs.ejabberd.im/admin/configuration/basic>
|
||||
# example configs:
|
||||
# - <https://github.com/vkleen/machines/blob/138a2586ce185d7cf201d4e1fe898c83c4af52eb/hosts/europium/ejabberd.nix>
|
||||
# - <https://github.com/Mic92/stockholm/blob/675ef0088624c9de1cb531f318446316884a9d3d/tv/3modules/ejabberd/default.nix>
|
||||
# - <https://github.com/buffet/tararice/blob/master/programs/ejabberd.nix>
|
||||
# - enables STUN and TURN
|
||||
# - only over UDP 3478, not firewall-forwarding any TURN port range
|
||||
# - uses stun_disco module (but with no options)
|
||||
# - <https://github.com/leo60228/dotfiles/blob/39b3abba3009bdc31413d4757ca2f882a33eec8b/files/ejabberd.yml>
|
||||
# - <https://github.com/Mic92/dotfiles/blob/ddf0f4821f554f7667fc803344657367c55fb9e6/nixos/eve/modules/ejabberd.nix>
|
||||
# - <nixpkgs:nixos/tests/xmpp/ejabberd.nix>
|
||||
# - 2013: <https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example>
|
||||
#
|
||||
# compliance tests:
|
||||
# - <https://compliance.conversations.im/server/uninsane.org/#xep0352>
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
# XXX: avatar support works in MUCs but not DMs
|
||||
# lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
3478 # STUN/TURN
|
||||
5222 # XMPP client -> server
|
||||
5223 # XMPPS client -> server (XMPP over TLS)
|
||||
5269 # XMPP server -> server
|
||||
5270 # XMPPS server -> server (XMPP over TLS)
|
||||
5280 # bosh
|
||||
5281 # bosh (https) ??
|
||||
5349 # STUN/TURN (TLS)
|
||||
5443 # web services (file uploads, websockets, admin)
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
3478 # STUN/TURN
|
||||
];
|
||||
networking.firewall.allowedTCPPortRanges = [{
|
||||
from = 49152; # TURN
|
||||
to = 65535;
|
||||
}];
|
||||
networking.firewall.allowedUDPPortRanges = [{
|
||||
from = 49152; # TURN
|
||||
to = 65535;
|
||||
}];
|
||||
|
||||
# provide access to certs
|
||||
# TODO: this should just be `acme`. then we also add nginx to the `acme` group.
|
||||
# why is /var/lib/acme/* owned by `nginx` group??
|
||||
users.users.ejabberd.extraGroups = [ "nginx" ];
|
||||
|
||||
security.acme.certs."uninsane.org".extraDomainNames = [
|
||||
"xmpp.uninsane.org"
|
||||
"muc.xmpp.uninsane.org"
|
||||
"pubsub.xmpp.uninsane.org"
|
||||
"upload.xmpp.uninsane.org"
|
||||
"vjid.xmpp.uninsane.org"
|
||||
];
|
||||
|
||||
# exists so the XMPP server's cert can obtain altNames for all its resources
|
||||
services.nginx.virtualHosts."xmpp.uninsane.org" = {
|
||||
useACMEHost = "uninsane.org";
|
||||
};
|
||||
services.nginx.virtualHosts."muc.xmpp.uninsane.org" = {
|
||||
useACMEHost = "uninsane.org";
|
||||
};
|
||||
services.nginx.virtualHosts."pubsub.xmpp.uninsane.org" = {
|
||||
useACMEHost = "uninsane.org";
|
||||
};
|
||||
services.nginx.virtualHosts."upload.xmpp.uninsane.org" = {
|
||||
useACMEHost = "uninsane.org";
|
||||
};
|
||||
services.nginx.virtualHosts."vjid.xmpp.uninsane.org" = {
|
||||
useACMEHost = "uninsane.org";
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
|
||||
A."xmpp" = "%NATIVE%";
|
||||
CNAME."muc.xmpp" = "xmpp";
|
||||
CNAME."pubsub.xmpp" = "xmpp";
|
||||
CNAME."upload.xmpp" = "xmpp";
|
||||
CNAME."vjid.xmpp" = "xmpp";
|
||||
|
||||
# _Service._Proto.Name TTL Class SRV Priority Weight Port Target
|
||||
# - <https://xmpp.org/extensions/xep-0368.html>
|
||||
# something's requesting the SRV records for muc.xmpp, so let's include it
|
||||
# nothing seems to request XMPP SRVs for the other records (except @)
|
||||
# lower numerical priority field tells clients to prefer this method
|
||||
SRV."_xmpps-client._tcp.muc.xmpp" = "3 50 5223 xmpp";
|
||||
SRV."_xmpps-server._tcp.muc.xmpp" = "3 50 5270 xmpp";
|
||||
SRV."_xmpp-client._tcp.muc.xmpp" = "5 50 5222 xmpp";
|
||||
SRV."_xmpp-server._tcp.muc.xmpp" = "5 50 5269 xmpp";
|
||||
|
||||
SRV."_xmpps-client._tcp" = "3 50 5223 xmpp";
|
||||
SRV."_xmpps-server._tcp" = "3 50 5270 xmpp";
|
||||
SRV."_xmpp-client._tcp" = "5 50 5222 xmpp";
|
||||
SRV."_xmpp-server._tcp" = "5 50 5269 xmpp";
|
||||
|
||||
SRV."_stun._udp" = "5 50 3478 xmpp";
|
||||
SRV."_stun._tcp" = "5 50 3478 xmpp";
|
||||
SRV."_stuns._tcp" = "5 50 5349 xmpp";
|
||||
SRV."_turn._udp" = "5 50 3478 xmpp";
|
||||
SRV."_turn._tcp" = "5 50 3478 xmpp";
|
||||
SRV."_turns._tcp" = "5 50 5349 xmpp";
|
||||
};
|
||||
|
||||
# TODO: allocate UIDs/GIDs ?
|
||||
services.ejabberd.enable = true;
|
||||
services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml";
|
||||
systemd.services.ejabberd.preStart = let
|
||||
config-in = pkgs.writeTextFile {
|
||||
name = "ejabberd.yaml.in";
|
||||
text = ''
|
||||
hosts:
|
||||
- uninsane.org
|
||||
|
||||
# none | emergency | alert | critical | error | warning | notice | info | debug
|
||||
loglevel: debug
|
||||
# loglevel: info
|
||||
# loglevel: notice
|
||||
|
||||
acme:
|
||||
auto: false
|
||||
certfiles:
|
||||
- /var/lib/acme/uninsane.org/full.pem
|
||||
# ca_file: ${pkgs.cacert.unbundled}/etc/ssl/certs/
|
||||
# ca_file: ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
|
||||
|
||||
pam_userinfotype: jid
|
||||
|
||||
acl:
|
||||
admin:
|
||||
user:
|
||||
- "colin@uninsane.org"
|
||||
local:
|
||||
user_regexp: ""
|
||||
loopback:
|
||||
ip:
|
||||
- 127.0.0.0/8
|
||||
- ::1/128
|
||||
|
||||
access_rules:
|
||||
local:
|
||||
allow: local
|
||||
c2s_access:
|
||||
allow: all
|
||||
announce:
|
||||
allow: admin
|
||||
configure:
|
||||
allow: admin
|
||||
muc_create:
|
||||
allow: local
|
||||
pubsub_createnode_access:
|
||||
allow: all
|
||||
trusted_network:
|
||||
allow: loopback
|
||||
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
|
||||
shaper_rules:
|
||||
# setting this to above 1 may break outgoing messages
|
||||
# - maybe some servers rate limit? or just don't understand simultaneous connections?
|
||||
max_s2s_connections: 1
|
||||
max_user_sessions: 10
|
||||
max_user_offline_messages: 5000
|
||||
c2s_shaper:
|
||||
fast: all
|
||||
s2s_shaper:
|
||||
med: all
|
||||
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
|
||||
# this limits the bytes/sec.
|
||||
# for example, burst: 3_000_000 and rate: 100_000 means:
|
||||
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
|
||||
shaper:
|
||||
fast: 1000000
|
||||
med: 500000
|
||||
# fast:
|
||||
# - rate: 1000000
|
||||
# - burst_size: 10000000
|
||||
# med:
|
||||
# - rate: 500000
|
||||
# - burst_size: 5000000
|
||||
|
||||
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
|
||||
# s2s_use_starttls: true
|
||||
s2s_use_starttls: optional
|
||||
# lessens 504: remote-server-timeout errors
|
||||
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
|
||||
negotiation_timeout: 60
|
||||
|
||||
listen:
|
||||
-
|
||||
port: 5222
|
||||
module: ejabberd_c2s
|
||||
shaper: c2s_shaper
|
||||
starttls: true
|
||||
access: c2s_access
|
||||
-
|
||||
port: 5223
|
||||
module: ejabberd_c2s
|
||||
shaper: c2s_shaper
|
||||
tls: true
|
||||
access: c2s_access
|
||||
-
|
||||
port: 5269
|
||||
module: ejabberd_s2s_in
|
||||
shaper: s2s_shaper
|
||||
-
|
||||
port: 5270
|
||||
module: ejabberd_s2s_in
|
||||
shaper: s2s_shaper
|
||||
tls: true
|
||||
-
|
||||
port: 5443
|
||||
module: ejabberd_http
|
||||
tls: true
|
||||
request_handlers:
|
||||
/admin: ejabberd_web_admin # TODO: ensure this actually works
|
||||
/api: mod_http_api # ejabberd API endpoint (to control server)
|
||||
/bosh: mod_bosh
|
||||
/upload: mod_http_upload
|
||||
/ws: ejabberd_http_ws
|
||||
# /.well-known/host-meta: mod_host_meta
|
||||
# /.well-known/host-meta.json: mod_host_meta
|
||||
-
|
||||
# STUN+TURN TCP
|
||||
# note that the full port range should be forwarded ("not NAT'd")
|
||||
# `use_turn=true` enables both TURN *and* STUN
|
||||
port: 3478
|
||||
module: ejabberd_stun
|
||||
transport: tcp
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
-
|
||||
# STUN+TURN UDP
|
||||
port: 3478
|
||||
module: ejabberd_stun
|
||||
transport: udp
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
-
|
||||
# STUN+TURN TLS over TCP
|
||||
port: 5349
|
||||
module: ejabberd_stun
|
||||
transport: tcp
|
||||
tls: true
|
||||
certfile: /var/lib/acme/uninsane.org/full.pem
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
|
||||
# TODO: enable mod_fail2ban
|
||||
# TODO(low): look into mod_http_fileserver for serving macros?
|
||||
modules:
|
||||
# mod_adhoc: {}
|
||||
# mod_announce:
|
||||
# access: admin
|
||||
# allows users to set avatars in vCard
|
||||
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
|
||||
mod_avatar: {}
|
||||
mod_caps: {} # for mod_pubsub
|
||||
mod_carboncopy: {} # allows multiple clients to receive a user's message
|
||||
# queues messages when recipient is offline, including PEP and presence messages.
|
||||
# compliance test suggests this be enabled
|
||||
mod_client_state: {}
|
||||
# mod_conversejs: TODO: enable once on 21.12
|
||||
# allows clients like Dino to discover where to upload files
|
||||
mod_disco:
|
||||
server_info:
|
||||
-
|
||||
modules: all
|
||||
name: abuse-addresses
|
||||
urls:
|
||||
- "mailto:admin.xmpp@uninsane.org"
|
||||
- "xmpp:colin@uninsane.org"
|
||||
-
|
||||
modules: all
|
||||
name: admin-addresses
|
||||
urls:
|
||||
- "mailto:admin.xmpp@uninsane.org"
|
||||
- "xmpp:colin@uninsane.org"
|
||||
mod_http_upload:
|
||||
host: upload.xmpp.uninsane.org
|
||||
hosts:
|
||||
- upload.xmpp.uninsane.org
|
||||
put_url: "https://@HOST@:5443/upload"
|
||||
dir_mode: "0750"
|
||||
file_mode: "0750"
|
||||
rm_on_unregister: false
|
||||
# allow discoverability of BOSH and websocket endpoints
|
||||
# TODO: enable once on ejabberd 22.05 (presently 21.04)
|
||||
# mod_host_meta: {}
|
||||
mod_jidprep: {} # probably not needed: lets clients normalize jids
|
||||
mod_last: {} # allow other users to know when i was last online
|
||||
mod_mam:
|
||||
# Mnesia is limited to 2GB, better to use an SQL backend
|
||||
# For small servers SQLite is a good fit and is very easy
|
||||
# to configure. Uncomment this when you have SQL configured:
|
||||
# db_type: sql
|
||||
assume_mam_usage: true
|
||||
default: always
|
||||
mod_muc:
|
||||
access:
|
||||
- allow
|
||||
access_admin:
|
||||
- allow: admin
|
||||
access_create: muc_create
|
||||
access_persistent: muc_create
|
||||
access_mam:
|
||||
- allow
|
||||
history_size: 100 # messages to show new participants
|
||||
host: muc.xmpp.uninsane.org
|
||||
hosts:
|
||||
- muc.xmpp.uninsane.org
|
||||
default_room_options:
|
||||
anonymous: false
|
||||
lang: en
|
||||
persistent: true
|
||||
mam: true
|
||||
mod_muc_admin: {}
|
||||
mod_offline: # store messages for a user when they're offline (TODO: understand multi-client workflow?)
|
||||
access_max_user_messages: max_user_offline_messages
|
||||
store_groupchat: true
|
||||
mod_ping: {}
|
||||
mod_privacy: {} # deprecated, but required for `ejabberctl export_piefxis`
|
||||
mod_private: {} # allow local clients to persist arbitrary data on my server
|
||||
# push notifications to services integrated with e.g. Apple/Android.
|
||||
# default is for a maximum amount of PII to be withheld, since these push notifs
|
||||
# generally traverse 3rd party services. can opt to include message body, etc, though.
|
||||
mod_push: {}
|
||||
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
|
||||
mod_push_keepalive: {}
|
||||
mod_roster:
|
||||
versioning: true
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
|
||||
# s2s dialback to verify inbound messages
|
||||
# unclear to what degree the XMPP network requires this
|
||||
mod_s2s_dialback: {}
|
||||
mod_shared_roster: {} # creates groups for @all, @online, and anything manually administered?
|
||||
mod_stream_mgmt:
|
||||
resend_on_timeout: if_offline # resend undelivered messages if the origin client is offline
|
||||
# fallback for when DNS-based STUN discovery is unsupported.
|
||||
# - see: <https://xmpp.org/extensions/xep-0215.html>
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-stun-disco>
|
||||
# people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?)
|
||||
mod_stun_disco: {}
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
|
||||
mod_vcard:
|
||||
allow_return_all: true # all users are discoverable (?)
|
||||
host: vjid.xmpp.uninsane.org
|
||||
hosts:
|
||||
- vjid.xmpp.uninsane.org
|
||||
search: true
|
||||
mod_vcard_xupdate: {} # needed for avatars
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-pubsub>
|
||||
mod_pubsub: # needed for avatars
|
||||
access_createnode: pubsub_createnode_access
|
||||
host: pubsub.xmpp.uninsane.org
|
||||
hosts:
|
||||
- pubsub.xmpp.uninsane.org
|
||||
ignore_pep_from_offline: false
|
||||
last_item_cache: true
|
||||
plugins:
|
||||
- pep
|
||||
- flat
|
||||
force_node_config:
|
||||
# ensure client bookmarks are private
|
||||
storage:bookmarks:
|
||||
access_model: whitelist
|
||||
urn:xmpp:avatar:data:
|
||||
access_model: open
|
||||
urn:xmpp:avatar:metadata:
|
||||
access_model: open
|
||||
mod_version: {}
|
||||
'';
|
||||
};
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
in ''
|
||||
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||
# config is 444 (not 644), so we want to write out-of-place and then atomically move
|
||||
# TODO: factor this out into `sane-woop` helper?
|
||||
rm -f /var/lib/ejabberd/ejabberd.yaml.new
|
||||
${sed} "s/%NATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
|
||||
'';
|
||||
|
||||
sane.services.dyn-dns.restartOnChange = [ "ejabberd.service" ];
|
||||
}
|
@@ -9,19 +9,16 @@
|
||||
# $ sudo -u freshrss -g freshrss FRESHRSS_DATA_PATH=/var/lib/freshrss ./result/cli/export-opml-for-user.php --user admin
|
||||
# ```
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
{
|
||||
sops.secrets.freshrss_passwd = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
owner = config.users.users.freshrss.name;
|
||||
mode = "400";
|
||||
mode = "0400";
|
||||
};
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "freshrss"; group = "freshrss"; directory = "/var/lib/freshrss"; }
|
||||
];
|
||||
|
||||
users.users.freshrss.uid = config.sane.allocations.freshrss-uid;
|
||||
users.groups.freshrss.gid = config.sane.allocations.freshrss-gid;
|
||||
services.freshrss.enable = true;
|
||||
services.freshrss.baseUrl = "https://rss.uninsane.org";
|
||||
services.freshrss.virtualHost = "rss.uninsane.org";
|
||||
@@ -29,9 +26,11 @@
|
||||
|
||||
systemd.services.freshrss-import-feeds =
|
||||
let
|
||||
feeds = sane-lib.feeds;
|
||||
fresh = config.systemd.services.freshrss-config;
|
||||
feeds = import ../../../modules/home-manager/feeds.nix { inherit lib; };
|
||||
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml feeds.all);
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml wanted-feeds);
|
||||
in {
|
||||
inherit (fresh) wantedBy environment;
|
||||
serviceConfig = {
|
||||
@@ -42,11 +41,23 @@
|
||||
description = "import sane RSS feed list";
|
||||
after = [ "freshrss-config.service" ];
|
||||
script = ''
|
||||
${pkgs.freshrss}/cli/import-for-user.php --user admin --filename ${opml}
|
||||
# easiest way to preserve feeds: delete the user, recreate it, import feeds
|
||||
${pkgs.freshrss}/cli/delete-user.php --user colin || true
|
||||
${pkgs.freshrss}/cli/create-user.php --user colin --password "$(cat ${config.services.freshrss.passwordFile})" || true
|
||||
${pkgs.freshrss}/cli/import-for-user.php --user colin --filename ${opml}
|
||||
'';
|
||||
};
|
||||
|
||||
# the default ("*:0/5") is to run every 5 minutes.
|
||||
# `systemctl list-timers` to show
|
||||
systemd.services.freshrss-updater.startAt = lib.mkForce "*:3/30";
|
||||
|
||||
services.nginx.virtualHosts."rss.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
# the routing is handled by services.freshrss.virtualHost
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."rss" = "native";
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "git"; group = "gitea"; directory = "/var/lib/gitea"; }
|
||||
];
|
||||
users.groups.gitea.gid = config.sane.allocations.gitea-gid;
|
||||
services.gitea.enable = true;
|
||||
services.gitea.user = "git"; # default is 'gitea'
|
||||
services.gitea.database.type = "postgres";
|
||||
@@ -16,6 +15,17 @@
|
||||
services.gitea.settings.session.COOKIE_SECURE = true;
|
||||
# services.gitea.disableRegistration = true;
|
||||
|
||||
# gitea doesn't create the git user
|
||||
users.users.git = {
|
||||
description = "Gitea Service";
|
||||
home = "/var/lib/gitea";
|
||||
useDefaultShell = true;
|
||||
group = "gitea";
|
||||
isSystemUser = true;
|
||||
# sendmail access (not 100% sure if this is necessary)
|
||||
extraGroups = [ "postdrop" ];
|
||||
};
|
||||
|
||||
services.gitea.settings = {
|
||||
server = {
|
||||
# options: "home", "explore", "organizations", "login" or URL fragment (or full URL)
|
||||
@@ -72,4 +82,18 @@
|
||||
"/var/lib/gitea"
|
||||
];
|
||||
};
|
||||
|
||||
# hosted git (web view and for `git <cmd>` use
|
||||
# TODO: enable publog?
|
||||
services.nginx.virtualHosts."git.uninsane.org" = {
|
||||
forceSSL = true; # gitea complains if served over a different protocol than its config file says
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:3000";
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = "native";
|
||||
}
|
@@ -25,6 +25,7 @@
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10s";
|
||||
|
||||
# hardening
|
||||
WorkingDirectory = "/tmp";
|
||||
@@ -42,4 +43,26 @@
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
|
||||
# server statistics
|
||||
services.nginx.virtualHosts."sink.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
root = "/var/lib/uninsane/sink";
|
||||
|
||||
locations."/ws" = {
|
||||
proxyPass = "http://127.0.0.1:7890";
|
||||
# XXX not sure how much of this is necessary
|
||||
extraConfig = ''
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 7d;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."sink" = "native";
|
||||
}
|
@@ -10,10 +10,32 @@
|
||||
|
||||
lib.mkIf false # i don't actively use ipfs anymore
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "261"; group = "261"; directory = "/var/lib/ipfs"; }
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 4001 ];
|
||||
networking.firewall.allowedUDPPorts = [ 4001 ];
|
||||
|
||||
services.nginx.virtualHosts."ipfs.uninsane.org" = {
|
||||
# don't default to ssl upgrades, since this may be dnslink'd from a different domain.
|
||||
# ideally we'd disable ssl entirely, but some places assume it?
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8080";
|
||||
extraConfig = ''
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Ipfs-Gateway-Prefix "";
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."ipfs" = "native";
|
||||
|
||||
# services.ipfs.enable = true;
|
||||
services.kubo.localDiscovery = true;
|
||||
services.kubo.settings = {
|
@@ -1,18 +1,32 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
||||
{ user = "root"; group = "root"; directory = "/var/lib/jackett"; }
|
||||
];
|
||||
services.jackett.enable = true;
|
||||
|
||||
systemd.services.jackett.after = ["wg0veth.service"];
|
||||
systemd.services.jackett.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
# patch jackett to listen on the public interfaces
|
||||
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
|
||||
};
|
||||
|
||||
# jackett torrent search
|
||||
services.nginx.virtualHosts."jackett.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
# proxyPass = "http://ovpns.uninsane.org:9117";
|
||||
proxyPass = "http://10.0.1.6:9117";
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
|
||||
}
|
||||
|
67
hosts/by-name/servo/services/jellyfin.nix
Normal file
67
hosts/by-name/servo/services/jellyfin.nix
Normal file
@@ -0,0 +1,67 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
|
||||
# else it's too spammy
|
||||
lib.mkIf false
|
||||
{
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
|
||||
];
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
|
||||
];
|
||||
|
||||
# Jellyfin multimedia server
|
||||
# this is mostly taken from the official jellfin.org docs
|
||||
services.nginx.virtualHosts."jelly.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8096";
|
||||
extraConfig = ''
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
|
||||
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
|
||||
proxy_buffering off;
|
||||
'';
|
||||
};
|
||||
# locations."/web/" = {
|
||||
# proxyPass = "http://127.0.0.1:8096/web/index.html";
|
||||
# extraConfig = ''
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
# proxy_set_header X-Forwarded-Host $http_host;
|
||||
# '';
|
||||
# };
|
||||
locations."/socket" = {
|
||||
proxyPass = "http://127.0.0.1:8096";
|
||||
extraConfig = ''
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
|
||||
|
||||
services.jellyfin.enable = true;
|
||||
}
|
17
hosts/by-name/servo/services/kiwix-serve.nix
Normal file
17
hosts/by-name/servo/services/kiwix-serve.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
{ ... }:
|
||||
{
|
||||
sane.services.kiwix-serve = {
|
||||
enable = true;
|
||||
port = 8013;
|
||||
zimPaths = [ "/var/lib/uninsane/www-archive/wikipedia_en_all_maxi_2022-05.zim" ];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."w.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/".proxyPass = "http://127.0.0.1:8013";
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."w" = "native";
|
||||
}
|
@@ -1,17 +1,23 @@
|
||||
# docs: https://nixos.wiki/wiki/Matrix
|
||||
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse
|
||||
{ config, lib, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./discord-puppet.nix
|
||||
# ./irc.nix
|
||||
./signal.nix
|
||||
];
|
||||
|
||||
sane.impermanence.service-dirs = [
|
||||
# allow synapse to read the registration files of its appservices
|
||||
users.users.matrix-synapse.extraGroups = [ "mautrix-signal" ];
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
|
||||
];
|
||||
services.matrix-synapse.enable = true;
|
||||
# this changes the default log level from INFO to WARN.
|
||||
# maybe there's an easier way?
|
||||
services.matrix-synapse.settings.log_config = ./synapse-log_level.yaml;
|
||||
services.matrix-synapse.settings.server_name = "uninsane.org";
|
||||
|
||||
@@ -77,9 +83,57 @@
|
||||
# create a token with limited uses:
|
||||
# curl -d '{ "uses_allowed": 1 }' --header "Authorization: Bearer <my_token>" localhost:8008/_synapse/admin/v1/registration_tokens/new
|
||||
|
||||
# matrix chat server
|
||||
# TODO: was `publog`
|
||||
services.nginx.virtualHosts."matrix.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
|
||||
sops.secrets.matrix_synapse_secrets = {
|
||||
sopsFile = ../../../../secrets/servo.yaml;
|
||||
# TODO colin: replace this with something helpful to the viewer
|
||||
# locations."/".extraConfig = ''
|
||||
# return 404;
|
||||
# '';
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8008";
|
||||
};
|
||||
# redirect browsers to the web client.
|
||||
# i don't think native matrix clients ever fetch the root.
|
||||
# ideally this would be put behind some user-agent test though.
|
||||
locations."= /" = {
|
||||
return = "301 https://web.matrix.uninsane.org";
|
||||
};
|
||||
|
||||
# locations."/_matrix" = {
|
||||
# proxyPass = "http://127.0.0.1:8008";
|
||||
# };
|
||||
};
|
||||
|
||||
# matrix web client
|
||||
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-element-web
|
||||
services.nginx.virtualHosts."web.matrix.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
|
||||
root = pkgs.element-web.override {
|
||||
conf = {
|
||||
default_server_config."m.homeserver" = {
|
||||
"base_url" = "https://matrix.uninsane.org";
|
||||
"server_name" = "uninsane.org";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
CNAME."matrix" = "native";
|
||||
CNAME."web.matrix" = "native";
|
||||
};
|
||||
|
||||
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
owner = config.users.users.matrix-synapse.name;
|
||||
};
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
|
||||
];
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
# TODO: should use a dedicated user
|
||||
systemd.services.mx-puppet-discord.serviceConfig = {
|
||||
# fix up to not use /var/lib/private, but just /var/lib
|
||||
DynamicUser = lib.mkForce false;
|
@@ -1,7 +1,7 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode?
|
||||
# user and group are both "matrix-appservice-irc"
|
||||
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }
|
35
hosts/by-name/servo/services/matrix/signal.nix
Normal file
35
hosts/by-name/servo/services/matrix/signal.nix
Normal file
@@ -0,0 +1,35 @@
|
||||
# config options:
|
||||
# - <https://github.com/mautrix/signal/blob/master/mautrix_signal/example-config.yaml>
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "mautrix-signal"; group = "mautrix-signal"; directory = "/var/lib/mautrix-signal"; }
|
||||
{ user = "signald"; group = "signald"; directory = "/var/lib/signald"; }
|
||||
];
|
||||
|
||||
services.signald.enable = true;
|
||||
services.mautrix-signal.enable = true;
|
||||
services.mautrix-signal.environmentFile =
|
||||
config.sops.secrets.mautrix_signal_env.path;
|
||||
|
||||
services.mautrix-signal.settings.signal.socket_path = "/run/signald/signald.sock";
|
||||
services.mautrix-signal.settings.homeserver.domain = "uninsane.org";
|
||||
services.mautrix-signal.settings.bridge.permissions."@colin:uninsane.org" = "admin";
|
||||
services.matrix-synapse.settings.app_service_config_files = [
|
||||
# auto-created by mautrix-signal service
|
||||
"/var/lib/mautrix-signal/signal-registration.yaml"
|
||||
];
|
||||
|
||||
systemd.services.mautrix-signal.serviceConfig = {
|
||||
# allow communication to signald
|
||||
SupplementaryGroups = [ "signald" ];
|
||||
ReadWritePaths = [ "/run/signald" ];
|
||||
};
|
||||
|
||||
sops.secrets."mautrix_signal_env" = {
|
||||
format = "binary";
|
||||
mode = "0440";
|
||||
owner = config.users.users.mautrix-signal.name;
|
||||
group = config.users.users.matrix-synapse.name;
|
||||
};
|
||||
}
|
40
hosts/by-name/servo/services/navidrome.nix
Normal file
40
hosts/by-name/servo/services/navidrome.nix
Normal file
@@ -0,0 +1,40 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/navidrome"; }
|
||||
];
|
||||
services.navidrome.enable = true;
|
||||
services.navidrome.settings = {
|
||||
# docs: https://www.navidrome.org/docs/usage/configuration-options/
|
||||
Address = "127.0.0.1";
|
||||
Port = 4533;
|
||||
MusicFolder = "/var/lib/uninsane/media/Music";
|
||||
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
|
||||
AutoImportPlaylists = false;
|
||||
ScanSchedule = "@every 1h";
|
||||
};
|
||||
|
||||
systemd.services.navidrome.serviceConfig = {
|
||||
# fix to use a normal user so we can configure perms correctly
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = "navidrome";
|
||||
Group = "navidrome";
|
||||
};
|
||||
|
||||
users.groups.navidrome = {};
|
||||
|
||||
users.users.navidrome = {
|
||||
group = "navidrome";
|
||||
isSystemUser = true;
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."music.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/".proxyPass = "http://127.0.0.1:4533";
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."music" = "native";
|
||||
}
|
166
hosts/by-name/servo/services/nginx.nix
Normal file
166
hosts/by-name/servo/services/nginx.nix
Normal file
@@ -0,0 +1,166 @@
|
||||
# docs: https://nixos.wiki/wiki/Nginx
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
# make the logs for this host "public" so that they show up in e.g. metrics
|
||||
publog = vhost: lib.attrsets.unionOfDisjoint vhost {
|
||||
extraConfig = (vhost.extraConfig or "") + ''
|
||||
access_log /var/log/nginx/public.log vcombined;
|
||||
'';
|
||||
};
|
||||
|
||||
# kTLS = true; # in-kernel TLS for better perf
|
||||
in
|
||||
{
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
|
||||
services.nginx.enable = true;
|
||||
services.nginx.appendConfig = ''
|
||||
# use 1 process per core.
|
||||
# may want to increase worker_connections too, but `ulimit -n` must be increased first.
|
||||
worker_processes auto;
|
||||
'';
|
||||
|
||||
# this is the standard `combined` log format, with the addition of $host
|
||||
# so that we have the virtualHost in the log.
|
||||
# KEEP IN SYNC WITH GOACCESS
|
||||
# goaccess calls this VCOMBINED:
|
||||
# - <https://gist.github.com/jyap808/10570005>
|
||||
services.nginx.commonHttpConfig = ''
|
||||
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referrer" "$http_user_agent"';
|
||||
access_log /var/log/nginx/private.log vcombined;
|
||||
'';
|
||||
# sets gzip_comp_level = 5
|
||||
services.nginx.recommendedGzipSettings = true;
|
||||
# enables OCSP stapling (so clients don't need contact the OCSP server -- i do instead)
|
||||
# - doesn't seem to, actually: <https://www.ssllabs.com/ssltest/analyze.html?d=uninsane.org>
|
||||
# caches TLS sessions for 10m
|
||||
services.nginx.recommendedTlsSettings = true;
|
||||
# enables sendfile, tcp_nopush, tcp_nodelay, keepalive_timeout 65
|
||||
services.nginx.recommendedOptimisation = true;
|
||||
|
||||
# web blog/personal site
|
||||
services.nginx.virtualHosts."uninsane.org" = publog {
|
||||
root = "${pkgs.uninsane-dot-org}/share/uninsane-dot-org";
|
||||
# a lot of places hardcode https://uninsane.org,
|
||||
# and then when we mix http + non-https, we get CORS violations
|
||||
# and things don't look right. so force SSL.
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
# for OCSP stapling
|
||||
sslTrustedCertificate = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||
|
||||
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
|
||||
# yes, nginx does not strip the prefix when evaluating against the root.
|
||||
locations."/share".root = "/var/lib/uninsane/root";
|
||||
|
||||
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
|
||||
locations."= /.well-known/matrix/server".extraConfig =
|
||||
let
|
||||
# use 443 instead of the default 8448 port to unite
|
||||
# the client-server and server-server port for simplicity
|
||||
server = { "m.server" = "matrix.uninsane.org:443"; };
|
||||
in ''
|
||||
add_header Content-Type application/json;
|
||||
return 200 '${builtins.toJSON server}';
|
||||
'';
|
||||
locations."= /.well-known/matrix/client".extraConfig =
|
||||
let
|
||||
client = {
|
||||
"m.homeserver" = { "base_url" = "https://matrix.uninsane.org"; };
|
||||
"m.identity_server" = { "base_url" = "https://vector.im"; };
|
||||
};
|
||||
# ACAO required to allow element-web on any URL to request this json file
|
||||
in ''
|
||||
add_header Content-Type application/json;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
return 200 '${builtins.toJSON client}';
|
||||
'';
|
||||
|
||||
# static URLs might not be aware of .well-known (e.g. registration confirmation URLs),
|
||||
# so hack around that.
|
||||
locations."/_matrix" = {
|
||||
proxyPass = "http://127.0.0.1:8008";
|
||||
};
|
||||
locations."/_synapse" = {
|
||||
proxyPass = "http://127.0.0.1:8008";
|
||||
};
|
||||
|
||||
# allow ActivityPub clients to discover how to reach @user@uninsane.org
|
||||
# TODO: waiting on https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3361/
|
||||
# locations."/.well-known/nodeinfo" = {
|
||||
# proxyPass = "http://127.0.0.1:4000";
|
||||
# extraConfig = pleromaExtraConfig;
|
||||
# };
|
||||
};
|
||||
|
||||
|
||||
# serve any site not listed above, if it's static.
|
||||
# because we define it dynamically, SSL isn't trivial. support only http
|
||||
# documented <https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name>
|
||||
services.nginx.virtualHosts."~^(?<domain>.+)$" = {
|
||||
default = true;
|
||||
addSSL = true;
|
||||
enableACME = false;
|
||||
sslCertificate = "/var/www/certs/wildcard/cert.pem";
|
||||
sslCertificateKey = "/var/www/certs/wildcard/key.pem";
|
||||
# sslCertificate = "/var/lib/acme/.minica/cert.pem";
|
||||
# sslCertificateKey = "/var/lib/acme/.minica/key.pem";
|
||||
# serverName = null;
|
||||
locations."/" = {
|
||||
# somehow this doesn't escape -- i get error 400 if i:
|
||||
# curl 'http://..' --resolve '..:80:127.0.0.1'
|
||||
root = "/var/www/sites/$domain";
|
||||
# tryFiles = "$domain/$uri $domain/$uri/ =404";
|
||||
};
|
||||
};
|
||||
|
||||
security.acme.acceptTerms = true;
|
||||
security.acme.defaults.email = "admin.acme@uninsane.org";
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode?
|
||||
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
|
||||
{ user = "colin"; group = "users"; directory = "/var/www/sites"; }
|
||||
];
|
||||
|
||||
# let's encrypt default chain looks like:
|
||||
# - End-entity certificate ← R3 ← ISRG Root X1 ← DST Root CA X3
|
||||
# - <https://community.letsencrypt.org/t/production-chain-changes/150739>
|
||||
# DST Root CA X3 expired in 2021 (?)
|
||||
# the alternative chain is:
|
||||
# - End-entity certificate ← R3 ← ISRG Root X1 (self-signed)
|
||||
# using this alternative chain grants more compatibility for services like ejabberd
|
||||
# but might decrease compatibility with very old clients that don't get updates (e.g. old android, iphone <= 4).
|
||||
# security.acme.defaults.extraLegoFlags = [
|
||||
security.acme.certs."uninsane.org" = rec {
|
||||
# ISRG Root X1 results in lets encrypt sending the same chain as default,
|
||||
# just without the final ISRG Root X1 ← DST Root CA X3 link.
|
||||
# i.e. we could alternative clip the last item and achieve the exact same thing.
|
||||
extraLegoRunFlags = [
|
||||
"--preferred-chain" "ISRG Root X1"
|
||||
];
|
||||
extraLegoRenewFlags = extraLegoRunFlags;
|
||||
};
|
||||
# TODO: alternatively, we could clip the last cert IF it's expired,
|
||||
# optionally outputting that to a new cert file.
|
||||
# security.acme.defaults.postRun = "";
|
||||
|
||||
# create a self-signed SSL certificate for use with literally any domain.
|
||||
# browsers will reject this, but proxies and local testing tools can be configured
|
||||
# to accept it.
|
||||
system.activationScripts.generate-x509-self-signed.text = ''
|
||||
mkdir -p /var/www/certs/wildcard
|
||||
test -f /var/www/certs/wildcard/key.pem || ${pkgs.openssl}/bin/openssl \
|
||||
req -x509 -newkey rsa:4096 \
|
||||
-keyout /var/www/certs/wildcard/key.pem \
|
||||
-out /var/www/certs/wildcard/cert.pem \
|
||||
-sha256 -nodes -days 3650 \
|
||||
-addext 'subjectAltName=DNS:*' \
|
||||
-subj '/CN=self-signed'
|
||||
chmod 640 /var/www/certs/wildcard/{key,cert}.pem
|
||||
chown root:nginx /var/www/certs/wildcard /var/www/certs/wildcard/{key,cert}.pem
|
||||
'';
|
||||
}
|
21
hosts/by-name/servo/services/nixserve.nix
Normal file
21
hosts/by-name/servo/services/nixserve.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
services.nginx.virtualHosts."nixcache.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
# serverAliases = [ "nixcache" ];
|
||||
locations."/".extraConfig = ''
|
||||
proxy_pass http://localhost:${toString config.services.nix-serve.port};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
'';
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../../../secrets/servo.yaml;
|
||||
}
|
@@ -6,12 +6,10 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "pleroma"; group = "pleroma"; directory = "/var/lib/pleroma"; }
|
||||
];
|
||||
users.users.pleroma.uid = config.sane.allocations.pleroma-uid;
|
||||
users.groups.pleroma.gid = config.sane.allocations.pleroma-gid;
|
||||
services.pleroma.enable = true;
|
||||
services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path;
|
||||
services.pleroma.configs = [
|
||||
@@ -113,7 +111,7 @@
|
||||
''
|
||||
];
|
||||
|
||||
systemd.services.pleroma.path = [
|
||||
systemd.services.pleroma.path = [
|
||||
# something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start
|
||||
pkgs.bash
|
||||
# used by Pleroma to strip geo tags from uploads
|
||||
@@ -127,6 +125,7 @@
|
||||
systemd.services.pleroma.serviceConfig = {
|
||||
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10s";
|
||||
};
|
||||
|
||||
# systemd.services.pleroma.serviceConfig = {
|
||||
@@ -136,8 +135,56 @@
|
||||
# CapabilityBoundingSet = lib.mkForce "~";
|
||||
# };
|
||||
|
||||
sops.secrets.pleroma_secrets = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
# this is required to allow pleroma to send email.
|
||||
# raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk.
|
||||
# hack to fix that.
|
||||
users.users.pleroma.extraGroups = [ "postdrop" ];
|
||||
|
||||
# Pleroma server and web interface
|
||||
# TODO: enable publog?
|
||||
services.nginx.virtualHosts."fed.uninsane.org" = {
|
||||
forceSSL = true; # pleroma redirects to https anyway
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:4000";
|
||||
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
|
||||
extraConfig = ''
|
||||
# XXX colin: this block is in the nixos examples: i don't understand all of it
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
|
||||
if ($request_method = OPTIONS) {
|
||||
return 204;
|
||||
}
|
||||
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Permitted-Cross-Domain-Policies none;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header Referrer-Policy same-origin;
|
||||
add_header X-Download-Options noopen;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
# proxy_set_header Host $http_host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# colin: added this due to Pleroma complaining in its logs
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
client_max_body_size 16m;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
|
||||
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
owner = config.users.users.pleroma.name;
|
||||
};
|
||||
}
|
@@ -16,7 +16,7 @@ let
|
||||
};
|
||||
in
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "opendkim"; group = "opendkim"; directory = "/var/lib/opendkim"; }
|
||||
{ user = "root"; group = "root"; directory = "/var/lib/postfix"; }
|
||||
@@ -25,6 +25,61 @@ in
|
||||
# "/var/lib/dhparams" # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/dhparams.nix
|
||||
# "/var/lib/dovecot"
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
25 # SMTP
|
||||
143 # IMAP
|
||||
465 # SMTPS
|
||||
587 # SMTPS/submission
|
||||
993 # IMAPS
|
||||
];
|
||||
|
||||
# exists only to manage certs for dovecot
|
||||
services.nginx.virtualHosts."imap.uninsane.org" = {
|
||||
enableACME = true;
|
||||
};
|
||||
# exists only to manage certs for Postfix
|
||||
services.nginx.virtualHosts."mx.uninsane.org" = {
|
||||
enableACME = true;
|
||||
};
|
||||
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
MX."@" = "10 mx.uninsane.org.";
|
||||
# XXX: RFC's specify that the MX record CANNOT BE A CNAME
|
||||
A."mx" = "185.157.162.178";
|
||||
CNAME."imap" = "native";
|
||||
|
||||
# Sender Policy Framework:
|
||||
# +mx => mail passes if it originated from the MX
|
||||
# +a => mail passes if it originated from the A address of this domain
|
||||
# +ip4:.. => mail passes if it originated from this IP
|
||||
# -all => mail fails if none of these conditions were met
|
||||
TXT."@" = "v=spf1 a mx -all";
|
||||
|
||||
# DKIM public key:
|
||||
TXT."mx._domainkey" =
|
||||
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkSyMufc2KrRx3j17e/LyB+3eYSBRuEFT8PUka8EDX04QzCwDPdkwgnj3GNDvnB5Ktb05Cf2SJ/S1OLqNsINxJRWtkVfZd/C339KNh9wrukMKRKNELL9HLUw0bczOI4gKKFqyrRE9qm+4csCMAR79Te9FCjGV/jVnrkLdPT0GtFwIDAQAB"
|
||||
;
|
||||
|
||||
# DMARC fields <https://datatracker.ietf.org/doc/html/rfc7489>:
|
||||
# p=none|quarantine|reject: what to do with failures
|
||||
# sp = p but for subdomains
|
||||
# rua = where to send aggregrate reports
|
||||
# ruf = where to send individual failure reports
|
||||
# fo=0|1|d|s controls WHEN to send failure reports
|
||||
# (1=on bad alignment; d=on DKIM failure; s=on SPF failure);
|
||||
# Additionally:
|
||||
# adkim=r|s (is DKIM relaxed [default] or strict)
|
||||
# aspf=r|s (is SPF relaxed [default] or strict)
|
||||
# pct = sampling ratio for punishing failures (default 100 for 100%)
|
||||
# rf = report format
|
||||
# ri = report interval
|
||||
TXT."_dmarc" =
|
||||
"v=DMARC1;p=quarantine;sp=reject;rua=mailto:admin+mail@uninsane.org;ruf=mailto:admin+mail@uninsane.org;fo=1:d:s"
|
||||
;
|
||||
};
|
||||
|
||||
services.postfix.enable = true;
|
||||
services.postfix.hostname = "mx.uninsane.org";
|
||||
services.postfix.origin = "uninsane.org";
|
||||
@@ -55,7 +110,8 @@ in
|
||||
services.postfix.enableSubmissions = true;
|
||||
services.postfix.submissionsOptions = submissionOptions;
|
||||
|
||||
systemd.services.postfix.after = [ "wg0veth.service" ];
|
||||
systemd.services.postfix.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.postfix.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.postfix.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
@@ -76,7 +132,8 @@ in
|
||||
# keeping this the same as the hostname seems simplest
|
||||
services.opendkim.selector = "mx";
|
||||
|
||||
systemd.services.opendkim.after = [ "wg0veth.service" ];
|
||||
systemd.services.opendkim.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.opendkim.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.opendkim.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
@@ -140,8 +197,7 @@ in
|
||||
# }
|
||||
];
|
||||
|
||||
sops.secrets.dovecot_passwd = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."dovecot_passwd" = {
|
||||
owner = config.users.users.dovecot2.name;
|
||||
# TODO: debug why mail can't be sent without this being world-readable
|
||||
mode = "0444";
|
@@ -1,7 +1,7 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode?
|
||||
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
|
||||
];
|
@@ -1,3 +1,5 @@
|
||||
# example configs:
|
||||
# - <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
|
||||
# create users with:
|
||||
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
|
||||
|
||||
@@ -7,13 +9,13 @@
|
||||
# nixnet runs ejabberd, so revisiting that.
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
5222 # XMPP client -> server
|
||||
5269 # XMPP server -> server
|
||||
5280 # Prosody HTTP port (necessary?)
|
||||
5280 # bosh
|
||||
5281 # Prosody HTTPS port (necessary?)
|
||||
];
|
||||
|
||||
@@ -34,7 +36,7 @@ lib.mkIf false
|
||||
# c2s_require_encryption = true
|
||||
# '';
|
||||
|
||||
# extraModules = [ "private" "vcard" "privacy" "compression" "component" "muc" "pep" "adhoc" "lastactivity" "admin_adhoc" "blocklist"];
|
||||
extraModules = [ "private" "vcard" "privacy" "compression" "component" "muc" "pep" "adhoc" "lastactivity" "admin_adhoc" "blocklist"];
|
||||
|
||||
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
||||
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
|
||||
@@ -51,7 +53,7 @@ lib.mkIf false
|
||||
domain = "localhost";
|
||||
enabled = true;
|
||||
};
|
||||
"uninsane.org" = {
|
||||
"xmpp.uninsane.org" = {
|
||||
domain = "uninsane.org";
|
||||
enabled = true;
|
||||
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
@@ -1,7 +1,7 @@
|
||||
{ ... }:
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
sane.persist.sys.plaintext = [
|
||||
# TODO: mode? we need this specifically for the stats tracking in .config/
|
||||
{ user = "transmission"; group = "transmission"; directory = "/var/lib/transmission"; }
|
||||
];
|
||||
@@ -40,11 +40,41 @@
|
||||
# transmission will by default not allow the world to read its files.
|
||||
services.transmission.downloadDirPermissions = "775";
|
||||
|
||||
systemd.services.transmission.after = ["wg0veth.service"];
|
||||
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.transmission.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.transmission.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
LogLevelMax = "warning";
|
||||
};
|
||||
|
||||
# service to automatically backup torrents i add to transmission
|
||||
systemd.services.backup-torrents = {
|
||||
description = "archive torrents to storage not owned by transmission";
|
||||
script = ''
|
||||
${pkgs.rsync}/bin/rsync -arv /var/lib/transmission/.config/transmission-daemon/torrents/ /var/backup/torrents/
|
||||
'';
|
||||
};
|
||||
systemd.timers.backup-torrents = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
timerConfig = {
|
||||
OnStartupSec = "11min";
|
||||
OnUnitActiveSec = "240min";
|
||||
};
|
||||
};
|
||||
|
||||
# transmission web client
|
||||
services.nginx.virtualHosts."bt.uninsane.org" = {
|
||||
# basicAuth is literally cleartext user/pw, so FORCE this to happen over SSL
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
# proxyPass = "http://ovpns.uninsane.org:9091";
|
||||
proxyPass = "http://10.0.1.6:9091";
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."bt" = "native";
|
||||
}
|
||||
|
67
hosts/by-name/servo/services/trust-dns.nix
Normal file
67
hosts/by-name/servo/services/trust-dns.nix
Normal file
@@ -0,0 +1,67 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.services.trust-dns.enable = true;
|
||||
|
||||
sane.services.trust-dns.listenAddrsIPv4 = [
|
||||
# specify each address explicitly, instead of using "*".
|
||||
# this ensures responses are sent from the address at which the request was received.
|
||||
"192.168.0.5"
|
||||
"10.0.1.5"
|
||||
];
|
||||
sane.services.trust-dns.quiet = true;
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".TTL = 900;
|
||||
|
||||
# SOA record structure: <https://en.wikipedia.org/wiki/SOA_record#Structure>
|
||||
# SOA MNAME RNAME (... rest)
|
||||
# MNAME = Master name server for this zone. this is where update requests should be sent.
|
||||
# RNAME = admin contact (encoded email address)
|
||||
# Serial = YYYYMMDDNN, where N is incremented every time this file changes, to trigger secondary NS to re-fetch it.
|
||||
# Refresh = how frequently secondary NS should query master
|
||||
# Retry = how long secondary NS should wait until re-querying master after a failure (must be < Refresh)
|
||||
# Expire = how long secondary NS should continue to reply to queries after master fails (> Refresh + Retry)
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
SOA."@" = ''
|
||||
ns1.uninsane.org. admin-dns.uninsane.org. (
|
||||
2022122101 ; Serial
|
||||
4h ; Refresh
|
||||
30m ; Retry
|
||||
7d ; Expire
|
||||
5m) ; Negative response TTL
|
||||
'';
|
||||
TXT."rev" = "2022122101";
|
||||
|
||||
# XXX NS records must also not be CNAME
|
||||
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
|
||||
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
|
||||
A."ns1" = "%NATIVE%";
|
||||
A."ns2" = "185.157.162.178";
|
||||
A."ns3" = "185.157.162.178";
|
||||
A."ovpns" = "185.157.162.178";
|
||||
A."native" = "%NATIVE%";
|
||||
A."@" = "%NATIVE%";
|
||||
NS."@" = [
|
||||
"ns1.uninsane.org."
|
||||
"ns2.uninsane.org."
|
||||
"ns3.uninsane.org."
|
||||
];
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".file =
|
||||
"/var/lib/trust-dns/uninsane.org.zone";
|
||||
|
||||
systemd.services.trust-dns.preStart = let
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
zone-dir = "/var/lib/trust-dns";
|
||||
zone-out = "${zone-dir}/uninsane.org.zone";
|
||||
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.generatedZones."uninsane.org";
|
||||
in ''
|
||||
# make WAN records available to trust-dns
|
||||
mkdir -p ${zone-dir}
|
||||
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||
${sed} s/%NATIVE%/$ip/ ${zone-template} > ${zone-out}
|
||||
'';
|
||||
|
||||
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
|
||||
}
|
30
hosts/by-name/servo/services/wikipedia.nix
Normal file
30
hosts/by-name/servo/services/wikipedia.nix
Normal file
@@ -0,0 +1,30 @@
|
||||
# docs: <https://nixos.wiki/wiki/MediaWiki>
|
||||
{ config, lib, ... }:
|
||||
|
||||
# XXX: working to host wikipedia with kiwix instead of mediawiki
|
||||
# mediawiki does more than i need and isn't obviously superior in any way
|
||||
# except that the dumps are more frequent/up-to-date.
|
||||
lib.mkIf false
|
||||
{
|
||||
sops.secrets."mediawiki_pw" = {
|
||||
owner = config.users.users.mediawiki.name;
|
||||
};
|
||||
|
||||
services.mediawiki.enable = true;
|
||||
services.mediawiki.name = "Uninsane Wiki";
|
||||
services.mediawiki.passwordFile = config.sops.secrets.mediawiki_pw.path;
|
||||
services.mediawiki.extraConfig = ''
|
||||
# Disable anonymous editing
|
||||
$wgGroupPermissions['*']['edit'] = false;
|
||||
'';
|
||||
services.mediawiki.virtualHost.listen = [
|
||||
{
|
||||
ip = "127.0.0.1";
|
||||
port = 8013;
|
||||
ssl = false;
|
||||
}
|
||||
];
|
||||
services.mediawiki.virtualHost.hostName = "w.uninsane.org";
|
||||
services.mediawiki.virtualHost.adminAddr = "admin+mediawiki@uninsane.org";
|
||||
# services.mediawiki.extensions = TODO: wikipedia sync extension?
|
||||
}
|
22
hosts/common/cross.nix
Normal file
22
hosts/common/cross.nix
Normal file
@@ -0,0 +1,22 @@
|
||||
{ config, ... }:
|
||||
|
||||
let
|
||||
mkCrossFrom = localSystem: pkgs: import pkgs.path {
|
||||
inherit localSystem;
|
||||
crossSystem = pkgs.stdenv.hostPlatform.system;
|
||||
inherit (config.nixpkgs) config overlays;
|
||||
};
|
||||
in
|
||||
{
|
||||
# the configuration of which specific package set `pkgs.cross` refers to happens elsewhere;
|
||||
# here we just define them all.
|
||||
nixpkgs.overlays = [
|
||||
(next: prev: {
|
||||
# non-emulated packages build *from* local *for* target.
|
||||
# for large packages like the linux kernel which are expensive to build under emulation,
|
||||
# the config can explicitly pull such packages from `pkgs.cross` to do more efficient cross-compilation.
|
||||
crossFrom."x86_64-linux" = mkCrossFrom "x86_64-linux" next;
|
||||
crossFrom."aarch64-linux" = mkCrossFrom "aarch64-linux" next;
|
||||
})
|
||||
];
|
||||
}
|
@@ -1,8 +1,12 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./cross.nix
|
||||
./feeds.nix
|
||||
./fs.nix
|
||||
./hardware
|
||||
./hardware.nix
|
||||
./i2p.nix
|
||||
./ids.nix
|
||||
./machine-id.nix
|
||||
./net.nix
|
||||
./secrets.nix
|
||||
@@ -16,6 +20,18 @@
|
||||
sane.packages.enableConsolePkgs = true;
|
||||
sane.packages.enableSystemPkgs = true;
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
"/var/log"
|
||||
"/var/backup" # for e.g. postgres dumps
|
||||
# TODO: move elsewhere
|
||||
"/var/lib/alsa" # preserve output levels, default devices
|
||||
"/var/lib/colord" # preserve color calibrations (?)
|
||||
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
|
||||
];
|
||||
|
||||
# some services which use private directories error if the parent (/var/lib/private) isn't 700.
|
||||
sane.fs."/var/lib/private".dir.acl.mode = "0700";
|
||||
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
# time.timeZone = "America/Los_Angeles";
|
||||
@@ -25,6 +41,11 @@
|
||||
nix.extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
'';
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
"nixpkgs-overlays=${../..}/overlays"
|
||||
];
|
||||
|
||||
# TODO: move this into home-manager?
|
||||
fonts = {
|
||||
@@ -56,19 +77,8 @@
|
||||
};
|
||||
# enable zsh completions
|
||||
environment.pathsToLink = [ "/share/zsh" ];
|
||||
environment.systemPackages = with pkgs; [
|
||||
# required for pam_mount
|
||||
gocryptfs
|
||||
];
|
||||
|
||||
# link debug symbols into /run/current-system/sw/lib/debug
|
||||
# hopefully picked up by gdb automatically?
|
||||
environment.enableDebugInfo = true;
|
||||
|
||||
security.pam.mount.enable = true;
|
||||
# security.pam.mount.debugLevel = 1;
|
||||
# security.pam.enableSSHAgentAuth = true; # ??
|
||||
# needed for `allow_other` in e.g. gocryptfs mounts
|
||||
# or i guess going through mount.fuse sets suid so that's not necessary?
|
||||
# programs.fuse.userAllowOther = true;
|
||||
}
|
||||
|
203
hosts/common/feeds.nix
Normal file
203
hosts/common/feeds.nix
Normal file
@@ -0,0 +1,203 @@
|
||||
{ lib, sane-data, ... }:
|
||||
let
|
||||
hourly = { freq = "hourly"; };
|
||||
daily = { freq = "daily"; };
|
||||
weekly = { freq = "weekly"; };
|
||||
infrequent = { freq = "infrequent"; };
|
||||
|
||||
art = { cat = "art"; };
|
||||
humor = { cat = "humor"; };
|
||||
pol = { cat = "pol"; }; # or maybe just "social"
|
||||
rat = { cat = "rat"; };
|
||||
tech = { cat = "tech"; };
|
||||
uncat = { cat = "uncat"; };
|
||||
|
||||
text = { format = "text"; };
|
||||
img = { format = "image"; };
|
||||
|
||||
mkRss = format: url: { inherit url format; } // uncat // infrequent;
|
||||
# format-specific helpers
|
||||
mkText = mkRss "text";
|
||||
mkImg = mkRss "image";
|
||||
mkPod = mkRss "podcast";
|
||||
|
||||
# host-specific helpers
|
||||
mkSubstack = subdomain: { substack = subdomain; };
|
||||
|
||||
fromDb = name:
|
||||
let
|
||||
raw = sane-data.feeds."${name}";
|
||||
in {
|
||||
url = raw.url;
|
||||
# not sure the exact mapping with velocity here: entries per day?
|
||||
freq = lib.mkIf (raw.velocity or 0 != 0) (lib.mkDefault (
|
||||
if raw.velocity > 2 then
|
||||
"hourly"
|
||||
else if raw.velocity > 0.5 then
|
||||
"daily"
|
||||
else if raw.velocity > 0.1 then
|
||||
"weekly"
|
||||
else
|
||||
"infrequent"
|
||||
));
|
||||
} // lib.optionalAttrs (raw.is_podcast or false) {
|
||||
format = "podcast";
|
||||
} // lib.optionalAttrs (raw.title or "" != "") {
|
||||
title = lib.mkDefault raw.title;
|
||||
};
|
||||
|
||||
podcasts = [
|
||||
(fromDb "lexfridman.com/podcast" // rat)
|
||||
## Astral Codex Ten
|
||||
(fromDb "sscpodcast.libsyn.com" // rat)
|
||||
## Econ Talk
|
||||
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat)
|
||||
## Cory Doctorow -- both podcast & text entries
|
||||
(fromDb "craphound.com" // pol)
|
||||
(fromDb "congressionaldish.libsyn.com" // pol)
|
||||
## Civboot -- https://anchor.fm/civboot
|
||||
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
|
||||
## Emerge: making sense of what's next -- <https://www.whatisemerging.com/emergepodcast>
|
||||
(mkPod "https://anchor.fm/s/21bc734/podcast/rss" // pol // infrequent)
|
||||
(fromDb "feeds.feedburner.com/80000HoursPodcast" // rat)
|
||||
(fromDb "allinchamathjason.libsyn.com" // pol)
|
||||
(fromDb "acquired.libsyn.com" // tech)
|
||||
# The Intercept - Deconstructed; also available: <rss.acast.com/deconstructed>
|
||||
(fromDb "rss.prod.firstlook.media/deconstructed/podcast.rss" // pol)
|
||||
## The Daily
|
||||
(mkPod "https://feeds.simplecast.com/54nAGcIl" // pol // daily)
|
||||
# The Intercept - Intercepted; also available: <https://rss.acast.com/intercepted-with-jeremy-scahill>
|
||||
(fromDb "rss.prod.firstlook.media/intercepted/podcast.rss" // pol)
|
||||
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
|
||||
## Eric Weinstein
|
||||
(fromDb "rss.art19.com/the-portal" // rat)
|
||||
(fromDb "darknetdiaries.com" // tech)
|
||||
## Radiolab -- also available here, but ONLY OVER HTTP: <http://feeds.wnyc.org/radiolab>
|
||||
(fromDb "feeds.feedburner.com/radiolab" // pol)
|
||||
## Sam Harris
|
||||
(fromDb "wakingup.libsyn.com" // pol)
|
||||
## 99% Invisible -- also available here: <https://feeds.simplecast.com/BqbsxVfO>
|
||||
(fromDb "feeds.99percentinvisible.org/99percentinvisible" // pol)
|
||||
(fromDb "rss.acast.com/ft-tech-tonic" // tech)
|
||||
(fromDb "feeds.feedburner.com/dancarlin/history" // rat)
|
||||
(fromDb "rss.art19.com/60-minutes" // pol)
|
||||
## The Verge - Decoder
|
||||
(fromDb "feeds.megaphone.fm/recodedecode" // tech)
|
||||
## Matrix (chat) Live
|
||||
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech)
|
||||
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
||||
(fromDb "rss.art19.com/your-welcome" // pol)
|
||||
(fromDb "seattlenice.buzzsprout.com" // pol)
|
||||
## Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
|
||||
(fromDb "talesfromthebridge.buzzsprout.com" // tech)
|
||||
];
|
||||
|
||||
texts = [
|
||||
# AGGREGATORS (> 1 post/day)
|
||||
(fromDb "lwn.net" // tech)
|
||||
(fromDb "lesswrong.com" // rat)
|
||||
(fromDb "econlib.org" // pol)
|
||||
|
||||
# AGGREGATORS (< 1 post/day)
|
||||
(fromDb "palladiummag.com" // uncat)
|
||||
(fromDb "profectusmag.com" // uncat)
|
||||
(fromDb "semiaccurate.com" // tech)
|
||||
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
||||
(fromDb "spectrum.ieee.org" // tech)
|
||||
## n.b.: quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
|
||||
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent)
|
||||
|
||||
## No Moods, Ads or Cutesy Fucking Icons
|
||||
(fromDb "rifters.com/crawl" // uncat)
|
||||
|
||||
# DEVELOPERS
|
||||
(fromDb "uninsane.org" // tech)
|
||||
(fromDb "mg.lol" // tech)
|
||||
(fromDb "drewdevault.com" // tech)
|
||||
## Ken Shirriff
|
||||
(fromDb "righto.com" // tech)
|
||||
## shared blog by a few NixOS devs, notably onny
|
||||
(fromDb "project-insanity.org" // tech)
|
||||
## Vitalik Buterin
|
||||
(fromDb "vitalik.ca" // tech)
|
||||
## ian (Sanctuary)
|
||||
(fromDb "sagacioussuricata.com" // tech)
|
||||
## Bunnie Juang
|
||||
(fromDb "bunniestudios.com" // tech)
|
||||
(fromDb "blog.danieljanus.pl" // tech)
|
||||
(fromDb "ianthehenry.com" // tech)
|
||||
(fromDb "bitbashing.io" // tech)
|
||||
(fromDb "idiomdrottning.org" // uncat)
|
||||
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
||||
(fromDb "jefftk.com" // tech)
|
||||
(fromDb "pomeroyb.com" // tech)
|
||||
|
||||
# (TECH; POL) COMMENTATORS
|
||||
## Matt Webb -- engineering-ish, but dreamy
|
||||
(fromDb "interconnected.org/home/feed" // rat)
|
||||
(fromDb "edwardsnowden.substack.com" // pol // text)
|
||||
## Julia Evans
|
||||
(mkText "https://jvns.ca/atom.xml" // tech // weekly)
|
||||
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
||||
## Ben Thompson
|
||||
(mkText "https://www.stratechery.com/rss" // pol // weekly)
|
||||
## Balaji
|
||||
(fromDb "balajis.com" // pol)
|
||||
(fromDb "ben-evans.com/benedictevans" // pol)
|
||||
(fromDb "lynalden.com" // pol)
|
||||
(fromDb "austinvernon.site" // tech)
|
||||
(mkSubstack "oversharing" // pol // daily)
|
||||
(mkSubstack "doomberg" // tech // weekly)
|
||||
## David Rosenthal
|
||||
(fromDb "blog.dshr.org" // pol)
|
||||
## Matt Levine
|
||||
(mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly)
|
||||
(fromDb "stpeter.im/atom.xml" // pol)
|
||||
## Peter Saint-Andre -- side project of stpeter.im
|
||||
(fromDb "philosopher.coach" // rat)
|
||||
|
||||
# RATIONALITY/PHILOSOPHY/ETC
|
||||
(mkSubstack "samkriss" // humor // infrequent)
|
||||
(fromDb "unintendedconsequenc.es" // rat)
|
||||
(fromDb "applieddivinitystudies.com" // rat)
|
||||
(fromDb "slimemoldtimemold.com" // rat)
|
||||
(fromDb "richardcarrier.info" // rat)
|
||||
(fromDb "gwern.net" // rat)
|
||||
## Jason Crawford
|
||||
(fromDb "rootsofprogress.org" // rat)
|
||||
## Robin Hanson
|
||||
(fromDb "overcomingbias.com" // rat)
|
||||
## Scott Alexander
|
||||
(mkSubstack "astralcodexten" // rat // daily)
|
||||
## Paul Christiano
|
||||
(fromDb "sideways-view.com" // rat)
|
||||
## Sean Carroll
|
||||
(fromDb "preposterousuniverse.com" // rat)
|
||||
|
||||
## mostly dating topics. not advice, or humor, but looking through a social lens
|
||||
(fromDb "putanumonit.com" // rat)
|
||||
|
||||
# CODE
|
||||
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
||||
];
|
||||
|
||||
images = [
|
||||
(fromDb "smbc-comics.com" // img // humor)
|
||||
(fromDb "xkcd.com" // img // humor)
|
||||
(fromDb "pbfcomics.com" // img // humor)
|
||||
# (mkImg "http://dilbert.com/feed" // humor // daily)
|
||||
|
||||
# ART
|
||||
(fromDb "miniature-calendar.com" // img // art // daily)
|
||||
];
|
||||
in
|
||||
{
|
||||
sane.feeds = texts ++ images ++ podcasts;
|
||||
|
||||
assertions = builtins.map
|
||||
(p: {
|
||||
assertion = p.format or "unknown" == "podcast";
|
||||
message = ''${p.url} is not a podcast: ${p.format or "unknown"}'';
|
||||
})
|
||||
podcasts;
|
||||
}
|
4
hosts/common/i2p.nix
Normal file
4
hosts/common/i2p.nix
Normal file
@@ -0,0 +1,4 @@
|
||||
{ ... }:
|
||||
{
|
||||
# services.i2p.enable = true;
|
||||
}
|
66
hosts/common/ids.nix
Normal file
66
hosts/common/ids.nix
Normal file
@@ -0,0 +1,66 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# legacy servo users, some are inconvenient to migrate
|
||||
sane.ids.dhcpcd.gid = 991;
|
||||
sane.ids.dhcpcd.uid = 992;
|
||||
sane.ids.gitea.gid = 993;
|
||||
sane.ids.git.uid = 994;
|
||||
sane.ids.jellyfin.gid = 994;
|
||||
sane.ids.pleroma.gid = 995;
|
||||
sane.ids.jellyfin.uid = 996;
|
||||
sane.ids.acme.gid = 996;
|
||||
sane.ids.pleroma.uid = 997;
|
||||
sane.ids.acme.uid = 998;
|
||||
|
||||
# greetd (used by sway)
|
||||
sane.ids.greeter.uid = 999;
|
||||
sane.ids.greeter.gid = 999;
|
||||
|
||||
# new servo users
|
||||
sane.ids.freshrss.uid = 2401;
|
||||
sane.ids.freshrss.gid = 2401;
|
||||
sane.ids.mediawiki.uid = 2402;
|
||||
sane.ids.signald.uid = 2403;
|
||||
sane.ids.signald.gid = 2403;
|
||||
sane.ids.mautrix-signal.uid = 2404;
|
||||
sane.ids.mautrix-signal.gid = 2404;
|
||||
sane.ids.navidrome.uid = 2405;
|
||||
sane.ids.navidrome.gid = 2405;
|
||||
|
||||
sane.ids.colin.uid = 1000;
|
||||
sane.ids.guest.uid = 1100;
|
||||
|
||||
# found on all hosts
|
||||
sane.ids.sshd.uid = 2001; # 997
|
||||
sane.ids.sshd.gid = 2001; # 997
|
||||
sane.ids.polkituser.gid = 2002; # 998
|
||||
sane.ids.systemd-coredump.gid = 2003; # 996
|
||||
sane.ids.nscd.uid = 2004;
|
||||
sane.ids.nscd.gid = 2004;
|
||||
sane.ids.systemd-oom.uid = 2005;
|
||||
sane.ids.systemd-oom.gid = 2005;
|
||||
|
||||
# found on graphical hosts
|
||||
sane.ids.nm-iodine.uid = 2101; # desko/moby/lappy
|
||||
|
||||
# found on desko host
|
||||
# from services.usbmuxd
|
||||
sane.ids.usbmux.uid = 2204;
|
||||
sane.ids.usbmux.gid = 2204;
|
||||
|
||||
|
||||
# originally found on moby host
|
||||
# gnome core-shell
|
||||
sane.ids.avahi.uid = 2304;
|
||||
sane.ids.avahi.gid = 2304;
|
||||
sane.ids.colord.uid = 2305;
|
||||
sane.ids.colord.gid = 2305;
|
||||
sane.ids.geoclue.uid = 2306;
|
||||
sane.ids.geoclue.gid = 2306;
|
||||
# gnome core-os-services
|
||||
sane.ids.rtkit.uid = 2307;
|
||||
sane.ids.rtkit.gid = 2307;
|
||||
# phosh
|
||||
sane.ids.feedbackd.gid = 2308;
|
||||
}
|
@@ -1,11 +1,16 @@
|
||||
{ ... }:
|
||||
{
|
||||
# we wan't an /etc/machine-id which is consistent across boot so that `journalctl` will actually show us
|
||||
# logs from previous boots.
|
||||
# maybe there's a config option for this (since persistent machine-id is bad for reasons listed in impermanence.nix),
|
||||
# but for now generate it from ssh keys.
|
||||
# /etc/machine-id is a globally unique identifier used for:
|
||||
# - systemd-networkd: DHCP lease renewal (instead of keying by the MAC address)
|
||||
# - systemd-journald: to filter logs by host
|
||||
# - chromium (potentially to track re-installations)
|
||||
# - gdbus; system services that might upgrade to AF_LOCAL if both services can confirm they're on the same machine
|
||||
# because of e.g. the chromium use, we *don't want* to persist this.
|
||||
# however, `journalctl` won't show logs from previous boots unless the machine-ids match.
|
||||
# so for now, generate something unique from the host ssh key.
|
||||
# TODO: move this into modules?
|
||||
system.activationScripts.machine-id = {
|
||||
deps = [ "persist-ssh-host-keys" ];
|
||||
deps = [ "etc" ];
|
||||
text = "sha256sum /etc/ssh/host_keys/ssh_host_ed25519_key | cut -c 1-32 > /etc/machine-id";
|
||||
};
|
||||
}
|
||||
|
@@ -1,16 +1,6 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
# if using router's DNS, these mappings will already exist.
|
||||
# if using a different DNS provider (which servo does), then we need to explicity provide them.
|
||||
# ugly hack. would be better to get servo to somehow use the router's DNS
|
||||
networking.hosts = {
|
||||
"192.168.0.5" = [ "servo" ];
|
||||
"192.168.0.20" = [ "lappy" ];
|
||||
"192.168.0.22" = [ "desko" ];
|
||||
"192.168.0.48" = [ "moby" ];
|
||||
};
|
||||
|
||||
# the default backend is "wpa_supplicant".
|
||||
# wpa_supplicant reliably picks weak APs to connect to.
|
||||
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
|
||||
@@ -30,50 +20,4 @@
|
||||
General.RoamThreshold = "-52"; # default -70
|
||||
General.RoamThreshold5G = "-52"; # default -76
|
||||
};
|
||||
|
||||
# TODO: don't need to depend on binsh if we were to use a nix-style shebang
|
||||
system.activationScripts.linkIwdKeys = let
|
||||
unwrapped = ../../scripts/install-iwd;
|
||||
install-iwd = pkgs.writeShellApplication {
|
||||
name = "install-iwd";
|
||||
runtimeInputs = with pkgs; [ coreutils gnused ];
|
||||
text = ''${unwrapped} "$@"'';
|
||||
};
|
||||
in (lib.stringAfter
|
||||
[ "setupSecrets" "binsh" ]
|
||||
''
|
||||
mkdir -p /var/lib/iwd
|
||||
${install-iwd}/bin/install-iwd /run/secrets/iwd /var/lib/iwd
|
||||
''
|
||||
);
|
||||
|
||||
# TODO: use a glob, or a list, or something?
|
||||
sops.secrets."iwd/community-university.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/community-university.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/friend-libertarian-dod.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/friend-libertarian-dod.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/friend-rationalist-empathist.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/friend-rationalist-empathist.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/home-bedroom.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/home-bedroom.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/home-shared-24G.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/home-shared-24G.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/home-shared.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/home-shared.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/iphone" = {
|
||||
sopsFile = ../../secrets/universal/net/iphone.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
}
|
||||
|
@@ -33,10 +33,6 @@
|
||||
# You can avoid this by adding a string to the full path instead, i.e.
|
||||
# sops.defaultSopsFile = "/root/.sops/secrets/example.yaml";
|
||||
sops.defaultSopsFile = ../../secrets/universal.yaml;
|
||||
# This will automatically import SSH keys as age keys
|
||||
sops.age.sshKeyPaths = [
|
||||
"/etc/ssh/host_keys/ssh_host_ed25519_key"
|
||||
];
|
||||
sops.gnupg.sshKeyPaths = []; # disable RSA key import
|
||||
# This is using an age key that is expected to already be in the filesystem
|
||||
# sops.age.keyFile = "/home/colin/.ssh/age.pub";
|
||||
@@ -48,6 +44,81 @@
|
||||
# owner = config.users.users.colin.name;
|
||||
# };
|
||||
# sops.secrets."myservice/my_subdir/my_secret" = {};
|
||||
|
||||
## universal secrets
|
||||
# TODO: glob these?
|
||||
|
||||
sops.secrets."jackett_apikey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
owner = config.users.users.colin.name;
|
||||
};
|
||||
sops.secrets."router_passwd" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_us_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_us-atl_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_us-mi_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_ukr_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."snippets" = {
|
||||
sopsFile = ../../secrets/universal/snippets.bin;
|
||||
format = "binary";
|
||||
owner = config.users.users.colin.name;
|
||||
};
|
||||
|
||||
sops.secrets."bt/car" = {
|
||||
sopsFile = ../../secrets/universal/bt/car.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."bt/earbuds" = {
|
||||
sopsFile = ../../secrets/universal/bt/earbuds.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."bt/portable-speaker" = {
|
||||
sopsFile = ../../secrets/universal/bt/portable-speaker.bin;
|
||||
format = "binary";
|
||||
};
|
||||
|
||||
sops.secrets."iwd/community-university.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/community-university.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/friend-libertarian-dod.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/friend-libertarian-dod.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/friend-rationalist-empathist.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/friend-rationalist-empathist.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/home-bedroom.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/home-bedroom.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/home-shared-24G.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/home-shared-24G.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/home-shared.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/home-shared.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/iphone" = {
|
||||
sopsFile = ../../secrets/universal/net/iphone.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/parents" = {
|
||||
sopsFile = ../../secrets/universal/net/parents.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,21 +1,33 @@
|
||||
{ ... }:
|
||||
{ config, lib, sane-data, sane-lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) head map mapAttrs tail;
|
||||
inherit (lib) concatStringsSep mkMerge reverseList;
|
||||
in
|
||||
{
|
||||
# we place the host keys (which we want to be persisted) into their own directory so that we can
|
||||
# bind mount that whole directory instead of doing it per-file.
|
||||
# otherwise, this is identical to nixos defaults
|
||||
sane.impermanence.service-dirs = [ "/etc/ssh/host_keys" ];
|
||||
sane.ssh.pubkeys =
|
||||
let
|
||||
# path is a DNS-style path like [ "org" "uninsane" "root" ]
|
||||
keyNameForPath = path:
|
||||
let
|
||||
rev = reverseList path;
|
||||
name = head rev;
|
||||
host = concatStringsSep "." (tail rev);
|
||||
in
|
||||
"${name}@${host}";
|
||||
|
||||
# we can't naively `mount /etc/ssh/host_keys` directly,
|
||||
# as /etc/fstab may not be populated yet (since that file depends on e.g. activationScripts.users)
|
||||
# we can't even depend on impermanence's `createPersistentStorageDirs` to create the source/target directories
|
||||
# since that also depends on `users`.
|
||||
system.activationScripts.persist-ssh-host-keys.text = ''
|
||||
mkdir -p /etc/ssh/host_keys
|
||||
mount --bind /nix/persist/etc/ssh/host_keys /etc/ssh/host_keys
|
||||
'';
|
||||
|
||||
services.openssh.hostKeys = [
|
||||
{ type = "rsa"; bits = 4096; path = "/etc/ssh/host_keys/ssh_host_rsa_key"; }
|
||||
{ type = "ed25519"; path = "/etc/ssh/host_keys/ssh_host_ed25519_key"; }
|
||||
];
|
||||
# [{ path :: [String], value :: String }] for the keys we want to install
|
||||
globalKeys = sane-lib.flattenAttrs sane-data.keys;
|
||||
domainKeys = sane-lib.flattenAttrs (
|
||||
mapAttrs (host: cfg: {
|
||||
colin = cfg.ssh.user_pubkey;
|
||||
root = cfg.ssh.host_pubkey;
|
||||
}) config.sane.hosts.by-name
|
||||
);
|
||||
in mkMerge (map
|
||||
({ path, value }: {
|
||||
"${keyNameForPath path}" = lib.mkIf (value != null) value;
|
||||
})
|
||||
(globalKeys ++ domainKeys)
|
||||
);
|
||||
}
|
||||
|
@@ -1,13 +1,10 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{ config, pkgs, lib, sane-lib, ... }:
|
||||
|
||||
# installer docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix
|
||||
with lib;
|
||||
let
|
||||
cfg = config.sane.users;
|
||||
# see nixpkgs/nixos/modules/services/networking/dhcpcd.nix
|
||||
hasDHCP = config.networking.dhcpcd.enable &&
|
||||
(config.networking.useDHCP || any (i: i.useDHCP == true) (attrValues config.networking.interfaces));
|
||||
|
||||
fs = sane-lib.fs;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
@@ -27,7 +24,8 @@ in
|
||||
# sets group to "users" (?)
|
||||
isNormalUser = true;
|
||||
home = "/home/colin";
|
||||
uid = config.sane.allocations.colin-uid;
|
||||
createHome = true;
|
||||
homeMode = "0700";
|
||||
# i don't get exactly what this is, but nixos defaults to this non-deterministically
|
||||
# in /var/lib/nixos/auto-subuid-map and i don't want that.
|
||||
subUidRanges = [
|
||||
@@ -50,36 +48,66 @@ in
|
||||
passwordFile = lib.mkIf (config.sops.secrets ? "colin-passwd") config.sops.secrets.colin-passwd.path;
|
||||
|
||||
shell = pkgs.zsh;
|
||||
openssh.authorizedKeys.keys = builtins.attrValues (import ../../modules/pubkeys.nix).users;
|
||||
|
||||
pamMount = {
|
||||
# mount encrypted stuff at login
|
||||
# requires that login password == fs encryption password
|
||||
# fstype = "fuse";
|
||||
# path = "${pkgs.gocryptfs}/bin/gocryptfs#/nix/persist/home/colin/private";
|
||||
fstype = "fuse.gocryptfs";
|
||||
path = "/nix/persist/home/colin/private";
|
||||
mountpoint = "/home/colin/private";
|
||||
options="nodev,nosuid,quiet,allow_other";
|
||||
# mount encrypted stuff at login
|
||||
# some other nix pam users:
|
||||
# - <https://github.com/g00pix/nixconf/blob/32c04f6fa843fed97639dd3f09e157668d3eea1f/profiles/sshfs.nix>
|
||||
# - <https://github.com/lourkeur/distro/blob/11173454c6bb50f7ccab28cc2c757dca21446d1d/nixos/profiles/users/louis-full.nix>
|
||||
# - <https://github.com/dnr/sample-nix-code/blob/03494480c1fae550c033aa54fd96aeb3827761c5/nixos/laptop.nix>
|
||||
pamMount = let
|
||||
priv = config.fileSystems."/home/colin/private";
|
||||
in {
|
||||
fstype = priv.fsType;
|
||||
path = priv.device;
|
||||
mountpoint = priv.mountPoint;
|
||||
options = builtins.concatStringsSep "," priv.options;
|
||||
};
|
||||
};
|
||||
|
||||
sane.impermanence.home-dirs = [
|
||||
# cache is probably too big to fit on the tmpfs
|
||||
# TODO: we could bind-mount it to something which gets cleared per boot, though.
|
||||
".cache"
|
||||
security.pam.mount.enable = true;
|
||||
|
||||
# ensure ~ perms are known to sane.fs module.
|
||||
# TODO: this is generic enough to be lifted up into sane.fs itself.
|
||||
sane.fs."/home/colin".dir.acl = {
|
||||
user = "colin";
|
||||
group = config.users.users.colin.group;
|
||||
mode = config.users.users.colin.homeMode;
|
||||
};
|
||||
|
||||
sane.persist.home.plaintext = [
|
||||
"archive"
|
||||
"dev"
|
||||
# TODO: records should be private
|
||||
"records"
|
||||
"ref"
|
||||
"tmp"
|
||||
"use"
|
||||
"Music"
|
||||
"Pictures"
|
||||
"Videos"
|
||||
|
||||
".cache/nix"
|
||||
".cargo"
|
||||
".rustup"
|
||||
".local/share/keyrings"
|
||||
];
|
||||
|
||||
sane.impermanence.service-dirs = mkIf cfg.guest.enable [
|
||||
{ user = "guest"; group = "users"; directory = "/home/guest"; }
|
||||
# convenience
|
||||
sane.fs."/home/colin/knowledge" = fs.wantedSymlinkTo "/home/colin/private/knowledge";
|
||||
sane.fs."/home/colin/nixos" = fs.wantedSymlinkTo "/home/colin/dev/nixos";
|
||||
sane.fs."/home/colin/Videos/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Videos";
|
||||
sane.fs."/home/colin/Videos/servo-incomplete" = fs.wantedSymlinkTo "/mnt/servo-media/incomplete";
|
||||
sane.fs."/home/colin/Music/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Music";
|
||||
|
||||
# used by password managers, e.g. unix `pass`
|
||||
sane.fs."/home/colin/.password-store" = fs.wantedSymlinkTo "/home/colin/knowledge/secrets/accounts";
|
||||
|
||||
sane.persist.sys.plaintext = mkIf cfg.guest.enable [
|
||||
# intentionally allow other users to write to the guest folder
|
||||
{ directory = "/home/guest"; user = "guest"; group = "users"; mode = "0775"; }
|
||||
];
|
||||
users.users.guest = mkIf cfg.guest.enable {
|
||||
isNormalUser = true;
|
||||
home = "/home/guest";
|
||||
uid = config.sane.allocations.guest-uid;
|
||||
subUidRanges = [
|
||||
{ startUid=200000; count=1; }
|
||||
];
|
||||
@@ -91,13 +119,6 @@ in
|
||||
];
|
||||
};
|
||||
|
||||
users.users.dhcpcd = mkIf hasDHCP {
|
||||
uid = config.sane.allocations.dhcpcd-uid;
|
||||
};
|
||||
users.groups.dhcpcd = mkIf hasDHCP {
|
||||
gid = config.sane.allocations.dhcpcd-gid;
|
||||
};
|
||||
|
||||
security.sudo = {
|
||||
enable = true;
|
||||
wheelNeedsPassword = false;
|
||||
@@ -105,34 +126,8 @@ in
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
permitRootLogin = "no";
|
||||
passwordAuthentication = false;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
|
||||
# affix some UIDs which were historically auto-generated
|
||||
users.users.sshd.uid = config.sane.allocations.sshd-uid;
|
||||
users.groups.polkituser.gid = config.sane.allocations.polkituser-gid;
|
||||
users.groups.sshd.gid = config.sane.allocations.sshd-gid;
|
||||
users.groups.systemd-coredump.gid = config.sane.allocations.systemd-coredump-gid;
|
||||
users.users.nscd.uid = config.sane.allocations.nscd-uid;
|
||||
users.groups.nscd.gid = config.sane.allocations.nscd-gid;
|
||||
users.users.systemd-oom.uid = config.sane.allocations.systemd-oom-uid;
|
||||
users.groups.systemd-oom.gid = config.sane.allocations.systemd-oom-gid;
|
||||
|
||||
# guarantee determinism in uid/gid generation for users:
|
||||
assertions = let
|
||||
uidAssertions = builtins.attrValues (builtins.mapAttrs (name: user: {
|
||||
assertion = user.uid != null;
|
||||
message = "non-deterministic uid detected for: ${name}";
|
||||
}) config.users.users);
|
||||
gidAssertions = builtins.attrValues (builtins.mapAttrs (name: group: {
|
||||
assertion = group.gid != null;
|
||||
message = "non-deterministic gid detected for: ${name}";
|
||||
}) config.users.groups);
|
||||
autoSubAssertions = builtins.attrValues (builtins.mapAttrs (name: user: {
|
||||
assertion = !user.autoSubUidGidRange;
|
||||
message = "non-deterministic subUids/Guids detected for: ${name}";
|
||||
}) config.users.users);
|
||||
in uidAssertions ++ gidAssertions ++ autoSubAssertions;
|
||||
};
|
||||
}
|
||||
|
@@ -1,58 +1,66 @@
|
||||
{ config, ... }:
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
networking.wg-quick.interfaces.ovpnd-us = {
|
||||
# to add a new OVPN VPN:
|
||||
# - generate a privkey `wg genkey`
|
||||
# - add this key to `sops secrets/universal.yaml`
|
||||
# - upload pubkey to OVPN.com
|
||||
# - generate config @ OVPN.com
|
||||
# - copy the Address, PublicKey, Endpoint from OVPN's config
|
||||
# N.B.: maximum interface name in Linux is 15 characters.
|
||||
let
|
||||
def-ovpn = name: { endpoint, publicKey, address }: {
|
||||
networking.wg-quick.interfaces."ovpnd-${name}" = {
|
||||
inherit address;
|
||||
privateKeyFile = config.sops.secrets."wg_ovpnd_${name}_privkey".path;
|
||||
dns = [
|
||||
"46.227.67.134"
|
||||
"192.165.9.158"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
allowedIPs = [
|
||||
"0.0.0.0/0"
|
||||
"::/0"
|
||||
];
|
||||
inherit endpoint publicKey;
|
||||
}
|
||||
];
|
||||
# to start: `systemctl start wg-quick-ovpnd-${name}`
|
||||
autostart = false;
|
||||
};
|
||||
};
|
||||
in lib.mkMerge [
|
||||
(def-ovpn "us" {
|
||||
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
|
||||
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
|
||||
address = [
|
||||
"172.27.237.218/32"
|
||||
"fd00:0000:1337:cafe:1111:1111:ab00:4c8f/128"
|
||||
];
|
||||
dns = [
|
||||
"46.227.67.134"
|
||||
"192.165.9.158"
|
||||
})
|
||||
# NB: us-* share the same wg key and link-local addrs, but distinct public addresses
|
||||
(def-ovpn "us-atl" {
|
||||
endpoint = "vpn18.prd.atlanta.ovpn.com:9929";
|
||||
publicKey = "Dpg/4v5s9u0YbrXukfrMpkA+XQqKIFpf8ZFgyw0IkE0=";
|
||||
address = [
|
||||
"172.21.182.178/32"
|
||||
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
allowedIPs = [
|
||||
"0.0.0.0/0"
|
||||
"::/0"
|
||||
];
|
||||
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
|
||||
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
|
||||
}
|
||||
})
|
||||
(def-ovpn "us-mi" {
|
||||
endpoint = "vpn34.prd.miami.ovpn.com:9929";
|
||||
publicKey = "VtJz2irbu8mdkIQvzlsYhU+k9d55or9mx4A2a14t0V0=";
|
||||
address = [
|
||||
"172.21.182.178/32"
|
||||
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
|
||||
];
|
||||
privateKeyFile = config.sops.secrets.wg_ovpnd_us_privkey.path;
|
||||
# to start: `systemctl start wg-quick-ovpnd-us`
|
||||
autostart = false;
|
||||
};
|
||||
|
||||
networking.wg-quick.interfaces.ovpnd-ukr = {
|
||||
})
|
||||
(def-ovpn "ukr" {
|
||||
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
|
||||
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
|
||||
address = [
|
||||
"172.18.180.159/32"
|
||||
"fd00:0000:1337:cafe:1111:1111:ec5c:add3/128"
|
||||
];
|
||||
dns = [
|
||||
"46.227.67.134"
|
||||
"192.165.9.158"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
allowedIPs = [
|
||||
"0.0.0.0/0"
|
||||
"::/0"
|
||||
];
|
||||
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
|
||||
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
|
||||
}
|
||||
];
|
||||
privateKeyFile = config.sops.secrets.wg_ovpnd_ukr_privkey.path;
|
||||
# to start: `systemctl start wg-quick-ovpnd-ukr`
|
||||
autostart = false;
|
||||
};
|
||||
|
||||
sops.secrets."wg_ovpnd_us_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_ukr_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
}
|
||||
})
|
||||
]
|
||||
|
@@ -1,10 +1,27 @@
|
||||
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
|
||||
|
||||
hostName: { ... }: {
|
||||
# args from flake-level `import`
|
||||
{ hostName, localSystem }:
|
||||
|
||||
# module args
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./${hostName}
|
||||
./by-name/${hostName}
|
||||
./common
|
||||
./modules
|
||||
];
|
||||
|
||||
networking.hostName = hostName;
|
||||
|
||||
nixpkgs.overlays = [
|
||||
(next: prev: {
|
||||
# for local != target we by default just emulate the target while building.
|
||||
# provide a `pkgs.cross.<pkg>` alias that consumers can use instead of `pkgs.<foo>`
|
||||
# to explicitly opt into non-emulated cross compilation for any specific package.
|
||||
# this is most beneficial for large packages with few pre-requisites -- like Linux.
|
||||
cross = next.crossFrom."${localSystem}";
|
||||
})
|
||||
];
|
||||
}
|
||||
|
12
hosts/modules/default.nix
Normal file
12
hosts/modules/default.nix
Normal file
@@ -0,0 +1,12 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./derived-secrets.nix
|
||||
./hardware
|
||||
./hostnames.nix
|
||||
./hosts.nix
|
||||
./roles
|
||||
./wg-home.nix
|
||||
];
|
||||
}
|
47
hosts/modules/derived-secrets.nix
Normal file
47
hosts/modules/derived-secrets.nix
Normal file
@@ -0,0 +1,47 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) toString;
|
||||
inherit (lib) mapAttrs mkOption types;
|
||||
cfg = config.sane.derived-secrets;
|
||||
secret = types.submodule {
|
||||
options = {
|
||||
len = mkOption {
|
||||
type = types.int;
|
||||
};
|
||||
encoding = mkOption {
|
||||
type = types.enum [ "base64" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.derived-secrets = mkOption {
|
||||
type = types.attrsOf secret;
|
||||
default = {};
|
||||
description = ''
|
||||
fs path => secret options.
|
||||
for each entry, we create an item at the given path whose value is deterministic,
|
||||
but also pseudo-random and not predictable by anyone without root access to the machine.
|
||||
as PRNG source we use the host ssh key, and derived secrets are salted based on the destination path.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
sane.fs = mapAttrs (path: c: {
|
||||
generated.script.script = ''
|
||||
echo "$1" | cat /dev/stdin /etc/ssh/host_keys/ssh_host_ed25519_key \
|
||||
| sha512sum \
|
||||
| cut -c 1-${toString (c.len * 2)} \
|
||||
| tr a-z A-Z \
|
||||
| basenc -d --base16 \
|
||||
| basenc --${c.encoding} \
|
||||
> "$1"
|
||||
'';
|
||||
generated.script.scriptArgs = [ path ];
|
||||
generated.acl.mode = "0600";
|
||||
}) cfg;
|
||||
};
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
|
||||
{
|
||||
imports = [
|
||||
./all.nix
|
||||
./x86_64.nix
|
||||
];
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
config = mkIf (pkgs.system == "x86_64-linux") {
|
||||
config = lib.mkIf (pkgs.system == "x86_64-linux") {
|
||||
boot.initrd.availableKernelModules = [
|
||||
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
||||
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
11
hosts/modules/hostnames.nix
Normal file
11
hosts/modules/hostnames.nix
Normal file
@@ -0,0 +1,11 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
# if using router's DNS, these mappings will already exist.
|
||||
# if using a different DNS provider (which servo does), then we need to explicity provide them.
|
||||
# ugly hack. would be better to get servo to somehow use the router's DNS
|
||||
networking.hosts = lib.mapAttrs' (host: cfg: {
|
||||
name = cfg.lan-ip;
|
||||
value = [ host ];
|
||||
}) config.sane.hosts.by-name;
|
||||
}
|
100
hosts/modules/hosts.nix
Normal file
100
hosts/modules/hosts.nix
Normal file
@@ -0,0 +1,100 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) attrValues filterAttrs mkMerge mkOption types;
|
||||
cfg = config.sane.hosts;
|
||||
|
||||
host = types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
ssh.user_pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ssh pubkey that the primary user of this machine will use when connecting to other machines.
|
||||
e.g. "ssh-ed25519 AAAA<base64>".
|
||||
'';
|
||||
};
|
||||
ssh.host_pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ssh pubkey which this host will present to connections initiated against it.
|
||||
e.g. "ssh-ed25519 AAAA<base64>".
|
||||
'';
|
||||
};
|
||||
wg-home.pubkey = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
wireguard public key for the wg-home VPN.
|
||||
e.g. "pWtnKW7f7sNIZQ2M83uJ7cHg3IL1tebE3IoVkCgjkXM=".
|
||||
'';
|
||||
};
|
||||
wg-home.ip = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
IP address to use on the wg-home VPN.
|
||||
e.g. "10.0.10.5";
|
||||
'';
|
||||
};
|
||||
wg-home.endpoint = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
lan-ip = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ip address when on the lan.
|
||||
e.g. "192.168.0.5";
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.hosts.by-name = mkOption {
|
||||
type = types.attrsOf host;
|
||||
default = {};
|
||||
description = ''
|
||||
map of hostname => attrset of information specific to that host,
|
||||
like its ssh pubkey, etc.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# TODO: this should be populated per-host
|
||||
sane.hosts.by-name."desko" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||
wg-home.pubkey = "17PMZssYi0D4t2d0vbmhjBKe1sGsE8kT8/dod0Q2CXc=";
|
||||
wg-home.ip = "10.0.10.22";
|
||||
lan-ip = "192.168.0.22";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."lappy" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
|
||||
wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
|
||||
wg-home.ip = "10.0.10.20";
|
||||
lan-ip = "192.168.0.20";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."moby" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
|
||||
wg-home.pubkey = "I7XIR1hm8bIzAtcAvbhWOwIAabGkuEvbWH/3kyIB1yA=";
|
||||
wg-home.ip = "10.0.10.48";
|
||||
lan-ip = "192.168.0.48";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."servo" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
|
||||
wg-home.pubkey = "roAw+IUFVtdpCcqa4khB385Qcv9l5JAB//730tyK4Wk=";
|
||||
wg-home.ip = "10.0.10.5";
|
||||
wg-home.endpoint = "uninsane.org:51820";
|
||||
lan-ip = "192.168.0.5";
|
||||
};
|
||||
};
|
||||
}
|
18
hosts/modules/roles/client/bluetooth-pairings.nix
Normal file
18
hosts/modules/roles/client/bluetooth-pairings.nix
Normal file
@@ -0,0 +1,18 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = lib.mkIf config.sane.roles.client {
|
||||
# persist external pairings by default
|
||||
sane.persist.sys.plaintext = [ "/var/lib/bluetooth" ];
|
||||
|
||||
sane.fs."/var/lib/bluetooth".generated.acl.mode = "0700";
|
||||
sane.fs."/var/lib/bluetooth/.secrets.stamp" = {
|
||||
wantedBeforeBy = [ "bluetooth.service" ];
|
||||
# XXX: install-bluetooth uses sed, but that's part of the default systemd unit path, it seems
|
||||
generated.script.script = builtins.readFile ../../../../scripts/install-bluetooth + ''
|
||||
touch "/var/lib/bluetooth/.secrets.stamp"
|
||||
'';
|
||||
generated.script.scriptArgs = [ "/run/secrets/bt" ];
|
||||
};
|
||||
};
|
||||
}
|
17
hosts/modules/roles/client/default.nix
Normal file
17
hosts/modules/roles/client/default.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./bluetooth-pairings.nix
|
||||
./wifi-pairings.nix
|
||||
];
|
||||
|
||||
# option is consumed by the other imports in this dir
|
||||
options.sane.roles.client = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
}
|
15
hosts/modules/roles/client/wifi-pairings.nix
Normal file
15
hosts/modules/roles/client/wifi-pairings.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = lib.mkIf config.sane.roles.client {
|
||||
sane.fs."/var/lib/iwd/.secrets.psk.stamp" = {
|
||||
wantedBeforeBy = [ "iwd.service" ];
|
||||
generated.acl.mode = "0600";
|
||||
# XXX: install-iwd uses sed, but that's part of the default systemd unit path, it seems
|
||||
generated.script.script = builtins.readFile ../../../../scripts/install-iwd + ''
|
||||
touch "/var/lib/iwd/.secrets.psk.stamp"
|
||||
'';
|
||||
generated.script.scriptArgs = [ "/run/secrets/iwd" "/var/lib/iwd" ];
|
||||
};
|
||||
};
|
||||
}
|
6
hosts/modules/roles/default.nix
Normal file
6
hosts/modules/roles/default.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./client
|
||||
];
|
||||
}
|
80
hosts/modules/wg-home.nix
Normal file
80
hosts/modules/wg-home.nix
Normal file
@@ -0,0 +1,80 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) filter map;
|
||||
inherit (lib) concatMap mapAttrsToList mkIf mkMerge mkOption optionalAttrs types;
|
||||
cfg = config.sane.services.wg-home;
|
||||
server-cfg = config.sane.hosts.by-name."servo".wg-home;
|
||||
mkPeer = { ips, pubkey, endpoint }: {
|
||||
publicKey = pubkey;
|
||||
allowedIPs = map (k: "${k}/32") ips;
|
||||
} // (optionalAttrs (endpoint != null) {
|
||||
inherit endpoint;
|
||||
# send keepalives every 25 seconds to keep NAT routes live.
|
||||
# only need to do this from client -> server though, i think.
|
||||
persistentKeepalive = 25;
|
||||
# allows wireguard to notice DNS/hostname changes, with this much effective TTL.
|
||||
dynamicEndpointRefreshSeconds = 600;
|
||||
});
|
||||
# make separate peers to route each given host
|
||||
mkClientPeers = hosts: map (p: mkPeer {
|
||||
inherit (p) pubkey endpoint;
|
||||
ips = [ p.ip ];
|
||||
}) hosts;
|
||||
# make a single peer which routes all the given hosts
|
||||
mkServerPeer = hosts: mkPeer {
|
||||
inherit (server-cfg) pubkey endpoint;
|
||||
ips = map (h: h.ip) hosts;
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.services.wg-home.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
sane.services.wg-home.ip = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# generate a (deterministic) wireguard private key
|
||||
sane.derived-secrets."/run/wg-home.priv" = {
|
||||
len = 32;
|
||||
encoding = "base64";
|
||||
};
|
||||
|
||||
# wireguard VPN which allows everything on my domain to speak to each other even when
|
||||
# not behind a shared LAN.
|
||||
# this config defines both the endpoint (server) and client configs
|
||||
|
||||
# for convenience, have both the server and client use the same port for their wireguard connections.
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
networking.wireguard.interfaces.wg-home = {
|
||||
listenPort = 51820;
|
||||
privateKeyFile = "/run/wg-home.priv";
|
||||
preSetup =
|
||||
let
|
||||
gen-key = config.sane.fs."/run/wg-home.priv".unit;
|
||||
in
|
||||
"${pkgs.systemd}/bin/systemctl start '${gen-key}'";
|
||||
|
||||
ips = [
|
||||
"${cfg.ip}/24"
|
||||
];
|
||||
|
||||
peers =
|
||||
let
|
||||
all-peers = mapAttrsToList (_: hostcfg: hostcfg.wg-home) config.sane.hosts.by-name;
|
||||
peer-list = filter (p: p.ip != null && p.ip != cfg.ip && p.pubkey != null) all-peers;
|
||||
in
|
||||
if cfg.ip == server-cfg.ip then
|
||||
# if we're the server, then we maintain the entire client list
|
||||
mkClientPeers peer-list
|
||||
else
|
||||
# but if we're a client, we maintain a single peer -- the server -- which does the actual routing
|
||||
[ (mkServerPeer peer-list) ];
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,140 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
networking.domain = "uninsane.org";
|
||||
|
||||
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
|
||||
# Per-interface useDHCP will be mandatory in the future, so this generated config
|
||||
# replicates the default behaviour.
|
||||
networking.useDHCP = false;
|
||||
networking.interfaces.eth0.useDHCP = true;
|
||||
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
|
||||
networking.wireless.enable = false;
|
||||
|
||||
# networking.firewall.enable = false;
|
||||
networking.firewall.enable = true;
|
||||
# TODO: split these into the submodules
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
25 # SMTP
|
||||
80 # HTTP
|
||||
143 # IMAP
|
||||
443 # HTTPS
|
||||
465 # SMTPS
|
||||
587 # SMTPS/submission
|
||||
993 # IMAPS
|
||||
4001 # IPFS
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
|
||||
4001 # IPFS
|
||||
];
|
||||
|
||||
# we need to use externally-visible nameservers in order for VPNs to be able to resolve hosts.
|
||||
networking.nameservers = [
|
||||
"1.1.1.1"
|
||||
"9.9.9.9"
|
||||
];
|
||||
|
||||
# OVPN CONFIG (https://www.ovpn.com):
|
||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
||||
networking.wireguard.enable = true;
|
||||
networking.wireguard.interfaces.wg0 = {
|
||||
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||
# wg is active only in this namespace.
|
||||
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
|
||||
# sudo ip netns exec ovpns ping www.google.com
|
||||
# note: without the namespace, you'll need to add a specific route through eth0 for the peer (185.157.162.178/32)
|
||||
interfaceNamespace = "ovpns";
|
||||
preSetup = "${pkgs.iproute2}/bin/ip netns add ovpns || true";
|
||||
postShutdown = "${pkgs.iproute2}/bin/ip netns delete ovpns";
|
||||
ips = [
|
||||
"185.157.162.178/32"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
||||
endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
|
||||
allowedIPs = [ "0.0.0.0/0" ];
|
||||
# nixOS says this is important for keeping NATs active
|
||||
persistentKeepalive = 25;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
systemd.services.wg0veth = {
|
||||
description = "veth pair to allow communication between host and wg0 netns";
|
||||
after = [ "wireguard-wg0.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
|
||||
ExecStart = with pkgs; writeScript "wg0veth-start" ''
|
||||
#!${bash}/bin/bash
|
||||
# create veth pair
|
||||
${iproute2}/bin/ip link add ovpns-veth-a type veth peer name ovpns-veth-b
|
||||
${iproute2}/bin/ip addr add 10.0.1.5/24 dev ovpns-veth-a
|
||||
${iproute2}/bin/ip link set ovpns-veth-a up
|
||||
# mv veth-b into the ovpns namespace
|
||||
${iproute2}/bin/ip link set ovpns-veth-b netns ovpns
|
||||
${iproute2}/bin/ip -n ovpns addr add 10.0.1.6/24 dev ovpns-veth-b
|
||||
${iproute2}/bin/ip -n ovpns link set ovpns-veth-b up
|
||||
# forward HTTP traffic, which we need for letsencrypt to work
|
||||
${iproute2}/bin/ip netns exec ovpns ${socat}/bin/socat TCP4-LISTEN:80,reuseaddr,fork,su=nobody TCP4:10.0.1.5:80 &
|
||||
'';
|
||||
|
||||
ExecStop = with pkgs; writeScript "wg0veth-stop" ''
|
||||
#!${bash}/bin/bash
|
||||
${iproute2}/bin/ip -n wg0 link del ovpns-veth-b
|
||||
${iproute2}/bin/ip link del ovpns-veth-a
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
sops.secrets."wg_ovpns_privkey" = {
|
||||
sopsFile = ../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
# HURRICANE ELECTRIC CONFIG:
|
||||
# networking.sits = {
|
||||
# hurricane = {
|
||||
# remote = "216.218.226.238";
|
||||
# local = "192.168.0.5";
|
||||
# # local = "10.0.0.5";
|
||||
# # remote = "10.0.0.1";
|
||||
# # local = "10.0.0.22";
|
||||
# dev = "eth0";
|
||||
# ttl = 255;
|
||||
# };
|
||||
# };
|
||||
# networking.interfaces."hurricane".ipv6 = {
|
||||
# addresses = [
|
||||
# # mx.uninsane.org (publically routed /64)
|
||||
# {
|
||||
# address = "2001:470:b:465::1";
|
||||
# prefixLength = 128;
|
||||
# }
|
||||
# # client addr
|
||||
# # {
|
||||
# # address = "2001:470:a:466::2";
|
||||
# # prefixLength = 64;
|
||||
# # }
|
||||
# ];
|
||||
# routes = [
|
||||
# {
|
||||
# address = "::";
|
||||
# prefixLength = 0;
|
||||
# # via = "2001:470:a:466::1";
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
|
||||
# # after configuration, we want the hurricane device to look like this:
|
||||
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
|
||||
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
|
||||
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
|
||||
# # sit txqueuelen 1000 (IPv6-in-IPv4)
|
||||
# # test with:
|
||||
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
||||
# # ping 2607:f8b0:400a:80b::2004
|
||||
}
|
@@ -1,84 +0,0 @@
|
||||
# docs:
|
||||
# - <https://docs.ejabberd.im/admin/configuration/basic>
|
||||
# example configs:
|
||||
# - 2013: <https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example>
|
||||
{ lib, ... }:
|
||||
|
||||
# XXX disabled: fails to start because of `mnesia_tm` dependency
|
||||
# lib.mkIf false
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
5222 # XMPP client -> server
|
||||
5269 # XMPP server -> server
|
||||
];
|
||||
|
||||
# provide access to certs
|
||||
users.users.ejabberd.extraGroups = [ "nginx" ];
|
||||
|
||||
# TODO: allocate UIDs/GIDs ?
|
||||
services.ejabberd.enable = true;
|
||||
services.ejabberd.configFile = builtins.toFile "ejabberd.yaml" ''
|
||||
hosts:
|
||||
- uninsane.org
|
||||
|
||||
# none | emergency | alert | critical | error | warning | notice | info | debug
|
||||
loglevel: debug
|
||||
|
||||
acme:
|
||||
auto: false
|
||||
certfiles:
|
||||
- /var/lib/acme/uninsane.org/fullchain.pem
|
||||
- /var/lib/acme/uninsane.org/key.pem
|
||||
|
||||
pam_userinfotype: jid
|
||||
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
|
||||
shaper_rules:
|
||||
max_s2s_connections: 3
|
||||
max_user_offline_messages: 5000
|
||||
c2s_shaper:
|
||||
fast: all
|
||||
s2s_shaper:
|
||||
med: all
|
||||
|
||||
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
|
||||
# this limits the bytes/sec.
|
||||
# for example, burst: 3_000_000 and rate: 100_000 means:
|
||||
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
|
||||
shaper:
|
||||
fast: 1000000
|
||||
med: 500000
|
||||
# fast:
|
||||
# - rate: 1000000
|
||||
# - burst_size: 10000000
|
||||
# med:
|
||||
# - rate: 500000
|
||||
# - burst_size: 5000000
|
||||
|
||||
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
|
||||
# TODO: host web admin panel
|
||||
s2s_use_starttls: true
|
||||
listen:
|
||||
-
|
||||
port: 5222
|
||||
module: ejabberd_c2s
|
||||
shaper: c2s_shaper
|
||||
starttls: true
|
||||
-
|
||||
port: 5269
|
||||
module: ejabberd_s2s_in
|
||||
shaper: s2s_shaper
|
||||
-
|
||||
port: 5280
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
/admin: ejabberd_web_admin
|
||||
/api: mod_http_api
|
||||
/bosh: mod_bosh
|
||||
/upload: mod_http_upload
|
||||
/ws: ejabberd_http_ws
|
||||
'';
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
|
||||
];
|
||||
|
||||
# users.users.jellyfin.uid = config.sane.allocations.jellyfin-uid;
|
||||
# users.groups.jellyfin.gid = config.sane.allocations.jellyfin-gid;
|
||||
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
|
||||
# else it's too spammy
|
||||
# services.jellyfin.enable = true;
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.service-dirs = [
|
||||
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
|
||||
];
|
||||
services.navidrome.enable = true;
|
||||
services.navidrome.settings = {
|
||||
# docs: https://www.navidrome.org/docs/usage/configuration-options/
|
||||
Address = "127.0.0.1";
|
||||
Port = 4533;
|
||||
MusicFolder = "/var/lib/uninsane/media/Music";
|
||||
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
|
||||
AutoImportPlaylists = false;
|
||||
ScanSchedule = "@every 1h";
|
||||
};
|
||||
}
|
@@ -1,383 +0,0 @@
|
||||
# docs: https://nixos.wiki/wiki/Nginx
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
let
|
||||
# make the logs for this host "public" so that they show up in e.g. metrics
|
||||
publog = vhost: vhost // {
|
||||
extraConfig = (vhost.extraConfig or "") + ''
|
||||
access_log /var/log/nginx/public.log vcombined;
|
||||
'';
|
||||
};
|
||||
|
||||
kTLS = true; # in-kernel TLS for better perf
|
||||
in
|
||||
{
|
||||
services.nginx.enable = true;
|
||||
services.nginx.appendConfig = ''
|
||||
# use 1 process per core.
|
||||
# may want to increase worker_connections too, but `ulimit -n` must be increased first.
|
||||
worker_processes auto;
|
||||
'';
|
||||
|
||||
# this is the standard `combined` log format, with the addition of $host
|
||||
# so that we have the virtualHost in the log.
|
||||
# KEEP IN SYNC WITH GOACCESS
|
||||
# goaccess calls this VCOMBINED:
|
||||
# - <https://gist.github.com/jyap808/10570005>
|
||||
services.nginx.commonHttpConfig = ''
|
||||
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referrer" "$http_user_agent"';
|
||||
access_log /var/log/nginx/private.log vcombined;
|
||||
'';
|
||||
# sets gzip_comp_level = 5
|
||||
services.nginx.recommendedGzipSettings = true;
|
||||
# enables OCSP stapling (so clients don't need contact the OCSP server -- i do instead)
|
||||
# - doesn't seem to, actually: <https://www.ssllabs.com/ssltest/analyze.html?d=uninsane.org>
|
||||
# caches TLS sessions for 10m
|
||||
services.nginx.recommendedTlsSettings = true;
|
||||
# enables sendfile, tcp_nopush, tcp_nodelay, keepalive_timeout 65
|
||||
services.nginx.recommendedOptimisation = true;
|
||||
|
||||
# web blog/personal site
|
||||
services.nginx.virtualHosts."uninsane.org" = publog {
|
||||
root = "${pkgs.uninsane-dot-org}/share/uninsane-dot-org";
|
||||
# a lot of places hardcode https://uninsane.org,
|
||||
# and then when we mix http + non-https, we get CORS violations
|
||||
# and things don't look right. so force SSL.
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
# for OCSP stapling
|
||||
sslTrustedCertificate = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||
|
||||
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
|
||||
# yes, nginx does not strip the prefix when evaluating against the root.
|
||||
locations."/share".root = "/var/lib/uninsane/root";
|
||||
|
||||
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
|
||||
locations."= /.well-known/matrix/server".extraConfig =
|
||||
let
|
||||
# use 443 instead of the default 8448 port to unite
|
||||
# the client-server and server-server port for simplicity
|
||||
server = { "m.server" = "matrix.uninsane.org:443"; };
|
||||
in ''
|
||||
add_header Content-Type application/json;
|
||||
return 200 '${builtins.toJSON server}';
|
||||
'';
|
||||
locations."= /.well-known/matrix/client".extraConfig =
|
||||
let
|
||||
client = {
|
||||
"m.homeserver" = { "base_url" = "https://matrix.uninsane.org"; };
|
||||
"m.identity_server" = { "base_url" = "https://vector.im"; };
|
||||
};
|
||||
# ACAO required to allow element-web on any URL to request this json file
|
||||
in ''
|
||||
add_header Content-Type application/json;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
return 200 '${builtins.toJSON client}';
|
||||
'';
|
||||
|
||||
# static URLs might not be aware of .well-known (e.g. registration confirmation URLs),
|
||||
# so hack around that.
|
||||
locations."/_matrix" = {
|
||||
proxyPass = "http://127.0.0.1:8008";
|
||||
};
|
||||
locations."/_synapse" = {
|
||||
proxyPass = "http://127.0.0.1:8008";
|
||||
};
|
||||
|
||||
# allow ActivityPub clients to discover how to reach @user@uninsane.org
|
||||
# TODO: waiting on https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3361/
|
||||
# locations."/.well-known/nodeinfo" = {
|
||||
# proxyPass = "http://127.0.0.1:4000";
|
||||
# extraConfig = pleromaExtraConfig;
|
||||
# };
|
||||
};
|
||||
|
||||
# server statistics
|
||||
services.nginx.virtualHosts."sink.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
root = "/var/lib/uninsane/sink";
|
||||
|
||||
locations."/ws" = {
|
||||
proxyPass = "http://127.0.0.1:7890";
|
||||
# XXX not sure how much of this is necessary
|
||||
extraConfig = ''
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 7d;
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# Pleroma server and web interface
|
||||
services.nginx.virtualHosts."fed.uninsane.org" = publog {
|
||||
forceSSL = true; # pleroma redirects to https anyway
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:4000";
|
||||
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
|
||||
extraConfig = ''
|
||||
# XXX colin: this block is in the nixos examples: i don't understand all of it
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
|
||||
if ($request_method = OPTIONS) {
|
||||
return 204;
|
||||
}
|
||||
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Permitted-Cross-Domain-Policies none;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header Referrer-Policy same-origin;
|
||||
add_header X-Download-Options noopen;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
# proxy_set_header Host $http_host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# colin: added this due to Pleroma complaining in its logs
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
client_max_body_size 16m;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# transmission web client
|
||||
services.nginx.virtualHosts."bt.uninsane.org" = {
|
||||
# basicAuth is literally cleartext user/pw, so FORCE this to happen over SSL
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
locations."/" = {
|
||||
# proxyPass = "http://ovpns.uninsane.org:9091";
|
||||
proxyPass = "http://10.0.1.6:9091";
|
||||
};
|
||||
};
|
||||
|
||||
# jackett torrent search
|
||||
services.nginx.virtualHosts."jackett.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
locations."/" = {
|
||||
# proxyPass = "http://ovpns.uninsane.org:9117";
|
||||
proxyPass = "http://10.0.1.6:9117";
|
||||
};
|
||||
};
|
||||
|
||||
# matrix chat server
|
||||
services.nginx.virtualHosts."matrix.uninsane.org" = publog {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
|
||||
# TODO colin: replace this with something helpful to the viewer
|
||||
# locations."/".extraConfig = ''
|
||||
# return 404;
|
||||
# '';
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8008";
|
||||
};
|
||||
# redirect browsers to the web client.
|
||||
# i don't think native matrix clients ever fetch the root.
|
||||
# ideally this would be put behind some user-agent test though.
|
||||
locations."= /" = {
|
||||
return = "301 https://web.matrix.uninsane.org";
|
||||
};
|
||||
|
||||
# locations."/_matrix" = {
|
||||
# proxyPass = "http://127.0.0.1:8008";
|
||||
# };
|
||||
};
|
||||
|
||||
# matrix web client
|
||||
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-element-web
|
||||
services.nginx.virtualHosts."web.matrix.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
|
||||
root = pkgs.element-web.override {
|
||||
conf = {
|
||||
default_server_config."m.homeserver" = {
|
||||
"base_url" = "https://matrix.uninsane.org";
|
||||
"server_name" = "uninsane.org";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# hosted git (web view and for `git <cmd>` use
|
||||
services.nginx.virtualHosts."git.uninsane.org" = publog {
|
||||
forceSSL = true; # gitea complains if served over a different protocol than its config file says
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:3000";
|
||||
};
|
||||
};
|
||||
|
||||
# Jellyfin multimedia server
|
||||
# this is mostly taken from the official jellfin.org docs
|
||||
services.nginx.virtualHosts."jelly.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8096";
|
||||
extraConfig = ''
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
|
||||
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
|
||||
proxy_buffering off;
|
||||
'';
|
||||
};
|
||||
# locations."/web/" = {
|
||||
# proxyPass = "http://127.0.0.1:8096/web/index.html";
|
||||
# extraConfig = ''
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
# proxy_set_header X-Forwarded-Host $http_host;
|
||||
# '';
|
||||
# };
|
||||
locations."/socket" = {
|
||||
proxyPass = "http://127.0.0.1:8096";
|
||||
extraConfig = ''
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."music.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
locations."/".proxyPass = "http://127.0.0.1:4533";
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."rss.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
# the routing is handled by freshrss.nix
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."ipfs.uninsane.org" = {
|
||||
# don't default to ssl upgrades, since this may be dnslink'd from a different domain.
|
||||
# ideally we'd disable ssl entirely, but some places assume it?
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8080";
|
||||
extraConfig = ''
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Ipfs-Gateway-Prefix "";
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# exists only to manage certs for dovecot
|
||||
services.nginx.virtualHosts."imap.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
};
|
||||
# exists only to manage certs for Postfix
|
||||
services.nginx.virtualHosts."mx.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
};
|
||||
services.nginx.virtualHosts."nixcache.uninsane.org" = {
|
||||
addSSL = true;
|
||||
enableACME = true;
|
||||
inherit kTLS;
|
||||
# serverAliases = [ "nixcache" ];
|
||||
locations."/".extraConfig = ''
|
||||
proxy_pass http://localhost:${toString config.services.nix-serve.port};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
'';
|
||||
};
|
||||
|
||||
# serve any site not listed above, if it's static.
|
||||
# because we define it dynamically, SSL isn't trivial. support only http
|
||||
# documented <https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name>
|
||||
services.nginx.virtualHosts."~^(?<domain>.+)$" = {
|
||||
default = true;
|
||||
addSSL = true;
|
||||
enableACME = false;
|
||||
sslCertificate = "/var/www/certs/wildcard/cert.pem";
|
||||
sslCertificateKey = "/var/www/certs/wildcard/key.pem";
|
||||
# sslCertificate = "/var/lib/acme/.minica/cert.pem";
|
||||
# sslCertificateKey = "/var/lib/acme/.minica/key.pem";
|
||||
# serverName = null;
|
||||
locations."/" = {
|
||||
# somehow this doesn't escape -- i get error 400 if i:
|
||||
# curl 'http://..' --resolve '..:80:127.0.0.1'
|
||||
root = "/var/www/sites/$domain";
|
||||
# tryFiles = "$domain/$uri $domain/$uri/ =404";
|
||||
};
|
||||
};
|
||||
|
||||
security.acme.acceptTerms = true;
|
||||
security.acme.defaults.email = "admin.acme@uninsane.org";
|
||||
|
||||
users.users.acme.uid = config.sane.allocations.acme-uid;
|
||||
users.groups.acme.gid = config.sane.allocations.acme-gid;
|
||||
sane.impermanence.service-dirs = [
|
||||
# TODO: mode?
|
||||
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
|
||||
{ user = "colin"; group = "users"; directory = "/var/www/sites"; }
|
||||
];
|
||||
|
||||
# create a self-signed SSL certificate for use with literally any domain.
|
||||
# browsers will reject this, but proxies and local testing tools can be configured
|
||||
# to accept it.
|
||||
system.activationScripts.generate-x509-self-signed.text = ''
|
||||
mkdir -p /var/www/certs/wildcard
|
||||
test -f /var/www/certs/wildcard/key.pem || ${pkgs.openssl}/bin/openssl \
|
||||
req -x509 -newkey rsa:4096 \
|
||||
-keyout /var/www/certs/wildcard/key.pem \
|
||||
-out /var/www/certs/wildcard/cert.pem \
|
||||
-sha256 -nodes -days 3650 \
|
||||
-addext 'subjectAltName=DNS:*' \
|
||||
-subj '/CN=self-signed'
|
||||
chmod 640 /var/www/certs/wildcard/{key,cert}.pem
|
||||
chown root:nginx /var/www/certs/wildcard /var/www/certs/wildcard/{key,cert}.pem
|
||||
'';
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
{ config, ... }:
|
||||
|
||||
# installer docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix
|
||||
{
|
||||
# automatically log in at the virtual consoles.
|
||||
# using root here makes sure we always have an escape hatch
|
||||
services.getty.autologinUser = "root";
|
||||
|
||||
# gitea doesn't create the git user
|
||||
users.users.git = {
|
||||
description = "Gitea Service";
|
||||
home = "/var/lib/gitea";
|
||||
useDefaultShell = true;
|
||||
group = "gitea";
|
||||
uid = config.sane.allocations.git-uid;
|
||||
isSystemUser = true;
|
||||
# sendmail access (not 100% sure if this is necessary)
|
||||
extraGroups = [ "postdrop" ];
|
||||
};
|
||||
|
||||
# this is required to allow pleroma to send email.
|
||||
# raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk.
|
||||
# hack to fix that.
|
||||
users.users.pleroma.extraGroups = [ "postdrop" ];
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
{ lib, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
mkId = id: mkOption {
|
||||
default = id;
|
||||
type = types.int;
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
# legacy servo users, some are inconvenient to migrate
|
||||
sane.allocations.dhcpcd-gid = mkId 991;
|
||||
sane.allocations.dhcpcd-uid = mkId 992;
|
||||
sane.allocations.gitea-gid = mkId 993;
|
||||
sane.allocations.git-uid = mkId 994;
|
||||
sane.allocations.jellyfin-gid = mkId 994;
|
||||
sane.allocations.pleroma-gid = mkId 995;
|
||||
sane.allocations.jellyfin-uid = mkId 996;
|
||||
sane.allocations.acme-gid = mkId 996;
|
||||
sane.allocations.pleroma-uid = mkId 997;
|
||||
sane.allocations.acme-uid = mkId 998;
|
||||
sane.allocations.greeter-uid = mkId 999;
|
||||
sane.allocations.greeter-gid = mkId 999;
|
||||
|
||||
sane.allocations.freshrss-uid = mkId 2401;
|
||||
sane.allocations.freshrss-gid = mkId 2401;
|
||||
|
||||
sane.allocations.colin-uid = mkId 1000;
|
||||
sane.allocations.guest-uid = mkId 1100;
|
||||
|
||||
# found on all hosts
|
||||
sane.allocations.sshd-uid = mkId 2001; # 997
|
||||
sane.allocations.sshd-gid = mkId 2001; # 997
|
||||
sane.allocations.polkituser-gid = mkId 2002; # 998
|
||||
sane.allocations.systemd-coredump-gid = mkId 2003; # 996
|
||||
sane.allocations.nscd-uid = mkId 2004;
|
||||
sane.allocations.nscd-gid = mkId 2004;
|
||||
sane.allocations.systemd-oom-uid = mkId 2005;
|
||||
sane.allocations.systemd-oom-gid = mkId 2005;
|
||||
|
||||
# found on graphical hosts
|
||||
sane.allocations.nm-iodine-uid = mkId 2101; # desko/moby/lappy
|
||||
|
||||
# found on desko host
|
||||
sane.allocations.usbmux-uid = mkId 2204;
|
||||
sane.allocations.usbmux-gid = mkId 2204;
|
||||
|
||||
|
||||
# originally found on moby host
|
||||
sane.allocations.avahi-uid = mkId 2304;
|
||||
sane.allocations.avahi-gid = mkId 2304;
|
||||
sane.allocations.colord-uid = mkId 2305;
|
||||
sane.allocations.colord-gid = mkId 2305;
|
||||
sane.allocations.geoclue-uid = mkId 2306;
|
||||
sane.allocations.geoclue-gid = mkId 2306;
|
||||
sane.allocations.rtkit-uid = mkId 2307;
|
||||
sane.allocations.rtkit-gid = mkId 2307;
|
||||
sane.allocations.feedbackd-gid = mkId 2308;
|
||||
};
|
||||
}
|
12
modules/data/default.nix
Normal file
12
modules/data/default.nix
Normal file
@@ -0,0 +1,12 @@
|
||||
# this directory contains data of a factual nature.
|
||||
# for example, public ssh keys, GPG keys, DNS-type name mappings.
|
||||
#
|
||||
# don't put things like fully-specific ~/.config files in here,
|
||||
# even if they're "relatively unopinionated".
|
||||
|
||||
moduleArgs:
|
||||
|
||||
{
|
||||
feeds = import ./feeds moduleArgs;
|
||||
keys = import ./keys.nix;
|
||||
}
|
51
modules/data/feeds/default.nix
Normal file
51
modules/data/feeds/default.nix
Normal file
@@ -0,0 +1,51 @@
|
||||
{ lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) concatLists concatStringsSep foldl' fromJSON map readDir readFile;
|
||||
inherit (lib) hasSuffix listToAttrs mapAttrsToList removeSuffix splitString;
|
||||
|
||||
# given a path to a .json file relative to sources, construct the best feed object we can.
|
||||
# the .json file could be empty, in which case we make assumptions about the feed based
|
||||
# on its fs path.
|
||||
# Type: feedFromSourcePath :: String -> { name = String; value = feed; }
|
||||
feedFromSourcePath = json-path:
|
||||
assert hasSuffix "/default.json" json-path;
|
||||
let
|
||||
canonical-name = removeSuffix "/default.json" json-path;
|
||||
default-url = "https://${canonical-name}";
|
||||
feed-details = { url = default-url; } // (tryImportJson (./sources/${json-path}));
|
||||
in { name = canonical-name; value = mkFeed feed-details; };
|
||||
|
||||
# TODO: for now, feeds are just ordinary Attrs.
|
||||
# in the future, we'd like to set them up with an update script.
|
||||
mkFeed = { url, ... }@details: details;
|
||||
|
||||
# return an AttrSet representing the json at the provided path,
|
||||
# or {} if the path is empty.
|
||||
tryImportJson = path:
|
||||
let
|
||||
as-str = readFile path;
|
||||
in
|
||||
if as-str == "" then
|
||||
{}
|
||||
else
|
||||
fromJSON as-str;
|
||||
|
||||
sources = enumerateFilePaths ./sources;
|
||||
|
||||
# like `lib.listFilesRecursive` but does not mangle paths.
|
||||
# Type: enumerateFilePaths :: path -> [String]
|
||||
enumerateFilePaths = base:
|
||||
concatLists (
|
||||
mapAttrsToList
|
||||
(name: type:
|
||||
if type == "directory" then
|
||||
# enumerate this directory and then prefix each result with the directory's name
|
||||
map (e: "${name}/${e}") (enumerateFilePaths (base + "/${name}"))
|
||||
else
|
||||
[ name ]
|
||||
)
|
||||
(readDir base)
|
||||
);
|
||||
in
|
||||
listToAttrs (map feedFromSourcePath sources)
|
21
modules/data/feeds/sources/acquired.libsyn.com/default.json
Normal file
21
modules/data/feeds/sources/acquired.libsyn.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1369733,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Every company has a story. Learn the playbooks that built the world’s greatest companies — and how you can apply them as a founder, operator, or investor.",
|
||||
"favicon": null,
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 173,
|
||||
"last_seen": "2023-01-11T15:26:37.515527+00:00",
|
||||
"last_updated": "2022-12-19T07:22:28+00:00",
|
||||
"score": 18,
|
||||
"self_url": "https://acquired.libsyn.com/rss",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"title": "Acquired",
|
||||
"url": "https://acquired.libsyn.com/rss",
|
||||
"velocity": 0.066,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1030773,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Industry veterans, degenerate gamblers & besties Chamath Palihapitiya, Jason Calacanis, David Sacks & David Friedberg cover all things economic, tech, political, social & poker.",
|
||||
"favicon": null,
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 124,
|
||||
"last_seen": "2023-01-11T12:44:53.606606+00:00",
|
||||
"last_updated": "2023-01-06T10:51:00+00:00",
|
||||
"score": 18,
|
||||
"self_url": "https://allinchamathjason.libsyn.com/rss",
|
||||
"site_name": "All-In with Chamath, Jason, Sacks & Friedberg",
|
||||
"site_url": "https://allinchamathjason.libsyn.com",
|
||||
"title": "All-In with Chamath, Jason, Sacks & Friedberg",
|
||||
"url": "https://allinchamathjason.libsyn.com/rss",
|
||||
"velocity": 0.12,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 13316,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "A podcast around the idea of creating a Civilizational Bootstrapper, a set of tools and technology that can be used to replicate the foundations of civilization along with itself.",
|
||||
"favicon": null,
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": true,
|
||||
"item_count": 6,
|
||||
"last_seen": "2023-01-11T16:11:01.720399+00:00",
|
||||
"last_updated": "2022-04-13T19:37:17+00:00",
|
||||
"score": 22,
|
||||
"self_url": "https://anchor.fm/s/34c7232c/podcast/rss",
|
||||
"site_name": "Anchor",
|
||||
"site_url": "https://anchor.fm",
|
||||
"title": "Civboot",
|
||||
"url": "https://anchor.fm/s/34c7232c/podcast/rss",
|
||||
"velocity": 0.009,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 208377,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "applieddivinitystudies@gmail.com",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 20,
|
||||
"last_updated": "2022-12-20T01:23:44.546000+00:00",
|
||||
"score": 26,
|
||||
"self_url": "https://applieddivinitystudies.com/atom.xml",
|
||||
"site_name": "Applied Divinity Studies",
|
||||
"site_url": "https://applieddivinitystudies.com",
|
||||
"title": "Applied Divinity Studies",
|
||||
"url": "https://applieddivinitystudies.com/atom.xml",
|
||||
"velocity": 0.079,
|
||||
"version": "atom10"
|
||||
}
|
21
modules/data/feeds/sources/austinvernon.site/default.json
Normal file
21
modules/data/feeds/sources/austinvernon.site/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 19245,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "Austin Vernon's Blog",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 42,
|
||||
"last_updated": "2023-01-05T00:00:00+00:00",
|
||||
"score": 24,
|
||||
"self_url": "",
|
||||
"site_name": "Austin Vernon - Austin Vernon's Blog",
|
||||
"site_url": "https://austinvernon.site",
|
||||
"title": "Austin Vernon",
|
||||
"url": "https://austinvernon.site/rss.xml",
|
||||
"velocity": 0.063,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/balajis.com/default.json
Normal file
21
modules/data/feeds/sources/balajis.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 453506,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "Balaji Srinivasan's personal blog. Formerly CTO of Coinbase and General Partner at a16z, @balajis is an investor and founder.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 15,
|
||||
"last_updated": "2022-04-28T18:22:11+00:00",
|
||||
"score": 16,
|
||||
"self_url": "https://balajis.com/rss/",
|
||||
"site_name": "Balaji Srinivasan",
|
||||
"site_url": "https://balajis.com",
|
||||
"title": "Balaji Srinivasan",
|
||||
"url": "https://balajis.com/rss/",
|
||||
"velocity": 0.01,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 213052,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "",
|
||||
"favicon": "https://images.squarespace-cdn.com/content/v1/50363cf324ac8e905e7df861/ebdb4645-db93-4967-881d-db698ee59c2c/favicon.ico?format=100w",
|
||||
"favicon_data_uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAB4FBMVEUAAADmRjIBAADmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjL///8mIgRMAAAAnnRSTlMAAAA6W1pZTTESpvnryogzAeaCFKwboQv38vP6X449P0FPe8b8vw5mhe02aAif/VhCZx/h4PVD1xwEmQVt3i1uBwwWPpv0Ybu8vtLx7m/JSf5IniT28DWLOTxAUX3EzSgXaXJxc0URa+elCjSdnKLWGewykUv4VSB87zgmHs8iKbZlCbFXhLfHGOpKY9VTjaOuUDAG2tvLGidGL+iWJUyE2tMAAAJwSURBVHja7df5U1JRGMZxnxItw6Q0FdTCoowyWiSJMLXQrMAyU9ojWxQFc8+MijYL23db+VsbTsCZC9xkbu9JnTnfH58Z+MywvAN5MplMJpPJFjuoRoqszNelVVC4ajUpAxTFMlqjL15bYgAEIbx160sBUQivbAMgCuGVVwCkSKUxmakq9dZUEyM1GzclMtdu3mKJsbZuo0XqAF7p9ko2WnfQIjvBJ6B+FxttuwUgfNxjYuteeoSHfQ1srRaL2OPj/kaBCGA2xkfHAXEI4DzIxhoDNQKeq4m974eaQYu0tFYkOnzE3dYen0xHqW+Xo+NYouMnPGzxdjqFH8iTpwzCT31Xx+lWiD/1pu4zAEiRnt5UqVtf7Esh+Fs5I2fPnU924eKly3+cK/7kM+Bq37XrWbtxE9pOPfoHAmwdHEoiQW9MpRA0fuMBnYcdr0IggVjVkGFNCFtdt9hc7hSA8LnAEp9HRjlC9XLxYGbna2xcgdh6rel5JjQjwORUfDa6FcjY7fFgWr7pf0DusHnmrgIJhJGZVgS4d5/N9gdKJAK6Xyvo746xHg5RIo8A3mP3ExtbbX2gREJPdYny62afJT+vz6NkiGpzL5ghFLG+NAhHXr1mhkik4U0YzFAgfmSWC/K2q0rZVLve8a4lYmCPVyI9ZW3DikIT0zkhJe9rlU02fhj9CE5wJEseXy5ItriwIGINIk97SwmZ8ZEjFvsnZZ+/NJMjc+7ovLKoixwJfEVGlIj6N14iEllGSBhZIkY8swNN6XV+o0TUKlrxH5DBZYZ4xb9c+D7yI5A9/U8ypP6XX6XIPAWw8P94mUwmk8lki95vmJ/g9Xmlv3QAAAAASUVORK5CYII=",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 20,
|
||||
"last_updated": "2022-12-14T09:43:49+00:00",
|
||||
"score": 8,
|
||||
"self_url": "",
|
||||
"site_name": "Benedict Evans",
|
||||
"site_url": "https://www.ben-evans.com",
|
||||
"title": "Essays - Benedict Evans",
|
||||
"url": "https://www.ben-evans.com/benedictevans?format=rss",
|
||||
"velocity": 0.033,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/bitbashing.io/default.json
Normal file
21
modules/data/feeds/sources/bitbashing.io/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 339384,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "Yet another programming blog. Thoughts on software and related misadventures.",
|
||||
"favicon": "https://bitbashing.io/favicon.ico",
|
||||
"favicon_data_uri": "data:image/png;base64,AAABAAEAEBACAAEAAQCwAAAAFgAAACgAAAAQAAAAIAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUFBQAAAAAAP//AAD//wAA7/8AAO//AADv/wAA7/8AAO//AADvAwAA7/8AAO8DAADv/wAA7/8AAO//AADv/wAA//8AAP//AAD//wAA//8AAO//AADv/wAA7/8AAO//AADv/wAA7wMAAO//AADvAwAA7/8AAO//AADv/wAA7/8AAP//AAD//wAA",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 10,
|
||||
"last_updated": "2022-11-22T00:00:00+00:00",
|
||||
"score": 20,
|
||||
"self_url": "https://bitbashing.io/feed.xml",
|
||||
"site_name": "Bit Bashing",
|
||||
"site_url": "https://bitbashing.io",
|
||||
"title": "Bit Bashing",
|
||||
"url": "https://bitbashing.io/feed.xml",
|
||||
"velocity": 0.003,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/blog.danieljanus.pl/default.json
Normal file
21
modules/data/feeds/sources/blog.danieljanus.pl/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 1,
|
||||
"content_length": 343256,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": null,
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 60,
|
||||
"last_updated": "2022-11-07T00:00:00+00:00",
|
||||
"score": -4,
|
||||
"self_url": "",
|
||||
"site_name": "Daniel Janus \u2013 blog",
|
||||
"site_url": "https://blog.danieljanus.pl",
|
||||
"title": "code \u00b7 words \u00b7 emotions: Daniel Janus\u2019s blog",
|
||||
"url": "https://blog.danieljanus.pl/atom.xml",
|
||||
"velocity": 0.011,
|
||||
"version": "atom10"
|
||||
}
|
23
modules/data/feeds/sources/blog.dshr.org/default.json
Normal file
23
modules/data/feeds/sources/blog.dshr.org/default.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 623592,
|
||||
"content_type": "application/atom+xml; charset=utf-8",
|
||||
"description": "I'm David Rosenthal, and this is a place to discuss the work I'm doing in Digital Preservation.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [
|
||||
"http://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": false,
|
||||
"is_push": true,
|
||||
"item_count": 25,
|
||||
"last_updated": "2023-01-10T17:59:42.157000+00:00",
|
||||
"score": 20,
|
||||
"self_url": "https://www.blogger.com/feeds/4503292949532760618/posts/default",
|
||||
"site_name": "DSHR's Blog",
|
||||
"site_url": "https://blog.dshr.org",
|
||||
"title": "DSHR's Blog",
|
||||
"url": "https://blog.dshr.org/feeds/posts/default",
|
||||
"velocity": 0.35,
|
||||
"version": "atom10"
|
||||
}
|
21
modules/data/feeds/sources/bunniestudios.com/default.json
Normal file
21
modules/data/feeds/sources/bunniestudios.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 45559,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "bunnie's blog",
|
||||
"favicon": "https://www.bunniestudios.com/favicon.ico",
|
||||
"favicon_data_uri": "data:image/png;base64,AAABAAEAEBAAAAAAAABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAFBQUADU1NQBdXV0AoKCgACYmJgBpaWkATk5OAIODgwAKCgoAfHx8ALi4uACxsbEAAgICAKOjowA+Pj4AAQEBAERERADy8vIABwcHABsbGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAASAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAALAAAAAAAAAAAAABMAAAAAAAAAAAAAAAAABQAACgAAAAAPAAAAAAAAAAkAAAAAAAAOAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCQAAAAAAAAAAAAAAAAAAABANAAAAAAAAAAAAAAAAAP//AADAfQAAwTgAAMeZAADPnwAAz88AAM/PAADPzwAAz58AAMefAADDHwAAyD8AAM//AADP/wAAD/8AAA//AAA=",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 5,
|
||||
"last_updated": "2023-01-04T15:49:57+00:00",
|
||||
"score": 20,
|
||||
"self_url": "https://www.bunniestudios.com/blog/?feed=rss2",
|
||||
"site_name": " bunnie's blog",
|
||||
"site_url": "https://www.bunniestudios.com",
|
||||
"title": "bunnie's blog",
|
||||
"url": "https://www.bunniestudios.com/blog/?feed=rss2",
|
||||
"velocity": 0.114,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 15076852,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Congressional Dish is a twice-monthly podcast that aims to draw attention to where the American people truly have power: Congress. From the perspective of a fed up taxpayer with no allegiance to any political party, Jennifer Briney will fill you in on the must-know information about what our representatives do AFTER the elections and how their actions can and will affect our day to day lives. \nHosted by @JenBriney. \n\nLinks to information sources available at www.congressionaldish.com",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 269,
|
||||
"last_updated": "2023-01-06T18:13:57+00:00",
|
||||
"score": 0,
|
||||
"self_url": "https://feeds.libsyn.com/39908/rss",
|
||||
"site_name": "",
|
||||
"site_url": "",
|
||||
"title": "Congressional Dish",
|
||||
"url": "https://feeds.libsyn.com/39908/rss",
|
||||
"velocity": 0.071,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/craphound.com/default.json
Normal file
21
modules/data/feeds/sources/craphound.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 56666,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Cory Doctorow's Literary Works",
|
||||
"favicon": "https://craphound.com/favicon.ico",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 20,
|
||||
"last_seen": "2023-01-11T12:55:10.545856+00:00",
|
||||
"last_updated": "2022-12-12T14:46:35+00:00",
|
||||
"score": 12,
|
||||
"self_url": "https://craphound.com/feed/",
|
||||
"site_name": "Cory Doctorow's craphound.com | Cory Doctorow's Literary Works",
|
||||
"site_url": "https://craphound.com",
|
||||
"title": "Cory Doctorow's craphound.com",
|
||||
"url": "https://craphound.com/feed/",
|
||||
"velocity": 0.069,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/darknetdiaries.com/default.json
Normal file
21
modules/data/feeds/sources/darknetdiaries.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 227480,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "True stories from the dark side of the Internet",
|
||||
"favicon": "https://darknetdiaries.com/imgs/favicon.png",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 131,
|
||||
"last_seen": "2023-01-11T14:49:53.136566+00:00",
|
||||
"last_updated": "2022-12-27T08:00:00+00:00",
|
||||
"score": 20,
|
||||
"self_url": "https://darknetdiaries.com/feedfree.xml",
|
||||
"site_name": "Darknet Diaries – True stories from the dark side of the Internet.",
|
||||
"site_url": "https://darknetdiaries.com",
|
||||
"title": "Darknet Diaries (ad free)",
|
||||
"url": "https://darknetdiaries.com/feedfree.xml",
|
||||
"velocity": 0.067,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/drewdevault.com/default.json
Normal file
21
modules/data/feeds/sources/drewdevault.com/default.json
Normal file
File diff suppressed because one or more lines are too long
21
modules/data/feeds/sources/econlib.org/default.json
Normal file
21
modules/data/feeds/sources/econlib.org/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 66775,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "The Library of Economics and Liberty",
|
||||
"favicon": null,
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 10,
|
||||
"last_seen": "2023-01-11T10:46:38.526754+00:00",
|
||||
"last_updated": "2023-01-10T05:21:31+00:00",
|
||||
"score": 14,
|
||||
"self_url": "https://www.econlib.org/feed/",
|
||||
"site_name": "Econlib",
|
||||
"site_url": "https://www.econlib.org",
|
||||
"title": "Econlib",
|
||||
"url": "https://www.econlib.org/feed/",
|
||||
"velocity": 2.549,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/econtalk.org/default.json
Normal file
21
modules/data/feeds/sources/econtalk.org/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 27185,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "The Library of Economics and Liberty",
|
||||
"favicon": null,
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 10,
|
||||
"last_seen": "2023-01-11T13:05:47.318206+00:00",
|
||||
"last_updated": "2023-01-09T11:30:25+00:00",
|
||||
"score": 14,
|
||||
"self_url": "https://www.econtalk.org/feed/",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"title": "EconTalk Podcast – Econlib",
|
||||
"url": "https://www.econtalk.org/feed",
|
||||
"velocity": 0.143,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 429348,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "The world's most famous whistleblower writes from exile on the intersection of technology, humanity, and power.",
|
||||
"favicon": "https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/2a7d3aa2-3c2f-4196-ab7c-31541be1272e/favicon.ico",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 16,
|
||||
"last_seen": "2023-01-11T12:32:02.320483+00:00",
|
||||
"last_updated": "2022-09-20T13:03:59+00:00",
|
||||
"score": 14,
|
||||
"self_url": "https://edwardsnowden.substack.com/feed",
|
||||
"site_name": "Continuing Ed — with Edward Snowden",
|
||||
"site_url": "https://edwardsnowden.substack.com",
|
||||
"title": "Continuing Ed — with Edward Snowden",
|
||||
"url": "https://edwardsnowden.substack.com/feed",
|
||||
"velocity": 0.032,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 281377,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "Matrix Live, now as an audio podcast",
|
||||
"favicon": "https://feed.podbean.com/favicon.ico",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 100,
|
||||
"last_seen": "2023-01-11T15:54:24.440541+00:00",
|
||||
"last_updated": "2023-01-06T16:45:00+00:00",
|
||||
"score": 18,
|
||||
"self_url": "https://feed.podbean.com/matrixlive/feed.xml",
|
||||
"site_name": null,
|
||||
"site_url": "https://feed.podbean.com",
|
||||
"title": "Matrix Live",
|
||||
"url": "https://feed.podbean.com/matrixlive/feed.xml",
|
||||
"velocity": 0.12,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1600578,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "Design is everywhere in our lives, perhaps most importantly in the places where we've just stopped noticing. 99% Invisible is a weekly exploration of the process and power of design and architecture. From award winning producer Roman Mars. Learn more at 99percentinvisible.org.",
|
||||
"favicon": null,
|
||||
"hubs": [
|
||||
"https://simplecast.superfeedr.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": true,
|
||||
"item_count": 577,
|
||||
"last_seen": "2023-01-11T15:25:01.536556+00:00",
|
||||
"last_updated": "2023-01-10T23:46:05+00:00",
|
||||
"score": 4,
|
||||
"self_url": "https://feeds.simplecast.com/BqbsxVfO",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"title": "99% Invisible",
|
||||
"url": "https://feeds.simplecast.com/BqbsxVfO",
|
||||
"velocity": 0.128,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1505641,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "<p>Unusually in-depth conversations about the world's most pressing problems and what you can do to solve them.<br /></p>",
|
||||
"favicon": null,
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": true,
|
||||
"item_count": 181,
|
||||
"last_seen": "2023-01-11T13:29:43.501516+00:00",
|
||||
"last_updated": "2023-01-09T22:57:00+00:00",
|
||||
"score": 14,
|
||||
"self_url": "https://feeds.backtracks.fm/feeds/80000hours/80000-hours-podcast-with-rob-wiblin/feed.xml",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"title": "80,000 Hours Podcast with Rob Wiblin",
|
||||
"url": "https://feeds.feedburner.com/80000HoursPodcast",
|
||||
"velocity": 0.087,
|
||||
"version": "rss20"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user