Compare commits
2 Commits
wip-secure
...
tx-to-desk
Author | SHA1 | Date | |
---|---|---|---|
24ff2feb70 | |||
21c033f63c |
25
README.md
25
README.md
@@ -2,8 +2,6 @@
|
||||
|
||||
# .❄️≡We|_c0m3 7o m`/ f14k≡❄️.
|
||||
|
||||
(er, it's not a flake anymore. welcome to my nix files.)
|
||||
|
||||
## What's Here
|
||||
|
||||
this is the top-level repo from which i configure/deploy all my NixOS machines:
|
||||
@@ -31,7 +29,11 @@ you might specifically be interested in these files (elaborated further in #key-
|
||||
|
||||
## Using This Repo In Your Own Config
|
||||
|
||||
follow the instructions [here][NUR] to access my packages through the Nix User Repositories.
|
||||
this should be a pretty "standard" flake. just reference it, and import either
|
||||
- `nixosModules.sane` (for the modules)
|
||||
- `overlays.pkgs` (for the packages)
|
||||
|
||||
or follow the instructions [here][NUR] to use it via the Nix User Repositories.
|
||||
|
||||
[NUR]: https://nur.nix-community.org/
|
||||
|
||||
@@ -39,15 +41,19 @@ follow the instructions [here][NUR] to access my packages through the Nix User R
|
||||
- `doc/`
|
||||
- instructions for tasks i find myself doing semi-occasionally in this repo.
|
||||
- `hosts/`
|
||||
- configs which aren't factored with external use in mind.
|
||||
- the bulk of config which isn't factored with external use in mind.
|
||||
- that is, if you were to add this repo to a flake.nix for your own use,
|
||||
you won't likely be depending on anything in this directory.
|
||||
- `integrations/`
|
||||
- code intended for consumption by external tools (e.g. the Nix User Repos).
|
||||
- code intended for consumption by external tools (e.g. the Nix User Repos)
|
||||
- `modules/`
|
||||
- config which is gated behind `enable` flags, in similar style to nixpkgs' `nixos/` directory.
|
||||
- if you depend on this repo for anything besides packages, it's most likely for something in this directory.
|
||||
- config which is gated behind `enable` flags, in similar style to nixpkgs'
|
||||
`nixos/` directory.
|
||||
- if you depend on this repo, it's most likely for something in this directory.
|
||||
- `nixpatches/`
|
||||
- literally, diffs i apply atop upstream nixpkgs before performing further eval.
|
||||
- `overlays/`
|
||||
- exposed via the `overlays` output in `flake.nix`.
|
||||
- predominantly a list of `callPackage` directives.
|
||||
- `pkgs/`
|
||||
- derivations for things not yet packaged in nixpkgs.
|
||||
@@ -55,12 +61,13 @@ follow the instructions [here][NUR] to access my packages through the Nix User R
|
||||
- inline code for wholly custom packages (e.g. `pkgs/additional/sane-scripts/` for CLI tools
|
||||
that are highly specific to my setup).
|
||||
- `scripts/`
|
||||
- scripts which aren't reachable on a deployed system, but may aid manual deployments.
|
||||
- scripts which aren't reachable on a deployed system, but may aid manual deployments
|
||||
- `secrets/`
|
||||
- encrypted keys, API tokens, anything which one or more of my machines needs
|
||||
read access to but shouldn't be world-readable.
|
||||
- not much to see here.
|
||||
- not much to see here
|
||||
- `templates/`
|
||||
- exposed via the `templates` output in `flake.nix`.
|
||||
- used to instantiate short-lived environments.
|
||||
- used to auto-fill the boiler-plate portions of new packages.
|
||||
|
||||
|
54
TODO.md
54
TODO.md
@@ -2,18 +2,21 @@
|
||||
- `rmDbusServices` may break sandboxing
|
||||
- e.g. if the package ships a systemd unit which references $out, then make-sandboxed won't properly update that unit.
|
||||
- `rmDbusServicesInPlace` is not affected
|
||||
- moby: touchscreen input is still enabled when screen is off
|
||||
- when moby wlan is explicitly set down (via ip link set wlan0 down), /var/lib/trust-dns/dhcp-configs doesn't get reset
|
||||
- `ip monitor` can detect those manual link state changes (NM-dispatcher it seems cannot)
|
||||
- or try dnsmasq?
|
||||
- trust-dns can't resolve `abs.twimg.com`
|
||||
- trust-dns can't resolve `social.kernel.org`
|
||||
- trust-dns: can't recursively resolve api.mangadex.org
|
||||
- and *sometimes* apple.com fails
|
||||
- sandbox: link cache means that if i update ~/.config/... files inline, sandboxed programs still see the old version
|
||||
- mpv: continues to play past the end of some audio files
|
||||
- mpv: audiocast has mpv sending its output to the builtin speakers unless manually changed
|
||||
- mpv: no way to exit fullscreen video on moby
|
||||
- uosc hides controls on FS, and touch doesn't support unhiding
|
||||
- Signal restart loop drains battery
|
||||
- decrease s6 restart time?
|
||||
- `ssh` access doesn't grant same linux capabilities as login
|
||||
- syshud (volume overlay): when casting with `blast`, syshud doesn't react to volume changes
|
||||
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
|
||||
- sysvol (volume overlay): when casting with `blast`, sysvol doesn't react to volume changes
|
||||
- moby: kaslr is effectively disabled
|
||||
- `dmesg | grep "KASLR disabled due to lack of seed"`
|
||||
- fix by adding `kaslrseed` to uboot script before `booti`
|
||||
@@ -22,15 +25,12 @@
|
||||
- moby: bpf is effectively disabled?
|
||||
- `dmesg | grep 'systemd[1]: bpf-lsm: Failed to load BPF object: No such process'`
|
||||
- `dmesg | grep 'hid_bpf: error while preloading HID BPF dispatcher: -22'`
|
||||
- `s6` is not re-entrant
|
||||
- so if the desktop crashes, the login process from `unl0kr` fails to re-launch the GUI
|
||||
|
||||
## REFACTORING:
|
||||
- add import checks to my Python nix-shell scripts
|
||||
- consolidate ~/dev and ~/ref
|
||||
- ~/dev becomes a link to ~/ref/cat/mine
|
||||
- fold hosts/common/home/ssh.nix -> hosts/common/users/colin.nix
|
||||
- don't hardcode IP addresses so much in servo
|
||||
|
||||
### sops/secrets
|
||||
- rework secrets to leverage `sane.fs`
|
||||
@@ -56,10 +56,6 @@
|
||||
- then i can tune the kernels for hardening, without duplicating that work 4 times
|
||||
- zfs: replace this with something which doesn't require a custom kernel build
|
||||
- mpv: add media looping controls (e.g. loop song, loop playlist)
|
||||
- curlftpfs: replace with something better
|
||||
- safer (rust? actively maintained? sandboxable?)
|
||||
- handles spaces/symbols in filenames
|
||||
- has better multi-stream perf (e.g. `sane-sync-music` should be able to copy N items in parallel)
|
||||
|
||||
### security/resilience
|
||||
- validate duplicity backups!
|
||||
@@ -68,9 +64,6 @@
|
||||
- /mnt/desko/home, etc, shouldn't include secrets (~/private)
|
||||
- 95% of its use is for remote media access and stuff which isn't in VCS (~/records)
|
||||
- port all sane.programs to be sandboxed
|
||||
- sandbox `curlftpfs`
|
||||
- sandbox `nix`
|
||||
- sandbox `sshfs-fuse`
|
||||
- enforce that all `environment.packages` has a sandbox profile (or explicitly opts out)
|
||||
- revisit "non-sandboxable" apps and check that i'm not actually just missing mountpoints
|
||||
- LL_FS_RW=/ isn't enough -- need all mount points like `=/:/proc:/sys:...`.
|
||||
@@ -79,18 +72,28 @@
|
||||
- lock down dbus calls within the sandbox
|
||||
- otherwise anyone can `systemd-run --user ...` to potentially escape a sandbox
|
||||
- <https://github.com/flatpak/xdg-dbus-proxy>
|
||||
- remove `.ssh` access from Firefox!
|
||||
- limit access to `~/knowledge/secrets` through an agent that requires GUI approval, so a firefox exploit can't steal all my logins
|
||||
- port sanebox to a compiled language (hare?)
|
||||
- it adds like 50-70ms launch time _on my laptop_. i'd hate to know how much that is on the pinephone.
|
||||
- make dconf stuff less monolithic
|
||||
- i.e. per-app dconf profiles for those which need it. possible static config.
|
||||
- flatpak/spectrum has some stuff to proxy dconf per-app
|
||||
- canaries for important services
|
||||
- e.g. daily email checks; daily backup checks
|
||||
- integrate `nix check` into Gitea actions?
|
||||
|
||||
#### sudo-free world
|
||||
- `systemctl restart FOO`: needs `sudo`
|
||||
- `systemctl daemon-reload`: needs sudo
|
||||
- `watch ifconfig`: needs `SANEBOX_DISABLE=1`
|
||||
|
||||
### user experience
|
||||
- rofi: sort items case-insensitively
|
||||
- xdg-desktop-portal shouldn't kill children on exit
|
||||
- *maybe* a job for `setsid -f`?
|
||||
- replace starship prompt with something more efficient
|
||||
- watch `forkstat`: it does way too much
|
||||
- cleanup waybar/nwg-panel so that it's not invoking playerctl every 2 seconds
|
||||
- nwg-panel: doesn't know that virtual-desktop 10/TV exists
|
||||
- cleanup waybar so that it's not invoking playerctl every 2 seconds
|
||||
- install apps:
|
||||
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
|
||||
- shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
|
||||
@@ -98,9 +101,8 @@
|
||||
- offline docs viewer (gtk): <https://github.com/workbenchdev/Biblioteca>
|
||||
- some type of games manager/launcher
|
||||
- Gnome Highscore (retro games)?: <https://gitlab.gnome.org/World/highscore>
|
||||
- better maps for mobile (Osmin (QtQuick)? Pure Maps (Qt/Kirigami)?)
|
||||
- better maps for mobile (Osmin (QtQuick)? Pure Maps (Qt/Kirigami)?
|
||||
- note-taking app: <https://linuxphoneapps.org/categories/note-taking/>
|
||||
- Folio is nice, uses standard markdown, though it only supports flat repos
|
||||
- OSK overlay specifically for mobile gaming
|
||||
- i.e. mock joysticks, for use with SuperTux and SuperTuxKart
|
||||
- install mobile-friendly games:
|
||||
@@ -121,11 +123,13 @@
|
||||
- don't show MPRIS if no players detected
|
||||
- this is a problem of playerctld, i guess
|
||||
- add option to change audio output
|
||||
- fix colors (red alert) to match overall theme
|
||||
- moby: tune GPS
|
||||
- fix iio-sensor-proxy magnetometer scaling
|
||||
- run only geoclue, and not gpsd, to save power?
|
||||
- tune QGPS setting in eg25-control, for less jitter?
|
||||
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
|
||||
- configure geoclue to do some smoothing?
|
||||
- manually do smoothing, as some layer between mepo and geoclue?
|
||||
- manually do smoothing, as some layer between mepo and geoclue/gpsd?
|
||||
- moby: port `freshen-agps` timer service to s6 (maybe i want some `s6-cron` or something)
|
||||
- moby: show battery state on ssh login
|
||||
- moby: improve gPodder launch time
|
||||
@@ -148,15 +152,17 @@
|
||||
- have xdg-open parse `<repo:...> URIs (or adjust them so that it _can_ parse)
|
||||
- sane-bt-search: show details like 5.1 vs stereo, h264 vs h265
|
||||
- maybe just color these "keywords" in all search results?
|
||||
- transmission: apply `sane-tag-media` path fix in `torrent-done` script
|
||||
- many .mkv files do appear to be tagged: i'd just need to add support in my own tooling
|
||||
- uninsane.org: make URLs relative to allow local use (and as offline homepage)
|
||||
- email: fix so that local mail doesn't go to junk
|
||||
- git sendmail flow adds the DKIM signatures, but gets delivered locally w/o having the sig checked, so goes into Junk
|
||||
- could change junk filter from "no DKIM success" to explicit "DKIM failed"
|
||||
- add an auto-reply address (e.g. `reply-test@uninsane.org`) which reflects all incoming mail; use this (or a friend running this) for liveness checks
|
||||
|
||||
### perf
|
||||
- debug nixos-rebuild times
|
||||
- use `systemctl list-jobs` to show what's being waited on
|
||||
- i think it's `systemd-networkd-wait-online.service` that's blocking this?
|
||||
- i wonder what interface it's waiting for. i should use `--ignore=...` to ignore interfaces i don't care about.
|
||||
- also `wireguard-wg-home.target` when net is offline
|
||||
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
|
||||
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
|
||||
- would be super handy for package prototyping!
|
||||
|
14
default.nix
14
default.nix
@@ -1,5 +1,9 @@
|
||||
{ ... }@args:
|
||||
let
|
||||
sane-nix-files = import ./pkgs/additional/sane-nix-files { };
|
||||
in
|
||||
import "${sane-nix-files}/impure.nix" args
|
||||
# limited, non-flake interface to this repo.
|
||||
# this file exposes the same view into `pkgs` which the flake would see when evaluated.
|
||||
#
|
||||
# the primary purpose of this file is so i can run `updateScript`s which expect
|
||||
# the root to be `default.nix`
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.appendOverlays [
|
||||
(import ./overlays/all.nix)
|
||||
]
|
||||
|
7
flake.lock
generated
Normal file
7
flake.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"nodes": {
|
||||
"root": {}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
603
flake.nix
Normal file
603
flake.nix
Normal file
@@ -0,0 +1,603 @@
|
||||
# FLAKE FEEDBACK:
|
||||
# - if flake inputs are meant to be human-readable, a human should be able to easily track them down given the URL.
|
||||
# - this is not the case with registry URLs, like `nixpkgs/nixos-22.11`.
|
||||
# - this is marginally the case with schemes like `github:nixos/nixpkgs`.
|
||||
# - given the *existing* `git+https://` scheme, i propose expressing github URLs similarly:
|
||||
# - `github+https://github.com/nixos/nixpkgs/tree/nixos-22.11`
|
||||
# - this would allow for the same optimizations as today's `github:nixos/nixpkgs`, but without obscuring the source.
|
||||
# a code reader could view the source being referenced simply by clicking the https:// portion of that URI.
|
||||
# - need some way to apply local patches to inputs.
|
||||
#
|
||||
#
|
||||
# DEVELOPMENT DOCS:
|
||||
# - Flake docs: <https://nixos.wiki/wiki/Flakes>
|
||||
# - Flake RFC: <https://github.com/tweag/rfcs/blob/flakes/rfcs/0049-flakes.md>
|
||||
# - Discussion: <https://github.com/NixOS/rfcs/pull/49>
|
||||
# - <https://serokell.io/blog/practical-nix-flakes>
|
||||
#
|
||||
#
|
||||
# COMMON OPERATIONS:
|
||||
# - update a specific flake input:
|
||||
# - `nix flake lock --update-input nixpkgs`
|
||||
|
||||
{
|
||||
outputs = {
|
||||
self,
|
||||
}@inputs:
|
||||
let
|
||||
inherit (builtins) attrNames elem listToAttrs map mapAttrs;
|
||||
# redefine some nixpkgs `lib` functions to avoid the infinite recursion
|
||||
# of if we tried to use patched `nixpkgs.lib` as part of the patching process.
|
||||
mapAttrs' = f: set:
|
||||
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
|
||||
optionalAttrs = cond: attrs: if cond then attrs else {};
|
||||
# mapAttrs but without the `name` argument
|
||||
mapAttrValues = f: mapAttrs (_: f);
|
||||
|
||||
nixpkgs' = import ./pkgs/additional/nixpkgs;
|
||||
nixpkgsUnpatched = nixpkgs' { doPatch = false; localSystem = "x86_64-linux"; };
|
||||
nixpkgsCompiledBy = { system, variant ? "master" }:
|
||||
(nixpkgs' { inherit variant system; }).legacyPackages."${system}";
|
||||
|
||||
evalHost = { name, local, target, variant ? null, nixpkgs ? nixpkgs' { localSystem = local; system = target;} }: nixpkgs.nixos (
|
||||
[
|
||||
(optionalAttrs (variant == "light") {
|
||||
sane.maxBuildCost = 2;
|
||||
})
|
||||
(optionalAttrs (variant == "min") {
|
||||
sane.maxBuildCost = 0;
|
||||
})
|
||||
(import ./hosts/instantiate.nix { hostName = name; })
|
||||
(import ./modules)
|
||||
(nixpkgs.appendOverlays [ self.overlays.pkgs ]).sops-nix.nixosModules.sops
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
self.overlays.sane-all
|
||||
];
|
||||
}
|
||||
]
|
||||
);
|
||||
in {
|
||||
nixosConfigurations = let
|
||||
hosts = {
|
||||
servo = { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
desko = { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
desko-light = { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; variant = "light"; };
|
||||
lappy = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
lappy-light = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; variant = "light"; };
|
||||
lappy-min = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; variant = "min"; };
|
||||
moby = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
||||
moby-light = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; variant = "light"; };
|
||||
moby-min = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; variant = "min"; };
|
||||
# crappy is technically armv7a, and armv7l uses only a _subset_ of the available ISA.
|
||||
# but it's not as widely cached.
|
||||
crappy = { name = "crappy"; local = "x86_64-linux"; target = "armv7l-linux"; };
|
||||
crappy-min = { name = "crappy"; local = "x86_64-linux"; target = "armv7l-linux"; variant = "min"; };
|
||||
crappy-7a = { name = "crappy"; local = "x86_64-linux"; target = "armv7a-linux"; variant = "min"; };
|
||||
rescue = { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
};
|
||||
hostsNext = mapAttrs' (h: v: {
|
||||
name = "${h}-next";
|
||||
value = v // {
|
||||
nixpkgs = nixpkgs' {
|
||||
localSystem = v.local;
|
||||
system = v.target;
|
||||
variant = "staging-next";
|
||||
};
|
||||
};
|
||||
}) hosts;
|
||||
hostsStaging = mapAttrs' (h: v: {
|
||||
name = "${h}-staging";
|
||||
value = v // {
|
||||
nixpkgs = nixpkgs' {
|
||||
localSystem = v.local;
|
||||
system = v.target;
|
||||
variant = "staging";
|
||||
};
|
||||
};
|
||||
}) hosts;
|
||||
in mapAttrValues evalHost (
|
||||
hosts // hostsNext // hostsStaging
|
||||
);
|
||||
|
||||
# unofficial output
|
||||
# this produces a EFI-bootable .img file (GPT with a /boot partition and a system (/ or /nix) partition).
|
||||
# after building this:
|
||||
# - flash it to a bootable medium (SD card, flash drive, HDD)
|
||||
# - resize the root partition (use cfdisk)
|
||||
# - mount the part
|
||||
# - chown root:nixbld <part>/nix/store
|
||||
# - chown root:root -R <part>/nix/store/*
|
||||
# - chown root:root -R <part>/persist # if using impermanence
|
||||
# - populate any important things (persist/, home/colin/.ssh, etc)
|
||||
# - boot
|
||||
# - if fs wasn't resized automatically, then `sudo btrfs filesystem resize max /`
|
||||
# - checkout this flake into /etc/nixos AND UPDATE THE FS UUIDS.
|
||||
# - `nixos-rebuild --flake './#<host>' switch`
|
||||
imgs = mapAttrValues (host: host.config.system.build.img) self.nixosConfigurations;
|
||||
|
||||
# unofficial output
|
||||
hostConfigs = mapAttrValues (host: host.config) self.nixosConfigurations;
|
||||
hostSystems = mapAttrValues (host: host.config.system.build.toplevel) self.nixosConfigurations;
|
||||
hostPkgs = mapAttrValues (host: host.config.system.build.pkgs) self.nixosConfigurations;
|
||||
hostPrograms = mapAttrValues (host: mapAttrValues (p: p.package) host.config.sane.programs) self.nixosConfigurations;
|
||||
|
||||
overlays = {
|
||||
# N.B.: `nix flake check` requires every overlay to take `final: prev:` at defn site,
|
||||
# hence the weird redundancy.
|
||||
default = final: prev: self.overlays.pkgs final prev;
|
||||
sane-all = final: prev: import ./overlays/all.nix final prev;
|
||||
pkgs = final: prev: import ./overlays/pkgs.nix final prev;
|
||||
preferences = final: prev: import ./overlays/preferences.nix final prev;
|
||||
};
|
||||
|
||||
# this includes both our native packages and all the nixpkgs packages.
|
||||
legacyPackages =
|
||||
let
|
||||
allPkgsFor = variant: additionalOverlays: system:
|
||||
(nixpkgs' { inherit system variant; localSystem = "x86_64-linux"; })
|
||||
.appendOverlays (
|
||||
[
|
||||
self.overlays.pkgs
|
||||
] ++ additionalOverlays
|
||||
);
|
||||
allPkgsFor' = system: allPkgsFor
|
||||
"master"
|
||||
[(self: super: {
|
||||
# build `pkgsNext.FOO` to build the package FOO from nixpkgs staging-next branch
|
||||
pkgsNext = allPkgsFor "staging-next" [] system;
|
||||
pkgsStaging = allPkgsFor "staging" [] system;
|
||||
})]
|
||||
system
|
||||
;
|
||||
in {
|
||||
x86_64-linux = allPkgsFor' "x86_64-linux";
|
||||
aarch64-linux = allPkgsFor' "aarch64-linux";
|
||||
};
|
||||
|
||||
# extract only our own packages from the full set.
|
||||
# because of `nix flake check`, we flatten the package set and only surface x86_64-linux packages.
|
||||
packages = mapAttrs
|
||||
(system: passthruPkgs: passthruPkgs.lib.filterAttrs
|
||||
(name: pkg:
|
||||
# keep only packages which will pass `nix flake check`, i.e. keep only:
|
||||
# - derivations (not package sets)
|
||||
# - packages that build for the given platform
|
||||
(! elem name [ "feeds" "pythonPackagesExtensions" ])
|
||||
&& (passthruPkgs.lib.meta.availableOn passthruPkgs.stdenv.hostPlatform pkg)
|
||||
)
|
||||
(
|
||||
import ./pkgs { pkgs = passthruPkgs; }
|
||||
)
|
||||
)
|
||||
# self.legacyPackages;
|
||||
{
|
||||
x86_64-linux = nixpkgs' { localSystem = "x86_64-linux"; };
|
||||
}
|
||||
;
|
||||
|
||||
apps."x86_64-linux" =
|
||||
let
|
||||
pkgs = self.legacyPackages."x86_64-linux";
|
||||
sanePkgs = import ./pkgs { inherit pkgs; };
|
||||
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
|
||||
set -e
|
||||
|
||||
host="${host}"
|
||||
addr="${addr}"
|
||||
action="${if action != null then action else ""}"
|
||||
runOnTarget() {
|
||||
# run the command ($@) on the machine we're deploying to.
|
||||
# if that's a remote machine, then do it via ssh, else local shell.
|
||||
if [ -n "$addr" ]; then
|
||||
ssh "$addr" "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
nix build ".#nixosConfigurations.$host.config.system.build.toplevel" --out-link "./build/result-$host" "$@"
|
||||
storePath="$(readlink ./build/result-$host)"
|
||||
|
||||
# mimic `nixos-rebuild --target-host`, in effect:
|
||||
# - nix-copy-closure ...
|
||||
# - nix-env --set ...
|
||||
# - switch-to-configuration <boot|dry-activate|switch|test|>
|
||||
# avoid the actual `nixos-rebuild` for a few reasons:
|
||||
# - fewer nix evals
|
||||
# - more introspectability and debuggability
|
||||
# - sandbox friendliness (especially: `git` doesn't have to be run as root)
|
||||
|
||||
if [ -n "$addr" ]; then
|
||||
sudo nix store sign -r -k /run/secrets/nix_signing_key "$storePath"
|
||||
# add more `-v` for more verbosity (up to 5).
|
||||
# builders-use-substitutes false: optimizes so that the remote machine doesn't try to get paths from its substituters.
|
||||
# we already have all paths here, and the remote substitution is slow to check and SERIOUSLY flaky on moby in particular.
|
||||
nix copy -vv --option builders-use-substitutes false --to "ssh-ng://$addr" "$storePath"
|
||||
fi
|
||||
|
||||
if [ -n "$action" ]; then
|
||||
runOnTarget sudo nix-env -p /nix/var/nix/profiles/system --set "$storePath"
|
||||
runOnTarget sudo "$storePath/bin/switch-to-configuration" "$action"
|
||||
fi
|
||||
'';
|
||||
deployApp = host: addr: action: {
|
||||
type = "app";
|
||||
program = ''${deployScript host addr action}'';
|
||||
};
|
||||
|
||||
# pkg updating.
|
||||
# a cleaner alternative lives here: <https://discourse.nixos.org/t/how-can-i-run-the-updatescript-of-personal-packages/25274/2>
|
||||
# mkUpdater :: [ String ] -> { type = "app"; program = path; }
|
||||
mkUpdater = attrPath: {
|
||||
type = "app";
|
||||
program = let
|
||||
pkg = pkgs.lib.getAttrFromPath attrPath sanePkgs;
|
||||
strAttrPath = pkgs.lib.concatStringsSep "." attrPath;
|
||||
commandArgv = pkg.updateScript.command or pkg.updateScript;
|
||||
command = pkgs.lib.escapeShellArgs commandArgv;
|
||||
in builtins.toString (pkgs.writeShellScript "update-${strAttrPath}" ''
|
||||
set -x
|
||||
env UPDATE_NIX_NAME=${pkg.name} UPDATE_NIX_PNAME=${pkg.pname} UPDATE_NIX_OLD_VERSION=${pkg.version} UPDATE_NIX_ATTR_PATH=${strAttrPath} ${command}
|
||||
'');
|
||||
};
|
||||
mkUpdatersNoAliases = opts: basePath: pkgs.lib.concatMapAttrs
|
||||
(name: pkg:
|
||||
if pkg.recurseForDerivations or false then {
|
||||
"${name}" = mkUpdaters opts (basePath ++ [ name ]);
|
||||
} else if pkg.updateScript or null != null then {
|
||||
"${name}" = mkUpdater (basePath ++ [ name ]);
|
||||
} else {}
|
||||
)
|
||||
(pkgs.lib.getAttrFromPath basePath sanePkgs);
|
||||
mkUpdaters = { ignore ? [], flakePrefix ? [] }@opts: basePath:
|
||||
let
|
||||
updaters = mkUpdatersNoAliases opts basePath;
|
||||
invokeUpdater = name: pkg:
|
||||
let
|
||||
fullPath = basePath ++ [ name ];
|
||||
doUpdateByDefault = !builtins.elem fullPath ignore;
|
||||
|
||||
# in case `name` has a `.` in it, we have to quote it
|
||||
escapedPath = builtins.map (p: ''"${p}"'') fullPath;
|
||||
updatePath = builtins.concatStringsSep "." (flakePrefix ++ escapedPath);
|
||||
in pkgs.lib.optionalString doUpdateByDefault (
|
||||
pkgs.lib.escapeShellArgs [
|
||||
"nix" "run" ".#${updatePath}"
|
||||
]
|
||||
);
|
||||
in {
|
||||
type = "app";
|
||||
# top-level app just invokes the updater of everything one layer below it
|
||||
program = builtins.toString (pkgs.writeShellScript
|
||||
(builtins.concatStringsSep "-" (flakePrefix ++ basePath))
|
||||
(builtins.concatStringsSep
|
||||
"\n"
|
||||
(pkgs.lib.mapAttrsToList invokeUpdater updaters)
|
||||
)
|
||||
);
|
||||
} // updaters;
|
||||
in {
|
||||
help = {
|
||||
type = "app";
|
||||
program = let
|
||||
helpMsg = builtins.toFile "nixos-config-help-message" ''
|
||||
commands:
|
||||
- `nix run '.#help'`
|
||||
- show this message
|
||||
- `nix run '.#update.pkgs'`
|
||||
- updates every package
|
||||
- `nix run '.#update.feeds'`
|
||||
- updates metadata for all feeds
|
||||
- `nix run '.#init-feed' <url>`
|
||||
- `nix run '.#deploy.{desko,lappy,moby,servo}[-light|-test]' [nix args ...]`
|
||||
- build and deploy the host
|
||||
- `nix run '.#preDeploy.{desko,lappy,moby,servo}[-light]' [nix args ...]`
|
||||
- copy closures to a host, but don't activate it
|
||||
- or `nix run '.#preDeploy'` to target all hosts
|
||||
- `nix run '.#check'`
|
||||
- make sure all systems build; NUR evaluates
|
||||
- `nix run '.#bench'`
|
||||
- benchmark the eval time of common targets this flake provides
|
||||
|
||||
specific build targets of interest:
|
||||
- `nix build '.#imgs.rescue'`
|
||||
'';
|
||||
in builtins.toString (pkgs.writeShellScript "nixos-config-help" ''
|
||||
cat ${helpMsg}
|
||||
echo ""
|
||||
echo "complete flake structure:"
|
||||
nix flake show --option allow-import-from-derivation true
|
||||
'');
|
||||
};
|
||||
# wrangle some names to get package updaters which refer back into the flake, but also conditionally ignore certain paths (e.g. sane.feeds).
|
||||
# TODO: better design
|
||||
update = rec {
|
||||
_impl.pkgs.sane = mkUpdaters { flakePrefix = [ "update" "_impl" "pkgs" ]; ignore = [ [ "sane" "feeds" ] ]; } [ "sane" ];
|
||||
pkgs = _impl.pkgs.sane;
|
||||
_impl.feeds.sane.feeds = mkUpdaters { flakePrefix = [ "update" "_impl" "feeds" ]; } [ "sane" "feeds" ];
|
||||
feeds = _impl.feeds.sane.feeds;
|
||||
};
|
||||
|
||||
init-feed = {
|
||||
type = "app";
|
||||
program = "${pkgs.feeds.init-feed}";
|
||||
};
|
||||
|
||||
deploy = {
|
||||
crappy = deployApp "crappy" "crappy" "switch";
|
||||
crappy-light = deployApp "crappy-light" "crappy" "switch";
|
||||
crappy-min = deployApp "crappy-min" "crappy" "switch";
|
||||
desko = deployApp "desko" "desko" "switch";
|
||||
desko-light = deployApp "desko-light" "desko" "switch";
|
||||
lappy = deployApp "lappy" "lappy" "switch";
|
||||
lappy-light = deployApp "lappy-light" "lappy" "switch";
|
||||
lappy-min = deployApp "lappy-min" "lappy" "switch";
|
||||
moby = deployApp "moby" "moby" "switch";
|
||||
moby-light = deployApp "moby-light" "moby" "switch";
|
||||
moby-min = deployApp "moby-min" "moby" "switch";
|
||||
moby-test = deployApp "moby" "moby" "test";
|
||||
servo = deployApp "servo" "servo" "switch";
|
||||
|
||||
# like `nixos-rebuild --flake . switch`
|
||||
self = deployApp "$(hostname)" "" "switch";
|
||||
self-light = deployApp "$(hostname)-light" "" "switch";
|
||||
self-min = deployApp "$(hostname)-min" "" "switch";
|
||||
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "deploy-all" ''
|
||||
nix run '.#deploy.lappy'
|
||||
nix run '.#deploy.moby'
|
||||
nix run '.#deploy.desko'
|
||||
nix run '.#deploy.servo'
|
||||
nix run '.#deploy.crappy'
|
||||
'');
|
||||
};
|
||||
preDeploy = {
|
||||
# build the host and copy the runtime closure to that host, but don't activate it.
|
||||
crappy = deployApp "crappy" "crappy" null;
|
||||
crappy-light = deployApp "crappy-light" "crappy" null;
|
||||
crappy-min = deployApp "crappy-min" "crappy" null;
|
||||
desko = deployApp "desko" "desko" null;
|
||||
desko-light = deployApp "desko-light" "desko" null;
|
||||
lappy = deployApp "lappy" "lappy" null;
|
||||
lappy-light = deployApp "lappy-light" "lappy" null;
|
||||
lappy-min = deployApp "lappy-min" "lappy" null;
|
||||
moby = deployApp "moby" "moby" null;
|
||||
moby-light = deployApp "moby-light" "moby" null;
|
||||
moby-min = deployApp "moby-min" "moby" null;
|
||||
servo = deployApp "servo" "servo" null;
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "predeploy-all" ''
|
||||
# copy the -min/-light variants first; this might be run while waiting on a full build. or the full build failed.
|
||||
nix run '.#preDeploy.moby-min' -- "$@"
|
||||
nix run '.#preDeploy.lappy-min' -- "$@"
|
||||
nix run '.#preDeploy.crappy-min' -- "$@"
|
||||
nix run '.#preDeploy.moby-light' -- "$@"
|
||||
nix run '.#preDeploy.lappy-light' -- "$@"
|
||||
nix run '.#preDeploy.desko-light' -- "$@"
|
||||
nix run '.#preDeploy.crappy-light' -- "$@"
|
||||
nix run '.#preDeploy.lappy' -- "$@"
|
||||
nix run '.#preDeploy.servo' -- "$@"
|
||||
nix run '.#preDeploy.moby' -- "$@"
|
||||
nix run '.#preDeploy.desko' -- "$@"
|
||||
nix run '.#preDeploy.crappy' -- "$@"
|
||||
'');
|
||||
};
|
||||
|
||||
sync = {
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "sync-all" ''
|
||||
RC_lappy=$(nix run '.#sync.lappy' -- "$@")
|
||||
RC_moby=$(nix run '.#sync.moby' -- "$@")
|
||||
RC_desko=$(nix run '.#sync.desko' -- "$@")
|
||||
|
||||
echo "lappy: $RC_lappy"
|
||||
echo "moby: $RC_moby"
|
||||
echo "desko: $RC_desko"
|
||||
'');
|
||||
};
|
||||
|
||||
sync.desko = {
|
||||
# copy music from servo to desko
|
||||
# can run this from any device that has ssh access to desko and servo
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "sync-to-desko" ''
|
||||
sudo mount /mnt/desko/home
|
||||
${pkgs.sane-scripts.sync-music}/bin/sane-sync-music --compat /mnt/servo/media/Music /mnt/desko/home/Music "$@"
|
||||
'');
|
||||
};
|
||||
|
||||
sync.lappy = {
|
||||
# copy music from servo to lappy
|
||||
# can run this from any device that has ssh access to lappy and servo
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "sync-to-lappy" ''
|
||||
sudo mount /mnt/lappy/home
|
||||
${pkgs.sane-scripts.sync-music}/bin/sane-sync-music --compress --compat /mnt/servo/media/Music /mnt/lappy/home/Music "$@"
|
||||
'');
|
||||
};
|
||||
|
||||
sync.moby = {
|
||||
# copy music from servo to moby
|
||||
# can run this from any device that has ssh access to moby and servo
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "sync-to-moby" ''
|
||||
sudo mount /mnt/moby/home
|
||||
sudo mount /mnt/desko/home
|
||||
sudo mount /mnt/servo/media/Books
|
||||
# copy photos/screenshots from moby to desko:
|
||||
${pkgs.rsync}/bin/rsync -arv --exclude servo-macros /mnt/moby/home/Pictures/ /mnt/desko/home/Pictures/moby/
|
||||
# copy books from servo to moby; delete old/untracked ones, but keep KOreader state files (sdr)
|
||||
${pkgs.rsync}/bin/rsync -arv --delete --exclude unprocessed --exclude '*.sdr' /mnt/servo/media/Books/ /mnt/moby/home/Books/local/servo/
|
||||
# N.B.: limited by network/disk -> reduce job count to improve pause/resume behavior
|
||||
${pkgs.sane-scripts.sync-music}/bin/sane-sync-music --compress --compat --jobs 4 /mnt/servo/media/Music /mnt/moby/home/Music "$@"
|
||||
'');
|
||||
};
|
||||
|
||||
check = {
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "check-all" ''
|
||||
nix run '.#check.nur'
|
||||
RC0=$?
|
||||
nix run '.#check.hostConfigs'
|
||||
RC1=$?
|
||||
nix run '.#check.rescue'
|
||||
RC2=$?
|
||||
echo "nur: $RC0"
|
||||
echo "hostConfigs: $RC1"
|
||||
echo "rescue: $RC2"
|
||||
exit $(($RC0 | $RC1 | $RC2))
|
||||
'');
|
||||
};
|
||||
|
||||
check.nur = {
|
||||
# `nix run '.#check-nur'`
|
||||
# validates that my repo can be included in the Nix User Repository
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "check-nur" ''
|
||||
cd ${./.}/integrations/nur
|
||||
NIX_PATH= NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 nix-env -f . -qa \* --meta --xml \
|
||||
--allowed-uris https://static.rust-lang.org \
|
||||
--option restrict-eval true \
|
||||
--option allow-import-from-derivation true \
|
||||
--drv-path --show-trace \
|
||||
-I nixpkgs=${nixpkgsUnpatched} \
|
||||
-I nixpkgs-overlays=${./.}/hosts/common/nix/overlay \
|
||||
-I ../../ \
|
||||
| tee # tee to prevent interactive mode
|
||||
'');
|
||||
};
|
||||
|
||||
check.hostConfigs = {
|
||||
type = "app";
|
||||
program = let
|
||||
checkHost = host: let
|
||||
shellHost = pkgs.lib.replaceStrings [ "-" ] [ "_" ] host;
|
||||
in ''
|
||||
nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./build/result-${host} -j2 "$@"
|
||||
RC_${shellHost}=$?
|
||||
'';
|
||||
in builtins.toString (pkgs.writeShellScript
|
||||
"check-host-configs"
|
||||
''
|
||||
# build minimally-usable hosts first, then their full image.
|
||||
# this gives me a minimal image i can deploy or copy over, early.
|
||||
${checkHost "lappy-min"}
|
||||
${checkHost "moby-min"}
|
||||
${checkHost "crappy-min"}
|
||||
|
||||
${checkHost "desko-light"}
|
||||
${checkHost "moby-light"}
|
||||
${checkHost "lappy-light"}
|
||||
${checkHost "crappy-light"}
|
||||
|
||||
${checkHost "desko"}
|
||||
${checkHost "lappy"}
|
||||
${checkHost "servo"}
|
||||
${checkHost "moby"}
|
||||
${checkHost "crappy"}
|
||||
${checkHost "rescue"}
|
||||
|
||||
# still want to build the -light variants first so as to avoid multiple simultaneous webkitgtk builds
|
||||
${checkHost "desko-light-next"}
|
||||
${checkHost "moby-light-next"}
|
||||
${checkHost "crappy-light-next"}
|
||||
|
||||
${checkHost "desko-next"}
|
||||
${checkHost "lappy-next"}
|
||||
${checkHost "servo-next"}
|
||||
${checkHost "moby-next"}
|
||||
${checkHost "crappy-next"}
|
||||
${checkHost "rescue-next"}
|
||||
|
||||
echo "desko: $RC_desko"
|
||||
echo "lappy: $RC_lappy"
|
||||
echo "servo: $RC_servo"
|
||||
echo "moby: $RC_moby"
|
||||
echo "crappy: $RC_crappy"
|
||||
echo "rescue: $RC_rescue"
|
||||
|
||||
echo "desko-next: $RC_desko_next"
|
||||
echo "lappy-next: $RC_lappy_next"
|
||||
echo "servo-next: $RC_servo_next"
|
||||
echo "moby-next: $RC_moby_next"
|
||||
echo "crappy-next: $RC_crappy_next"
|
||||
echo "rescue-next: $RC_rescue_next"
|
||||
|
||||
# i don't really care if the -next hosts fail. i build them mostly to keep the cache fresh/ready
|
||||
exit $(($RC_desko | $RC_lappy | $RC_servo | $RC_moby | $RC_crappy | $RC_rescue))
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
check.rescue = {
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "check-rescue" ''
|
||||
nix build -v '.#imgs.rescue' --out-link ./build/result-rescue-img -j2
|
||||
'');
|
||||
};
|
||||
|
||||
bench = {
|
||||
type = "app";
|
||||
program = builtins.toString (pkgs.writeShellScript "bench" ''
|
||||
doBench() {
|
||||
attrPath="$1"
|
||||
shift
|
||||
echo -n "benchmarking eval of '$attrPath'... "
|
||||
/run/current-system/sw/bin/time -f "%e sec" -o /dev/stdout \
|
||||
nix eval --no-eval-cache --quiet --raw ".#$attrPath" --apply 'result: if result != null then "" else "unexpected null"' $@ 2> /dev/null
|
||||
}
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
doBench "$@"
|
||||
else
|
||||
doBench hostConfigs
|
||||
doBench hostConfigs.lappy
|
||||
doBench hostConfigs.lappy.sane.programs
|
||||
doBench hostConfigs.lappy.sane.users.colin
|
||||
doBench hostConfigs.lappy.sane.fs
|
||||
doBench hostConfigs.lappy.environment.systemPackages
|
||||
fi
|
||||
'');
|
||||
};
|
||||
};
|
||||
|
||||
templates = {
|
||||
env.python-data = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#env.python-data'`
|
||||
# then enter with:
|
||||
# - `nix develop`
|
||||
path = ./templates/env/python-data;
|
||||
description = "python environment for data processing";
|
||||
};
|
||||
|
||||
pkgs.make = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.make'`
|
||||
path = ./templates/pkgs/make;
|
||||
description = "default Makefile-based derivation";
|
||||
};
|
||||
pkgs.python = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.python'`
|
||||
path = ./templates/pkgs/python;
|
||||
description = "python package";
|
||||
};
|
||||
pkgs.rust-inline = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.rust-inline'`
|
||||
path = ./templates/pkgs/rust-inline;
|
||||
description = "rust package and development environment (inline rust sources)";
|
||||
};
|
||||
pkgs.rust = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.rust'`
|
||||
path = ./templates/pkgs/rust;
|
||||
description = "rust package fit to ship in nixpkgs";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -12,23 +12,15 @@
|
||||
|
||||
users.users.colin.initialPassword = "147147";
|
||||
sane.programs.sway.enableFor.user.colin = true;
|
||||
|
||||
sane.programs.calls.enableFor.user.colin = false;
|
||||
sane.programs.consoleMediaUtils.enableFor.user.colin = true;
|
||||
sane.programs.epiphany.enableFor.user.colin = true;
|
||||
sane.programs.geary.enableFor.user.colin = false;
|
||||
sane.programs."gnome.geary".enableFor.user.colin = false;
|
||||
# sane.programs.firefox.enableFor.user.colin = true;
|
||||
sane.programs.portfolio-filemanager.enableFor.user.colin = true;
|
||||
sane.programs.signal-desktop.enableFor.user.colin = false;
|
||||
sane.programs.wike.enableFor.user.colin = true;
|
||||
|
||||
sane.programs.dino.config.autostart = false;
|
||||
sane.programs.dissent.config.autostart = false;
|
||||
sane.programs.fractal.config.autostart = false;
|
||||
sane.programs.sway.config.mod = "Mod1"; #< alt key instead of Super
|
||||
|
||||
# sane.programs.guiApps.enableFor.user.colin = false;
|
||||
|
||||
# sane.programs.pcGuiApps.enableFor.user.colin = false; #< errors!
|
||||
|
||||
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
||||
|
@@ -10,13 +10,9 @@
|
||||
|
||||
# don't enable wifi by default: it messes with connectivity.
|
||||
# systemd.services.iwd.enable = false;
|
||||
# networking.wireless.enable = false;
|
||||
# systemd.services.wpa_supplicant.enable = false;
|
||||
# sane.programs.wpa_supplicant.enableFor.user.colin = lib.mkForce false;
|
||||
# sane.programs.wpa_supplicant.enableFor.system = lib.mkForce false;
|
||||
# don't auto-connect to wifi networks
|
||||
# see: <https://networkmanager.dev/docs/api/latest/NetworkManager.conf.html#device-spec>
|
||||
networking.networkmanager.unmanaged = [ "type:wifi" ];
|
||||
sane.programs.wpa_supplicant.enableFor.user.colin = lib.mkForce false;
|
||||
sane.programs.wpa_supplicant.enableFor.system = lib.mkForce false;
|
||||
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
@@ -29,25 +25,16 @@
|
||||
sane.ovpn.addrV4 = "172.26.55.21";
|
||||
# sane.ovpn.addrV6 = "fd00:0000:1337:cafe:1111:1111:20c1:a73c";
|
||||
sane.services.duplicity.enable = true;
|
||||
sane.services.rsync-net.enable = true;
|
||||
|
||||
sane.nixcache.remote-builders.desko = false;
|
||||
|
||||
sane.programs.sane-private-unlock-remote.enableFor.user.colin = true;
|
||||
sane.programs.sane-private-unlock-remote.config.hosts = [ "servo" ];
|
||||
|
||||
sane.programs.sway.enableFor.user.colin = true;
|
||||
sane.programs.iphoneUtils.enableFor.user.colin = true;
|
||||
sane.programs.steam.enableFor.user.colin = true;
|
||||
|
||||
sane.programs.geary.config.autostart = true;
|
||||
sane.programs."gnome.geary".config.autostart = true;
|
||||
sane.programs.signal-desktop.config.autostart = true;
|
||||
|
||||
sane.programs.nwg-panel.config = {
|
||||
battery = false;
|
||||
brightness = false;
|
||||
};
|
||||
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
# needed to use libimobiledevice/ifuse, for iphone sync
|
||||
|
@@ -15,19 +15,14 @@
|
||||
# sane.guest.enable = true;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
sane.programs.sane-private-unlock-remote.enableFor.user.colin = true;
|
||||
sane.programs.sane-private-unlock-remote.config.hosts = [ "servo" ];
|
||||
|
||||
sane.programs.stepmania.enableFor.user.colin = true;
|
||||
sane.programs.sway.enableFor.user.colin = true;
|
||||
|
||||
sane.programs.geary.config.autostart = true;
|
||||
sane.programs."gnome.geary".config.autostart = true;
|
||||
sane.programs.signal-desktop.config.autostart = true;
|
||||
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
sane.services.rsync-net.enable = true;
|
||||
|
||||
# default config: https://man.archlinux.org/man/snapper-configs.5
|
||||
# defaults to something like:
|
||||
# - hourly snapshots
|
||||
|
@@ -10,6 +10,7 @@
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
./gps.nix
|
||||
];
|
||||
|
||||
sane.hal.pine64.enable = true;
|
||||
@@ -23,13 +24,11 @@
|
||||
# XXX colin: phosh doesn't work well with passwordless login,
|
||||
# so set this more reliable default password should anything go wrong
|
||||
users.users.colin.initialPassword = "147147";
|
||||
# services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
||||
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
sane.services.rsync-net.enable = true;
|
||||
|
||||
sane.programs.sway.enableFor.user.colin = true;
|
||||
sane.programs.sway.config.mod = "Mod1"; #< alt key instead of Super
|
||||
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
||||
sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile
|
||||
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
|
||||
@@ -37,12 +36,12 @@
|
||||
|
||||
# enabled for easier debugging
|
||||
sane.programs.eg25-control.enableFor.user.colin = true;
|
||||
# sane.programs.rtl8723cs-wowlan.enableFor.user.colin = true;
|
||||
sane.programs.rtl8723cs-wowlan.enableFor.user.colin = true;
|
||||
|
||||
# sane.programs.ntfy-sh.config.autostart = true;
|
||||
sane.programs.dino.config.autostart = true;
|
||||
sane.programs.signal-desktop.config.autostart = true;
|
||||
# sane.programs.geary.config.autostart = true;
|
||||
# sane.programs.signal-desktop.config.autostart = true; # TODO: enable once electron stops derping.
|
||||
# sane.programs."gnome.geary".config.autostart = true;
|
||||
# sane.programs.calls.config.autostart = true;
|
||||
|
||||
sane.programs.pipewire.config = {
|
||||
|
68
hosts/by-name/moby/gps.nix
Normal file
68
hosts/by-name/moby/gps.nix
Normal file
@@ -0,0 +1,68 @@
|
||||
# pinephone GPS happens in EG25 modem
|
||||
# serial control interface to modem is /dev/ttyUSB2
|
||||
# after enabling GPS, readout is /dev/ttyUSB1
|
||||
#
|
||||
# minimal process to enable modem and GPS:
|
||||
# - `echo 1 > /sys/class/modem-power/modem-power/device/powered`
|
||||
# - `screen /dev/ttyUSB2 115200`
|
||||
# - `AT+QGPSCFG="nmeasrc",1`
|
||||
# - `AT+QGPS=1`
|
||||
# this process is automated by my `eg25-control` program and services (`eg25-control-powered`, `eg25-control-gps`)
|
||||
# - see the `modules/` directory further up this repository.
|
||||
#
|
||||
# now, something like `gpsd` can directly read from /dev/ttyUSB1,
|
||||
# or geoclue can query the GPS directly through modem-manager
|
||||
#
|
||||
# initial GPS fix can take 15+ minutes.
|
||||
# meanwhile, services like eg25-manager or eg25-control-freshen-agps can speed this up by uploading assisted GPS data to the modem.
|
||||
#
|
||||
# support/help:
|
||||
# - geoclue, gnome-maps
|
||||
# - irc: #gnome-maps on irc.gimp.org
|
||||
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
|
||||
#
|
||||
# programs to pair this with:
|
||||
# - `satellite-gtk`: <https://codeberg.org/tpikonen/satellite>
|
||||
# - shows/tracks which satellites the GPS is connected to; useful to understand fix characteristics
|
||||
# - `gnome-maps`: uses geoclue, has route planning
|
||||
# - `mepo`: uses gpsd, minimalist, flaky, and buttons are kinda hard to activate on mobile
|
||||
# - puremaps?
|
||||
# - osmin?
|
||||
#
|
||||
# known/outstanding bugs:
|
||||
# - `systemctl start eg25-control-gps` can the hang the whole system (2023/10/06)
|
||||
# - i think it's actually `eg25-control-powered` which does this (started by the gps)
|
||||
# - best guess is modem draws so much power at launch that other parts of the system see undervoltage
|
||||
# - workaround is to hard power-cycle the system. the modem may not bring up after reboot: leave unpowered for 60s and boot again.
|
||||
#
|
||||
# future work:
|
||||
# - integrate with [wigle](https://www.wigle.net/) for offline equivalent to Mozilla Location Services
|
||||
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
# test gpsd with `gpspipe -w -n 10 2> /dev/null | grep -m 1 TPV | jq '.lat, .lon' | tr '\n' ' '`
|
||||
# ^ should return <lat> <long>
|
||||
services.gpsd.enable = true;
|
||||
services.gpsd.devices = [ "/dev/ttyUSB1" ];
|
||||
|
||||
# test geoclue2 by building `geoclue2-with-demo-agent`
|
||||
# and running "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/where-am-i"
|
||||
# note that geoclue is dbus-activated, and auto-stops after 60s with no caller
|
||||
services.geoclue2.enable = true;
|
||||
services.geoclue2.appConfig.where-am-i = {
|
||||
# this is the default "agent", shipped by geoclue package: allow it to use location
|
||||
isAllowed = true;
|
||||
isSystem = false;
|
||||
# XXX: setting users != [] might be causing `where-am-i` to time out
|
||||
users = [
|
||||
# restrict to only one set of users. empty array (default) means "allow any user to access geolocation".
|
||||
(builtins.toString config.users.users.colin.uid)
|
||||
];
|
||||
};
|
||||
systemd.services.geoclue.after = lib.mkForce []; #< defaults to network-online, but not all my sources require network
|
||||
users.users.geoclue.extraGroups = [
|
||||
"dialout" # TODO: figure out if dialout is required. that's for /dev/ttyUSB1, but geoclue probably doesn't read that?
|
||||
];
|
||||
|
||||
sane.programs.where-am-i.enableFor.user.colin = true;
|
||||
}
|
@@ -7,14 +7,15 @@
|
||||
./services
|
||||
];
|
||||
|
||||
# for administering services
|
||||
sane.programs.clightning-sane.enableFor.user.colin = true;
|
||||
# sane.programs.freshrss.enableFor.user.colin = true;
|
||||
# sane.programs.signaldctl.enableFor.user.colin = true;
|
||||
# sane.programs.matrix-synapse.enableFor.user.colin = true;
|
||||
sane.programs = {
|
||||
# for administering services
|
||||
freshrss.enableFor.user.colin = true;
|
||||
matrix-synapse.enableFor.user.colin = true;
|
||||
signaldctl.enableFor.user.colin = true;
|
||||
};
|
||||
|
||||
sane.roles.build-machine.enable = true;
|
||||
sane.programs.sane-deadlines.config.showOnLogin = false; # ~/knowledge doesn't always exist
|
||||
sane.programs.zsh.config.showDeadlines = false; # ~/knowledge doesn't always exist
|
||||
sane.programs.consoleUtils.suggestedPrograms = [
|
||||
"consoleMediaUtils" # notably, for go2tv / casting
|
||||
"pcConsoleUtils"
|
||||
@@ -32,12 +33,10 @@
|
||||
sane.nixcache.remote-builders.desko = false;
|
||||
sane.nixcache.remote-builders.servo = false;
|
||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||
sane.services.rsync-net.enable = true;
|
||||
|
||||
# automatically log in at the virtual consoles.
|
||||
# using root here makes sure we always have an escape hatch.
|
||||
# XXX(2024-07-27): this is incompatible with my s6-rc stuff, which needs to auto-login as `colin` to start its user services.
|
||||
# services.getty.autologinUser = "root";
|
||||
# using root here makes sure we always have an escape hatch
|
||||
services.getty.autologinUser = "root";
|
||||
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
|
@@ -14,7 +14,7 @@
|
||||
# show zfs datasets: `zfs list` (will be empty if haven't imported)
|
||||
# show zfs properties (e.g. compression): `zfs get all pool`
|
||||
# set zfs properties: `zfs set compression=on pool`
|
||||
{ lib, pkgs, ... }:
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# hostId: not used for anything except zfs guardrail?
|
||||
@@ -54,7 +54,7 @@
|
||||
options = [ "acl" ]; #< not sure if this `acl` flag is actually necessary. it mounts without it.
|
||||
};
|
||||
# services.zfs.zed = ... # TODO: zfs can send me emails when disks fail
|
||||
sane.programs.sysadminUtils.suggestedPrograms = [ "zfs-tools" ];
|
||||
sane.programs.sysadminUtils.suggestedPrograms = [ "zfs" ];
|
||||
|
||||
sane.persist.stores."ext" = {
|
||||
origin = "/mnt/pool/persist";
|
||||
@@ -131,20 +131,6 @@
|
||||
the contents should be a subset of what's in ../media/datasets.
|
||||
'';
|
||||
|
||||
systemd.services.dedupe-media = {
|
||||
description = "transparently de-duplicate /var/media entries by using block-level hardlinks";
|
||||
script = ''
|
||||
${lib.getExe' pkgs.util-linux "hardlink"} /var/media --reflink=always --ignore-time --verbose
|
||||
'';
|
||||
};
|
||||
systemd.timers.dedupe-media = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
timerConfig = {
|
||||
OnStartupSec = "23min";
|
||||
OnUnitActiveSec = "720min";
|
||||
};
|
||||
};
|
||||
|
||||
# btrfs doesn't easily support swapfiles
|
||||
# swapDevices = [
|
||||
# { device = "/nix/persist/swapfile"; size = 4096; }
|
||||
|
@@ -3,19 +3,9 @@
|
||||
let
|
||||
portOpts = with lib; types.submodule {
|
||||
options = {
|
||||
visibleTo.ovpns = mkOption {
|
||||
visibleTo.ovpn = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
whether to forward inbound traffic on the OVPN vpn port to the corresponding localhost port.
|
||||
'';
|
||||
};
|
||||
visibleTo.doof = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
whether to forward inbound traffic on the doofnet vpn port to the corresponding localhost port.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -23,21 +13,13 @@ in
|
||||
{
|
||||
options = with lib; {
|
||||
sane.ports.ports = mkOption {
|
||||
# add the `visibleTo.{doof,ovpns}` options
|
||||
# add the `visibleTo.ovpn` option
|
||||
type = types.attrsOf portOpts;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
networking.domain = "uninsane.org";
|
||||
systemd.network.networks."50-eth0" = {
|
||||
matchConfig.Name = "eth0";
|
||||
networkConfig.Address = [
|
||||
"205.201.63.12/32"
|
||||
"10.78.79.51/22"
|
||||
];
|
||||
networkConfig.DNS = [ "10.78.79.1" ];
|
||||
};
|
||||
|
||||
sane.ports.openFirewall = true;
|
||||
sane.ports.openUpnp = true;
|
||||
@@ -58,16 +40,18 @@ in
|
||||
|
||||
# tun-sea config
|
||||
sane.dns.zones."uninsane.org".inet.A."doof.tunnel" = "205.201.63.12";
|
||||
# sane.dns.zones."uninsane.org".inet.AAAA."doof.tunnel" = "2602:fce8:106::51"; #< TODO: enable IPv6
|
||||
networking.wireguard.interfaces.wg-doof = {
|
||||
sane.dns.zones."uninsane.org".inet.AAAA."doof.tunnel" = "2602:fce8:106::51";
|
||||
networking.wireguard.interfaces.wg-doof = let
|
||||
ip = "${pkgs.iproute2}/bin/ip";
|
||||
in {
|
||||
privateKeyFile = config.sops.secrets.wg_doof_privkey.path;
|
||||
# wg is active only in this namespace.
|
||||
# run e.g. ip netns exec doof <some command like ping/curl/etc, it'll go through wg>
|
||||
# sudo ip netns exec doof ping www.google.com
|
||||
interfaceNamespace = "doof";
|
||||
ips = [
|
||||
"205.201.63.12"
|
||||
# "2602:fce8:106::51/128" #< TODO: enable IPv6
|
||||
"205.201.63.12/32"
|
||||
"2602:fce8:106::51/128"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
@@ -79,24 +63,45 @@ in
|
||||
persistentKeepalive = 25; #< keep the NAT alive
|
||||
}
|
||||
];
|
||||
preSetup = ''
|
||||
${ip} netns add doof || (test -e /run/netns/doof && echo "doof already exists")
|
||||
'';
|
||||
postShutdown = ''
|
||||
${ip} netns delete doof || echo "couldn't delete doof"
|
||||
'';
|
||||
};
|
||||
sane.netns.doof.hostVethIpv4 = "10.0.2.5";
|
||||
sane.netns.doof.netnsVethIpv4 = "10.0.2.6";
|
||||
sane.netns.doof.netnsPubIpv4 = "205.201.63.12";
|
||||
sane.netns.doof.routeTable = 12;
|
||||
|
||||
# OVPN CONFIG (https://www.ovpn.com):
|
||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
||||
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
||||
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
||||
networking.wireguard.enable = true;
|
||||
networking.wireguard.interfaces.wg-ovpns = {
|
||||
networking.wireguard.interfaces.wg-ovpns = let
|
||||
ip = "${pkgs.iproute2}/bin/ip";
|
||||
in-ns = "${ip} netns exec ovpns";
|
||||
iptables = "${pkgs.iptables}/bin/iptables";
|
||||
veth-host-ip = "10.0.1.5";
|
||||
veth-local-ip = "10.0.1.6";
|
||||
vpn-ip = "185.157.162.178";
|
||||
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
|
||||
vpn-dns = "46.227.67.134";
|
||||
bridgePort = port: proto: ''
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}
|
||||
'';
|
||||
bridgeStatements = lib.foldlAttrs
|
||||
(acc: port: portCfg: acc ++ (builtins.map (bridgePort port) portCfg.protocol))
|
||||
[]
|
||||
config.sane.ports.ports;
|
||||
in {
|
||||
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||
# wg is active only in this namespace.
|
||||
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
|
||||
# sudo ip netns exec ovpns ping www.google.com
|
||||
interfaceNamespace = "ovpns";
|
||||
ips = [ "185.157.162.178" ];
|
||||
ips = [
|
||||
"185.157.162.178/32"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
||||
@@ -114,11 +119,99 @@ in
|
||||
# dynamicEndpointRefreshRestartSeconds = 5;
|
||||
}
|
||||
];
|
||||
preSetup = ''
|
||||
${ip} netns add ovpns || (test -e /run/netns/ovpns && echo "ovpns already exists")
|
||||
'';
|
||||
postShutdown = ''
|
||||
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||
# restore rules/routes
|
||||
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
|
||||
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
|
||||
${ip} rule add from all lookup local pref 0
|
||||
${ip} rule del from all lookup local pref 100
|
||||
'';
|
||||
postSetup = ''
|
||||
# DOCS:
|
||||
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
||||
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
||||
# create veth pair
|
||||
${ip} link add ovpns-veth-a type veth peer name ovpns-veth-b
|
||||
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
|
||||
${ip} link set ovpns-veth-a up
|
||||
|
||||
# mv veth-b into the ovpns namespace
|
||||
${ip} link set ovpns-veth-b netns ovpns
|
||||
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
|
||||
${in-ns} ip link set ovpns-veth-b up
|
||||
|
||||
# make it so traffic originating from the host side of the veth
|
||||
# is sent over the veth no matter its destination.
|
||||
${ip} rule add from ${veth-host-ip} lookup ovpns pref 50
|
||||
# for traffic originating at the host veth to the WAN, use the veth as our gateway
|
||||
# not sure if the metric 1002 matters.
|
||||
${ip} route add default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
|
||||
# give the default route lower priority
|
||||
${ip} rule add from all lookup local pref 100
|
||||
${ip} rule del from all lookup local pref 0
|
||||
|
||||
# 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
|
||||
'' + (lib.concatStringsSep "\n" bridgeStatements);
|
||||
};
|
||||
sane.netns.ovpns.hostVethIpv4 = "10.0.1.5";
|
||||
sane.netns.ovpns.netnsVethIpv4 = "10.0.1.6";
|
||||
sane.netns.ovpns.netnsPubIpv4 = "185.157.162.178";
|
||||
sane.netns.ovpns.routeTable = 11;
|
||||
sane.netns.ovpns.dns = "46.227.67.134"; #< DNS requests inside the namespace are forwarded here
|
||||
|
||||
# create a new routing table that we can use to proxy traffic out of the root namespace
|
||||
# through the ovpns namespace, and to the WAN via VPN.
|
||||
networking.iproute2.rttablesExtraConfig = ''
|
||||
5 ovpns
|
||||
'';
|
||||
networking.iproute2.enable = true;
|
||||
|
||||
|
||||
# HURRICANE ELECTRIC CONFIG:
|
||||
# networking.sits = {
|
||||
# hurricane = {
|
||||
# remote = "216.218.226.238";
|
||||
# local = "192.168.0.5";
|
||||
# # local = "10.0.0.5";
|
||||
# # remote = "10.0.0.1";
|
||||
# # local = "10.0.0.22";
|
||||
# dev = "eth0";
|
||||
# ttl = 255;
|
||||
# };
|
||||
# };
|
||||
# networking.interfaces."hurricane".ipv6 = {
|
||||
# addresses = [
|
||||
# # mx.uninsane.org (publically routed /64)
|
||||
# {
|
||||
# address = "2001:470:b:465::1";
|
||||
# prefixLength = 128;
|
||||
# }
|
||||
# # client addr
|
||||
# # {
|
||||
# # address = "2001:470:a:466::2";
|
||||
# # prefixLength = 64;
|
||||
# # }
|
||||
# ];
|
||||
# routes = [
|
||||
# {
|
||||
# address = "::";
|
||||
# prefixLength = 0;
|
||||
# # via = "2001:470:a:466::1";
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
|
||||
# # after configuration, we want the hurricane device to look like this:
|
||||
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
|
||||
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
|
||||
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
|
||||
# # sit txqueuelen 1000 (IPv6-in-IPv4)
|
||||
# # test with:
|
||||
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
||||
# # ping 2607:f8b0:400a:80b::2004
|
||||
};
|
||||
}
|
||||
|
34
hosts/by-name/servo/services/calibre.nix
Normal file
34
hosts/by-name/servo/services/calibre.nix
Normal file
@@ -0,0 +1,34 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cweb-cfg = config.services.calibre-web;
|
||||
inherit (cweb-cfg) user group;
|
||||
inherit (cweb-cfg.listen) ip port;
|
||||
svc-dir = "/var/lib/${cweb-cfg.dataDir}";
|
||||
in
|
||||
# XXX: disabled because of runtime errors like:
|
||||
# > File "/nix/store/c7jqvx980nlg9xhxi065cba61r2ain9y-calibre-web-0.6.19/lib/python3.10/site-packages/calibreweb/cps/db.py", line 926, in speaking_language
|
||||
# > languages = self.session.query(Languages) \
|
||||
# > AttributeError: 'NoneType' object has no attribute 'query'
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ inherit user group; mode = "0700"; path = svc-dir; method = "bind"; }
|
||||
];
|
||||
|
||||
services.calibre-web.enable = true;
|
||||
services.calibre-web.listen.ip = "127.0.0.1";
|
||||
# XXX: externally populate `${svc-dir}/metadata.db` (once) from
|
||||
# <https://github.com/janeczku/calibre-web/blob/master/library/metadata.db>
|
||||
# i don't know why you have to do this??
|
||||
# services.calibre-web.options.calibreLibrary = svc-dir;
|
||||
|
||||
services.nginx.virtualHosts."calibre.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://${ip}:${builtins.toString port}";
|
||||
};
|
||||
};
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."calibre" = "native";
|
||||
}
|
@@ -36,8 +36,7 @@
|
||||
# - rb = received bytes
|
||||
# - sp = sent packets
|
||||
# - sb = sent bytes
|
||||
|
||||
{ config, lib, ... }:
|
||||
{ lib, ... }:
|
||||
let
|
||||
# TURN port range (inclusive).
|
||||
# default coturn behavior is to use the upper quarter of all ports. i.e. 49152 - 65535.
|
||||
@@ -56,7 +55,7 @@ in
|
||||
# protocol = [ "tcp" "udp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpns = true; # forward traffic from the VPN to the root NS
|
||||
# visibleTo.ovpn = true; # forward traffic from the VPN to the root NS
|
||||
# description = "colin-stun-turn";
|
||||
# };
|
||||
# "5349" = {
|
||||
@@ -64,7 +63,7 @@ in
|
||||
# protocol = [ "tcp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpns = true;
|
||||
# visibleTo.ovpn = true;
|
||||
# description = "colin-stun-turn-over-tls";
|
||||
# };
|
||||
# }
|
||||
@@ -77,7 +76,7 @@ in
|
||||
# protocol = [ "tcp" "udp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpns = true;
|
||||
# visibleTo.ovpn = true;
|
||||
# description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||
# };
|
||||
# })
|
||||
@@ -131,11 +130,11 @@ in
|
||||
"verbose"
|
||||
# "Verbose" #< even MORE verbosity than "verbose" (it's TOO MUCH verbosity really)
|
||||
"no-multicast-peers" # disables sending to IPv4 broadcast addresses (e.g. 224.0.0.0/3)
|
||||
# "listening-ip=${config.sane.netns.ovpns.hostVethIpv4}" "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}" #< 2024/04/25: works, if running in root namespace
|
||||
"listening-ip=${config.sane.netns.ovpns.netnsPubIpv4}" "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}"
|
||||
# "listening-ip=10.0.1.5" "external-ip=185.157.162.178" #< 2024/04/25: works, if running in root namespace
|
||||
"listening-ip=185.157.162.178" "external-ip=185.157.162.178"
|
||||
|
||||
# old attempts:
|
||||
# "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}/${config.sane.netns.ovpns.hostVethIpv4}"
|
||||
# "external-ip=185.157.162.178/10.0.1.5"
|
||||
# "listening-ip=10.78.79.51" # can be specified multiple times; omit for *
|
||||
# "external-ip=97.113.128.229/10.78.79.51"
|
||||
# "external-ip=97.113.128.229"
|
||||
|
@@ -16,17 +16,14 @@
|
||||
# - validate with `bitcoin-cli -netinfo`
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
let
|
||||
# bitcoind = config.sane.programs.bitcoind.packageUnwrapped;
|
||||
bitcoind = pkgs.bitcoind;
|
||||
# wrapper to run bitcoind with the tor onion address as externalip (computed at runtime)
|
||||
_bitcoindWithExternalIp = pkgs.writeShellScriptBin "bitcoind" ''
|
||||
set -xeu
|
||||
_bitcoindWithExternalIp = with pkgs; writeShellScriptBin "bitcoind" ''
|
||||
externalip="$(cat /var/lib/tor/onion/bitcoind/hostname)"
|
||||
exec ${bitcoind}/bin/bitcoind "-externalip=$externalip" "$@"
|
||||
'';
|
||||
# the package i provide to services.bitcoind ends up on system PATH, and used by other tools like clightning.
|
||||
# therefore, even though services.bitcoind only needs `bitcoind` binary, provide all the other bitcoin-related binaries (notably `bitcoin-cli`) as well:
|
||||
bitcoindWithExternalIp = pkgs.symlinkJoin {
|
||||
bitcoindWithExternalIp = with pkgs; symlinkJoin {
|
||||
name = "bitcoind-with-external-ip";
|
||||
paths = [ _bitcoindWithExternalIp bitcoind ];
|
||||
};
|
||||
@@ -64,62 +61,23 @@ in
|
||||
passwordHMAC = "30002c05d82daa210550e17a182db3f3$6071444151281e1aa8a2729f75e3e2d224e9d7cac3974810dab60e7c28ffaae4";
|
||||
};
|
||||
extraConfig = ''
|
||||
# checkblocks: default 6: how many blocks to verify on start
|
||||
checkblocks=3
|
||||
# don't load the wallet, and disable wallet RPC calls
|
||||
disablewallet=1
|
||||
# proxy all outbound traffic through Tor
|
||||
proxy=127.0.0.1:9050
|
||||
'';
|
||||
extraCmdlineOptions = [
|
||||
# "-debug"
|
||||
# "-debug=estimatefee"
|
||||
# "-debug=http"
|
||||
# "-debug=net"
|
||||
"-debug=proxy"
|
||||
"-debug=rpc"
|
||||
# "-debug=validation"
|
||||
];
|
||||
};
|
||||
|
||||
users.users.bitcoind-mainnet.extraGroups = [ "tor" ];
|
||||
|
||||
systemd.services.bitcoind-mainnet = {
|
||||
after = [ "tor.service" ];
|
||||
requires = [ "tor.service" ];
|
||||
serviceConfig.RestartSec = "30s"; #< default is 0
|
||||
|
||||
# hardening (systemd-analyze security bitcoind-mainnet)
|
||||
serviceConfig.StateDirectory = "bitcoind-mainnet";
|
||||
serviceConfig.LockPersonality = true;
|
||||
serviceConfig.MemoryDenyWriteExecute = "true";
|
||||
serviceConfig.NoNewPrivileges = "true";
|
||||
serviceConfig.PrivateDevices = "true";
|
||||
serviceConfig.PrivateMounts = true;
|
||||
serviceConfig.PrivateTmp = "true";
|
||||
serviceConfig.PrivateUsers = true;
|
||||
serviceConfig.ProcSubset = "pid";
|
||||
serviceConfig.ProtectControlGroups = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectHostname = true;
|
||||
serviceConfig.ProtectKernelLogs = true;
|
||||
serviceConfig.ProtectKernelModules = true;
|
||||
serviceConfig.ProtectKernelTunables = true;
|
||||
serviceConfig.ProtectProc = "invisible";
|
||||
serviceConfig.ProtectSystem = lib.mkForce "strict";
|
||||
serviceConfig.RemoveIPC = true;
|
||||
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
serviceConfig.RestrictNamespaces = true;
|
||||
serviceConfig.RestrictSUIDSGID = true;
|
||||
serviceConfig.SystemCallArchitectures = "native";
|
||||
serviceConfig.SystemCallFilter = [ "@system-service" ];
|
||||
};
|
||||
systemd.services.bitcoind-mainnet.serviceConfig.RestartSec = "30s"; #< default is 0
|
||||
|
||||
sane.users.colin.fs.".bitcoin/bitcoin.conf" = sane-lib.fs.wantedSymlinkTo config.sops.secrets."bitcoin.conf".path;
|
||||
sops.secrets."bitcoin.conf" = {
|
||||
mode = "0600";
|
||||
owner = "colin";
|
||||
group = "users";
|
||||
};
|
||||
|
||||
sane.programs.bitcoin-cli.enableFor.user.colin = true; # for debugging/administration: `bitcoin-cli`
|
||||
sane.programs.bitcoind.enableFor.user.colin = true; # for debugging/administration: `bitcoin-cli`
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p pyln-client -p python3
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.pyln-client ])"
|
||||
|
||||
"""
|
||||
clightning-sane: helper to perform common Lightning node admin operations:
|
||||
@@ -742,7 +742,7 @@ def main():
|
||||
logging.basicConfig()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
parser = argparse.ArgumentParser(usage=__doc__)
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--verbose", action="store_true", help="more logging")
|
||||
parser.add_argument("--min-msat", default="999", help="min transaction size")
|
||||
parser.add_argument("--max-msat", default="1000000", help="max transaction size")
|
@@ -72,11 +72,13 @@
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
# clightning takes up only a few MB. but then several hundred MB of crash logs that i should probably GC.
|
||||
sane.persist.sys.byStore.ext = [
|
||||
{ user = "clightning"; group = "clightning"; mode = "0710"; path = "/var/lib/clightning"; method = "bind"; }
|
||||
];
|
||||
|
||||
# `lightning-cli` finds its RPC file via `~/.lightning/bitcoin/lightning-rpc`, to message the daemon
|
||||
sane.user.fs.".lightning".symlink.target = "/var/lib/clightning";
|
||||
|
||||
# see bitcoin.nix for how to generate this
|
||||
services.bitcoind.mainnet.rpc.users.clightning.passwordHMAC =
|
||||
"befcb82d9821049164db5217beb85439$2c31ac7db3124612e43893ae13b9527dbe464ab2d992e814602e7cb07dc28985";
|
||||
@@ -103,7 +105,6 @@
|
||||
users.users.clightning.extraGroups = [ "tor" ];
|
||||
|
||||
systemd.services.clightning.after = [ "tor.service" ];
|
||||
systemd.services.clightning.requires = [ "tor.service" ];
|
||||
|
||||
# lightning-config contains fields from here:
|
||||
# - <https://docs.corelightning.org/docs/configuration>
|
||||
@@ -115,16 +116,11 @@
|
||||
# - fee-per-satoshi=<ppm>
|
||||
# - feature configs (i.e. experimental-xyz options)
|
||||
sane.services.clightning.extraConfig = ''
|
||||
# log levels: "io", "debug", "info", "unusual", "broken"
|
||||
log-level=info
|
||||
# log-level=info:lightningd
|
||||
# log-level=debug:lightningd
|
||||
# log-level=debug
|
||||
|
||||
log-level=debug:lightningd
|
||||
# peerswap:
|
||||
# - config example: <https://github.com/fort-nix/nix-bitcoin/pull/462/files#diff-b357d832705b8ce8df1f41934d613f79adb77c4cd5cd9e9eb12a163fca3e16c6>
|
||||
# XXX: peerswap crashes clightning on launch. stacktrace is useless.
|
||||
# plugin={pkgs.peerswap}/bin/peerswap
|
||||
# plugin=${pkgs.peerswap}/bin/peerswap
|
||||
# peerswap-db-path=/var/lib/clightning/peerswap/swaps
|
||||
# peerswap-policy-path=...
|
||||
'';
|
||||
@@ -135,5 +131,5 @@
|
||||
group = "clightning";
|
||||
};
|
||||
|
||||
sane.programs.lightning-cli.enableFor.user.colin = true; # for debugging/admin:
|
||||
sane.programs.clightning.enableFor.user.colin = true; # for debugging/admin: `lightning-cli`
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{ lib, ... }:
|
||||
lib.mkIf false #< 2024/07/27: i don't use it, too much surface-area for me to run it pro-bono (`systemd-analyze security monero`)
|
||||
{ ... }:
|
||||
{
|
||||
services.i2p.enable = true;
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
# as of 2023/11/26: complete downloaded blockchain should be 200GiB on disk, give or take.
|
||||
{ lib, ... }:
|
||||
lib.mkIf false #< 2024/07/27: i don't use it, too much surface-area for me to run it pro-bono (`systemd-analyze security monero`)
|
||||
{ ... }:
|
||||
{
|
||||
sane.persist.sys.byStore.ext = [
|
||||
# /var/lib/monero/lmdb is what consumes most of the space
|
||||
|
@@ -1,10 +1,10 @@
|
||||
# tor settings: <https://2019.www.torproject.org/docs/tor-manual.html.en>
|
||||
{ lib, ... }:
|
||||
{
|
||||
sane.persist.sys.byStore.ephemeral = [
|
||||
# N.B.: tor hidden service hostnames aren't deterministic, so if you need them
|
||||
# to be preserved across reboots then persist /var/lib/tor/onion in "private" store.
|
||||
{ user = "tor"; group = "tor"; mode = "0710"; path = "/var/lib/tor"; method = "bind"; }
|
||||
# tor hidden service hostnames aren't deterministic, so persist.
|
||||
# might be able to get away with just persisting /var/lib/tor/onion, not sure.
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "tor"; group = "tor"; mode = "0710"; path = "/var/lib/tor"; method = "bind"; }
|
||||
];
|
||||
|
||||
# tor: `tor.enable` doesn't start a relay, exit node, proxy, etc. it's minimal.
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./calibre.nix
|
||||
./coturn.nix
|
||||
./cryptocurrencies
|
||||
./email
|
||||
@@ -10,7 +11,7 @@
|
||||
./gitea.nix
|
||||
./goaccess.nix
|
||||
./ipfs.nix
|
||||
./jackett
|
||||
./jackett.nix
|
||||
./jellyfin.nix
|
||||
./kiwix-serve.nix
|
||||
./komga.nix
|
||||
@@ -25,7 +26,7 @@
|
||||
./postgres.nix
|
||||
./prosody
|
||||
./slskd.nix
|
||||
./transmission
|
||||
./transmission.nix
|
||||
./trust-dns.nix
|
||||
./wikipedia.nix
|
||||
];
|
||||
|
@@ -44,61 +44,61 @@ in
|
||||
# everything configured below was fine: used ejabberd for several months.
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "ejabberd"; group = "ejabberd"; path = "/var/lib/ejabberd"; method = "bind"; }
|
||||
];
|
||||
sane.ports.ports = lib.mkMerge ([
|
||||
{
|
||||
"3478" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn";
|
||||
};
|
||||
"5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
"5223" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||
};
|
||||
"5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
"5270" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||
};
|
||||
"5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
"5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh-https";
|
||||
};
|
||||
"5349" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn-over-tls";
|
||||
};
|
||||
"5443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
||||
};
|
||||
}
|
||||
@@ -109,8 +109,8 @@ lib.mkIf false
|
||||
numPorts = turnPortHigh - turnPortLow + 1;
|
||||
in {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||
};
|
||||
})
|
||||
|
@@ -8,14 +8,14 @@
|
||||
{
|
||||
sane.ports.ports."143" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imap-imap.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."993" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imaps-imap.uninsane.org";
|
||||
};
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
# sieve_plugins = sieve_imapsieve
|
||||
# }
|
||||
|
||||
# mail_debug = yes
|
||||
# auth_debug = yes
|
||||
mail_debug = yes
|
||||
auth_debug = yes
|
||||
# verbose_ssl = yes
|
||||
'';
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# postfix config options: <https://www.postfix.org/postconf.5.html>
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
submissionOptions = {
|
||||
@@ -18,10 +18,10 @@ let
|
||||
};
|
||||
in
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "opendkim"; group = "opendkim"; path = "/var/lib/opendkim"; method = "bind"; }
|
||||
{ user = "root"; group = "root"; path = "/var/lib/postfix"; method = "bind"; } #< probably not *all* of postfix needs to actually be persisted (e.g. not the conf dir)
|
||||
{ user = "root"; group = "root"; path = "/var/lib/postfix"; method = "bind"; }
|
||||
{ user = "root"; group = "root"; path = "/var/spool/mail"; method = "bind"; }
|
||||
# *probably* don't need these dirs:
|
||||
# "/var/lib/dhparams" # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/dhparams.nix
|
||||
@@ -56,7 +56,8 @@ in
|
||||
|
||||
sane.dns.zones."uninsane.org".inet = {
|
||||
MX."@" = "10 mx.uninsane.org.";
|
||||
A."mx" = "%AOVPNS%"; #< XXX: RFC's specify that the MX record CANNOT BE A CNAME. TODO: use "%AOVPNS%?
|
||||
# XXX: RFC's specify that the MX record CANNOT BE A CNAME
|
||||
A."mx" = "185.157.162.178";
|
||||
|
||||
# Sender Policy Framework:
|
||||
# +mx => mail passes if it originated from the MX
|
||||
|
@@ -37,8 +37,7 @@
|
||||
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||
file.text = ''
|
||||
- media/ read-only: Videos, Music, Books, etc
|
||||
- playground/ read-write: use it to share files with other users of this server, inaccessible from the www
|
||||
- pub/ read-only: content made to be shared with the www
|
||||
- playground/ read-write: use it to share files with other users of this server
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -51,11 +50,4 @@
|
||||
- be a friendly troll
|
||||
'';
|
||||
};
|
||||
|
||||
sane.fs."/var/export/.public_for_test/test" = {
|
||||
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||
file.text = ''
|
||||
automated tests read this file to probe connectivity
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
@@ -9,10 +9,10 @@
|
||||
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
let
|
||||
external_auth_hook = pkgs.static-nix-shell.mkPython3 {
|
||||
external_auth_hook = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "external_auth_hook";
|
||||
srcRoot = ./.;
|
||||
pkgs = [ "python3.pkgs.passlib" ];
|
||||
pyPkgs = [ "passlib" ];
|
||||
};
|
||||
# Client initiates a FTP "control connection" on port 21.
|
||||
# - this handles the client -> server commands, and the server -> client status, but not the actual data
|
||||
@@ -27,12 +27,13 @@ in
|
||||
"21" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
# visibleTo.wan = true;
|
||||
description = "colin-FTP server";
|
||||
};
|
||||
"990" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-FTPS server";
|
||||
};
|
||||
} // (sane-lib.mapToAttrs
|
||||
@@ -40,8 +41,8 @@ in
|
||||
name = builtins.toString port;
|
||||
value = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-FTP server data port range";
|
||||
};
|
||||
})
|
||||
@@ -80,6 +81,12 @@ in
|
||||
port = 21;
|
||||
debug = true;
|
||||
}
|
||||
{
|
||||
# binding this means any LAN client can connect (also WAN traffic forwarded from the gateway)
|
||||
address = "10.78.79.51";
|
||||
port = 21;
|
||||
debug = true;
|
||||
}
|
||||
{
|
||||
# binding this means any wireguard client can connect
|
||||
address = "10.0.10.5";
|
||||
@@ -90,26 +97,6 @@ in
|
||||
{
|
||||
# binding this means any LAN client can connect (also WAN traffic forwarded from the gateway)
|
||||
address = "10.78.79.51";
|
||||
port = 21;
|
||||
debug = true;
|
||||
}
|
||||
{
|
||||
# binding this means any LAN client can connect (also WAN traffic forwarded from the gateway)
|
||||
address = "10.78.79.51";
|
||||
port = 990;
|
||||
debug = true;
|
||||
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
|
||||
}
|
||||
{
|
||||
# binding this means any doof client can connect (TLS only)
|
||||
address = config.sane.netns.doof.hostVethIpv4;
|
||||
port = 990;
|
||||
debug = true;
|
||||
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
|
||||
}
|
||||
{
|
||||
# binding this means any LAN client can connect via `ftp.uninsane.org` (TLS only)
|
||||
address = config.sane.netns.doof.netnsPubIpv4;
|
||||
port = 990;
|
||||
debug = true;
|
||||
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
|
||||
@@ -130,7 +117,7 @@ in
|
||||
banner = ''
|
||||
Welcome, friends, to Colin's FTP server! Also available via NFS on the same host, but LAN-only.
|
||||
|
||||
Read-only access (LAN clients see everything; WAN clients can only see /pub):
|
||||
Read-only access (LAN-restricted):
|
||||
Username: "anonymous"
|
||||
Password: "anonymous"
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p python3 -p python3.pkgs.passlib
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.passlib ])"
|
||||
# vim: set filetype=python :
|
||||
#
|
||||
# available environment variables:
|
||||
@@ -45,8 +45,6 @@ from hmac import compare_digest
|
||||
|
||||
authFail = dict(username="")
|
||||
|
||||
PERM_DENY = []
|
||||
PERM_LIST = [ "list" ]
|
||||
PERM_RO = [ "list", "download" ]
|
||||
PERM_RW = [
|
||||
# read-only:
|
||||
@@ -129,14 +127,12 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
|
||||
return mkAuthOk(username, permissions = {
|
||||
"/": PERM_RW,
|
||||
"/playground": PERM_RW,
|
||||
"/.public_for_test": PERM_RO,
|
||||
})
|
||||
if isWireguard(ip):
|
||||
# allow any user from wireguard
|
||||
return mkAuthOk(username, permissions = {
|
||||
"/": PERM_RW,
|
||||
"/playground": PERM_RW,
|
||||
"/.public_for_test": PERM_RO,
|
||||
})
|
||||
if isLan(ip):
|
||||
if username == "anonymous":
|
||||
@@ -144,19 +140,7 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
|
||||
return mkAuthOk("anonymous", permissions = {
|
||||
"/": PERM_RO,
|
||||
"/playground": PERM_RW,
|
||||
"/.public_for_test": PERM_RO,
|
||||
})
|
||||
if username == "anonymous":
|
||||
# anonymous users from the www can have even more limited access.
|
||||
# mostly because i need an easy way to test WAN connectivity :-)
|
||||
return mkAuthOk("anonymous", permissions = {
|
||||
# "/": PERM_DENY,
|
||||
"/": PERM_LIST, #< REQUIRED, even for lftp to list a subdir
|
||||
"/media": PERM_DENY,
|
||||
"/playground": PERM_DENY,
|
||||
"/.public_for_test": PERM_RO,
|
||||
# "/README.md": PERM_RO, #< does not work
|
||||
})
|
||||
|
||||
return authFail
|
||||
|
||||
|
@@ -10,7 +10,6 @@
|
||||
# ```
|
||||
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
lib.mkIf false #< 2024/07/04: i haven't actively used this for months
|
||||
{
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
owner = config.users.users.freshrss.name;
|
||||
|
@@ -2,8 +2,9 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
{ user = "git"; group = "gitea"; mode = "0750"; path = "/var/lib/gitea"; method = "bind"; }
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "git"; group = "gitea"; path = "/var/lib/gitea"; method = "bind"; }
|
||||
];
|
||||
services.gitea.enable = true;
|
||||
services.gitea.user = "git"; # default is 'gitea'
|
||||
@@ -37,23 +38,19 @@
|
||||
ROOT_URL = "https://git.uninsane.org/";
|
||||
};
|
||||
service = {
|
||||
# timeout for email approval. 5760 = 4 days. 10080 = 7 days
|
||||
ACTIVE_CODE_LIVE_MINUTES = 10080;
|
||||
# timeout for email approval. 5760 = 4 days
|
||||
ACTIVE_CODE_LIVE_MINUTES = 5760;
|
||||
# REGISTER_EMAIL_CONFIRM = false;
|
||||
# REGISTER_MANUAL_CONFIRM = true;
|
||||
REGISTER_EMAIL_CONFIRM = true;
|
||||
# not sure what this notifies *on*...
|
||||
# not sure what this notified on?
|
||||
ENABLE_NOTIFY_MAIL = true;
|
||||
# defaults to image-based captcha.
|
||||
# also supports recaptcha (with custom URLs) or hCaptcha.
|
||||
ENABLE_CAPTCHA = true;
|
||||
NOREPLY_ADDRESS = "noreply.anonymous.git@uninsane.org";
|
||||
};
|
||||
session = {
|
||||
COOKIE_SECURE = true;
|
||||
# keep me logged in for 30 days
|
||||
SESSION_LIFE_TIME = 60 * 60 * 24 * 30;
|
||||
};
|
||||
session.COOKIE_SECURE = true;
|
||||
repository = {
|
||||
DEFAULT_BRANCH = "master";
|
||||
ENABLE_PUSH_CREATE_USER = true;
|
||||
@@ -63,8 +60,8 @@
|
||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
|
||||
};
|
||||
ui = {
|
||||
# options: "gitea-auto" (adapt to system theme), "gitea-dark", "gitea-light"
|
||||
# DEFAULT_THEME = "gitea-auto";
|
||||
# options: "auto", "gitea", "arc-green"
|
||||
DEFAULT_THEME = "arc-green";
|
||||
# cache frontend assets if true
|
||||
# USE_SERVICE_WORKER = true;
|
||||
};
|
||||
@@ -73,10 +70,9 @@
|
||||
# alternative is to use nixos-level config:
|
||||
# services.gitea.mailerPasswordFile = ...
|
||||
ENABLED = true;
|
||||
MAILER_TYPE = "sendmail";
|
||||
FROM = "notify.git@uninsane.org";
|
||||
PROTOCOL = "sendmail";
|
||||
SENDMAIL_PATH = "${pkgs.postfix}/bin/sendmail";
|
||||
SENDMAIL_ARGS = "--"; # most "sendmail" programs take options, "--" will prevent an email address being interpreted as an option.
|
||||
};
|
||||
time = {
|
||||
# options: ANSIC, UnixDate, RubyDate, RFC822, RFC822Z, RFC850, RFC1123, RFC1123Z, RFC3339, RFC3339Nano, Kitchen, Stamp, StampMilli, StampMicro, StampNano
|
||||
@@ -108,10 +104,6 @@
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:3000";
|
||||
};
|
||||
# fuck you @anthropic
|
||||
locations."= /robots.txt".extraConfig = ''
|
||||
return 200 "User-agent: *\nDisallow: /\n";
|
||||
'';
|
||||
# gitea serves all `raw` files as content-type: plain, but i'd like to serve them as their actual content type.
|
||||
# or at least, enough to make specific pages viewable (serving unoriginal content as arbitrary content type is dangerous).
|
||||
locations."~ ^/colin/phone-case-cq/raw/.*.html" = {
|
||||
@@ -137,7 +129,7 @@
|
||||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-git@git.uninsane.org";
|
||||
};
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
lib.mkIf false # i don't actively use ipfs anymore
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "261"; group = "261"; path = "/var/lib/ipfs"; method = "bind"; }
|
||||
];
|
||||
|
36
hosts/by-name/servo/services/jackett.nix
Normal file
36
hosts/by-name/servo/services/jackett.nix
Normal file
@@ -0,0 +1,36 @@
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
lib.mkIf false #< TODO: re-enable once confident of sandboxing
|
||||
{
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
||||
{ user = "root"; group = "root"; path = "/var/lib/jackett"; method = "bind"; }
|
||||
];
|
||||
services.jackett.enable = true;
|
||||
|
||||
systemd.services.jackett.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
|
||||
|
||||
# patch jackett to listen on the public interfaces
|
||||
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
|
||||
};
|
||||
|
||||
# jackett torrent search
|
||||
services.nginx.virtualHosts."jackett.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
# proxyPass = "http://ovpns.uninsane.org:9117";
|
||||
proxyPass = "http://10.0.1.6:9117";
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
};
|
||||
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
|
||||
}
|
||||
|
@@ -1,68 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.jackett;
|
||||
in
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
||||
{ user = "jackett"; group = "jackett"; path = "/var/lib/jackett"; method = "bind"; }
|
||||
];
|
||||
services.jackett.enable = true;
|
||||
|
||||
systemd.services.jackett.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett = {
|
||||
# run this behind the OVPN static VPN
|
||||
serviceConfig.NetworkNamespacePath = "/run/netns/ovpns";
|
||||
serviceConfig.ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
||||
# patch in `--ListenPublic` so that it's reachable from the netns veth.
|
||||
# this also makes it reachable from the VPN pub address. oh well.
|
||||
serviceConfig.ExecStart = lib.mkForce "${cfg.package}/bin/Jackett --ListenPublic --NoUpdates --DataFolder '${cfg.dataDir}'";
|
||||
serviceConfig.RestartSec = "30s";
|
||||
|
||||
# hardening (systemd-analyze security jackett)
|
||||
# TODO: upstream into nixpkgs
|
||||
serviceConfig.StateDirectory = "jackett";
|
||||
serviceConfig.LockPersonality = true;
|
||||
serviceConfig.NoNewPrivileges = true;
|
||||
# serviceConfig.MemoryDenyWriteExecute = true; #< Failed to create CoreCLR, HRESULT: 0x80004005
|
||||
serviceConfig.PrivateDevices = true;
|
||||
serviceConfig.PrivateMounts = true;
|
||||
serviceConfig.PrivateTmp = true;
|
||||
serviceConfig.PrivateUsers = true;
|
||||
serviceConfig.ProcSubset = "pid";
|
||||
serviceConfig.ProtectClock = true;
|
||||
serviceConfig.ProtectControlGroups = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectHostname = true;
|
||||
serviceConfig.ProtectKernelLogs = true;
|
||||
serviceConfig.ProtectKernelModules = true;
|
||||
serviceConfig.ProtectKernelTunables = true;
|
||||
serviceConfig.ProtectProc = "invisible";
|
||||
serviceConfig.ProtectSystem = "strict";
|
||||
serviceConfig.RemoveIPC = true;
|
||||
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
serviceConfig.RestrictNamespaces = true;
|
||||
serviceConfig.RestrictSUIDSGID = true;
|
||||
serviceConfig.SystemCallArchitectures = "native";
|
||||
serviceConfig.SystemCallFilter = [ "@system-service" "~@privileged" ];
|
||||
};
|
||||
|
||||
# jackett torrent search
|
||||
services.nginx.virtualHosts."jackett.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9117";
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
locations."= /robots.txt".extraConfig = ''
|
||||
return 200 "User-agent: *\nDisallow: /\n";
|
||||
'';
|
||||
};
|
||||
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
|
||||
}
|
||||
|
@@ -21,9 +21,6 @@
|
||||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/".proxyPass = "http://127.0.0.1:8013";
|
||||
locations."= /robots.txt".extraConfig = ''
|
||||
return 200 "User-agent: *\nDisallow: /\n";
|
||||
'';
|
||||
};
|
||||
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."w" = "native";
|
||||
|
@@ -17,9 +17,6 @@ in
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${builtins.toString port}";
|
||||
};
|
||||
locations."= /robots.txt".extraConfig = ''
|
||||
return 200 "User-agent: *\nDisallow: /\n";
|
||||
'';
|
||||
};
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."komga" = "native";
|
||||
}
|
||||
|
@@ -38,10 +38,14 @@ in {
|
||||
nginx.enable = true;
|
||||
};
|
||||
|
||||
systemd.services.lemmy.serviceConfig = {
|
||||
# fix to use a normal user so we can configure perms correctly
|
||||
DynamicUser = mkForce false;
|
||||
User = "lemmy";
|
||||
Group = "lemmy";
|
||||
};
|
||||
systemd.services.lemmy.environment = {
|
||||
RUST_BACKTRACE = "full";
|
||||
RUST_LOG = "error";
|
||||
# RUST_LOG = "warn";
|
||||
# RUST_LOG = "debug";
|
||||
# RUST_LOG = "trace";
|
||||
# upstream defaults LEMMY_DATABASE_URL = "postgres:///lemmy?host=/run/postgresql";
|
||||
@@ -68,73 +72,6 @@ in {
|
||||
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
|
||||
|
||||
systemd.services.lemmy = {
|
||||
# fix to use a normal user so we can configure perms correctly
|
||||
# XXX(2024-07-28): this hasn't been rigorously tested:
|
||||
# possible that i've set something too strict and won't notice right away
|
||||
serviceConfig.DynamicUser = mkForce false;
|
||||
serviceConfig.User = "lemmy";
|
||||
serviceConfig.Group = "lemmy";
|
||||
|
||||
# hardening (systemd-analyze security lemmy)
|
||||
# a handful of these are specified in upstream nixpkgs, but mostly not
|
||||
serviceConfig.LockPersonality = true;
|
||||
serviceConfig.NoNewPrivileges = true;
|
||||
serviceConfig.MemoryDenyWriteExecute = true;
|
||||
serviceConfig.PrivateDevices = true;
|
||||
serviceConfig.PrivateMounts = true;
|
||||
serviceConfig.PrivateTmp = true;
|
||||
serviceConfig.PrivateUsers = true;
|
||||
serviceConfig.ProcSubset = "pid";
|
||||
|
||||
serviceConfig.ProtectClock = true;
|
||||
serviceConfig.ProtectControlGroups = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectHostname = true;
|
||||
serviceConfig.ProtectKernelLogs = true;
|
||||
serviceConfig.ProtectKernelModules = true;
|
||||
serviceConfig.ProtectKernelTunables = true;
|
||||
serviceConfig.ProtectProc = "invisible";
|
||||
serviceConfig.ProtectSystem = "strict";
|
||||
serviceConfig.RemoveIPC = true;
|
||||
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
|
||||
serviceConfig.RestrictNamespaces = true;
|
||||
serviceConfig.RestrictSUIDSGID = true;
|
||||
serviceConfig.SystemCallArchitectures = "native";
|
||||
serviceConfig.SystemCallFilter = [ "@system-service" ];
|
||||
};
|
||||
|
||||
systemd.services.lemmy-ui = {
|
||||
# hardening (systemd-analyze security lemmy-ui)
|
||||
# TODO: upstream into nixpkgs
|
||||
serviceConfig.LockPersonality = true;
|
||||
serviceConfig.NoNewPrivileges = true;
|
||||
# serviceConfig.MemoryDenyWriteExecute = true; #< it uses v8, JIT
|
||||
serviceConfig.PrivateDevices = true;
|
||||
serviceConfig.PrivateMounts = true;
|
||||
serviceConfig.PrivateTmp = true;
|
||||
serviceConfig.PrivateUsers = true;
|
||||
serviceConfig.ProcSubset = "pid";
|
||||
|
||||
serviceConfig.ProtectClock = true;
|
||||
serviceConfig.ProtectControlGroups = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectHostname = true;
|
||||
serviceConfig.ProtectKernelLogs = true;
|
||||
serviceConfig.ProtectKernelModules = true;
|
||||
serviceConfig.ProtectKernelTunables = true;
|
||||
serviceConfig.ProtectProc = "invisible";
|
||||
serviceConfig.ProtectSystem = "strict";
|
||||
serviceConfig.RemoveIPC = true;
|
||||
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
|
||||
serviceConfig.RestrictNamespaces = true;
|
||||
serviceConfig.RestrictSUIDSGID = true;
|
||||
serviceConfig.SystemCallArchitectures = "native";
|
||||
serviceConfig.SystemCallFilter = [ "@system-service" "@pkey" "@sandbox" ];
|
||||
};
|
||||
|
||||
#v DO NOT REMOVE: defaults to 0.3, instead of latest, so always need to explicitly set this.
|
||||
services.pict-rs.package = pict-rs;
|
||||
|
||||
@@ -144,38 +81,10 @@ in {
|
||||
# - via CLI flags (overrides everything above)
|
||||
# some of the CLI flags have defaults, making it the only actual way to configure certain things even when docs claim otherwise.
|
||||
# CLI args: <https://git.asonix.dog/asonix/pict-rs#user-content-running>
|
||||
systemd.services.pict-rs = {
|
||||
serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [
|
||||
"${lib.getBin pict-rs}/bin/pict-rs run"
|
||||
"--media-video-max-frame-count" (builtins.toString (30*60*60))
|
||||
"--media-process-timeout 120"
|
||||
"--media-video-allow-audio" # allow audio
|
||||
]);
|
||||
|
||||
# hardening (systemd-analyze security pict-rs)
|
||||
# TODO: upstream into nixpkgs
|
||||
serviceConfig.LockPersonality = true;
|
||||
serviceConfig.NoNewPrivileges = true;
|
||||
serviceConfig.MemoryDenyWriteExecute = true;
|
||||
serviceConfig.PrivateDevices = true;
|
||||
serviceConfig.PrivateMounts = true;
|
||||
serviceConfig.PrivateTmp = true;
|
||||
serviceConfig.PrivateUsers = true;
|
||||
serviceConfig.ProcSubset = "pid";
|
||||
serviceConfig.ProtectClock = true;
|
||||
serviceConfig.ProtectControlGroups = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectHostname = true;
|
||||
serviceConfig.ProtectKernelLogs = true;
|
||||
serviceConfig.ProtectKernelModules = true;
|
||||
serviceConfig.ProtectKernelTunables = true;
|
||||
serviceConfig.ProtectProc = "invisible";
|
||||
serviceConfig.ProtectSystem = "strict";
|
||||
serviceConfig.RemoveIPC = true;
|
||||
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
serviceConfig.RestrictNamespaces = true;
|
||||
serviceConfig.RestrictSUIDSGID = true;
|
||||
serviceConfig.SystemCallArchitectures = "native";
|
||||
serviceConfig.SystemCallFilter = [ "@system-service" ];
|
||||
};
|
||||
systemd.services.pict-rs.serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [
|
||||
"${lib.getBin pict-rs}/bin/pict-rs run"
|
||||
"--media-video-max-frame-count" (builtins.toString (30*60*60))
|
||||
"--media-process-timeout 120"
|
||||
"--media-video-allow-audio" # allow audio
|
||||
]);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# docs: <https://nixos.wiki/wiki/Matrix>
|
||||
# docs: <https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse>
|
||||
# example config: <https://github.com/element-hq/synapse/blob/develop/docs/sample_config.yaml>
|
||||
# example config: <https://github.com/matrix-org/synapse/blob/develop/docs/sample_config.yaml>
|
||||
#
|
||||
# ENABLING PUSH NOTIFICATIONS (with UnifiedPush/ntfy):
|
||||
# - Matrix "pushers" API spec: <https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3pushersset>
|
||||
@@ -20,12 +20,14 @@
|
||||
./signal.nix
|
||||
];
|
||||
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/matrix-synapse"; method = "bind"; }
|
||||
];
|
||||
services.matrix-synapse.enable = true;
|
||||
services.matrix-synapse.log.root.level = "ERROR"; # accepts "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" (?)
|
||||
services.matrix-synapse.settings = {
|
||||
# this changes the default log level from INFO to WARN.
|
||||
# maybe there's an easier way?
|
||||
log_config = ./synapse-log_level.yaml;
|
||||
server_name = "uninsane.org";
|
||||
|
||||
# services.matrix-synapse.enable_registration_captcha = true;
|
||||
|
@@ -5,7 +5,7 @@
|
||||
# - recommended to use mautrix-discord: <https://github.com/NixOS/nixpkgs/pull/200462>
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/mx-puppet-discord"; method = "bind"; }
|
||||
];
|
||||
|
||||
|
@@ -1,13 +1,15 @@
|
||||
# config docs:
|
||||
# - <https://github.com/matrix-org/matrix-appservice-irc/blob/develop/config.sample.yaml>
|
||||
# probably want to remove that.
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
ircServer = { name, additionalAddresses ? [], ssl ? true, sasl ? true, port ? if ssl then 6697 else 6667 }: let
|
||||
ircServer = { name, additionalAddresses ? [], sasl ? true, port ? 6697 }: let
|
||||
lowerName = lib.toLower name;
|
||||
in {
|
||||
# XXX sasl: appservice doesn't support NickServ identification (only SASL, or PASS if sasl = false)
|
||||
inherit additionalAddresses name port sasl ssl;
|
||||
inherit name additionalAddresses sasl port;
|
||||
ssl = true;
|
||||
botConfig = {
|
||||
# bot has no presence in IRC channel; only real Matrix users
|
||||
enabled = false;
|
||||
@@ -99,7 +101,7 @@ in
|
||||
})
|
||||
];
|
||||
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode?
|
||||
{ user = "matrix-appservice-irc"; group = "matrix-appservice-irc"; path = "/var/lib/matrix-appservice-irc"; method = "bind"; }
|
||||
];
|
||||
@@ -127,7 +129,6 @@ in
|
||||
};
|
||||
|
||||
ircService = {
|
||||
logging.level = "warn"; # "error", "warn", "info", "debug"
|
||||
servers = {
|
||||
"irc.esper.net" = ircServer {
|
||||
name = "esper";
|
||||
@@ -155,10 +156,6 @@ in
|
||||
# - #sxmo-offtopic
|
||||
};
|
||||
"irc.rizon.net" = ircServer { name = "Rizon"; };
|
||||
"wigle.net" = ircServer {
|
||||
name = "WiGLE";
|
||||
ssl = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
lib.mkIf false # disabled 2024/01/11: i don't use it, and pkgs.mautrix-signal had some API changes
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "mautrix-signal"; group = "mautrix-signal"; path = "/var/lib/mautrix-signal"; method = "bind"; }
|
||||
{ user = "signald"; group = "signald"; path = "/var/lib/signald"; method = "bind"; }
|
||||
];
|
||||
|
27
hosts/by-name/servo/services/matrix/synapse-log_level.yaml
Normal file
27
hosts/by-name/servo/services/matrix/synapse-log_level.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: 1
|
||||
|
||||
# In systemd's journal, loglevel is implicitly stored, so let's omit it
|
||||
# from the message text.
|
||||
formatters:
|
||||
journal_fmt:
|
||||
format: '%(name)s: [%(request)s] %(message)s'
|
||||
|
||||
filters:
|
||||
context:
|
||||
(): synapse.util.logcontext.LoggingContextFilter
|
||||
request: ""
|
||||
|
||||
handlers:
|
||||
journal:
|
||||
class: systemd.journal.JournalHandler
|
||||
formatter: journal_fmt
|
||||
filters: [context]
|
||||
SYSLOG_IDENTIFIER: synapse
|
||||
|
||||
# default log level: INFO
|
||||
root:
|
||||
level: WARN
|
||||
handlers: [journal]
|
||||
|
||||
disable_existing_loggers: False
|
||||
|
@@ -17,24 +17,18 @@ in
|
||||
sane.ports.ports."80" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.ovpns = true; # so that letsencrypt can procure a cert for the mx record
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
|
||||
description = "colin-http-uninsane.org";
|
||||
};
|
||||
sane.ports.ports."443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-https-uninsane.org";
|
||||
};
|
||||
|
||||
services.nginx.enable = true;
|
||||
# nginxStable is one release behind nginxMainline.
|
||||
# nginx itself recommends running mainline; nixos defaults to stable.
|
||||
# services.nginx.package = pkgs.nginxMainline;
|
||||
# XXX(2024-07-31): nixos defaults to zlib-ng -- supposedly more performant, but spams log with
|
||||
# "gzip filter failed to use preallocated memory: ..."
|
||||
services.nginx.package = pkgs.nginxMainline.override { zlib = pkgs.zlib; };
|
||||
services.nginx.appendConfig = ''
|
||||
# use 1 process per core.
|
||||
# may want to increase worker_connections too, but `ulimit -n` must be increased first.
|
||||
@@ -50,10 +44,8 @@ in
|
||||
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referrer" "$http_user_agent"';
|
||||
access_log /var/log/nginx/private.log vcombined;
|
||||
'';
|
||||
# enables gzip and sets gzip_comp_level = 5
|
||||
# sets gzip_comp_level = 5
|
||||
services.nginx.recommendedGzipSettings = true;
|
||||
# enables zstd and sets zstd_comp_level = 9
|
||||
services.nginx.recommendedZstdSettings = true;
|
||||
# enables OCSP stapling (so clients don't need contact the OCSP server -- i do instead)
|
||||
# - doesn't seem to, actually: <https://www.ssllabs.com/ssltest/analyze.html?d=uninsane.org>
|
||||
# caches TLS sessions for 10m
|
||||
@@ -107,16 +99,6 @@ in
|
||||
disable_symlinks on;
|
||||
'';
|
||||
};
|
||||
locations."/share/Ubunchu/" = {
|
||||
alias = "/var/media/Books/Visual/HiroshiSeo/Ubunchu/";
|
||||
extraConfig = ''
|
||||
# autoindex => render directory listings
|
||||
autoindex on;
|
||||
# don't follow any symlinks when serving files
|
||||
# otherwise it allows a directory escape
|
||||
disable_symlinks on;
|
||||
'';
|
||||
};
|
||||
|
||||
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
|
||||
locations."= /.well-known/matrix/server".extraConfig =
|
||||
@@ -198,15 +180,8 @@ in
|
||||
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "acme"; group = "acme"; path = "/var/lib/acme"; method = "bind"; }
|
||||
];
|
||||
sane.persist.sys.byStore.private = [
|
||||
{ user = "colin"; group = "users"; path = "/var/www/sites"; method = "bind"; }
|
||||
];
|
||||
sane.persist.sys.byStore.ephemeral = [
|
||||
# logs *could* be persisted to private storage, but then there's the issue of
|
||||
# "what if servo boots, isn't unlocked, and the whole / tmpfs is consumed by logs"
|
||||
{ user = "nginx"; group = "nginx"; path = "/var/log/nginx"; method = "bind"; }
|
||||
];
|
||||
|
||||
# let's encrypt default chain looks like:
|
||||
# - End-entity certificate ← R3 ← ISRG Root X1 ← DST Root CA X3
|
||||
|
@@ -30,7 +30,7 @@ let
|
||||
altPort = 2587;
|
||||
in
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# not 100% necessary to persist this, but ntfy does keep a 12hr (by default) cache
|
||||
# for pushing notifications to users who become offline.
|
||||
# ACLs also live here.
|
||||
@@ -46,7 +46,7 @@ in
|
||||
# defaults to 45s.
|
||||
# note that the client may still do its own TCP-level keepalives, typically every 30s
|
||||
keepalive-interval = "15m";
|
||||
log-level = "info"; # trace, debug, info (default), warn, error
|
||||
log-level = "trace"; # trace, debug, info (default), warn, error
|
||||
auth-default-access = "deny-all";
|
||||
};
|
||||
systemd.services.ntfy-sh.serviceConfig.DynamicUser = lib.mkForce false;
|
||||
@@ -86,7 +86,7 @@ in
|
||||
sane.ports.ports."${builtins.toString altPort}" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-ntfy.uninsane.org";
|
||||
};
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p ntfy-sh -p python3
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p ntfy-sh
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
@@ -47,7 +47,7 @@ in
|
||||
};
|
||||
sane.ntfy-waiter.package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.static-nix-shell.mkPython3 {
|
||||
default = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "ntfy-waiter";
|
||||
srcRoot = ./.;
|
||||
pkgs = [ "ntfy-sh" ];
|
||||
@@ -62,8 +62,8 @@ in
|
||||
sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: {
|
||||
"${builtins.toString port}" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-notification-waiter-${builtins.toString (port - portLow + 1)}-of-${builtins.toString numPorts}";
|
||||
};
|
||||
}));
|
||||
|
@@ -7,15 +7,14 @@
|
||||
# to run it in a oci-container: <https://github.com/barrucadu/nixfiles/blob/master/services/pleroma.nix>
|
||||
#
|
||||
# admin frontend: <https://fed.uninsane.org/pleroma/admin>
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
let
|
||||
logLevel = "warn";
|
||||
# logLevel = "debug";
|
||||
in
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
# contains media i've uploaded to the server
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; method = "bind"; }
|
||||
];
|
||||
services.pleroma.enable = true;
|
||||
@@ -136,53 +135,26 @@ in
|
||||
# something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start
|
||||
pkgs.bash
|
||||
# used by Pleroma to strip geo tags from uploads
|
||||
config.sane.programs.exiftool.package
|
||||
pkgs.exiftool
|
||||
# i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical
|
||||
config.sane.programs.gawk.package
|
||||
pkgs.gawk
|
||||
# needed for email operations like password reset
|
||||
pkgs.postfix
|
||||
];
|
||||
|
||||
systemd.services.pleroma = {
|
||||
systemd.services.pleroma.serviceConfig = {
|
||||
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
|
||||
serviceConfig.Restart = "on-failure";
|
||||
serviceConfig.RestartSec = "10s";
|
||||
|
||||
# hardening (systemd-analyze security pleroma)
|
||||
# XXX(2024-07-28): this hasn't been rigorously tested:
|
||||
# possible that i've set something too strict and won't notice right away
|
||||
# make sure to test:
|
||||
# - image/media uploading
|
||||
serviceConfig.CapabilityBoundingSet = "~CAP_SYS_ADMIN"; #< TODO: reduce this. try: CAP_SYS_NICE CAP_DAC_READ_SEARCH CAP_SYS_CHROOT CAP_SETGID CAP_SETUID
|
||||
serviceConfig.LockPersonality = true;
|
||||
serviceConfig.NoNewPrivileges = true;
|
||||
serviceConfig.MemoryDenyWriteExecute = true;
|
||||
serviceConfig.PrivateDevices = lib.mkForce true; #< dunno why nixpkgs has this set false; it seems to work as true
|
||||
serviceConfig.PrivateMounts = true;
|
||||
serviceConfig.PrivateTmp = true;
|
||||
serviceConfig.PrivateUsers = true;
|
||||
|
||||
serviceConfig.ProtectProc = "invisible";
|
||||
serviceConfig.ProcSubset = "all"; #< needs /proc/sys/kernel/overflowuid for bwrap
|
||||
|
||||
serviceConfig.ProtectClock = true;
|
||||
serviceConfig.ProtectControlGroups = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectKernelModules = true;
|
||||
serviceConfig.ProtectSystem = lib.mkForce "strict";
|
||||
serviceConfig.RemoveIPC = true;
|
||||
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
|
||||
|
||||
serviceConfig.RestrictSUIDSGID = true;
|
||||
serviceConfig.SystemCallArchitectures = "native";
|
||||
serviceConfig.SystemCallFilter = [ "@system-service" "@mount" "@sandbox" ]; #< "sandbox" might not actually be necessary
|
||||
|
||||
serviceConfig.ProtectHostname = false; #< else brap can't mount /proc
|
||||
serviceConfig.ProtectKernelLogs = false; #< else breaks exiftool ("bwrap: Can't mount proc on /newroot/proc: Operation not permitted")
|
||||
serviceConfig.ProtectKernelTunables = false; #< else breaks exiftool
|
||||
serviceConfig.RestrictNamespaces = false; # media uploads require bwrap
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10s";
|
||||
};
|
||||
|
||||
# systemd.services.pleroma.serviceConfig = {
|
||||
# # required for sendmail. see https://git.pleroma.social/pleroma/pleroma/-/issues/2259
|
||||
# NoNewPrivileges = lib.mkForce false;
|
||||
# PrivateTmp = lib.mkForce false;
|
||||
# CapabilityBoundingSet = lib.mkForce "~";
|
||||
# };
|
||||
|
||||
# this is required to allow pleroma to send email.
|
||||
# raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk.
|
||||
# hack to fix that.
|
||||
|
@@ -6,9 +6,9 @@ let
|
||||
KiB = n: 1024*n;
|
||||
in
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
{ user = "postgres"; group = "postgres"; mode = "0750"; path = "/var/lib/postgresql"; method = "bind"; }
|
||||
{ user = "postgres"; group = "postgres"; mode = "0750"; path = "/var/backup/postgresql"; method = "bind"; }
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode?
|
||||
{ user = "postgres"; group = "postgres"; path = "/var/lib/postgresql"; method = "bind"; }
|
||||
];
|
||||
services.postgresql.enable = true;
|
||||
|
||||
|
@@ -56,48 +56,47 @@ let
|
||||
enableDebug = false;
|
||||
in
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
# TODO: mode?
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "prosody"; group = "prosody"; path = "/var/lib/prosody"; method = "bind"; }
|
||||
];
|
||||
sane.ports.ports."5000" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-prosody-fileshare-proxy65";
|
||||
};
|
||||
sane.ports.ports."5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
sane.ports.ports."5223" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
sane.ports.ports."5270" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
sane.ports.ports."5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-prosody-https"; # necessary?
|
||||
};
|
||||
|
||||
|
@@ -10,9 +10,7 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.persist.sys.byStore.ephemeral = [
|
||||
# {data,downloads,incomplete,logs}: contains logs, search history, and downloads
|
||||
# so, move the downloaded data to persistent storage regularly, or configure the downloads/incomplete dirs to point to persisted storage (in nixpkgs slskd config)
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ user = "slskd"; group = "media"; path = "/var/lib/slskd"; method = "bind"; }
|
||||
];
|
||||
sops.secrets."slskd_env" = {
|
||||
@@ -24,7 +22,8 @@
|
||||
|
||||
sane.ports.ports."50300" = {
|
||||
protocol = [ "tcp" ];
|
||||
# visibleTo.ovpns = true; #< not needed: it runs in the ovpns namespace
|
||||
# not visible to WAN: i run this in a separate netns
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-soulseek";
|
||||
};
|
||||
|
||||
@@ -34,7 +33,7 @@
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:5030";
|
||||
proxyPass = "http://10.0.1.6:5030";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
@@ -70,20 +69,12 @@
|
||||
# flags.volatile = true; # store searches and active transfers in RAM (completed transfers still go to disk). rec for btrfs/zfs
|
||||
};
|
||||
|
||||
systemd.services.slskd = {
|
||||
systemd.services.slskd.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
serviceConfig.NetworkNamespacePath = "/run/netns/ovpns";
|
||||
serviceConfig.ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
|
||||
|
||||
serviceConfig.Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
|
||||
serviceConfig.RestartSec = "60s";
|
||||
|
||||
# hardening (systemd-analyze security slskd)
|
||||
# upstream nixpkgs specifies moderate defaults; these are supplementary
|
||||
# serviceConfig.MemoryDenyWriteExecute = true;
|
||||
# serviceConfig.ProcSubset = "pid";
|
||||
# serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
# serviceConfig.SystemCallArchitectures = "native";
|
||||
# serviceConfig.SystemCallFilter = [ "@system-service" ];
|
||||
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
|
||||
RestartSec = "60s";
|
||||
};
|
||||
}
|
||||
|
@@ -22,23 +22,71 @@ let
|
||||
--replace-fail 'set(TR_USER_AGENT_PREFIX "''${TR_SEMVER}")' 'set(TR_USER_AGENT_PREFIX "3.00")'
|
||||
'';
|
||||
});
|
||||
download-dir = "/var/media/torrents"; #< keep in sync with consts embedded in `torrent-done`
|
||||
torrent-done = pkgs.static-nix-shell.mkBash {
|
||||
pname = "torrent-done";
|
||||
srcRoot = ./.;
|
||||
pkgs = [
|
||||
"acl"
|
||||
"coreutils"
|
||||
"findutils"
|
||||
"rsync"
|
||||
download-dir = "/var/media/torrents";
|
||||
torrent-done = pkgs.writeShellApplication {
|
||||
name = "torrent-done";
|
||||
runtimeInputs = with pkgs; [
|
||||
acl
|
||||
coreutils
|
||||
findutils
|
||||
rsync
|
||||
util-linux
|
||||
];
|
||||
text = ''
|
||||
destructive() {
|
||||
if [ -n "''${TR_DRY_RUN-}" ]; then
|
||||
echo "$*"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
if [[ "$TR_TORRENT_DIR" =~ ^.*freeleech.*$ ]]; then
|
||||
# freeleech torrents have no place in my permanent library
|
||||
echo "freeleech: nothing to do"
|
||||
exit 0
|
||||
fi
|
||||
if ! [[ "$TR_TORRENT_DIR" =~ ^${download-dir}/.*$ ]]; then
|
||||
echo "unexpected torrent dir, aborting: $TR_TORRENT_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REL_DIR="''${TR_TORRENT_DIR#${download-dir}/}"
|
||||
MEDIA_DIR="/var/media/$REL_DIR"
|
||||
|
||||
destructive mkdir -p "$(dirname "$MEDIA_DIR")"
|
||||
destructive rsync -arv "$TR_TORRENT_DIR/" "$MEDIA_DIR/"
|
||||
# make the media rwx by anyone in the group
|
||||
destructive find "$MEDIA_DIR" -type d -exec setfacl --recursive --modify d:g::rwx,o::rx {} \;
|
||||
destructive find "$MEDIA_DIR" -type d -exec chmod g+rw,a+rx {} \;
|
||||
|
||||
# if there's a single directory inside the media dir, then inline that
|
||||
subdirs=("$MEDIA_DIR"/*)
|
||||
if [ ''${#subdirs} -eq 1 ]; then
|
||||
dirname="''${subdirs[0]}"
|
||||
if [ -d "$dirname" ]; then
|
||||
mv "$dirname"/* "$MEDIA_DIR/" && rmdir "$dirname"
|
||||
fi
|
||||
fi
|
||||
|
||||
# remove noisy files:
|
||||
find "$MEDIA_DIR/" -type f \(\
|
||||
-iname 'www.YTS.*.jpg' \
|
||||
-o -iname 'WWW.YIFY*.COM.jpg' \
|
||||
-o -iname 'YIFY*.com.txt' \
|
||||
-o -iname 'YTS*.com.txt' \
|
||||
\) -exec rm {} \;
|
||||
|
||||
# dedupe the whole media library.
|
||||
# yeah, a bit excessive: move this to a cron job if that's problematic.
|
||||
destructive hardlink /var/media --reflink=always --ignore-time --verbose
|
||||
'';
|
||||
};
|
||||
in
|
||||
lib.mkIf false #< TODO: re-enable once confident of sandboxing
|
||||
{
|
||||
sane.persist.sys.byStore.private = [
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode? we need this specifically for the stats tracking in .config/
|
||||
{ user = "transmission"; group = config.users.users.transmission.group; path = "/var/lib/transmission"; method = "bind"; }
|
||||
{ user = "transmission"; group = config.users.users.transmission.group; path = "/var/backup/torrents"; method = "bind"; }
|
||||
];
|
||||
users.users.transmission.extraGroups = [ "media" ];
|
||||
|
||||
@@ -58,8 +106,8 @@ in
|
||||
# DOCUMENTATION/options list: <https://github.com/transmission/transmission/blob/main/docs/Editing-Configuration-Files.md#options>
|
||||
|
||||
# message-level = 3; #< enable for debug logging. 0-3, default is 2.
|
||||
# ovpns.netnsVethIpv4 => allow rpc only from the root servo ns. it'll tunnel things to the net, if need be.
|
||||
rpc-bind-address = config.sane.netns.ovpns.netnsVethIpv4;
|
||||
# 10.0.1.6 => allow rpc only from the root servo ns. it'll tunnel things to the net, if need be.
|
||||
rpc-bind-address = "10.0.1.6";
|
||||
#rpc-host-whitelist = "bt.uninsane.org";
|
||||
#rpc-whitelist = "*.*.*.*";
|
||||
rpc-authentication-required = true;
|
||||
@@ -70,7 +118,7 @@ in
|
||||
rpc-whitelist-enabled = false;
|
||||
|
||||
# force behind ovpns in case the NetworkNamespace fails somehow
|
||||
bind-address-ipv4 = config.sane.netns.ovpns.netnsPubIpv4;
|
||||
bind-address-ipv4 = "185.157.162.178";
|
||||
port-forwarding-enabled = false;
|
||||
|
||||
# hopefully, make the downloads world-readable
|
||||
@@ -107,31 +155,16 @@ in
|
||||
script-torrent-done-filename = "${torrent-done}/bin/torrent-done";
|
||||
};
|
||||
|
||||
systemd.services.transmission = {
|
||||
after = [ "wireguard-wg-ovpns.service" ];
|
||||
partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
environment.TR_DEBUG = "1";
|
||||
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.transmission.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.transmission.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
serviceConfig.NetworkNamespacePath = "/run/netns/ovpns";
|
||||
serviceConfig.ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
|
||||
|
||||
serviceConfig.Restart = "on-failure";
|
||||
serviceConfig.RestartSec = "30s";
|
||||
serviceConfig.BindPaths = [ "/var/media" ]; #< so it can move completed torrents into the media library
|
||||
serviceConfig.SystemCallFilter = lib.mkForce [
|
||||
# the torrent-done script does stuff which fails the nixos default syscall filter.
|
||||
# allow a bunch of stuff, speculatively, to hopefully fix that:
|
||||
"@aio"
|
||||
"@basic-io"
|
||||
"@chown"
|
||||
"@file-system"
|
||||
"@io-event"
|
||||
"@process"
|
||||
"@sandbox"
|
||||
"@sync"
|
||||
"@system-service"
|
||||
"quotactl"
|
||||
];
|
||||
Restart = "on-failure";
|
||||
RestartSec = "30s";
|
||||
BindPaths = [ "/var/media" ]; #< so it can move completed torrents into the media library
|
||||
};
|
||||
|
||||
# service to automatically backup torrents i add to transmission
|
||||
@@ -157,14 +190,14 @@ in
|
||||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
# proxyPass = "http://ovpns.uninsane.org:9091";
|
||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9091";
|
||||
proxyPass = "http://10.0.1.6:9091";
|
||||
};
|
||||
};
|
||||
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."bt" = "native";
|
||||
sane.ports.ports."51413" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
# visibleTo.ovpns = true; #< not needed: it runs in the ovpns namespace
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-bittorrent";
|
||||
};
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p acl -p bash -p coreutils -p findutils -p rsync
|
||||
|
||||
# transmission invokes this with no args, and the following env vars:
|
||||
# - TR_TORRENT_DIR: full path to the folder i told transmission to download it to.
|
||||
# e.g. /var/media/torrents/Videos/Film/Jason.Bourne-2016
|
||||
# optionally:
|
||||
# - TR_DRY_RUN=1
|
||||
# - TR_DEBUG=1
|
||||
|
||||
DOWNLOAD_DIR=/var/media/torrents
|
||||
|
||||
destructive() {
|
||||
if [ -n "${TR_DRY_RUN-}" ]; then
|
||||
echo "[dry-run] $*"
|
||||
else
|
||||
debug "$@"
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
debug() {
|
||||
if [ -n "${TR_DEBUG-}" ]; then
|
||||
echo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "TR_TORRENT_DIR=$TR_TORRENT_DIR torrent-done $*"
|
||||
|
||||
if [[ "$TR_TORRENT_DIR" =~ ^.*freeleech.*$ ]]; then
|
||||
# freeleech torrents have no place in my permanent library
|
||||
echo "freeleech: nothing to do"
|
||||
exit 0
|
||||
fi
|
||||
if ! [[ "$TR_TORRENT_DIR" =~ ^$DOWNLOAD_DIR/.*$ ]]; then
|
||||
echo "unexpected torrent dir, aborting: $TR_TORRENT_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REL_DIR="${TR_TORRENT_DIR#$DOWNLOAD_DIR/}"
|
||||
MEDIA_DIR="/var/media/$REL_DIR"
|
||||
|
||||
destructive mkdir -p "$(dirname "$MEDIA_DIR")"
|
||||
destructive rsync -arv "$TR_TORRENT_DIR/" "$MEDIA_DIR/"
|
||||
# make the media rwx by anyone in the group
|
||||
destructive find "$MEDIA_DIR" -type d -exec setfacl --recursive --modify d:g::rwx,o::rx {} \;
|
||||
destructive find "$MEDIA_DIR" -type d -exec chmod g+rw,a+rx {} \;
|
||||
|
||||
# if there's a single directory inside the media dir, then inline that
|
||||
subdirs=("$MEDIA_DIR"/*)
|
||||
debug "top-level items in torrent dir:" "${subdirs[@]}"
|
||||
if [ ${#subdirs[@]} -eq 1 ]; then
|
||||
dirname="${subdirs[0]}"
|
||||
debug "exactly one top-level item, checking if directory: $dirname"
|
||||
if [ -d "$dirname" ]; then
|
||||
destructive mv "$dirname"/* "$MEDIA_DIR/" && destructive rmdir "$dirname"
|
||||
fi
|
||||
fi
|
||||
|
||||
# remove noisy files:
|
||||
# -iname means "insensitive", but the syntax is NOT regex -- more similar to shell matching
|
||||
destructive find "$MEDIA_DIR/" -type f \(\
|
||||
-iname '*downloaded?from*' \
|
||||
-o -iname 'source.txt' \
|
||||
-o -iname '*upcoming?releases*' \
|
||||
-o -iname 'www.YTS*.jpg' \
|
||||
-o -iname 'WWW.YIFY*.COM.jpg' \
|
||||
-o -iname 'YIFY*.com.txt' \
|
||||
-o -iname 'YTS*.com.txt' \
|
||||
\) -exec rm {} \;
|
@@ -4,14 +4,14 @@
|
||||
let
|
||||
dyn-dns = config.sane.services.dyn-dns;
|
||||
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
|
||||
bindOvpn = "10.0.1.5";
|
||||
in
|
||||
{
|
||||
sane.ports.ports."53" = {
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
# visibleTo.wan = true;
|
||||
visibleTo.ovpns = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-dns-hosting";
|
||||
};
|
||||
|
||||
@@ -39,7 +39,6 @@ in
|
||||
CNAME."native" = "%CNAMENATIVE%";
|
||||
A."@" = "%ANATIVE%";
|
||||
A."servo.wan" = "%AWAN%";
|
||||
A."servo.doof" = "%ADOOF%";
|
||||
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
||||
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
|
||||
@@ -47,99 +46,114 @@ in
|
||||
# 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" = "%ANATIVE%";
|
||||
A."ns2" = "%ADOOF%";
|
||||
A."ovpns" = "%AOVPNS%";
|
||||
A."ns2" = "185.157.162.178";
|
||||
A."ns3" = "185.157.162.178";
|
||||
A."ovpns" = "185.157.162.178";
|
||||
NS."@" = [
|
||||
"ns1.uninsane.org."
|
||||
"ns2.uninsane.org."
|
||||
"ns3.uninsane.org."
|
||||
];
|
||||
};
|
||||
|
||||
services.trust-dns.settings.zones = [ "uninsane.org" ];
|
||||
|
||||
|
||||
networking.nat.enable = true; #< TODO: try removing this?
|
||||
# networking.nat.extraCommands = ''
|
||||
# # redirect incoming DNS requests from LAN addresses
|
||||
# # to the LAN-specialized DNS service
|
||||
# # N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||
# # because they get cleanly reset across activations or `systemctl restart firewall`
|
||||
# # instead of accumulating cruft
|
||||
# iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
|
||||
# -m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
# -j DNAT --to-destination :1053
|
||||
# iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
|
||||
# -m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
# -j DNAT --to-destination :1053
|
||||
# '';
|
||||
# sane.ports.ports."1053" = {
|
||||
# # because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
||||
# # TODO: try nixos-nat-post instead?
|
||||
# # TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
|
||||
# # - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
|
||||
# protocol = [ "udp" "tcp" ];
|
||||
# visibleTo.lan = true;
|
||||
# description = "colin-redirected-dns-for-lan-namespace";
|
||||
# };
|
||||
networking.nat.enable = true;
|
||||
networking.nat.extraCommands = ''
|
||||
# redirect incoming DNS requests from LAN addresses
|
||||
# to the LAN-specialized DNS service
|
||||
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||
# because they get cleanly reset across activations or `systemctl restart firewall`
|
||||
# instead of accumulating cruft
|
||||
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
'';
|
||||
sane.ports.ports."1053" = {
|
||||
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
||||
# TODO: try nixos-nat-post instead?
|
||||
# TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
|
||||
# - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-redirected-dns-for-lan-namespace";
|
||||
};
|
||||
|
||||
|
||||
sane.services.trust-dns.enable = true;
|
||||
sane.services.trust-dns.instances = let
|
||||
mkSubstitutions = flavor: {
|
||||
"%ADOOF%" = config.sane.netns.doof.netnsPubIpv4;
|
||||
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
|
||||
"%AOVPNS%" = config.sane.netns.ovpns.netnsPubIpv4;
|
||||
"%AWAN%" = "$(cat '${dyn-dns.ipPath}')";
|
||||
"%CNAMENATIVE%" = "servo.${flavor}";
|
||||
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
|
||||
"%AOVPNS%" = "185.157.162.178";
|
||||
};
|
||||
in
|
||||
{
|
||||
doof = {
|
||||
substitutions = mkSubstitutions "doof";
|
||||
wan = {
|
||||
substitutions = mkSubstitutions "wan";
|
||||
listenAddrsIpv4 = [
|
||||
config.sane.netns.doof.hostVethIpv4
|
||||
config.sane.netns.doof.netnsPubIpv4
|
||||
nativeAddrs."servo.lan"
|
||||
# config.sane.netns.ovpns.hostVethIpv4
|
||||
bindOvpn
|
||||
];
|
||||
};
|
||||
lan = {
|
||||
substitutions = mkSubstitutions "lan";
|
||||
listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
|
||||
port = 1053;
|
||||
};
|
||||
hn = {
|
||||
substitutions = mkSubstitutions "hn";
|
||||
listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
|
||||
enableRecursiveResolver = true; #< allow wireguard clients to use this as their DNS resolver
|
||||
# extraConfig = {
|
||||
# zones = [
|
||||
# {
|
||||
# # forward the root zone to the local DNS resolver
|
||||
# # to allow wireguard clients to use this as their DNS resolver
|
||||
# zone = ".";
|
||||
# zone_type = "Forward";
|
||||
# stores = {
|
||||
# type = "forward";
|
||||
# name_servers = [
|
||||
# {
|
||||
# socket_addr = "127.0.0.53:53";
|
||||
# protocol = "udp";
|
||||
# trust_nx_responses = true;
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
port = 1053;
|
||||
};
|
||||
# lan = {
|
||||
# substitutions = mkSubstitutions "lan";
|
||||
# listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
|
||||
# # port = 1053;
|
||||
# };
|
||||
# wan = {
|
||||
# substitutions = mkSubstitutions "wan";
|
||||
# listenAddrsIpv4 = [
|
||||
# nativeAddrs."servo.lan"
|
||||
# ];
|
||||
# hn-resolver = {
|
||||
# # don't need %AWAN% here because we forward to the hn instance.
|
||||
# listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
|
||||
# extraConfig = {
|
||||
# zones = [
|
||||
# {
|
||||
# zone = "uninsane.org";
|
||||
# zone_type = "Forward";
|
||||
# stores = {
|
||||
# type = "forward";
|
||||
# name_servers = [
|
||||
# {
|
||||
# socket_addr = "${nativeAddrs."servo.hn"}:1053";
|
||||
# protocol = "udp";
|
||||
# trust_nx_responses = true;
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
# }
|
||||
# {
|
||||
# # forward the root zone to the local DNS resolver
|
||||
# zone = ".";
|
||||
# zone_type = "Forward";
|
||||
# stores = {
|
||||
# type = "forward";
|
||||
# name_servers = [
|
||||
# {
|
||||
# socket_addr = "127.0.0.53:53";
|
||||
# protocol = "udp";
|
||||
# trust_nx_responses = true;
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
# };
|
||||
};
|
||||
|
||||
sane.services.dyn-dns.restartOnChange = lib.map (c: "${c.service}.service") (builtins.attrValues config.sane.services.trust-dns.instances);
|
||||
sane.services.dyn-dns.restartOnChange = [
|
||||
"trust-dns-wan.service"
|
||||
"trust-dns-lan.service"
|
||||
"trust-dns-hn.service"
|
||||
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
|
||||
];
|
||||
}
|
||||
|
@@ -46,6 +46,5 @@
|
||||
# manifests as spurious "No space left on device" when trying to install watches,
|
||||
# e.g. in dyn-dns by `systemctl start dyn-dns-watcher.path`.
|
||||
# see: <https://askubuntu.com/questions/828779/failed-to-add-run-systemd-ask-password-to-directory-watch-no-space-left-on-dev>
|
||||
boot.kernel.sysctl."fs.inotify.max_user_watches" = 4194304;
|
||||
boot.kernel.sysctl."fs.inotify.max_user_instances" = 4194304;
|
||||
boot.kernel.sysctl."fs.inotify.max_user_watches" = 1048576;
|
||||
}
|
||||
|
@@ -9,7 +9,8 @@
|
||||
./ids.nix
|
||||
./machine-id.nix
|
||||
./net
|
||||
./nix.nix
|
||||
./nix
|
||||
./persist.nix
|
||||
./polyunfill.nix
|
||||
./programs
|
||||
./quirks.nix
|
||||
|
@@ -1,11 +1,14 @@
|
||||
# where to find good stuff?
|
||||
# - universal search/directory: <https://podcastindex.org>
|
||||
# - list of lists: <https://en.wikipedia.org/wiki/Category:Lists_of_podcasts>
|
||||
# - podcasts w/ a community: <https://lemmyverse.net/communities?query=podcast>
|
||||
# - podcast recs:
|
||||
# - active lemmy: <https://slrpnk.net/c/podcasts>
|
||||
# - old thread: <https://lemmy.ml/post/1565858>
|
||||
# - podcast rec thread: <https://lemmy.ml/post/1565858>
|
||||
#
|
||||
# candidates:
|
||||
# - The Nonlinear Library (podcast): <https://forum.effectivealtruism.org/posts/JTZTBienqWEAjGDRv/listen-to-more-ea-content-with-the-nonlinear-library>
|
||||
# - has ~10 posts per day, text-to-speech; i would need better tagging before adding this
|
||||
# - <https://www.metaculus.com/questions/11102/introducing-the-metaculus-journal-podcast/>
|
||||
# - dead since 2022/10 - 2023/03
|
||||
|
||||
{ lib, sane-data, ... }:
|
||||
let
|
||||
hourly = { freq = "hourly"; };
|
||||
@@ -72,17 +75,15 @@ let
|
||||
(fromDb "feeds.feedburner.com/radiolab" // pol) # Radiolab -- also available here, but ONLY OVER HTTP: <http://feeds.wnyc.org/radiolab>
|
||||
(fromDb "feeds.megaphone.fm/behindthebastards" // pol) # also Maggie Killjoy
|
||||
(fromDb "feeds.megaphone.fm/recodedecode" // tech) # The Verge - Decoder
|
||||
(fromDb "feeds.simplecast.com/54nAGcIl" // pol) # The Daily
|
||||
(fromDb "feeds.simplecast.com/82FI35Px" // pol) # Ezra Klein Show
|
||||
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat) # Econ Talk
|
||||
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
|
||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||
(fromDb "feeds.transistor.fm/complex-systems-with-patrick-mckenzie-patio11" // tech) # Patrick Mackenzie (from Bits About Money)
|
||||
(fromDb "feeds.twit.tv/floss.xml" // tech)
|
||||
(fromDb "fulltimenix.com" // tech)
|
||||
(fromDb "futureofcoding.org/episodes" // tech)
|
||||
(fromDb "hackerpublicradio.org" // tech)
|
||||
(fromDb "lexfridman.com/podcast" // rat)
|
||||
(fromDb "linktr.ee/betteroffline" // pol)
|
||||
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
|
||||
(fromDb "microarch.club" // tech)
|
||||
(fromDb "mintcast.org" // tech)
|
||||
@@ -91,6 +92,7 @@ let
|
||||
(fromDb "omny.fm/shows/money-stuff-the-podcast") # Matt Levine
|
||||
(fromDb "omny.fm/shows/the-dollop-with-dave-anthony-and-gareth-reynolds") # The Dollop history/comedy
|
||||
(fromDb "originstories.libsyn.com" // uncat)
|
||||
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
|
||||
(fromDb "politicalorphanage.libsyn.com" // pol)
|
||||
(fromDb "reverseengineering.libsyn.com/rss" // tech) # UnNamed Reverse Engineering Podcast
|
||||
(fromDb "rss.acast.com/deconstructed") # The Intercept - Deconstructed
|
||||
@@ -101,7 +103,6 @@ let
|
||||
(fromDb "seattlenice.buzzsprout.com" // pol)
|
||||
(fromDb "srslywrong.com" // pol)
|
||||
(fromDb "sharkbytes.transistor.fm" // tech) # Wireshark Podcast o_0
|
||||
(fromDb "sharptech.fm/feed/podcast" // tech)
|
||||
(fromDb "sscpodcast.libsyn.com" // rat) # Astral Codex Ten
|
||||
(fromDb "talesfromthebridge.buzzsprout.com" // tech) # Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
|
||||
(fromDb "theamphour.com" // tech)
|
||||
@@ -113,9 +114,7 @@ let
|
||||
|
||||
# (fromDb "feeds.libsyn.com/421877" // rat) # Less Wrong Curated
|
||||
# (fromDb "feeds.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
|
||||
# (fromDb "feeds.simplecast.com/54nAGcIl" // pol) # The Daily
|
||||
# (fromDb "feeds.simplecast.com/l2i9YnTd" // tech // pol) # Hard Fork (NYtimes tech)
|
||||
# (fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
|
||||
# (fromDb "podcast.thelinuxexp.com" // tech) # low-brow linux/foss PR announcements
|
||||
# (fromDb "rss.art19.com/your-welcome" // pol) # Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
||||
# (fromDb "rss.prod.firstlook.media/deconstructed/podcast.rss" // pol) #< possible URL rot
|
||||
@@ -136,7 +135,6 @@ let
|
||||
(fromDb "artemis.sh" // tech)
|
||||
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
||||
(fromDb "austinvernon.site" // tech)
|
||||
(fromDb "buttondown.email" // tech)
|
||||
(fromDb "ben-evans.com/benedictevans" // pol)
|
||||
(fromDb "bitbashing.io" // tech)
|
||||
(fromDb "bitsaboutmoney.com" // uncat)
|
||||
@@ -197,8 +195,6 @@ let
|
||||
(fromDb "weekinethereumnews.com" // tech)
|
||||
(fromDb "willow.phantoma.online") # wizard@xyzzy.link
|
||||
(fromDb "xn--gckvb8fzb.com" // tech)
|
||||
(fromDb "xorvoid.com" // tech)
|
||||
(fromDb "www.thebignewsletter.com" // pol)
|
||||
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
|
||||
(mkSubstack "eliqian" // rat // weekly)
|
||||
(mkSubstack "oversharing" // pol // daily)
|
||||
@@ -241,9 +237,9 @@ let
|
||||
(fromDb "youtube.com/@TheB1M")
|
||||
(fromDb "youtube.com/@TomScottGo")
|
||||
(fromDb "youtube.com/@Vihart")
|
||||
|
||||
# (fromDb "youtube.com/@Vox")
|
||||
(fromDb "youtube.com/@Vox")
|
||||
# (fromDb "youtube.com/@Vsauce") # they're all like 1-minute long videos now? what happened @Vsauce?
|
||||
|
||||
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
|
||||
];
|
||||
|
||||
|
@@ -26,6 +26,10 @@ let
|
||||
# lazyMount: defer mounting until first access from userspace.
|
||||
# see: `man systemd.automount`, `man automount`, `man autofs`
|
||||
lazyMount = noauto ++ automount;
|
||||
wg = [
|
||||
"x-systemd.requires=wireguard-wg-home.service"
|
||||
"x-systemd.after=wireguard-wg-home.service"
|
||||
];
|
||||
|
||||
fuse = [
|
||||
"allow_other" # allow users other than the one who mounts it to access it. needed, if systemd is the one mounting this fs (as root)
|
||||
@@ -121,61 +125,52 @@ let
|
||||
dir.acl.mode = "0700";
|
||||
};
|
||||
};
|
||||
remoteServo = subdir: let
|
||||
localPath = "/mnt/servo/${subdir}";
|
||||
systemdName = utils.escapeSystemdPath localPath;
|
||||
in {
|
||||
remoteServo = subdir: {
|
||||
sane.programs.curlftpfs.enableFor.system = true;
|
||||
sane.fs."${localPath}" = sane-lib.fs.wanted {
|
||||
sane.fs."/mnt/servo/${subdir}" = sane-lib.fs.wanted {
|
||||
dir.acl.user = "colin";
|
||||
dir.acl.group = "users";
|
||||
dir.acl.mode = "0750";
|
||||
};
|
||||
fileSystems."${localPath}" = {
|
||||
fileSystems."/mnt/servo/${subdir}" = {
|
||||
device = "ftp://servo-hn:/${subdir}";
|
||||
noCheck = true;
|
||||
fsType = "fuse.curlftpfs";
|
||||
options = fsOpts.ftp ++ fsOpts.noauto;
|
||||
options = fsOpts.ftp ++ fsOpts.noauto ++ fsOpts.wg;
|
||||
# fsType = "nfs";
|
||||
# options = fsOpts.nfs ++ fsOpts.lazyMount;
|
||||
# options = fsOpts.nfs ++ fsOpts.lazyMount ++ fsOpts.wg;
|
||||
};
|
||||
|
||||
systemd.mounts = let
|
||||
fsEntry = config.fileSystems."${localPath}";
|
||||
in [{
|
||||
#VVV repeat what systemd would ordinarily scrape from /etc/fstab
|
||||
where = localPath;
|
||||
what = fsEntry.device;
|
||||
type = fsEntry.fsType;
|
||||
options = lib.concatStringsSep "," fsEntry.options;
|
||||
systemd.services."automount-servo-${utils.escapeSystemdPath subdir}" = let
|
||||
fs = config.fileSystems."/mnt/servo/${subdir}";
|
||||
in {
|
||||
# this is a *flaky* network mount, especially on moby.
|
||||
# if done as a normal autofs mount, access will eternally block when network is dropped.
|
||||
# notably, this would block *any* sandboxed app which allows media access, whether they actually try to use that media or not.
|
||||
# a practical solution is this: mount as a service -- instead of autofs -- and unmount on timeout error, in a restart loop.
|
||||
# until the ftp handshake succeeds, nothing is actually mounted to the vfs, so this doesn't slow down any I/O when network is down.
|
||||
description = "automount /mnt/servo/${subdir} in a fault-tolerant and non-blocking manner";
|
||||
after = [ "network-online.target" ];
|
||||
requires = [ "network-online.target" ];
|
||||
wantedBy = [ "default.target" ]; #< TODO: move this into nixos fileSystems
|
||||
#VVV patch so that when the mount fails, we start a timer to remount it.
|
||||
# and for a disconnection after a good mount (onSuccess), restart the timer to be more aggressive
|
||||
onFailure = [ "${systemdName}.timer" ];
|
||||
onSuccess = [ "${systemdName}-restart-timer.target" ];
|
||||
}];
|
||||
systemd.targets."${systemdName}-restart-timer" = {
|
||||
# hack unit which, when started, stops the timer (if running), and then starts it again.
|
||||
after = [ "${systemdName}.timer" ];
|
||||
conflicts = [ "${systemdName}.timer" ];
|
||||
upholds = [ "${systemdName}.timer" ];
|
||||
unitConfig.StopWhenUnneeded = true;
|
||||
};
|
||||
systemd.timers."${systemdName}" = {
|
||||
timerConfig.Unit = "${systemdName}.mount";
|
||||
timerConfig.AccuracySec = "2s";
|
||||
timerConfig.OnActiveSec = [
|
||||
# try to remount at these timestamps, backing off gradually
|
||||
# there seems to be an implicit mount attempt at t=0.
|
||||
"10s"
|
||||
"30s"
|
||||
"60s"
|
||||
"120s"
|
||||
wantedBy = [ "default.target" ];
|
||||
|
||||
serviceConfig.Type = "simple";
|
||||
serviceConfig.ExecStart = lib.escapeShellArgs [
|
||||
"/usr/bin/env"
|
||||
"PATH=/run/current-system/sw/bin"
|
||||
"mount.${fs.fsType}"
|
||||
"-f" # foreground (i.e. don't daemonize)
|
||||
"-s" # single-threaded (TODO: it's probably ok to disable this?)
|
||||
"-o"
|
||||
(lib.concatStringsSep "," (lib.filter (o: !lib.hasPrefix "x-systemd." o) fs.options))
|
||||
fs.device
|
||||
"/mnt/servo/${subdir}"
|
||||
];
|
||||
# cap the backoff to a fixed interval.
|
||||
timerConfig.OnUnitActiveSec = [ "120s" ];
|
||||
# not sure if this configures a linear, or exponential backoff.
|
||||
# but the first restart will be after `RestartSec`, and the n'th restart (n = RestartSteps) will be RestartMaxDelaySec after the n-1'th exit.
|
||||
serviceConfig.Restart = "always";
|
||||
serviceConfig.RestartSec = "10s";
|
||||
serviceConfig.RestartMaxDelaySec = "120s";
|
||||
serviceConfig.RestartSteps = "5";
|
||||
};
|
||||
};
|
||||
in
|
||||
|
@@ -1,13 +1,21 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
sane.user.persist.byStore.plaintext = [
|
||||
# TODO: some of ~/dev should be private too, but maybe not all 800+ GB of it
|
||||
# perhaps i ought to rethink how it's organized
|
||||
"archive"
|
||||
"dev"
|
||||
# TODO: records should be private
|
||||
"records"
|
||||
"ref"
|
||||
"tmp"
|
||||
"use"
|
||||
"Books/local"
|
||||
"Music"
|
||||
"Pictures/albums"
|
||||
"Pictures/cat"
|
||||
"Pictures/from"
|
||||
"Pictures/Screenshots" #< XXX: something is case-sensitive about this?
|
||||
"Pictures/Photos"
|
||||
"Videos/local"
|
||||
|
||||
# these are persisted simply to save on RAM.
|
||||
# ~/.cache/nix can become several GB.
|
||||
@@ -17,17 +25,7 @@
|
||||
".cache/nix"
|
||||
];
|
||||
sane.user.persist.byStore.private = [
|
||||
"archive"
|
||||
"Pictures/albums"
|
||||
"Pictures/cat"
|
||||
"Pictures/from"
|
||||
"Pictures/Screenshots" #< XXX: something is case-sensitive about this?
|
||||
"Pictures/Photos"
|
||||
"records"
|
||||
"tmp"
|
||||
|
||||
"knowledge"
|
||||
"Videos/local"
|
||||
];
|
||||
|
||||
# convenience
|
||||
@@ -36,7 +34,7 @@
|
||||
in {
|
||||
".persist/private" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.private.origin; };
|
||||
".persist/plaintext" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.plaintext.origin; };
|
||||
".persist/ephemeral" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.ephemeral.origin; };
|
||||
".persist/ephemeral" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.cryptClearOnBoot.origin; };
|
||||
|
||||
"nixos".symlink.target = "dev/nixos";
|
||||
|
||||
|
@@ -62,7 +62,6 @@
|
||||
sane.ids.clightning.gid = 2419;
|
||||
sane.ids.nix-serve.uid = 2420;
|
||||
sane.ids.nix-serve.gid = 2420;
|
||||
sane.ids.plugdev.gid = 2421;
|
||||
|
||||
sane.ids.colin.uid = 1000;
|
||||
sane.ids.guest.uid = 1100;
|
||||
|
@@ -12,7 +12,6 @@
|
||||
|
||||
systemd.network.enable = true;
|
||||
networking.useNetworkd = true;
|
||||
networking.usePredictableInterfaceNames = false; #< set false to get `eth0`, `wlan0`, etc instead of `enp3s0`/etc
|
||||
|
||||
# view refused/dropped packets with: `sudo journalctl -k`
|
||||
# networking.firewall.logRefusedPackets = true;
|
||||
|
@@ -20,7 +20,7 @@
|
||||
# - each namespace may use a different /etc/resolv.conf to specify different DNS servers
|
||||
# - nscd breaks namespacing: the host nscd is unaware of the guest's /etc/resolv.conf, and so directs the guest's DNS requests to the host's servers.
|
||||
# - this is fixed by either removing `/var/run/nscd/socket` from the namespace, or disabling nscd altogether.
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, ... }:
|
||||
lib.mkMerge [
|
||||
{
|
||||
sane.services.trust-dns.enable = lib.mkDefault config.sane.services.trust-dns.asSystemResolver;
|
||||
@@ -59,35 +59,15 @@ lib.mkMerge [
|
||||
# in the netns and we query upstream DNS more often than needed. hm.
|
||||
# services.nscd.enableNsncd = true;
|
||||
|
||||
# disabling nscd LOSES US SOME FUNCTIONALITY. in particular, only the glibc-builtin modules are accessible via /etc/resolv.conf (er, did i mean /etc/nsswitch.conf?).
|
||||
# disabling nscd LOSES US SOME FUNCTIONALITY. in particular, only the glibc-builtin modules are accessible via /etc/resolv.conf.
|
||||
# - dns: glibc-bultin
|
||||
# - files: glibc-builtin
|
||||
# - myhostname: systemd
|
||||
# - mymachines: systemd
|
||||
# - resolve: systemd
|
||||
# in practice, i see no difference with nscd disabled.
|
||||
# - the exception is when the system dns resolver doesn't do everything.
|
||||
# for example, systemd-resolved does mDNS. hickory-dns does not. a hickory-dns system won't be mDNS-capable.
|
||||
# disabling nscd VASTLY simplifies netns and process isolation. see explainer at top of file.
|
||||
services.nscd.enable = false;
|
||||
# system.nssModules = lib.mkForce [];
|
||||
sane.silencedAssertions = [''.*Loading NSS modules from system.nssModules.*requires services.nscd.enable being set to true.*''];
|
||||
# add NSS modules into their own subdirectory.
|
||||
# then i can add just the NSS modules library path to the global LD_LIBRARY_PATH, rather than ALL of /run/current-system/sw/lib.
|
||||
# TODO: i'm doing this so as to achieve mdns DNS resolution (avahi). it would be better to just have trust-dns delegate .local to avahi
|
||||
# (except avahi doesn't act as a local resolver over DNS protocol -- only dbus).
|
||||
environment.systemPackages = [(pkgs.symlinkJoin {
|
||||
name = "nss-modules";
|
||||
paths = config.system.nssModules.list;
|
||||
postBuild = ''
|
||||
mkdir nss
|
||||
mv $out/lib/libnss_* nss
|
||||
rm -rf $out
|
||||
mkdir -p $out/lib
|
||||
mv nss $out/lib
|
||||
'';
|
||||
})];
|
||||
environment.variables.LD_LIBRARY_PATH = [ "/run/current-system/sw/lib/nss" ];
|
||||
systemd.globalEnvironment.LD_LIBRARY_PATH = "/run/current-system/sw/lib/nss"; #< specifically for `geoclue.service`
|
||||
system.nssModules = lib.mkForce [];
|
||||
}
|
||||
]
|
||||
|
@@ -14,6 +14,7 @@
|
||||
# after = [ "polkit.service" ];
|
||||
# requires = [ "polkit.service" ];
|
||||
wantedBy = [ "network.target" ]; #< default is `multi-user.target`, somehow it doesn't auto-start with that...
|
||||
# path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
|
||||
|
||||
# serviceConfig.Type = "dbus";
|
||||
# serviceConfig.BusName = "org.freedesktop.ModemManager1";
|
||||
|
@@ -2,28 +2,21 @@
|
||||
let
|
||||
# networkmanager = pkgs.networkmanager;
|
||||
networkmanager = pkgs.networkmanager.overrideAttrs (upstream: {
|
||||
# src = pkgs.fetchFromGitea {
|
||||
# domain = "git.uninsane.org";
|
||||
# owner = "colin";
|
||||
# repo = "NetworkManager";
|
||||
# # patched to fix polkit permissions (with `nmcli`) when NetworkManager runs as user networkmanager
|
||||
# rev = "dev-sane-1.48.0";
|
||||
# hash = "sha256-vGmOKtwVItxjYioZJlb1og3K6u9s4rcmDnjAPLBC3ao=";
|
||||
# };
|
||||
patches = (upstream.patches or []) ++ [
|
||||
(pkgs.fetchpatch {
|
||||
name = "polkit: add owner annotations to all actions";
|
||||
url = "https://git.uninsane.org/colin/NetworkManager/commit/a01293861fa24201ffaeb84c07f1c71136c49759.patch";
|
||||
hash = "sha256-th1/M2slo7rjkVBwETZII53Lmhyw8OMS0aT9QYI5Uvk=";
|
||||
})
|
||||
];
|
||||
src = pkgs.fetchFromGitea {
|
||||
domain = "git.uninsane.org";
|
||||
owner = "colin";
|
||||
repo = "NetworkManager";
|
||||
# patched to fix polkit permissions (with `nmcli`) when NetworkManager runs as user networkmanager
|
||||
rev = "dev-sane-1.48.0";
|
||||
hash = "sha256-vGmOKtwVItxjYioZJlb1og3K6u9s4rcmDnjAPLBC3ao=";
|
||||
};
|
||||
# patches = [];
|
||||
});
|
||||
# split the package into `daemon` and `nmcli` outputs, because the networkmanager *service*
|
||||
# doesn't need `nmcli`/`nmtui` tooling
|
||||
networkmanager-split = pkgs.networkmanager-split.override { inherit networkmanager; };
|
||||
in {
|
||||
networking.networkmanager.enable = true;
|
||||
systemd.network.wait-online.enable = false; # systemd-networkd-wait-online.service reliably fails on lappy. docs don't match behavior. shit software.
|
||||
# plugins mostly add support for establishing different VPN connections.
|
||||
# the default plugin set includes mostly proprietary VPNs:
|
||||
# - fortisslvpn (Fortinet)
|
||||
@@ -206,12 +199,6 @@ in {
|
||||
logging.level = "INFO";
|
||||
|
||||
# main.dhcp = "internal"; #< default
|
||||
# main.dns controls what to do when NM gets a DNS server via DHCP
|
||||
# - "none" (populate /run/NetworkManager/resolv.conf with DHCP settings)
|
||||
# - "internal" (?)
|
||||
# - "systemd-resolved" (tell systemd-resolved about it, and point /run/NetworkManager/resolv.conf -> systemd)
|
||||
# without this, systemd-resolved won't be able to resolve anything (because it has no upstream servers)
|
||||
# note that NM's resolv.conf isn't (necessarily) /etc/resolv.conf -- that is managed by nixos (via symlinking)
|
||||
main.dns = if config.services.resolved.enable then
|
||||
"systemd-resolved"
|
||||
else if config.sane.services.trust-dns.enable && config.sane.services.trust-dns.asSystemResolver then
|
||||
|
@@ -59,18 +59,14 @@
|
||||
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
|
||||
# "nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay"
|
||||
# as long as my system itself doesn't rely on NIXPKGS at runtime, we can point the overlays to git
|
||||
# to avoid `switch`ing so much during development.
|
||||
# TODO: it would be nice to remove this someday!
|
||||
# it's an impurity that touches way more than i need and tends to cause hard-to-debug eval issues
|
||||
# when it goes wrong. should i port my `nix-shell` scripts to something more tailored to my uses
|
||||
# and then delete `nixpkgs-overlays`?
|
||||
"nixpkgs-overlays=/home/colin/dev/nixos/integrations/nixpkgs/nixpkgs-overlays.nix"
|
||||
# to avoid switching so much during development
|
||||
"nixpkgs-overlays=/home/colin/dev/nixos/hosts/common/nix/overlay"
|
||||
];
|
||||
|
||||
# ensure new deployments have a source of this repo with which they can bootstrap.
|
||||
# this however changes on every commit and can be slow to copy for e.g. `moby`.
|
||||
environment.etc."nixos" = lib.mkIf (config.sane.maxBuildCost >= 3) {
|
||||
source = pkgs.sane-nix-files;
|
||||
source = ../../..;
|
||||
};
|
||||
environment.etc."nix/registry.json" = lib.mkIf (config.sane.maxBuildCost < 3) {
|
||||
enable = false;
|
4
hosts/common/nix/overlay/default.nix
Normal file
4
hosts/common/nix/overlay/default.nix
Normal file
@@ -0,0 +1,4 @@
|
||||
# XXX: NIX_PATH=...:nixpkgs-overlays=... will import every overlay in the directory
|
||||
# so we prefer to give it a directory with just this *one* overlay, otherwise it imports conflicting overlays
|
||||
# and gets stuck in a loop until it OOMs
|
||||
import ../../../../overlays/all.nix
|
17
hosts/common/persist.nix
Normal file
17
hosts/common/persist.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# store /home/colin/a/b in /mnt/persist/private/a/b instead of /mnt/persist/private/home/colin/a/b
|
||||
sane.persist.stores.private.prefix = "/home/colin";
|
||||
|
||||
sane.persist.sys.byStore.initrd = [
|
||||
"/var/log"
|
||||
];
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: these should be private.. somehow
|
||||
"/var/backup" # for e.g. postgres dumps
|
||||
];
|
||||
sane.persist.sys.byStore.cryptClearOnBoot = [
|
||||
"/var/lib/systemd/coredump"
|
||||
];
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
# strictly *decrease* the scope of the default nixos installation/config
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ lib, pkgs, ... }:
|
||||
let
|
||||
suidlessPam = pkgs.pam.overrideAttrs (upstream: {
|
||||
# nixpkgs' pam hardcodes unix_chkpwd path to the /run/wrappers one,
|
||||
@@ -111,11 +111,7 @@ in
|
||||
# pkgs.which
|
||||
# pkgs.zstd
|
||||
];
|
||||
conveniencePackages = [
|
||||
config.boot.kernelPackages.cpupower # <repo:nixos/nixpkgs:nixos/modules/tasks/cpu-freq.nix> places it on PATH for convenience if powerManagement.cpuFreqGovernor is set
|
||||
pkgs.kbd # <repo:nixos/nixpkgs:nixos/modules/config/console.nix> places it on PATH as part of console/virtual TTYs, but probably not needed unless you want to set console fonts
|
||||
];
|
||||
in lib.filter (p: ! builtins.elem p (requiredPackages ++ conveniencePackages));
|
||||
in lib.filter (p: ! builtins.elem p requiredPackages);
|
||||
};
|
||||
|
||||
options.system.fsPackages = lib.mkOption {
|
||||
@@ -216,14 +212,5 @@ in
|
||||
|
||||
# see: <repo:nixos/nixpkgs:nixos/modules/virtualisation/nixos-containers.nix>
|
||||
boot.enableContainers = lib.mkDefault false;
|
||||
|
||||
# see: <repo:nixos/nixpkgs:nixos/modules/tasks/lvm.nix>
|
||||
# lvm places `pkgs.lvm2` onto PATH, which has like 100 binaries.
|
||||
# it is, actually, needed for some userspace tools (cryptsetup). probably just the udev rules. try to reduce this set?
|
||||
services.lvm.enable = lib.mkDefault false;
|
||||
services.udev.packages = [ pkgs.lvm2.out ]; #< N.B. `lvm2.out` != `lvm2`
|
||||
# systemd.packages = [ pkgs.lvm2 ];
|
||||
# systemd.tmpfiles.packages = [ pkgs.lvm2.out ];
|
||||
# environment.systemPackages = [ pkgs.lvm2 ];
|
||||
};
|
||||
}
|
||||
|
@@ -39,7 +39,6 @@ in
|
||||
"btrfs-progs"
|
||||
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
||||
"cryptsetup"
|
||||
"curl"
|
||||
"ddrescue"
|
||||
"dig"
|
||||
"dtc" # device tree [de]compiler
|
||||
@@ -51,14 +50,12 @@ in
|
||||
"fd"
|
||||
"file"
|
||||
"forkstat" # monitor every spawned/forked process
|
||||
"free"
|
||||
# "fwupd"
|
||||
"gawk"
|
||||
"gdb" # to debug segfaults
|
||||
"git"
|
||||
"gptfdisk" # gdisk
|
||||
"hdparm"
|
||||
"hping"
|
||||
"htop"
|
||||
"iftop"
|
||||
"inetutils" # for telnet
|
||||
@@ -80,7 +77,6 @@ in
|
||||
"neovim"
|
||||
"netcat"
|
||||
"nethogs"
|
||||
"nix"
|
||||
"nmap"
|
||||
"nmcli"
|
||||
"nvme-cli" # nvme
|
||||
@@ -88,7 +84,6 @@ in
|
||||
"parted"
|
||||
"pciutils"
|
||||
"powertop"
|
||||
"ps"
|
||||
"pstree"
|
||||
"ripgrep"
|
||||
"s6-rc" # service manager
|
||||
@@ -102,7 +97,6 @@ in
|
||||
"usbutils" # lsusb
|
||||
"util-linux" # lsblk, lscpu, etc
|
||||
"valgrind"
|
||||
"watch"
|
||||
"wget"
|
||||
"wirelesstools" # iwlist
|
||||
# "xq" # jq for XML
|
||||
@@ -127,7 +121,6 @@ in
|
||||
# "dmidecode"
|
||||
"dtrx" # `unar` alternative, "Do The Right eXtraction"
|
||||
# "efivar"
|
||||
"exiftool"
|
||||
"eza" # a better 'ls'
|
||||
# "flashrom"
|
||||
"git" # needed as a user package, for config.
|
||||
@@ -155,16 +148,11 @@ in
|
||||
# "ponymix"
|
||||
"pulsemixer"
|
||||
"python3-repl"
|
||||
# "python3.pkgs.eyeD3" # music tagging
|
||||
# "python3Packages.eyeD3" # music tagging
|
||||
"ripgrep" # needed as a user package so that its user-level config file can be installed
|
||||
"rsync"
|
||||
"rsyslog" # KEEP THIS HERE if you want persistent logging
|
||||
"sane-deadlines"
|
||||
"sane-scripts.bittorrent"
|
||||
"sane-scripts.cli"
|
||||
"sane-secrets-unlock"
|
||||
"sane-sysload"
|
||||
"sc-im"
|
||||
# "snapper"
|
||||
"sops" # for manually viewing secrets; outside `sane-secrets` (TODO: improve sane-secrets!)
|
||||
"speedtest-cli"
|
||||
@@ -184,12 +172,8 @@ in
|
||||
# "gh" # MS GitHub cli
|
||||
"nix-index"
|
||||
"nixpkgs-review"
|
||||
"qmk-udev-rules"
|
||||
"sane-scripts.dev"
|
||||
"sequoia"
|
||||
# "via"
|
||||
"wally-cli"
|
||||
# "zsa-udev-rules"
|
||||
];
|
||||
|
||||
consoleMediaUtils = declPackageSet [
|
||||
@@ -289,28 +273,27 @@ in
|
||||
# "gnome.cheese"
|
||||
# "gnome-feeds" # RSS reader (with claimed mobile support)
|
||||
# "gnome.file-roller"
|
||||
"geary" # adaptive e-mail client; uses webkitgtk 4.1
|
||||
"gnome-calculator"
|
||||
"gnome-calendar"
|
||||
"gnome.geary" # adaptive e-mail client; uses webkitgtk 4.1
|
||||
"gnome.gnome-calculator"
|
||||
"gnome.gnome-calendar"
|
||||
"gnome.gnome-clocks"
|
||||
"gnome.gnome-maps"
|
||||
# "gnome-podcasts"
|
||||
# "gnome.gnome-system-monitor"
|
||||
# "gnome.gnome-terminal" # works on phosh
|
||||
"gnome.gnome-weather"
|
||||
# "seahorse" # keyring/secret manager
|
||||
# "gnome.seahorse" # keyring/secret manager
|
||||
"gnome-frog" # OCR/QR decoder
|
||||
"gpodder"
|
||||
# "gst-device-monitor" # for debugging audio/video
|
||||
"gst-device-monitor" # for debugging audio/video
|
||||
# "gthumb"
|
||||
# "lemoa" # lemmy app
|
||||
# "libcamera" # for `cam` binary (useful for debugging cameras)
|
||||
"libcamera" # for `cam` binary (useful for debugging cameras)
|
||||
"libnotify" # for notify-send; debugging
|
||||
# "lollypop"
|
||||
"loupe" # image viewer
|
||||
"mate.engrampa" # archive manager
|
||||
"mepo" # maps viewer
|
||||
# "mesa-demos" # for eglinfo, glxinfo & other testing tools
|
||||
"mpv"
|
||||
"networkmanagerapplet" # for nm-connection-editor: it's better than not having any gui!
|
||||
"ntfy-sh" # notification service
|
||||
@@ -320,7 +303,7 @@ in
|
||||
# "picard" # music tagging
|
||||
# "libsForQt5.plasmatube" # Youtube player
|
||||
"signal-desktop"
|
||||
# "snapshot" # camera app
|
||||
"snapshot" # camera app
|
||||
"spot" # Gnome Spotify client
|
||||
# "sublime-music"
|
||||
# "tdesktop" # broken on phosh
|
||||
@@ -329,19 +312,17 @@ in
|
||||
"vulkan-tools" # vulkaninfo
|
||||
# "whalebird" # pleroma client (Electron). input is broken on phosh.
|
||||
"xdg-terminal-exec"
|
||||
"youtube-tui"
|
||||
"zathura" # PDF/CBZ/ePUB viewer
|
||||
];
|
||||
|
||||
handheldGuiApps = declPackageSet [
|
||||
# "celluloid" # mpv frontend
|
||||
# "chatty" # matrix/xmpp/irc client (2023/12/29: disabled because broken cross build)
|
||||
# "cozy" # audiobook player
|
||||
"cozy" # audiobook player
|
||||
"epiphany" # gnome's web browser
|
||||
# "iotas" # note taking app
|
||||
"komikku"
|
||||
"koreader"
|
||||
"lgtrombetta-compass"
|
||||
"megapixels" # camera app
|
||||
"notejot" # note taking, e.g. shopping list
|
||||
"planify" # todo-tracker/planner
|
||||
@@ -363,7 +344,7 @@ in
|
||||
# "chromium" # chromium takes hours to build. brave is chromium-based, distributed in binary form, so prefer it.
|
||||
# "cups"
|
||||
"discord" # x86-only
|
||||
# "electrum"
|
||||
"electrum"
|
||||
"element-desktop"
|
||||
"firefox"
|
||||
"font-manager"
|
||||
@@ -371,14 +352,13 @@ in
|
||||
"gimp" # broken on phosh
|
||||
# "gnome.dconf-editor"
|
||||
# "gnome.file-roller"
|
||||
"gnome-disk-utility"
|
||||
"nautilus" # file browser
|
||||
"gnome.gnome-disk-utility"
|
||||
"gnome.nautilus" # file browser
|
||||
# "gnome.totem" # video player, supposedly supports UPnP
|
||||
# "handbrake" #< TODO: fix build
|
||||
"handbrake"
|
||||
"inkscape"
|
||||
# "jellyfin-media-player"
|
||||
"kdenlive"
|
||||
# "keymapp"
|
||||
# "kid3" # audio tagging
|
||||
"krita"
|
||||
"libreoffice" # TODO: replace with an office suite that uses saner packaging?
|
||||
@@ -387,7 +367,7 @@ in
|
||||
# "monero-gui" # x86-only
|
||||
# "mumble"
|
||||
# "nheko" # Matrix chat client
|
||||
"nicotine-plus" # soulseek client
|
||||
# "nicotine-plus" # soulseek client. before re-enabling this make sure it's properly sandboxed!
|
||||
# "obsidian"
|
||||
# "openscad" # 3d modeling
|
||||
# "rhythmbox" # local music player
|
||||
@@ -438,11 +418,6 @@ in
|
||||
|
||||
clang = {};
|
||||
|
||||
clightning-sane.sandbox.method = "bwrap";
|
||||
clightning-sane.sandbox.extraPaths = [
|
||||
"/var/lib/clightning/bitcoin/lightning-rpc"
|
||||
];
|
||||
|
||||
# cryptsetup: typical use is `cryptsetup open /dev/loopxyz mappedName`, and creates `/dev/mapper/mappedName`
|
||||
cryptsetup.sandbox.method = "landlock";
|
||||
cryptsetup.sandbox.extraPaths = [
|
||||
@@ -512,7 +487,7 @@ in
|
||||
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
|
||||
electrum.sandbox.whitelistWayland = true;
|
||||
electrum.persist.byStore.ephemeral = [ ".electrum" ]; #< TODO: use XDG dirs!
|
||||
electrum.persist.byStore.cryptClearOnBoot = [ ".electrum" ]; #< TODO: use XDG dirs!
|
||||
|
||||
endless-sky.buildCost = 1;
|
||||
endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ];
|
||||
@@ -530,10 +505,8 @@ in
|
||||
ethtool.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
# eza `ls` replacement
|
||||
# bwrap causes `/proc` files to be listed differently (e.g. `eza /proc/sys/net/ipv6/conf/`)
|
||||
# bwrap loses group info (so files owned by other users appear as owner "nobody")
|
||||
eza.sandbox.method = "landlock";
|
||||
# eza.sandbox.method = "bwrap";
|
||||
# eza.sandbox.method = "landlock";
|
||||
eza.sandbox.method = "bwrap"; #< note that bwrap causes `/proc` files to be listed differently (e.g. `eza /proc/sys/net/ipv6/conf/`)
|
||||
eza.sandbox.autodetectCliPaths = "existing";
|
||||
eza.sandbox.whitelistPwd = true;
|
||||
eza.sandbox.extraHomePaths = [
|
||||
@@ -597,6 +570,10 @@ in
|
||||
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
|
||||
gawk.sandbox.autodetectCliPaths = "existingFile";
|
||||
|
||||
gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
|
||||
# gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root
|
||||
gdb.sandbox.autodetectCliPaths = true;
|
||||
|
||||
geoclue2-with-demo-agent = {};
|
||||
|
||||
# MS GitHub stores auth token in .config
|
||||
@@ -623,37 +600,32 @@ in
|
||||
"/tmp" # "Cannot open display:" if it can't mount /tmp 👀
|
||||
];
|
||||
|
||||
gnome-calculator.buildCost = 1;
|
||||
gnome-calculator.sandbox.method = "bwrap";
|
||||
gnome-calculator.sandbox.whitelistWayland = true;
|
||||
"gnome.gnome-calculator".buildCost = 1;
|
||||
"gnome.gnome-calculator".sandbox.method = "bwrap";
|
||||
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
|
||||
|
||||
gnome-calendar.buildCost = 1;
|
||||
"gnome.gnome-calendar".buildCost = 1;
|
||||
# gnome-calendar surely has data to persist, but i use it strictly to do date math, not track events.
|
||||
gnome-calendar.sandbox.method = "bwrap";
|
||||
gnome-calendar.sandbox.whitelistWayland = true;
|
||||
"gnome.gnome-calendar".sandbox.method = "bwrap";
|
||||
"gnome.gnome-calendar".sandbox.whitelistWayland = true;
|
||||
|
||||
# gnome-disks
|
||||
gnome-disk-utility.buildCost = 1;
|
||||
gnome-disk-utility.sandbox.method = "bwrap";
|
||||
gnome-disk-utility.sandbox.whitelistDbus = [ "system" ];
|
||||
gnome-disk-utility.sandbox.whitelistWayland = true;
|
||||
gnome-disk-utility.sandbox.extraHomePaths = [
|
||||
"gnome.gnome-disk-utility".buildCost = 1;
|
||||
"gnome.gnome-disk-utility".sandbox.method = "bwrap";
|
||||
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
|
||||
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
|
||||
"gnome.gnome-disk-utility".sandbox.extraHomePaths = [
|
||||
"tmp"
|
||||
"use/iso"
|
||||
# TODO: probably need /dev and such
|
||||
];
|
||||
|
||||
hping.sandbox.method = "landlock";
|
||||
hping.sandbox.net = "all";
|
||||
hping.sandbox.capabilities = [ "net_raw" ];
|
||||
hping.sandbox.autodetectCliPaths = "existingFile"; # for sending packet data from file
|
||||
|
||||
# seahorse: dump gnome-keyring secrets.
|
||||
seahorse.buildCost = 1;
|
||||
# N.B. it can lso manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
|
||||
seahorse.sandbox.method = "bwrap";
|
||||
seahorse.sandbox.whitelistDbus = [ "user" ];
|
||||
seahorse.sandbox.whitelistWayland = true;
|
||||
"gnome.seahorse".buildCost = 1;
|
||||
# N.B.: it can also manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
|
||||
"gnome.seahorse".sandbox.method = "bwrap";
|
||||
"gnome.seahorse".sandbox.whitelistDbus = [ "user" ];
|
||||
"gnome.seahorse".sandbox.whitelistWayland = true;
|
||||
|
||||
gnome-2048.buildCost = 1;
|
||||
gnome-2048.sandbox.method = "bwrap";
|
||||
@@ -678,7 +650,7 @@ in
|
||||
"Pictures/Screenshots"
|
||||
"Pictures/servo-macros"
|
||||
];
|
||||
gnome-frog.persist.byStore.ephemeral = [
|
||||
gnome-frog.persist.byStore.cryptClearOnBoot = [
|
||||
".local/share/tessdata" # 15M; dunno what all it is.
|
||||
];
|
||||
|
||||
@@ -816,14 +788,6 @@ in
|
||||
libnotify.sandbox.method = "bwrap";
|
||||
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
|
||||
|
||||
lightning-cli.packageUnwrapped = pkgs.linkBinIntoOwnPackage pkgs.clightning "lightning-cli";
|
||||
lightning-cli.sandbox.method = "bwrap";
|
||||
lightning-cli.sandbox.extraHomePaths = [
|
||||
".lightning/bitcoin/lightning-rpc"
|
||||
];
|
||||
# `lightning-cli` finds its RPC file via `~/.lightning/bitcoin/lightning-rpc`, to message the daemon
|
||||
lightning-cli.fs.".lightning".symlink.target = "/var/lib/clightning";
|
||||
|
||||
losslesscut-bin.buildCost = 1;
|
||||
losslesscut-bin.sandbox.method = "bwrap";
|
||||
losslesscut-bin.sandbox.extraHomePaths = [
|
||||
@@ -851,11 +815,6 @@ in
|
||||
mercurial.sandbox.net = "clearnet";
|
||||
mercurial.sandbox.whitelistPwd = true;
|
||||
|
||||
mesa-demos.sandbox.method = "bwrap";
|
||||
mesa-demos.sandbox.whitelistDri = true;
|
||||
mesa-demos.sandbox.whitelistWayland = true;
|
||||
mesa-demos.sandbox.whitelistX = true;
|
||||
|
||||
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
||||
monero-gui.buildCost = 1;
|
||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||
@@ -900,7 +859,7 @@ in
|
||||
nixpkgs-review.sandbox.extraPaths = [
|
||||
"/nix"
|
||||
];
|
||||
nixpkgs-review.persist.byStore.ephemeral = [
|
||||
nixpkgs-review.persist.byStore.cryptClearOnBoot = [
|
||||
".cache/nixpkgs-review" #< help it not exhaust / tmpfs
|
||||
];
|
||||
|
||||
@@ -951,7 +910,7 @@ in
|
||||
"/sys/devices"
|
||||
];
|
||||
|
||||
"perlPackages.FileMimeInfo" = {};
|
||||
"perlPackages.FileMimeInfo".sandbox.enable = false; #< TODO: sandbox `mimetype` but not `mimeopen`.
|
||||
|
||||
powertop.sandbox.method = "landlock";
|
||||
powertop.sandbox.capabilities = [ "ipc_lock" "sys_admin" ];
|
||||
@@ -984,9 +943,7 @@ in
|
||||
|
||||
python3-repl.packageUnwrapped = pkgs.python3.withPackages (ps: with ps; [
|
||||
psutil
|
||||
pykakasi
|
||||
requests
|
||||
unidecode
|
||||
]);
|
||||
python3-repl.sandbox.method = "bwrap";
|
||||
python3-repl.sandbox.net = "clearnet";
|
||||
@@ -1014,17 +971,9 @@ in
|
||||
sane-weather.sandbox.method = "bwrap";
|
||||
sane-weather.sandbox.net = "clearnet";
|
||||
|
||||
sc-im.sandbox.method = "bwrap";
|
||||
sc-im.sandbox.autodetectCliPaths = "existingFile";
|
||||
|
||||
screen.sandbox.enable = false; #< tty; needs to run anything
|
||||
|
||||
sequoia.packageUnwrapped = pkgs.sequoia.overrideAttrs (_: {
|
||||
# XXX(2024-07-30): sq_autocrypt_import test failure: "Warning: 9B7DD433F254904A is expired."
|
||||
doCheck = false;
|
||||
});
|
||||
sequoia.buildCost = 1;
|
||||
sequoia.sandbox.method = "bwrap";
|
||||
sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
sequoia.sandbox.whitelistPwd = true;
|
||||
sequoia.sandbox.autodetectCliPaths = "existingFileOrParent"; # supports `-o <file-to-create>`
|
||||
|
||||
@@ -1068,9 +1017,7 @@ in
|
||||
"Music"
|
||||
"tmp"
|
||||
"use"
|
||||
".config/dconf"
|
||||
];
|
||||
soundconverter.sandbox.whitelistDbus = [ "user" ]; # for dconf
|
||||
soundconverter.sandbox.extraPaths = [
|
||||
"/mnt/servo/media/Music"
|
||||
"/mnt/servo/media/games"
|
||||
@@ -1150,6 +1097,9 @@ in
|
||||
valgrind.buildCost = 1;
|
||||
valgrind.sandbox.enable = false; #< it's a launcher: can't sandbox
|
||||
|
||||
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
visidata.sandbox.autodetectCliPaths = true;
|
||||
|
||||
# `vulkaninfo`, `vkcube`
|
||||
vulkan-tools.sandbox.method = "landlock";
|
||||
|
||||
@@ -1167,8 +1117,6 @@ in
|
||||
"tmp"
|
||||
];
|
||||
|
||||
watch.sandbox.enable = false; #< it executes the command it's given
|
||||
|
||||
wdisplays.sandbox.method = "bwrap";
|
||||
wdisplays.sandbox.whitelistWayland = true;
|
||||
|
||||
@@ -1181,12 +1129,10 @@ in
|
||||
|
||||
# `wg`, `wg-quick`
|
||||
wireguard-tools.sandbox.method = "landlock";
|
||||
wireguard-tools.sandbox.net = "all";
|
||||
wireguard-tools.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
# provides `iwconfig`, `iwlist`, `iwpriv`, ...
|
||||
wirelesstools.sandbox.method = "landlock";
|
||||
wirelesstools.sandbox.net = "all";
|
||||
wirelesstools.sandbox.capabilities = [ "net_admin" ];
|
||||
|
||||
wl-clipboard.sandbox.method = "bwrap";
|
||||
@@ -1209,6 +1155,8 @@ in
|
||||
yt-dlp.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||
yt-dlp.sandbox.net = "all";
|
||||
yt-dlp.sandbox.whitelistPwd = true; # saves to pwd by default
|
||||
|
||||
zfs = {};
|
||||
};
|
||||
|
||||
sane.persist.sys.byStore.plaintext = lib.mkIf config.sane.programs.guiApps.enabled [
|
||||
@@ -1225,12 +1173,13 @@ in
|
||||
];
|
||||
};
|
||||
|
||||
hardware.graphics = lib.mkIf config.sane.programs.guiApps.enabled ({
|
||||
hardware.opengl = lib.mkIf config.sane.programs.guiApps.enabled ({
|
||||
enable = true;
|
||||
driSupport = lib.mkDefault true;
|
||||
} // (lib.optionalAttrs pkgs.stdenv.isx86_64 {
|
||||
# for 32 bit applications
|
||||
# upstream nixpkgs forbids setting enable32Bit unless specifically x86_64 (so aarch64 isn't allowed)
|
||||
enable32Bit = lib.mkDefault true;
|
||||
# upstream nixpkgs forbids setting driSupport32Bit unless specifically x86_64 (so aarch64 isn't allowed)
|
||||
driSupport32Bit = lib.mkDefault true;
|
||||
}));
|
||||
|
||||
system.activationScripts.notifyActive = lib.mkIf config.sane.programs.guiApps.enabled {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.ausyscall = {
|
||||
packageUnwrapped = pkgs.linkBinIntoOwnPackage pkgs.audit "ausyscall";
|
||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.audit "bin/ausyscall";
|
||||
|
||||
sandbox.method = "landlock";
|
||||
};
|
||||
|
@@ -1,43 +0,0 @@
|
||||
# Avahi zeroconf (mDNS) implementation.
|
||||
# runs as systemd `avahi-daemon.service`
|
||||
#
|
||||
# - <https://avahi.org/>
|
||||
# - code: <https://github.com/avahi/avahi>
|
||||
# - IRC: #avahi on irc.libera.chat
|
||||
#
|
||||
# - `avahi-browse --help` for usage
|
||||
# - `man avahi-daemon.conf`
|
||||
# - `LD_LIBRARY_PATH=/nix/store/ngwj3jqmxh8k4qji2z0lj7y1f8vzqrn2-nss-mdns-0.15.1/lib getent hosts desko.local`
|
||||
# nss-mdns goes through avahi-daemon, so there IS caching here
|
||||
#
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
sane.programs.avahi = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [ "system" ];
|
||||
sandbox.net = "all"; #< otherwise it will show 'null' in place of each interface name.
|
||||
sandbox.extraPaths = [
|
||||
"/" #< else the daemon exits immediately. TODO: decrease this scope.
|
||||
];
|
||||
};
|
||||
services.avahi = lib.mkIf config.sane.programs.avahi.enabled {
|
||||
enable = true;
|
||||
package = config.sane.programs.avahi.package;
|
||||
publish.enable = true;
|
||||
publish.userServices = true;
|
||||
nssmdns4 = true;
|
||||
nssmdns6 = true;
|
||||
# reflector = true;
|
||||
allowInterfaces = [
|
||||
# particularly, the default config disallows loopback, which is kinda fucking retarded, right?
|
||||
"ens1" #< servo
|
||||
"enp5s0" #< desko
|
||||
"eth0"
|
||||
"lo"
|
||||
"wg-home"
|
||||
"wlan0" #< moby
|
||||
"wlp3s0" #< lappy
|
||||
"wlp4s0" #< desko
|
||||
];
|
||||
};
|
||||
}
|
@@ -95,7 +95,7 @@ in
|
||||
|
||||
packageUnwrapped = pkgs.bemenu.overrideAttrs (upstream: {
|
||||
nativeBuildInputs = (upstream.nativeBuildInputs or []) ++ [
|
||||
pkgs.makeBinaryWrapper
|
||||
pkgs.makeWrapper
|
||||
];
|
||||
# can alternatively be specified as CLI flags
|
||||
postInstall = (upstream.postInstall or "") + ''
|
||||
|
@@ -1,13 +0,0 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.bitcoin-cli = {
|
||||
packageUnwrapped = pkgs.linkBinIntoOwnPackage pkgs.bitcoind "bitcoin-cli";
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.autodetectCliPaths = "existing"; #< for `bitcoin-cli -datadir=/var/lib/...`
|
||||
sandbox.extraHomePaths = [
|
||||
".bitcoin/bitcoin.conf"
|
||||
];
|
||||
sandbox.net = "all"; # actually needs only localhost
|
||||
secrets.".bitcoin/bitcoin.conf" = ../../../secrets/servo/bitcoin.conf.bin;
|
||||
};
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p blast-ugjka -p python3
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p blast-ugjka
|
||||
# vim: set filetype=python :
|
||||
|
||||
import logging
|
||||
|
@@ -31,7 +31,7 @@ in
|
||||
|
||||
sane.programs.blast-to-default = {
|
||||
# helper to deal with blast's interactive CLI
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkPython3 {
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "blast-to-default";
|
||||
pkgs = [ "blast-ugjka" ];
|
||||
srcRoot = ./.;
|
||||
|
@@ -111,7 +111,7 @@ in
|
||||
'';
|
||||
});
|
||||
|
||||
fs.".config/bonsai/bonsai_tree.json".symlink.target = pkgs.writers.writeJSON "bonsai_tree.json" cfg.config.transitions;
|
||||
fs.".config/bonsai/bonsai_tree.json".symlink.text = builtins.toJSON cfg.config.transitions;
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.extraRuntimePaths = [
|
||||
|
@@ -21,7 +21,7 @@
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
|
||||
persist.byStore.ephemeral = [
|
||||
persist.byStore.cryptClearOnBoot = [
|
||||
".cache/BraveSoftware"
|
||||
".config/BraveSoftware"
|
||||
];
|
||||
|
@@ -13,7 +13,7 @@
|
||||
sane.programs.callaudiod = {
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.callaudiod;
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
# probably more needed once i enable proper sandboxing, but for now this ensures the service isn't started too early!
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
|
||||
|
@@ -24,7 +24,7 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.calls.overrideAttrs (upstream: {
|
||||
packageUnwrapped = pkgs.calls.overrideAttrs (upstream: {
|
||||
patches = (upstream.patches or []) ++ [
|
||||
(pkgs.fetchpatch {
|
||||
# usability improvement... if the UI is visible, then i can receive calls. otherwise, i can't!
|
||||
@@ -33,10 +33,10 @@ in
|
||||
hash = "sha256-NoVQV2TlkCcsBt0uwSyK82hBKySUW4pADrJVfLFvWgU=";
|
||||
})
|
||||
];
|
||||
}));
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "vpn.wg-home"; #< XXX(2024/07/05): my cell carrier seems to block RTP, so tunnel it.
|
||||
sandbox.net = "clearnet";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDbus = [ "user" ]; # necessary for secrets, at the minimum
|
||||
sandbox.whitelistWayland = true;
|
||||
|
@@ -66,7 +66,6 @@ end
|
||||
if vars.percent ~= nil then
|
||||
bat_args = bat_args .. " --percent-suffix '" .. vars.percent .. "'"
|
||||
end
|
||||
bat_args = bat_args .. " {bat}"
|
||||
|
||||
-- N.B.: `[[ <text> ]]` is Lua's multiline string literal
|
||||
conky.text = [[
|
||||
@@ -74,8 +73,8 @@ ${color1}${shadecolor 707070}${font sans-serif:size=50:style=Bold}${alignc}${exe
|
||||
${color2}${shadecolor a4d7d0}${font sans-serif:size=20}${alignc}${exec date +"%a %d %b"}${font}
|
||||
|
||||
|
||||
${color1}${shadecolor}${font sans-serif:size=22:style=Bold}${alignc}${execp sane-sysload ]] .. bat_args .. [[ }${font}
|
||||
${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 timeout 20 sane-weather }${font}
|
||||
${color1}${shadecolor}${font sans-serif:size=22:style=Bold}${alignc}${execp @bat@ ]] .. bat_args .. [[ }${font}
|
||||
${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 @weather@ }${font}
|
||||
|
||||
|
||||
${color2}${shadecolor a4d7d0}${font sans-serif:size=16}${alignc}⇅ ${downspeedf wlan0}]] .. vars.kBps .. [[${font}
|
||||
|
@@ -1,22 +1,38 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.sane-battery-estimate = {
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkBash {
|
||||
pname = "sane-battery-estimate";
|
||||
srcRoot = ./.;
|
||||
};
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.extraPaths = [
|
||||
"/sys/class/power_supply"
|
||||
"/sys/devices"
|
||||
];
|
||||
};
|
||||
|
||||
sane.programs.conky = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "clearnet"; #< for the scripts it calls (weather)
|
||||
sandbox.extraPaths = [
|
||||
"/sys/class/power_supply"
|
||||
"/sys/devices" # needed by sane-sysload
|
||||
"/sys/devices" # needed by battery_estimate
|
||||
# "/sys/devices/cpu"
|
||||
# "/sys/devices/system"
|
||||
];
|
||||
sandbox.whitelistWayland = true;
|
||||
|
||||
suggestedPrograms = [
|
||||
"sane-sysload"
|
||||
"sane-battery-estimate"
|
||||
"sane-weather"
|
||||
];
|
||||
|
||||
fs.".config/conky/conky.conf".symlink.target = ./conky.conf;
|
||||
fs.".config/conky/conky.conf".symlink.target = pkgs.substituteAll {
|
||||
src = ./conky.conf;
|
||||
bat = "sane-battery-estimate";
|
||||
weather = "timeout 20 sane-weather";
|
||||
};
|
||||
|
||||
services.conky = {
|
||||
description = "conky dynamic desktop background";
|
||||
|
183
hosts/common/programs/conky/sane-battery-estimate
Executable file
183
hosts/common/programs/conky/sane-battery-estimate
Executable file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash
|
||||
|
||||
usage() {
|
||||
echo "usage: battery_estimate [options...]"
|
||||
echo
|
||||
echo "pretty-prints a battery estimate (icon to indicate state, and a duration estimate)"
|
||||
echo
|
||||
echo "options:"
|
||||
echo " --debug: output additional information, to stderr"
|
||||
echo " --minute-suffix <string>: use the provided string as a minutes suffix"
|
||||
echo " --hour-suffix <string>: use the provided string as an hours suffix"
|
||||
echo " --icon-suffix <string>: use the provided string as an icon suffix"
|
||||
echo " --percent-suffix <string>: use the provided string when displaying percents"
|
||||
}
|
||||
|
||||
# these icons may only render in nerdfonts
|
||||
icon_bat_chg=("" "" "" "")
|
||||
icon_bat_dis=("" "" "" "")
|
||||
suffix_icon=" " # thin space
|
||||
suffix_percent="%"
|
||||
# suffix_icon=" "
|
||||
|
||||
# render time like: 2ʰ08ᵐ
|
||||
# unicode sub/super-scripts: <https://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts>
|
||||
# symbol_hr="ʰ"
|
||||
# symbol_min="ᵐ"
|
||||
|
||||
# render time like: 2ₕ08ₘ
|
||||
# symbol_hr="ₕ"
|
||||
# symbol_min="ₘ"
|
||||
|
||||
# render time like: 2h08m
|
||||
# symbol_hr="h"
|
||||
# symbol_min="m"
|
||||
|
||||
# render time like: 2:08
|
||||
# symbol_hr=":"
|
||||
# symbol_min=
|
||||
|
||||
# render time like: 2꞉08⧗
|
||||
symbol_hr="꞉"
|
||||
symbol_min="⧗"
|
||||
# variants:
|
||||
# symbol_hr=":"
|
||||
# symbol_min="⧖"
|
||||
# symbol_min="⌛"
|
||||
|
||||
# render time like: 2'08"
|
||||
# symbol_hr="'"
|
||||
# symbol_min='"'
|
||||
|
||||
log() {
|
||||
if [ "$BATTERY_ESTIMATE_DEBUG" = "1" ]; then
|
||||
printf "$@" >&2
|
||||
echo >&2
|
||||
fi
|
||||
}
|
||||
|
||||
render_icon() {
|
||||
# args:
|
||||
# 1: "chg" or "dis"
|
||||
# 2: current battery percentage
|
||||
level=$(($2 / 25))
|
||||
level=$(($level > 3 ? 3 : $level))
|
||||
level=$(($level < 0 ? 0 : $level))
|
||||
log "icon: %s %d" "$1" "$level"
|
||||
if [ "$1" = "dis" ]; then
|
||||
printf "%s" "${icon_bat_dis[$level]}"
|
||||
elif [ "$1" = "chg" ]; then
|
||||
printf "%s" "${icon_bat_chg[$level]}"
|
||||
fi
|
||||
}
|
||||
|
||||
try_path() {
|
||||
# assigns output variables:
|
||||
# - perc, perc_from_full (0-100)
|
||||
# - full, rate (pos means charging)
|
||||
if [ -f "$1/capacity" ]; then
|
||||
log "perc, perc_from_full from %s" "$1/capacity"
|
||||
perc=$(cat "$1/capacity")
|
||||
perc_from_full=$((100 - $perc))
|
||||
fi
|
||||
|
||||
if [ -f "$1/charge_full_design" ] && [ -f "$1/current_now" ]; then
|
||||
log "full, rate from %s and %s" "$1/charge_full_design" "$1/current_now"
|
||||
# current is positive when charging
|
||||
full=$(cat "$1/charge_full_design")
|
||||
rate=$(cat "$1/current_now")
|
||||
elif [ -f "$1/energy_full" ] && [ -f "$1/power_now" ]; then
|
||||
log "full, rate from %s and %s" "$1/energy_full" "$1/power_now"
|
||||
# power_now is positive when discharging
|
||||
full=$(cat "$1/energy_full")
|
||||
rate=-$(cat "$1/power_now")
|
||||
elif [ -f "$1/energy_full" ] && [ -f "$1/energy_now" ]; then
|
||||
log "full, rate from %s and %s" "$1/energy_full" "$1/energy_now"
|
||||
log " this is a compatibility path for legacy Thinkpad batteries which do not populate the 'power_now' field, and incorrectly populate 'energy_now' with power info"
|
||||
# energy_now is positive when discharging
|
||||
full=$(cat "$1/energy_full")
|
||||
rate=-$(cat "$1/energy_now")
|
||||
fi
|
||||
}
|
||||
|
||||
try_all_paths() {
|
||||
try_path "/sys/class/power_supply/axp20x-battery" # Pinephone
|
||||
try_path "/sys/class/power_supply/BAT0" # Thinkpad
|
||||
log "perc: %d, perc_from_full: %d" "$perc" "$perc_from_full"
|
||||
log "full: %f, rate: %f" "$full" "$rate"
|
||||
log " rate > 0 means charging, else discharging"
|
||||
}
|
||||
|
||||
fmt_minutes() {
|
||||
# args:
|
||||
# 1: icon to render
|
||||
# 2: string to show if charge/discharge time is indefinite
|
||||
# 3: minutes to stable state (i.e. to full charge or full discharge)
|
||||
# - we work in minutes instead of hours for precision: bash math is integer-only
|
||||
log "charge/discharge time: %f min" "$3"
|
||||
# args: <battery symbol> <text if ludicrous estimate> <estimated minutes to full/empty>
|
||||
if [ -n "$3" ] && [ "$3" -lt 1440 ]; then
|
||||
hr=$(($3 / 60))
|
||||
hr_in_min=$(($hr * 60))
|
||||
min=$(($3 - $hr_in_min))
|
||||
printf "%s%s%d%s%02d%s" "$1" "$suffix_icon" "$hr" "$symbol_hr" "$min" "$symbol_min"
|
||||
else
|
||||
log "charge/discharge duration > 1d"
|
||||
printf "%s%s%s" "$1" "$suffix_icon" "$2" # more than 1d
|
||||
fi
|
||||
}
|
||||
|
||||
pretty_output() {
|
||||
if [ -n "$perc" ]; then
|
||||
duration=""
|
||||
if [ "$rate" -gt 0 ]; then
|
||||
log "charging"
|
||||
icon="$(render_icon chg $perc)"
|
||||
duration="$(($full * 60 * $perc_from_full / (100 * $rate)))"
|
||||
else
|
||||
log "discharging"
|
||||
icon="$(render_icon dis $perc)"
|
||||
if [ "$rate" -lt 0 ]; then
|
||||
duration="$(($full * 60 * $perc / (-100 * $rate)))"
|
||||
fi
|
||||
fi
|
||||
fmt_minutes "$icon" "$perc$suffix_percent" "$duration"
|
||||
fi
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
"--debug")
|
||||
shift
|
||||
BATTERY_ESTIMATE_DEBUG=1
|
||||
;;
|
||||
"--icon-suffix")
|
||||
shift
|
||||
suffix_icon="$1"
|
||||
shift
|
||||
;;
|
||||
"--hour-suffix")
|
||||
shift
|
||||
symbol_hr="$1"
|
||||
shift
|
||||
;;
|
||||
"--minute-suffix")
|
||||
shift
|
||||
symbol_min="$1"
|
||||
shift
|
||||
;;
|
||||
"--percent-suffix")
|
||||
shift
|
||||
suffix_percent="$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
try_all_paths
|
||||
pretty_output
|
@@ -1,8 +0,0 @@
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.curl = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "all";
|
||||
sandbox.autodetectCliPaths = "parent"; #< for `-o` option
|
||||
};
|
||||
}
|
@@ -10,9 +10,7 @@
|
||||
./assorted.nix
|
||||
./audacity.nix
|
||||
./ausyscall.nix
|
||||
./avahi.nix
|
||||
./bemenu.nix
|
||||
./bitcoin-cli.nix
|
||||
./blast-ugjka
|
||||
./bonsai.nix
|
||||
./brave.nix
|
||||
@@ -27,7 +25,6 @@
|
||||
./conky
|
||||
./cozy.nix
|
||||
./cups.nix
|
||||
./curl.nix
|
||||
./curlftpfs.nix
|
||||
./dbus.nix
|
||||
./dconf.nix
|
||||
@@ -42,23 +39,18 @@
|
||||
./epiphany.nix
|
||||
./errno.nix
|
||||
./evince.nix
|
||||
./exiftool.nix
|
||||
./fcitx5.nix
|
||||
./feedbackd.nix
|
||||
./firefox.nix
|
||||
./flare-signal.nix
|
||||
./fontconfig.nix
|
||||
./fractal.nix
|
||||
./free.nix
|
||||
./frozen-bubble.nix
|
||||
./fwupd.nix
|
||||
./g4music.nix
|
||||
./gajim.nix
|
||||
./gdb.nix
|
||||
./gdbus.nix
|
||||
./geary.nix
|
||||
./geoclue-demo-agent.nix
|
||||
./geoclue2.nix
|
||||
./git.nix
|
||||
./gnome-clocks.nix
|
||||
./gnome-feeds.nix
|
||||
@@ -67,8 +59,6 @@
|
||||
./gnome-weather.nix
|
||||
./go2tv.nix
|
||||
./gpodder.nix
|
||||
./gpsd.nix
|
||||
./gps-share.nix
|
||||
./grimshot.nix
|
||||
./gst-device-monitor.nix
|
||||
./gthumb.nix
|
||||
@@ -76,16 +66,13 @@
|
||||
./handbrake.nix
|
||||
./helix.nix
|
||||
./htop
|
||||
./iio-sensor-proxy.nix
|
||||
./imagemagick.nix
|
||||
./jellyfin-media-player.nix
|
||||
./kdenlive.nix
|
||||
./keymapp.nix
|
||||
./komikku.nix
|
||||
./koreader
|
||||
./less.nix
|
||||
./lftp.nix
|
||||
./lgtrombetta-compass.nix
|
||||
./libreoffice.nix
|
||||
./lemoa.nix
|
||||
./loupe.nix
|
||||
@@ -93,54 +80,38 @@
|
||||
./megapixels.nix
|
||||
./mepo.nix
|
||||
./mimeo
|
||||
./mimetype.nix
|
||||
./mmcli.nix
|
||||
./mopidy.nix
|
||||
./mpv
|
||||
./msmtp.nix
|
||||
./nautilus.nix
|
||||
./neovim.nix
|
||||
./networkmanager_dmenu
|
||||
./newsflash.nix
|
||||
./nheko.nix
|
||||
./nicotine-plus.nix
|
||||
./nix-index.nix
|
||||
./nix.nix
|
||||
./nmcli.nix
|
||||
./notejot.nix
|
||||
./ntfy-sh.nix
|
||||
./nwg-panel
|
||||
./objdump.nix
|
||||
./obsidian.nix
|
||||
./offlineimap.nix
|
||||
./ols.nix
|
||||
./open-in-mpv.nix
|
||||
./pactl.nix
|
||||
./pidof.nix
|
||||
./pipewire
|
||||
./pkill.nix
|
||||
./pipewire.nix
|
||||
./planify.nix
|
||||
./portfolio-filemanager.nix
|
||||
./playerctl.nix
|
||||
./ps.nix
|
||||
./qmk-udev-rules.nix
|
||||
./rhythmbox.nix
|
||||
./ripgrep.nix
|
||||
./rofi
|
||||
./rsyslog
|
||||
./rtkit.nix
|
||||
./s6-rc.nix
|
||||
./sane-deadlines.nix
|
||||
./sane-input-handler
|
||||
./sane-open.nix
|
||||
./sane-private-unlock-remote.nix
|
||||
./sane-screenshot.nix
|
||||
./sane-scripts.nix
|
||||
./sane-secrets-unlock.nix
|
||||
./sane-sysload.nix
|
||||
./sane-theme.nix
|
||||
./sanebox.nix
|
||||
./satellite.nix
|
||||
./schlock.nix
|
||||
./seatd.nix
|
||||
./sfeed.nix
|
||||
@@ -159,19 +130,14 @@
|
||||
./swayidle.nix
|
||||
./swaylock.nix
|
||||
./swaynotificationcenter
|
||||
./switchboard.nix
|
||||
./syshud.nix
|
||||
./sysvol.nix
|
||||
./tangram.nix
|
||||
./tor-browser.nix
|
||||
./tuba.nix
|
||||
./unl0kr
|
||||
./via.nix
|
||||
./visidata.nix
|
||||
./vlc.nix
|
||||
./wally-cli.nix
|
||||
./waybar
|
||||
./waylock.nix
|
||||
./where-am-i.nix
|
||||
./wike.nix
|
||||
./wine.nix
|
||||
./wireplumber.nix
|
||||
@@ -183,13 +149,10 @@
|
||||
./xdg-desktop-portal-wlr.nix
|
||||
./xdg-terminal-exec.nix
|
||||
./xdg-utils.nix
|
||||
./youtube-tui.nix
|
||||
./zathura.nix
|
||||
./zeal.nix
|
||||
./zecwallet-lite.nix
|
||||
./zulip.nix
|
||||
./zsa-udev-rules.nix
|
||||
./zfs-tools.nix
|
||||
./zsh
|
||||
];
|
||||
|
||||
|
@@ -50,13 +50,32 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
packageUnwrapped = pkgs.dino.override {
|
||||
packageUnwrapped = (pkgs.dino.override {
|
||||
# XXX(2024/04/24): build without echo cancelation (i.e. force WITH_VOICE_PROCESSOR to be undefined).
|
||||
# this means that if the other end of the call is on speaker phone, i'm liable to hear my own voice
|
||||
# leave their speaker, enter their mic, and then return to me.
|
||||
# the benefit is a >50% reduction in CPU use. insignificant on any modern PC; make-or-break on a low-power Pinephone.
|
||||
webrtc-audio-processing = null;
|
||||
};
|
||||
}).overrideAttrs (upstream: {
|
||||
# i'm updating experimentally to see if it improves call performance.
|
||||
# i don't *think* this is actually necessary; i don't notice any difference.
|
||||
version = "0.4.3-unstable-2024-04-28";
|
||||
src = lib.warnIf (lib.versionOlder "0.4.3" upstream.version) "dino update: safe to remove sane patches" pkgs.fetchFromGitHub {
|
||||
owner = "dino";
|
||||
repo = "dino";
|
||||
rev = "657502955567dd538e56f300e075c7db52e25d74";
|
||||
hash = "sha256-SApJy9FgxxLOB5A/zGtpdFZtSqSiS03vggRrCte1tFE=";
|
||||
};
|
||||
# avoid double-application of upstreamed patches
|
||||
# https://github.com/NixOS/nixpkgs/pull/309265
|
||||
patches = [];
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
./xmpp-vala-test
|
||||
# ./signal-protocol-vala-test # doesn't exist anymore
|
||||
runHook postCheck
|
||||
'';
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "clearnet";
|
||||
|
@@ -6,17 +6,6 @@ in
|
||||
sane.programs.eg25-control = {
|
||||
suggestedPrograms = [ "mmcli" ];
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.extraPaths = [
|
||||
"/sys/class/modem-power"
|
||||
"/sys/devices"
|
||||
# "/var/lib/eg25-control"
|
||||
];
|
||||
sandbox.net = "all"; #< for downloading the almanac
|
||||
sandbox.whitelistDbus = [
|
||||
"system" #< used by `mmcli`
|
||||
];
|
||||
|
||||
services.eg25-control-powered = {
|
||||
description = "eg25-control-powered: power to the Qualcomm eg25 modem used by PinePhone";
|
||||
startCommand = "eg25-control --power-on --verbose";
|
||||
@@ -32,7 +21,6 @@ in
|
||||
startCommand = "eg25-control --enable-gps --dump-debug-info --verbose";
|
||||
cleanupCommand = "eg25-control --disable-gps --dump-debug-info --verbose";
|
||||
depends = [ "eg25-control-powered" ];
|
||||
partOf = [ "gps" ];
|
||||
};
|
||||
|
||||
persist.byStore.plaintext = [ ".cache/eg25-control" ]; #< for cached agps data
|
||||
|
@@ -45,9 +45,6 @@
|
||||
"Videos/servo"
|
||||
"tmp"
|
||||
];
|
||||
sandbox.extraPaths = [
|
||||
"/dev/snd" #< needed only when playing embedded audio (not embedded video!)
|
||||
];
|
||||
|
||||
# creds/session keys, etc
|
||||
persist.byStore.private = [ ".config/Element" ];
|
||||
|
@@ -1,15 +1,19 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.errno = {
|
||||
# packageUnwrapped = pkgs.linkBinIntoOwnPackage pkgs.moreutils "errno";
|
||||
# packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.moreutils "bin/errno";
|
||||
# actually, don't build all of moreutils because not all of it builds for cross targets.
|
||||
# some of this can be simplified after <https://github.com/NixOS/nixpkgs/pull/316446>
|
||||
packageUnwrapped = pkgs.moreutils.overrideAttrs (base: {
|
||||
makeFlags = (base.makeFlags or []) ++ [
|
||||
"BINS=errno"
|
||||
"MANS=errno.1"
|
||||
"PERLSCRIPTS=errno" #< Makefile errors if empty, but this works :)
|
||||
"INSTALL_BIN=install"
|
||||
];
|
||||
buildInputs = []; #< errno has no runtime perl deps, and they don't cross compile, so disable them.
|
||||
#v disable the perl-specific stuff
|
||||
propagatedBuildInputs = [];
|
||||
postInstall = "";
|
||||
});
|
||||
|
||||
sandbox.method = "landlock";
|
||||
|
@@ -1,7 +0,0 @@
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.exiftool = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.autodetectCliPaths = "existingFile";
|
||||
};
|
||||
}
|
@@ -55,7 +55,7 @@ in
|
||||
# - theme-demo
|
||||
# - timeout-completed
|
||||
# - window-close
|
||||
fs.".config/feedbackd/themes/proxied.json".symlink.target = pkgs.writers.writeJSON "proxied.json" {
|
||||
fs.".config/feedbackd/themes/proxied.json".symlink.text = builtins.toJSON {
|
||||
name = "proxied";
|
||||
parent-theme = "default";
|
||||
profiles = [
|
||||
|
@@ -105,22 +105,6 @@ let
|
||||
};
|
||||
# extraPrefs = ...
|
||||
}).overrideAttrs (base: {
|
||||
nativeBuildInputs = (base.nativeBuildInputs or []) ++ [
|
||||
pkgs.copyDesktopItems
|
||||
];
|
||||
desktopItems = (base.desktopItems or []) ++ [
|
||||
(pkgs.makeDesktopItem {
|
||||
name = "${cfg.browser.libName}-in-vpn";
|
||||
desktopName = "${cfg.browser.libName} (VPN)";
|
||||
genericName = "Web Browser";
|
||||
# N.B.: --new-instance ensures we don't reuse an existing non-vpn instance.
|
||||
# OTOH, it may error about "only one instance can run at a time": close the non-VPN instance if you see that.
|
||||
exec = "${lib.getExe pkgs.sane-scripts.vpn} do - -- ${cfg.browser.libName} --new-instance";
|
||||
icon = cfg.browser.libName;
|
||||
categories = [ "Network" "WebBrowser" ];
|
||||
type = "Application";
|
||||
})
|
||||
];
|
||||
# de-associate `ctrl+shift+c` from activating the devtools.
|
||||
# based on <https://stackoverflow.com/a/54260938>
|
||||
# TODO: could use `zip -f` to only update the one changed file, instead of rezipping everything.
|
||||
@@ -146,8 +130,7 @@ let
|
||||
echo "omni.ja AFTER:"
|
||||
ls -l $out/lib/${cfg.browser.libName}/browser/omni.ja
|
||||
|
||||
runHook postBuild
|
||||
runHook postInstall
|
||||
# runHook postFixup to allow sane.programs sandbox wrappers to wrap the binaries
|
||||
runHook postFixup
|
||||
'';
|
||||
});
|
||||
@@ -177,7 +160,7 @@ let
|
||||
persistCache = mkOption {
|
||||
description = "optional store name to which persist browser cache";
|
||||
type = types.nullOr types.str;
|
||||
default = "ephemeral";
|
||||
default = "cryptClearOnBoot";
|
||||
};
|
||||
addons = mkOption {
|
||||
type = types.attrsOf addonOpts;
|
||||
@@ -280,7 +263,7 @@ in
|
||||
# TODO: find a way to not expose ~/.ssh to firefox
|
||||
# - unlock sops at login (or before firefox launch)?
|
||||
# - see if ssh has a more formal type of subkey system?
|
||||
# ".ssh/id_ed25519"
|
||||
".ssh/id_ed25519"
|
||||
# ".config/sops"
|
||||
"knowledge/secrets/accounts"
|
||||
];
|
||||
@@ -389,14 +372,14 @@ in
|
||||
if (cfg.persistData != null) then
|
||||
cfg.persistData
|
||||
else
|
||||
"ephemeral"
|
||||
"cryptClearOnBoot"
|
||||
;
|
||||
|
||||
persist.byPath."${cfg.browser.dotDir}/default".store =
|
||||
if (cfg.persistData != null) then
|
||||
cfg.persistData
|
||||
else
|
||||
"ephemeral"
|
||||
"cryptClearOnBoot"
|
||||
;
|
||||
};
|
||||
|
||||
|
@@ -1,9 +0,0 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.free = {
|
||||
packageUnwrapped = pkgs.linkBinIntoOwnPackage pkgs.procps "free";
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.isolatePids = false;
|
||||
};
|
||||
}
|
||||
|
@@ -1,13 +0,0 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.gdb = {
|
||||
sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
|
||||
# sandbox.method = "landlock"; # permission denied when trying to attach, even as root
|
||||
sandbox.autodetectCliPaths = true;
|
||||
fs.".config/gdb/gdbinit".symlink.text = ''
|
||||
# enable commands like `py-bt`, `py-list`, etc.
|
||||
# for usage, see: <https://wiki.python.org/moin/DebuggingWithGdb>
|
||||
source ${pkgs.python3}/share/gdb/libpython.py
|
||||
'';
|
||||
};
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.gdbus = {
|
||||
packageUnwrapped = pkgs.linkBinIntoOwnPackage pkgs.glib "gdbus";
|
||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.glib "bin/gdbus";
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [ "user" ]; #< XXX: maybe future users will also want system access
|
||||
|
@@ -5,10 +5,10 @@
|
||||
# <https://gitlab.gnome.org/GNOME/geary/-/issues/1212>
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs."geary";
|
||||
cfg = config.sane.programs."gnome.geary";
|
||||
in
|
||||
{
|
||||
sane.programs."geary" = {
|
||||
sane.programs."gnome.geary" = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
|
@@ -1,23 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
sane.programs.geoclue-demo-agent = {
|
||||
packageUnwrapped = pkgs.linkFarm "geoclue-demo-agent" [{
|
||||
# bring the demo agent into a `bin/` directory so it can be invokable via PATH
|
||||
name = "bin/geoclue-demo-agent";
|
||||
path = "${config.sane.programs.geoclue2.packageUnwrapped}/libexec/geoclue-2.0/demos/agent";
|
||||
}];
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [
|
||||
"system"
|
||||
];
|
||||
|
||||
services.geoclue-agent = {
|
||||
description = "geoclue 'demo' agent";
|
||||
# XXX: i don't actually understand how this works: upstream dbus rules would appear to restrict
|
||||
# the dbus owner to just root/geoclue, but we're neither and this still works (and breaks if i remove the agent service!)
|
||||
command = "geoclue-demo-agent";
|
||||
partOf = [ "graphical-session" ];
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
# geoclue location services daemon.
|
||||
#
|
||||
# SUPPORT:
|
||||
# - irc: #gnome-maps on irc.gimp.org
|
||||
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
|
||||
# - forums: <https://discourse.gnome.org/c/platform>
|
||||
# - git: <https://gitlab.freedesktop.org/geoclue/geoclue/>
|
||||
# - D-Bus API docs: <https://www.freedesktop.org/software/geoclue/docs/>
|
||||
#
|
||||
# HOW TO TEST:
|
||||
# - just invoke `where-am-i`: it should output the current latitude/longitude.
|
||||
## more manual testing:
|
||||
# - build `geoclue2-with-demo-agent`
|
||||
# - run the service: `systemctl start geoclue` or "${geoclue2-with-demo-agent}/libexec/geoclue"
|
||||
# - run "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/agent"
|
||||
# - keep this running in the background
|
||||
# - run "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/where-am-i"
|
||||
#
|
||||
# DATA FLOW:
|
||||
# - geoclue2 does http calls into local `ols`, which either hits the local disk or queries https://wigle.net.
|
||||
# - geoclue users like gnome-maps somehow depend on an "agent",
|
||||
# a user service which launches the geoclue system service on-demand (via dbus activation).
|
||||
#
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.geoclue2;
|
||||
in
|
||||
{
|
||||
sane.programs.geoclue2 = {
|
||||
# packageUnwrapped = pkgs.rmDbusServices pkgs.geoclue2;
|
||||
# packageUnwrapped = pkgs.geoclue2.override { withDemoAgent = true; };
|
||||
packageUnwrapped = pkgs.geoclue2-with-demo-agent;
|
||||
suggestedPrograms = [
|
||||
"avahi" #< to discover LAN gps devices
|
||||
"geoclue-demo-agent"
|
||||
# "gps-share"
|
||||
"iio-sensor-proxy"
|
||||
"ols" #< WiFi SSID -> lat/long lookups
|
||||
"satellite" #< graphical view into GPS fix data
|
||||
"where-am-i" #< handy debugging/testing tool
|
||||
];
|
||||
|
||||
# XXX(2024/07/05): no way to plumb my sandboxed geoclue into `services.geoclue2`.
|
||||
# then, the package doesn't get used directly anywhere. but other programs reference `packageUnwrapped`,
|
||||
# so keep that part still.
|
||||
sandbox.enable = false;
|
||||
package = lib.mkForce null;
|
||||
|
||||
# experimental sandboxing (2024/07/05)
|
||||
# sandbox.method = "bwrap";
|
||||
# sandbox.whitelistDbus = [
|
||||
# "system"
|
||||
# ];
|
||||
# sandbox.net = "all";
|
||||
};
|
||||
|
||||
# sane.programs.geoclue2.enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
|
||||
|
||||
services.geoclue2 = lib.mkIf cfg.enabled {
|
||||
enable = true;
|
||||
geoProviderUrl = "http://127.0.0.1:8088/v1/geolocate"; #< ols
|
||||
# XXX(2024/06/25): when Geoclue uses ModemManager's GPS API, it wants to enable GPS
|
||||
# tracking at the start, and disable it at the end. that causes tracking to be lost, regularly.
|
||||
# this is not optional behavior: if Geoclue fails to control modem manager (because of a polkit policy, say),
|
||||
# then it won't even try to read the data from modem manager.
|
||||
# SOLUTION: tell Geoclue to get GPS from gps-share ("enableNmea", i.e. `network-nmea.enable`)
|
||||
# and NOT from modem manager.
|
||||
enableModemGPS = false;
|
||||
enableNmea = true;
|
||||
};
|
||||
systemd.user.services = lib.mkIf cfg.enabled {
|
||||
# nixos services.geoclue2 runs the agent as a user service by default, but i don't use systemd so that doesn't work.
|
||||
# i manage the agent myself, in sane.programs.geoclue-demo-agent.
|
||||
geoclue-agent.enable = false;
|
||||
};
|
||||
}
|
@@ -40,7 +40,6 @@ in
|
||||
alias.amend = "commit --amend --no-edit";
|
||||
alias.br = "branch";
|
||||
alias.co = "checkout";
|
||||
alias.com = "commit";
|
||||
alias.cp = "cherry-pick";
|
||||
alias.d = "difftool";
|
||||
alias.dif = "diff"; # common typo
|
||||
|
@@ -1,12 +1,12 @@
|
||||
# gnome feeds RSS viewer
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
{ config, lib, sane-lib, ... }:
|
||||
|
||||
let
|
||||
feeds = sane-lib.feeds;
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||
in {
|
||||
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.target = pkgs.writers.writeJSON "org.gabmus.gfeeds.json" {
|
||||
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.text = builtins.toJSON {
|
||||
# feed format is a map from URL to a dict,
|
||||
# with dict["tags"] a list of string tags.
|
||||
feeds = sane-lib.mapToAttrs (feed: {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
sane.programs.gnome-keyring = {
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome-keyring;
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring;
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
sandbox.extraRuntimePaths = [
|
||||
|
@@ -1,30 +1,7 @@
|
||||
# SUPPORT:
|
||||
# - irc: #gnome-maps on irc.gimp.org
|
||||
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
|
||||
#
|
||||
# INTEGRATIONS:
|
||||
# - uses https://graphhopper.com for routing
|
||||
# - <https://github.com/graphhopper/graphhopper> (not packaged for Nix)
|
||||
# - uses https://tile.openstreetmap.org for tiles
|
||||
# - uses https://overpass-api.de for ... ?
|
||||
# TIPS:
|
||||
# - use "Northwest" instead of "NW", and "Street" instead of "St", etc.
|
||||
# otherwise, it might not find your destination!
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs."gnome.gnome-maps" = {
|
||||
packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.gnome.gnome-maps.overrideAttrs (base: {
|
||||
# default .desktop file is trying to do some dbus launch (?) which fails even *if* i install `gapplication` (glib.bin)
|
||||
postPatch = (base.postPatch or "") + ''
|
||||
substituteInPlace data/org.gnome.Maps.desktop.in.in \
|
||||
--replace-fail 'Exec=gapplication launch @app-id@ %U' 'Exec=gnome-maps %U'
|
||||
'';
|
||||
}));
|
||||
suggestedPrograms = [
|
||||
"geoclue2"
|
||||
];
|
||||
|
||||
sandbox.wrapperType = "inplace"; #< /share directory contains Gir info which references libgnome-maps.so by path
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps;
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDri = true; # for perf
|
||||
sandbox.whitelistDbus = [
|
||||
|
@@ -1,57 +0,0 @@
|
||||
# gps-share: <https://github.com/zeenix/gps-share>
|
||||
# takes a local GPS device (e.g. /dev/ttyUSB1) and makes it available over TCP/Avahi (multicast DNS).
|
||||
#
|
||||
# common usecases:
|
||||
# 1. make positioning available to any device on a network, even if that device has no local GPS
|
||||
# - e.g. my desktop can use my phone's GPS device, if on the same network.
|
||||
# 2. allow multiple clients to share a GPS device.
|
||||
# GPS devices are serial devices, and so only one process can consume the data at a time.
|
||||
# gps-share can camp the serial device, and then allow *multiple* subscribers
|
||||
# 3. provide a *read-only* API to clients like Geoclue.
|
||||
# that is, expose the GPS device *output* to a client, but don't let the client write to the device (e.g. enable/disable the GPS).
|
||||
# this is the primary function i derive from gps-share
|
||||
#
|
||||
# HOW TO TEST:
|
||||
# - `nc localhost 10110`
|
||||
# should stream GPS NMEA output to the console
|
||||
# - `avahi-browse --resolve _nmea-0183._tcp`: should show hosts on the local network which provide GPS info
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.gps-share;
|
||||
in
|
||||
{
|
||||
sane.programs.gps-share = {
|
||||
suggestedPrograms = [
|
||||
"jq"
|
||||
# and systemd, for udevadm
|
||||
];
|
||||
services.gps-share = {
|
||||
description = "gps-share: make local GPS serial readings available over Avahi";
|
||||
# usage:
|
||||
# gps-share --no-announce # to disable Avahi
|
||||
# gps-share --no-tcp # only makes sense if using --socket-path
|
||||
# gps-share --network-interface lo # defaults to all interfaces, but firewalling means actually more restrictive
|
||||
# gps-share --socket-path $XDG_RUNTIME_DIR/gps-share/gps-share.sock # share over a unix socket
|
||||
command = pkgs.writeShellScript "gps-share" ''
|
||||
dev=$(udevadm info --property-match=ID_MM_PORT_TYPE_GPS=1 --json=pretty --export-db | jq -r .DEVNAME)
|
||||
if [ -z "$dev" ]; then
|
||||
echo "no GPS device found"
|
||||
exit 1
|
||||
fi
|
||||
echo "using $dev for GPS NMEA"
|
||||
gps-share "$dev"
|
||||
'';
|
||||
# N.B.: it fails to launch if the NMEA device doesn't yet exist, so don't launch by default; only launch as part of GPS
|
||||
# dependencyOf = [ "geoclue-agent" ];
|
||||
partOf = [ "gps" ];
|
||||
depends = [ "eg25-control-powered" ];
|
||||
};
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "all";
|
||||
sandbox.autodetectCliPaths = "existing"; #< N.B.: `test -f /dev/ttyUSB1` fails, we can't use `existingFile`
|
||||
};
|
||||
|
||||
# TODO: restrict this to just LAN devices!!
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 10110 ];
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user