Compare commits
555 Commits
testing/mu
...
wip/overla
Author | SHA1 | Date | |
---|---|---|---|
5801da97f3 | |||
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 | |||
e8a7a1dc75 | |||
992efc1093 | |||
d320fa39f3 | |||
e40156ed9a | |||
656837c810 | |||
0533ea1cc2 | |||
a1911f3001 | |||
24967c53a7 | |||
8b9c18aee1 | |||
8d3acb104a | |||
69eacf6c4d | |||
d7ad414a9c | |||
533b0a91bd | |||
56d87da650 | |||
3f33b2cb76 | |||
f8a1df790f | |||
e94186e9c9 | |||
82d11a7ae1 | |||
0253774622 | |||
2f45c57310 | |||
5d1e8f5f60 | |||
ff9c26b03d | |||
b9533d7ee3 | |||
103f7b1b2c | |||
16327fd323 | |||
abcfa2dbea | |||
27403fa36d | |||
96b3ac26dd | |||
1accf264cf | |||
3772a428da | |||
a56f2008d3 | |||
04ea55499a | |||
59244fa50c | |||
c2a2b27002 | |||
7bd6015a9f | |||
2a010f7882 | |||
b566910da0 | |||
ca43811c16 | |||
7284452aa5 | |||
f772300d88 | |||
eccb5ff3d6 | |||
0c6b949a72 | |||
9a6c83776d | |||
e408e77026 | |||
e0612ccfa8 | |||
a0e85ff31b | |||
1d448a4114 | |||
ed52b5f251 | |||
dc21b0d68c | |||
18ec4f9b4d | |||
84a17f4599 | |||
43fa7fdd9f | |||
8fc6b05c07 | |||
439c7d9ef2 | |||
9633c4f012 | |||
b869617b09 | |||
ce323ffcf9 | |||
ac153aecd3 | |||
353d97b661 | |||
1150ee4b50 | |||
9e51eafff0 | |||
afaa6343ab | |||
67dff6069c | |||
dea7ca9474 | |||
ad7ae94501 | |||
1a0bd16b44 | |||
56f89bb3f7 | |||
92a67253c3 | |||
8d0ded0ea1 | |||
de820e32b7 | |||
be286cd190 | |||
7cacbd9580 | |||
c84f10e060 | |||
fd8f660ee0 | |||
205b6a9afb | |||
6b7a544df3 | |||
c3eacf7126 | |||
3b6f638f98 | |||
6057a2e665 | |||
f45b032e48 | |||
1c810dc1b8 | |||
fdd9833b01 | |||
25854d3135 | |||
45f8cc3894 | |||
fc4138327a | |||
889c47e884 | |||
f6f500c592 | |||
6fa9fb740a | |||
10a665d11c | |||
77baf03496 | |||
26f920e119 | |||
88fba6f496 | |||
9f43444f0c | |||
b68fd881e4 | |||
5cca6ede0d | |||
3b4e394ce8 | |||
d9b3fccdfa | |||
94366d4bf6 | |||
12b5e68b25 | |||
a0d332766a | |||
cdd9672654 | |||
887a431956 | |||
502ebafb0a | |||
57ada6af4f | |||
d1d64b7376 | |||
f2188be9f2 | |||
6d52c0e8ab | |||
14b334ff55 | |||
730fa8ba4b | |||
8817f661ac | |||
3b0f505864 | |||
b559d334c3 | |||
f6e4c0058c | |||
775fc979fc | |||
ad6daa4e5b | |||
3ecfea158a | |||
5ff47b3719 | |||
03ea7e7fa5 | |||
ca93518dda | |||
7f7041351b | |||
1c62bcd50c | |||
bad4fe0e76 | |||
8b473ff88f | |||
ad54b9c5fb | |||
b805a101ba | |||
69a3aaa086 | |||
9acf2dfde1 | |||
4b5accac88 | |||
cb00ae4f92 | |||
7c38c1dbe9 | |||
b3b45ec0f2 | |||
34d77542e7 | |||
6236c14def | |||
0c0f8c44bd | |||
7f97786a88 | |||
db2e156f15 | |||
43efec495e | |||
279f9ce614 | |||
7d02652e08 | |||
10e224be0d | |||
e25c92794f | |||
a8d2b7196d | |||
a6cbecbc74 | |||
518d2f60c0 | |||
70e5ccc968 | |||
c44cad9c16 | |||
e3bf585382 | |||
1fea9618ba | |||
8d89f828b6 | |||
e2985ef018 | |||
d54b595e45 | |||
ad75ed352c | |||
306836042c | |||
965181c8b0 | |||
b344c38bfb | |||
174bc539bc | |||
9ef457c0dd | |||
939278b970 | |||
3d0bd0fbf4 | |||
36d8a711ac | |||
4c4b73f693 | |||
9151f58b37 | |||
b2c55ed98a | |||
1721546410 | |||
c833c68d83 | |||
9a4c2613c1 | |||
8de5b0a79d | |||
ced64e63ef | |||
8dd267db30 | |||
10541698a7 | |||
b658b93c64 | |||
f68bc342e8 | |||
e3221bf8b9 | |||
3cfe236e90 | |||
2b14648587 | |||
0753aa59e9 | |||
55cbce17c2 | |||
ebf3152ced | |||
8345375bc4 | |||
cc63cacf28 | |||
8f61ba6085 | |||
b43103a024 | |||
187a52527b | |||
b26e826b3b | |||
3851136398 | |||
635fee1bda | |||
5048ee1ce5 | |||
e787dc29c6 | |||
7cc44f9455 | |||
419ababe6f | |||
e4c0a0d468 | |||
0e63cd4e11 | |||
9328e5ff32 | |||
87dda0ad11 | |||
46783cd0e2 | |||
f7d3b8128e | |||
9119f0b092 | |||
17189b22e9 | |||
7db3816511 | |||
8c20017544 | |||
4c1f68f82f | |||
289745f41a | |||
d9caf70c6c | |||
cf95a6e321 | |||
155c095be8 | |||
bafe7aa3c7 | |||
c9d57f2995 | |||
a8227bbcbc | |||
1623367b13 |
14
.sops.yaml
14
.sops.yaml
@@ -23,6 +23,7 @@ creation_rules:
|
|||||||
key_groups:
|
key_groups:
|
||||||
- age:
|
- age:
|
||||||
- *user_desko_colin
|
- *user_desko_colin
|
||||||
|
- *user_lappy_colin
|
||||||
- *user_servo_colin
|
- *user_servo_colin
|
||||||
- *host_servo
|
- *host_servo
|
||||||
- path_regex: secrets/desko.yaml$
|
- path_regex: secrets/desko.yaml$
|
||||||
@@ -31,3 +32,16 @@ creation_rules:
|
|||||||
- *user_desko_colin
|
- *user_desko_colin
|
||||||
- *user_lappy_colin
|
- *user_lappy_colin
|
||||||
- *host_desko
|
- *host_desko
|
||||||
|
- path_regex: secrets/lappy.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *user_lappy_colin
|
||||||
|
- *user_desko_colin
|
||||||
|
- *host_lappy
|
||||||
|
- path_regex: secrets/moby.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *user_desko_colin
|
||||||
|
- *user_lappy_colin
|
||||||
|
- *user_moby_colin
|
||||||
|
- *host_moby
|
||||||
|
16
TODO.md
16
TODO.md
@@ -1,16 +0,0 @@
|
|||||||
# features/tweaks
|
|
||||||
- emoji picker application
|
|
||||||
- find a Masto/Pleroma app which works on mobile
|
|
||||||
- remove hardcoded uid/gids outside of allocations.nix (used in impermanence code -- replace with username/groupname)
|
|
||||||
|
|
||||||
|
|
||||||
# speed up cross compiling
|
|
||||||
- <https://nixos.wiki/wiki/Cross_Compiling>
|
|
||||||
- <https://nixos.wiki/wiki/NixOS_on_ARM>
|
|
||||||
```nix
|
|
||||||
overlays = [{ ... }: {
|
|
||||||
nixpkgs.crossSystem.system = "aarch64-linux";
|
|
||||||
}];
|
|
||||||
```
|
|
||||||
- <https://github.com/nix-community/aarch64-build-box>
|
|
||||||
- apply for access to the community arm build box
|
|
134
flake.lock
generated
134
flake.lock
generated
@@ -22,11 +22,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665475263,
|
"lastModified": 1667907331,
|
||||||
"narHash": "sha256-T4at7d+KsQNWh5rfjvOtQCaIMWjSDlSgQZKvxb+LcEY=",
|
"narHash": "sha256-bHkAwkYlBjkupPUFcQjimNS8gxWSWjOTevEuwdnp5m0=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "17208be516fc36e2ab0ceb064d931e90eb88b2a3",
|
"rev": "6639e3a837fc5deb6f99554072789724997bc8e5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -36,29 +36,14 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"impermanence": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1661933071,
|
|
||||||
"narHash": "sha256-RFgfzldpbCvS+H2qwH+EvNejvqs+NhPVD5j1I7HQQPY=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "impermanence",
|
|
||||||
"rev": "def994adbdfc28974e87b0e4c949e776207d5557",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "impermanence",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mobile-nixos": {
|
"mobile-nixos": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665711470,
|
"lastModified": 1670131242,
|
||||||
"narHash": "sha256-9cjKbTkxyWjswVExtIpglpvlR+iDY9/DWmXpZyzk5cY=",
|
"narHash": "sha256-T/o1/3gffr010fsqgNshs1NJJjsnUYvQnUZgm6hilsY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "mobile-nixos",
|
"repo": "mobile-nixos",
|
||||||
"rev": "e4b6f680b2a4f29f087a7c1299c11499d1a367b6",
|
"rev": "5ee45cc1f8e43f4af14ee17ccef9156b0db8cd77",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -68,12 +53,60 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-unpatched"
|
||||||
|
]
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665732960,
|
"lastModified": 1,
|
||||||
"narHash": "sha256-WBZ+uSHKFyjvd0w4inbm0cNExYTn8lpYFcHEes8tmec=",
|
"narHash": "sha256-5eJxyBRYQCoRt92ZFUOdT237Z0VscuNRd0pktDYWJYE=",
|
||||||
|
"path": "nixpatches",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"path": "nixpatches",
|
||||||
|
"type": "path"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673163619,
|
||||||
|
"narHash": "sha256-B33PFBL64ZgTWgMnhFL3jgheAN/DjHPsZ1Ih3z0VE5I=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "4428e23312933a196724da2df7ab78eb5e67a88e",
|
"rev": "8c54d842d9544361aac5f5b212ba04e4089e8efe",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-22.11",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673100377,
|
||||||
|
"narHash": "sha256-mT76pTd0YFxT6CwtPhDgHJhuIgLY+ZLSMiQpBufwMG4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "9f11a2df77cb945c115ae2a65f53f38121597d73",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "release-22.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-unpatched": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673226411,
|
||||||
|
"narHash": "sha256-b6cGb5Ln7Zy80YO66+cbTyGdjZKtkoqB/iIIhDX9gRA=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "aa1d74709f5dac623adb4d48fdfb27cc2c92a4d4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -82,46 +115,15 @@
|
|||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-22_05": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1665279158,
|
|
||||||
"narHash": "sha256-TpbWNzoJ5RaZ302dzvjY2o//WxtOJuYT3CnDj5N69Hs=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "b3783bcfb8ec54e0de26feccfc6cc36b8e202ed5",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "release-22.05",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-stable": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1665613119,
|
|
||||||
"narHash": "sha256-VTutbv5YKeBGWou6ladtgfx11h6et+Wlkdyh4jPJ3p0=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "e06bd4b64bbfda91d74f13cb5eca89485d47528f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"id": "nixpkgs",
|
|
||||||
"ref": "nixos-22.05",
|
|
||||||
"type": "indirect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"impermanence": "impermanence",
|
|
||||||
"mobile-nixos": "mobile-nixos",
|
"mobile-nixos": "mobile-nixos",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs-stable": "nixpkgs-stable",
|
"nixpkgs-stable": "nixpkgs-stable",
|
||||||
|
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"uninsane": "uninsane"
|
"uninsane-dot-org": "uninsane-dot-org"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sops-nix": {
|
"sops-nix": {
|
||||||
@@ -129,14 +131,14 @@
|
|||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"nixpkgs-22_05": "nixpkgs-22_05"
|
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665289655,
|
"lastModified": 1673147300,
|
||||||
"narHash": "sha256-j1Q9mNBhbzeJykhObiXwEGres9qvP4vH7gxdJ+ihkLI=",
|
"narHash": "sha256-gR9OEfTzWfL6vG0qkbn1TlBAOlg4LuW8xK/u0V41Ihc=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "0ce0449e6404c4ff9d1b7bd657794ae5ca54deb3",
|
"rev": "2253120d2a6147e57bafb5c689e086221df8032f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -145,7 +147,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uninsane": {
|
"uninsane-dot-org": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -153,11 +155,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665758541,
|
"lastModified": 1666870107,
|
||||||
"narHash": "sha256-ibR8bPwHlDjavri5cNVnoo5FmFk1IfNMmQXxat5biqs=",
|
"narHash": "sha256-b9eXZxSwhzdJI5uQgfrMhu4SY2POrPkinUg7F5gQVYo=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "4ad1801f6cecd678bbeae5dfe5933448dd7b3360",
|
"rev": "80c6ec95bd430e29d231cf745f19279bb76fb382",
|
||||||
"revCount": 163,
|
"revCount": 164,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.uninsane.org/colin/uninsane"
|
"url": "https://git.uninsane.org/colin/uninsane"
|
||||||
},
|
},
|
||||||
|
204
flake.nix
204
flake.nix
@@ -4,8 +4,12 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs-stable.url = "nixpkgs/nixos-22.05";
|
nixpkgs-stable.url = "nixpkgs/nixos-22.11";
|
||||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
nixpkgs-unpatched.url = "nixpkgs/nixos-unstable";
|
||||||
|
nixpkgs = {
|
||||||
|
url = "path:nixpatches";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
|
};
|
||||||
mobile-nixos = {
|
mobile-nixos = {
|
||||||
url = "github:nixos/mobile-nixos";
|
url = "github:nixos/mobile-nixos";
|
||||||
flake = false;
|
flake = false;
|
||||||
@@ -18,62 +22,63 @@
|
|||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
impermanence.url = "github:nix-community/impermanence";
|
uninsane-dot-org = {
|
||||||
uninsane = {
|
|
||||||
url = "git+https://git.uninsane.org/colin/uninsane";
|
url = "git+https://git.uninsane.org/colin/uninsane";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, nixpkgs-stable, mobile-nixos, home-manager, sops-nix, impermanence, uninsane }:
|
outputs = {
|
||||||
let
|
self,
|
||||||
patchedPkgs = system: nixpkgs.legacyPackages.${system}.applyPatches {
|
nixpkgs,
|
||||||
name = "nixpkgs-patched-uninsane";
|
nixpkgs-stable,
|
||||||
src = nixpkgs;
|
nixpkgs-unpatched,
|
||||||
patches = import ./nixpatches/list.nix nixpkgs.legacyPackages.${system}.fetchpatch;
|
mobile-nixos,
|
||||||
};
|
home-manager,
|
||||||
# return something which behaves like `pkgs`, for the provided system
|
sops-nix,
|
||||||
# `local` = architecture of builder. `target` = architecture of the system beying deployed to
|
uninsane-dot-org
|
||||||
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-machine = { name, local, target }:
|
|
||||||
let
|
let
|
||||||
nixosSystem = import ((patchedPkgs target) + "/nixos/lib/eval-config.nix");
|
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
|
||||||
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
|
|
||||||
./machines/${name}
|
|
||||||
(import ./helpers/set-hostname.nix name)
|
|
||||||
home-manager.nixosModule
|
|
||||||
impermanence.nixosModule
|
|
||||||
sops-nix.nixosModules.sops
|
|
||||||
{
|
|
||||||
nixpkgs.config.allowUnfree = true;
|
|
||||||
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; };
|
|
||||||
# pinned packages:
|
|
||||||
electrum = stable.electrum; # 2022-10-10: build break
|
|
||||||
sequoia = stable.sequoia; # 2022-10-13: build break
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
decl-bootable-machine = { name, local, target }: rec {
|
evalHost = { name, local, target }:
|
||||||
nixosConfiguration = decl-machine { inherit name local target; };
|
let
|
||||||
|
# XXX: we'd prefer to use `nixosSystem = (nixpkgsCompiledBy local).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.
|
||||||
|
nixosSystem = import ((nixpkgsCompiledBy local).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
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
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).
|
# this produces a EFI-bootable .img file (GPT with a /boot partition and a system (/ or /nix) partition).
|
||||||
# after building this:
|
# after building this:
|
||||||
# - flash it to a bootable medium (SD card, flash drive, HDD)
|
# - flash it to a bootable medium (SD card, flash drive, HDD)
|
||||||
@@ -86,37 +91,76 @@
|
|||||||
# - boot
|
# - boot
|
||||||
# - if fs wasn't resized automatically, then `sudo btrfs filesystem resize max /`
|
# - if fs wasn't resized automatically, then `sudo btrfs filesystem resize max /`
|
||||||
# - checkout this flake into /etc/nixos AND UPDATE THE FS UUIDS.
|
# - checkout this flake into /etc/nixos AND UPDATE THE FS UUIDS.
|
||||||
# - `nixos-rebuild --flake './#<machine>' switch`
|
# - `nixos-rebuild --flake './#<host>' switch`
|
||||||
img = nixosConfiguration.config.system.build.img;
|
imgs = builtins.mapAttrs (_: host-dfn: host-dfn.config.system.build.img) self.nixosConfigurations;
|
||||||
};
|
|
||||||
machines.servo = decl-bootable-machine { name = "servo"; local = "aarch64-linux"; target = "aarch64-linux"; };
|
overlays = rec {
|
||||||
machines.desko = decl-bootable-machine { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
default = pkgs;
|
||||||
machines.lappy = decl-bootable-machine { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
pkgs = import ./pkgs/overlay.nix;
|
||||||
machines.moby = decl-bootable-machine { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
|
passthru =
|
||||||
# special cross-compiled variant, to speed up deploys from an x86 box to the arm target
|
let
|
||||||
# note that these *do* produce different store paths, because the closure for the tools used to cross compile
|
stable = next: prev: {
|
||||||
# v.s. emulate differ.
|
stable = nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform}";
|
||||||
# so deploying moby-cross and then moby incurs some rebuilding.
|
};
|
||||||
machines.moby-cross = decl-bootable-machine { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||||
machines.rescue = decl-bootable-machine { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
uninsane = uninsane-dot-org.overlay;
|
||||||
in {
|
in
|
||||||
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) machines;
|
next: prev:
|
||||||
imgs = builtins.mapAttrs (name: value: value.img) machines;
|
(stable next prev) // (mobile next prev) // (uninsane next prev);
|
||||||
packages = let
|
|
||||||
custom-x86_64 = customPackagesFor "x86_64-linux" "x86_64-linux";
|
|
||||||
custom-aarch64 = customPackagesFor "aarch64-linux" "aarch64-linux";
|
|
||||||
nixpkgs-x86_64 = nixpkgsFor "x86_64-linux" "x86_64-linux";
|
|
||||||
nixpkgs-aarch64 = nixpkgsFor "aarch64-linux" "aarch64-linux";
|
|
||||||
in {
|
|
||||||
x86_64-linux = custom-x86_64 // {
|
|
||||||
nixpkgs = nixpkgs-x86_64;
|
|
||||||
uninsane = uninsane.packages.x86_64-linux;
|
|
||||||
};
|
};
|
||||||
aarch64-linux = custom-aarch64 // {
|
|
||||||
nixpkgs = nixpkgs-aarch64;
|
nixosModules = rec {
|
||||||
uninsane = uninsane.packages.aarch64-linux;
|
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 = {
|
||||||
|
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";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
hostName: { ... }:
|
|
||||||
{
|
|
||||||
networking.hostName = hostName;
|
|
||||||
}
|
|
16
hosts/common/bluetooth.nix
Normal file
16
hosts/common/bluetooth.nix
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# 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" ];
|
||||||
|
};
|
||||||
|
}
|
15
hosts/common/cross.nix
Normal file
15
hosts/common/cross.nix
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# 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" = (prev.forceSystem "x86_64-linux" null).appendOverlays next.overlays;
|
||||||
|
crossFrom."aarch64-linux" = (prev.forceSystem "aarch64-linux" null).appendOverlays next.overlays;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
77
hosts/common/default.nix
Normal file
77
hosts/common/default.nix
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./bluetooth.nix
|
||||||
|
./cross.nix
|
||||||
|
./feeds.nix
|
||||||
|
./fs.nix
|
||||||
|
./hardware
|
||||||
|
./i2p.nix
|
||||||
|
./ids.nix
|
||||||
|
./machine-id.nix
|
||||||
|
./net.nix
|
||||||
|
./secrets.nix
|
||||||
|
./ssh.nix
|
||||||
|
./users.nix
|
||||||
|
./vpn.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
sane.home-manager.enable = true;
|
||||||
|
sane.nixcache.enable-trusted-keys = true;
|
||||||
|
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.
|
||||||
|
];
|
||||||
|
|
||||||
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|
||||||
|
# time.timeZone = "America/Los_Angeles";
|
||||||
|
time.timeZone = "Etc/UTC"; # DST is too confusing for me => use a stable timezone
|
||||||
|
|
||||||
|
# allow `nix flake ...` command
|
||||||
|
nix.extraOptions = ''
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
'';
|
||||||
|
|
||||||
|
# TODO: move this into home-manager?
|
||||||
|
fonts = {
|
||||||
|
enableDefaultFonts = true;
|
||||||
|
fonts = with pkgs; [ font-awesome twitter-color-emoji hack-font ];
|
||||||
|
fontconfig.enable = true;
|
||||||
|
fontconfig.defaultFonts = {
|
||||||
|
emoji = [ "Font Awesome 6 Free" "Twitter Color Emoji" ];
|
||||||
|
monospace = [ "Hack" ];
|
||||||
|
serif = [ "DejaVu Serif" ];
|
||||||
|
sansSerif = [ "DejaVu Sans" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# disable non-required packages like nano, perl, rsync, strace
|
||||||
|
environment.defaultPackages = [];
|
||||||
|
|
||||||
|
# programs.vim.defaultEditor = true;
|
||||||
|
environment.variables = {
|
||||||
|
EDITOR = "vim";
|
||||||
|
# git claims it should use EDITOR, but it doesn't!
|
||||||
|
GIT_EDITOR = "vim";
|
||||||
|
# TODO: these should be moved to `home.sessionVariables` (home-manager)
|
||||||
|
# Electron apps should use native wayland backend:
|
||||||
|
# https://nixos.wiki/wiki/Slack#Wayland
|
||||||
|
# Discord under sway crashes with this.
|
||||||
|
# NIXOS_OZONE_WL = "1";
|
||||||
|
# LIBGL_ALWAYS_SOFTWARE = "1";
|
||||||
|
};
|
||||||
|
# enable zsh completions
|
||||||
|
environment.pathsToLink = [ "/share/zsh" ];
|
||||||
|
|
||||||
|
# link debug symbols into /run/current-system/sw/lib/debug
|
||||||
|
# hopefully picked up by gdb automatically?
|
||||||
|
environment.enableDebugInfo = true;
|
||||||
|
}
|
@@ -1,5 +1,4 @@
|
|||||||
{ lib }:
|
{ ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
hourly = { freq = "hourly"; };
|
hourly = { freq = "hourly"; };
|
||||||
daily = { freq = "daily"; };
|
daily = { freq = "daily"; };
|
||||||
@@ -13,20 +12,15 @@ let
|
|||||||
tech = { cat = "tech"; };
|
tech = { cat = "tech"; };
|
||||||
uncat = { cat = "uncat"; };
|
uncat = { cat = "uncat"; };
|
||||||
|
|
||||||
text = { format = "text"; };
|
|
||||||
image = { format = "image"; };
|
|
||||||
podcast = { format = "podcast"; };
|
|
||||||
|
|
||||||
mkRss = format: url: { inherit url format; } // uncat // infrequent;
|
mkRss = format: url: { inherit url format; } // uncat // infrequent;
|
||||||
mkText = mkRss text;
|
# format-specific helpers
|
||||||
mkImg = mkRss image;
|
mkText = mkRss "text";
|
||||||
mkPod = mkRss podcast;
|
mkImg = mkRss "image";
|
||||||
|
mkPod = mkRss "podcast";
|
||||||
|
|
||||||
|
# host-specific helpers
|
||||||
|
mkSubstack = subdomain: { substack = subdomain; };
|
||||||
|
|
||||||
# merge the attrs `new` into each value of the attrs `addTo`
|
|
||||||
addAttrs = new: addTo: builtins.mapAttrs (k: v: v // new) addTo;
|
|
||||||
# for each value in `attrs`, add a value to the child attrs which holds its key within the parent attrs.
|
|
||||||
withInverseMapping = key: attrs: builtins.mapAttrs (k: v: v // { "${key}" = k; }) attrs;
|
|
||||||
in rec {
|
|
||||||
podcasts = [
|
podcasts = [
|
||||||
(mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly)
|
(mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly)
|
||||||
## Astral Codex Ten
|
## Astral Codex Ten
|
||||||
@@ -57,6 +51,12 @@ in rec {
|
|||||||
(mkPod "https://feeds.feedburner.com/dancarlin/history?format=xml" // rat // infrequent)
|
(mkPod "https://feeds.feedburner.com/dancarlin/history?format=xml" // rat // infrequent)
|
||||||
## 60 minutes (NB: this features more than *just* audio?)
|
## 60 minutes (NB: this features more than *just* audio?)
|
||||||
(mkPod "https://www.cbsnews.com/latest/rss/60-minutes" // pol // infrequent)
|
(mkPod "https://www.cbsnews.com/latest/rss/60-minutes" // pol // infrequent)
|
||||||
|
## The Verge - Decoder
|
||||||
|
(mkPod "https://feeds.megaphone.fm/recodedecode" // tech // weekly)
|
||||||
|
## Matrix (chat) Live
|
||||||
|
(mkPod "https://feed.podbean.com/matrixlive/feed.xml" // tech // weekly)
|
||||||
|
## Michael Malice - Your Welcome
|
||||||
|
(mkPod "https://www.podcastone.com/podcast?categoryID2=2232" // pol // weekly)
|
||||||
];
|
];
|
||||||
|
|
||||||
texts = [
|
texts = [
|
||||||
@@ -75,6 +75,7 @@ in rec {
|
|||||||
(mkText "https://www.rifters.com/crawl/?feed=rss2" // uncat // weekly)
|
(mkText "https://www.rifters.com/crawl/?feed=rss2" // uncat // weekly)
|
||||||
|
|
||||||
# DEVELOPERS
|
# DEVELOPERS
|
||||||
|
(mkText "https://uninsane.org/atom.xml" // infrequent // tech)
|
||||||
(mkText "https://mg.lol/blog/rss/" // infrequent // tech)
|
(mkText "https://mg.lol/blog/rss/" // infrequent // tech)
|
||||||
## Ken Shirriff
|
## Ken Shirriff
|
||||||
(mkText "https://www.righto.com/feeds/posts/default" // tech // infrequent)
|
(mkText "https://www.righto.com/feeds/posts/default" // tech // infrequent)
|
||||||
@@ -88,8 +89,12 @@ in rec {
|
|||||||
(mkText "https://ianthehenry.com/feed.xml" // tech // infrequent)
|
(mkText "https://ianthehenry.com/feed.xml" // tech // infrequent)
|
||||||
(mkText "https://bitbashing.io/feed.xml" // tech // infrequent)
|
(mkText "https://bitbashing.io/feed.xml" // tech // infrequent)
|
||||||
(mkText "https://idiomdrottning.org/feed.xml" // uncat // daily)
|
(mkText "https://idiomdrottning.org/feed.xml" // uncat // daily)
|
||||||
|
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
||||||
|
(mkText "https://www.jefftk.com/news.rss" // tech // daily)
|
||||||
|
(mkText "https://pomeroyb.com/feed.xml" // tech // infrequent)
|
||||||
|
|
||||||
# (TECH; POL) COMMENTATORS
|
# (TECH; POL) COMMENTATORS
|
||||||
|
(mkSubstack "edwardsnowden" // pol // infrequent)
|
||||||
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
||||||
## Ben Thompson
|
## Ben Thompson
|
||||||
(mkText "https://www.stratechery.com/rss" // pol // weekly)
|
(mkText "https://www.stratechery.com/rss" // pol // weekly)
|
||||||
@@ -98,15 +103,16 @@ in rec {
|
|||||||
(mkText "https://www.ben-evans.com/benedictevans/rss.xml" // pol // weekly)
|
(mkText "https://www.ben-evans.com/benedictevans/rss.xml" // pol // weekly)
|
||||||
(mkText "https://www.lynalden.com/feed" // pol // infrequent)
|
(mkText "https://www.lynalden.com/feed" // pol // infrequent)
|
||||||
(mkText "https://austinvernon.site/rss.xml" // tech // infrequent)
|
(mkText "https://austinvernon.site/rss.xml" // tech // infrequent)
|
||||||
(mkText "https://oversharing.substack.com/feed" // pol // daily)
|
(mkSubstack "oversharing" // pol // daily)
|
||||||
(mkText "https://doomberg.substack.com/feed" // tech // weekly)
|
(mkSubstack "doomberg" // tech // weekly)
|
||||||
## David Rosenthal
|
## David Rosenthal
|
||||||
(mkText "https://blog.dshr.org/rss.xml" // pol // weekly)
|
(mkText "https://blog.dshr.org/rss.xml" // pol // weekly)
|
||||||
## Matt Levine
|
## Matt Levine
|
||||||
(mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly)
|
(mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly)
|
||||||
|
(mkText "https://stpeter.im/atom.xml" // pol // weekly)
|
||||||
|
|
||||||
# RATIONALITY/PHILOSOPHY/ETC
|
# RATIONALITY/PHILOSOPHY/ETC
|
||||||
(mkText "https://samkriss.substack.com/feed" // humor // infrequent)
|
(mkSubstack "samkriss" // humor // infrequent)
|
||||||
(mkText "https://unintendedconsequenc.es/feed" // rat // infrequent)
|
(mkText "https://unintendedconsequenc.es/feed" // rat // infrequent)
|
||||||
(mkText "https://applieddivinitystudies.com/atom.xml" // rat // weekly)
|
(mkText "https://applieddivinitystudies.com/atom.xml" // rat // weekly)
|
||||||
(mkText "https://slimemoldtimemold.com/feed.xml" // rat // weekly)
|
(mkText "https://slimemoldtimemold.com/feed.xml" // rat // weekly)
|
||||||
@@ -117,59 +123,29 @@ in rec {
|
|||||||
## Robin Hanson
|
## Robin Hanson
|
||||||
(mkText "https://www.overcomingbias.com/feed" // rat // daily)
|
(mkText "https://www.overcomingbias.com/feed" // rat // daily)
|
||||||
## Scott Alexander
|
## Scott Alexander
|
||||||
(mkText "https://astralcodexten.substack.com/feed.xml" // rat // daily)
|
(mkSubstack "astralcodexten" // rat // daily)
|
||||||
## Paul Christiano
|
## Paul Christiano
|
||||||
(mkText "https://sideways-view.com/feed" // rat // infrequent)
|
(mkText "https://sideways-view.com/feed" // rat // infrequent)
|
||||||
## Sean Carroll
|
## Sean Carroll
|
||||||
(mkText "https://www.preposterousuniverse.com/rss" // rat // infrequent)
|
(mkText "https://www.preposterousuniverse.com/rss" // rat // infrequent)
|
||||||
|
|
||||||
|
## mostly dating topics. not advice, or humor, but looking through a social lens
|
||||||
|
(mkText "https://putanumonit.com/feed" // rat // infrequent)
|
||||||
|
|
||||||
# CODE
|
# CODE
|
||||||
(mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
||||||
];
|
];
|
||||||
|
|
||||||
images = [
|
images = [
|
||||||
(mkImg "https://www.smbc-comics.com/comic/rss" // humor // daily)
|
(mkImg "https://www.smbc-comics.com/comic/rss" // humor // daily)
|
||||||
(mkImg "https://xkcd.com/atom.xml" // humor // daily)
|
(mkImg "https://xkcd.com/atom.xml" // humor // daily)
|
||||||
(mkImg "http://dilbert.com/feed" // humor // daily)
|
(mkImg "https://pbfcomics.com/feed" // humor // infrequent)
|
||||||
|
# (mkImg "http://dilbert.com/feed" // humor // daily)
|
||||||
|
|
||||||
# ART
|
# ART
|
||||||
(mkImg "https://miniature-calendar.com/feed" // art // daily)
|
(mkImg "https://miniature-calendar.com/feed" // art // daily)
|
||||||
];
|
];
|
||||||
|
in
|
||||||
all = texts ++ images ++ podcasts;
|
{
|
||||||
|
sane.feeds = texts ++ images ++ podcasts;
|
||||||
# return only the feed items which match this category (e.g. "tech")
|
|
||||||
filterCat = cat: feeds: builtins.filter (item: item.cat == cat) feeds;
|
|
||||||
# return only the feed items which match this format (e.g. "podcast")
|
|
||||||
filterFormat = format: feeds: builtins.filter (item: item.format == format) feeds;
|
|
||||||
|
|
||||||
# transform a list of feeds into an attrs mapping cat => [ feed0 feed1 ... ]
|
|
||||||
partitionByCat = feeds: builtins.groupBy (f: f.cat) feeds;
|
|
||||||
|
|
||||||
# represents a single RSS feed.
|
|
||||||
opmlTerminal = feed: ''<outline xmlUrl="${feed.url}" type="rss"/>'';
|
|
||||||
# a list of RSS feeds.
|
|
||||||
opmlTerminals = feeds: lib.strings.concatStringsSep "\n" (builtins.map opmlTerminal feeds);
|
|
||||||
# one node which packages some flat grouping of terminals.
|
|
||||||
opmlGroup = title: feeds: ''
|
|
||||||
<outline text="${title}" title="${title}">
|
|
||||||
${opmlTerminals feeds}
|
|
||||||
</outline>
|
|
||||||
'';
|
|
||||||
# a list of groups (`groupMap` is an attrs mapping groupName => [ feed0 feed1 ... ]).
|
|
||||||
opmlGroups = groupMap: lib.strings.concatStringsSep "\n" (
|
|
||||||
builtins.attrValues (builtins.mapAttrs opmlGroup groupMap)
|
|
||||||
);
|
|
||||||
# top-level OPML file which could be consumed by something else.
|
|
||||||
opmlTopLevel = body: ''
|
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<opml version="2.0">
|
|
||||||
<body>
|
|
||||||
${body}
|
|
||||||
</body>
|
|
||||||
</opml>
|
|
||||||
'';
|
|
||||||
|
|
||||||
# **primary API**: generate a OPML file from the provided feeds
|
|
||||||
feedsToOpml = feeds: opmlTopLevel (opmlGroups (partitionByCat feeds));
|
|
||||||
}
|
}
|
@@ -19,11 +19,17 @@ let sshOpts = rec {
|
|||||||
|
|
||||||
optionsRoot = optionsBase ++ [
|
optionsRoot = optionsBase ++ [
|
||||||
# we don't transform_symlinks because that breaks the validity of remote /nix stores
|
# we don't transform_symlinks because that breaks the validity of remote /nix stores
|
||||||
"sftp_server=/run/wrappers/bin/sudo\\040${pkgs.openssh}/libexec/sftp-server"
|
"sftp_server=/run/wrappers/bin/sudo\\040/run/current-system/sw/libexec/sftp-server"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
environment.pathsToLink = [
|
||||||
|
# needed to achieve superuser access for user-mounted filesystems (see optionsRoot above)
|
||||||
|
# we can only link whole directories here, even though we're only interested in pkgs.openssh
|
||||||
|
"/libexec"
|
||||||
|
];
|
||||||
|
|
||||||
fileSystems."/mnt/servo-media-wan" = {
|
fileSystems."/mnt/servo-media-wan" = {
|
||||||
device = "colin@uninsane.org:/var/lib/uninsane/media";
|
device = "colin@uninsane.org:/var/lib/uninsane/media";
|
||||||
inherit (sshOpts) fsType;
|
inherit (sshOpts) fsType;
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
{
|
{
|
||||||
config = mkIf (pkgs.system == "x86_64-linux") {
|
config = mkIf (pkgs.system == "x86_64-linux") {
|
||||||
boot.initrd.availableKernelModules = [
|
boot.initrd.availableKernelModules = [
|
||||||
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
||||||
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
4
hosts/common/i2p.nix
Normal file
4
hosts/common/i2p.nix
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
services.i2p.enable = true;
|
||||||
|
}
|
60
hosts/common/ids.nix
Normal file
60
hosts/common/ids.nix
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# 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.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;
|
||||||
|
}
|
16
hosts/common/machine-id.nix
Normal file
16
hosts/common/machine-id.nix
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
# /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 = [ "etc" ];
|
||||||
|
text = "sha256sum /etc/ssh/host_keys/ssh_host_ed25519_key | cut -c 1-32 > /etc/machine-id";
|
||||||
|
};
|
||||||
|
}
|
43
hosts/common/net.nix
Normal file
43
hosts/common/net.nix
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{ 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>
|
||||||
|
# iwd is an alternative that shouldn't have this problem
|
||||||
|
# docs:
|
||||||
|
# - <https://nixos.wiki/wiki/Iwd>
|
||||||
|
# - <https://iwd.wiki.kernel.org/networkmanager>
|
||||||
|
# - `man iwd.config` for global config
|
||||||
|
# - `man iwd.network` for per-SSID config
|
||||||
|
# use `iwctl` to control
|
||||||
|
networking.networkmanager.wifi.backend = "iwd";
|
||||||
|
networking.wireless.iwd.enable = true;
|
||||||
|
networking.wireless.iwd.settings = {
|
||||||
|
# auto-connect to a stronger network if signal drops below this value
|
||||||
|
# bedroom -> bedroom connection is -35 to -40 dBm
|
||||||
|
# bedroom -> living room connection is -60 dBm
|
||||||
|
General.RoamThreshold = "-52"; # default -70
|
||||||
|
General.RoamThreshold5G = "-52"; # default -76
|
||||||
|
};
|
||||||
|
|
||||||
|
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" ];
|
||||||
|
};
|
||||||
|
}
|
124
hosts/common/secrets.nix
Normal file
124
hosts/common/secrets.nix
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# SOPS configuration:
|
||||||
|
# docs: https://github.com/Mic92/sops-nix
|
||||||
|
#
|
||||||
|
# for each new user you want to edit sops files:
|
||||||
|
# create a private age key from ssh key:
|
||||||
|
# $ mkdir -p ~/.config/sops/age; ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt; chmod 600 ~/.config/sops/age/keys.txt
|
||||||
|
# if the private key was password protected, then first decrypt it:
|
||||||
|
# $ cp ~/.ssh/id_ed25519 /tmp/id_ed25519
|
||||||
|
# $ ssh-keygen -p -N "" -f /tmp/id_ed25519
|
||||||
|
#
|
||||||
|
# for each user you want to decrypt secrets:
|
||||||
|
# $ cat ~/.ssh/id_ed25519.pub | ssh-to-age
|
||||||
|
# add the result to .sops.yaml
|
||||||
|
# since we specify ssh pubkeys in the nix config, you can just grep for `ssh-ed25519` here and use those instead
|
||||||
|
#
|
||||||
|
# for each host you want to decrypt secrets:
|
||||||
|
# $ cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age
|
||||||
|
# add the result to .sops.yaml
|
||||||
|
# $ sops updatekeys secrets/example.yaml
|
||||||
|
#
|
||||||
|
# to create a new secret:
|
||||||
|
# $ sops secrets/example.yaml
|
||||||
|
# control access below (sops.secret.<x>.owner = ...)
|
||||||
|
#
|
||||||
|
# to read a secret:
|
||||||
|
# $ cat /run/secrets/example_key
|
||||||
|
|
||||||
|
# sops.age.sshKeyPaths = [ "/home/colin/.ssh/id_ed25519_dec" ];
|
||||||
|
# This will add secrets.yaml to the nix store
|
||||||
|
# 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;
|
||||||
|
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";
|
||||||
|
# sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||||
|
# This will generate a new key if the key specified above does not exist
|
||||||
|
# sops.age.generateKey = true;
|
||||||
|
# This is the actual specification of the secrets.
|
||||||
|
# sops.secrets.example_key = {
|
||||||
|
# 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";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
24
hosts/common/ssh.nix
Normal file
24
hosts/common/ssh.nix
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{ config, lib, sane-data, sane-lib, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
sane.ssh.pubkeys =
|
||||||
|
let
|
||||||
|
# path is a DNS-style path like [ "org" "uninsane" "root" ]
|
||||||
|
keyNameForPath = path:
|
||||||
|
let
|
||||||
|
rev = lib.reverseList path;
|
||||||
|
name = builtins.head rev;
|
||||||
|
host = lib.concatStringsSep "." (builtins.tail rev);
|
||||||
|
in
|
||||||
|
"${name}@${host}";
|
||||||
|
|
||||||
|
# [{ path :: [String], value :: String }] for the keys we want to install
|
||||||
|
globalKeys = sane-lib.flattenAttrs sane-data.keys;
|
||||||
|
localKeys = sane-lib.flattenAttrs sane-data.keys.org.uninsane.local;
|
||||||
|
in lib.mkMerge (builtins.map
|
||||||
|
({ path, value }: {
|
||||||
|
"${keyNameForPath path}" = value;
|
||||||
|
})
|
||||||
|
(globalKeys ++ localKeys)
|
||||||
|
);
|
||||||
|
}
|
132
hosts/common/users.nix
Normal file
132
hosts/common/users.nix
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
{ 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;
|
||||||
|
fs = sane-lib.fs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
sane.users.guest.enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
# Users are exactly these specified here;
|
||||||
|
# old ones will be deleted (from /etc/passwd, etc) upon upgrade.
|
||||||
|
users.mutableUsers = false;
|
||||||
|
|
||||||
|
# docs: https://nixpkgs-manual-sphinx-markedown-example.netlify.app/generated/options-db.xml.html#users-users
|
||||||
|
users.users.colin = {
|
||||||
|
# sets group to "users" (?)
|
||||||
|
isNormalUser = true;
|
||||||
|
home = "/home/colin";
|
||||||
|
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 = [
|
||||||
|
{ startUid=100000; count=1; }
|
||||||
|
];
|
||||||
|
group = "users";
|
||||||
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
|
"nixbuild"
|
||||||
|
"networkmanager"
|
||||||
|
# phosh/mobile. XXX colin: unsure if necessary
|
||||||
|
"video"
|
||||||
|
"feedbackd"
|
||||||
|
"dialout" # required for modem access
|
||||||
|
];
|
||||||
|
|
||||||
|
# initial password is empty, in case anything goes wrong.
|
||||||
|
# if `colin-passwd` (a password hash) is successfully found/decrypted, that becomes the password at boot.
|
||||||
|
initialPassword = lib.mkDefault "";
|
||||||
|
passwordFile = lib.mkIf (config.sops.secrets ? "colin-passwd") config.sops.secrets.colin-passwd.path;
|
||||||
|
|
||||||
|
shell = pkgs.zsh;
|
||||||
|
|
||||||
|
# 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
".cargo"
|
||||||
|
".rustup"
|
||||||
|
];
|
||||||
|
|
||||||
|
# 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";
|
||||||
|
subUidRanges = [
|
||||||
|
{ startUid=200000; count=1; }
|
||||||
|
];
|
||||||
|
group = "users";
|
||||||
|
initialPassword = lib.mkDefault "";
|
||||||
|
shell = pkgs.zsh;
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
# TODO: insert pubkeys that should be allowed in
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
security.sudo = {
|
||||||
|
enable = true;
|
||||||
|
wheelNeedsPassword = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
permitRootLogin = "no";
|
||||||
|
passwordAuthentication = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
66
hosts/common/vpn.nix
Normal file
66
hosts/common/vpn.nix
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
# 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"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
(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"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
(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"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
]
|
@@ -4,19 +4,27 @@
|
|||||||
./fs.nix
|
./fs.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# sane.packages.enableDevPkgs = true;
|
||||||
|
|
||||||
sane.gui.sway.enable = true;
|
sane.gui.sway.enable = true;
|
||||||
sane.services.duplicity.enable = true;
|
sane.services.duplicity.enable = true;
|
||||||
sane.services.nixserve.enable = true;
|
sane.services.nixserve.enable = true;
|
||||||
sane.services.nixserve.sopsFile = ../../secrets/desko.yaml;
|
sane.services.nixserve.sopsFile = ../../secrets/desko.yaml;
|
||||||
sane.impermanence.enable = true;
|
sane.persist.enable = true;
|
||||||
|
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
# needed to use libimobiledevice/ifuse, for iphone sync
|
# needed to use libimobiledevice/ifuse, for iphone sync
|
||||||
services.usbmuxd.enable = true;
|
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;
|
||||||
|
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
|
# default config: https://man.archlinux.org/man/snapper-configs.5
|
||||||
# defaults to something like:
|
# defaults to something like:
|
||||||
@@ -42,7 +50,7 @@
|
|||||||
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
||||||
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
||||||
};
|
};
|
||||||
sane.impermanence.home-dirs = [
|
sane.persist.home.plaintext = [
|
||||||
".steam"
|
".steam"
|
||||||
".local/share/Steam"
|
".local/share/Steam"
|
||||||
];
|
];
|
@@ -1,16 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
sane.persist.root-on-tmpfs = true;
|
||||||
fileSystems."/" = {
|
|
||||||
device = "none";
|
|
||||||
fsType = "tmpfs";
|
|
||||||
options = [
|
|
||||||
"mode=755"
|
|
||||||
"size=1G"
|
|
||||||
"defaults"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
# we need a /tmp for building large nix things.
|
# we need a /tmp for building large nix things.
|
||||||
# a cross-compiled kernel, particularly, will easily use 30+GB of tmp
|
# a cross-compiled kernel, particularly, will easily use 30+GB of tmp
|
||||||
fileSystems."/tmp" = {
|
fileSystems."/tmp" = {
|
23
hosts/instantiate.nix
Normal file
23
hosts/instantiate.nix
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
|
||||||
|
|
||||||
|
{ hostName, localSystem }:
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./${hostName}
|
||||||
|
./common
|
||||||
|
];
|
||||||
|
|
||||||
|
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}";
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
@@ -4,14 +4,19 @@
|
|||||||
./fs.nix
|
./fs.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# sane.packages.enableDevPkgs = true;
|
||||||
|
|
||||||
# sane.users.guest.enable = true;
|
# sane.users.guest.enable = true;
|
||||||
sane.gui.sway.enable = true;
|
sane.gui.sway.enable = true;
|
||||||
sane.impermanence.enable = true;
|
sane.persist.enable = true;
|
||||||
sane.nixcache.enable = true;
|
sane.nixcache.enable = true;
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
users.users.colin.initialPassword = "147147";
|
sops.secrets.colin-passwd = {
|
||||||
|
sopsFile = ../../secrets/lappy.yaml;
|
||||||
|
neededForUsers = true;
|
||||||
|
};
|
||||||
|
|
||||||
# default config: https://man.archlinux.org/man/snapper-configs.5
|
# default config: https://man.archlinux.org/man/snapper-configs.5
|
||||||
# defaults to something like:
|
# defaults to something like:
|
@@ -1,16 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
sane.persist.root-on-tmpfs = true;
|
||||||
fileSystems."/" = {
|
|
||||||
device = "none";
|
|
||||||
fsType = "tmpfs";
|
|
||||||
options = [
|
|
||||||
"mode=755"
|
|
||||||
"size=1G"
|
|
||||||
"defaults"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
# we need a /tmp of default size (half RAM) for building large nix things
|
# we need a /tmp of default size (half RAM) for building large nix things
|
||||||
fileSystems."/tmp" = {
|
fileSystems."/tmp" = {
|
||||||
device = "none";
|
device = "none";
|
@@ -1,4 +1,4 @@
|
|||||||
{ config, pkgs, lib, mobile-nixos, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./firmware.nix
|
./firmware.nix
|
||||||
@@ -13,27 +13,37 @@
|
|||||||
# TODO: we could *maybe* inject pkgs.buildPackages.xyz = cross.buildPackages.xyz?
|
# TODO: we could *maybe* inject pkgs.buildPackages.xyz = cross.buildPackages.xyz?
|
||||||
documentation.nixos.enable = false;
|
documentation.nixos.enable = false;
|
||||||
|
|
||||||
# XXX colin: phosh doesn't work well with passwordless login
|
# XXX colin: phosh doesn't work well with passwordless login,
|
||||||
|
# so set this more reliable default password should anything go wrong
|
||||||
users.users.colin.initialPassword = "147147";
|
users.users.colin.initialPassword = "147147";
|
||||||
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
||||||
|
|
||||||
|
sops.secrets.colin-passwd = {
|
||||||
|
sopsFile = ../../secrets/moby.yaml;
|
||||||
|
neededForUsers = true;
|
||||||
|
};
|
||||||
|
|
||||||
# usability compromises
|
# usability compromises
|
||||||
sane.impermanence.home-dirs = [
|
sane.web-browser.persistCache = "private";
|
||||||
".librewolf"
|
sane.web-browser.persistData = "private";
|
||||||
|
sane.persist.home.plaintext = [
|
||||||
|
".config/pulse" # persist pulseaudio volume
|
||||||
];
|
];
|
||||||
|
|
||||||
# sane.home-packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
|
# sane.packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
|
||||||
sane.home-manager.extraPackages = [
|
sane.packages.extraUserPkgs = [
|
||||||
pkgs.plasma5Packages.konsole # terminal
|
pkgs.plasma5Packages.konsole # terminal
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.nixcache.enable = true;
|
sane.nixcache.enable = true;
|
||||||
sane.impermanence.enable = true;
|
sane.persist.enable = true;
|
||||||
sane.gui.phosh.enable = true;
|
sane.gui.phosh.enable = true;
|
||||||
|
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
# /boot space is at a premium. default was 20.
|
# /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.bootloader.enable = false;
|
||||||
# mobile.boot.stage-1.enable = false;
|
# mobile.boot.stage-1.enable = false;
|
||||||
# boot.initrd.systemd.enable = false;
|
# boot.initrd.systemd.enable = false;
|
@@ -1,17 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
sane.persist.root-on-tmpfs = true;
|
||||||
fileSystems."/" = {
|
|
||||||
device = "none";
|
|
||||||
fsType = "tmpfs";
|
|
||||||
options = [
|
|
||||||
"mode=755"
|
|
||||||
"size=1G"
|
|
||||||
"defaults"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/nix" = {
|
fileSystems."/nix" = {
|
||||||
device = "/dev/disk/by-uuid/1f1271f8-53ce-4081-8a29-60a4a6b5d6f9";
|
device = "/dev/disk/by-uuid/1f1271f8-53ce-4081-8a29-60a4a6b5d6f9";
|
||||||
fsType = "btrfs";
|
fsType = "btrfs";
|
@@ -8,9 +8,6 @@
|
|||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
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
|
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
||||||
system.stateVersion = "21.05";
|
system.stateVersion = "21.05";
|
||||||
}
|
}
|
@@ -3,27 +3,22 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./fs.nix
|
./fs.nix
|
||||||
./hardware.nix
|
|
||||||
./net.nix
|
./net.nix
|
||||||
./users.nix
|
./users.nix
|
||||||
./services
|
./services
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.home-manager.enable = true;
|
sane.packages.extraUserPkgs = [
|
||||||
sane.home-manager.extraPackages = [
|
|
||||||
# for administering services
|
# for administering services
|
||||||
pkgs.matrix-synapse
|
pkgs.matrix-synapse
|
||||||
pkgs.freshrss
|
pkgs.freshrss
|
||||||
];
|
];
|
||||||
sane.impermanence.enable = true;
|
sane.persist.enable = true;
|
||||||
sane.services.duplicity.enable = true;
|
sane.services.dyn-dns.enable = true;
|
||||||
sane.services.nixserve.enable = true;
|
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||||
|
|
||||||
# TODO: look into the EFI stuff
|
|
||||||
boot.loader.grub.enable = false;
|
|
||||||
boot.loader.generic-extlinux-compatible.enable = true;
|
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-u-boot-rpi-aarch64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
sops.secrets.duplicity_passphrase = {
|
sops.secrets.duplicity_passphrase = {
|
||||||
sopsFile = ../../secrets/servo.yaml;
|
sopsFile = ../../secrets/servo.yaml;
|
||||||
@@ -32,7 +27,7 @@
|
|||||||
# both transmission and ipfs try to set different net defaults.
|
# both transmission and ipfs try to set different net defaults.
|
||||||
# we just use the most aggressive of the two here:
|
# we just use the most aggressive of the two here:
|
||||||
boot.kernel.sysctl = {
|
boot.kernel.sysctl = {
|
||||||
"net.core.rmem_max" = "4194304"; # 4MB
|
"net.core.rmem_max" = 4194304; # 4MB
|
||||||
};
|
};
|
||||||
|
|
||||||
# This value determines the NixOS release from which the default
|
# This value determines the NixOS release from which the default
|
||||||
@@ -41,6 +36,6 @@
|
|||||||
# this value at the release version of the first install of this system.
|
# this value at the release version of the first install of this system.
|
||||||
# Before changing this value read the documentation for this option
|
# Before changing this value read the documentation for this option
|
||||||
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
||||||
system.stateVersion = "21.11"; # Did you read the comment?
|
system.stateVersion = "21.11";
|
||||||
}
|
}
|
||||||
|
|
93
hosts/servo/fs.nix
Normal file
93
hosts/servo/fs.nix
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
sane.persist.root-on-tmpfs = true;
|
||||||
|
# we need a /tmp for building large nix things
|
||||||
|
fileSystems."/tmp" = {
|
||||||
|
device = "none";
|
||||||
|
fsType = "tmpfs";
|
||||||
|
options = [
|
||||||
|
"mode=777"
|
||||||
|
"defaults"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/nix" = {
|
||||||
|
device = "/dev/disk/by-uuid/cc81cca0-3cc7-4d82-a00c-6243af3e7776";
|
||||||
|
fsType = "btrfs";
|
||||||
|
options = [
|
||||||
|
"compress=zstd"
|
||||||
|
"defaults"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot" = {
|
||||||
|
device = "/dev/disk/by-uuid/6EE3-4171";
|
||||||
|
fsType = "vfat";
|
||||||
|
};
|
||||||
|
|
||||||
|
# slow, external storage (for archiving, etc)
|
||||||
|
fileSystems."/mnt/persist/ext" = {
|
||||||
|
device = "/dev/disk/by-uuid/aa272cff-0fcc-498e-a4cb-0d95fb60631b";
|
||||||
|
fsType = "btrfs";
|
||||||
|
options = [
|
||||||
|
"compress=zstd"
|
||||||
|
"defaults"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
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"; }
|
||||||
|
];
|
||||||
|
# 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 = {
|
||||||
|
# enable = true;
|
||||||
|
# };
|
||||||
|
|
||||||
|
# btrfs doesn't easily support swapfiles
|
||||||
|
# swapDevices = [
|
||||||
|
# { device = "/nix/persist/swapfile"; size = 4096; }
|
||||||
|
# ];
|
||||||
|
|
||||||
|
# this can be a partition. create with:
|
||||||
|
# fdisk <dev>
|
||||||
|
# n
|
||||||
|
# <default partno>
|
||||||
|
# <start>
|
||||||
|
# <end>
|
||||||
|
# t
|
||||||
|
# <partno>
|
||||||
|
# 19 # set part type to Linux swap
|
||||||
|
# w # write changes
|
||||||
|
# mkswap -L swap <part>
|
||||||
|
# swapDevices = [
|
||||||
|
# {
|
||||||
|
# label = "swap";
|
||||||
|
# # TODO: randomEncryption.enable = true;
|
||||||
|
# }
|
||||||
|
# ];
|
||||||
|
}
|
||||||
|
|
212
hosts/servo/net.nix
Normal file
212
hosts/servo/net.nix
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
{ 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 `wg0` interface.
|
||||||
|
# # i hope that from the root ns, these aren't visible.
|
||||||
|
# DNS=46.227.67.134%wg0 192.165.9.158%wg0
|
||||||
|
# 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-wg0`, 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.wg0 = 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;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
31
hosts/servo/services/ddns-afraid.nix
Normal file
31
hosts/servo/services/ddns-afraid.nix
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets."ddns_afraid" = {
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
|
};
|
||||||
|
}
|
@@ -1,5 +1,7 @@
|
|||||||
{ config, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
# we use manual DDNS now
|
||||||
|
lib.mkIf false
|
||||||
{
|
{
|
||||||
systemd.services.ddns-he = {
|
systemd.services.ddns-he = {
|
||||||
description = "update dynamic DNS entries for HurricaneElectric";
|
description = "update dynamic DNS entries for HurricaneElectric";
|
@@ -1,18 +1,26 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./ddns-afraid.nix
|
||||||
./ddns-he.nix
|
./ddns-he.nix
|
||||||
|
./ejabberd.nix
|
||||||
./freshrss.nix
|
./freshrss.nix
|
||||||
./gitea.nix
|
./gitea.nix
|
||||||
|
./goaccess.nix
|
||||||
./ipfs.nix
|
./ipfs.nix
|
||||||
./jackett.nix
|
./jackett.nix
|
||||||
./jellyfin.nix
|
./jellyfin.nix
|
||||||
|
./kiwix-serve.nix
|
||||||
./matrix
|
./matrix
|
||||||
./navidrome.nix
|
./navidrome.nix
|
||||||
|
./nixserve.nix
|
||||||
./nginx.nix
|
./nginx.nix
|
||||||
./pleroma.nix
|
./pleroma.nix
|
||||||
./postfix.nix
|
./postfix.nix
|
||||||
./postgres.nix
|
./postgres.nix
|
||||||
|
./prosody.nix
|
||||||
./transmission.nix
|
./transmission.nix
|
||||||
|
./trust-dns.nix
|
||||||
|
./wikipedia.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
393
hosts/servo/services/ejabberd.nix
Normal file
393
hosts/servo/services/ejabberd.nix
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
# 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
|
||||||
|
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,17 @@
|
|||||||
# $ sudo -u freshrss -g freshrss FRESHRSS_DATA_PATH=/var/lib/freshrss ./result/cli/export-opml-for-user.php --user admin
|
# $ 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 = {
|
sops.secrets.freshrss_passwd = {
|
||||||
sopsFile = ../../../secrets/servo.yaml;
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
owner = config.users.users.freshrss.name;
|
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"; }
|
{ 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.enable = true;
|
||||||
services.freshrss.baseUrl = "https://rss.uninsane.org";
|
services.freshrss.baseUrl = "https://rss.uninsane.org";
|
||||||
services.freshrss.virtualHost = "rss.uninsane.org";
|
services.freshrss.virtualHost = "rss.uninsane.org";
|
||||||
@@ -29,9 +27,11 @@
|
|||||||
|
|
||||||
systemd.services.freshrss-import-feeds =
|
systemd.services.freshrss-import-feeds =
|
||||||
let
|
let
|
||||||
|
feeds = sane-lib.feeds;
|
||||||
fresh = config.systemd.services.freshrss-config;
|
fresh = config.systemd.services.freshrss-config;
|
||||||
feeds = import ../../../modules/universal/env/feeds.nix { inherit lib; };
|
all-feeds = config.sane.feeds;
|
||||||
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml feeds.all);
|
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||||
|
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml wanted-feeds);
|
||||||
in {
|
in {
|
||||||
inherit (fresh) wantedBy environment;
|
inherit (fresh) wantedBy environment;
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
@@ -45,4 +45,17 @@
|
|||||||
${pkgs.freshrss}/cli/import-for-user.php --user admin --filename ${opml}
|
${pkgs.freshrss}/cli/import-for-user.php --user admin --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, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "git"; group = "gitea"; directory = "/var/lib/gitea"; }
|
{ user = "git"; group = "gitea"; directory = "/var/lib/gitea"; }
|
||||||
];
|
];
|
||||||
users.groups.gitea.gid = config.sane.allocations.gitea-gid;
|
|
||||||
services.gitea.enable = true;
|
services.gitea.enable = true;
|
||||||
services.gitea.user = "git"; # default is 'gitea'
|
services.gitea.user = "git"; # default is 'gitea'
|
||||||
services.gitea.database.type = "postgres";
|
services.gitea.database.type = "postgres";
|
||||||
@@ -72,4 +71,18 @@
|
|||||||
"/var/lib/gitea"
|
"/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";
|
||||||
}
|
}
|
68
hosts/servo/services/goaccess.nix
Normal file
68
hosts/servo/services/goaccess.nix
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
# based on <https://bytes.fyi/real-time-goaccess-reports-with-nginx/>
|
||||||
|
# log-format setting can be derived with this tool if custom:
|
||||||
|
# - <https://github.com/stockrt/nginx2goaccess>
|
||||||
|
# config options:
|
||||||
|
# - <https://github.com/allinurl/goaccess/blob/master/config/goaccess.conf>
|
||||||
|
|
||||||
|
systemd.services.goaccess = {
|
||||||
|
description = "GoAccess server monitoring";
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = ''
|
||||||
|
${pkgs.goaccess}/bin/goaccess \
|
||||||
|
-f /var/log/nginx/public.log \
|
||||||
|
--log-format=VCOMBINED \
|
||||||
|
--real-time-html \
|
||||||
|
--html-refresh=30 \
|
||||||
|
--no-query-string \
|
||||||
|
--anonymize-ip \
|
||||||
|
--ignore-panel=HOSTS \
|
||||||
|
--ws-url=wss://sink.uninsane.org:443/ws \
|
||||||
|
--port=7890 \
|
||||||
|
-o /var/lib/uninsane/sink/index.html
|
||||||
|
'';
|
||||||
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
|
||||||
|
# hardening
|
||||||
|
WorkingDirectory = "/tmp";
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @privileged @reboot @resources @setuid @swap @raw-io";
|
||||||
|
ReadOnlyPaths = "/";
|
||||||
|
ReadWritePaths = [ "/proc/self" "/var/lib/uninsane/sink" ];
|
||||||
|
PrivateDevices = "yes";
|
||||||
|
ProtectKernelModules = "yes";
|
||||||
|
ProtectKernelTunables = "yes";
|
||||||
|
};
|
||||||
|
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";
|
||||||
|
}
|
@@ -6,26 +6,50 @@
|
|||||||
# - number of open peer connections:
|
# - number of open peer connections:
|
||||||
# - sudo -u ipfs -g ipfs ipfs -c /var/lib/ipfs/ swarm peers | wc -l
|
# - sudo -u ipfs -g ipfs ipfs -c /var/lib/ipfs/ swarm peers | wc -l
|
||||||
|
|
||||||
{ ... }:
|
{ lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf false # i don't actively use ipfs anymore
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "261"; group = "261"; directory = "/var/lib/ipfs"; }
|
{ 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.ipfs.enable = true;
|
||||||
services.kubo.localDiscovery = true;
|
services.kubo.localDiscovery = true;
|
||||||
services.kubo.swarmAddress = [
|
services.kubo.settings = {
|
||||||
# "/dns4/ipfs.uninsane.org/tcp/4001"
|
|
||||||
# "/ip4/0.0.0.0/tcp/4001"
|
|
||||||
"/dns4/ipfs.uninsane.org/udp/4001/quic"
|
|
||||||
"/ip4/0.0.0.0/udp/4001/quic"
|
|
||||||
];
|
|
||||||
services.kubo.extraConfig = {
|
|
||||||
Addresses = {
|
Addresses = {
|
||||||
Announce = [
|
Announce = [
|
||||||
# "/dns4/ipfs.uninsane.org/tcp/4001"
|
# "/dns4/ipfs.uninsane.org/tcp/4001"
|
||||||
"/dns4/ipfs.uninsane.org/udp/4001/quic"
|
"/dns4/ipfs.uninsane.org/udp/4001/quic"
|
||||||
];
|
];
|
||||||
|
Swarm = [
|
||||||
|
# "/dns4/ipfs.uninsane.org/tcp/4001"
|
||||||
|
# "/ip4/0.0.0.0/tcp/4001"
|
||||||
|
"/dns4/ipfs.uninsane.org/udp/4001/quic"
|
||||||
|
"/ip4/0.0.0.0/udp/4001/quic"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
Gateway = {
|
Gateway = {
|
||||||
# the gateway can only be used to serve content already replicated on this host
|
# the gateway can only be used to serve content already replicated on this host
|
@@ -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?
|
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
||||||
{ user = "root"; group = "root"; directory = "/var/lib/jackett"; }
|
{ user = "root"; group = "root"; directory = "/var/lib/jackett"; }
|
||||||
];
|
];
|
||||||
services.jackett.enable = true;
|
services.jackett.enable = true;
|
||||||
|
|
||||||
systemd.services.jackett.after = ["wg0veth.service"];
|
systemd.services.jackett.after = [ "wireguard-wg0.service" ];
|
||||||
|
systemd.services.jackett.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.jackett.serviceConfig = {
|
systemd.services.jackett.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
# patch jackett to listen on the public interfaces
|
# patch jackett to listen on the public interfaces
|
||||||
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
|
# 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/servo/services/jellyfin.nix
Normal file
67
hosts/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/servo/services/kiwix-serve.nix
Normal file
17
hosts/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,6 +1,6 @@
|
|||||||
# docs: https://nixos.wiki/wiki/Matrix
|
# docs: https://nixos.wiki/wiki/Matrix
|
||||||
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse
|
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse
|
||||||
{ config, lib, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
# ./irc.nix
|
# ./irc.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
|
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
|
||||||
];
|
];
|
||||||
services.matrix-synapse.enable = true;
|
services.matrix-synapse.enable = true;
|
||||||
@@ -77,6 +77,55 @@
|
|||||||
# create a token with limited uses:
|
# create a token with limited uses:
|
||||||
# curl -d '{ "uses_allowed": 1 }' --header "Authorization: Bearer <my_token>" localhost:8008/_synapse/admin/v1/registration_tokens/new
|
# 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;
|
||||||
|
|
||||||
|
# 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 = {
|
sops.secrets.matrix_synapse_secrets = {
|
||||||
sopsFile = ../../../../secrets/servo.yaml;
|
sopsFile = ../../../../secrets/servo.yaml;
|
@@ -1,6 +1,6 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
|
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
|
||||||
];
|
];
|
||||||
|
|
@@ -1,7 +1,7 @@
|
|||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
# user and group are both "matrix-appservice-irc"
|
# user and group are both "matrix-appservice-irc"
|
||||||
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }
|
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }
|
29
hosts/servo/services/navidrome.nix
Normal file
29
hosts/servo/services/navidrome.nix
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
sane.persist.sys.plaintext = [
|
||||||
|
# TODO: we don't have a static user allocated for navidrome!
|
||||||
|
# the chown would happen too early for us to set static perms
|
||||||
|
"/var/lib/private/navidrome"
|
||||||
|
# { 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";
|
||||||
|
};
|
||||||
|
|
||||||
|
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/servo/services/nginx.nix
Normal file
166
hosts/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/servo/services/nixserve.nix
Normal file
21
hosts/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;
|
||||||
|
}
|
@@ -1,15 +1,15 @@
|
|||||||
# docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix
|
# docs:
|
||||||
|
# - https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix
|
||||||
|
# - https://docs.pleroma.social/backend/configuration/cheatsheet/
|
||||||
#
|
#
|
||||||
# to run it in a oci-container: https://github.com/barrucadu/nixfiles/blob/master/services/pleroma.nix
|
# to run it in a oci-container: https://github.com/barrucadu/nixfiles/blob/master/services/pleroma.nix
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "pleroma"; group = "pleroma"; directory = "/var/lib/pleroma"; }
|
{ 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.enable = true;
|
||||||
services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path;
|
services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path;
|
||||||
services.pleroma.configs = [
|
services.pleroma.configs = [
|
||||||
@@ -48,16 +48,19 @@
|
|||||||
redirect_on_failure: true
|
redirect_on_failure: true
|
||||||
#base_url: "https://cache.pleroma.social"
|
#base_url: "https://cache.pleroma.social"
|
||||||
|
|
||||||
|
# see for reference:
|
||||||
|
# - `force_custom_plan`: <https://docs.pleroma.social/backend/configuration/postgresql/#disable-generic-query-plans>
|
||||||
config :pleroma, Pleroma.Repo,
|
config :pleroma, Pleroma.Repo,
|
||||||
adapter: Ecto.Adapters.Postgres,
|
adapter: Ecto.Adapters.Postgres,
|
||||||
username: "pleroma",
|
username: "pleroma",
|
||||||
database: "pleroma",
|
database: "pleroma",
|
||||||
hostname: "localhost",
|
hostname: "localhost",
|
||||||
pool_size: 10,
|
pool_size: 10,
|
||||||
prepare: :named,
|
|
||||||
parameters: [
|
parameters: [
|
||||||
plan_cache_mode: "force_custom_plan"
|
plan_cache_mode: "force_custom_plan"
|
||||||
]
|
]
|
||||||
|
# XXX: prepare: :named is needed only for PG <= 12
|
||||||
|
# prepare: :named,
|
||||||
# password: "{secrets.pleroma.db_password}",
|
# password: "{secrets.pleroma.db_password}",
|
||||||
|
|
||||||
# Configure web push notifications
|
# Configure web push notifications
|
||||||
@@ -74,9 +77,10 @@
|
|||||||
config :pleroma, configurable_from_database: false
|
config :pleroma, configurable_from_database: false
|
||||||
|
|
||||||
# strip metadata from uploaded images
|
# strip metadata from uploaded images
|
||||||
config :pleroma, Pleroma.Upload, filters: [Pleroma.Upload.Filter.Exiftool]
|
config :pleroma, Pleroma.Upload, filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
|
||||||
|
|
||||||
# TODO: GET /api/pleroma/captcha is broken
|
# TODO: GET /api/pleroma/captcha is broken
|
||||||
|
# there was a nixpkgs PR to fix this around 2022/10 though.
|
||||||
config :pleroma, Pleroma.Captcha,
|
config :pleroma, Pleroma.Captcha,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
method: Pleroma.Captcha.Native
|
method: Pleroma.Captcha.Native
|
||||||
@@ -109,9 +113,9 @@
|
|||||||
|
|
||||||
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
|
# something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start
|
||||||
pkgs.bash
|
pkgs.bash
|
||||||
# used by Pleroma to strip geo tags from uploads
|
# used by Pleroma to strip geo tags from uploads
|
||||||
pkgs.exiftool
|
pkgs.exiftool
|
||||||
# i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical
|
# i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical
|
||||||
pkgs.gawk
|
pkgs.gawk
|
||||||
# needed for email operations like password reset
|
# needed for email operations like password reset
|
||||||
@@ -121,6 +125,7 @@
|
|||||||
systemd.services.pleroma.serviceConfig = {
|
systemd.services.pleroma.serviceConfig = {
|
||||||
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
|
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
};
|
};
|
||||||
|
|
||||||
# systemd.services.pleroma.serviceConfig = {
|
# systemd.services.pleroma.serviceConfig = {
|
||||||
@@ -130,6 +135,50 @@
|
|||||||
# CapabilityBoundingSet = lib.mkForce "~";
|
# CapabilityBoundingSet = lib.mkForce "~";
|
||||||
# };
|
# };
|
||||||
|
|
||||||
|
# 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 = {
|
sops.secrets.pleroma_secrets = {
|
||||||
sopsFile = ../../../secrets/servo.yaml;
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
owner = config.users.users.pleroma.name;
|
owner = config.users.users.pleroma.name;
|
@@ -16,11 +16,70 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "opendkim"; group = "opendkim"; directory = "/var/lib/opendkim"; }
|
{ user = "opendkim"; group = "opendkim"; directory = "/var/lib/opendkim"; }
|
||||||
{ user = "root"; group = "root"; directory = "/var/lib/postfix"; }
|
{ user = "root"; group = "root"; directory = "/var/lib/postfix"; }
|
||||||
|
{ user = "root"; group = "root"; directory = "/var/spool/mail"; }
|
||||||
|
# *probably* don't need these dirs:
|
||||||
|
# "/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.enable = true;
|
||||||
services.postfix.hostname = "mx.uninsane.org";
|
services.postfix.hostname = "mx.uninsane.org";
|
||||||
services.postfix.origin = "uninsane.org";
|
services.postfix.origin = "uninsane.org";
|
||||||
@@ -51,7 +110,8 @@ in
|
|||||||
services.postfix.enableSubmissions = true;
|
services.postfix.enableSubmissions = true;
|
||||||
services.postfix.submissionsOptions = submissionOptions;
|
services.postfix.submissionsOptions = submissionOptions;
|
||||||
|
|
||||||
systemd.services.postfix.after = [ "wg0veth.service" ];
|
systemd.services.postfix.after = [ "wireguard-wg0.service" ];
|
||||||
|
systemd.services.postfix.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.postfix.serviceConfig = {
|
systemd.services.postfix.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
@@ -72,7 +132,8 @@ in
|
|||||||
# keeping this the same as the hostname seems simplest
|
# keeping this the same as the hostname seems simplest
|
||||||
services.opendkim.selector = "mx";
|
services.opendkim.selector = "mx";
|
||||||
|
|
||||||
systemd.services.opendkim.after = [ "wg0veth.service" ];
|
systemd.services.opendkim.after = [ "wireguard-wg0.service" ];
|
||||||
|
systemd.services.opendkim.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.opendkim.serviceConfig = {
|
systemd.services.opendkim.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
@@ -1,7 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
|
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
|
||||||
];
|
];
|
||||||
@@ -17,6 +17,11 @@
|
|||||||
# LC_CTYPE = "C";
|
# LC_CTYPE = "C";
|
||||||
# '';
|
# '';
|
||||||
|
|
||||||
|
# TODO: perf tuning
|
||||||
|
# - for recommended values see: <https://pgtune.leopard.in.ua/>
|
||||||
|
# - for official docs (sparse), see: <https://www.postgresql.org/docs/11/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE>
|
||||||
|
# services.postgresql.settings = { ... }
|
||||||
|
|
||||||
# daily backups to /var/backup
|
# daily backups to /var/backup
|
||||||
services.postgresqlBackup.enable = true;
|
services.postgresqlBackup.enable = true;
|
||||||
|
|
64
hosts/servo/services/prosody.nix
Normal file
64
hosts/servo/services/prosody.nix
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# example configs:
|
||||||
|
# - <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
|
||||||
|
# create users with:
|
||||||
|
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
|
||||||
|
|
||||||
|
{ lib, ... }:
|
||||||
|
|
||||||
|
# XXX disabled: doesn't send messages to nixnet.social (only receives them).
|
||||||
|
# nixnet runs ejabberd, so revisiting that.
|
||||||
|
lib.mkIf false
|
||||||
|
{
|
||||||
|
sane.persist.sys.plaintext = [
|
||||||
|
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
||||||
|
];
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
5222 # XMPP client -> server
|
||||||
|
5269 # XMPP server -> server
|
||||||
|
5280 # bosh
|
||||||
|
5281 # Prosody HTTPS port (necessary?)
|
||||||
|
];
|
||||||
|
|
||||||
|
# provide access to certs
|
||||||
|
users.users.prosody.extraGroups = [ "nginx" ];
|
||||||
|
|
||||||
|
security.acme.certs."uninsane.org".extraDomainNames = [
|
||||||
|
"conference.xmpp.uninsane.org"
|
||||||
|
"upload.xmpp.uninsane.org"
|
||||||
|
];
|
||||||
|
|
||||||
|
services.prosody = {
|
||||||
|
enable = true;
|
||||||
|
admins = [ "colin@uninsane.org" ];
|
||||||
|
# allowRegistration = false;
|
||||||
|
# extraConfig = ''
|
||||||
|
# s2s_require_encryption = true
|
||||||
|
# c2s_require_encryption = true
|
||||||
|
# '';
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
muc = [
|
||||||
|
{
|
||||||
|
domain = "conference.xmpp.uninsane.org";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
uploadHttp.domain = "upload.xmpp.uninsane.org";
|
||||||
|
|
||||||
|
virtualHosts = {
|
||||||
|
localhost = {
|
||||||
|
domain = "localhost";
|
||||||
|
enabled = true;
|
||||||
|
};
|
||||||
|
"xmpp.uninsane.org" = {
|
||||||
|
domain = "uninsane.org";
|
||||||
|
enabled = true;
|
||||||
|
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
||||||
|
ssl.key = "/var/lib/acme/uninsane.org/key.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/
|
# TODO: mode? we need this specifically for the stats tracking in .config/
|
||||||
{ user = "transmission"; group = "transmission"; directory = "/var/lib/transmission"; }
|
{ user = "transmission"; group = "transmission"; directory = "/var/lib/transmission"; }
|
||||||
];
|
];
|
||||||
@@ -40,11 +40,41 @@
|
|||||||
# transmission will by default not allow the world to read its files.
|
# transmission will by default not allow the world to read its files.
|
||||||
services.transmission.downloadDirPermissions = "775";
|
services.transmission.downloadDirPermissions = "775";
|
||||||
|
|
||||||
systemd.services.transmission.after = ["wg0veth.service"];
|
systemd.services.transmission.after = [ "wireguard-wg0.service" ];
|
||||||
|
systemd.services.transmission.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.transmission.serviceConfig = {
|
systemd.services.transmission.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
LogLevelMax = "warning";
|
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";
|
||||||
}
|
}
|
||||||
|
|
66
hosts/servo/services/trust-dns.nix
Normal file
66
hosts/servo/services/trust-dns.nix
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{ 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.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" ];
|
||||||
|
}
|
31
hosts/servo/services/wikipedia.nix
Normal file
31
hosts/servo/services/wikipedia.nix
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# 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;
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
|
};
|
||||||
|
|
||||||
|
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?
|
||||||
|
}
|
@@ -12,7 +12,6 @@
|
|||||||
home = "/var/lib/gitea";
|
home = "/var/lib/gitea";
|
||||||
useDefaultShell = true;
|
useDefaultShell = true;
|
||||||
group = "gitea";
|
group = "gitea";
|
||||||
uid = config.sane.allocations.git-uid;
|
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
# sendmail access (not 100% sure if this is necessary)
|
# sendmail access (not 100% sure if this is necessary)
|
||||||
extraGroups = [ "postdrop" ];
|
extraGroups = [ "postdrop" ];
|
@@ -1,69 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# 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"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
# we need a /tmp for building large nix things
|
|
||||||
fileSystems."/tmp" = {
|
|
||||||
device = "none";
|
|
||||||
fsType = "tmpfs";
|
|
||||||
options = [
|
|
||||||
"size=40G"
|
|
||||||
"mode=777"
|
|
||||||
"defaults"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/nix" = {
|
|
||||||
device = "/dev/disk/by-uuid/aa272cff-0fcc-498e-a4cb-0d95fb60631b";
|
|
||||||
fsType = "btrfs";
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/boot" = {
|
|
||||||
device = "/dev/disk/by-uuid/31D3-40CB";
|
|
||||||
fsType = "vfat";
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
# fileSystems."/var/lib/pleroma" = {
|
|
||||||
# device = "/opt/pleroma";
|
|
||||||
# options = [ "bind" ];
|
|
||||||
# };
|
|
||||||
|
|
||||||
# in-memory compressed RAM (seems to be dynamically sized)
|
|
||||||
zramSwap = {
|
|
||||||
enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# btrfs doesn't easily support swapfiles
|
|
||||||
# swapDevices = [
|
|
||||||
# { device = "/nix/persist/swapfile"; size = 4096; }
|
|
||||||
# ];
|
|
||||||
|
|
||||||
# this can be a partition. create with:
|
|
||||||
# fdisk <dev>
|
|
||||||
# n
|
|
||||||
# <default partno>
|
|
||||||
# <start>
|
|
||||||
# <end>
|
|
||||||
# t
|
|
||||||
# <partno>
|
|
||||||
# 19 # set part type to Linux swap
|
|
||||||
# w # write changes
|
|
||||||
# mkswap -L swap <part>
|
|
||||||
swapDevices = [
|
|
||||||
{
|
|
||||||
label = "swap";
|
|
||||||
# TODO: randomEncryption.enable = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
@@ -1,75 +0,0 @@
|
|||||||
# this file originates from ‘nixos-generate-config’
|
|
||||||
# but has been heavily modified
|
|
||||||
{ pkgs, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# i changed this becuse linux 5.10 didn't have rpi-400 device tree blob.
|
|
||||||
# nixos-22.05 linux 5.15 DOES have these now.
|
|
||||||
# it should be possible to remove this if desired, but i'm not sure how the rpi-specific kernel differs.
|
|
||||||
# see: https://github.com/raspberrypi/linux
|
|
||||||
boot.kernelPackages = pkgs.linuxPackages_rpi4;
|
|
||||||
|
|
||||||
# raspberryPi boot loader creates extlinux.conf.
|
|
||||||
# otherwise, enable the generic-extlinux-compatible loader below.
|
|
||||||
# note: THESE ARE MUTUALLY EXCLUSIVE. generic-extlinux-compatible causes uboot to not be built
|
|
||||||
|
|
||||||
boot.initrd.availableKernelModules = [
|
|
||||||
"bcm2711_thermal"
|
|
||||||
"bcm_phy_lib"
|
|
||||||
"brcmfmac"
|
|
||||||
"brcmutil"
|
|
||||||
"broadcom"
|
|
||||||
"clk_raspberrypi"
|
|
||||||
"drm" # Direct Render Manager
|
|
||||||
"enclosure" # SCSI ?
|
|
||||||
"fuse"
|
|
||||||
"mdio_bcm_unimac"
|
|
||||||
"pcie_brcmstb"
|
|
||||||
"raspberrypi_cpufreq"
|
|
||||||
"raspberrypi_hwmon"
|
|
||||||
"ses" # SCSI Enclosure Services
|
|
||||||
"uas" # USB attached storage
|
|
||||||
"uio" # userspace IO
|
|
||||||
"uio_pdrv_genirq"
|
|
||||||
"xhci_pci"
|
|
||||||
"xhci_pci_renesas"
|
|
||||||
];
|
|
||||||
# boot.initrd.compressor = "gzip"; # defaults to zstd
|
|
||||||
|
|
||||||
# ondemand power scaling keeps the cpu at low frequency when idle, and sets to max frequency
|
|
||||||
# when load is detected. (v.s. the "performance" default, which always uses the max frequency)
|
|
||||||
powerManagement.cpuFreqGovernor = "ondemand";
|
|
||||||
|
|
||||||
# XXX colin: this allows one to `systemctl halt` and then not remove power until the HDD has spun down.
|
|
||||||
# however, it doesn't work with reboot because systemd will spin the drive up again to read its reboot bin.
|
|
||||||
# a better solution would be to put the drive behind a powered USB hub (or get a SSD).
|
|
||||||
# systemd.services.diskguard = {
|
|
||||||
# description = "Safely power off spinning media";
|
|
||||||
# before = [ "shutdown.target" ];
|
|
||||||
# wantedBy = [ "sysinit.target" ];
|
|
||||||
# # old (creates dep loop, but works)
|
|
||||||
# # before = [ "systemd-remount-fs.service" "shutdown.target" ];
|
|
||||||
# # wantedBy = [ "systemd-remount-fs.service" ];
|
|
||||||
# serviceConfig = {
|
|
||||||
# Type = "oneshot";
|
|
||||||
# RemainAfterExit = true;
|
|
||||||
# ExecStart = "${pkgs.coreutils}/bin/true";
|
|
||||||
# ExecStop = with pkgs; writeScript "diskguard" ''
|
|
||||||
# #!${bash}/bin/bash
|
|
||||||
# if ${procps}/bin/pgrep nixos-rebuild ;
|
|
||||||
# then
|
|
||||||
# exit 0 # don't halt drives unless we're actually shutting down. maybe better way to do this (check script args?)
|
|
||||||
# fi
|
|
||||||
# # ${coreutils}/bin/sync
|
|
||||||
# # ${util-linux}/bin/mount -o remount,ro /nix/store
|
|
||||||
# # ${util-linux}/bin/mount -o remount,ro /
|
|
||||||
# # -S 1 retracts the spindle after 5 seconds of idle
|
|
||||||
# # -B 1 spins down the drive after <vendor specific duration>
|
|
||||||
# ${hdparm}/sbin/hdparm -S 1 -B 1 /dev/sda
|
|
||||||
# # TODO: monitor smartmonctl until disk is idle? or try hdparm -Y
|
|
||||||
# # ${coreutils}/bin/sleep 20
|
|
||||||
# # exec ${util-linux}/bin/umount --all -t ext4,vfat,ext2
|
|
||||||
# '';
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
}
|
|
@@ -1,139 +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;
|
|
||||||
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,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,281 +0,0 @@
|
|||||||
# docs: https://nixos.wiki/wiki/Nginx
|
|
||||||
{ config, pkgs, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
services.nginx.enable = true;
|
|
||||||
|
|
||||||
# web blog/personal site
|
|
||||||
services.nginx.virtualHosts."uninsane.org" = {
|
|
||||||
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;
|
|
||||||
|
|
||||||
# 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;
|
|
||||||
# };
|
|
||||||
};
|
|
||||||
|
|
||||||
# Pleroma server and web interface
|
|
||||||
services.nginx.virtualHosts."fed.uninsane.org" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
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;
|
|
||||||
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;
|
|
||||||
locations."/" = {
|
|
||||||
# proxyPass = "http://ovpns.uninsane.org:9117";
|
|
||||||
proxyPass = "http://10.0.1.6:9117";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# matrix chat server
|
|
||||||
services.nginx.virtualHosts."matrix.uninsane.org" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
|
|
||||||
# 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;
|
|
||||||
|
|
||||||
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" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
locations."/".proxyPass = "http://127.0.0.1:4533";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."rss.uninsane.org" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
# 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;
|
|
||||||
|
|
||||||
default = true;
|
|
||||||
|
|
||||||
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;
|
|
||||||
# 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;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
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/lib/uninsane"; }
|
|
||||||
];
|
|
||||||
}
|
|
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/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": 27184,
|
||||||
|
"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-09T11:30:25+00:00",
|
||||||
|
"score": -18,
|
||||||
|
"self_url": "http://www.econtalk.org/feed/",
|
||||||
|
"site_name": null,
|
||||||
|
"site_url": null,
|
||||||
|
"title": "EconTalk Podcast – Econlib",
|
||||||
|
"url": "http://www.econtalk.org/feed/",
|
||||||
|
"velocity": 0.143,
|
||||||
|
"version": "rss20"
|
||||||
|
}
|
21
modules/data/feeds/sources/lesswrong.com/default.json
Normal file
21
modules/data/feeds/sources/lesswrong.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"bozo": 0,
|
||||||
|
"content_length": 337440,
|
||||||
|
"content_type": "application/rss+xml; charset=utf-8",
|
||||||
|
"description": "A community blog devoted to refining the art of rationality",
|
||||||
|
"favicon": "https://res.cloudinary.com/lesswrong-2-0/image/upload/v1497915096/favicon_lncumn.ico",
|
||||||
|
"hubs": [],
|
||||||
|
"is_podcast": false,
|
||||||
|
"is_push": false,
|
||||||
|
"item_count": 10,
|
||||||
|
"last_seen": "2023-01-11T10:39:58.575828+00:00",
|
||||||
|
"last_updated": "2023-01-11T09:58:49+00:00",
|
||||||
|
"score": 32,
|
||||||
|
"self_url": "https://www.lesswrong.com/feed.xml?view=rss&karmaThreshold=2",
|
||||||
|
"site_name": "LessWrong",
|
||||||
|
"site_url": "https://www.lesswrong.com",
|
||||||
|
"title": "LessWrong",
|
||||||
|
"url": "https://www.lesswrong.com/feed.xml",
|
||||||
|
"velocity": 12.052,
|
||||||
|
"version": "rss20"
|
||||||
|
}
|
23
modules/data/feeds/sources/lexfridman.com/default.json
Normal file
23
modules/data/feeds/sources/lexfridman.com/default.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"bozo": 0,
|
||||||
|
"content_length": 841679,
|
||||||
|
"content_type": "application/rss+xml; charset=utf-8",
|
||||||
|
"description": "Conversations about AI, science, technology, history, philosophy and the nature of intelligence, consciousness, love, and power.",
|
||||||
|
"favicon": "https://lexfridman.com/wordpress/wp-content/uploads/2017/06/cropped-lex-favicon-4-1-32x32.png",
|
||||||
|
"hubs": [
|
||||||
|
"https://pubsubhubbub.appspot.com/"
|
||||||
|
],
|
||||||
|
"is_podcast": true,
|
||||||
|
"is_push": true,
|
||||||
|
"item_count": 300,
|
||||||
|
"last_seen": "2023-01-08T23:41:32.928322+00:00",
|
||||||
|
"last_updated": "2022-12-29T17:35:50+00:00",
|
||||||
|
"score": 20,
|
||||||
|
"self_url": "https://lexfridman.com/feed/podcast/",
|
||||||
|
"site_name": "Lex Fridman",
|
||||||
|
"site_url": "https://lexfridman.com",
|
||||||
|
"title": "Lex Fridman Podcast",
|
||||||
|
"url": "https://lexfridman.com/feed/podcast/",
|
||||||
|
"velocity": 0.265,
|
||||||
|
"version": "rss20"
|
||||||
|
}
|
21
modules/data/feeds/sources/xkcd.com/default.json
Normal file
21
modules/data/feeds/sources/xkcd.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"bozo": 0,
|
||||||
|
"content_length": 2302,
|
||||||
|
"content_type": "text/xml; charset=utf-8",
|
||||||
|
"description": null,
|
||||||
|
"favicon": "https://xkcd.com/s/919f27.ico",
|
||||||
|
"hubs": [],
|
||||||
|
"is_podcast": false,
|
||||||
|
"is_push": false,
|
||||||
|
"item_count": 4,
|
||||||
|
"last_seen": "2023-01-11T10:29:36.530001+00:00",
|
||||||
|
"last_updated": "2023-01-09T00:00:00+00:00",
|
||||||
|
"score": 16,
|
||||||
|
"self_url": null,
|
||||||
|
"site_name": "xkcd",
|
||||||
|
"site_url": "https://xkcd.com",
|
||||||
|
"title": "xkcd.com",
|
||||||
|
"url": "https://xkcd.com/atom.xml",
|
||||||
|
"velocity": 0.429,
|
||||||
|
"version": "atom10"
|
||||||
|
}
|
24
modules/data/keys.nix
Normal file
24
modules/data/keys.nix
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# hierarchical, DNS-like mapping from <name> => ssh host/user for that name.
|
||||||
|
# host keys are represented as user keys, just with the user specified as "root".
|
||||||
|
|
||||||
|
{
|
||||||
|
org.uninsane = rec {
|
||||||
|
root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
|
||||||
|
git.root = root;
|
||||||
|
|
||||||
|
local = {
|
||||||
|
# machine aliases i specify on my lan; not actually asserted as DNS
|
||||||
|
desko.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
||||||
|
desko.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||||
|
|
||||||
|
lappy.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
|
||||||
|
lappy.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
|
||||||
|
|
||||||
|
moby.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
|
||||||
|
moby.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
|
||||||
|
|
||||||
|
servo.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
|
||||||
|
servo.root = root;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@@ -1,14 +1,23 @@
|
|||||||
{ ... }:
|
{ lib, utils, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./feeds.nix
|
||||||
|
./fs
|
||||||
./gui
|
./gui
|
||||||
./hardware
|
./home-manager
|
||||||
|
./ids.nix
|
||||||
|
./packages.nix
|
||||||
./image.nix
|
./image.nix
|
||||||
./impermanence.nix
|
|
||||||
./nixcache.nix
|
./nixcache.nix
|
||||||
./services/duplicity.nix
|
./persist
|
||||||
./services/nixserve.nix
|
./services
|
||||||
./universal
|
./sops.nix
|
||||||
|
./ssh.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
_module.args = {
|
||||||
|
sane-lib = import ./lib { inherit lib utils; };
|
||||||
|
sane-data = import ./data { inherit lib; };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
51
modules/feeds.nix
Normal file
51
modules/feeds.nix
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
feed = types.submodule ({ config, ... }: {
|
||||||
|
options = {
|
||||||
|
freq = mkOption {
|
||||||
|
type = types.enum [ "hourly" "daily" "weekly" "infrequent" ];
|
||||||
|
default = "infrequent";
|
||||||
|
};
|
||||||
|
cat = mkOption {
|
||||||
|
type = types.enum [ "art" "humor" "pol" "rat" "tech" "uncat" ];
|
||||||
|
default = "uncat";
|
||||||
|
};
|
||||||
|
format = mkOption {
|
||||||
|
type = types.enum [ "text" "image" "podcast" ];
|
||||||
|
default = "text";
|
||||||
|
};
|
||||||
|
url = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
url to a RSS feed
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
substack = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
if the feed is a substack domain, just enter the subdomain here and the url/format field can be populated automatically
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = lib.mkIf (config.substack != null) {
|
||||||
|
url = "https://${config.substack}.substack.com/feed";
|
||||||
|
format = "text";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# we don't explicitly generate anything from the feeds here.
|
||||||
|
# instead, config.sane.feeds is used by a variety of services at their definition site.
|
||||||
|
options = {
|
||||||
|
sane.feeds = mkOption {
|
||||||
|
type = types.listOf feed;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
RSS feeds indexed by a human-readable name.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
357
modules/fs/default.nix
Normal file
357
modules/fs/default.nix
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
{ config, lib, pkgs, utils, sane-lib, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
path-lib = sane-lib.path;
|
||||||
|
sane-types = sane-lib.types;
|
||||||
|
cfg = config.sane.fs;
|
||||||
|
|
||||||
|
mountNameFor = path: "${utils.escapeSystemdPath path}.mount";
|
||||||
|
serviceNameFor = path: "ensure-${utils.escapeSystemdPath path}";
|
||||||
|
|
||||||
|
# sane.fs."<path>" top-level options
|
||||||
|
fsEntry = types.submodule ({ name, config, ...}: let
|
||||||
|
parent = path-lib.parent name;
|
||||||
|
has-parent = path-lib.hasParent name;
|
||||||
|
parent-cfg = if has-parent then cfg."${parent}" else {};
|
||||||
|
parent-acl = if has-parent then parent-cfg.generated.acl else {};
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
dir = mkOption {
|
||||||
|
type = types.nullOr dirEntry;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
symlink = mkOption {
|
||||||
|
type = types.nullOr (symlinkEntryFor name);
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
generated = mkOption {
|
||||||
|
type = generatedEntry;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
mount = mkOption {
|
||||||
|
type = types.nullOr (mountEntryFor name);
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
wantedBy = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
list of units or targets which, when activated, should trigger this fs entry to be created.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
wantedBeforeBy = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
list of units or targets which, when activated, should first start and wait for this fs entry to be created.
|
||||||
|
if this unit fails, it will not block the targets in this list.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
unit = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "name of the systemd unit which ensures this entry";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = let
|
||||||
|
default-acl = {
|
||||||
|
user = lib.mkDefault (parent-acl.user or "root");
|
||||||
|
group = lib.mkDefault (parent-acl.group or "root");
|
||||||
|
mode = lib.mkDefault (parent-acl.mode or "0755");
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
# we put this here instead of as a `default` to ensure that users who specify additional
|
||||||
|
# dependencies still get a dep on the parent (unless they assign with `mkForce`).
|
||||||
|
generated.depends = if has-parent then [ parent-cfg.unit ] else [];
|
||||||
|
|
||||||
|
# populate generated items from `dir` or `symlink` shorthands
|
||||||
|
generated.acl = lib.mkMerge [
|
||||||
|
default-acl
|
||||||
|
(lib.mkIf (config.dir != null)
|
||||||
|
(sane-lib.filterNonNull config.dir.acl))
|
||||||
|
(lib.mkIf (config.symlink != null)
|
||||||
|
(sane-lib.filterNonNull config.symlink.acl))
|
||||||
|
];
|
||||||
|
|
||||||
|
# actually generate the item
|
||||||
|
generated.script = lib.mkMerge [
|
||||||
|
(lib.mkIf (config.dir != null) (ensureDirScript name config.dir))
|
||||||
|
(lib.mkIf (config.symlink != null) (ensureSymlinkScript name config.symlink))
|
||||||
|
];
|
||||||
|
|
||||||
|
# make the unit file which generates the underlying thing available so that `mount` can use it.
|
||||||
|
generated.unit = (serviceNameFor name) + ".service";
|
||||||
|
|
||||||
|
# if we were asked to mount, make sure we create the dir that we mount over
|
||||||
|
dir = lib.mkIf (config.mount != null) {};
|
||||||
|
|
||||||
|
# if defaulted, this module is responsible for finalizing the entry.
|
||||||
|
# the user could override this if, say, they finalize some aspect of the entry
|
||||||
|
# with a custom service.
|
||||||
|
unit = lib.mkDefault (
|
||||||
|
if config.mount != null then
|
||||||
|
config.mount.unit
|
||||||
|
else
|
||||||
|
config.generated.unit
|
||||||
|
);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
# options which can be set in dir/symlink generated items,
|
||||||
|
# with intention that they just propagate down
|
||||||
|
propagatedGenerateMod = {
|
||||||
|
options = {
|
||||||
|
acl = mkOption {
|
||||||
|
type = sane-types.aclOverride;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# sane.fs."<path>".dir sub-options
|
||||||
|
# takes no special options
|
||||||
|
dirEntry = types.submodule propagatedGenerateMod;
|
||||||
|
|
||||||
|
symlinkEntryFor = path: types.submodule ({ config, ...}: {
|
||||||
|
options = {
|
||||||
|
inherit (propagatedGenerateMod.options) acl;
|
||||||
|
target = mkOption {
|
||||||
|
type = types.coercedTo types.package toString types.str;
|
||||||
|
description = "fs path to link to";
|
||||||
|
};
|
||||||
|
text = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "create a file in the /nix/store with the provided text and use that as the target";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
target = lib.mkIf (config.text != null) (
|
||||||
|
pkgs.writeText (path-lib.leaf path) config.text
|
||||||
|
);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
generatedEntry = types.submodule {
|
||||||
|
options = {
|
||||||
|
acl = mkOption {
|
||||||
|
type = sane-types.acl;
|
||||||
|
};
|
||||||
|
depends = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = ''
|
||||||
|
list of systemd units needed to be run before this item can be generated.
|
||||||
|
'';
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
script.script = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
};
|
||||||
|
script.scriptArgs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
unit = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "name of the systemd unit which ensures this directory";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# sane.fs."<path>".mount sub-options
|
||||||
|
mountEntryFor = path: types.submodule {
|
||||||
|
options = {
|
||||||
|
bind = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
description = "fs path to bind-mount from";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
depends = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = ''
|
||||||
|
list of systemd units needed to be run before this entry can be mounted
|
||||||
|
'';
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
unit = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "name of the systemd unit which mounts this path";
|
||||||
|
default = mountNameFor path;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkGeneratedConfig = path: opt: let
|
||||||
|
gen-opt = opt.generated;
|
||||||
|
wrapper = generateWrapperScript path gen-opt;
|
||||||
|
in {
|
||||||
|
systemd.services."${serviceNameFor path}" = {
|
||||||
|
description = "prepare ${path}";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
|
||||||
|
script = wrapper.script;
|
||||||
|
scriptArgs = builtins.concatStringsSep " " wrapper.scriptArgs;
|
||||||
|
|
||||||
|
after = gen-opt.depends;
|
||||||
|
wants = gen-opt.depends;
|
||||||
|
# prevent systemd making this unit implicitly dependent on sysinit.target.
|
||||||
|
# see: <https://www.freedesktop.org/software/systemd/man/systemd.special.html>
|
||||||
|
unitConfig.DefaultDependencies = "no";
|
||||||
|
|
||||||
|
before = opt.wantedBeforeBy;
|
||||||
|
wantedBy = opt.wantedBy ++ opt.wantedBeforeBy;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# given a mountEntry definition, evaluate its toplevel `config` output.
|
||||||
|
mkMountConfig = path: opt: (let
|
||||||
|
device = config.fileSystems."${path}".device;
|
||||||
|
underlying = cfg."${device}";
|
||||||
|
isBind = opt.mount.bind != null;
|
||||||
|
ifBind = lib.mkIf isBind;
|
||||||
|
# before mounting:
|
||||||
|
# - create the target directory
|
||||||
|
# - prepare the source directory -- assuming it's not an external device
|
||||||
|
# - satisfy any user-specified prerequisites ("depends")
|
||||||
|
requires = [ opt.generated.unit ]
|
||||||
|
++ (if lib.hasPrefix "/dev/disk/" device then [] else [ underlying.unit ])
|
||||||
|
++ opt.mount.depends;
|
||||||
|
in {
|
||||||
|
fileSystems."${path}" = {
|
||||||
|
device = ifBind opt.mount.bind;
|
||||||
|
options = (if isBind then ["bind"] else [])
|
||||||
|
++ [
|
||||||
|
# disable defaults: don't require this to be mount as part of local-fs.target
|
||||||
|
# we'll handle that stuff precisely.
|
||||||
|
"noauto"
|
||||||
|
"nofail"
|
||||||
|
# x-systemd options documented here:
|
||||||
|
# - <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
||||||
|
]
|
||||||
|
++ (builtins.map (unit: "x-systemd.requires=${unit}") requires)
|
||||||
|
++ (builtins.map (unit: "x-systemd.before=${unit}") opt.wantedBeforeBy)
|
||||||
|
++ (builtins.map (unit: "x-systemd.wanted-by=${unit}") (opt.wantedBy ++ opt.wantedBeforeBy));
|
||||||
|
noCheck = ifBind true;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
mkFsConfig = path: opt: lib.mkMerge [
|
||||||
|
(mkGeneratedConfig path opt)
|
||||||
|
(lib.mkIf (opt.mount != null) (mkMountConfig path opt))
|
||||||
|
];
|
||||||
|
|
||||||
|
generateWrapperScript = path: gen-opt: {
|
||||||
|
script = ''
|
||||||
|
fspath="$1"
|
||||||
|
acluser="$2"
|
||||||
|
aclgroup="$3"
|
||||||
|
aclmode="$4"
|
||||||
|
shift 4
|
||||||
|
|
||||||
|
# ensure any things created by the user script have the desired mode.
|
||||||
|
# chmod doesn't work on symlinks, so we *have* to use this umask approach.
|
||||||
|
decmask=$(( 0777 - "$aclmode" ))
|
||||||
|
octmask=$(printf "%o" "$decmask")
|
||||||
|
umask "$octmask"
|
||||||
|
|
||||||
|
# try to chmod/chown the result even if the user script errors
|
||||||
|
_status=0
|
||||||
|
trap "_status=\$?" ERR
|
||||||
|
|
||||||
|
${gen-opt.script.script}
|
||||||
|
|
||||||
|
# claim ownership of the new thing (DON'T traverse symlinks)
|
||||||
|
chown --no-dereference "$acluser:$aclgroup" "$fspath"
|
||||||
|
# AS LONG AS IT'S NOT A SYMLINK, try to fix perms in case the entity existed before this script was called
|
||||||
|
if ! test -L "$fspath"
|
||||||
|
then
|
||||||
|
chmod "$aclmode" "$fspath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit "$_status"
|
||||||
|
'';
|
||||||
|
scriptArgs = [ path gen-opt.acl.user gen-opt.acl.group gen-opt.acl.mode ] ++ gen-opt.script.scriptArgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
# systemd/shell script used to create and set perms for a specific dir
|
||||||
|
ensureDirScript = path: dir-cfg: {
|
||||||
|
script = ''
|
||||||
|
dirpath="$1"
|
||||||
|
|
||||||
|
if ! test -d "$dirpath"
|
||||||
|
then
|
||||||
|
# if the directory *doesn't* exist, try creating it
|
||||||
|
# if we fail to create it, ensure we raced with something else and that it's actually a directory
|
||||||
|
mkdir "$dirpath" || test -d "$dirpath"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
scriptArgs = [ path ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# systemd/shell script used to create a symlink
|
||||||
|
ensureSymlinkScript = path: link-cfg: {
|
||||||
|
script = ''
|
||||||
|
lnfrom="$1"
|
||||||
|
lnto="$2"
|
||||||
|
|
||||||
|
# ln is clever when there's something else at the place we want to create the link
|
||||||
|
# only create the link if nothing's there or what is there is another link,
|
||||||
|
# otherwise you'll get links at unexpected fs locations
|
||||||
|
! test -e "$lnfrom" || test -L "$lnfrom" && ln -sf --no-dereference "$lnto" "$lnfrom"
|
||||||
|
'';
|
||||||
|
scriptArgs = [ path link-cfg.target ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# return all ancestors of this path.
|
||||||
|
# e.g. ancestorsOf "/foo/bar/baz" => [ "/" "/foo" "/foo/bar" ]
|
||||||
|
ancestorsOf = path: lib.init (path-lib.walk "/" path);
|
||||||
|
|
||||||
|
# attrsOf fsEntry type which for every entry ensures that all ancestor entries are created.
|
||||||
|
# we do this with a custom type to ensure that users can access `config.sane.fs."/parent/path"`
|
||||||
|
# when inferred.
|
||||||
|
fsTree = let
|
||||||
|
baseType = types.attrsOf fsEntry;
|
||||||
|
# merge is called once, with all collected `sane.fs` definitions passed and we coalesce those
|
||||||
|
# into a single value `x` as if the user had wrote simply `sane.fs = x` in a single location.
|
||||||
|
# so option defaulting and such happens *after* `merge` is called.
|
||||||
|
merge = loc: defs: let
|
||||||
|
# loc is the location of the option holding this type, e.g. ["sane" "fs"].
|
||||||
|
# each def is an { value = attrsOf fsEntry instance; file = "..."; }
|
||||||
|
pathsForDef = def: attrNames def.value;
|
||||||
|
origPaths = concatLists (builtins.map pathsForDef defs);
|
||||||
|
extraPaths = concatLists (builtins.map ancestorsOf origPaths);
|
||||||
|
extraDefs = builtins.map (p: {
|
||||||
|
file = ./.;
|
||||||
|
value = {
|
||||||
|
"${p}".dir = {};
|
||||||
|
};
|
||||||
|
}) extraPaths;
|
||||||
|
in
|
||||||
|
baseType.merge loc (defs ++ extraDefs);
|
||||||
|
in
|
||||||
|
lib.mkOptionType {
|
||||||
|
inherit merge;
|
||||||
|
name = "fsTree";
|
||||||
|
description = "attrset representation of a file-system tree";
|
||||||
|
# ensure that every path is in canonical form, else we might get duplicates and subtle errors
|
||||||
|
check = tree: builtins.all (p: p == path-lib.norm p) (builtins.attrNames tree);
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
sane.fs = mkOption {
|
||||||
|
# type = types.attrsOf fsEntry;
|
||||||
|
type = fsTree;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
configs = lib.mapAttrsToList mkFsConfig cfg;
|
||||||
|
take = f: {
|
||||||
|
systemd.services = f.systemd.services;
|
||||||
|
fileSystems = f.fileSystems;
|
||||||
|
};
|
||||||
|
in take (sane-lib.mkTypedMerge take configs);
|
||||||
|
}
|
@@ -8,6 +8,7 @@ in
|
|||||||
imports = [
|
imports = [
|
||||||
./gnome.nix
|
./gnome.nix
|
||||||
./phosh.nix
|
./phosh.nix
|
||||||
|
./plasma.nix
|
||||||
./plasma-mobile.nix
|
./plasma-mobile.nix
|
||||||
./sway.nix
|
./sway.nix
|
||||||
];
|
];
|
||||||
@@ -21,9 +22,10 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
sane.home-packages.enableGuiPkgs = lib.mkDefault true;
|
sane.packages.enableGuiPkgs = lib.mkDefault true;
|
||||||
sane.home-manager.enable = lib.mkDefault true;
|
|
||||||
# all GUIs use network manager?
|
# preserve backlight brightness across power cycles
|
||||||
users.users.nm-iodine.uid = config.sane.allocations.nm-iodine-uid;
|
# see `man systemd-backlight`
|
||||||
|
sane.persist.sys.plaintext = [ "/var/lib/systemd/backlight" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -15,15 +15,6 @@ in
|
|||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
sane.gui.enable = true;
|
sane.gui.enable = true;
|
||||||
|
|
||||||
users.users.avahi.uid = config.sane.allocations.avahi-uid;
|
|
||||||
users.groups.avahi.gid = config.sane.allocations.avahi-gid;
|
|
||||||
users.users.colord.uid = config.sane.allocations.colord-uid;
|
|
||||||
users.groups.colord.gid = config.sane.allocations.colord-gid;
|
|
||||||
users.users.geoclue.uid = config.sane.allocations.geoclue-uid;
|
|
||||||
users.groups.geoclue.gid = config.sane.allocations.geoclue-gid;
|
|
||||||
users.users.rtkit.uid = config.sane.allocations.rtkit-uid;
|
|
||||||
users.groups.rtkit.gid = config.sane.allocations.rtkit-gid;
|
|
||||||
|
|
||||||
# start gnome/gdm on boot
|
# start gnome/gdm on boot
|
||||||
services.xserver.enable = true;
|
services.xserver.enable = true;
|
||||||
services.xserver.desktopManager.gnome.enable = true;
|
services.xserver.desktopManager.gnome.enable = true;
|
||||||
|
@@ -24,16 +24,6 @@ in
|
|||||||
{
|
{
|
||||||
sane.gui.enable = true;
|
sane.gui.enable = true;
|
||||||
|
|
||||||
users.users.avahi.uid = config.sane.allocations.avahi-uid;
|
|
||||||
users.users.colord.uid = config.sane.allocations.colord-uid;
|
|
||||||
users.users.geoclue.uid = config.sane.allocations.geoclue-uid;
|
|
||||||
users.users.rtkit.uid = config.sane.allocations.rtkit-uid;
|
|
||||||
users.groups.avahi.gid = config.sane.allocations.avahi-gid;
|
|
||||||
users.groups.colord.gid = config.sane.allocations.colord-gid;
|
|
||||||
users.groups.feedbackd.gid = config.sane.allocations.feedbackd-gid;
|
|
||||||
users.groups.geoclue.gid = config.sane.allocations.geoclue-gid;
|
|
||||||
users.groups.rtkit.gid = config.sane.allocations.rtkit-gid;
|
|
||||||
|
|
||||||
# docs: https://github.com/NixOS/nixpkgs/blob/nixos-22.05/nixos/modules/services/x11/desktop-managers/phosh.nix
|
# docs: https://github.com/NixOS/nixpkgs/blob/nixos-22.05/nixos/modules/services/x11/desktop-managers/phosh.nix
|
||||||
services.xserver.desktopManager.phosh = {
|
services.xserver.desktopManager.phosh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -69,7 +59,7 @@ in
|
|||||||
NIXOS_OZONE_WL = "1";
|
NIXOS_OZONE_WL = "1";
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.home-manager.extraPackages = with pkgs; [
|
sane.packages.extraUserPkgs = with pkgs; [
|
||||||
phosh-mobile-settings
|
phosh-mobile-settings
|
||||||
|
|
||||||
# TODO: see about removing this if the in-built gnome-settings bluetooth manager can work
|
# TODO: see about removing this if the in-built gnome-settings bluetooth manager can work
|
||||||
@@ -89,19 +79,16 @@ in
|
|||||||
services.xserver.displayManager.lightdm.extraSeatDefaults = ''
|
services.xserver.displayManager.lightdm.extraSeatDefaults = ''
|
||||||
user-session = phosh
|
user-session = phosh
|
||||||
'';
|
'';
|
||||||
services.xserver.displayManager.lightdm.greeters.gtk.enable = false; # gtk greeter overrides our own?
|
# services.xserver.displayManager.lightdm.greeters.gtk.enable = false; # gtk greeter overrides our own?
|
||||||
services.xserver.displayManager.lightdm.greeter = {
|
# services.xserver.displayManager.lightdm.greeter = {
|
||||||
enable = true;
|
# enable = true;
|
||||||
package = pkgs.lightdm-mobile-greeter.xgreeters;
|
# package = pkgs.lightdm-mobile-greeter.xgreeters;
|
||||||
name = "lightdm-mobile-greeter";
|
# name = "lightdm-mobile-greeter";
|
||||||
};
|
# };
|
||||||
# services.xserver.displayManager.lightdm.enable = true;
|
# # services.xserver.displayManager.lightdm.enable = true;
|
||||||
# # services.xserver.displayManager.lightdm.greeters.enso.enable = true; # tried (with reboot); got a mouse then died. next time was black
|
|
||||||
# # services.xserver.displayManager.lightdm.greeters.gtk.enable = true; # tried (with reboot); unusable without OSK
|
services.xserver.displayManager.lightdm.enable = true;
|
||||||
# # services.xserver.displayManager.lightdm.greeters.mini.enable = true; # tried (with reboot); unusable without OSK
|
services.xserver.displayManager.lightdm.greeters.mobile.enable = true;
|
||||||
# # services.xserver.displayManager.lightdm.greeters.pantheon.enable = true; # tried (no reboot); unusable without OSK
|
|
||||||
# services.xserver.displayManager.lightdm.greeters.slick.enable = true; # tried; unusable without OSK (a11y -> OSK doesn't work)
|
|
||||||
# # services.xserver.displayManager.lightdm.greeters.tiny.enable = true; # tried; block screen
|
|
||||||
|
|
||||||
systemd.services.phosh.wantedBy = lib.mkForce []; # disable auto-start
|
systemd.services.phosh.wantedBy = lib.mkForce []; # disable auto-start
|
||||||
})
|
})
|
||||||
|
28
modules/gui/plasma.nix
Normal file
28
modules/gui/plasma.nix
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{ lib, config, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.gui.plasma;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
sane.gui.plasma.enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
sane.gui.enable = true;
|
||||||
|
|
||||||
|
# start plasma on boot
|
||||||
|
services.xserver.enable = true;
|
||||||
|
services.xserver.desktopManager.plasma5.enable = true;
|
||||||
|
services.xserver.displayManager.sddm.enable = true;
|
||||||
|
|
||||||
|
# gnome does networking stuff with networkmanager
|
||||||
|
networking.useDHCP = false;
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
networking.wireless.enable = lib.mkForce false;
|
||||||
|
};
|
||||||
|
}
|
12
modules/gui/snippets.txt
Normal file
12
modules/gui/snippets.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
https://search.nixos.org/options?channel=unstable&query=
|
||||||
|
https://search.nixos.org/packages?channel=unstable&query=
|
||||||
|
https://nixos.wiki/index.php?go=Go&search=
|
||||||
|
https://github.com/nixos/nixpkgs/pulls?q=
|
||||||
|
https://nur.nix-community.org/
|
||||||
|
https://nix-community.github.io/home-manager/options.html
|
||||||
|
https://w.uninsane.org/viewer#search?books.name=wikipedia_en_all_maxi_2022-05&pattern=
|
||||||
|
https://jackett.uninsane.org/UI/Dashboard#search=
|
||||||
|
https://fed.uninsane.org
|
||||||
|
https://bt.uninsane.org
|
||||||
|
https://sci-hub.se
|
||||||
|
https://news.ycombinator.com
|
@@ -11,34 +11,52 @@ in
|
|||||||
default = false;
|
default = false;
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
};
|
};
|
||||||
|
sane.gui.sway.useGreeter = mkOption {
|
||||||
|
description = ''
|
||||||
|
launch sway via a greeter (like greetd's gtkgreet).
|
||||||
|
sway is usable without a greeter, but skipping the greeter means no PAM session.
|
||||||
|
'';
|
||||||
|
default = true;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
sane.gui.enable = true;
|
sane.gui.enable = true;
|
||||||
users.users.greeter.uid = config.sane.allocations.greeter-uid;
|
|
||||||
users.groups.greeter.gid = config.sane.allocations.greeter-gid;
|
|
||||||
programs.sway = {
|
programs.sway = {
|
||||||
# we configure sway with home-manager, but this enable gets us e.g. opengl and fonts
|
# we configure sway with home-manager, but this enable gets us e.g. opengl and fonts
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
# alternatively, could use SDDM
|
# instead of using `services.greetd`, can instead use SDDM by swapping in these lines.
|
||||||
|
# services.xserver.displayManager.sddm.enable = true;
|
||||||
|
# services.xserver.enable = true;
|
||||||
services.greetd = let
|
services.greetd = let
|
||||||
swayConfig = pkgs.writeText "greetd-sway-config" ''
|
swayConfig-greeter = pkgs.writeText "greetd-sway-config" ''
|
||||||
# `-l` activates layer-shell mode.
|
# `-l` activates layer-shell mode.
|
||||||
exec "${pkgs.greetd.gtkgreet}/bin/gtkgreet -l -c sway"
|
exec "${pkgs.greetd.gtkgreet}/bin/gtkgreet -l -c sway"
|
||||||
'';
|
'';
|
||||||
in {
|
default_session = {
|
||||||
# greetd source/docs:
|
"01" = {
|
||||||
# - <https://git.sr.ht/~kennylevinsen/greetd>
|
# greeter session config
|
||||||
enable = true;
|
command = "${pkgs.sway}/bin/sway --config ${swayConfig-greeter}";
|
||||||
settings = {
|
|
||||||
default_session = {
|
|
||||||
command = "${pkgs.sway}/bin/sway --config ${swayConfig}";
|
|
||||||
# alternatives:
|
# alternatives:
|
||||||
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
|
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
|
||||||
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
|
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
|
||||||
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
|
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
|
||||||
};
|
};
|
||||||
|
"0" = {
|
||||||
|
# no greeter
|
||||||
|
command = "${pkgs.sway}/bin/sway";
|
||||||
|
user = "colin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
# greetd source/docs:
|
||||||
|
# - <https://git.sr.ht/~kennylevinsen/greetd>
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
default_session = default_session."0${builtins.toString cfg.useGreeter}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,18 +72,50 @@ in
|
|||||||
pulse.enable = true;
|
pulse.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
hardware.bluetooth.enable = true;
|
|
||||||
services.blueman.enable = true;
|
|
||||||
|
|
||||||
networking.useDHCP = false;
|
networking.useDHCP = false;
|
||||||
networking.networkmanager.enable = true;
|
networking.networkmanager.enable = true;
|
||||||
networking.wireless.enable = lib.mkForce false;
|
networking.wireless.enable = lib.mkForce false;
|
||||||
|
|
||||||
|
hardware.bluetooth.enable = true;
|
||||||
|
services.blueman.enable = true;
|
||||||
|
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
|
||||||
|
services.gnome.gnome-settings-daemon.enable = true;
|
||||||
|
# start the components of gsd we need at login
|
||||||
|
systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
|
||||||
|
# go ahead and `systemctl --user cat gnome-session-initialized.target`. i dare you.
|
||||||
|
# the only way i can figure out how to get Rfkill to actually load is to just disable all the shit it depends on.
|
||||||
|
# it doesn't actually seem to need ANY of them in the first place T_T
|
||||||
|
systemd.user.targets."gnome-session-initialized".enable = false;
|
||||||
|
# bluez can't connect to audio devices unless pipewire is running.
|
||||||
|
# a system service can't depend on a user service, so just launch it at graphical-session
|
||||||
|
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
||||||
|
|
||||||
sane.home-manager.windowManager.sway = {
|
sane.home-manager.windowManager.sway = {
|
||||||
enable = true;
|
enable = true;
|
||||||
wrapperFeatures.gtk = true;
|
wrapperFeatures.gtk = true;
|
||||||
config = rec {
|
config = let
|
||||||
terminal = "${pkgs.kitty}/bin/kitty";
|
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
|
||||||
|
sed = "${pkgs.gnused}/bin/sed";
|
||||||
|
wtype = "${pkgs.wtype}/bin/wtype";
|
||||||
|
kitty = "${pkgs.kitty}/bin/kitty";
|
||||||
|
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
|
||||||
|
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
|
||||||
|
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
|
||||||
|
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
|
||||||
|
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
|
||||||
|
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
|
||||||
|
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
|
||||||
|
# "bookmarking"/snippets inspired by Luke Smith:
|
||||||
|
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
||||||
|
snip-file = ./snippets.txt;
|
||||||
|
# TODO: querying sops here breaks encapsulation
|
||||||
|
list-snips = "cat ${snip-file} ${config.sops.secrets.snippets.path}";
|
||||||
|
strip-comments = "${sed} 's/ #.*$//'";
|
||||||
|
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
|
||||||
|
# TODO: next splatmoji release should allow `-s none` to disable skin tones
|
||||||
|
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
|
||||||
|
in rec {
|
||||||
|
terminal = kitty;
|
||||||
window = {
|
window = {
|
||||||
border = 3; # pixel boundary between windows
|
border = 3; # pixel boundary between windows
|
||||||
hideEdgeBorders = "smart"; # don't show border if only window on workspace
|
hideEdgeBorders = "smart"; # don't show border if only window on workspace
|
||||||
@@ -86,7 +136,7 @@ in
|
|||||||
modifier = "Mod1";
|
modifier = "Mod1";
|
||||||
# list of launchers: https://www.reddit.com/r/swaywm/comments/v39hxa/your_favorite_launcher/
|
# list of launchers: https://www.reddit.com/r/swaywm/comments/v39hxa/your_favorite_launcher/
|
||||||
# menu = "${pkgs.dmenu}/bin/dmenu_path";
|
# menu = "${pkgs.dmenu}/bin/dmenu_path";
|
||||||
menu = "${pkgs.fuzzel}/bin/fuzzel";
|
menu = fuzzel;
|
||||||
# menu = "${pkgs.albert}/bin/albert";
|
# menu = "${pkgs.albert}/bin/albert";
|
||||||
left = "h";
|
left = "h";
|
||||||
down = "j";
|
down = "j";
|
||||||
@@ -97,7 +147,9 @@ in
|
|||||||
"${modifier}+Return" = "exec ${terminal}";
|
"${modifier}+Return" = "exec ${terminal}";
|
||||||
"${modifier}+Shift+q" = "kill";
|
"${modifier}+Shift+q" = "kill";
|
||||||
"${modifier}+d" = "exec ${menu}";
|
"${modifier}+d" = "exec ${menu}";
|
||||||
"${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
|
"${modifier}+s" = "exec ${snip-cmd}";
|
||||||
|
"${modifier}+l" = "exec ${lock-cmd}";
|
||||||
|
"${modifier}+slash" = "exec ${emoji-cmd}";
|
||||||
|
|
||||||
# "${modifier}+${left}" = "focus left";
|
# "${modifier}+${left}" = "focus left";
|
||||||
# "${modifier}+${down}" = "focus down";
|
# "${modifier}+${down}" = "focus down";
|
||||||
@@ -124,7 +176,7 @@ in
|
|||||||
"${modifier}+f" = "fullscreen toggle";
|
"${modifier}+f" = "fullscreen toggle";
|
||||||
"${modifier}+a" = "focus parent";
|
"${modifier}+a" = "focus parent";
|
||||||
|
|
||||||
"${modifier}+s" = "layout stacking";
|
# "${modifier}+s" = "layout stacking";
|
||||||
"${modifier}+w" = "layout tabbed";
|
"${modifier}+w" = "layout tabbed";
|
||||||
"${modifier}+e" = "layout toggle split";
|
"${modifier}+e" = "layout toggle split";
|
||||||
|
|
||||||
@@ -168,19 +220,20 @@ in
|
|||||||
"exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
|
"exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
|
||||||
|
|
||||||
"${modifier}+r" = "mode resize";
|
"${modifier}+r" = "mode resize";
|
||||||
} // {
|
|
||||||
# media keys
|
# media keys
|
||||||
XF86MonBrightnessDown = ''exec "${pkgs.brightnessctl}/bin/brightnessctl set 2%-"'';
|
XF86MonBrightnessDown = "exec ${brightness-down-cmd}";
|
||||||
XF86MonBrightnessUp = ''exec "${pkgs.brightnessctl}/bin/brightnessctl set +2%"'';
|
XF86MonBrightnessUp = "exec ${brightness-up-cmd}";
|
||||||
|
|
||||||
XF86AudioRaiseVolume = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5'";
|
# TODO: hook into a visual prompt to display volume?
|
||||||
XF86AudioLowerVolume = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5'";
|
XF86AudioRaiseVolume = "exec ${vol-up-cmd}";
|
||||||
XF86AudioMute = "exec '${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute'";
|
XF86AudioLowerVolume = "exec ${vol-down-cmd}";
|
||||||
|
XF86AudioMute = "exec ${mute-cmd}";
|
||||||
|
|
||||||
"${modifier}+Page_Up" = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5'";
|
"${modifier}+Page_Up" = "exec ${vol-up-cmd}";
|
||||||
"${modifier}+Page_Down" = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5'";
|
"${modifier}+Page_Down" = "exec ${vol-down-cmd}";
|
||||||
|
|
||||||
"${modifier}+Print" = "exec '${pkgs.sway-contrib.grimshot}/bin/grimshot copy area'";
|
"${modifier}+Print" = "exec ${screenshot-cmd}";
|
||||||
};
|
};
|
||||||
|
|
||||||
# mostly defaults:
|
# mostly defaults:
|
||||||
@@ -580,7 +633,7 @@ in
|
|||||||
# }
|
# }
|
||||||
# '';
|
# '';
|
||||||
};
|
};
|
||||||
sane.home-manager.extraPackages = with pkgs; [
|
sane.packages.extraUserPkgs = with pkgs; [
|
||||||
swaylock
|
swaylock
|
||||||
swayidle # (unused)
|
swayidle # (unused)
|
||||||
wl-clipboard
|
wl-clipboard
|
||||||
|
12
modules/home-manager/aerc.nix
Normal file
12
modules/home-manager/aerc.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Terminal UI mail client
|
||||||
|
{ config, lib, sane-lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
sops.secrets."aerc_accounts" = {
|
||||||
|
owner = config.users.users.colin.name;
|
||||||
|
sopsFile = ../../secrets/universal/aerc_accounts.conf;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sane.fs."/home/colin/.config/aerc/accounts.conf" = sane-lib.fs.wantedSymlinkTo config.sops.secrets.aerc_accounts.path;
|
||||||
|
}
|
144
modules/home-manager/default.nix
Normal file
144
modules/home-manager/default.nix
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# docs:
|
||||||
|
# https://rycee.gitlab.io/home-manager/
|
||||||
|
# https://rycee.gitlab.io/home-manager/options.html
|
||||||
|
# man home-configuration.nix
|
||||||
|
#
|
||||||
|
|
||||||
|
{ lib, config, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.home-manager;
|
||||||
|
# extract `pkg` from `sane.packages.enabledUserPkgs`
|
||||||
|
pkg-list = pkgspec: builtins.map (e: e.pkg) pkgspec;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./aerc.nix
|
||||||
|
./firefox.nix
|
||||||
|
./gfeeds.nix
|
||||||
|
./git.nix
|
||||||
|
./gpodder.nix
|
||||||
|
./keyring.nix
|
||||||
|
./kitty.nix
|
||||||
|
./mpv.nix
|
||||||
|
./neovim.nix
|
||||||
|
./newsflash.nix
|
||||||
|
./splatmoji.nix
|
||||||
|
./ssh.nix
|
||||||
|
./sublime-music.nix
|
||||||
|
./vlc.nix
|
||||||
|
./zsh
|
||||||
|
];
|
||||||
|
|
||||||
|
options = {
|
||||||
|
sane.home-manager.enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
# attributes to copy directly to home-manager's `wayland.windowManager` option
|
||||||
|
sane.home-manager.windowManager = mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
# extra attributes to include in home-manager's `programs` option
|
||||||
|
sane.home-manager.programs = mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.attrs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
home-manager.useGlobalPkgs = true;
|
||||||
|
home-manager.useUserPackages = true;
|
||||||
|
|
||||||
|
# XXX this weird rename + closure is to get home-manager's `config.lib.file` to exist.
|
||||||
|
# see: https://github.com/nix-community/home-manager/issues/589#issuecomment-950474105
|
||||||
|
home-manager.users.colin = let sysconfig = config; in { config, ... }: {
|
||||||
|
|
||||||
|
# run `home-manager-help` to access manpages
|
||||||
|
# or `man home-configuration.nix`
|
||||||
|
manual.html.enable = false; # TODO: set to true later (build failure)
|
||||||
|
manual.manpages.enable = false; # TODO: enable after https://github.com/nix-community/home-manager/issues/3344
|
||||||
|
|
||||||
|
home.packages = pkg-list sysconfig.sane.packages.enabledUserPkgs;
|
||||||
|
wayland.windowManager = cfg.windowManager;
|
||||||
|
|
||||||
|
home.stateVersion = "21.11";
|
||||||
|
home.username = "colin";
|
||||||
|
home.homeDirectory = "/home/colin";
|
||||||
|
|
||||||
|
# XDG defines things like ~/Desktop, ~/Downloads, etc.
|
||||||
|
# these clutter the home, so i mostly don't use them.
|
||||||
|
xdg.userDirs = {
|
||||||
|
enable = true;
|
||||||
|
createDirectories = false; # on headless systems, most xdg dirs are noise
|
||||||
|
desktop = "$HOME/.xdg/Desktop";
|
||||||
|
documents = "$HOME/dev";
|
||||||
|
download = "$HOME/tmp";
|
||||||
|
music = "$HOME/Music";
|
||||||
|
pictures = "$HOME/Pictures";
|
||||||
|
publicShare = "$HOME/.xdg/Public";
|
||||||
|
templates = "$HOME/.xdg/Templates";
|
||||||
|
videos = "$HOME/Videos";
|
||||||
|
};
|
||||||
|
|
||||||
|
# the xdg mime type for a file can be found with:
|
||||||
|
# - `xdg-mime query filetype path/to/thing.ext`
|
||||||
|
xdg.mimeApps.enable = true;
|
||||||
|
xdg.mimeApps.defaultApplications = let
|
||||||
|
www = sysconfig.sane.web-browser.browser.desktop;
|
||||||
|
pdf = "org.gnome.Evince.desktop";
|
||||||
|
md = "obsidian.desktop";
|
||||||
|
thumb = "org.gnome.gThumb.desktop";
|
||||||
|
video = "vlc.desktop";
|
||||||
|
# audio = "mpv.desktop";
|
||||||
|
audio = "vlc.desktop";
|
||||||
|
in {
|
||||||
|
# HTML
|
||||||
|
"text/html" = [ www ];
|
||||||
|
"x-scheme-handler/http" = [ www ];
|
||||||
|
"x-scheme-handler/https" = [ www ];
|
||||||
|
"x-scheme-handler/about" = [ www ];
|
||||||
|
"x-scheme-handler/unknown" = [ www ];
|
||||||
|
# RICH-TEXT DOCUMENTS
|
||||||
|
"application/pdf" = [ pdf ];
|
||||||
|
"text/markdown" = [ md ];
|
||||||
|
# IMAGES
|
||||||
|
"image/heif" = [ thumb ]; # apple codec
|
||||||
|
"image/png" = [ thumb ];
|
||||||
|
"image/jpeg" = [ thumb ];
|
||||||
|
# VIDEO
|
||||||
|
"video/mp4" = [ video ];
|
||||||
|
"video/quicktime" = [ video ];
|
||||||
|
"video/x-matroska" = [ video ];
|
||||||
|
# AUDIO
|
||||||
|
"audio/flac" = [ audio ];
|
||||||
|
"audio/mpeg" = [ audio ];
|
||||||
|
"audio/x-vorbis+ogg" = [ audio ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# libreoffice: disable first-run stuff
|
||||||
|
xdg.configFile."libreoffice/4/user/registrymodifications.xcu".text = ''
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="FirstRun" oor:op="fuse"><value>false</value></prop></item>
|
||||||
|
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="ShowTipOfTheDay" oor:op="fuse"><value>false</value></prop></item>
|
||||||
|
</oor:items>
|
||||||
|
'';
|
||||||
|
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeDonateShown" oor:op="fuse"><value>1667693880</value></prop></item>
|
||||||
|
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeGetInvolvedShown" oor:op="fuse"><value>1667693880</value></prop></item>
|
||||||
|
|
||||||
|
|
||||||
|
programs = lib.mkMerge [
|
||||||
|
{
|
||||||
|
home-manager.enable = true; # this lets home-manager manage dot-files in user dirs, i think
|
||||||
|
# "command not found" will cause the command to be searched in nixpkgs
|
||||||
|
nix-index.enable = true;
|
||||||
|
}
|
||||||
|
cfg.programs
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
161
modules/home-manager/firefox.nix
Normal file
161
modules/home-manager/firefox.nix
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
# common settings to toggle (at runtime, in about:config):
|
||||||
|
# > security.ssl.require_safe_negotiation
|
||||||
|
|
||||||
|
# librewolf is a forked firefox which patches firefox to allow more things
|
||||||
|
# (like default search engines) to be configurable at runtime.
|
||||||
|
# many of the settings below won't have effect without those patches.
|
||||||
|
# see: https://gitlab.com/librewolf-community/settings/-/blob/master/distribution/policies.json
|
||||||
|
|
||||||
|
{ config, lib, pkgs, sane-lib, ...}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.web-browser;
|
||||||
|
# allow easy switching between firefox and librewolf with `defaultSettings`, below
|
||||||
|
librewolfSettings = {
|
||||||
|
browser = pkgs.librewolf-unwrapped;
|
||||||
|
# browser = pkgs.librewolf-unwrapped.overrideAttrs (drv: {
|
||||||
|
# # this allows side-loading unsigned addons
|
||||||
|
# MOZ_REQUIRE_SIGNING = false;
|
||||||
|
# });
|
||||||
|
libName = "librewolf";
|
||||||
|
dotDir = ".librewolf";
|
||||||
|
cacheDir = ".cache/librewolf"; # TODO: is it?
|
||||||
|
desktop = "librewolf.desktop";
|
||||||
|
};
|
||||||
|
firefoxSettings = {
|
||||||
|
browser = pkgs.firefox-esr-unwrapped;
|
||||||
|
libName = "firefox";
|
||||||
|
dotDir = ".mozilla/firefox";
|
||||||
|
cacheDir = ".cache/mozilla";
|
||||||
|
desktop = "firefox.desktop";
|
||||||
|
};
|
||||||
|
defaultSettings = firefoxSettings;
|
||||||
|
# defaultSettings = librewolfSettings;
|
||||||
|
|
||||||
|
package = pkgs.wrapFirefox cfg.browser.browser {
|
||||||
|
# inherit the default librewolf.cfg
|
||||||
|
# it can be further customized via ~/.librewolf/librewolf.overrides.cfg
|
||||||
|
inherit (pkgs.librewolf-unwrapped) extraPrefsFiles;
|
||||||
|
inherit (cfg.browser) libName;
|
||||||
|
|
||||||
|
extraNativeMessagingHosts = [ pkgs.browserpass ];
|
||||||
|
# extraNativeMessagingHosts = [ pkgs.gopass-native-messaging-host ];
|
||||||
|
|
||||||
|
nixExtensions = let
|
||||||
|
addon = name: extid: hash: pkgs.fetchFirefoxAddon {
|
||||||
|
inherit name hash;
|
||||||
|
url = "https://addons.mozilla.org/firefox/downloads/latest/${name}/latest.xpi";
|
||||||
|
# extid can be found by unar'ing the above xpi, and copying browser_specific_settings.gecko.id field
|
||||||
|
fixedExtid = extid;
|
||||||
|
};
|
||||||
|
localAddon = pkg: pkgs.fetchFirefoxAddon {
|
||||||
|
inherit (pkg) name;
|
||||||
|
src = "${pkg}/share/mozilla/extensions/\\{ec8030f7-c20a-464f-9b0e-13a3a9e97384\\}/${pkg.extid}.xpi";
|
||||||
|
fixedExtid = pkg.extid;
|
||||||
|
};
|
||||||
|
in [
|
||||||
|
# get names from:
|
||||||
|
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
|
||||||
|
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
|
||||||
|
(addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-a/ivUmY1P6teq9x0dt4CbgHt+3kBsEMMXlOfZ5Hx7cg=")
|
||||||
|
(addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-d2K3ufvurWnYVzqLbyR//MgejybkY9exitAf9RdLNRo=")
|
||||||
|
(addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-t6Q335Nq60mDILPmzem+DT5KflleAPVJL3bsaA+UL0g=")
|
||||||
|
(addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=")
|
||||||
|
(addon "ether-metamask" "webextension@metamask.io" "sha256-G+MwJDOcsaxYSUXjahHJmkWnjLeQ0Wven8DU/lGeMzA=")
|
||||||
|
(addon "ublacklist" "@ublacklist" "sha256-vHe/7EYOzcKeAbTElmt0Rb4E2rX0f3JgXThJaUmaz+M=")
|
||||||
|
(addon "i2p-in-private-browsing" "i2ppb@eyedeekay.github.io" "sha256-dJcJ3jxeAeAkRvhODeIVrCflvX+S4E0wT/PyYzQBQWs=")
|
||||||
|
# (addon "browserpass-ce" "browserpass@maximbaz.com" "sha256-sXgUBbRvMnRpeIW1MTkmTcoqtW/8RDXAkxAq1evFkpc=")
|
||||||
|
(localAddon pkgs.browserpass-extension)
|
||||||
|
];
|
||||||
|
|
||||||
|
extraPolicies = {
|
||||||
|
NoDefaultBookmarks = true;
|
||||||
|
SearchEngines = {
|
||||||
|
Default = "DuckDuckGo";
|
||||||
|
};
|
||||||
|
AppUpdateURL = "https://localhost";
|
||||||
|
DisableAppUpdate = true;
|
||||||
|
OverrideFirstRunPage = "";
|
||||||
|
OverridePostUpdatePage = "";
|
||||||
|
DisableSystemAddonUpdate = true;
|
||||||
|
DisableFirefoxStudies = true;
|
||||||
|
DisableTelemetry = true;
|
||||||
|
DisableFeedbackCommands = true;
|
||||||
|
DisablePocket = true;
|
||||||
|
DisableSetDesktopBackground = false;
|
||||||
|
|
||||||
|
# remove many default search providers
|
||||||
|
# XXX this seems to prevent the `nixExtensions` from taking effect
|
||||||
|
# Extensions.Uninstall = [
|
||||||
|
# "google@search.mozilla.org"
|
||||||
|
# "bing@search.mozilla.org"
|
||||||
|
# "amazondotcom@search.mozilla.org"
|
||||||
|
# "ebay@search.mozilla.org"
|
||||||
|
# "twitter@search.mozilla.org"
|
||||||
|
# ];
|
||||||
|
# XXX doesn't seem to have any effect...
|
||||||
|
# docs: https://github.com/mozilla/policy-templates#homepage
|
||||||
|
# Homepage = {
|
||||||
|
# HomepageURL = "https://uninsane.org/";
|
||||||
|
# StartPage = "homepage";
|
||||||
|
# };
|
||||||
|
# NewTabPage = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
sane.web-browser.browser = mkOption {
|
||||||
|
default = defaultSettings;
|
||||||
|
type = types.attrs;
|
||||||
|
};
|
||||||
|
sane.web-browser.persistData = mkOption {
|
||||||
|
description = "optional store name to which persist browsing data (like history)";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
sane.web-browser.persistCache = mkOption {
|
||||||
|
description = "optional store name to which persist browser cache";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = "cryptClearOnBoot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf config.sane.home-manager.enable {
|
||||||
|
|
||||||
|
# uBlock filter list configuration.
|
||||||
|
# specifically, enable the GDPR cookie prompt blocker.
|
||||||
|
# data.toOverwrite.filterLists is additive (i.e. it supplements the default filters)
|
||||||
|
# this configuration method is documented here:
|
||||||
|
# - <https://github.com/gorhill/uBlock/issues/2986#issuecomment-364035002>
|
||||||
|
# the specific attribute path is found via scraping ublock code here:
|
||||||
|
# - <https://github.com/gorhill/uBlock/blob/master/src/js/storage.js>
|
||||||
|
# - <https://github.com/gorhill/uBlock/blob/master/assets/assets.json>
|
||||||
|
sane.fs."/home/colin/${cfg.browser.dotDir}/managed-storage/uBlock0@raymondhill.net.json" = sane-lib.fs.wantedText ''
|
||||||
|
{
|
||||||
|
"name": "uBlock0@raymondhill.net",
|
||||||
|
"description": "ignored",
|
||||||
|
"type": "storage",
|
||||||
|
"data": {
|
||||||
|
"toOverwrite": "{\"filterLists\": [\"fanboy-cookiemonster\"]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
sane.fs."/home/colin/${cfg.browser.dotDir}/${cfg.browser.libName}.overrides.cfg" = sane-lib.fs.wantedText ''
|
||||||
|
// if we can't query the revocation status of a SSL cert because the issuer is offline,
|
||||||
|
// treat it as unrevoked.
|
||||||
|
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
|
||||||
|
defaultPref("security.OCSP.require", false);
|
||||||
|
'';
|
||||||
|
|
||||||
|
sane.packages.extraGuiPkgs = [ package ];
|
||||||
|
# flood the cache to disk to avoid it taking up too much tmp
|
||||||
|
sane.persist.home.byPath."${cfg.browser.cacheDir}" = lib.mkIf (cfg.persistCache != null) {
|
||||||
|
store = cfg.persistCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.persist.home.byPath."${cfg.browser.dotDir}" = lib.mkIf (cfg.persistData != null) {
|
||||||
|
store = cfg.persistData;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
42
modules/home-manager/gfeeds.nix
Normal file
42
modules/home-manager/gfeeds.nix
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# gnome feeds RSS viewer
|
||||||
|
{ config, lib, sane-lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
feeds = sane-lib.feeds;
|
||||||
|
all-feeds = config.sane.feeds;
|
||||||
|
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||||
|
in {
|
||||||
|
sane.fs."/home/colin/.config/org.gabmus.gfeeds.json" = sane-lib.fs.wantedText (
|
||||||
|
builtins.toJSON {
|
||||||
|
# feed format is a map from URL to a dict,
|
||||||
|
# with dict["tags"] a list of string tags.
|
||||||
|
feeds = sane-lib.mapToAttrs (feed: {
|
||||||
|
name = feed.url;
|
||||||
|
value.tags = [ feed.cat feed.freq ];
|
||||||
|
}) wanted-feeds;
|
||||||
|
dark_reader = false;
|
||||||
|
new_first = true;
|
||||||
|
# windowsize = {
|
||||||
|
# width = 350;
|
||||||
|
# height = 650;
|
||||||
|
# };
|
||||||
|
max_article_age_days = 90;
|
||||||
|
enable_js = false;
|
||||||
|
max_refresh_threads = 3;
|
||||||
|
# saved_items = {};
|
||||||
|
# read_items = [];
|
||||||
|
show_read_items = true;
|
||||||
|
full_article_title = true;
|
||||||
|
# views: "webview", "reader", "rsscont"
|
||||||
|
default_view = "rsscont";
|
||||||
|
open_links_externally = true;
|
||||||
|
full_feed_name = false;
|
||||||
|
refresh_on_startup = true;
|
||||||
|
tags = lib.unique (
|
||||||
|
(builtins.catAttrs "cat" wanted-feeds) ++ (builtins.catAttrs "freq" wanted-feeds)
|
||||||
|
);
|
||||||
|
open_youtube_externally = false;
|
||||||
|
media_player = "vlc"; # default: mpv
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
20
modules/home-manager/git.nix
Normal file
20
modules/home-manager/git.nix
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
home-manager.users.colin.programs.git = {
|
||||||
|
enable = true;
|
||||||
|
userName = "colin";
|
||||||
|
userEmail = "colin@uninsane.org";
|
||||||
|
|
||||||
|
aliases = { co = "checkout"; };
|
||||||
|
extraConfig = {
|
||||||
|
# difftastic docs:
|
||||||
|
# - <https://difftastic.wilfred.me.uk/git.html>
|
||||||
|
diff.tool = "difftastic";
|
||||||
|
difftool.prompt = false;
|
||||||
|
"difftool \"difftastic\"".cmd = ''${pkgs.difftastic}/bin/difft "$LOCAL" "$REMOTE"'';
|
||||||
|
# now run `git difftool` to use difftastic git
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
12
modules/home-manager/gpodder.nix
Normal file
12
modules/home-manager/gpodder.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# gnome feeds RSS viewer
|
||||||
|
{ config, sane-lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
feeds = sane-lib.feeds;
|
||||||
|
all-feeds = config.sane.feeds;
|
||||||
|
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
|
||||||
|
in {
|
||||||
|
sane.fs."/home/colin/.config/gpodderFeeds.opml" = sane-lib.fs.wantedText (
|
||||||
|
feeds.feedsToOpml wanted-feeds
|
||||||
|
);
|
||||||
|
}
|
11
modules/home-manager/keyring.nix
Normal file
11
modules/home-manager/keyring.nix
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{ config, lib, sane-lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
sane.persist.home.private = [ ".local/share/keyrings" ];
|
||||||
|
|
||||||
|
sane.fs."/home/colin/private/.local/share/keyrings/default" = {
|
||||||
|
generated.script.script = builtins.readFile ../../scripts/init-keyring;
|
||||||
|
wantedBy = [ config.sane.fs."/home/colin/private".unit ];
|
||||||
|
};
|
||||||
|
}
|
71
modules/home-manager/kitty.nix
Normal file
71
modules/home-manager/kitty.nix
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
home-manager.users.colin.programs.kitty = {
|
||||||
|
enable = true;
|
||||||
|
# docs: https://sw.kovidgoyal.net/kitty/conf/
|
||||||
|
settings = {
|
||||||
|
# disable terminal bell (when e.g. you backspace too many times)
|
||||||
|
enable_audio_bell = false;
|
||||||
|
};
|
||||||
|
keybindings = {
|
||||||
|
"ctrl+n" = "new_os_window_with_cwd";
|
||||||
|
};
|
||||||
|
# docs: https://github.com/kovidgoyal/kitty-themes
|
||||||
|
# theme = "1984 Light"; # dislike: awful, harsh blues/teals
|
||||||
|
# theme = "Adventure Time"; # dislike: harsh (dark)
|
||||||
|
# theme = "Atom One Light"; # GOOD: light theme. all color combos readable. not a huge fan of the blue.
|
||||||
|
# theme = "Belafonte Day"; # dislike: too low contrast for text colors
|
||||||
|
# theme = "Belafonte Night"; # better: dark theme that's easy on the eyes. all combos readable. low contrast.
|
||||||
|
# theme = "Catppuccin"; # dislike: a bit pale/low-contrast (dark)
|
||||||
|
# theme = "Desert"; # mediocre: colors are harsh
|
||||||
|
# theme = "Earthsong"; # BEST: dark theme. readable, good contrast. unique, but decent colors.
|
||||||
|
# theme = "Espresso Libre"; # better: dark theme. readable, but meh colors
|
||||||
|
# theme = "Forest Night"; # decent: very pastel. it's workable, but unconventional and muted/flat.
|
||||||
|
# theme = "Gruvbox Material Light Hard"; # mediocre light theme.
|
||||||
|
# theme = "kanagawabones"; # better: dark theme. colors are too background-y
|
||||||
|
# theme = "Kaolin Dark"; # dislike: too dark
|
||||||
|
# theme = "Kaolin Breeze"; # mediocre: not-too-harsh light theme, but some parts are poor contrast
|
||||||
|
# theme = "Later This Evening"; # mediocre: not-too-harsh dark theme, but cursor is poor contrast
|
||||||
|
# theme = "Material"; # decent: light theme, few colors.
|
||||||
|
# theme = "Mayukai"; # decent: not-too-harsh dark theme. the teal is a bit straining
|
||||||
|
# theme = "Nord"; # mediocre: pale background, low contrast
|
||||||
|
# theme = "One Half Light"; # better: not-too-harsh light theme. contrast could be better
|
||||||
|
theme = "PaperColor Dark"; # BEST: dark theme, very readable still the colors are background-y
|
||||||
|
# theme = "Parasio Dark"; # dislike: too low contrast
|
||||||
|
# theme = "Pencil Light"; # better: not-too-harsh light theme. decent contrast.
|
||||||
|
# theme = "Pnevma"; # dislike: too low contrast
|
||||||
|
# theme = "Piatto Light"; # better: readable light theme. pleasing colors. powerline prompt is hard to read.
|
||||||
|
# theme = "Rosé Pine Dawn"; # GOOD: light theme. all color combinations are readable. it is very mild -- may need to manually tweak contrast. tasteful colors
|
||||||
|
# theme = "Rosé Pine Moon"; # GOOD: dark theme. tasteful colors. but background is a bit intense
|
||||||
|
# theme = "Sea Shells"; # mediocre. not all color combos are readable
|
||||||
|
# theme = "Solarized Light"; # mediocre: not-too-harsh light theme; GREAT background; but some colors are low contrast
|
||||||
|
# theme = "Solarized Dark Higher Contrast"; # better: dark theme, decent colors
|
||||||
|
# theme = "Sourcerer"; # mediocre: ugly colors
|
||||||
|
# theme = "Space Gray"; # mediocre: too muted
|
||||||
|
# theme = "Space Gray Eighties"; # better: all readable, decent colors
|
||||||
|
# theme = "Spacemacs"; # mediocre: too muted
|
||||||
|
# theme = "Spring"; # mediocre: readable light theme, but the teal is ugly.
|
||||||
|
# theme = "Srcery"; # better: highly readable. colors are ehhh
|
||||||
|
# theme = "Substrata"; # decent: nice colors, but a bit flat.
|
||||||
|
# theme = "Sundried"; # mediocre: the solar text makes me squint
|
||||||
|
# theme = "Symfonic"; # mediocre: the dark purple has low contrast to the black bg.
|
||||||
|
# theme = "Tango Light"; # dislike: teal is too grating
|
||||||
|
# theme = "Tokyo Night Day"; # medicore: too muted
|
||||||
|
# theme = "Tokyo Night"; # better: tasteful. a bit flat
|
||||||
|
# theme = "Tomorrow"; # GOOD: all color combinations are readable. contrast is slightly better than Rose. on the blander side
|
||||||
|
# theme = "Treehouse"; # dislike: the orange is harsh on my eyes.
|
||||||
|
# theme = "Urple"; # dislike: weird palette
|
||||||
|
# theme = "Warm Neon"; # decent: not-too-harsh dark theme. the green is a bit unattractive
|
||||||
|
# theme = "Wild Cherry"; # GOOD: dark theme: nice colors. a bit flat
|
||||||
|
# theme = "Xcodedark"; # dislike: bad palette
|
||||||
|
# theme = "citylights"; # decent: dark theme. some parts have just a bit low contrast
|
||||||
|
# theme = "neobones_light"; # better light theme. the background is maybe too muted
|
||||||
|
# theme = "vimbones";
|
||||||
|
# theme = "zenbones_dark"; # mediocre: readable, but meh colors
|
||||||
|
# theme = "zenbones_light"; # decent: light theme. all colors are readable. contrast is passable but not excellent. highlight color is BAD
|
||||||
|
# theme = "zenwritten_dark"; # mediocre: looks same as zenbones_dark
|
||||||
|
# extraConfig = "";
|
||||||
|
};
|
||||||
|
}
|
13
modules/home-manager/mpv.nix
Normal file
13
modules/home-manager/mpv.nix
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
home-manager.users.colin.programs.mpv = {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
save-position-on-quit = true;
|
||||||
|
keep-open = "yes";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
109
modules/home-manager/neovim.nix
Normal file
109
modules/home-manager/neovim.nix
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
# private because there could be sensitive things in the swap
|
||||||
|
sane.persist.home.private = [ ".cache/vim-swap" ];
|
||||||
|
|
||||||
|
home-manager.users.colin.programs.neovim = {
|
||||||
|
# neovim: https://github.com/neovim/neovim
|
||||||
|
enable = true;
|
||||||
|
viAlias = true;
|
||||||
|
vimAlias = true;
|
||||||
|
plugins = with pkgs.vimPlugins; [
|
||||||
|
# docs: surround-nvim: https://github.com/ur4ltz/surround.nvim/
|
||||||
|
# docs: vim-surround: https://github.com/tpope/vim-surround
|
||||||
|
vim-surround
|
||||||
|
# docs: fzf-vim (fuzzy finder): https://github.com/junegunn/fzf.vim
|
||||||
|
fzf-vim
|
||||||
|
# docs: https://github.com/KeitaNakamura/tex-conceal.vim/
|
||||||
|
({
|
||||||
|
plugin = tex-conceal-vim;
|
||||||
|
type = "viml";
|
||||||
|
config = ''
|
||||||
|
" present prettier fractions
|
||||||
|
let g:tex_conceal_frac=1
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
({
|
||||||
|
plugin = vim-SyntaxRange;
|
||||||
|
type = "viml";
|
||||||
|
config = ''
|
||||||
|
" enable markdown-style codeblock highlighting for tex code
|
||||||
|
autocmd BufEnter * call SyntaxRange#Include('```tex', '```', 'tex', 'NonText')
|
||||||
|
" autocmd Syntax tex set conceallevel=2
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
# treesitter syntax highlighting: https://nixos.wiki/wiki/Tree_sitters
|
||||||
|
# docs: https://github.com/nvim-treesitter/nvim-treesitter
|
||||||
|
# config taken from: https://github.com/i077/system/blob/master/modules/home/neovim/default.nix
|
||||||
|
# this is required for tree-sitter to even highlight
|
||||||
|
({
|
||||||
|
plugin = nvim-treesitter.withAllGrammars;
|
||||||
|
type = "lua";
|
||||||
|
config = ''
|
||||||
|
require'nvim-treesitter.configs'.setup {
|
||||||
|
highlight = {
|
||||||
|
enable = true,
|
||||||
|
-- disable treesitter on Rust so that we can use SyntaxRange
|
||||||
|
-- and leverage TeX rendering in rust projects
|
||||||
|
disable = { "rust", "tex", "latex" },
|
||||||
|
-- disable = { "tex", "latex" },
|
||||||
|
-- true to also use builtin vim syntax highlighting when treesitter fails
|
||||||
|
additional_vim_regex_highlighting = false
|
||||||
|
},
|
||||||
|
incremental_selection = {
|
||||||
|
enable = true,
|
||||||
|
keymaps = {
|
||||||
|
init_selection = "gnn",
|
||||||
|
node_incremental = "grn",
|
||||||
|
mcope_incremental = "grc",
|
||||||
|
node_decremental = "grm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indent = {
|
||||||
|
enable = true,
|
||||||
|
disable = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vim.o.foldmethod = 'expr'
|
||||||
|
vim.o.foldexpr = 'nvim_treesitter#foldexpr()'
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
extraConfig = ''
|
||||||
|
" let the terminal handle mouse events, that way i get OS-level ctrl+shift+c/etc
|
||||||
|
" this used to be default, until <https://github.com/neovim/neovim/pull/19290>
|
||||||
|
set mouse=
|
||||||
|
|
||||||
|
" copy/paste to system clipboard
|
||||||
|
set clipboard=unnamedplus
|
||||||
|
|
||||||
|
" screw tabs; always expand them into spaces
|
||||||
|
set expandtab
|
||||||
|
|
||||||
|
" at least don't open files with sections folded by default
|
||||||
|
set nofoldenable
|
||||||
|
|
||||||
|
" allow text substitutions for certain glyphs.
|
||||||
|
" higher number = more aggressive substitution (0, 1, 2, 3)
|
||||||
|
" i only make use of this for tex, but it's unclear how to
|
||||||
|
" apply that *just* to tex and retain the SyntaxRange stuff.
|
||||||
|
set conceallevel=2
|
||||||
|
|
||||||
|
" horizontal rule under the active line
|
||||||
|
" set cursorline
|
||||||
|
|
||||||
|
" highlight trailing space & related syntax errors (doesn't seem to work??)
|
||||||
|
" let c_space_errors=1
|
||||||
|
" let python_space_errors=1
|
||||||
|
|
||||||
|
" enable highlighting of leading/trailing spaces,
|
||||||
|
" and especially tabs
|
||||||
|
" source: https://www.reddit.com/r/neovim/comments/chlmfk/highlight_trailing_whitespaces_in_neovim/
|
||||||
|
set list
|
||||||
|
set listchars=tab:▷\·,trail:·,extends:◣,precedes:◢,nbsp:○
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
12
modules/home-manager/newsflash.nix
Normal file
12
modules/home-manager/newsflash.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# news-flash RSS viewer
|
||||||
|
{ config, sane-lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
feeds = sane-lib.feeds;
|
||||||
|
all-feeds = config.sane.feeds;
|
||||||
|
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||||
|
in {
|
||||||
|
sane.fs."/home/colin/.config/newsflashFeeds.opml" = sane-lib.fs.wantedText (
|
||||||
|
feeds.feedsToOpml wanted-feeds
|
||||||
|
);
|
||||||
|
}
|
19
modules/home-manager/splatmoji.nix
Normal file
19
modules/home-manager/splatmoji.nix
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# borrows from:
|
||||||
|
# - default config: <https://github.com/cspeterson/splatmoji/blob/master/splatmoji.config>
|
||||||
|
# - wayland: <https://github.com/cspeterson/splatmoji/issues/32#issuecomment-830862566>
|
||||||
|
{ pkgs, sane-lib, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
sane.persist.home.plaintext = [ ".local/state/splatmoji" ];
|
||||||
|
sane.fs."/home/colin/.config/splatmoji/splatmoji.config" = sane-lib.fs.wantedText ''
|
||||||
|
history_file=/home/colin/.local/state/splatmoji/history
|
||||||
|
history_length=5
|
||||||
|
# TODO: wayland equiv
|
||||||
|
paste_command=xdotool key ctrl+v
|
||||||
|
# rofi_command=${pkgs.wofi}/bin/wofi --dmenu --insensitive --cache-file /dev/null
|
||||||
|
rofi_command=${pkgs.fuzzel}/bin/fuzzel -d -i -w 60
|
||||||
|
xdotool_command=${pkgs.wtype}/bin/wtype
|
||||||
|
# TODO: wayland equiv
|
||||||
|
xsel_command=xsel -b -i
|
||||||
|
'';
|
||||||
|
}
|
23
modules/home-manager/ssh.nix
Normal file
23
modules/home-manager/ssh.nix
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{ config, lib, pkgs, sane-lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
host = config.networking.hostName;
|
||||||
|
user-pubkey = config.sane.ssh.pubkeys."colin@${host}".asUserKey;
|
||||||
|
host-keys = filter (k: k.user == "root") (attrValues config.sane.ssh.pubkeys);
|
||||||
|
known-hosts-text = concatStringsSep
|
||||||
|
"\n"
|
||||||
|
(map (k: k.asHostKey) host-keys)
|
||||||
|
;
|
||||||
|
in lib.mkIf config.sane.home-manager.enable {
|
||||||
|
# ssh key is stored in private storage
|
||||||
|
sane.persist.home.private = [ ".ssh/id_ed25519" ];
|
||||||
|
sane.fs."/home/colin/.ssh/id_ed25519.pub" = sane-lib.fs.wantedText user-pubkey;
|
||||||
|
sane.fs."/home/colin/.ssh/known_hosts" = sane-lib.fs.wantedText known-hosts-text;
|
||||||
|
|
||||||
|
users.users.colin.openssh.authorizedKeys.keys =
|
||||||
|
let
|
||||||
|
user-keys = filter (k: k.user == "colin") (attrValues config.sane.ssh.pubkeys);
|
||||||
|
in
|
||||||
|
map (k: k.asUserKey) user-keys;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user