Compare commits
531 Commits
testing/mu
...
wip/feeds
Author | SHA1 | Date | |
---|---|---|---|
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
|
|
90
flake.lock
generated
90
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": {
|
||||||
@@ -69,11 +54,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665732960,
|
"lastModified": 1672953546,
|
||||||
"narHash": "sha256-WBZ+uSHKFyjvd0w4inbm0cNExYTn8lpYFcHEes8tmec=",
|
"narHash": "sha256-oz757DnJ1ITvwyTovuwG3l9cX6j9j6/DH9eH+cXFJmc=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "4428e23312933a196724da2df7ab78eb5e67a88e",
|
"rev": "a518c77148585023ff56022f09c4b2c418a51ef5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -82,41 +67,40 @@
|
|||||||
"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": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665613119,
|
"lastModified": 1673163619,
|
||||||
"narHash": "sha256-VTutbv5YKeBGWou6ladtgfx11h6et+Wlkdyh4jPJ3p0=",
|
"narHash": "sha256-B33PFBL64ZgTWgMnhFL3jgheAN/DjHPsZ1Ih3z0VE5I=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e06bd4b64bbfda91d74f13cb5eca89485d47528f",
|
"rev": "8c54d842d9544361aac5f5b212ba04e4089e8efe",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
"ref": "nixos-22.05",
|
"ref": "nixos-22.11",
|
||||||
"type": "indirect"
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
"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",
|
||||||
@@ -129,14 +113,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": {
|
||||||
@@ -153,11 +137,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"
|
||||||
},
|
},
|
||||||
|
83
flake.nix
83
flake.nix
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs-stable.url = "nixpkgs/nixos-22.05";
|
nixpkgs-stable.url = "nixpkgs/nixos-22.11";
|
||||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||||
mobile-nixos = {
|
mobile-nixos = {
|
||||||
url = "github:nixos/mobile-nixos";
|
url = "github:nixos/mobile-nixos";
|
||||||
@@ -18,41 +18,46 @@
|
|||||||
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 = {
|
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,
|
||||||
|
nixpkgs,
|
||||||
|
nixpkgs-stable,
|
||||||
|
mobile-nixos,
|
||||||
|
home-manager,
|
||||||
|
sops-nix,
|
||||||
|
uninsane
|
||||||
|
}: let
|
||||||
patchedPkgs = system: nixpkgs.legacyPackages.${system}.applyPatches {
|
patchedPkgs = system: nixpkgs.legacyPackages.${system}.applyPatches {
|
||||||
name = "nixpkgs-patched-uninsane";
|
name = "nixpkgs-patched-uninsane";
|
||||||
src = nixpkgs;
|
src = nixpkgs;
|
||||||
patches = import ./nixpatches/list.nix nixpkgs.legacyPackages.${system}.fetchpatch;
|
patches = import ./nixpatches/list.nix {
|
||||||
|
inherit (nixpkgs.legacyPackages.${system}) fetchpatch;
|
||||||
|
inherit (nixpkgs.lib) fakeHash;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
# return something which behaves like `pkgs`, for the provided system
|
# return something which behaves like `pkgs`, for the provided system
|
||||||
# `local` = architecture of builder. `target` = architecture of the system beying deployed to
|
# `local` = architecture of builder. `target` = architecture of the system beying deployed to
|
||||||
nixpkgsFor = local: target: import (patchedPkgs target) { crossSystem = target; localSystem = local; };
|
nixpkgsFor = local: target: import (patchedPkgs target) { crossSystem = target; localSystem = local; };
|
||||||
# evaluate ONLY our overlay, for the provided system
|
# evaluate ONLY our overlay, for the provided system
|
||||||
customPackagesFor = local: target: import ./pkgs/overlay.nix (nixpkgsFor local target) (nixpkgsFor local target);
|
customPackagesFor = local: target: import ./pkgs/overlay.nix (nixpkgsFor local target) (nixpkgsFor local target);
|
||||||
decl-machine = { name, local, target }:
|
decl-host = { name, local, target }:
|
||||||
let
|
let
|
||||||
nixosSystem = import ((patchedPkgs target) + "/nixos/lib/eval-config.nix");
|
nixosSystem = import ((patchedPkgs target) + "/nixos/lib/eval-config.nix");
|
||||||
in (nixosSystem {
|
in (nixosSystem {
|
||||||
# by default the local system is the same as the target, employing emulation when they differ
|
# by default the local system is the same as the target, employing emulation when they differ
|
||||||
system = target;
|
system = target;
|
||||||
specialArgs = { inherit mobile-nixos home-manager impermanence; };
|
|
||||||
modules = [
|
modules = [
|
||||||
./modules
|
./modules
|
||||||
./machines/${name}
|
(import ./hosts/instantiate.nix name)
|
||||||
(import ./helpers/set-hostname.nix name)
|
|
||||||
home-manager.nixosModule
|
home-manager.nixosModule
|
||||||
impermanence.nixosModule
|
|
||||||
sops-nix.nixosModules.sops
|
sops-nix.nixosModules.sops
|
||||||
{
|
{
|
||||||
nixpkgs.config.allowUnfree = true;
|
|
||||||
nixpkgs.overlays = [
|
nixpkgs.overlays = [
|
||||||
(import "${mobile-nixos}/overlay/overlay.nix")
|
(import "${mobile-nixos}/overlay/overlay.nix")
|
||||||
uninsane.overlay
|
uninsane.overlay
|
||||||
@@ -63,17 +68,19 @@
|
|||||||
# the config can explicitly pull such packages from `pkgs.cross` to do more efficient cross-compilation.
|
# the config can explicitly pull such packages from `pkgs.cross` to do more efficient cross-compilation.
|
||||||
cross = (nixpkgsFor local target) // (customPackagesFor local target);
|
cross = (nixpkgsFor local target) // (customPackagesFor local target);
|
||||||
stable = import nixpkgs-stable { system = target; };
|
stable = import nixpkgs-stable { system = target; };
|
||||||
|
|
||||||
|
# cross-compatible packages
|
||||||
|
# gocryptfs = cross.gocryptfs;
|
||||||
|
|
||||||
# pinned packages:
|
# 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 {
|
decl-bootable-host = { name, local, target }: rec {
|
||||||
nixosConfiguration = decl-machine { inherit name local target; };
|
nixosConfiguration = decl-host { inherit name local target; };
|
||||||
# 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,35 +93,39 @@
|
|||||||
# - 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;
|
img = nixosConfiguration.config.system.build.img;
|
||||||
};
|
};
|
||||||
machines.servo = decl-bootable-machine { name = "servo"; local = "aarch64-linux"; target = "aarch64-linux"; };
|
hosts.servo = decl-bootable-host { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||||
machines.desko = decl-bootable-machine { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
hosts.desko = decl-bootable-host { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||||
machines.lappy = decl-bootable-machine { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
hosts.lappy = decl-bootable-host { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||||
machines.moby = decl-bootable-machine { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
|
hosts.moby = decl-bootable-host { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
|
||||||
# special cross-compiled variant, to speed up deploys from an x86 box to the arm target
|
# 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
|
# note that these *do* produce different store paths, because the closure for the tools used to cross compile
|
||||||
# v.s. emulate differ.
|
# v.s. emulate differ.
|
||||||
# so deploying moby-cross and then moby incurs some rebuilding.
|
# so deploying foo-cross and then foo incurs some rebuilding.
|
||||||
machines.moby-cross = decl-bootable-machine { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
hosts.moby-cross = decl-bootable-host { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
||||||
machines.rescue = decl-bootable-machine { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
hosts.rescue = decl-bootable-host { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||||
in {
|
in {
|
||||||
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) machines;
|
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) hosts;
|
||||||
imgs = builtins.mapAttrs (name: value: value.img) machines;
|
imgs = builtins.mapAttrs (name: value: value.img) hosts;
|
||||||
packages = let
|
packages = let
|
||||||
custom-x86_64 = customPackagesFor "x86_64-linux" "x86_64-linux";
|
allPkgsFor = sys: (customPackagesFor sys sys) // {
|
||||||
custom-aarch64 = customPackagesFor "aarch64-linux" "aarch64-linux";
|
nixpkgs = nixpkgsFor sys sys;
|
||||||
nixpkgs-x86_64 = nixpkgsFor "x86_64-linux" "x86_64-linux";
|
uninsane = uninsane.packages."${sys}";
|
||||||
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 // {
|
in {
|
||||||
nixpkgs = nixpkgs-aarch64;
|
x86_64-linux = allPkgsFor "x86_64-linux";
|
||||||
uninsane = uninsane.packages.aarch64-linux;
|
aarch64-linux = allPkgsFor "aarch64-linux";
|
||||||
|
};
|
||||||
|
templates = {
|
||||||
|
python-data = {
|
||||||
|
# initialize with:
|
||||||
|
# - `nix flake init -t '/home/colin/dev/nixos/#python-data'`
|
||||||
|
# then enter with:
|
||||||
|
# - `nix develop`
|
||||||
|
path = ./templates/python-data;
|
||||||
|
description = "python environment for data processing";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -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" ];
|
||||||
|
};
|
||||||
|
}
|
76
hosts/common/default.nix
Normal file
76
hosts/common/default.nix
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./bluetooth.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" = {
|
10
hosts/instantiate.nix
Normal file
10
hosts/instantiate.nix
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
|
||||||
|
|
||||||
|
hostName: { ... }: {
|
||||||
|
imports = [
|
||||||
|
./${hostName}
|
||||||
|
./common
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.hostName = hostName;
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
58
modules/data/feeds/default.nix
Normal file
58
modules/data/feeds/default.nix
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (builtins) concatLists concatStringsSep foldl' fromJSON map readDir readFile;
|
||||||
|
inherit (lib) init mapAttrsToList removePrefix removeSuffix splitString;
|
||||||
|
inherit (lib.attrsets) recursiveUpdate setAttrByPath;
|
||||||
|
inherit (lib.filesystem) listFilesRecursive;
|
||||||
|
|
||||||
|
# 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 -> { path = [String]; value = feed; }
|
||||||
|
feedFromSourcePath = json-path:
|
||||||
|
let
|
||||||
|
canonical-name = removeSuffix "/default" (lib.removeSuffix ".json" json-path);
|
||||||
|
default-url = "https://${canonical-name}";
|
||||||
|
attr-path = splitString "/" canonical-name;
|
||||||
|
feed-details = { url = default-url; } // (tryImportJson (./sources/${json-path}));
|
||||||
|
in { path = attr-path; 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)
|
||||||
|
);
|
||||||
|
|
||||||
|
# like listToAttrs, except takes { path, value } pairs instead of { name, value } pairs.
|
||||||
|
# Type: listToAttrsByPath :: [{ path = [String]; value = Any; }] -> Attrs
|
||||||
|
listToAttrsByPath = items:
|
||||||
|
foldl' (acc: { path, value }: recursiveUpdate acc (setAttrByPath path value)) {} items;
|
||||||
|
in
|
||||||
|
listToAttrsByPath (map feedFromSourcePath sources)
|
0
modules/data/feeds/sources/xkcd.com/default.json
Normal file
0
modules/data/feeds/sources/xkcd.com/default.json
Normal file
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;
|
||||||
|
}
|
12
modules/home-manager/sublime-music.nix
Normal file
12
modules/home-manager/sublime-music.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{ config, lib, sane-lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
# TODO: this should only be shipped on gui platforms
|
||||||
|
sops.secrets."sublime_music_config" = {
|
||||||
|
owner = config.users.users.colin.name;
|
||||||
|
sopsFile = ../../secrets/universal/sublime_music_config.json.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sane.fs."/home/colin/.config/sublime-music/config.json" = sane-lib.fs.wantedSymlinkTo config.sops.secrets.sublime_music_config.path;
|
||||||
|
}
|
21
modules/home-manager/vlc.nix
Normal file
21
modules/home-manager/vlc.nix
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{ config, lib, sane-lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
feeds = sane-lib.feeds;
|
||||||
|
all-feeds = config.sane.feeds;
|
||||||
|
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
|
||||||
|
podcast-urls = lib.concatStringsSep "|" (
|
||||||
|
builtins.map (feed: feed.url) wanted-feeds
|
||||||
|
);
|
||||||
|
in
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
sane.fs."/home/colin/.config/vlc/vlcrc" = sane-lib.fs.wantedText ''
|
||||||
|
[podcast]
|
||||||
|
podcast-urls=${podcast-urls}
|
||||||
|
[core]
|
||||||
|
metadata-network-access=0
|
||||||
|
[qt]
|
||||||
|
qt-privacy-ask=0
|
||||||
|
'';
|
||||||
|
}
|
108
modules/home-manager/zsh/default.nix
Normal file
108
modules/home-manager/zsh/default.nix
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf config.sane.home-manager.enable
|
||||||
|
{
|
||||||
|
sane.persist.home.plaintext = [
|
||||||
|
# we don't need to full zsh dir -- just the history file --
|
||||||
|
# but zsh will sometimes backup the history file and we get fewer errors if we do proper mounts instead of symlinks.
|
||||||
|
# TODO: should be private?
|
||||||
|
".local/share/zsh"
|
||||||
|
# cache gitstatus otherwise p10k fetched it from the net EVERY BOOT
|
||||||
|
".cache/gitstatus"
|
||||||
|
];
|
||||||
|
|
||||||
|
home-manager.users.colin.programs.zsh = {
|
||||||
|
enable = true;
|
||||||
|
enableSyntaxHighlighting = true;
|
||||||
|
enableVteIntegration = true;
|
||||||
|
history.ignorePatterns = [ "rm *" ];
|
||||||
|
dotDir = ".config/zsh";
|
||||||
|
history.path = "/home/colin/.local/share/zsh/history";
|
||||||
|
|
||||||
|
# defaultKeymap = "vicmd"; # vim normal mode (cmd mode)
|
||||||
|
|
||||||
|
# powerlevel10k prompt config
|
||||||
|
# p10k.zsh is the auto-generated config, and i overwrite those defaults here, below.
|
||||||
|
initExtraBeforeCompInit = (builtins.readFile ./p10k.zsh) + ''
|
||||||
|
# powerlevel10k launches a gitstatusd daemon to accelerate git prompt queries.
|
||||||
|
# this keeps open file handles for any git repo i touch for 60 minutes (by default).
|
||||||
|
# that prevents unmounting whatever device the git repo is on -- particularly problematic for ~/private.
|
||||||
|
# i can disable gitstatusd and get slower fallback git queries:
|
||||||
|
# - either universally
|
||||||
|
# - or selectively by path
|
||||||
|
# see: <https://github.com/romkatv/powerlevel10k/issues/246>
|
||||||
|
typeset -g POWERLEVEL9K_VCS_DISABLED_DIR_PATTERN='(/home/colin/private/*|/home/colin/knowledge/*)'
|
||||||
|
# typeset -g POWERLEVEL9K_DISABLE_GITSTATUS=true
|
||||||
|
|
||||||
|
# show user@host also when logged into the current machine.
|
||||||
|
# default behavior is to show it only over ssh.
|
||||||
|
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION='$P9K_CONTENT'
|
||||||
|
'';
|
||||||
|
|
||||||
|
initExtra = ''
|
||||||
|
# zmv is a way to do rich moves/renames, with pattern matching/substitution.
|
||||||
|
# see for an example: <https://filipe.kiss.ink/zmv-zsh-rename/>
|
||||||
|
autoload -Uz zmv
|
||||||
|
|
||||||
|
# disable `rm *` confirmations
|
||||||
|
setopt rmstarsilent
|
||||||
|
|
||||||
|
function nd() {
|
||||||
|
mkdir -p "$1";
|
||||||
|
pushd "$1";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
|
||||||
|
# see: https://github.com/sorin-ionescu/prezto
|
||||||
|
prezto = {
|
||||||
|
enable = true;
|
||||||
|
pmodules = [
|
||||||
|
# configures jobs to persist after shell exit; other basic niceties
|
||||||
|
"environment"
|
||||||
|
# auto-titles terminal (e.g. based on cwd)
|
||||||
|
"terminal"
|
||||||
|
# configures shortcuts like Ctrl+U=undo, Ctrl+L=clear
|
||||||
|
"editor"
|
||||||
|
# adds `history-stat` alias, setopts for good history defaults
|
||||||
|
"history"
|
||||||
|
# sets AUTO_CD, adds `d` alias to list directory stack, and `1`-`9` to cd that far back the stack
|
||||||
|
"directory"
|
||||||
|
# helpers for term colors and styling. used by prompts? might be unnecessary
|
||||||
|
"spectrum"
|
||||||
|
# configures aliases like `ll`, `la`, disables globbing for things like rsync
|
||||||
|
# adds aliases like `get` to fetch a file. also adds `http-serve` alias??
|
||||||
|
"utility"
|
||||||
|
# tab completion. requires `utility` module prior to loading
|
||||||
|
# TODO: enable AUTO_PARAM_SLASH
|
||||||
|
"completion"
|
||||||
|
"prompt"
|
||||||
|
# TODO: enable syntax-highlighting ?
|
||||||
|
];
|
||||||
|
prompt.theme = "powerlevel10k";
|
||||||
|
utility.safeOps = false; # disable `mv` confirmation (and supposedly `rm`, too)
|
||||||
|
# editor.keymap = "vi";
|
||||||
|
};
|
||||||
|
|
||||||
|
dirHashes = {
|
||||||
|
# convenient `cd`-isms
|
||||||
|
"3rd" = "/home/colin/dev/3rd";
|
||||||
|
"dev" = "/home/colin/dev";
|
||||||
|
"knowledge" = "/home/colin/knowledge";
|
||||||
|
"nixos" = "/home/colin/nixos";
|
||||||
|
"nixpkgs" = "/home/colin/dev/3rd/nixpkgs";
|
||||||
|
"ref" = "/home/colin/ref";
|
||||||
|
"secrets" = "/home/colin/knowledge/secrets";
|
||||||
|
"tmp" = "/home/colin/tmp";
|
||||||
|
"uninsane" = "/home/colin/dev/uninsane";
|
||||||
|
"Videos" = "/home/colin/Videos";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
home-manager.users.colin.home.shellAliases = {
|
||||||
|
":q" = "exit";
|
||||||
|
# common typos
|
||||||
|
"cd.." = "cd ..";
|
||||||
|
"cd../" = "cd ../";
|
||||||
|
};
|
||||||
|
}
|
1635
modules/home-manager/zsh/p10k.zsh
Normal file
1635
modules/home-manager/zsh/p10k.zsh
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user