Compare commits
202 Commits
ryzen-serv
...
wip/imperm
Author | SHA1 | Date | |
---|---|---|---|
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 |
60
flake.lock
generated
60
flake.lock
generated
@@ -54,11 +54,11 @@
|
|||||||
"mobile-nixos": {
|
"mobile-nixos": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1668897543,
|
"lastModified": 1670131242,
|
||||||
"narHash": "sha256-1bjvy5zi/6KDzhN3ihOUEA6y5FFEOf5xvIbf65RWIh0=",
|
"narHash": "sha256-T/o1/3gffr010fsqgNshs1NJJjsnUYvQnUZgm6hilsY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "mobile-nixos",
|
"repo": "mobile-nixos",
|
||||||
"rev": "25eec596116553112681d72ee4880107fc3957fa",
|
"rev": "5ee45cc1f8e43f4af14ee17ccef9156b0db8cd77",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -69,11 +69,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1668994630,
|
"lastModified": 1671722432,
|
||||||
"narHash": "sha256-1lqx6HLyw6fMNX/hXrrETG1vMvZRGm2XVC9O/Jt0T6c=",
|
"narHash": "sha256-ojcZUekIQeOZkHHzR81st7qxX99dB1Eaaq6PU5MNeKc=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "af50806f7c6ab40df3e6b239099e8f8385f6c78b",
|
"rev": "652e92b8064949a11bc193b90b74cb727f2a1405",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -82,37 +82,37 @@
|
|||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-22_05": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1668908668,
|
|
||||||
"narHash": "sha256-oimCE4rY7Btuo/VYmA8khIyTHSMV7qUWTpz9w8yc9LQ=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "b68a6a27adb452879ab66c0eaac0c133e32823b2",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "release-22.05",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1668984258,
|
"lastModified": 1671883564,
|
||||||
"narHash": "sha256-0gDMJ2T3qf58xgcSbYoXiRGUkPWmKyr5C3vcathWhKs=",
|
"narHash": "sha256-C15oAtyupmLB3coZY7qzEHXjhtUx/+77olVdqVMruAg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "cf63ade6f74bbc9d2a017290f1b2e33e8fbfa70a",
|
"rev": "dac57a4eccf1442e8bf4030df6fcbb55883cb682",
|
||||||
"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": 1671923641,
|
||||||
|
"narHash": "sha256-flPauiL5UrfRJD+1oAcEefpEIUqTqnyKScWe/UUU+lE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "939c05a176b8485971463c18c44f48e56a7801c9",
|
||||||
|
"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",
|
||||||
@@ -129,14 +129,14 @@
|
|||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"nixpkgs-22_05": "nixpkgs-22_05"
|
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1668915833,
|
"lastModified": 1671937829,
|
||||||
"narHash": "sha256-7VYPiDJZdGct8Nl3kKhg580XZfoRcViO+zUGPkfBsqM=",
|
"narHash": "sha256-YtaNB+mLw0d67JFYNjRWM+/AL3JCXuD/DGlnTlyX1tY=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "f72e050c3ef148b1131a0d2df55385c045e4166b",
|
"rev": "855b8d51fc3991bd817978f0f093aa6ae0fae738",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
24
flake.nix
24
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";
|
||||||
@@ -38,7 +38,10 @@
|
|||||||
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
|
||||||
@@ -69,8 +72,15 @@
|
|||||||
# 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
|
# cross-compatible packages
|
||||||
# gocryptfs = cross.gocryptfs;
|
# gocryptfs = cross.gocryptfs;
|
||||||
|
|
||||||
|
# pinned packages:
|
||||||
|
# 2022/12/13: grpc does not build on aarch64-linux. https://github.com/NixOS/nixpkgs/issues/205887
|
||||||
|
grpc = stable.grpc;
|
||||||
|
# depends on grpc, so pinned.
|
||||||
|
duplicity = stable.duplicity;
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -116,6 +126,16 @@
|
|||||||
x86_64-linux = allPkgsFor "x86_64-linux";
|
x86_64-linux = allPkgsFor "x86_64-linux";
|
||||||
aarch64-linux = allPkgsFor "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";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
hosts/common/bluetooth.nix
Normal file
18
hosts/common/bluetooth.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# TODO: don't need to depend on binsh if we were to use a nix-style shebang
|
||||||
|
system.activationScripts.linkBluetoothKeys = let
|
||||||
|
unwrapped = ../../scripts/install-bluetooth;
|
||||||
|
install-bluetooth = pkgs.writeShellApplication {
|
||||||
|
name = "install-bluetooth";
|
||||||
|
runtimeInputs = with pkgs; [ coreutils gnused ];
|
||||||
|
text = ''${unwrapped} "$@"'';
|
||||||
|
};
|
||||||
|
in (lib.stringAfter
|
||||||
|
[ "setupSecrets" "binsh" ]
|
||||||
|
''
|
||||||
|
${install-bluetooth}/bin/install-bluetooth /run/secrets/bt
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
@@ -1,8 +1,10 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./bluetooth.nix
|
||||||
./fs.nix
|
./fs.nix
|
||||||
./hardware
|
./hardware
|
||||||
|
./i2p.nix
|
||||||
./machine-id.nix
|
./machine-id.nix
|
||||||
./net.nix
|
./net.nix
|
||||||
./secrets.nix
|
./secrets.nix
|
||||||
@@ -16,6 +18,16 @@
|
|||||||
sane.packages.enableConsolePkgs = true;
|
sane.packages.enableConsolePkgs = true;
|
||||||
sane.packages.enableSystemPkgs = true;
|
sane.packages.enableSystemPkgs = true;
|
||||||
|
|
||||||
|
sane.impermanence.dirs = [
|
||||||
|
"/var/log"
|
||||||
|
"/var/backup" # for e.g. postgres dumps
|
||||||
|
# TODO: move elsewhere
|
||||||
|
"/var/lib/alsa" # preserve output levels, default devices
|
||||||
|
"/var/lib/bluetooth" # preserve bluetooth handshakes
|
||||||
|
"/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;
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|
||||||
# time.timeZone = "America/Los_Angeles";
|
# time.timeZone = "America/Los_Angeles";
|
||||||
@@ -56,19 +68,8 @@
|
|||||||
};
|
};
|
||||||
# enable zsh completions
|
# enable zsh completions
|
||||||
environment.pathsToLink = [ "/share/zsh" ];
|
environment.pathsToLink = [ "/share/zsh" ];
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
# required for pam_mount
|
|
||||||
gocryptfs
|
|
||||||
];
|
|
||||||
|
|
||||||
# link debug symbols into /run/current-system/sw/lib/debug
|
# link debug symbols into /run/current-system/sw/lib/debug
|
||||||
# hopefully picked up by gdb automatically?
|
# hopefully picked up by gdb automatically?
|
||||||
environment.enableDebugInfo = true;
|
environment.enableDebugInfo = true;
|
||||||
|
|
||||||
security.pam.mount.enable = true;
|
|
||||||
# security.pam.mount.debugLevel = 1;
|
|
||||||
# security.pam.enableSSHAgentAuth = true; # ??
|
|
||||||
# needed for `allow_other` in e.g. gocryptfs mounts
|
|
||||||
# or i guess going through mount.fuse sets suid so that's not necessary?
|
|
||||||
# programs.fuse.userAllowOther = true;
|
|
||||||
}
|
}
|
||||||
|
4
hosts/common/i2p.nix
Normal file
4
hosts/common/i2p.nix
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
services.i2p.enable = true;
|
||||||
|
}
|
@@ -1,11 +1,16 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
# we wan't an /etc/machine-id which is consistent across boot so that `journalctl` will actually show us
|
# /etc/machine-id is a globally unique identifier used for:
|
||||||
# logs from previous boots.
|
# - systemd-networkd: DHCP lease renewal (instead of keying by the MAC address)
|
||||||
# maybe there's a config option for this (since persistent machine-id is bad for reasons listed in impermanence.nix),
|
# - systemd-journald: to filter logs by host
|
||||||
# but for now generate it from ssh keys.
|
# - 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 = {
|
system.activationScripts.machine-id = {
|
||||||
deps = [ "persist-ssh-host-keys" ];
|
deps = [ "etc" ];
|
||||||
text = "sha256sum /etc/ssh/host_keys/ssh_host_ed25519_key | cut -c 1-32 > /etc/machine-id";
|
text = "sha256sum /etc/ssh/host_keys/ssh_host_ed25519_key | cut -c 1-32 > /etc/machine-id";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -46,34 +46,4 @@
|
|||||||
${install-iwd}/bin/install-iwd /run/secrets/iwd /var/lib/iwd
|
${install-iwd}/bin/install-iwd /run/secrets/iwd /var/lib/iwd
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
# TODO: use a glob, or a list, or something?
|
|
||||||
sops.secrets."iwd/community-university.psk" = {
|
|
||||||
sopsFile = ../../secrets/universal/net/community-university.psk.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sops.secrets."iwd/friend-libertarian-dod.psk" = {
|
|
||||||
sopsFile = ../../secrets/universal/net/friend-libertarian-dod.psk.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sops.secrets."iwd/friend-rationalist-empathist.psk" = {
|
|
||||||
sopsFile = ../../secrets/universal/net/friend-rationalist-empathist.psk.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sops.secrets."iwd/home-bedroom.psk" = {
|
|
||||||
sopsFile = ../../secrets/universal/net/home-bedroom.psk.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sops.secrets."iwd/home-shared-24G.psk" = {
|
|
||||||
sopsFile = ../../secrets/universal/net/home-shared-24G.psk.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sops.secrets."iwd/home-shared.psk" = {
|
|
||||||
sopsFile = ../../secrets/universal/net/home-shared.psk.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sops.secrets."iwd/iphone" = {
|
|
||||||
sopsFile = ../../secrets/universal/net/iphone.psk.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@@ -33,10 +33,6 @@
|
|||||||
# You can avoid this by adding a string to the full path instead, i.e.
|
# You can avoid this by adding a string to the full path instead, i.e.
|
||||||
# sops.defaultSopsFile = "/root/.sops/secrets/example.yaml";
|
# sops.defaultSopsFile = "/root/.sops/secrets/example.yaml";
|
||||||
sops.defaultSopsFile = ../../secrets/universal.yaml;
|
sops.defaultSopsFile = ../../secrets/universal.yaml;
|
||||||
# This will automatically import SSH keys as age keys
|
|
||||||
sops.age.sshKeyPaths = [
|
|
||||||
"/etc/ssh/host_keys/ssh_host_ed25519_key"
|
|
||||||
];
|
|
||||||
sops.gnupg.sshKeyPaths = []; # disable RSA key import
|
sops.gnupg.sshKeyPaths = []; # disable RSA key import
|
||||||
# This is using an age key that is expected to already be in the filesystem
|
# 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 = "/home/colin/.ssh/age.pub";
|
||||||
@@ -48,6 +44,81 @@
|
|||||||
# owner = config.users.users.colin.name;
|
# owner = config.users.users.colin.name;
|
||||||
# };
|
# };
|
||||||
# sops.secrets."myservice/my_subdir/my_secret" = {};
|
# sops.secrets."myservice/my_subdir/my_secret" = {};
|
||||||
|
|
||||||
|
## universal secrets
|
||||||
|
# TODO: glob these?
|
||||||
|
|
||||||
|
sops.secrets."jackett_apikey" = {
|
||||||
|
sopsFile = ../../secrets/universal.yaml;
|
||||||
|
owner = config.users.users.colin.name;
|
||||||
|
};
|
||||||
|
sops.secrets."router_passwd" = {
|
||||||
|
sopsFile = ../../secrets/universal.yaml;
|
||||||
|
};
|
||||||
|
sops.secrets."wg_ovpnd_us_privkey" = {
|
||||||
|
sopsFile = ../../secrets/universal.yaml;
|
||||||
|
};
|
||||||
|
sops.secrets."wg_ovpnd_us-atl_privkey" = {
|
||||||
|
sopsFile = ../../secrets/universal.yaml;
|
||||||
|
};
|
||||||
|
sops.secrets."wg_ovpnd_us-mi_privkey" = {
|
||||||
|
sopsFile = ../../secrets/universal.yaml;
|
||||||
|
};
|
||||||
|
sops.secrets."wg_ovpnd_ukr_privkey" = {
|
||||||
|
sopsFile = ../../secrets/universal.yaml;
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets."snippets" = {
|
||||||
|
sopsFile = ../../secrets/universal/snippets.bin;
|
||||||
|
format = "binary";
|
||||||
|
owner = config.users.users.colin.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets."bt/car" = {
|
||||||
|
sopsFile = ../../secrets/universal/bt/car.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."bt/earbuds" = {
|
||||||
|
sopsFile = ../../secrets/universal/bt/earbuds.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."bt/portable-speaker" = {
|
||||||
|
sopsFile = ../../secrets/universal/bt/portable-speaker.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets."iwd/community-university.psk" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/community-university.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."iwd/friend-libertarian-dod.psk" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/friend-libertarian-dod.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."iwd/friend-rationalist-empathist.psk" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/friend-rationalist-empathist.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."iwd/home-bedroom.psk" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/home-bedroom.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."iwd/home-shared-24G.psk" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/home-shared-24G.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."iwd/home-shared.psk" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/home-shared.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."iwd/iphone" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/iphone.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
|
sops.secrets."iwd/parents" = {
|
||||||
|
sopsFile = ../../secrets/universal/net/parents.psk.bin;
|
||||||
|
format = "binary";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,21 +1,10 @@
|
|||||||
{ ... }:
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
# we place the host keys (which we want to be persisted) into their own directory so that we can
|
environment.etc."ssh/host_keys".source = "/nix/persist/etc/ssh/host_keys";
|
||||||
# bind mount that whole directory instead of doing it per-file.
|
|
||||||
# otherwise, this is identical to nixos defaults
|
|
||||||
sane.impermanence.service-dirs = [ "/etc/ssh/host_keys" ];
|
|
||||||
|
|
||||||
# we can't naively `mount /etc/ssh/host_keys` directly,
|
|
||||||
# as /etc/fstab may not be populated yet (since that file depends on e.g. activationScripts.users)
|
|
||||||
# we can't even depend on impermanence's `createPersistentStorageDirs` to create the source/target directories
|
|
||||||
# since that also depends on `users`.
|
|
||||||
system.activationScripts.persist-ssh-host-keys.text = ''
|
|
||||||
mkdir -p /etc/ssh/host_keys
|
|
||||||
mount --bind /nix/persist/etc/ssh/host_keys /etc/ssh/host_keys
|
|
||||||
'';
|
|
||||||
|
|
||||||
services.openssh.hostKeys = [
|
services.openssh.hostKeys = [
|
||||||
{ type = "rsa"; bits = 4096; path = "/etc/ssh/host_keys/ssh_host_rsa_key"; }
|
{ type = "rsa"; bits = 4096; path = "/etc/ssh/host_keys/ssh_host_rsa_key"; }
|
||||||
{ type = "ed25519"; path = "/etc/ssh/host_keys/ssh_host_ed25519_key"; }
|
{ type = "ed25519"; path = "/etc/ssh/host_keys/ssh_host_ed25519_key"; }
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,8 @@ in
|
|||||||
# sets group to "users" (?)
|
# sets group to "users" (?)
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
home = "/home/colin";
|
home = "/home/colin";
|
||||||
|
createHome = true;
|
||||||
|
homeMode = "700";
|
||||||
uid = config.sane.allocations.colin-uid;
|
uid = config.sane.allocations.colin-uid;
|
||||||
# i don't get exactly what this is, but nixos defaults to this non-deterministically
|
# 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.
|
# in /var/lib/nixos/auto-subuid-map and i don't want that.
|
||||||
@@ -52,28 +54,45 @@ in
|
|||||||
shell = pkgs.zsh;
|
shell = pkgs.zsh;
|
||||||
openssh.authorizedKeys.keys = builtins.attrValues (import ../../modules/pubkeys.nix).users;
|
openssh.authorizedKeys.keys = builtins.attrValues (import ../../modules/pubkeys.nix).users;
|
||||||
|
|
||||||
|
# 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 = {
|
pamMount = {
|
||||||
# mount encrypted stuff at login
|
# mount encrypted stuff at login
|
||||||
# requires that login password == fs encryption password
|
# requires that login password == fs encryption password
|
||||||
# fstype = "fuse";
|
fstype = "fuse";
|
||||||
|
path = "gocryptfs#/nix/persist/home/colin/private";
|
||||||
# path = "${pkgs.gocryptfs}/bin/gocryptfs#/nix/persist/home/colin/private";
|
# path = "${pkgs.gocryptfs}/bin/gocryptfs#/nix/persist/home/colin/private";
|
||||||
fstype = "fuse.gocryptfs";
|
# fstype = "fuse.gocryptfs";
|
||||||
path = "/nix/persist/home/colin/private";
|
# path = "/nix/persist/home/colin/private";
|
||||||
mountpoint = "/home/colin/private";
|
mountpoint = "/home/colin/private";
|
||||||
options="nodev,nosuid,quiet,allow_other";
|
# without allow_other, *root* isn't allowed to list anything in ~/private.
|
||||||
|
# which is weird (root can just `su colin`), but probably doesn't *hurt* anything -- right?
|
||||||
|
options="nodev,nosuid,quiet"; # allow_other
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# required for PAM to find gocryptfs
|
||||||
|
security.pam.mount.additionalSearchPaths = [ pkgs.gocryptfs ];
|
||||||
|
security.pam.mount.enable = true;
|
||||||
|
# security.pam.mount.debugLevel = 1;
|
||||||
|
# security.pam.enableSSHAgentAuth = true; # ??
|
||||||
|
# needed for `allow_other` in e.g. gocryptfs mounts
|
||||||
|
# or i guess going through mount.fuse sets suid so that's not necessary?
|
||||||
|
# programs.fuse.userAllowOther = true;
|
||||||
|
|
||||||
sane.impermanence.home-dirs = [
|
sane.impermanence.home-dirs = [
|
||||||
# cache is probably too big to fit on the tmpfs
|
# cache is probably too big to fit on the tmpfs
|
||||||
# TODO: we could bind-mount it to something which gets cleared per boot, though.
|
# { directory = ".cache"; encryptedClearOnBoot = true; }
|
||||||
".cache"
|
{ directory = ".cache/mozilla"; encryptedClearOnBoot = true; }
|
||||||
".cargo"
|
".cargo"
|
||||||
".rustup"
|
".rustup"
|
||||||
|
# TODO: move this to ~/private!
|
||||||
".local/share/keyrings"
|
".local/share/keyrings"
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.impermanence.service-dirs = mkIf cfg.guest.enable [
|
sane.impermanence.dirs = mkIf cfg.guest.enable [
|
||||||
{ user = "guest"; group = "users"; directory = "/home/guest"; }
|
{ user = "guest"; group = "users"; directory = "/home/guest"; }
|
||||||
];
|
];
|
||||||
users.users.guest = mkIf cfg.guest.enable {
|
users.users.guest = mkIf cfg.guest.enable {
|
||||||
|
@@ -1,58 +1,66 @@
|
|||||||
{ config, ... }:
|
{ 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 = [
|
||||||
{
|
{
|
||||||
networking.wg-quick.interfaces.ovpnd-us = {
|
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 = [
|
address = [
|
||||||
"172.27.237.218/32"
|
"172.27.237.218/32"
|
||||||
"fd00:0000:1337:cafe:1111:1111:ab00:4c8f/128"
|
"fd00:0000:1337:cafe:1111:1111:ab00:4c8f/128"
|
||||||
];
|
];
|
||||||
dns = [
|
})
|
||||||
"46.227.67.134"
|
# NB: us-* share the same wg key and link-local addrs, but distinct public addresses
|
||||||
"192.165.9.158"
|
(def-ovpn "us-atl" {
|
||||||
|
endpoint = "vpn18.prd.atlanta.ovpn.com:9929";
|
||||||
|
publicKey = "Dpg/4v5s9u0YbrXukfrMpkA+XQqKIFpf8ZFgyw0IkE0=";
|
||||||
|
address = [
|
||||||
|
"172.21.182.178/32"
|
||||||
|
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
|
||||||
];
|
];
|
||||||
peers = [
|
})
|
||||||
{
|
(def-ovpn "us-mi" {
|
||||||
allowedIPs = [
|
endpoint = "vpn34.prd.miami.ovpn.com:9929";
|
||||||
"0.0.0.0/0"
|
publicKey = "VtJz2irbu8mdkIQvzlsYhU+k9d55or9mx4A2a14t0V0=";
|
||||||
"::/0"
|
address = [
|
||||||
|
"172.21.182.178/32"
|
||||||
|
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
|
||||||
];
|
];
|
||||||
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
|
})
|
||||||
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
|
(def-ovpn "ukr" {
|
||||||
}
|
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
|
||||||
];
|
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
|
||||||
privateKeyFile = config.sops.secrets.wg_ovpnd_us_privkey.path;
|
|
||||||
# to start: `systemctl start wg-quick-ovpnd-us`
|
|
||||||
autostart = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.wg-quick.interfaces.ovpnd-ukr = {
|
|
||||||
address = [
|
address = [
|
||||||
"172.18.180.159/32"
|
"172.18.180.159/32"
|
||||||
"fd00:0000:1337:cafe:1111:1111:ec5c:add3/128"
|
"fd00:0000:1337:cafe:1111:1111:ec5c:add3/128"
|
||||||
];
|
];
|
||||||
dns = [
|
})
|
||||||
"46.227.67.134"
|
]
|
||||||
"192.165.9.158"
|
|
||||||
];
|
|
||||||
peers = [
|
|
||||||
{
|
|
||||||
allowedIPs = [
|
|
||||||
"0.0.0.0/0"
|
|
||||||
"::/0"
|
|
||||||
];
|
|
||||||
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
|
|
||||||
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
privateKeyFile = config.sops.secrets.wg_ovpnd_ukr_privkey.path;
|
|
||||||
# to start: `systemctl start wg-quick-ovpnd-ukr`
|
|
||||||
autostart = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
sops.secrets."wg_ovpnd_us_privkey" = {
|
|
||||||
sopsFile = ../../secrets/universal.yaml;
|
|
||||||
};
|
|
||||||
sops.secrets."wg_ovpnd_ukr_privkey" = {
|
|
||||||
sopsFile = ../../secrets/universal.yaml;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@@ -25,6 +25,9 @@
|
|||||||
neededForUsers = true;
|
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:
|
||||||
# - hourly snapshots
|
# - hourly snapshots
|
||||||
|
@@ -1,16 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
sane.impermanence.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" = {
|
||||||
|
@@ -1,16 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
sane.impermanence.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";
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
# usability compromises
|
# usability compromises
|
||||||
sane.impermanence.home-dirs = [
|
sane.impermanence.home-dirs = [
|
||||||
config.sane.web-browser.dotDir
|
config.sane.web-browser.dotDir
|
||||||
|
".config/pulse" # persist pulseaudio volume
|
||||||
];
|
];
|
||||||
|
|
||||||
# sane.packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
|
# sane.packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
|
||||||
@@ -39,7 +40,9 @@
|
|||||||
|
|
||||||
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.impermanence.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";
|
||||||
|
@@ -14,9 +14,8 @@
|
|||||||
pkgs.freshrss
|
pkgs.freshrss
|
||||||
];
|
];
|
||||||
sane.impermanence.enable = true;
|
sane.impermanence.enable = true;
|
||||||
|
sane.services.dyn-dns.enable = true;
|
||||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||||
sane.services.nixserve.enable = true;
|
|
||||||
sane.services.nixserve.sopsFile = ../../secrets/servo.yaml;
|
|
||||||
|
|
||||||
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 ];
|
||||||
|
@@ -1,16 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
|
sane.impermanence.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
|
||||||
fileSystems."/tmp" = {
|
fileSystems."/tmp" = {
|
||||||
device = "none";
|
device = "none";
|
||||||
@@ -45,7 +36,7 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
# TODO: this is overly broad; only need media and share directories to be persisted
|
# TODO: this is overly broad; only need media and share directories to be persisted
|
||||||
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
|
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
|
||||||
];
|
];
|
||||||
|
@@ -13,83 +13,155 @@
|
|||||||
|
|
||||||
# networking.firewall.enable = false;
|
# networking.firewall.enable = false;
|
||||||
networking.firewall.enable = true;
|
networking.firewall.enable = true;
|
||||||
# TODO: split these into the submodules
|
|
||||||
networking.firewall.allowedTCPPorts = [
|
# this is needed to forward packets from the VPN to the host
|
||||||
25 # SMTP
|
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||||
80 # HTTP
|
|
||||||
143 # IMAP
|
# unless we add interface-specific settings for each VPN, we have to define nameservers globally.
|
||||||
443 # HTTPS
|
# networking.nameservers = [
|
||||||
465 # SMTPS
|
# "1.1.1.1"
|
||||||
587 # SMTPS/submission
|
# "9.9.9.9"
|
||||||
993 # IMAPS
|
# ];
|
||||||
4001 # IPFS
|
|
||||||
];
|
# use systemd's stub resolver.
|
||||||
networking.firewall.allowedUDPPorts = [
|
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
|
||||||
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
|
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
|
||||||
4001 # IPFS
|
# 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"
|
||||||
];
|
];
|
||||||
|
|
||||||
# we need to use externally-visible nameservers in order for VPNs to be able to resolve hosts.
|
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
|
||||||
networking.nameservers = [
|
# in a way that's unaware of my VPN routing, so routes are frequently poor against
|
||||||
"1.1.1.1"
|
# services which advertise different IPs based on geolocation.
|
||||||
"9.9.9.9"
|
# 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):
|
# OVPN CONFIG (https://www.ovpn.com):
|
||||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
# 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.enable = true;
|
||||||
networking.wireguard.interfaces.wg0 = {
|
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;
|
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||||
# wg is active only in this namespace.
|
# 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>
|
# 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
|
# 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";
|
interfaceNamespace = "ovpns";
|
||||||
preSetup = "${pkgs.iproute2}/bin/ip netns add ovpns || true";
|
|
||||||
postShutdown = "${pkgs.iproute2}/bin/ip netns delete ovpns";
|
|
||||||
ips = [
|
ips = [
|
||||||
"185.157.162.178/32"
|
"185.157.162.178/32"
|
||||||
];
|
];
|
||||||
peers = [
|
peers = [
|
||||||
{
|
{
|
||||||
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
||||||
endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
|
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" ];
|
allowedIPs = [ "0.0.0.0/0" ];
|
||||||
# nixOS says this is important for keeping NATs active
|
# nixOS says this is important for keeping NATs active
|
||||||
persistentKeepalive = 25;
|
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"
|
||||||
systemd.services.wg0veth = {
|
'';
|
||||||
description = "veth pair to allow communication between host and wg0 netns";
|
postShutdown = "" + ''
|
||||||
after = [ "wireguard-wg0.service" ];
|
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||||
wantedBy = [ "multi-user.target" ];
|
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||||
serviceConfig = {
|
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||||
Type = "oneshot";
|
# restore rules/routes
|
||||||
RemainAfterExit = true;
|
${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"
|
||||||
ExecStart = with pkgs; writeScript "wg0veth-start" ''
|
${ip} rule add from all lookup local pref 0
|
||||||
#!${bash}/bin/bash
|
${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
|
# create veth pair
|
||||||
${iproute2}/bin/ip link add ovpns-veth-a type veth peer name ovpns-veth-b
|
${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
|
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
|
||||||
${iproute2}/bin/ip link set ovpns-veth-a up
|
${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" ''
|
# mv veth-b into the ovpns namespace
|
||||||
#!${bash}/bin/bash
|
${ip} link set ovpns-veth-b netns ovpns
|
||||||
${iproute2}/bin/ip -n wg0 link del ovpns-veth-b
|
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
|
||||||
${iproute2}/bin/ip link del ovpns-veth-a
|
${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" = {
|
sops.secrets."wg_ovpns_privkey" = {
|
||||||
sopsFile = ../../secrets/servo.yaml;
|
sopsFile = ../../secrets/servo.yaml;
|
||||||
|
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,6 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./ddns-afraid.nix
|
||||||
./ddns-he.nix
|
./ddns-he.nix
|
||||||
./ejabberd.nix
|
./ejabberd.nix
|
||||||
./freshrss.nix
|
./freshrss.nix
|
||||||
@@ -9,13 +10,17 @@
|
|||||||
./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
|
./prosody.nix
|
||||||
./transmission.nix
|
./transmission.nix
|
||||||
|
./trust-dns.nix
|
||||||
|
./wikipedia.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -1,48 +1,393 @@
|
|||||||
# docs:
|
# docs:
|
||||||
# - <https://docs.ejabberd.im/admin/configuration/basic>
|
# - <https://docs.ejabberd.im/admin/configuration/basic>
|
||||||
{ lib, ... }:
|
# 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 disabled: fails to start because of `mnesia_tm` dependency
|
# XXX: avatar support works in MUCs but not DMs
|
||||||
# lib.mkIf false
|
# lib.mkIf false
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
||||||
];
|
];
|
||||||
networking.firewall.allowedTCPPorts = [
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
3478 # STUN/TURN
|
||||||
5222 # XMPP client -> server
|
5222 # XMPP client -> server
|
||||||
|
5223 # XMPPS client -> server (XMPP over TLS)
|
||||||
5269 # XMPP server -> server
|
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
|
# provide access to certs
|
||||||
users.users.ejabberd.extraGroups = [ "nginx" ];
|
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 ?
|
# TODO: allocate UIDs/GIDs ?
|
||||||
services.ejabberd.enable = true;
|
services.ejabberd.enable = true;
|
||||||
services.ejabberd.configFile = builtins.toFile "ejabberd.yaml" ''
|
services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml";
|
||||||
|
systemd.services.ejabberd.preStart = let
|
||||||
|
config-in = pkgs.writeTextFile {
|
||||||
|
name = "ejabberd.yaml.in";
|
||||||
|
text = ''
|
||||||
hosts:
|
hosts:
|
||||||
- uninsane.org
|
- uninsane.org
|
||||||
|
|
||||||
# none | emergency | alert | critical | error | warning | notice | info | debug
|
# none | emergency | alert | critical | error | warning | notice | info | debug
|
||||||
loglevel: debug
|
loglevel: debug
|
||||||
|
# loglevel: info
|
||||||
|
# loglevel: notice
|
||||||
|
|
||||||
acme:
|
acme:
|
||||||
auto: false
|
auto: false
|
||||||
certfiles:
|
certfiles:
|
||||||
- /var/lib/acme/uninsane.org/fullchain.pem
|
- /var/lib/acme/uninsane.org/full.pem
|
||||||
- /var/lib/acme/uninsane.org/key.pem
|
# ca_file: ${pkgs.cacert.unbundled}/etc/ssl/certs/
|
||||||
|
# ca_file: ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
|
||||||
|
|
||||||
pam_userinfotype: jid
|
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/>
|
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
|
||||||
# TODO: host web admin panel
|
# 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:
|
listen:
|
||||||
-
|
-
|
||||||
port: 5222
|
port: 5222
|
||||||
module: ejabberd_c2s
|
module: ejabberd_c2s
|
||||||
|
shaper: c2s_shaper
|
||||||
starttls: true
|
starttls: true
|
||||||
|
access: c2s_access
|
||||||
|
-
|
||||||
|
port: 5223
|
||||||
|
module: ejabberd_c2s
|
||||||
|
shaper: c2s_shaper
|
||||||
|
tls: true
|
||||||
|
access: c2s_access
|
||||||
-
|
-
|
||||||
port: 5269
|
port: 5269
|
||||||
module: ejabberd_s2s_in
|
module: ejabberd_s2s_in
|
||||||
starttls: true
|
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" ];
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
owner = config.users.users.freshrss.name;
|
owner = config.users.users.freshrss.name;
|
||||||
mode = "400";
|
mode = "400";
|
||||||
};
|
};
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
{ user = "freshrss"; group = "freshrss"; directory = "/var/lib/freshrss"; }
|
{ user = "freshrss"; group = "freshrss"; directory = "/var/lib/freshrss"; }
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -49,4 +49,13 @@
|
|||||||
# the default ("*:0/5") is to run every 5 minutes.
|
# the default ("*:0/5") is to run every 5 minutes.
|
||||||
# `systemctl list-timers` to show
|
# `systemctl list-timers` to show
|
||||||
systemd.services.freshrss-updater.startAt = lib.mkForce "*:3/30";
|
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,7 +1,7 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
# 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"; }
|
||||||
];
|
];
|
||||||
@@ -72,4 +72,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" ];
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||||
Type = "simple";
|
Type = "simple";
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
|
||||||
# hardening
|
# hardening
|
||||||
WorkingDirectory = "/tmp";
|
WorkingDirectory = "/tmp";
|
||||||
@@ -42,4 +43,26 @@
|
|||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
wantedBy = [ "multi-user.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,12 +6,36 @@
|
|||||||
# - 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.impermanence.dirs = [
|
||||||
# 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.settings = {
|
services.kubo.settings = {
|
||||||
|
@@ -1,18 +1,32 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
# 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" ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,14 +1,69 @@
|
|||||||
{ config, ... }:
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
|
||||||
|
# else it's too spammy
|
||||||
|
lib.mkIf false
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
networking.firewall.allowedUDPPorts = [
|
||||||
|
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
|
||||||
|
];
|
||||||
|
sane.impermanence.dirs = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
|
{ 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" ];
|
||||||
|
|
||||||
# users.users.jellyfin.uid = config.sane.allocations.jellyfin-uid;
|
# users.users.jellyfin.uid = config.sane.allocations.jellyfin-uid;
|
||||||
# users.groups.jellyfin.gid = config.sane.allocations.jellyfin-gid;
|
# users.groups.jellyfin.gid = config.sane.allocations.jellyfin-gid;
|
||||||
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
|
services.jellyfin.enable = true;
|
||||||
# else it's too spammy
|
|
||||||
# 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.impermanence.dirs = [
|
||||||
{ 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.impermanence.dirs = [
|
||||||
{ 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.impermanence.dirs = [
|
||||||
# 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"; }
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
|
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
|
||||||
];
|
];
|
||||||
services.navidrome.enable = true;
|
services.navidrome.enable = true;
|
||||||
@@ -14,4 +14,13 @@
|
|||||||
AutoImportPlaylists = false;
|
AutoImportPlaylists = false;
|
||||||
ScanSchedule = "@every 1h";
|
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" ];
|
||||||
}
|
}
|
||||||
|
@@ -9,9 +9,12 @@ let
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
kTLS = true; # in-kernel TLS for better perf
|
# kTLS = true; # in-kernel TLS for better perf
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
|
||||||
services.nginx.enable = true;
|
services.nginx.enable = true;
|
||||||
services.nginx.appendConfig = ''
|
services.nginx.appendConfig = ''
|
||||||
# use 1 process per core.
|
# use 1 process per core.
|
||||||
@@ -31,6 +34,7 @@ in
|
|||||||
# sets gzip_comp_level = 5
|
# sets gzip_comp_level = 5
|
||||||
services.nginx.recommendedGzipSettings = true;
|
services.nginx.recommendedGzipSettings = true;
|
||||||
# enables OCSP stapling (so clients don't need contact the OCSP server -- i do instead)
|
# 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
|
# caches TLS sessions for 10m
|
||||||
services.nginx.recommendedTlsSettings = true;
|
services.nginx.recommendedTlsSettings = true;
|
||||||
# enables sendfile, tcp_nopush, tcp_nodelay, keepalive_timeout 65
|
# enables sendfile, tcp_nopush, tcp_nodelay, keepalive_timeout 65
|
||||||
@@ -44,7 +48,9 @@ in
|
|||||||
# and things don't look right. so force SSL.
|
# and things don't look right. so force SSL.
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
inherit kTLS;
|
# inherit kTLS;
|
||||||
|
# for OCSP stapling
|
||||||
|
sslTrustedCertificate = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||||
|
|
||||||
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
|
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
|
||||||
# yes, nginx does not strip the prefix when evaluating against the root.
|
# yes, nginx does not strip the prefix when evaluating against the root.
|
||||||
@@ -90,247 +96,25 @@ in
|
|||||||
# };
|
# };
|
||||||
};
|
};
|
||||||
|
|
||||||
# server statistics
|
|
||||||
services.nginx.virtualHosts."sink.uninsane.org" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
root = "/var/lib/uninsane/sink";
|
|
||||||
|
|
||||||
locations."/ws" = {
|
|
||||||
proxyPass = "http://127.0.0.1:7890";
|
|
||||||
# XXX not sure how much of this is necessary
|
|
||||||
extraConfig = ''
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $connection_upgrade;
|
|
||||||
proxy_buffering off;
|
|
||||||
proxy_read_timeout 7d;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
# Pleroma server and web interface
|
|
||||||
services.nginx.virtualHosts."fed.uninsane.org" = publog {
|
|
||||||
forceSSL = true; # pleroma redirects to https anyway
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:4000";
|
|
||||||
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
|
|
||||||
extraConfig = ''
|
|
||||||
# XXX colin: this block is in the nixos examples: i don't understand all of it
|
|
||||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
|
|
||||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
|
|
||||||
add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
|
|
||||||
if ($request_method = OPTIONS) {
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
add_header X-Permitted-Cross-Domain-Policies none;
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header Referrer-Policy same-origin;
|
|
||||||
add_header X-Download-Options noopen;
|
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
# proxy_set_header Host $http_host;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
|
|
||||||
# colin: added this due to Pleroma complaining in its logs
|
|
||||||
# proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
|
|
||||||
client_max_body_size 16m;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# transmission web client
|
|
||||||
services.nginx.virtualHosts."bt.uninsane.org" = {
|
|
||||||
# basicAuth is literally cleartext user/pw, so FORCE this to happen over SSL
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
locations."/" = {
|
|
||||||
# proxyPass = "http://ovpns.uninsane.org:9091";
|
|
||||||
proxyPass = "http://10.0.1.6:9091";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# jackett torrent search
|
|
||||||
services.nginx.virtualHosts."jackett.uninsane.org" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
locations."/" = {
|
|
||||||
# proxyPass = "http://ovpns.uninsane.org:9117";
|
|
||||||
proxyPass = "http://10.0.1.6:9117";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# matrix chat server
|
|
||||||
services.nginx.virtualHosts."matrix.uninsane.org" = publog {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
|
|
||||||
# TODO colin: replace this with something helpful to the viewer
|
|
||||||
# locations."/".extraConfig = ''
|
|
||||||
# return 404;
|
|
||||||
# '';
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8008";
|
|
||||||
};
|
|
||||||
# redirect browsers to the web client.
|
|
||||||
# i don't think native matrix clients ever fetch the root.
|
|
||||||
# ideally this would be put behind some user-agent test though.
|
|
||||||
locations."= /" = {
|
|
||||||
return = "301 https://web.matrix.uninsane.org";
|
|
||||||
};
|
|
||||||
|
|
||||||
# locations."/_matrix" = {
|
|
||||||
# proxyPass = "http://127.0.0.1:8008";
|
|
||||||
# };
|
|
||||||
};
|
|
||||||
|
|
||||||
# matrix web client
|
|
||||||
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-element-web
|
|
||||||
services.nginx.virtualHosts."web.matrix.uninsane.org" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
|
|
||||||
root = pkgs.element-web.override {
|
|
||||||
conf = {
|
|
||||||
default_server_config."m.homeserver" = {
|
|
||||||
"base_url" = "https://matrix.uninsane.org";
|
|
||||||
"server_name" = "uninsane.org";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# hosted git (web view and for `git <cmd>` use
|
|
||||||
services.nginx.virtualHosts."git.uninsane.org" = publog {
|
|
||||||
forceSSL = true; # gitea complains if served over a different protocol than its config file says
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:3000";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Jellyfin multimedia server
|
|
||||||
# this is mostly taken from the official jellfin.org docs
|
|
||||||
services.nginx.virtualHosts."jelly.uninsane.org" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8096";
|
|
||||||
extraConfig = ''
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Host $http_host;
|
|
||||||
|
|
||||||
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
|
|
||||||
proxy_buffering off;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
# locations."/web/" = {
|
|
||||||
# proxyPass = "http://127.0.0.1:8096/web/index.html";
|
|
||||||
# extraConfig = ''
|
|
||||||
# proxy_set_header Host $host;
|
|
||||||
# proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
# proxy_set_header X-Forwarded-Protocol $scheme;
|
|
||||||
# proxy_set_header X-Forwarded-Host $http_host;
|
|
||||||
# '';
|
|
||||||
# };
|
|
||||||
locations."/socket" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8096";
|
|
||||||
extraConfig = ''
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Host $http_host;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."music.uninsane.org" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
locations."/".proxyPass = "http://127.0.0.1:4533";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."rss.uninsane.org" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
# the routing is handled by freshrss.nix
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."ipfs.uninsane.org" = {
|
|
||||||
# don't default to ssl upgrades, since this may be dnslink'd from a different domain.
|
|
||||||
# ideally we'd disable ssl entirely, but some places assume it?
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
inherit kTLS;
|
|
||||||
|
|
||||||
|
# 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;
|
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;
|
addSSL = true;
|
||||||
enableACME = true;
|
enableACME = false;
|
||||||
inherit kTLS;
|
sslCertificate = "/var/www/certs/wildcard/cert.pem";
|
||||||
# serverAliases = [ "nixcache" ];
|
sslCertificateKey = "/var/www/certs/wildcard/key.pem";
|
||||||
locations."/".extraConfig = ''
|
# sslCertificate = "/var/lib/acme/.minica/cert.pem";
|
||||||
proxy_pass http://localhost:${toString config.services.nix-serve.port};
|
# sslCertificateKey = "/var/lib/acme/.minica/key.pem";
|
||||||
proxy_set_header Host $host;
|
# serverName = null;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
locations."/" = {
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
# 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.acceptTerms = true;
|
||||||
@@ -338,8 +122,47 @@ in
|
|||||||
|
|
||||||
users.users.acme.uid = config.sane.allocations.acme-uid;
|
users.users.acme.uid = config.sane.allocations.acme-uid;
|
||||||
users.groups.acme.gid = config.sane.allocations.acme-gid;
|
users.groups.acme.gid = config.sane.allocations.acme-gid;
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
|
{ 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;
|
||||||
|
}
|
@@ -6,7 +6,7 @@
|
|||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
# 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"; }
|
||||||
];
|
];
|
||||||
@@ -127,6 +127,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 = {
|
||||||
@@ -136,6 +137,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,7 +16,7 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
# 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"; }
|
||||||
@@ -25,6 +25,61 @@ in
|
|||||||
# "/var/lib/dhparams" # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/dhparams.nix
|
# "/var/lib/dhparams" # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/dhparams.nix
|
||||||
# "/var/lib/dovecot"
|
# "/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";
|
||||||
@@ -55,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";
|
||||||
@@ -76,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.impermanence.dirs = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
|
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
|
||||||
];
|
];
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
# example configs:
|
||||||
|
# - <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
|
||||||
# create users with:
|
# create users with:
|
||||||
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
|
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
|
||||||
|
|
||||||
@@ -7,13 +9,13 @@
|
|||||||
# nixnet runs ejabberd, so revisiting that.
|
# nixnet runs ejabberd, so revisiting that.
|
||||||
lib.mkIf false
|
lib.mkIf false
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
||||||
];
|
];
|
||||||
networking.firewall.allowedTCPPorts = [
|
networking.firewall.allowedTCPPorts = [
|
||||||
5222 # XMPP client -> server
|
5222 # XMPP client -> server
|
||||||
5269 # XMPP server -> server
|
5269 # XMPP server -> server
|
||||||
5280 # Prosody HTTP port (necessary?)
|
5280 # bosh
|
||||||
5281 # Prosody HTTPS port (necessary?)
|
5281 # Prosody HTTPS port (necessary?)
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ lib.mkIf false
|
|||||||
# c2s_require_encryption = true
|
# c2s_require_encryption = true
|
||||||
# '';
|
# '';
|
||||||
|
|
||||||
# extraModules = [ "private" "vcard" "privacy" "compression" "component" "muc" "pep" "adhoc" "lastactivity" "admin_adhoc" "blocklist"];
|
extraModules = [ "private" "vcard" "privacy" "compression" "component" "muc" "pep" "adhoc" "lastactivity" "admin_adhoc" "blocklist"];
|
||||||
|
|
||||||
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
||||||
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
|
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
|
||||||
@@ -51,7 +53,7 @@ lib.mkIf false
|
|||||||
domain = "localhost";
|
domain = "localhost";
|
||||||
enabled = true;
|
enabled = true;
|
||||||
};
|
};
|
||||||
"uninsane.org" = {
|
"xmpp.uninsane.org" = {
|
||||||
domain = "uninsane.org";
|
domain = "uninsane.org";
|
||||||
enabled = true;
|
enabled = true;
|
||||||
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{ ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.impermanence.service-dirs = [
|
sane.impermanence.dirs = [
|
||||||
# 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" ];
|
||||||
|
}
|
33
hosts/servo/services/wikipedia.nix
Normal file
33
hosts/servo/services/wikipedia.nix
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.mediawiki.uid = config.sane.allocations.mediawiki-uid;
|
||||||
|
|
||||||
|
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?
|
||||||
|
}
|
@@ -23,8 +23,10 @@ in
|
|||||||
sane.allocations.greeter-uid = mkId 999;
|
sane.allocations.greeter-uid = mkId 999;
|
||||||
sane.allocations.greeter-gid = mkId 999;
|
sane.allocations.greeter-gid = mkId 999;
|
||||||
|
|
||||||
|
# new servo users
|
||||||
sane.allocations.freshrss-uid = mkId 2401;
|
sane.allocations.freshrss-uid = mkId 2401;
|
||||||
sane.allocations.freshrss-gid = mkId 2401;
|
sane.allocations.freshrss-gid = mkId 2401;
|
||||||
|
sane.allocations.mediawiki-uid = mkId 2402;
|
||||||
|
|
||||||
sane.allocations.colin-uid = mkId 1000;
|
sane.allocations.colin-uid = mkId 1000;
|
||||||
sane.allocations.guest-uid = mkId 1100;
|
sane.allocations.guest-uid = mkId 1100;
|
||||||
|
@@ -3,12 +3,14 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./allocations.nix
|
./allocations.nix
|
||||||
|
./fs.nix
|
||||||
./gui
|
./gui
|
||||||
./home-manager
|
./home-manager
|
||||||
./packages.nix
|
./packages.nix
|
||||||
./image.nix
|
./image.nix
|
||||||
./impermanence.nix
|
./impermanence
|
||||||
./nixcache.nix
|
./nixcache.nix
|
||||||
./services
|
./services
|
||||||
|
./sops.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
155
modules/fs.nix
Normal file
155
modules/fs.nix
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
{ config, lib, pkgs, utils, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.fs;
|
||||||
|
|
||||||
|
# sane.fs."<path>" top-level options
|
||||||
|
fsEntry = types.submodule ({ name, ...}: let
|
||||||
|
parent = parentDir name;
|
||||||
|
has-parent = hasParent name;
|
||||||
|
parent-cfg = if has-parent then cfg."${parent}" else {};
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
dir = mkOption {
|
||||||
|
type = mkDirEntryType (parent-cfg.dir or {
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
mode = "0755";
|
||||||
|
});
|
||||||
|
};
|
||||||
|
depends = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "list of systemd services needed to be run before this service";
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
service = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "name of the systemd service which ensures this entry";
|
||||||
|
default = "ensure-${utils.escapeSystemdPath name}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
# 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`).
|
||||||
|
depends = if has-parent then [ "${parent-cfg.service}.service" ] else [];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
# sane.fs."<path>".dir sub-options
|
||||||
|
mkDirEntryType = defaults: types.submodule {
|
||||||
|
options = {
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str; # TODO: use uid?
|
||||||
|
};
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
mode = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = lib.mkDefault defaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
# given a fsEntry definition, output the `config` attrs it generates.
|
||||||
|
mkFsConfig = path: opt: {
|
||||||
|
systemd.services."${opt.service}" = {
|
||||||
|
description = "prepare ${path}";
|
||||||
|
script = ensure-dir-script;
|
||||||
|
scriptArgs = "${path} ${opt.dir.user} ${opt.dir.group} ${opt.dir.mode}";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
after = opt.depends;
|
||||||
|
wants = 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# systemd/shell script used to create and set perms for a specific dir
|
||||||
|
ensure-dir-script = ''
|
||||||
|
path="$1"
|
||||||
|
user="$2"
|
||||||
|
group="$3"
|
||||||
|
mode="$4"
|
||||||
|
|
||||||
|
if ! test -d "$path"
|
||||||
|
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 "$path" || test -d "$path"
|
||||||
|
fi
|
||||||
|
chmod "$mode" "$path"
|
||||||
|
chown "$user:$group" "$path"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# split the string path into a list of string components.
|
||||||
|
# root directory "/" becomes the empty list [].
|
||||||
|
# implicitly performs normalization so that:
|
||||||
|
# splitPath "a//b/" => ["a" "b"]
|
||||||
|
# splitPath "/a/b" => ["a" "b"]
|
||||||
|
splitPath = str: builtins.filter (seg: (builtins.isString seg) && seg != "" ) (builtins.split "/" str);
|
||||||
|
# return a string path, with leading slash but no trailing slash
|
||||||
|
joinPathAbs = comps: "/" + (builtins.concatStringsSep "/" comps);
|
||||||
|
concatPaths = paths: joinPathAbs (builtins.concatLists (builtins.map (p: splitPath p) paths));
|
||||||
|
# normalize the given path
|
||||||
|
normPath = str: joinPathAbs (splitPath str);
|
||||||
|
# return the parent directory. doesn't care about leading/trailing slashes.
|
||||||
|
# the parent of "/" is "/".
|
||||||
|
parentDir = str: normPath (builtins.dirOf (normPath str));
|
||||||
|
hasParent = str: (parentDir str) != (normPath str);
|
||||||
|
|
||||||
|
# return all ancestors of this path.
|
||||||
|
# e.g. ancestorsOf "/foo/bar/baz" => [ "/" "/foo" "/foo/bar" ]
|
||||||
|
ancestorsOf = path: if hasParent path then
|
||||||
|
ancestorsOf (parentDir path) ++ [ (parentDir path) ]
|
||||||
|
else
|
||||||
|
[ ]
|
||||||
|
;
|
||||||
|
|
||||||
|
# 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 == normPath p) (builtins.attrNames tree);
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
sane.fs = mkOption {
|
||||||
|
# type = types.attrsOf fsEntry;
|
||||||
|
type = fsTree;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = let
|
||||||
|
cfgs = builtins.attrValues (builtins.mapAttrs mkFsConfig cfg);
|
||||||
|
in {
|
||||||
|
# we can't lib.mkMerge at the top-level, so do it per-attribute
|
||||||
|
systemd = lib.mkMerge (catAttrs "systemd" cfgs);
|
||||||
|
};
|
||||||
|
}
|
11
modules/gui/snippets.txt
Normal file
11
modules/gui/snippets.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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://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
|
@@ -81,8 +81,29 @@ in
|
|||||||
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
|
||||||
@@ -103,7 +124,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";
|
||||||
@@ -114,7 +135,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";
|
||||||
@@ -141,7 +164,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";
|
||||||
|
|
||||||
@@ -185,19 +208,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:
|
||||||
|
@@ -12,24 +12,24 @@ let
|
|||||||
# extract package from `sane.packages.enabledUserPkgs`
|
# extract package from `sane.packages.enabledUserPkgs`
|
||||||
pkg-list = pkgspec: builtins.map (e: e.pkg or e) pkgspec;
|
pkg-list = pkgspec: builtins.map (e: e.pkg or e) pkgspec;
|
||||||
# extract `dir` from `sane.packages.enabledUserPkgs`
|
# extract `dir` from `sane.packages.enabledUserPkgs`
|
||||||
dir-list = pkgspec: builtins.concatLists (builtins.map (e: if e ? "dir" then [ e.dir ] else []) pkgspec);
|
dir-list = pkgspec: builtins.concatLists (builtins.map (e: e.dir or []) pkgspec);
|
||||||
private-list = pkgspec: builtins.concatLists (builtins.map (e: if e ? "private" then [ e.private ] else []) pkgspec);
|
private-list = pkgspec: builtins.concatLists (builtins.map (e: e.private or []) pkgspec);
|
||||||
feeds = import ./feeds.nix { inherit lib; };
|
feeds = import ./feeds.nix { inherit lib; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./aerc.nix
|
./aerc.nix
|
||||||
./discord.nix
|
|
||||||
./firefox.nix
|
./firefox.nix
|
||||||
./git.nix
|
./git.nix
|
||||||
./kitty.nix
|
./kitty.nix
|
||||||
./mpv.nix
|
./mpv.nix
|
||||||
./nb.nix
|
./nb.nix
|
||||||
./neovim.nix
|
./neovim.nix
|
||||||
|
./splatmoji.nix
|
||||||
./ssh.nix
|
./ssh.nix
|
||||||
./sublime-music.nix
|
./sublime-music.nix
|
||||||
./vlc.nix
|
./vlc.nix
|
||||||
./zsh.nix
|
./zsh
|
||||||
];
|
];
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
@@ -101,14 +101,14 @@ in
|
|||||||
);
|
);
|
||||||
in {
|
in {
|
||||||
# convenience
|
# convenience
|
||||||
"knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/knowledge";
|
"knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/private/knowledge";
|
||||||
"nixos".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/nixos";
|
"nixos".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/nixos";
|
||||||
"Videos/servo".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/Videos";
|
"Videos/servo".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/Videos";
|
||||||
"Videos/servo-incomplete".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/incomplete";
|
"Videos/servo-incomplete".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/incomplete";
|
||||||
"Music/servo".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/Music";
|
"Music/servo".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/Music";
|
||||||
|
|
||||||
# used by password managers, e.g. unix `pass`
|
# used by password managers, e.g. unix `pass`
|
||||||
".password-store".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/knowledge/secrets/accounts";
|
".password-store".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/knowledge/secrets/accounts";
|
||||||
} // privates;
|
} // privates;
|
||||||
|
|
||||||
# XDG defines things like ~/Desktop, ~/Downloads, etc.
|
# XDG defines things like ~/Desktop, ~/Downloads, etc.
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
lib.mkIf config.sane.home-manager.enable
|
|
||||||
{
|
|
||||||
# TODO: this should only be enabled on gui devices
|
|
||||||
# make Discord usable even when client is "outdated"
|
|
||||||
home-manager.users.colin.xdg.configFile."discord/settings.json".text = ''
|
|
||||||
{
|
|
||||||
"SKIP_HOST_UPDATE": true
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
}
|
|
@@ -63,6 +63,10 @@ in rec {
|
|||||||
(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
|
## The Verge - Decoder
|
||||||
(mkPod "https://feeds.megaphone.fm/recodedecode" // tech // weekly)
|
(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 = [
|
||||||
@@ -97,6 +101,7 @@ in rec {
|
|||||||
(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://anish.lakhwara.com/home.html" // tech // weekly)
|
||||||
(mkText "https://www.jefftk.com/news.rss" // tech // daily)
|
(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)
|
(mkSubstack "edwardsnowden" // pol // infrequent)
|
||||||
@@ -114,6 +119,7 @@ in rec {
|
|||||||
(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
|
||||||
(mkSubstack "samkriss" // humor // infrequent)
|
(mkSubstack "samkriss" // humor // infrequent)
|
||||||
@@ -133,8 +139,11 @@ in rec {
|
|||||||
## 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 = [
|
||||||
|
@@ -43,6 +43,7 @@ let
|
|||||||
addon = name: extid: hash: pkgs.fetchFirefoxAddon {
|
addon = name: extid: hash: pkgs.fetchFirefoxAddon {
|
||||||
inherit name hash;
|
inherit name hash;
|
||||||
url = "https://addons.mozilla.org/firefox/downloads/latest/${name}/latest.xpi";
|
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;
|
fixedExtid = extid;
|
||||||
};
|
};
|
||||||
localAddon = pkg: pkgs.fetchFirefoxAddon {
|
localAddon = pkg: pkgs.fetchFirefoxAddon {
|
||||||
@@ -51,11 +52,16 @@ let
|
|||||||
fixedExtid = pkg.extid;
|
fixedExtid = pkg.extid;
|
||||||
};
|
};
|
||||||
in [
|
in [
|
||||||
(addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-C+VQyaJ8BA0ErXGVTdnppJZ6J9SP+izf6RFxdS4VJoU=")
|
# get names from:
|
||||||
(addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-au5GGn22n4i6VrdOKqNMOrWdMoVCcpLdjO2wwRvyx7E=")
|
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
|
||||||
(addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-m14onUlnpLDPHezA/soKygcc76tF1fLG52tM/LkbAXQ=")
|
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
|
||||||
|
(addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-+xc4lcdsOwXxMsr4enFsdePbIb6GHq0bFLpqvH5xXos=")
|
||||||
|
(addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-30F8oDIgshXVY7YKgnfoc1tUTHfgeFbzXISJuVJs0AM=")
|
||||||
|
(addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-7ZDkG8O1rEYdh/La0PLi9tp92JxYeQvaOFt/BmnDv3U=")
|
||||||
(addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=")
|
(addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=")
|
||||||
(addon "ether-metamask" "webextension@metamask.io" "sha256-dnpwKpNF0KgHMAlz5btkkZySjMsnrXECS35ClkD2XHc=")
|
(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=")
|
# (addon "browserpass-ce" "browserpass@maximbaz.com" "sha256-sXgUBbRvMnRpeIW1MTkmTcoqtW/8RDXAkxAq1evFkpc=")
|
||||||
(localAddon pkgs.browserpass-extension)
|
(localAddon pkgs.browserpass-extension)
|
||||||
];
|
];
|
||||||
|
@@ -17,7 +17,7 @@ lib.mkIf false # XXX disabled!
|
|||||||
|
|
||||||
home-manager.users.colin = { config, ... }: {
|
home-manager.users.colin = { config, ... }: {
|
||||||
# nb markdown/personal knowledge manager
|
# nb markdown/personal knowledge manager
|
||||||
home.file.".nb/knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/knowledge";
|
home.file.".nb/knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/knowledge";
|
||||||
home.file.".nb/.current".text = "knowledge";
|
home.file.".nb/.current".text = "knowledge";
|
||||||
home.file.".nbrc".text = ''
|
home.file.".nbrc".text = ''
|
||||||
# manage with `nb settings`
|
# manage with `nb settings`
|
||||||
|
@@ -33,15 +33,6 @@ lib.mkIf config.sane.home-manager.enable
|
|||||||
" autocmd Syntax tex set conceallevel=2
|
" autocmd Syntax tex set conceallevel=2
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
# nabla renders inline math in any document, but it's buggy.
|
|
||||||
# https://github.com/jbyuki/nabla.nvim
|
|
||||||
# ({
|
|
||||||
# plugin = pkgs.nabla;
|
|
||||||
# type = "lua";
|
|
||||||
# config = ''
|
|
||||||
# require'nabla'.enable_virt()
|
|
||||||
# '';
|
|
||||||
# })
|
|
||||||
# treesitter syntax highlighting: https://nixos.wiki/wiki/Tree_sitters
|
# treesitter syntax highlighting: https://nixos.wiki/wiki/Tree_sitters
|
||||||
# docs: https://github.com/nvim-treesitter/nvim-treesitter
|
# docs: https://github.com/nvim-treesitter/nvim-treesitter
|
||||||
# config taken from: https://github.com/i077/system/blob/master/modules/home/neovim/default.nix
|
# config taken from: https://github.com/i077/system/blob/master/modules/home/neovim/default.nix
|
||||||
|
20
modules/home-manager/splatmoji.nix
Normal file
20
modules/home-manager/splatmoji.nix
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# borrows from:
|
||||||
|
# - default config: <https://github.com/cspeterson/splatmoji/blob/master/splatmoji.config>
|
||||||
|
# - wayland: <https://github.com/cspeterson/splatmoji/issues/32#issuecomment-830862566>
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
home-manager.users.colin = {
|
||||||
|
xdg.configFile."splatmoji/splatmoji.config".text = ''
|
||||||
|
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
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
@@ -1,63 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
lib.mkIf config.sane.home-manager.enable
|
|
||||||
{
|
|
||||||
# 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.
|
|
||||||
sane.impermanence.home-dirs = [ ".local/share/zsh" ];
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
initExtraBeforeCompInit = ''
|
|
||||||
# p10k instant prompt
|
|
||||||
# run p10k configure to configure, but it can't write out its file :-(
|
|
||||||
POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true
|
|
||||||
'';
|
|
||||||
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 = [
|
|
||||||
"environment"
|
|
||||||
"terminal"
|
|
||||||
"editor"
|
|
||||||
"history"
|
|
||||||
"directory"
|
|
||||||
"spectrum"
|
|
||||||
"utility"
|
|
||||||
"completion"
|
|
||||||
"prompt"
|
|
||||||
"git"
|
|
||||||
];
|
|
||||||
prompt.theme = "powerlevel10k";
|
|
||||||
utility.safeOps = false; # disable `mv` confirmation (and supposedly `rm`, too)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
home-manager.users.colin.home.shellAliases = {
|
|
||||||
":q" = "exit";
|
|
||||||
# common typos
|
|
||||||
"cd.." = "cd ..";
|
|
||||||
"cd../" = "cd ../";
|
|
||||||
};
|
|
||||||
}
|
|
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.impermanence.home-dirs = [
|
||||||
|
# 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
@@ -1,94 +0,0 @@
|
|||||||
# borrows from:
|
|
||||||
# https://xeiaso.net/blog/paranoid-nixos-2021-07-18
|
|
||||||
# https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/
|
|
||||||
# https://github.com/nix-community/impermanence
|
|
||||||
{ lib, config, impermanence, ... }:
|
|
||||||
|
|
||||||
with lib;
|
|
||||||
let
|
|
||||||
cfg = config.sane.impermanence;
|
|
||||||
# taken from sops-nix code: checks if any secrets are needed to create /etc/shadow
|
|
||||||
secretsForUsers = (lib.filterAttrs (_: v: v.neededForUsers) config.sops.secrets) != {};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
sane.impermanence.enable = mkOption {
|
|
||||||
default = false;
|
|
||||||
type = types.bool;
|
|
||||||
};
|
|
||||||
sane.impermanence.home-dirs = mkOption {
|
|
||||||
default = [];
|
|
||||||
type = types.listOf (types.either types.str (types.attrsOf types.str));
|
|
||||||
};
|
|
||||||
sane.impermanence.service-dirs = mkOption {
|
|
||||||
default = [];
|
|
||||||
type = types.listOf (types.either types.str (types.attrsOf types.str));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = let
|
|
||||||
map-dir = defaults: dir: if isString dir then
|
|
||||||
map-dir defaults { directory = "${defaults.directory}${dir}"; }
|
|
||||||
else
|
|
||||||
defaults // dir
|
|
||||||
;
|
|
||||||
map-dirs = defaults: dirs: builtins.map (map-dir defaults) dirs;
|
|
||||||
|
|
||||||
map-home-dirs = map-dirs { user = "colin"; group = "users"; mode = "0755"; directory = "/home/colin/"; };
|
|
||||||
map-sys-dirs = map-dirs { user = "root"; group = "root"; mode = "0755"; directory = ""; };
|
|
||||||
|
|
||||||
in mkIf cfg.enable {
|
|
||||||
sane.image.extraDirectories = [ "/nix/persist/var/log" ];
|
|
||||||
environment.persistence."/nix/persist" = {
|
|
||||||
directories = (map-home-dirs cfg.home-dirs) ++ (map-sys-dirs [
|
|
||||||
# NB: this `0700` here clobbers the perms for /persist/etc, breaking boot on freshly-deployed devices
|
|
||||||
# { mode = "0700"; directory = "/etc/NetworkManager/system-connections"; }
|
|
||||||
# "/etc/nixos"
|
|
||||||
# "/etc/ssh" # persist only the specific files we want, instead
|
|
||||||
"/var/log"
|
|
||||||
"/var/backup" # for e.g. postgres dumps
|
|
||||||
# "/var/lib/AccountsService" # not sure what this is, but it's empty
|
|
||||||
"/var/lib/alsa" # preserve output levels, default devices
|
|
||||||
# "/var/lib/blueman" # files aren't human readable
|
|
||||||
"/var/lib/bluetooth" # preserve bluetooth handshakes
|
|
||||||
"/var/lib/colord" # preserve color calibrations (?)
|
|
||||||
# "/var/lib/dhclient" # empty on lappy; dunno about desko
|
|
||||||
# "/var/lib/fwupd" # not sure why this would need persistent state
|
|
||||||
# "/var/lib/geoclue" # empty on lappy
|
|
||||||
# "/var/lib/lockdown" # empty on desko; might store secrets after iOS handshake?
|
|
||||||
# "/var/lib/logrotate.status" # seems redundant with what's in /var/log?
|
|
||||||
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
|
|
||||||
# "/var/lib/misc" # empty on lappy
|
|
||||||
# "/var/lib/NetworkManager" # looks to be mostly impermanent state?
|
|
||||||
# "/var/lib/NetworkManager-fortisslvpn" # empty on lappy
|
|
||||||
# "/var/lib/nixos" # has some uid/gid maps, but we enforce these to be deterministic.
|
|
||||||
# "/var/lib/PackageKit" # wtf is this?
|
|
||||||
# "/var/lib/power-profiles-daemon" # redundant with nixos declarations
|
|
||||||
# "/var/lib/private" # empty on lappy
|
|
||||||
# "/var/lib/systemd" # nothing obviously necessary
|
|
||||||
# "/var/lib/udisks2" # empty on lappy
|
|
||||||
# "/var/lib/upower" # historic charge data. unnecessary, but maybe used somewhere?
|
|
||||||
#
|
|
||||||
# servo additions:
|
|
||||||
] ++ cfg.service-dirs);
|
|
||||||
# /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
|
|
||||||
# of these, systemd-networkd is the only legitimate case to persist the machine-id.
|
|
||||||
# depersisting it should be "safe"; edge-cases like systemd-networkd can be directed to use some other ID if necessary.
|
|
||||||
# nixos-impermanence shows binding the host ssh priv key to this; i could probably hash the host key into /etc/machine-id if necessary.
|
|
||||||
# files = [ "/etc/machine-id" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# secret decoding depends on /etc/ssh keys, which may be persisted
|
|
||||||
system.activationScripts.setupSecrets.deps = [ "persist-ssh-host-keys" ];
|
|
||||||
system.activationScripts.setupSecretsForUsers = lib.mkIf secretsForUsers {
|
|
||||||
deps = [ "persist-ssh-host-keys" ];
|
|
||||||
};
|
|
||||||
# populated by ssh.nix, which persists /etc/ssh/host_keys
|
|
||||||
system.activationScripts.persist-ssh-host-keys.text = lib.mkDefault "";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
232
modules/impermanence/default.nix
Normal file
232
modules/impermanence/default.nix
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
# borrows from:
|
||||||
|
# https://xeiaso.net/blog/paranoid-nixos-2021-07-18
|
||||||
|
# https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/
|
||||||
|
# https://github.com/nix-community/impermanence
|
||||||
|
{ config, lib, pkgs, utils, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.impermanence;
|
||||||
|
getStore = { encryptedClearOnBoot, ... }: (
|
||||||
|
if encryptedClearOnBoot then {
|
||||||
|
device = "/mnt/impermanence/crypt/clearedonboot";
|
||||||
|
underlying = {
|
||||||
|
path = "/nix/persist/crypt/clearedonboot";
|
||||||
|
# TODO: consider moving this to /tmp, but that requires tmp be mounted first?
|
||||||
|
type = "gocryptfs";
|
||||||
|
key = "/mnt/impermanence/crypt/clearedonboot.key";
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
device = "/nix/persist";
|
||||||
|
# device = "/mnt/impermenanence/persist/plain";
|
||||||
|
# underlying = {
|
||||||
|
# path = "/nix/persist";
|
||||||
|
# type = "bind";
|
||||||
|
# };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
home-dir-defaults = {
|
||||||
|
user = "colin";
|
||||||
|
group = "users";
|
||||||
|
mode = "0755";
|
||||||
|
relativeTo = "/home/colin";
|
||||||
|
};
|
||||||
|
sys-dir-defaults = {
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
mode = "0755";
|
||||||
|
relativeTo = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
# turn a path into a name suitable for systemd
|
||||||
|
cleanName = utils.escapeSystemdPath;
|
||||||
|
|
||||||
|
# split the string path into a list of string components.
|
||||||
|
# root directory "/" becomes the empty list [].
|
||||||
|
# implicitly performs normalization so that:
|
||||||
|
# splitPath "a//b/" => ["a" "b"]
|
||||||
|
# splitPath "/a/b" => ["a" "b"]
|
||||||
|
splitPath = str: builtins.filter (seg: (builtins.isString seg) && seg != "" ) (builtins.split "/" str);
|
||||||
|
# return a string path, with leading slash but no trailing slash
|
||||||
|
joinPathAbs = comps: "/" + (builtins.concatStringsSep "/" comps);
|
||||||
|
concatPaths = paths: joinPathAbs (builtins.concatLists (builtins.map (p: splitPath p) paths));
|
||||||
|
|
||||||
|
dirOptions = defaults: types.submodule {
|
||||||
|
options = {
|
||||||
|
encryptedClearOnBoot = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
directory = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = defaults.user;
|
||||||
|
};
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = defaults.group;
|
||||||
|
};
|
||||||
|
mode = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = defaults.mode;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
mkDirsOption = defaults: mkOption {
|
||||||
|
default = [];
|
||||||
|
type = types.listOf (types.coercedTo types.str (d: { directory = d; }) (dirOptions defaults));
|
||||||
|
# apply = map (d: if isString d then { directory = d; } else d);
|
||||||
|
};
|
||||||
|
|
||||||
|
# expand user options with more context
|
||||||
|
ingestDirOption = defaults: opt: {
|
||||||
|
inherit (opt) user group mode;
|
||||||
|
directory = concatPaths [ defaults.relativeTo opt.directory ];
|
||||||
|
|
||||||
|
## helpful context
|
||||||
|
store = builtins.addErrorContext ''while ingestDirOption on ${opt.directory} with attrs ${builtins.concatStringsSep " " (attrNames opt)}''
|
||||||
|
(getStore opt);
|
||||||
|
};
|
||||||
|
|
||||||
|
ingestDirOptions = defaults: opts: builtins.map (ingestDirOption defaults) opts;
|
||||||
|
ingested-home-dirs = ingestDirOptions home-dir-defaults cfg.home-dirs;
|
||||||
|
ingested-sys-dirs = ingestDirOptions sys-dir-defaults cfg.dirs;
|
||||||
|
ingested-dirs = ingested-home-dirs ++ ingested-sys-dirs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
sane.impermanence.enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
sane.impermanence.root-on-tmpfs = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
description = "define / to be a tmpfs. make sure to mount some other device to /nix";
|
||||||
|
};
|
||||||
|
sane.impermanence.home-dirs = mkDirsOption home-dir-defaults;
|
||||||
|
sane.impermanence.dirs = mkDirsOption sys-dir-defaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
./root-on-tmpfs.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
config = mkIf cfg.enable (lib.mkMerge [
|
||||||
|
{
|
||||||
|
# TODO: move to sane.fs, to auto-ensure all user dirs?
|
||||||
|
sane.fs."/home/colin".dir = {
|
||||||
|
user = "colin";
|
||||||
|
group = config.users.users.colin.group;
|
||||||
|
mode = config.users.users.colin.homeMode;
|
||||||
|
};
|
||||||
|
|
||||||
|
# without this, we get `fusermount: fuse device not found, try 'modprobe fuse' first`.
|
||||||
|
# - that only happens after a activation-via-boot -- not activation-after-rebuild-switch.
|
||||||
|
# it seems likely that systemd loads `fuse` by default. see:
|
||||||
|
# - </etc/systemd/system/sysinit.target.wants/sys-fs-fuse-connections.mount>
|
||||||
|
# - triggers: /etc/systemd/system/modprobe@.service
|
||||||
|
# - calls `modprobe`
|
||||||
|
# note: even `boot.kernelModules = ...` isn't enough: that option creates /etc/modules-load.d/, which is ingested only by systemd.
|
||||||
|
# note: `boot.initrd.availableKernelModules` ALSO isn't enough: idk why.
|
||||||
|
# TODO: might not be necessary now we're using fileSystems and systemd
|
||||||
|
boot.initrd.kernelModules = [ "fuse" ];
|
||||||
|
|
||||||
|
# TODO: convert this to a systemd unit file?
|
||||||
|
system.activationScripts.prepareEncryptedClearedOnBoot =
|
||||||
|
let
|
||||||
|
script = pkgs.writeShellApplication {
|
||||||
|
name = "prepareEncryptedClearedOnBoot";
|
||||||
|
runtimeInputs = with pkgs; [ gocryptfs ];
|
||||||
|
text = ''
|
||||||
|
backing="$1"
|
||||||
|
passfile="$2"
|
||||||
|
if ! test -e "$passfile"
|
||||||
|
then
|
||||||
|
tmpdir=$(dirname "$passfile")
|
||||||
|
mkdir -p "$backing" "$tmpdir"
|
||||||
|
# if the key doesn't exist, it's probably not mounted => delete the backing dir
|
||||||
|
rm -rf "''${backing:?}"/*
|
||||||
|
# generate key. we can "safely" keep it around for the lifetime of this boot
|
||||||
|
dd if=/dev/random bs=128 count=1 | base64 --wrap=0 > "$passfile"
|
||||||
|
# initialize the crypt store
|
||||||
|
gocryptfs -quiet -passfile "$passfile" -init "$backing"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
store = getStore { encryptedClearOnBoot = true; };
|
||||||
|
in {
|
||||||
|
text = ''${script}/bin/prepareEncryptedClearedOnBoot ${store.underlying.path} ${store.underlying.key}'';
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems = let
|
||||||
|
store = getStore { encryptedClearOnBoot = true; };
|
||||||
|
in {
|
||||||
|
"${store.device}" = {
|
||||||
|
device = store.underlying.path;
|
||||||
|
fsType = "fuse.gocryptfs";
|
||||||
|
options = [
|
||||||
|
"nodev"
|
||||||
|
"nosuid"
|
||||||
|
"allow_other"
|
||||||
|
"passfile=${store.underlying.key}"
|
||||||
|
"defaults"
|
||||||
|
];
|
||||||
|
noCheck = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
let cfgFor = opt:
|
||||||
|
let
|
||||||
|
# systemd creates <path>.mount services for every fileSystems entry.
|
||||||
|
# <path> gets escaped as part of that: this code tries to guess that escaped name here.
|
||||||
|
# backing-mount = cleanName opt.store.device;
|
||||||
|
mount-service = cleanName opt.directory;
|
||||||
|
backing-path = concatPaths [ opt.store.device opt.directory ];
|
||||||
|
|
||||||
|
dir-service = config.sane.fs."${opt.directory}".service;
|
||||||
|
backing-service = config.sane.fs."${backing-path}".service;
|
||||||
|
in {
|
||||||
|
# create destination and backing directory, with correct perms
|
||||||
|
sane.fs."${opt.directory}".dir = {
|
||||||
|
inherit (opt) user group mode;
|
||||||
|
};
|
||||||
|
sane.fs."${backing-path}".dir = {
|
||||||
|
inherit (opt) user group mode;
|
||||||
|
};
|
||||||
|
# define the mountpoint.
|
||||||
|
fileSystems."${opt.directory}" = {
|
||||||
|
device = backing-path;
|
||||||
|
options = [
|
||||||
|
"bind"
|
||||||
|
# "x-systemd.requires=${backing-mount}.mount" # this should be implicit
|
||||||
|
"x-systemd.after=${backing-service}"
|
||||||
|
"x-systemd.after=${dir-service}"
|
||||||
|
# `wants` doesn't seem to make it to the service file here :-(
|
||||||
|
# "x-systemd.wants=${backing-service}"
|
||||||
|
# "x-systemd.wants=${dir-service}"
|
||||||
|
];
|
||||||
|
# fsType = "bind";
|
||||||
|
noCheck = true;
|
||||||
|
};
|
||||||
|
systemd.services."${backing-service}".wantedBy = [ "${mount-service}.mount" ];
|
||||||
|
systemd.services."${dir-service}".wantedBy = [ "${mount-service}.mount" ];
|
||||||
|
|
||||||
|
};
|
||||||
|
cfgs = builtins.map cfgFor ingested-dirs;
|
||||||
|
in {
|
||||||
|
fileSystems = lib.mkMerge (catAttrs "fileSystems" cfgs);
|
||||||
|
sane.fs = lib.mkMerge (catAttrs "fs" (catAttrs "sane" cfgs));
|
||||||
|
systemd = lib.mkMerge (catAttrs "systemd" cfgs);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
16
modules/impermanence/root-on-tmpfs.nix
Normal file
16
modules/impermanence/root-on-tmpfs.nix
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.sane.impermanence;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
fileSystems."/" = lib.mkIf (cfg.enable && cfg.root-on-tmpfs) {
|
||||||
|
device = "none";
|
||||||
|
fsType = "tmpfs";
|
||||||
|
options = [
|
||||||
|
"mode=755"
|
||||||
|
"size=1G"
|
||||||
|
"defaults"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
@@ -4,6 +4,11 @@ with lib;
|
|||||||
with pkgs;
|
with pkgs;
|
||||||
let
|
let
|
||||||
cfg = config.sane.packages;
|
cfg = config.sane.packages;
|
||||||
|
|
||||||
|
imagemagick = pkgs.imagemagick.override {
|
||||||
|
ghostscriptSupport = true;
|
||||||
|
};
|
||||||
|
|
||||||
consolePkgs = [
|
consolePkgs = [
|
||||||
backblaze-b2
|
backblaze-b2
|
||||||
cdrtools
|
cdrtools
|
||||||
@@ -12,11 +17,13 @@ let
|
|||||||
efivar
|
efivar
|
||||||
flashrom
|
flashrom
|
||||||
fwupd
|
fwupd
|
||||||
|
ghostscript # TODO: imagemagick wrapper should add gs to PATH
|
||||||
gnupg
|
gnupg
|
||||||
gocryptfs
|
gocryptfs
|
||||||
gopass
|
gopass
|
||||||
gopass-jsonapi
|
gopass-jsonapi
|
||||||
ifuse
|
ifuse
|
||||||
|
imagemagick
|
||||||
ipfs
|
ipfs
|
||||||
libimobiledevice
|
libimobiledevice
|
||||||
libsecret # for managing user keyrings
|
libsecret # for managing user keyrings
|
||||||
@@ -59,18 +66,18 @@ let
|
|||||||
celluloid # mpv frontend
|
celluloid # mpv frontend
|
||||||
chromium
|
chromium
|
||||||
clinfo
|
clinfo
|
||||||
{ pkg = dino; private = ".local/share/dino"; }
|
{ pkg = dino; private = [ ".local/share/dino" ]; }
|
||||||
electrum
|
electrum
|
||||||
|
|
||||||
# creds/session keys, etc
|
# creds/session keys, etc
|
||||||
{ pkg = element-desktop; private = ".config/Element"; }
|
{ pkg = element-desktop; private = [ ".config/Element" ]; }
|
||||||
# `emote` will show a first-run dialog based on what's in this directory.
|
# `emote` will show a first-run dialog based on what's in this directory.
|
||||||
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
|
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
|
||||||
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
|
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
|
||||||
{ pkg = emote; dir = ".local/share/Emote"; }
|
{ pkg = emote; dir = [ ".local/share/Emote" ]; }
|
||||||
evince # works on phosh
|
evince # works on phosh
|
||||||
|
|
||||||
# { pkg = fluffychat-moby; dir = ".local/share/chat.fluffy.fluffychat"; } # TODO: ship normal fluffychat on non-moby?
|
# { pkg = fluffychat-moby; dir = [ ".local/share/chat.fluffy.fluffychat" ]; } # TODO: ship normal fluffychat on non-moby?
|
||||||
|
|
||||||
foliate
|
foliate
|
||||||
font-manager
|
font-manager
|
||||||
@@ -78,8 +85,10 @@ let
|
|||||||
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
|
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
|
||||||
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
|
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
|
||||||
# then reboot (so that libsecret daemon re-loads the keyring...?)
|
# then reboot (so that libsecret daemon re-loads the keyring...?)
|
||||||
{ pkg = fractal-next; private = ".local/share/fractal"; }
|
{ pkg = fractal-latest; private = [ ".local/share/fractal" ]; }
|
||||||
|
# { pkg = fractal-next; private = [ ".local/share/fractal" ]; }
|
||||||
|
|
||||||
|
gajim # XMPP client
|
||||||
gimp # broken on phosh
|
gimp # broken on phosh
|
||||||
gnome.cheese
|
gnome.cheese
|
||||||
gnome.dconf-editor
|
gnome.dconf-editor
|
||||||
@@ -93,7 +102,7 @@ let
|
|||||||
gnome.gnome-terminal # works on phosh
|
gnome.gnome-terminal # works on phosh
|
||||||
gnome.gnome-weather
|
gnome.gnome-weather
|
||||||
|
|
||||||
{ pkg = gpodder-configured; dir = "gPodder/Downloads"; }
|
{ pkg = gpodder-configured; dir = [ "gPodder/Downloads" ]; }
|
||||||
|
|
||||||
gthumb
|
gthumb
|
||||||
handbrake
|
handbrake
|
||||||
@@ -106,15 +115,21 @@ let
|
|||||||
lollypop
|
lollypop
|
||||||
mesa-demos
|
mesa-demos
|
||||||
|
|
||||||
{ pkg = mpv; dir = ".config/mpv/watch_later"; }
|
{ pkg = mpv; dir = [ ".config/mpv/watch_later" ]; }
|
||||||
|
|
||||||
networkmanagerapplet
|
networkmanagerapplet
|
||||||
|
|
||||||
# not strictly necessary, but allows caching articles; offline use, etc.
|
# not strictly necessary, but allows caching articles; offline use, etc.
|
||||||
{ pkg = newsflash; dir = ".local/share/news-flash"; }
|
{ pkg = newsflash; dir = [ ".local/share/news-flash" ]; }
|
||||||
|
|
||||||
|
{ pkg = nheko; private = [
|
||||||
|
".config/nheko" # config file (including client token)
|
||||||
|
".cache/nheko" # media cache
|
||||||
|
".local/share/nheko" # per-account state database
|
||||||
|
]; }
|
||||||
|
|
||||||
# settings (electron app). TODO: can i manage these settings with home-manager?
|
# settings (electron app). TODO: can i manage these settings with home-manager?
|
||||||
{ pkg = obsidian; dir = ".config/obsidian"; }
|
{ pkg = obsidian; dir = [ ".config/obsidian" ]; }
|
||||||
|
|
||||||
pavucontrol
|
pavucontrol
|
||||||
# picard # music tagging
|
# picard # music tagging
|
||||||
@@ -127,13 +142,14 @@ let
|
|||||||
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
|
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
|
||||||
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json
|
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json
|
||||||
# possible to pass config as a CLI arg (sublime-music -c config.json)
|
# possible to pass config as a CLI arg (sublime-music -c config.json)
|
||||||
{ pkg = sublime-music; dir = ".local/share/sublime-music"; }
|
# { pkg = sublime-music; dir = [ ".local/share/sublime-music" ]; }
|
||||||
|
{ pkg = sublime-music-mobile; dir = [ ".local/share/sublime-music" ]; }
|
||||||
tdesktop # broken on phosh
|
tdesktop # broken on phosh
|
||||||
|
|
||||||
{ pkg = tokodon; dir = ".cache/KDE/tokodon"; }
|
{ pkg = tokodon; private = [ ".cache/KDE/tokodon" ]; }
|
||||||
|
|
||||||
# vlc remembers play position in ~/.config/vlc/vlc-qt-interface.conf
|
# vlc remembers play position in ~/.config/vlc/vlc-qt-interface.conf
|
||||||
{ pkg = vlc; dir = ".config/vlc"; }
|
{ pkg = vlc; dir = [ ".config/vlc" ]; }
|
||||||
|
|
||||||
whalebird # pleroma client. input is broken on phosh
|
whalebird # pleroma client. input is broken on phosh
|
||||||
xdg-utils # for xdg-open
|
xdg-utils # for xdg-open
|
||||||
@@ -148,41 +164,44 @@ let
|
|||||||
# XXX 2022-07-31: fix to allow links to open in default web-browser:
|
# XXX 2022-07-31: fix to allow links to open in default web-browser:
|
||||||
# https://github.com/NixOS/nixpkgs/issues/78961
|
# https://github.com/NixOS/nixpkgs/issues/78961
|
||||||
nss = pkgs.nss_latest;
|
nss = pkgs.nss_latest;
|
||||||
}); in { pkg = discord; dir = ".config/discord"; })
|
}); in { pkg = discord; private = [ ".config/discord" ]; })
|
||||||
|
|
||||||
# kaiteki # Pleroma client
|
# kaiteki # Pleroma client
|
||||||
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
|
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
|
||||||
|
# gpt2tc # XXX: unreliable mirror
|
||||||
|
|
||||||
logseq
|
logseq
|
||||||
losslesscut-bin
|
losslesscut-bin
|
||||||
makemkv
|
makemkv
|
||||||
|
|
||||||
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
||||||
{ pkg = monero-gui; dir = ".bitmonero"; }
|
{ pkg = monero-gui; dir = [ ".bitmonero" ]; }
|
||||||
|
|
||||||
# creds, media
|
# creds, media
|
||||||
{ pkg = signal-desktop; dir = ".config/Signal"; }
|
{ pkg = signal-desktop; private = [ ".config/Signal" ]; }
|
||||||
|
|
||||||
# creds. TODO: can i manage this with home-manager?
|
# creds. TODO: can i manage this with home-manager?
|
||||||
{ pkg = spotify; dir = ".config/spotify"; }
|
{ pkg = spotify; dir = [ ".config/spotify" ]; }
|
||||||
|
|
||||||
# hardenedMalloc solves a crash at startup
|
# hardenedMalloc solves a crash at startup
|
||||||
(tor-browser-bundle-bin.override { useHardenedMalloc = false; })
|
(tor-browser-bundle-bin.override { useHardenedMalloc = false; })
|
||||||
|
|
||||||
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
|
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
|
||||||
{ pkg = zecwallet-lite; private = ".zcash"; }
|
{ pkg = zecwallet-lite; private = [ ".zcash" ]; }
|
||||||
] else []);
|
] else []);
|
||||||
|
|
||||||
# general-purpose utilities that we want any user to be able to access
|
# general-purpose utilities that we want any user to be able to access
|
||||||
# (specifically: root, in case of rescue)
|
# (specifically: root, in case of rescue)
|
||||||
systemPkgs = [
|
systemPkgs = [
|
||||||
btrfs-progs
|
btrfs-progs
|
||||||
|
cacert.unbundled # some services require unbundled /etc/ssl/certs
|
||||||
cryptsetup
|
cryptsetup
|
||||||
dig
|
dig
|
||||||
efibootmgr
|
efibootmgr
|
||||||
fatresize
|
fatresize
|
||||||
fd
|
fd
|
||||||
file
|
file
|
||||||
|
gawk
|
||||||
gptfdisk
|
gptfdisk
|
||||||
hdparm
|
hdparm
|
||||||
htop
|
htop
|
||||||
@@ -200,10 +219,14 @@ let
|
|||||||
parted
|
parted
|
||||||
pciutils
|
pciutils
|
||||||
powertop
|
powertop
|
||||||
|
pstree
|
||||||
ripgrep
|
ripgrep
|
||||||
screen
|
screen
|
||||||
smartmontools
|
smartmontools
|
||||||
socat
|
socat
|
||||||
|
strace
|
||||||
|
tcpdump
|
||||||
|
tree
|
||||||
usbutils
|
usbutils
|
||||||
wget
|
wget
|
||||||
];
|
];
|
||||||
@@ -223,15 +246,31 @@ let
|
|||||||
rustup
|
rustup
|
||||||
swig
|
swig
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pkgSpec = types.submodule {
|
||||||
|
options = {
|
||||||
|
pkg = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
};
|
||||||
|
dir = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "list of home-relative paths to persist for this package";
|
||||||
|
};
|
||||||
|
private = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "list of home-relative paths to persist (in encrypted format) for this package";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
# packages to deploy to the user's home
|
# packages to deploy to the user's home
|
||||||
sane.packages.extraUserPkgs = mkOption {
|
sane.packages.extraUserPkgs = mkOption {
|
||||||
default = [ ];
|
default = [ ];
|
||||||
# each entry can be either a package, or attrs:
|
type = types.listOf (types.either types.package pkgSpec);
|
||||||
# { pkg = package; dir = optional string; private = optional string };
|
|
||||||
type = types.listOf (types.either types.package types.attrs);
|
|
||||||
};
|
};
|
||||||
sane.packages.enableConsolePkgs = mkOption {
|
sane.packages.enableConsolePkgs = mkOption {
|
||||||
default = false;
|
default = false;
|
||||||
@@ -268,5 +307,7 @@ in
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
environment.systemPackages = mkIf cfg.enableSystemPkgs systemPkgs;
|
environment.systemPackages = mkIf cfg.enableSystemPkgs systemPkgs;
|
||||||
|
# XXX: this might not be necessary. try removing this and cacert.unbundled?
|
||||||
|
environment.etc."ssl/certs".source = mkIf cfg.enableSystemPkgs "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,9 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./duplicity.nix
|
./duplicity.nix
|
||||||
|
./dyn-dns.nix
|
||||||
|
./kiwix-serve.nix
|
||||||
./nixserve.nix
|
./nixserve.nix
|
||||||
|
./trust-dns.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ in
|
|||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
# we need this mostly because of the size of duplicity's cache
|
# we need this mostly because of the size of duplicity's cache
|
||||||
sane.impermanence.service-dirs = [ "/var/lib/duplicity" ];
|
sane.impermanence.dirs = [ "/var/lib/duplicity" ];
|
||||||
|
|
||||||
services.duplicity.enable = true;
|
services.duplicity.enable = true;
|
||||||
services.duplicity.targetUrl = "$DUPLICITY_URL";
|
services.duplicity.targetUrl = "$DUPLICITY_URL";
|
||||||
|
91
modules/services/dyn-dns.nix
Normal file
91
modules/services/dyn-dns.nix
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.services.dyn-dns;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
sane.services.dyn-dns = {
|
||||||
|
enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
|
||||||
|
ipPath = mkOption {
|
||||||
|
default = "/var/lib/uninsane/wan.txt";
|
||||||
|
type = types.str;
|
||||||
|
description = "where to store the latest WAN IPv4 address";
|
||||||
|
};
|
||||||
|
|
||||||
|
ipCmd = mkOption {
|
||||||
|
default = "${pkgs.sane-scripts}/bin/sane-ip-check-router-wan";
|
||||||
|
type = types.path;
|
||||||
|
description = "command to run to query the current WAN IP";
|
||||||
|
};
|
||||||
|
|
||||||
|
interval = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "10min";
|
||||||
|
description = "systemd time string for how frequently to re-check the IP";
|
||||||
|
};
|
||||||
|
|
||||||
|
restartOnChange = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "list of systemd unit files to restart when the IP changes";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services.dyn-dns = {
|
||||||
|
description = "update this host's record of its WAN IP";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
restartTriggers = [(builtins.toJSON cfg)];
|
||||||
|
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = cfg.restartOnChange;
|
||||||
|
before = cfg.restartOnChange;
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
mkdir -p "$(dirname '${cfg.ipPath}')"
|
||||||
|
newIp=$(${cfg.ipCmd})
|
||||||
|
oldIp=$(cat '${cfg.ipPath}' || true)
|
||||||
|
# systemd path units are triggered on any file write action,
|
||||||
|
# regardless of content change. so only update the file if our IP *actually* changed
|
||||||
|
if [ "$newIp" != "$oldIp" ]
|
||||||
|
then
|
||||||
|
echo "$newIp" > '${cfg.ipPath}'
|
||||||
|
echo "WAN ip changed $oldIp -> $newIp"
|
||||||
|
fi
|
||||||
|
exit $(test -f '${cfg.ipPath}')
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.dyn-dns = {
|
||||||
|
# if anything wants dyn-dns.service, they surely want the timer too.
|
||||||
|
wantedBy = [ "dyn-dns.service" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnUnitActiveSec = cfg.interval;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.paths.dyn-dns-watcher = {
|
||||||
|
before = [ "dyn-dns.timer" ];
|
||||||
|
wantedBy = [ "dyn-dns.timer" ];
|
||||||
|
pathConfig = {
|
||||||
|
Unit = "dyn-dns-reactor.service";
|
||||||
|
PathChanged = [ cfg.ipPath ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.dyn-dns-reactor = {
|
||||||
|
description = "react to the system's WAN IP changing";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
script = if cfg.restartOnChange != [] then ''
|
||||||
|
${pkgs.systemd}/bin/systemctl restart ${toString cfg.restartOnChange}
|
||||||
|
'' else "${pkgs.coreutils}/bin/true";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
55
modules/services/kiwix-serve.nix
Normal file
55
modules/services/kiwix-serve.nix
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.services.kiwix-serve;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
sane.services.kiwix-serve = {
|
||||||
|
enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = pkgs.kiwix-tools;
|
||||||
|
defaultText = literalExpression "pkgs.kiwix-tools";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
The package that provides `bin/kiwix-serve`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 80;
|
||||||
|
description = lib.mdDoc "Port number to listen on.";
|
||||||
|
};
|
||||||
|
listenAddress = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
IP address to listen on. Listens on all available addresses if unspecified.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
zimPaths = mkOption {
|
||||||
|
type = types.nonEmptyListOf (types.either types.str types.path);
|
||||||
|
description = lib.mdDoc "ZIM file path(s)";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services.kiwix-serve = {
|
||||||
|
description = "Deliver ZIM file(s) articles via HTTP";
|
||||||
|
serviceConfig = let
|
||||||
|
maybeListenAddress = lib.optionals (cfg.listenAddress != null) ["-l" cfg.listenAddress];
|
||||||
|
args = maybeListenAddress ++ ["-p" cfg.port] ++ cfg.zimPaths;
|
||||||
|
in {
|
||||||
|
ExecStart = "${cfg.package}/bin/kiwix-serve ${lib.escapeShellArgs args}";
|
||||||
|
Type = "simple";
|
||||||
|
};
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
167
modules/services/trust-dns.nix
Normal file
167
modules/services/trust-dns.nix
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.sane.services.trust-dns;
|
||||||
|
toml = pkgs.formats.toml { };
|
||||||
|
fmtRecord = proto: rrtype: name: value: "${name}\t${proto}\t${rrtype}\t${value}";
|
||||||
|
fmtRecordList = proto: rrtype: name: values: concatStringsSep
|
||||||
|
"\n"
|
||||||
|
(map (fmtRecord proto rrtype name) values)
|
||||||
|
;
|
||||||
|
fmtRecordAttrs = proto: rrtype: rrAttrs:
|
||||||
|
concatStringsSep
|
||||||
|
"\n"
|
||||||
|
(
|
||||||
|
attrValues (
|
||||||
|
mapAttrs
|
||||||
|
(name: fmtRecordList proto rrtype name)
|
||||||
|
rrAttrs
|
||||||
|
)
|
||||||
|
);
|
||||||
|
fmtIncludes = paths: concatStringsSep
|
||||||
|
"\n"
|
||||||
|
(map (path: "$INCLUDE ${path}") paths);
|
||||||
|
|
||||||
|
genZone = zcfg: ''
|
||||||
|
$TTL ${toString zcfg.TTL}
|
||||||
|
${fmtRecordAttrs "IN" "SOA" zcfg.inet.SOA}
|
||||||
|
${fmtRecordAttrs "IN" "A" zcfg.inet.A}
|
||||||
|
${fmtRecordAttrs "IN" "CNAME" zcfg.inet.CNAME}
|
||||||
|
${fmtRecordAttrs "IN" "MX" zcfg.inet.MX}
|
||||||
|
${fmtRecordAttrs "IN" "NS" zcfg.inet.NS}
|
||||||
|
${fmtRecordAttrs "IN" "SRV" zcfg.inet.SRV}
|
||||||
|
${fmtRecordAttrs "IN" "TXT" zcfg.inet.TXT}
|
||||||
|
${fmtIncludes zcfg.include}
|
||||||
|
${zcfg.extraConfig}
|
||||||
|
'';
|
||||||
|
|
||||||
|
configFile = toml.generate "trust-dns.toml" {
|
||||||
|
listen_addrs_ipv4 = cfg.listenAddrsIPv4;
|
||||||
|
zones = attrValues (
|
||||||
|
mapAttrs (zname: zcfg: rec {
|
||||||
|
zone = if zcfg.name == null then zname else zcfg.name;
|
||||||
|
zone_type = "Primary";
|
||||||
|
file = if zcfg.file == null then
|
||||||
|
pkgs.writeText "${zone}.zone" (genZone zcfg)
|
||||||
|
else
|
||||||
|
zcfg.file;
|
||||||
|
}) cfg.zones
|
||||||
|
);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
sane.services.trust-dns = {
|
||||||
|
enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
listenAddrsIPv4 = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "array of ipv4 addresses on which to listen for DNS queries";
|
||||||
|
};
|
||||||
|
# reference <nixpkgs:nixos/modules/services/web-servers/nginx/vhost-options.nix>
|
||||||
|
zones = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
description = "zone name. defaults to the attribute name in zones";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
TTL = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
description = "default TTL";
|
||||||
|
default = 3600;
|
||||||
|
};
|
||||||
|
include = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "paths of other zone files to $INCLUDE into this one";
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
description = "extra lines to append to the zone file";
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
inet = {
|
||||||
|
SOA = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
description = "Start of Authority record(s)";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
A = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
description = "IPv4 address record(s)";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
CNAME = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
description = "canonical name record(s)";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
MX = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
description = "mail exchanger record(s)";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
NS = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
description = "name server record(s)";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
SRV = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
description = "service record(s)";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
TXT = mkOption {
|
||||||
|
type = types.attrsOf (types.listOf types.str);
|
||||||
|
description = "text record(s)";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
file = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "instead of using the generated zone file, use the specified path";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = {};
|
||||||
|
description = "Declarative zone config";
|
||||||
|
};
|
||||||
|
|
||||||
|
generatedZones = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
description = "generated zone text for each zone";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
sane.services.trust-dns.generatedZones = mapAttrs (zone: zcfg: genZone zcfg) cfg.zones;
|
||||||
|
networking.firewall.allowedTCPPorts = [ 53 ];
|
||||||
|
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||||
|
|
||||||
|
systemd.services.trust-dns = {
|
||||||
|
description = "trust-dns DNS server";
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = ''
|
||||||
|
${pkgs.trust-dns}/bin/named \
|
||||||
|
--config ${configFile} \
|
||||||
|
--zonedir /
|
||||||
|
'';
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
# TODO: hardening (like, don't run as root!)
|
||||||
|
};
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
32
modules/sops.nix
Normal file
32
modules/sops.nix
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# taken from sops-nix code: checks if any secrets are needed to create /etc/shadow
|
||||||
|
secrets-for-users = (lib.filterAttrs (_: v: v.neededForUsers) config.sops.secrets) != {};
|
||||||
|
sops-files = config.sops.age.sshKeyPaths ++ config.sops.gnupg.sshKeyPaths ++ [ config.sops.age.keyFile ];
|
||||||
|
keys-in-etc = builtins.any (p: builtins.substring 0 5 p == "/etc/") sops-files;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf (secrets-for-users && keys-in-etc) {
|
||||||
|
# secret decoding depends on keys in /etc/ (like the ssh host key), so make sure those are present.
|
||||||
|
system.activationScripts.setupSecretsForUsers = lib.mkIf secrets-for-users {
|
||||||
|
deps = [ "etc" ];
|
||||||
|
};
|
||||||
|
# TODO: we should selectively remove "users" and "groups", but keep manually specified deps?
|
||||||
|
system.activationScripts.etc.deps = lib.mkForce [];
|
||||||
|
assertions = builtins.concatLists (builtins.attrValues (
|
||||||
|
builtins.mapAttrs
|
||||||
|
(path: value: [
|
||||||
|
{
|
||||||
|
assertion = (builtins.substring 0 1 value.user) == "+";
|
||||||
|
message = "non-numeric user for /etc/${path}: ${value.user} prevents early /etc linking";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = (builtins.substring 0 1 value.group) == "+";
|
||||||
|
message = "non-numeric group for /etc/${path}: ${value.group} prevents early /etc linking";
|
||||||
|
}
|
||||||
|
])
|
||||||
|
config.environment.etc
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
26
nixpatches/2022-12-19-i2p-aarch64.patch
Normal file
26
nixpatches/2022-12-19-i2p-aarch64.patch
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
diff --git a/pkgs/tools/networking/i2p/default.nix b/pkgs/tools/networking/i2p/default.nix
|
||||||
|
index e835007fdc5..1406486c7d4 100644
|
||||||
|
--- a/pkgs/tools/networking/i2p/default.nix
|
||||||
|
+++ b/pkgs/tools/networking/i2p/default.nix
|
||||||
|
@@ -50,7 +50,7 @@ stdenv.mkDerivation rec {
|
||||||
|
binaryBytecode # source bundles dependencies as jars
|
||||||
|
];
|
||||||
|
license = licenses.gpl2;
|
||||||
|
- platforms = [ "x86_64-linux" "i686-linux" ];
|
||||||
|
+ platforms = [ "x86_64-linux" "i686-linux" "aarch64-linux" ];
|
||||||
|
maintainers = with maintainers; [ joelmo ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
diff --git a/pkgs/tools/system/java-service-wrapper/default.nix b/pkgs/tools/system/java-service-wrapper/default.nix
|
||||||
|
index 93d86a75d18..ab563085f93 100644
|
||||||
|
--- a/pkgs/tools/system/java-service-wrapper/default.nix
|
||||||
|
+++ b/pkgs/tools/system/java-service-wrapper/default.nix
|
||||||
|
@@ -44,7 +44,7 @@ stdenv.mkDerivation rec {
|
||||||
|
homepage = "https://wrapper.tanukisoftware.com/";
|
||||||
|
changelog = "https://wrapper.tanukisoftware.com/doc/english/release-notes.html#${version}";
|
||||||
|
license = licenses.gpl2Only;
|
||||||
|
- platforms = [ "x86_64-linux" "i686-linux" ];
|
||||||
|
+ platforms = [ "x86_64-linux" "i686-linux" "aarch64-linux" ];
|
||||||
|
maintainers = [ maintainers.suhr ];
|
||||||
|
};
|
||||||
|
}
|
@@ -1,17 +1,4 @@
|
|||||||
fetchpatch: [
|
{ fakeHash, fetchpatch }: [
|
||||||
# phosh: 0.21.1 -> 0.22.0
|
|
||||||
(fetchpatch {
|
|
||||||
url = "https://github.com/NixOS/nixpkgs/pull/201881.diff";
|
|
||||||
sha256 = "sha256-7tV7F1gKTfMwNJ0evweD7p6RXOvOHQXXtuuBqnRGyCc=";
|
|
||||||
})
|
|
||||||
# phosh-mobile-settings: init at 0.21.1
|
|
||||||
(fetchpatch {
|
|
||||||
# url = "https://git.uninsane.org/colin/nixpkgs/commit/0c1a7e8504291eb0076bbee3f8ebf693f4641112.diff";
|
|
||||||
# sha256 = "sha256-OczjlQcG7sTM/V9Y9VL/qdwaWPKfjAJsh3czqqhRQig=";
|
|
||||||
url = "https://github.com/NixOS/nixpkgs/pull/193845.diff";
|
|
||||||
sha256 = "sha256-/9c8hUF7DO54f8/6oSRzxLOwMdts5UPa4pfXsdBa2pM=";
|
|
||||||
})
|
|
||||||
|
|
||||||
# librewolf: build with `MOZ_REQUIRE_SIGNING=false`
|
# librewolf: build with `MOZ_REQUIRE_SIGNING=false`
|
||||||
(fetchpatch {
|
(fetchpatch {
|
||||||
url = "https://github.com/NixOS/nixpkgs/pull/199134.diff";
|
url = "https://github.com/NixOS/nixpkgs/pull/199134.diff";
|
||||||
@@ -19,6 +6,21 @@ fetchpatch: [
|
|||||||
sha256 = "sha256-Ne4hyHQDwBHUlWo8Z3QyRdmEv1rYGOjFGxSfOAcLUvQ=";
|
sha256 = "sha256-Ne4hyHQDwBHUlWo8Z3QyRdmEv1rYGOjFGxSfOAcLUvQ=";
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# trust-dns: init at 0.22.0
|
||||||
|
(fetchpatch {
|
||||||
|
# https://git.uninsane.org/colin/nixpkgs/compare/master...pr-trust-dns.diff
|
||||||
|
url = "https://git.uninsane.org/colin/nixpkgs/commit/feee7e0357a74ab0510b2d113a3bdede1d509759.diff";
|
||||||
|
sha256 = "sha256-t4sG+xLDaxbJ/mV5G18N4ag8EC3IXPgtN5FJGANh1Dc=";
|
||||||
|
})
|
||||||
|
|
||||||
|
# kiwix-tools: init at 3.4.0
|
||||||
|
(fetchpatch {
|
||||||
|
url = "https://github.com/NixOS/nixpkgs/pull/206254.diff";
|
||||||
|
sha256 = "sha256-Z4V9mOv4HYg3kDnWoYcxz3ch03I/1USrLjzlq4X9YqI=";
|
||||||
|
})
|
||||||
|
|
||||||
|
./2022-12-19-i2p-aarch64.patch
|
||||||
|
|
||||||
# # kaiteki: init at 2022-09-03
|
# # kaiteki: init at 2022-09-03
|
||||||
# vendorHash changes too frequently (might not be reproducible).
|
# vendorHash changes too frequently (might not be reproducible).
|
||||||
# using local package defn until stabilized
|
# using local package defn until stabilized
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
{ stdenv, pkgs }:
|
{ stdenv, tow-boot-rp4, raspberrypifw, raspberrypi-armstubs }:
|
||||||
|
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
pname = "bootpart-tow-boot-rpi-aarch64";
|
pname = "bootpart-tow-boot-rpi-aarch64";
|
||||||
version = "1";
|
version = "1";
|
||||||
|
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with [
|
||||||
tow-boot-rpi4 # for Tow-Boot.*.bin
|
tow-boot-rpi4 # for Tow-Boot.*.bin
|
||||||
raspberrypifw # for bootcode.bin, *.dat, *.elf, *.dtb
|
raspberrypifw # for bootcode.bin, *.dat, *.elf, *.dtb
|
||||||
raspberrypi-armstubs # for armstub*
|
raspberrypi-armstubs # for armstub*
|
||||||
@@ -15,7 +15,7 @@ stdenv.mkDerivation rec {
|
|||||||
|
|
||||||
dontUnpack = true;
|
dontUnpack = true;
|
||||||
|
|
||||||
installPhase = with pkgs; ''
|
installPhase = ''
|
||||||
mkdir "$out"
|
mkdir "$out"
|
||||||
cp ${tow-boot-rpi4}/Tow-Boot.noenv.*.bin "$out"/
|
cp ${tow-boot-rpi4}/Tow-Boot.noenv.*.bin "$out"/
|
||||||
cp -R ${raspberrypifw}/share/raspberrypi/boot/*.dtb "$out"/
|
cp -R ${raspberrypifw}/share/raspberrypi/boot/*.dtb "$out"/
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
{ stdenv, pkgs }:
|
{ stdenv
|
||||||
|
, ubootRaspberryPi4_64bit
|
||||||
|
, raspberrypifw
|
||||||
|
, raspberrypi-armstubs
|
||||||
|
}:
|
||||||
|
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
pname = "bootpart-u-boot-rpi-aarch64";
|
pname = "bootpart-u-boot-rpi-aarch64";
|
||||||
version = "1";
|
version = "1";
|
||||||
|
|
||||||
buildInputs = with pkgs; [
|
buildInputs = [
|
||||||
ubootRaspberryPi4_64bit
|
ubootRaspberryPi4_64bit
|
||||||
raspberrypifw # for bootcode.bin, *.dat, *.elf, *.dtb
|
raspberrypifw # for bootcode.bin, *.dat, *.elf, *.dtb
|
||||||
raspberrypi-armstubs # for armstub*
|
raspberrypi-armstubs # for armstub*
|
||||||
@@ -15,7 +19,7 @@ stdenv.mkDerivation rec {
|
|||||||
|
|
||||||
dontUnpack = true;
|
dontUnpack = true;
|
||||||
|
|
||||||
installPhase = with pkgs; ''
|
installPhase = ''
|
||||||
mkdir "$out"
|
mkdir "$out"
|
||||||
cp ${ubootRaspberryPi4_64bit}/u-boot.bin "$out"/
|
cp ${ubootRaspberryPi4_64bit}/u-boot.bin "$out"/
|
||||||
cp ${ubootRaspberryPi4_64bit}/*.dtb "$out"/
|
cp ${ubootRaspberryPi4_64bit}/*.dtb "$out"/
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
{ stdenv, pkgs }:
|
{ stdenv, syslinux }:
|
||||||
|
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
pname = "bootpart-uefi-x86_64";
|
pname = "bootpart-uefi-x86_64";
|
||||||
version = "1";
|
version = "1";
|
||||||
|
|
||||||
buildInputs = [ pkgs.syslinux ];
|
buildInputs = [ syslinux ];
|
||||||
|
|
||||||
dontUnpack = true;
|
dontUnpack = true;
|
||||||
|
|
||||||
installPhase = with pkgs; ''
|
installPhase = ''
|
||||||
# populate the EFI directory with syslinux, and configure it to read that extlinux.conf file managed by nixos
|
# populate the EFI directory with syslinux, and configure it to read that extlinux.conf file managed by nixos
|
||||||
mkdir -p "$out/EFI/syslinux" "$out/EFI/BOOT" "$out/syslinux"
|
mkdir -p "$out/EFI/syslinux" "$out/EFI/BOOT" "$out/syslinux"
|
||||||
cp -R "${syslinux}/share/syslinux/efi64"/* "$out/EFI/syslinux"
|
cp -R "${syslinux}/share/syslinux/efi64"/* "$out/EFI/syslinux"
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
{ pkgs
|
{ lib
|
||||||
|
, browserpass
|
||||||
, bash
|
, bash
|
||||||
, fetchFromGitea
|
, fetchFromGitea
|
||||||
, gnused
|
, gnused
|
||||||
, lib
|
|
||||||
, sane-scripts
|
, sane-scripts
|
||||||
, sops
|
, sops
|
||||||
, stdenv
|
, stdenv
|
||||||
@@ -26,7 +26,7 @@ let
|
|||||||
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
(pkgs.browserpass.overrideAttrs (upstream: {
|
(browserpass.overrideAttrs (upstream: {
|
||||||
src = fetchFromGitea {
|
src = fetchFromGitea {
|
||||||
domain = "git.uninsane.org";
|
domain = "git.uninsane.org";
|
||||||
owner = "colin";
|
owner = "colin";
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{ pkgs }:
|
{ firefox-unwrapped }:
|
||||||
|
|
||||||
(pkgs.firefox-unwrapped.overrideAttrs (upstream: {
|
(firefox-unwrapped.overrideAttrs (upstream: {
|
||||||
# NB: firefox takes about 1hr to build on my 24-thread ryzen desktop
|
# NB: firefox takes about 1hr to build on my 24-thread ryzen desktop
|
||||||
patches = (upstream.patches or []) ++ [
|
patches = (upstream.patches or []) ++ [
|
||||||
# see https://gitlab.com/librewolf-community/browser/source/-/blob/main/patches/sed-patches/allow-searchengines-non-esr.patch
|
# see https://gitlab.com/librewolf-community/browser/source/-/blob/main/patches/sed-patches/allow-searchengines-non-esr.patch
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
{ pkgs }:
|
{ symlinkJoin, fluffychat, makeWrapper }:
|
||||||
|
|
||||||
(pkgs.symlinkJoin {
|
(symlinkJoin {
|
||||||
name = "fluffychat-moby";
|
name = "fluffychat-moby";
|
||||||
paths = [ pkgs.fluffychat ];
|
paths = [ fluffychat ];
|
||||||
buildInputs = [ pkgs.makeWrapper ];
|
buildInputs = [ makeWrapper ];
|
||||||
|
|
||||||
# ordinary fluffychat on moby displays blank window;
|
# ordinary fluffychat on moby displays blank window;
|
||||||
# > Failed to start Flutter renderer: Unable to create a GL context
|
# > Failed to start Flutter renderer: Unable to create a GL context
|
||||||
|
18
pkgs/fractal-latest/default.nix
Normal file
18
pkgs/fractal-latest/default.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{ fractal-next, fetchFromGitLab, rustPlatform }:
|
||||||
|
|
||||||
|
(fractal-next.overrideAttrs (prev: rec {
|
||||||
|
version = "20221220";
|
||||||
|
src = fetchFromGitLab {
|
||||||
|
domain = "gitlab.gnome.org";
|
||||||
|
owner = "GNOME";
|
||||||
|
repo = "fractal";
|
||||||
|
rev = "d394badd0bf36c43026e01dbeb1cd7f881cf3440";
|
||||||
|
hash = "sha256-JgLrDrMLEh7302tZ3NOJ12dCMiSxGgecaUjcuDPcGSg=";
|
||||||
|
};
|
||||||
|
patches = [];
|
||||||
|
postPatch = "";
|
||||||
|
cargoDeps = rustPlatform.fetchCargoTarball {
|
||||||
|
inherit src;
|
||||||
|
hash = "sha256-hHsMcU6s42yKn2+LkWraLDhnmWNY72dL2cJoy6uoOKI=";
|
||||||
|
};
|
||||||
|
}))
|
@@ -1,15 +1,20 @@
|
|||||||
{ pkgs, lib, ... }:
|
{ fuse, gocryptfs, util-linux, lib }:
|
||||||
|
|
||||||
(pkgs.gocryptfs.overrideAttrs (upstream: {
|
(gocryptfs.overrideAttrs (upstream: {
|
||||||
# XXX `su colin` hangs when pam_mount tries to mount a gocryptfs system
|
# XXX `su colin` hangs when pam_mount tries to mount a gocryptfs system
|
||||||
# unless `logger` (util-linux) is accessible from gocryptfs.
|
# unless `logger` (util-linux) is accessible from gocryptfs.
|
||||||
# this is surprising: the code LOOKS like it's meant to handle logging failures.
|
# this is surprising: the code LOOKS like it's meant to handle logging failures.
|
||||||
# propagating util-linux through either `environment.systemPackages` or `security.pam.mount.additionalSearchPaths` DOES NOT WORK.
|
# propagating util-linux through either `environment.systemPackages` or `security.pam.mount.additionalSearchPaths` DOES NOT WORK.
|
||||||
#
|
#
|
||||||
# TODO: see about upstreaming this
|
# TODO: see about upstreaming this
|
||||||
|
#
|
||||||
|
# additionally, we need /run/wrappers/bin EXPLICITLY in PATH, for when we run not as root.
|
||||||
|
# but we want to keep `fuse` for when we ARE running as root -- particularly during an activation script BEFORE the wrappers exist.
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
wrapProgram $out/bin/gocryptfs \
|
wrapProgram $out/bin/gocryptfs \
|
||||||
--suffix PATH : ${lib.makeBinPath [ pkgs.fuse pkgs.util-linux ]}
|
--suffix PATH : ${lib.makeBinPath [ util-linux ]} \
|
||||||
|
--suffix PATH : /run/wrappers/bin \
|
||||||
|
--suffix PATH : ${lib.makeBinPath [ fuse ]}
|
||||||
ln -s $out/bin/gocryptfs $out/bin/mount.fuse.gocryptfs
|
ln -s $out/bin/gocryptfs $out/bin/mount.fuse.gocryptfs
|
||||||
'';
|
'';
|
||||||
}))
|
}))
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
{ pkgs
|
{ makeWrapper
|
||||||
|
, gpodder
|
||||||
|
, symlinkJoin
|
||||||
, writeShellScript
|
, writeShellScript
|
||||||
, config
|
, config
|
||||||
}:
|
}:
|
||||||
|
|
||||||
(pkgs.symlinkJoin {
|
(symlinkJoin {
|
||||||
name = "gpodder-configured";
|
name = "gpodder-configured";
|
||||||
paths = [ pkgs.gpodder ];
|
paths = [ gpodder ];
|
||||||
buildInputs = [ pkgs.makeWrapper ];
|
buildInputs = [ makeWrapper ];
|
||||||
|
|
||||||
# gpodder keeps all its feeds in a sqlite3 database.
|
# gpodder keeps all its feeds in a sqlite3 database.
|
||||||
# we can configure the feeds externally by wrapping gpodder and just instructing it to import
|
# we can configure the feeds externally by wrapping gpodder and just instructing it to import
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{ pkgs }:
|
{ jackett }:
|
||||||
|
|
||||||
(pkgs.jackett.overrideAttrs (upstream: {
|
(jackett.overrideAttrs (upstream: {
|
||||||
# 2022-07-29: check phase segfaults on arm (with or without my patches)
|
# 2022-07-29: check phase segfaults on arm (with or without my patches)
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
patches = (upstream.patches or []) ++ [
|
patches = (upstream.patches or []) ++ [
|
||||||
|
@@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
buildLinux (args // rec {
|
let
|
||||||
version = "6.0.2";
|
base = "6.1.0";
|
||||||
|
# set to empty if not a release candidate
|
||||||
|
rc = "-rc7";
|
||||||
|
in buildLinux (args // rec {
|
||||||
|
version = base + rc;
|
||||||
|
|
||||||
# modDirVersion needs to be x.y.z, will automatically add .0 if needed
|
# modDirVersion needs to be x.y.z, will automatically add .0 if needed
|
||||||
modDirVersion = if (modDirVersionArg == null) then concatStringsSep "." (take 3 (splitVersion "${version}.0")) else modDirVersionArg;
|
modDirVersion = if (modDirVersionArg == null) then concatStringsSep "." (take 3 (splitVersion "${version}.0")) + rc else modDirVersionArg;
|
||||||
|
|
||||||
# branchVersion needs to be x.y
|
# branchVersion needs to be x.y
|
||||||
extraMeta.branch = versions.majorMinor version;
|
extraMeta.branch = versions.majorMinor version;
|
||||||
@@ -14,8 +18,7 @@ buildLinux (args // rec {
|
|||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "megous";
|
owner = "megous";
|
||||||
repo = "linux";
|
repo = "linux";
|
||||||
# branch: orange-pi-6.0
|
rev = "orange-pi-6.1-20221128-1027";
|
||||||
rev = "2683672a2052ffda995bb987fa62a1abe8424ef4";
|
hash = "sha256-kEujs4v5rPHPYy4YLyEWHa1Bu0sxoXLgSvmOH9QPWos=";
|
||||||
hash = "sha256-hL/SbLgaTk/CqFLFrAK/OV9/OS20O42zJvSScsvWBQk=";
|
|
||||||
};
|
};
|
||||||
} // (args.argsOverride or { }))
|
} // (args.argsOverride or { }))
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
{ pkgs, fetchFromGitHub, ... }:
|
|
||||||
|
|
||||||
# buildVimPluginFrom2Nix {
|
|
||||||
pkgs.vimUtils.buildVimPlugin {
|
|
||||||
pname = "nabla";
|
|
||||||
version = "2022-08-17";
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "jbyuki";
|
|
||||||
repo = "nabla.nvim";
|
|
||||||
rev = "5379635d71b9877eaa4df822e8a2a5c575d808b0";
|
|
||||||
sha256 = "sha256-1VabgTnOSsfdhmHnfXl/h9djgNV3Gqro5VOr8ZbUlWw=";
|
|
||||||
};
|
|
||||||
meta.homepage = "https://github.com/jbyuki/nabla.nvim/";
|
|
||||||
}
|
|
@@ -3,14 +3,14 @@
|
|||||||
sane-scripts = prev.callPackage ./sane-scripts { };
|
sane-scripts = prev.callPackage ./sane-scripts { };
|
||||||
tow-boot-pinephone = prev.callPackage ./tow-boot-pinephone { };
|
tow-boot-pinephone = prev.callPackage ./tow-boot-pinephone { };
|
||||||
tow-boot-rpi4 = prev.callPackage ./tow-boot-rpi4 { };
|
tow-boot-rpi4 = prev.callPackage ./tow-boot-rpi4 { };
|
||||||
bootpart-uefi-x86_64 = prev.callPackage ./bootpart-uefi-x86_64 { pkgs = prev; };
|
bootpart-uefi-x86_64 = prev.callPackage ./bootpart-uefi-x86_64 { };
|
||||||
bootpart-tow-boot-rpi-aarch64 = prev.callPackage ./bootpart-tow-boot-rpi-aarch64 {
|
bootpart-tow-boot-rpi-aarch64 = prev.callPackage ./bootpart-tow-boot-rpi-aarch64 {
|
||||||
# not sure why i can't just do pkgs = next here
|
# not sure why i can't just do `next.callPackage` instead
|
||||||
pkgs = prev // { inherit tow-boot-rpi4; };
|
inherit tow-boot-rpi4;
|
||||||
};
|
};
|
||||||
bootpart-u-boot-rpi-aarch64 = prev.callPackage ./bootpart-u-boot-rpi-aarch64 {
|
bootpart-u-boot-rpi-aarch64 = prev.callPackage ./bootpart-u-boot-rpi-aarch64 {
|
||||||
# not sure why i can't just do pkgs = next here
|
# not sure why i can't just do `next.callPackage` instead
|
||||||
pkgs = prev // { inherit ubootRaspberryPi4_64bit; };
|
inherit ubootRaspberryPi4_64bit;
|
||||||
};
|
};
|
||||||
rtl8723cs-firmware = prev.callPackage ./rtl8723cs-firmware { };
|
rtl8723cs-firmware = prev.callPackage ./rtl8723cs-firmware { };
|
||||||
linux-megous = prev.callPackage ./linux-megous {
|
linux-megous = prev.callPackage ./linux-megous {
|
||||||
@@ -20,31 +20,36 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sublime-music-mobile = prev.callPackage ./sublime-music-mobile { };
|
||||||
|
|
||||||
#### customized packages
|
#### customized packages
|
||||||
fluffychat-moby = prev.callPackage ./fluffychat-moby { pkgs = prev; };
|
fluffychat-moby = prev.callPackage ./fluffychat-moby { };
|
||||||
gpodder-configured = prev.callPackage ./gpodder-configured { pkgs = prev; };
|
gpodder-configured = prev.callPackage ./gpodder-configured { };
|
||||||
# nixos-unstable pleroma is too far out-of-date for our db
|
# nixos-unstable pleroma is too far out-of-date for our db
|
||||||
pleroma = prev.callPackage ./pleroma { };
|
pleroma = prev.callPackage ./pleroma { };
|
||||||
# jackett doesn't allow customization of the bind address: this will probably always be here.
|
# jackett doesn't allow customization of the bind address: this will probably always be here.
|
||||||
jackett = prev.callPackage ./jackett { pkgs = prev; };
|
jackett = prev.callPackage ./jackett { inherit (prev) jackett; };
|
||||||
# mozilla keeps nerfing itself and removing configuration options
|
# mozilla keeps nerfing itself and removing configuration options
|
||||||
firefox-unwrapped = prev.callPackage ./firefox-unwrapped { pkgs = prev; };
|
firefox-unwrapped = prev.callPackage ./firefox-unwrapped { };
|
||||||
|
|
||||||
# patch rpi uboot with something that fixes USB HDD boot
|
# patch rpi uboot with something that fixes USB HDD boot
|
||||||
ubootRaspberryPi4_64bit = prev.callPackage ./ubootRaspberryPi4_64bit { pkgs = prev; };
|
ubootRaspberryPi4_64bit = prev.callPackage ./ubootRaspberryPi4_64bit { };
|
||||||
|
|
||||||
gocryptfs = prev.callPackage ./gocryptfs { pkgs = prev; };
|
gocryptfs = prev.callPackage ./gocryptfs { inherit (prev) gocryptfs; };
|
||||||
|
|
||||||
browserpass = prev.callPackage ./browserpass { pkgs = prev; inherit sane-scripts; };
|
browserpass = prev.callPackage ./browserpass { inherit (prev) browserpass; inherit sane-scripts; };
|
||||||
|
|
||||||
|
fractal-latest = prev.callPackage ./fractal-latest { };
|
||||||
|
|
||||||
#### TEMPORARY: PACKAGES WAITING TO BE UPSTREAMED
|
#### TEMPORARY: PACKAGES WAITING TO BE UPSTREAMED
|
||||||
kaiteki = prev.callPackage ./kaiteki { };
|
kaiteki = prev.callPackage ./kaiteki { };
|
||||||
lightdm-mobile-greeter = prev.callPackage ./lightdm-mobile-greeter { pkgs = next; };
|
lightdm-mobile-greeter = prev.callPackage ./lightdm-mobile-greeter { };
|
||||||
browserpass-extension = prev.callPackage ./browserpass-extension { };
|
browserpass-extension = prev.callPackage ./browserpass-extension { };
|
||||||
gopass-native-messaging-host = prev.callPackage ./gopass-native-messaging-host { };
|
gopass-native-messaging-host = prev.callPackage ./gopass-native-messaging-host { };
|
||||||
tokodon = prev.libsForQt5.callPackage ./tokodon { };
|
tokodon = prev.libsForQt5.callPackage ./tokodon { };
|
||||||
|
signaldctl = prev.callPackage ./signaldctl { };
|
||||||
|
splatmoji = prev.callPackage ./splatmoji { };
|
||||||
|
# trust-dns = prev.callPackage ./trust-dns { };
|
||||||
# kaiteki = prev.kaiteki;
|
# kaiteki = prev.kaiteki;
|
||||||
# TODO: upstream, or delete nabla
|
|
||||||
nabla = prev.callPackage ./nabla { };
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -18,24 +18,34 @@ resholve.mkDerivation {
|
|||||||
scripts = [ "bin/*" ];
|
scripts = [ "bin/*" ];
|
||||||
interpreter = "${pkgs.bash}/bin/bash";
|
interpreter = "${pkgs.bash}/bin/bash";
|
||||||
inputs = with pkgs; [
|
inputs = with pkgs; [
|
||||||
coreutils
|
# string is interpreted as relative path from @OUT@.
|
||||||
|
# this lets our scripts reference eachother.
|
||||||
|
# see: <https://github.com/abathur/resholve/issues/26>
|
||||||
|
"bin"
|
||||||
|
coreutils-full
|
||||||
curl
|
curl
|
||||||
duplicity
|
duplicity
|
||||||
file
|
file
|
||||||
findutils
|
findutils
|
||||||
|
git
|
||||||
gnugrep
|
gnugrep
|
||||||
|
gnused
|
||||||
gocryptfs
|
gocryptfs
|
||||||
ifuse
|
ifuse
|
||||||
inetutils
|
inetutils
|
||||||
inotify-tools
|
inotify-tools
|
||||||
|
iwd
|
||||||
|
jq
|
||||||
ncurses
|
ncurses
|
||||||
oath-toolkit
|
oath-toolkit
|
||||||
openssh
|
openssh
|
||||||
|
openssl
|
||||||
rmlint
|
rmlint
|
||||||
rsync
|
rsync
|
||||||
ssh-to-age
|
ssh-to-age
|
||||||
sops
|
sops
|
||||||
sudo
|
sudo
|
||||||
|
systemd
|
||||||
util-linux
|
util-linux
|
||||||
which
|
which
|
||||||
];
|
];
|
||||||
@@ -45,37 +55,52 @@ resholve.mkDerivation {
|
|||||||
"/tmp/rmlint.sh" = true;
|
"/tmp/rmlint.sh" = true;
|
||||||
# intentionally escapes (into user code)
|
# intentionally escapes (into user code)
|
||||||
"$external_cmd" = true;
|
"$external_cmd" = true;
|
||||||
|
"$maybe_sudo" = true;
|
||||||
};
|
};
|
||||||
fake = {
|
fake = {
|
||||||
external = [
|
external = [
|
||||||
# https://github.com/abathur/resholve/issues/29
|
# https://github.com/abathur/resholve/issues/29
|
||||||
"umount"
|
# "umount"
|
||||||
|
# "/run/wrappers/bin/sudo"
|
||||||
"sudo"
|
"sudo"
|
||||||
|
|
||||||
# these are used internally; probably a better fix
|
|
||||||
"sane-mount-servo"
|
|
||||||
"sane-private-unlock"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
fix = {
|
||||||
|
# this replaces umount with the non-setuid-wrapper umount.
|
||||||
|
# not sure if/where that lack of suid causes problems.
|
||||||
|
umount = true;
|
||||||
|
};
|
||||||
|
prologue = "bin/sane-resholve-prologue";
|
||||||
|
|
||||||
# list of programs which *can* or *cannot* exec their arguments
|
# list of programs which *can* or *cannot* exec their arguments
|
||||||
execer = with pkgs; [
|
execer = with pkgs; [
|
||||||
"cannot:${duplicity}/bin/duplicity"
|
"cannot:${duplicity}/bin/duplicity"
|
||||||
|
"cannot:${git}/bin/git"
|
||||||
"cannot:${gocryptfs}/bin/gocryptfs"
|
"cannot:${gocryptfs}/bin/gocryptfs"
|
||||||
"cannot:${ifuse}/bin/ifuse"
|
"cannot:${ifuse}/bin/ifuse"
|
||||||
|
"cannot:${iwd}/bin/iwctl"
|
||||||
"cannot:${oath-toolkit}/bin/oathtool"
|
"cannot:${oath-toolkit}/bin/oathtool"
|
||||||
"cannot:${openssh}/bin/ssh-keygen"
|
"cannot:${openssh}/bin/ssh-keygen"
|
||||||
"cannot:${rmlint}/bin/rmlint"
|
"cannot:${rmlint}/bin/rmlint"
|
||||||
"cannot:${rsync}/bin/rsync"
|
"cannot:${rsync}/bin/rsync"
|
||||||
"cannot:${sops}/bin/sops"
|
"cannot:${sops}/bin/sops"
|
||||||
"cannot:${ssh-to-age}/bin/ssh-to-age"
|
"cannot:${ssh-to-age}/bin/ssh-to-age"
|
||||||
|
"cannot:${systemd}/bin/systemctl"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
patchPhase = ''
|
||||||
|
# remove python scripts
|
||||||
|
# TODO: figure out how to make resholve process only shell scripts
|
||||||
|
rm sane-bt-search
|
||||||
|
rm sane-date-math
|
||||||
|
rm sane-reclaim-boot-space
|
||||||
|
'';
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p "$out/bin"
|
mkdir -p $out/bin
|
||||||
cp -R * "$out"/bin/
|
cp -R * $out/bin/
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
|
71
pkgs/sane-scripts/src/sane-bt-search
Executable file
71
pkgs/sane-scripts/src/sane-bt-search
Executable file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
usage: sane-bt-search <query_string>
|
||||||
|
|
||||||
|
searches Jackett for torrent files matching the title.
|
||||||
|
returns select results and magnet links
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
import natsort
|
||||||
|
import requests
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
SERVICE = "https://jackett.uninsane.org"
|
||||||
|
ENDPOINTS = dict(
|
||||||
|
results="api/v2.0/indexers/all/results"
|
||||||
|
)
|
||||||
|
|
||||||
|
@dataclass(eq=True, order=True)
|
||||||
|
class Torrent:
|
||||||
|
seeders: int
|
||||||
|
pub_date: datetime
|
||||||
|
title: str
|
||||||
|
magnet: str
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.seeders}[S]\t{self.pub_date}\t{self.title}\t{self.magnet}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(d: dict) -> 'Torrent':
|
||||||
|
seeders = d.get("Seeders")
|
||||||
|
pub_date = d.get("PublishDate")
|
||||||
|
title = d.get("Title")
|
||||||
|
magnet = d.get("MagnetUri")
|
||||||
|
if seeders is not None and pub_date is not None and title is not None and magnet is not None:
|
||||||
|
pub_date = datetime.fromisoformat(pub_date)
|
||||||
|
return Torrent(seeders, pub_date, title, magnet)
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def __init__(self):
|
||||||
|
self.apikey = open("/run/secrets/jackett_apikey").read()
|
||||||
|
|
||||||
|
def api_call(self, method: str, params: dict) -> dict:
|
||||||
|
endpoint = ENDPOINTS[method]
|
||||||
|
url = f"{SERVICE}/{endpoint}"
|
||||||
|
params = params.copy()
|
||||||
|
params.update(apikey=self.apikey, _=str(int(time.time())))
|
||||||
|
print(url)
|
||||||
|
print(params)
|
||||||
|
resp = requests.get(url, params=params)
|
||||||
|
print(resp)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
def query(self, q: str) -> list:
|
||||||
|
torrents = []
|
||||||
|
api_res = self.api_call("results", dict(Query=q))
|
||||||
|
for r in api_res["Results"]:
|
||||||
|
t = Torrent.from_dict(r)
|
||||||
|
if t is not None:
|
||||||
|
torrents.append(t)
|
||||||
|
|
||||||
|
return sorted(torrents, reverse=True)
|
||||||
|
|
||||||
|
q = " ".join(sys.argv[1:])
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
res = client.query(q)
|
||||||
|
for r in res[:5]:
|
||||||
|
print(r)
|
347
pkgs/sane-scripts/src/sane-date-math
Executable file
347
pkgs/sane-scripts/src/sane-date-math
Executable file
@@ -0,0 +1,347 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# i just went overboard playing around with parsers, is all.
|
||||||
|
# use this like `./sane-date-math 'today - 5d'`
|
||||||
|
# of course, it handles parentheses and operator precedence/associativity, so you can do sillier things like
|
||||||
|
# `./sane-date-math ' today - (1+3 *4 - ((0)) ) *7d '`
|
||||||
|
|
||||||
|
|
||||||
|
import abc
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class Token:
|
||||||
|
def __init__(self, c: str):
|
||||||
|
self.c = c
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.c!r}"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.c
|
||||||
|
|
||||||
|
def __eq__(self, other: 'Token') -> bool:
|
||||||
|
return self.c == other.c
|
||||||
|
|
||||||
|
PLUS = Token('+')
|
||||||
|
MINUS = Token('-')
|
||||||
|
ASTERISK = Token('*')
|
||||||
|
SPACE = Token(' ')
|
||||||
|
OPEN_PAREN = Token('(')
|
||||||
|
CLOSE_PAREN = Token(')')
|
||||||
|
UNDERSCORE = Token('_')
|
||||||
|
DIGITS = [Token(c) for c in '0123456789']
|
||||||
|
ALPHA_LOWER = [Token(c) for c in 'abcdefghijklmnopqrstuvwxyz']
|
||||||
|
ALPHA_UPPER = [Token(t.c.upper()) for t in ALPHA_LOWER]
|
||||||
|
ALPHA = ALPHA_LOWER + ALPHA_UPPER
|
||||||
|
ALPHA_UNDER = ALPHA + [UNDERSCORE]
|
||||||
|
ALPHA_NUM_UNDER = ALPHA_UNDER + DIGITS
|
||||||
|
|
||||||
|
class ParserContext:
|
||||||
|
def feed(self, token: Token) -> 'ParserContext':
|
||||||
|
return None # can't ingest the token
|
||||||
|
|
||||||
|
def upgrade(self) -> 'ParserContext':
|
||||||
|
return None # no upgrade path
|
||||||
|
|
||||||
|
class Parser:
|
||||||
|
"""
|
||||||
|
LR parser.
|
||||||
|
keeps exactly one root item, and for each input token
|
||||||
|
feeds it to the root, possibly "upgrading" the root N times
|
||||||
|
before it's able to be fed.
|
||||||
|
"""
|
||||||
|
def __init__(self, root: ParserContext):
|
||||||
|
self.root = root
|
||||||
|
|
||||||
|
def feed(self, token: Token) -> bool:
|
||||||
|
new_root = self.root.feed(token)
|
||||||
|
if new_root is not None:
|
||||||
|
self.root = new_root
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# root can't directly accept this item.
|
||||||
|
# "upgrade" it and try again.
|
||||||
|
new_root = self.root.upgrade()
|
||||||
|
if new_root is None: return False
|
||||||
|
self.root = new_root
|
||||||
|
return self.feed(token)
|
||||||
|
|
||||||
|
def complete(self) -> ParserContext:
|
||||||
|
# upgrade the root as far as possible before returning
|
||||||
|
root = None
|
||||||
|
new_root = self.root
|
||||||
|
while new_root is not None:
|
||||||
|
root = new_root
|
||||||
|
new_root = root.upgrade()
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
class ReprParserContext(ParserContext):
|
||||||
|
""" helper that gives a good default repr to most contexts """
|
||||||
|
def __init__(self, items: list = None):
|
||||||
|
self.items = items if items is not None else []
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'{self.__class__.__name__}({self.items!r})'
|
||||||
|
|
||||||
|
|
||||||
|
class BaseContext(ReprParserContext):
|
||||||
|
""" empty context; initial state of the parser """
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
if token == SPACE:
|
||||||
|
return self
|
||||||
|
if token == OPEN_PAREN:
|
||||||
|
return ParenContext(BaseContext())
|
||||||
|
if token in DIGITS:
|
||||||
|
return IntegerContext([token])
|
||||||
|
if token in ALPHA_UNDER:
|
||||||
|
return IdentifierContext([token])
|
||||||
|
|
||||||
|
class IdentifierContext(ReprParserContext):
|
||||||
|
""" context is an identifier like `today` """
|
||||||
|
def __init__(self, tokens: list):
|
||||||
|
super().__init__(tokens)
|
||||||
|
self.tokens = tokens
|
||||||
|
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
if token in ALPHA_NUM_UNDER:
|
||||||
|
return IdentifierContext(self.tokens + [token])
|
||||||
|
|
||||||
|
def upgrade(self) -> ParserContext:
|
||||||
|
return StrongValueContext(self)
|
||||||
|
|
||||||
|
class IntegerContext(ReprParserContext):
|
||||||
|
""" context is an integer like `45` """
|
||||||
|
def __init__(self, tokens: list):
|
||||||
|
super().__init__(tokens)
|
||||||
|
self.tokens = tokens
|
||||||
|
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
if token in DIGITS:
|
||||||
|
return IntegerContext(self.tokens + [token])
|
||||||
|
if token == Token('d'):
|
||||||
|
return DurationContext(self)
|
||||||
|
|
||||||
|
def upgrade(self) -> ParserContext:
|
||||||
|
# can't continue the integer; it becomes a value
|
||||||
|
return StrongValueContext(self)
|
||||||
|
|
||||||
|
class DurationContext(ReprParserContext):
|
||||||
|
""" context is a duration like `14d` """
|
||||||
|
def __init__(self, value: IntegerContext):
|
||||||
|
super().__init__([value])
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def upgrade(self) -> ParserContext:
|
||||||
|
return StrongValueContext(self)
|
||||||
|
|
||||||
|
class BaseValueContext(ReprParserContext):
|
||||||
|
""" abstract base for types that can be used in compound expressions """
|
||||||
|
def __init__(self, value: ParserContext):
|
||||||
|
super().__init__([value])
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
if token == SPACE:
|
||||||
|
return self
|
||||||
|
|
||||||
|
class StrongValueContext(BaseValueContext):
|
||||||
|
"""
|
||||||
|
in the context of operators, a strong value is something which prefers
|
||||||
|
to not be grabbed by a lhs value.
|
||||||
|
|
||||||
|
so for example, strong values have the opportunity to initiate a multiply operation before the lhs closes an addition operation that this strong value is a part of
|
||||||
|
"""
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
if token == ASTERISK:
|
||||||
|
return BinaryOpContext(self, token, BaseContext())
|
||||||
|
return super().feed(token)
|
||||||
|
|
||||||
|
def upgrade(self) -> ParserContext:
|
||||||
|
return WeakValueContext(self.value)
|
||||||
|
|
||||||
|
class WeakValueContext(BaseValueContext):
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
if token == PLUS:
|
||||||
|
return BinaryOpContext(self, token, BaseContext())
|
||||||
|
if token == MINUS:
|
||||||
|
return BinaryOpContext(self, token, BaseContext())
|
||||||
|
|
||||||
|
return super().feed(token)
|
||||||
|
|
||||||
|
class BinaryOpContext(ReprParserContext):
|
||||||
|
""" context for a binary operation. the LHS and operator are parsed, but the rhs may not yet contain a value """
|
||||||
|
def __init__(self, lhs: BaseValueContext, oper: Token, rhs: ParserContext):
|
||||||
|
super().__init__([lhs, oper, rhs])
|
||||||
|
self.lhs = lhs
|
||||||
|
self.oper = oper
|
||||||
|
self.rhs = rhs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def precedence_class(self) -> type:
|
||||||
|
if self.oper in [PLUS, MINUS]:
|
||||||
|
return WeakValueContext
|
||||||
|
if self.oper == ASTERISK:
|
||||||
|
return StrongValueContext
|
||||||
|
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
new_rhs = self.rhs.feed(token)
|
||||||
|
if new_rhs is not None:
|
||||||
|
return BinaryOpContext(self.lhs, self.oper, new_rhs)
|
||||||
|
|
||||||
|
def upgrade(self) -> ParserContext:
|
||||||
|
new_rhs = self.rhs.upgrade()
|
||||||
|
if new_rhs is None: return None
|
||||||
|
|
||||||
|
# upgrade self once the rhs has reach the required precedence compatible with this operator
|
||||||
|
new_self = BinaryOpContext(self.lhs, self.oper, new_rhs)
|
||||||
|
if isinstance(new_rhs, self.precedence_class):
|
||||||
|
return StrongValueContext(self) # close the operation
|
||||||
|
|
||||||
|
return new_self
|
||||||
|
|
||||||
|
class ParenContext(ReprParserContext):
|
||||||
|
""" context for a value contained within parentheses """
|
||||||
|
def __init__(self, inner: ParserContext):
|
||||||
|
super().__init__([inner])
|
||||||
|
self.inner = inner
|
||||||
|
|
||||||
|
def feed(self, token: Token) -> ParserContext:
|
||||||
|
new_inner = self.inner.feed(token)
|
||||||
|
if new_inner is not None:
|
||||||
|
return ParenContext(new_inner)
|
||||||
|
|
||||||
|
if token == CLOSE_PAREN and isinstance(self.inner, WeakValueContext):
|
||||||
|
return StrongValueContext(self)
|
||||||
|
|
||||||
|
def upgrade(self) -> ParserContext:
|
||||||
|
new_inner = self.inner.upgrade()
|
||||||
|
if new_inner is not None:
|
||||||
|
return ParenContext(new_inner)
|
||||||
|
|
||||||
|
|
||||||
|
## AstItems are produced from a ParserContext input
|
||||||
|
## ParserContext parse outputs are translated into `AstItem`s before evaluation
|
||||||
|
## so that we can operate on a higher-level tree that directly encodes native values like integers
|
||||||
|
|
||||||
|
class AstItem(metaclass=abc.ABCMeta):
|
||||||
|
@abc.abstractmethod
|
||||||
|
def eval(self, context: dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode_item(p: ParserContext) -> 'AstItem':
|
||||||
|
if isinstance(p, IntegerContext):
|
||||||
|
return Literal(AstItem.decode_integer(p))
|
||||||
|
if isinstance(p, DurationContext):
|
||||||
|
return Literal(timedelta(AstItem.decode_integer(p.value)))
|
||||||
|
if isinstance(p, IdentifierContext):
|
||||||
|
return Variable(AstItem.decode_identifier(p))
|
||||||
|
if isinstance(p, BaseValueContext):
|
||||||
|
return AstItem.decode_item(p.value)
|
||||||
|
if isinstance(p, BinaryOpContext):
|
||||||
|
return AstItem.decode_bin_op(
|
||||||
|
p.oper.c,
|
||||||
|
AstItem.decode_item(p.lhs),
|
||||||
|
AstItem.decode_item(p.rhs)
|
||||||
|
)
|
||||||
|
if isinstance(p, ParenContext):
|
||||||
|
return AstItem.decode_item(p.inner)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode_integer(p: IntegerContext) -> int:
|
||||||
|
return int(''.join(t.c for t in p.tokens))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode_identifier(p: IdentifierContext) -> str:
|
||||||
|
return ''.join(t.c for t in p.tokens)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode_bin_op(ty: str, lhs: 'AstItem', rhs: 'AstItem') -> 'BinaryOp':
|
||||||
|
if ty == '+':
|
||||||
|
return AddOp(lhs, rhs)
|
||||||
|
if ty == '-':
|
||||||
|
return SubOp(lhs, rhs)
|
||||||
|
if ty == '*':
|
||||||
|
return MulOp(lhs, rhs)
|
||||||
|
|
||||||
|
class Literal(AstItem):
|
||||||
|
def __init__(self, v):
|
||||||
|
self.v = v
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return str(self.v)
|
||||||
|
|
||||||
|
def eval(self, context: dict):
|
||||||
|
return self.v
|
||||||
|
|
||||||
|
class Variable(AstItem):
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def eval(self, context: dict):
|
||||||
|
return context[self.name]
|
||||||
|
|
||||||
|
class BinaryOp(AstItem):
|
||||||
|
def __init__(self, lhs, rhs):
|
||||||
|
self.lhs = lhs
|
||||||
|
self.rhs = rhs
|
||||||
|
|
||||||
|
class AddOp(BinaryOp):
|
||||||
|
def __str__(self):
|
||||||
|
return f"({self.lhs} + {self.rhs})"
|
||||||
|
|
||||||
|
def eval(self, context: dict):
|
||||||
|
return self.lhs.eval(context) + self.rhs.eval(context)
|
||||||
|
|
||||||
|
class SubOp(BinaryOp):
|
||||||
|
def __str__(self):
|
||||||
|
return f"({self.lhs} - {self.rhs})"
|
||||||
|
|
||||||
|
def eval(self, context: dict):
|
||||||
|
return self.lhs.eval(context) - self.rhs.eval(context)
|
||||||
|
|
||||||
|
class MulOp(BinaryOp):
|
||||||
|
def __str__(self):
|
||||||
|
return f"({self.lhs} * {self.rhs})"
|
||||||
|
|
||||||
|
def eval(self, context: dict):
|
||||||
|
return self.lhs.eval(context) * self.rhs.eval(context)
|
||||||
|
|
||||||
|
|
||||||
|
## toplevel routine. tokenize -> parse -> decode to AST -> evaluate
|
||||||
|
|
||||||
|
def tokenize(stream: str) -> list:
|
||||||
|
return [Token(char) for char in stream]
|
||||||
|
|
||||||
|
def parse(tokens: list) -> ParserContext:
|
||||||
|
parser = Parser(BaseContext())
|
||||||
|
for i, t in enumerate(tokens):
|
||||||
|
result = parser.feed(t)
|
||||||
|
# print(f"i={i}; t={t}; state: {ctx!r}")
|
||||||
|
assert result, f"unexpected token '{t}' at {i}; state: {parser.complete()!r}"
|
||||||
|
|
||||||
|
return parser.complete()
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate(expr: str) -> object:
|
||||||
|
tok = tokenize(expr)
|
||||||
|
parse_tree = parse(tok)
|
||||||
|
print(parse_tree)
|
||||||
|
ast = AstItem.decode_item(parse_tree)
|
||||||
|
print(ast)
|
||||||
|
|
||||||
|
env = dict(
|
||||||
|
today=datetime.now()
|
||||||
|
)
|
||||||
|
return ast.eval(env)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
expr = " ".join(sys.argv[1:])
|
||||||
|
print(evaluate(expr))
|
||||||
|
|
14
pkgs/sane-scripts/src/sane-git-init
Executable file
14
pkgs/sane-scripts/src/sane-git-init
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# initialize a repository with each of my machines configured as remotes.
|
||||||
|
# it's assumed each machine stores the repo at the same fs path
|
||||||
|
|
||||||
|
path=$PWD
|
||||||
|
|
||||||
|
git init
|
||||||
|
git remote add desko "colin@desko:$path"
|
||||||
|
git remote add lappy "colin@lappy:$path"
|
||||||
|
git remote add moby "colin@moby:$path"
|
||||||
|
git remote add servo "colin@servo:$path"
|
3
pkgs/sane-scripts/src/sane-ip-check
Executable file
3
pkgs/sane-scripts/src/sane-ip-check
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
curl https://ipinfo.io/ip
|
||||||
|
echo
|
15
pkgs/sane-scripts/src/sane-ip-check-router-wan
Executable file
15
pkgs/sane-scripts/src/sane-ip-check-router-wan
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# query the WAN IP address OF MY ROUTER
|
||||||
|
# requires creds
|
||||||
|
passwd=$(sudo cat /run/secrets/router_passwd)
|
||||||
|
cookie=$(mktemp)
|
||||||
|
|
||||||
|
# authenticate
|
||||||
|
curl -s --insecure --cookie-jar $cookie \
|
||||||
|
--data "username=admin&password=$passwd" \
|
||||||
|
https://192.168.0.1
|
||||||
|
# query the WAN IP
|
||||||
|
curl -s --insecure --cookie $cookie \
|
||||||
|
-H "X-Requested-With: XMLHttpRequest" \
|
||||||
|
"https://192.168.0.1/cgi/cgi_action?Action=GetConnectionStatus" \
|
||||||
|
| jq -r .wan_status.ipaddr
|
20
pkgs/sane-scripts/src/sane-ip-reconnect
Executable file
20
pkgs/sane-scripts/src/sane-ip-reconnect
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# reconnect to best wifi network.
|
||||||
|
# lappy frequently DCs from the ideal network
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
sudo iwctl station wlan0 scan
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# get networks. remove control characters (colors), then leading info, then take the top-rated network
|
||||||
|
networks=$(iwctl station wlan0 get-networks rssi-dbms | sed 's/\[[0-9;]*m//g' | sed 's/ [ >]*/ /g' | sed 's/^ //' | tail -n +5)
|
||||||
|
|
||||||
|
strengths=$(echo "$networks" | grep -o '\-[0-9][0-9]* *$')
|
||||||
|
best_strength=$(echo "$strengths" | sort -h | tail -n 1)
|
||||||
|
best_line=$(echo "$networks" | grep -- "$best_strength$")
|
||||||
|
# network names could have spaces in them if someone's evil, so rather than `cut`, we trim the `psk` and `db` columnds
|
||||||
|
best_network=$(echo "$best_line" | sed 's/ [a-z][a-z]* -[0-9][0-9]* *$//g')
|
||||||
|
|
||||||
|
sudo iwctl station wlan0 connect "$best_network"
|
11
pkgs/sane-scripts/src/sane-private-do
Executable file
11
pkgs/sane-scripts/src/sane-private-do
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# unlock the ~/private store, run some command, and then re-lock the store
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
external_cmd=$@
|
||||||
|
|
||||||
|
sane-private-unlock
|
||||||
|
$external_cmd
|
||||||
|
exec sane-private-lock
|
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
# configure persistent, encrypted storage that is auto-mounted on login.
|
# mounts ~/private
|
||||||
# this is a one-time setup and user should log out/back in after running it.
|
|
||||||
|
|
||||||
mount=/home/colin/private
|
mount=/home/colin/private
|
||||||
cipher="/nix/persist$mount"
|
cipher="/nix/persist$mount"
|
||||||
|
@@ -1,3 +1,10 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
# copy some remote file(s) to the working directory, with sane defaults
|
# rsync, with sane defaults
|
||||||
rsync -arv --progress --append-verify "$@" .
|
# + verbosity
|
||||||
|
# + default to cwd as destination if none is provided
|
||||||
|
dest=
|
||||||
|
if (( $# <= 1 )); then
|
||||||
|
# rsync to the current directory by default
|
||||||
|
dest='.'
|
||||||
|
fi
|
||||||
|
rsync -arv --progress --append-verify "$@" $dest
|
||||||
|
194
pkgs/sane-scripts/src/sane-reclaim-boot-space
Executable file
194
pkgs/sane-scripts/src/sane-reclaim-boot-space
Executable file
@@ -0,0 +1,194 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
EXTLINUX_CONF = "/boot/extlinux/extlinux.conf"
|
||||||
|
|
||||||
|
class ConfItem:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ConfLine:
|
||||||
|
""" uninteresting line in the config """
|
||||||
|
def __init__(self, line: str):
|
||||||
|
self.line = line
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.line
|
||||||
|
|
||||||
|
class ConfEntry:
|
||||||
|
""" boot entry, with label/linux/etc """
|
||||||
|
menu = linux = initrd = append = fdtdir = None
|
||||||
|
def __init__(self, label: str):
|
||||||
|
self.label = label
|
||||||
|
|
||||||
|
def format_attr(self, attr_name: str) -> str:
|
||||||
|
attr_val = getattr(self, attr_name)
|
||||||
|
assert attr_val is not None, f"not set: {attr_name}"
|
||||||
|
return f"{attr_name.upper()} {attr_val}"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"""
|
||||||
|
{self.format_attr("label")}
|
||||||
|
{self.format_attr("menu")}
|
||||||
|
{self.format_attr("linux")}
|
||||||
|
{self.format_attr("initrd")}
|
||||||
|
{self.format_attr("append")}
|
||||||
|
{self.format_attr("fdtdir")}
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
def parse(self, line: str) -> None:
|
||||||
|
split_at = line.index(" ")
|
||||||
|
directive, contents = line[:split_at], line[1+split_at:]
|
||||||
|
self.setattr(directive.lower(), contents)
|
||||||
|
|
||||||
|
def setattr(self, directive: str, contents: str) -> None:
|
||||||
|
assert getattr(self, directive) is None, f"attr already set: {directive} = {contents!r}"
|
||||||
|
setattr(self, directive, contents)
|
||||||
|
|
||||||
|
|
||||||
|
class UseTracker:
|
||||||
|
def __init__(self):
|
||||||
|
self.linux = []
|
||||||
|
self.initrd = []
|
||||||
|
self.fdtdir = []
|
||||||
|
self.sizes = {} # item: str -> [num_using, bytes]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_items(entries: list) -> 'UseTracker':
|
||||||
|
me = UseTracker()
|
||||||
|
me.populate_from_fs()
|
||||||
|
for i in entries:
|
||||||
|
me.track(i)
|
||||||
|
return me
|
||||||
|
|
||||||
|
def populate_from_fs(self) -> None:
|
||||||
|
for entry in os.listdir("/boot/nixos"):
|
||||||
|
item = os.path.join("../nixos", entry)
|
||||||
|
self.sizes[item] = [0, self._get_size(item)]
|
||||||
|
|
||||||
|
def append_unique(self, list_: list, item: str) -> None:
|
||||||
|
if item not in list_:
|
||||||
|
list_.append(item)
|
||||||
|
self.sizes[item][0] += 1
|
||||||
|
|
||||||
|
def track(self, entry: ConfItem) -> None:
|
||||||
|
if isinstance(entry, ConfEntry):
|
||||||
|
self.append_unique(self.linux, entry.linux)
|
||||||
|
self.append_unique(self.initrd, entry.initrd)
|
||||||
|
self.append_unique(self.fdtdir, entry.fdtdir)
|
||||||
|
|
||||||
|
def get_use_count(self, item: str) -> int:
|
||||||
|
return self.sizes[item][0]
|
||||||
|
|
||||||
|
def get_size(self, item: str) -> int:
|
||||||
|
return self.sizes[item][1]
|
||||||
|
|
||||||
|
def used_size(self) -> int:
|
||||||
|
return sum(i[1] for i in self.sizes.values() if i[0] != 0)
|
||||||
|
|
||||||
|
def get_unused(self) -> list:
|
||||||
|
return [i for (i, v) in self.sizes.items() if v[0] == 0]
|
||||||
|
|
||||||
|
def unused_size(self) -> int:
|
||||||
|
return sum(self.get_size(i) for i in self.get_unused())
|
||||||
|
|
||||||
|
def _get_size(self, item: str) -> int:
|
||||||
|
path = os.path.join("/boot/extlinux", item)
|
||||||
|
du_output = subprocess.check_output(["du", "-b", "-c", path], text=True).strip()
|
||||||
|
last = du_output.split("\n")[-1]
|
||||||
|
size, label = last.split("\t")
|
||||||
|
assert label == "total", f"unexpected du output: {last}"
|
||||||
|
return int(size)
|
||||||
|
|
||||||
|
def print_use_by_cat(tracker: UseTracker, label: str) -> None:
|
||||||
|
items = getattr(tracker, label)
|
||||||
|
formatted_items = []
|
||||||
|
for item in items:
|
||||||
|
count, size = tracker.get_use_count(item), tracker.get_size(item)
|
||||||
|
formatted_items.append(f"\n {item}\n {count}x {size}")
|
||||||
|
print(f" {label}:{''.join(formatted_items)}")
|
||||||
|
|
||||||
|
def print_tracker_use(tracker: UseTracker) -> None:
|
||||||
|
print("in use:")
|
||||||
|
print_use_by_cat(tracker, "linux")
|
||||||
|
print_use_by_cat(tracker, "initrd")
|
||||||
|
print_use_by_cat(tracker, "fdtdir")
|
||||||
|
print("unused:")
|
||||||
|
for i in tracker.get_unused():
|
||||||
|
print(f" {i}\n {tracker.get_size(i)}")
|
||||||
|
print(f"used space: {tracker.used_size()}")
|
||||||
|
|
||||||
|
def delete_unused_from_disk(tracker: UseTracker) -> None:
|
||||||
|
for f in tracker.get_unused():
|
||||||
|
path = os.path.join("/boot/extlinux", f)
|
||||||
|
cmd = ["rm", "-r", "-f", path]
|
||||||
|
print(" ".join(cmd))
|
||||||
|
subprocess.check_output(cmd)
|
||||||
|
|
||||||
|
def parse_extlinux(contents: str) -> list:
|
||||||
|
items = []
|
||||||
|
active_entry = None
|
||||||
|
for line in contents.split("\n"):
|
||||||
|
if line.startswith("#") or line == "" or line.startswith("DEFAULT ") or line.startswith("MENU ") or line.startswith("TIMEOUT "):
|
||||||
|
items.append(ConfLine(line))
|
||||||
|
elif line.startswith("LABEL "):
|
||||||
|
items.append(ConfEntry(line[len("LABEL "):]))
|
||||||
|
elif line.startswith(" "):
|
||||||
|
items[-1].parse(line[2:])
|
||||||
|
else:
|
||||||
|
assert False, f"unknown directive {line!r}"
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
def write_extlinux(contents: str) -> None:
|
||||||
|
with open(EXTLINUX_CONF, "r+") as new:
|
||||||
|
# backup file
|
||||||
|
with open("./extlinux.conf.back", "w") as back:
|
||||||
|
back.write(new.read())
|
||||||
|
|
||||||
|
new.seek(0)
|
||||||
|
new.write(new_extlinux)
|
||||||
|
new.truncate()
|
||||||
|
|
||||||
|
def dump_items(items: list) -> str:
|
||||||
|
return "\n".join(str(i) for i in items)
|
||||||
|
|
||||||
|
def prompt_continue() -> None:
|
||||||
|
if input("continue? [y/N] ").lower() != "y":
|
||||||
|
print("aborting")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
orig_extlinux = open(EXTLINUX_CONF, "r").read()
|
||||||
|
items = parse_extlinux(orig_extlinux)
|
||||||
|
tracker = UseTracker.from_items(items)
|
||||||
|
print_tracker_use(tracker)
|
||||||
|
print()
|
||||||
|
|
||||||
|
if tracker.get_unused():
|
||||||
|
print(f"recommended to delete unused items from disk to save {tracker.unused_size()}b")
|
||||||
|
prompt_continue()
|
||||||
|
else:
|
||||||
|
orig_tracker = tracker
|
||||||
|
rmcount = 0
|
||||||
|
while tracker.used_size() == orig_tracker.used_size():
|
||||||
|
item = items.pop()
|
||||||
|
rmcount += isinstance(item, ConfEntry)
|
||||||
|
tracker = UseTracker.from_items(items)
|
||||||
|
|
||||||
|
orig_size = orig_tracker.used_size()
|
||||||
|
new_size = tracker.used_size()
|
||||||
|
print(f"recommended to delete {rmcount} oldest entries to save {orig_size - new_size}b")
|
||||||
|
print_tracker_use(tracker)
|
||||||
|
prompt_continue()
|
||||||
|
print()
|
||||||
|
|
||||||
|
new_extlinux = dump_items(items)
|
||||||
|
print(f"new contents:\n{new_extlinux}")
|
||||||
|
prompt_continue()
|
||||||
|
|
||||||
|
write_extlinux(new_extlinux)
|
||||||
|
|
||||||
|
delete_unused_from_disk(tracker)
|
3
pkgs/sane-scripts/src/sane-resholve-prologue
Normal file
3
pkgs/sane-scripts/src/sane-resholve-prologue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# sudo, umount, others, are located here.
|
||||||
|
# resholve can't inline their path, since it's not a store path
|
||||||
|
PATH=$PATH:/run/wrappers/bin
|
15
pkgs/sane-scripts/src/sane-ssl-dump
Executable file
15
pkgs/sane-scripts/src/sane-ssl-dump
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# dump info about the provided SSL certificate
|
||||||
|
cert="$1"
|
||||||
|
maybe_sudo=
|
||||||
|
|
||||||
|
if ! (test -e "$cert")
|
||||||
|
then
|
||||||
|
cert="/var/lib/acme/${cert}/full.pem"
|
||||||
|
maybe_sudo=sudo
|
||||||
|
fi
|
||||||
|
|
||||||
|
# $maybe_sudo openssl x509 -in "$file" -text
|
||||||
|
$maybe_sudo openssl crl2pkcs7 -nocrl -certfile "$cert" | openssl pkcs7 -print_certs -text -noout
|
||||||
|
|
@@ -8,4 +8,4 @@ sudo systemctl stop nginx
|
|||||||
sudo systemctl stop postgresql
|
sudo systemctl stop postgresql
|
||||||
sudo systemctl stop duplicity.timer
|
sudo systemctl stop duplicity.timer
|
||||||
sudo systemctl stop duplicity
|
sudo systemctl stop duplicity
|
||||||
sudo systemctl stop wg0veth wireguard-wg0
|
sudo systemctl stop wireguard-wg0
|
||||||
|
@@ -2,15 +2,18 @@
|
|||||||
|
|
||||||
# first arg should be the region, e.g. `us` or `ukr`
|
# first arg should be the region, e.g. `us` or `ukr`
|
||||||
|
|
||||||
case $1 in
|
vpns=$(systemctl list-unit-files | grep wg-quick-ovpnd- | cut -f 1 -d ' ' | sed s'/^wg-quick-ovpnd-\([a-zA-Z-]*\)\.service$/\1/g')
|
||||||
ukr)
|
|
||||||
iface=wg-quick-ovpnd-ukr;;
|
|
||||||
us)
|
|
||||||
iface=wg-quick-ovpnd-us;;
|
|
||||||
*)
|
|
||||||
echo "invalid vpn name '$1'"; exit 1;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo vpn: $(curl https://ipinfo.io/ip)
|
if ! [ $(echo "$vpns" | grep "^$1$") ]
|
||||||
|
then
|
||||||
|
echo "invalid vpn name '$1'"
|
||||||
|
echo "choices:"
|
||||||
|
echo "$vpns"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
iface=wg-quick-ovpnd-$1.service
|
||||||
|
|
||||||
|
echo vpn: $(sane-ip-check)
|
||||||
sudo systemctl stop $iface
|
sudo systemctl stop $iface
|
||||||
echo plain: $(curl https://ipinfo.io/ip)
|
echo plain: $(sane-ip-check)
|
||||||
|
@@ -2,15 +2,18 @@
|
|||||||
|
|
||||||
# first arg should be the region, e.g. `us` or `ukr`
|
# first arg should be the region, e.g. `us` or `ukr`
|
||||||
|
|
||||||
case $1 in
|
vpns=$(systemctl list-unit-files | grep wg-quick-ovpnd- | cut -f 1 -d ' ' | sed s'/^wg-quick-ovpnd-\([a-zA-Z-]*\)\.service$/\1/g')
|
||||||
ukr)
|
|
||||||
iface=wg-quick-ovpnd-ukr;;
|
|
||||||
us)
|
|
||||||
iface=wg-quick-ovpnd-us;;
|
|
||||||
*)
|
|
||||||
echo "invalid vpn name '$1'"; exit 1;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo plain: $(curl https://ipinfo.io/ip)
|
if ! [ $(echo "$vpns" | grep "^$1$") ]
|
||||||
|
then
|
||||||
|
echo "invalid vpn name '$1'"
|
||||||
|
echo "choices:"
|
||||||
|
echo "$vpns"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
iface=wg-quick-ovpnd-$1.service
|
||||||
|
|
||||||
|
echo plain: $(sane-ip-check)
|
||||||
sudo systemctl start $iface
|
sudo systemctl start $iface
|
||||||
echo vpn: $(curl https://ipinfo.io/ip)
|
echo vpn: $(sane-ip-check)
|
||||||
|
24
pkgs/signaldctl/default.nix
Normal file
24
pkgs/signaldctl/default.nix
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{ lib
|
||||||
|
, buildGoModule
|
||||||
|
, fetchFromGitLab }:
|
||||||
|
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "signaldctl";
|
||||||
|
version = "0.6.1";
|
||||||
|
src = fetchFromGitLab {
|
||||||
|
owner = "signald";
|
||||||
|
repo = "signald-go";
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-lMJyr4BPZ8V2f//CUkr7CVQ6o8nRyeLBHMDEyLcHSgQ=";
|
||||||
|
};
|
||||||
|
|
||||||
|
vendorHash = "sha256-LGIWAVhDJCg6Ox7U4ZK15K8trjsvSZm4/0jNpIDmG7I=";
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "A golang library for communicating with signald";
|
||||||
|
homepage = "https://signald.org/signaldctl/";
|
||||||
|
license = licenses.gpl3;
|
||||||
|
maintainers = with maintainers; [ colinsane ];
|
||||||
|
platforms = [ "x86_64-linux" "aarch64-linux" ];
|
||||||
|
};
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user