Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
b129321af9 | |||
a5c36d39f4 | |||
787e6af646 | |||
4fd2db4e51 | |||
ac04dc639f | |||
4a2eb7ebec | |||
b8bf763c11 | |||
b82a31a3ac | |||
c8e5edd61a | |||
1b4a6207e2 |
|
@ -3,7 +3,6 @@ keys:
|
||||||
- &user_lappy_colin age1j2pqnl8j0krdzk6npe93s4nnqrzwx978qrc0u570gzlamqpnje9sc8le2g
|
- &user_lappy_colin age1j2pqnl8j0krdzk6npe93s4nnqrzwx978qrc0u570gzlamqpnje9sc8le2g
|
||||||
- &user_servo_colin age1z8fauff34cdecr6sjkre260luzxcca05kpcwvhx988d306tpcejsp63znu
|
- &user_servo_colin age1z8fauff34cdecr6sjkre260luzxcca05kpcwvhx988d306tpcejsp63znu
|
||||||
- &user_moby_colin age1zsrsvd7j6l62fjxpfd2qnhqlk8wk4p8r0dtxpe4sdgnh2474095qdu7xj9
|
- &user_moby_colin age1zsrsvd7j6l62fjxpfd2qnhqlk8wk4p8r0dtxpe4sdgnh2474095qdu7xj9
|
||||||
- &host_crappy age1hl50ufuxnqy0jnk8fqeu4tclh4vte2xn2d59pxff0gun20vsmv5sp78chj
|
|
||||||
- &host_desko age1vnw7lnfpdpjn62l3u5nyv5xt2c965k96p98kc43mcnyzpetrts9q54mc9v
|
- &host_desko age1vnw7lnfpdpjn62l3u5nyv5xt2c965k96p98kc43mcnyzpetrts9q54mc9v
|
||||||
- &host_lappy age1w7mectcjku6x3sd8plm8wkn2qfrhv9n6zhzlf329e2r2uycgke8qkf9dyn
|
- &host_lappy age1w7mectcjku6x3sd8plm8wkn2qfrhv9n6zhzlf329e2r2uycgke8qkf9dyn
|
||||||
- &host_servo age1tzlyex2z6t88tg9h82943e39shxhmqeyr7ywhlwpdjmyqsndv3qq27x0rf
|
- &host_servo age1tzlyex2z6t88tg9h82943e39shxhmqeyr7ywhlwpdjmyqsndv3qq27x0rf
|
||||||
|
@ -16,7 +15,6 @@ creation_rules:
|
||||||
- *user_lappy_colin
|
- *user_lappy_colin
|
||||||
- *user_servo_colin
|
- *user_servo_colin
|
||||||
- *user_moby_colin
|
- *user_moby_colin
|
||||||
- *host_crappy
|
|
||||||
- *host_desko
|
- *host_desko
|
||||||
- *host_lappy
|
- *host_lappy
|
||||||
- *host_servo
|
- *host_servo
|
||||||
|
|
31
README.md
31
README.md
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
# .❄️≡We|_c0m3 7o m`/ f14k≡❄️.
|
# .❄️≡We|_c0m3 7o m`/ f14k≡❄️.
|
||||||
|
|
||||||
(er, it's not a flake anymore. welcome to my nix files.)
|
|
||||||
|
|
||||||
## What's Here
|
## What's Here
|
||||||
|
|
||||||
this is the top-level repo from which i configure/deploy all my NixOS machines:
|
this is the top-level repo from which i configure/deploy all my NixOS machines:
|
||||||
|
@ -18,12 +16,13 @@ building [hosts/](./hosts/) will require [sops][sops].
|
||||||
|
|
||||||
you might specifically be interested in these files (elaborated further in #key-points-of-interest):
|
you might specifically be interested in these files (elaborated further in #key-points-of-interest):
|
||||||
- ~~[`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)~~
|
- ~~[`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)~~
|
||||||
|
- ~~[example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)~~
|
||||||
- these files will remain until my config settles down, but i no longer use or maintain SXMO.
|
- these files will remain until my config settles down, but i no longer use or maintain SXMO.
|
||||||
- [my implementation of impermanence](./modules/persist/default.nix)
|
- [my implementation of impermanence](./modules/persist/default.nix)
|
||||||
- my way of deploying dotfiles/configuring programs per-user:
|
- my way of deploying dotfiles/configuring programs per-user:
|
||||||
- [modules/fs/](./modules/fs/default.nix)
|
- [modules/fs/](./modules/fs/default.nix)
|
||||||
- [modules/programs/](./modules/programs/default.nix)
|
- [modules/programs/](./modules/programs/default.nix)
|
||||||
- [modules/users/](./modules/users/default.nix)
|
- [modules/users.nix](./modules/users.nix)
|
||||||
|
|
||||||
[nixpkgs]: https://github.com/NixOS/nixpkgs
|
[nixpkgs]: https://github.com/NixOS/nixpkgs
|
||||||
[sops]: https://github.com/Mic92/sops-nix
|
[sops]: https://github.com/Mic92/sops-nix
|
||||||
|
@ -31,7 +30,11 @@ you might specifically be interested in these files (elaborated further in #key-
|
||||||
|
|
||||||
## Using This Repo In Your Own Config
|
## 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/
|
[NUR]: https://nur.nix-community.org/
|
||||||
|
|
||||||
|
@ -39,15 +42,19 @@ follow the instructions [here][NUR] to access my packages through the Nix User R
|
||||||
- `doc/`
|
- `doc/`
|
||||||
- instructions for tasks i find myself doing semi-occasionally in this repo.
|
- instructions for tasks i find myself doing semi-occasionally in this repo.
|
||||||
- `hosts/`
|
- `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,
|
- 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.
|
you won't likely be depending on anything in this directory.
|
||||||
- `integrations/`
|
- `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/`
|
- `modules/`
|
||||||
- config which is gated behind `enable` flags, in similar style to nixpkgs' `nixos/` directory.
|
- config which is gated behind `enable` flags, in similar style to nixpkgs'
|
||||||
- if you depend on this repo for anything besides packages, it's most likely for something in this directory.
|
`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/`
|
- `overlays/`
|
||||||
|
- exposed via the `overlays` output in `flake.nix`.
|
||||||
- predominantly a list of `callPackage` directives.
|
- predominantly a list of `callPackage` directives.
|
||||||
- `pkgs/`
|
- `pkgs/`
|
||||||
- derivations for things not yet packaged in nixpkgs.
|
- derivations for things not yet packaged in nixpkgs.
|
||||||
|
@ -55,12 +62,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
|
- inline code for wholly custom packages (e.g. `pkgs/additional/sane-scripts/` for CLI tools
|
||||||
that are highly specific to my setup).
|
that are highly specific to my setup).
|
||||||
- `scripts/`
|
- `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/`
|
- `secrets/`
|
||||||
- encrypted keys, API tokens, anything which one or more of my machines needs
|
- encrypted keys, API tokens, anything which one or more of my machines needs
|
||||||
read access to but shouldn't be world-readable.
|
read access to but shouldn't be world-readable.
|
||||||
- not much to see here.
|
- not much to see here
|
||||||
- `templates/`
|
- `templates/`
|
||||||
|
- exposed via the `templates` output in `flake.nix`.
|
||||||
- used to instantiate short-lived environments.
|
- used to instantiate short-lived environments.
|
||||||
- used to auto-fill the boiler-plate portions of new packages.
|
- used to auto-fill the boiler-plate portions of new packages.
|
||||||
|
|
||||||
|
@ -101,10 +109,9 @@ i.e. you might find value in using these in your own config:
|
||||||
- `sane.programs.firefox.sandbox.whitelistWayland = true; # allow it to render a wayland window`
|
- `sane.programs.firefox.sandbox.whitelistWayland = true; # allow it to render a wayland window`
|
||||||
- `sane.programs.firefox.sandbox.extraHomePaths = [ "Downloads" ]; # allow it read/write access to ~/Downloads`
|
- `sane.programs.firefox.sandbox.extraHomePaths = [ "Downloads" ]; # allow it read/write access to ~/Downloads`
|
||||||
- integrated with `fs` and `persist` modules so that programs' config files and persisted data stores are linked into the sandbox w/o any extra involvement.
|
- integrated with `fs` and `persist` modules so that programs' config files and persisted data stores are linked into the sandbox w/o any extra involvement.
|
||||||
- `modules/users/`
|
- `modules/users.nix`
|
||||||
- convenience layer atop the above modules so that you can just write
|
- convenience layer atop the above modules so that you can just write
|
||||||
`fs.".config/git"` instead of `fs."/home/colin/.config/git"`
|
`fs.".config/git"` instead of `fs."/home/colin/.config/git"`
|
||||||
- per-user services managed by [s6-rc](https://www.skarnet.org/software/s6-rc/)
|
|
||||||
|
|
||||||
some things in here could easily find broader use. if you would find benefit in
|
some things in here could easily find broader use. if you would find benefit in
|
||||||
them being factored out of my config, message me and we could work to make that happen.
|
them being factored out of my config, message me and we could work to make that happen.
|
||||||
|
|
67
TODO.md
67
TODO.md
|
@ -1,39 +1,9 @@
|
||||||
## BUGS
|
## BUGS
|
||||||
- `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
|
|
||||||
- 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 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: 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
|
|
||||||
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
|
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
|
||||||
- syshud (volume overlay): when casting with `blast`, syshud doesn't react to volume changes
|
- `nix` operations from lappy hang when `desko` is unreachable
|
||||||
- moby: kaslr is effectively disabled
|
- could at least direct the cache to `http://desko-hn:5001`
|
||||||
- `dmesg | grep "KASLR disabled due to lack of seed"`
|
|
||||||
- fix by adding `kaslrseed` to uboot script before `booti`
|
|
||||||
- <https://github.com/armbian/build/pull/4352>
|
|
||||||
- not sure how that's supposed to work with tow-boot; maybe i should just update tow-boot
|
|
||||||
- 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
|
|
||||||
- swaync brightness slider does not work
|
|
||||||
- it reads brightness from /sys/class/backlight/....
|
|
||||||
- but to *set* the brightness it assumes systemd logind is running
|
|
||||||
<repo:ErikReider/SwayNotificationCenter:src/controlCenter/widgets/backlight/backlightUtil.vala>
|
|
||||||
no reason i can't just write to that file, or exec brightnessctl (if i learn vala)
|
|
||||||
|
|
||||||
## REFACTORING:
|
## REFACTORING:
|
||||||
- add import checks to my Python nix-shell scripts
|
|
||||||
- consolidate ~/dev and ~/ref
|
- consolidate ~/dev and ~/ref
|
||||||
- ~/dev becomes a link to ~/ref/cat/mine
|
- ~/dev becomes a link to ~/ref/cat/mine
|
||||||
- fold hosts/common/home/ssh.nix -> hosts/common/users/colin.nix
|
- fold hosts/common/home/ssh.nix -> hosts/common/users/colin.nix
|
||||||
|
@ -50,19 +20,13 @@
|
||||||
|
|
||||||
### upstreaming
|
### upstreaming
|
||||||
- add updateScripts to all my packages in nixpkgs
|
- add updateScripts to all my packages in nixpkgs
|
||||||
|
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
|
||||||
|
|
||||||
#### upstreaming to non-nixpkgs repos
|
#### upstreaming to non-nixpkgs repos
|
||||||
- gtk: build schemas even on cross compilation: <https://github.com/NixOS/nixpkgs/pull/247844>
|
- gtk: build schemas even on cross compilation: <https://github.com/NixOS/nixpkgs/pull/247844>
|
||||||
|
|
||||||
|
|
||||||
## IMPROVEMENTS:
|
## IMPROVEMENTS:
|
||||||
- systemd/journalctl: use a less shit pager
|
|
||||||
- there's an env var for it: SYSTEMD_PAGER? and a flag for journalctl
|
|
||||||
- kernels: ship the same kernel on every machine
|
|
||||||
- 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)
|
|
||||||
|
|
||||||
### security/resilience
|
### security/resilience
|
||||||
- validate duplicity backups!
|
- validate duplicity backups!
|
||||||
- encrypt more ~ dirs (~/archives, ~/records, ..?)
|
- encrypt more ~ dirs (~/archives, ~/records, ..?)
|
||||||
|
@ -80,25 +44,22 @@
|
||||||
- <https://github.com/flatpak/xdg-dbus-proxy>
|
- <https://github.com/flatpak/xdg-dbus-proxy>
|
||||||
- remove `.ssh` access from Firefox!
|
- 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
|
- 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?)
|
- port sane-sandboxed 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.
|
- it adds like 50-70ms launch time _on my laptop_. i'd hate to know how much that is on the pinephone.
|
||||||
|
- remove /run/wrappers from the sandbox path
|
||||||
|
- they're mostly useless when using no-new-privs, just an opportunity to forget to specify deps
|
||||||
- make dconf stuff less monolithic
|
- make dconf stuff less monolithic
|
||||||
- i.e. per-app dconf profiles for those which need it. possible static config.
|
- 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
|
- canaries for important services
|
||||||
- e.g. daily email checks; daily backup checks
|
- e.g. daily email checks; daily backup checks
|
||||||
- integrate `nix check` into Gitea actions?
|
- integrate `nix check` into Gitea actions?
|
||||||
|
|
||||||
### user experience
|
### user experience
|
||||||
- rofi: sort items case-insensitively
|
|
||||||
- xdg-desktop-portal shouldn't kill children on exit
|
- xdg-desktop-portal shouldn't kill children on exit
|
||||||
- *maybe* a job for `setsid -f`?
|
- *maybe* a job for `setsid -f`?
|
||||||
- replace starship prompt with something more efficient
|
- replace starship prompt with something more efficient
|
||||||
- watch `forkstat`: it does way too much
|
- watch `forkstat`: it does way too much
|
||||||
- cleanup waybar/nwg-panel so that it's not invoking playerctl every 2 seconds
|
- cleanup waybar so that it's not invoking playerctl every 2 seconds
|
||||||
- nwg-panel: swaync icon is stuck as the refresh icon
|
|
||||||
- nwg-panel: doesn't appear on all desktops
|
|
||||||
- nwg-panel: doesn't know that virtual-desktop 10/TV exists
|
|
||||||
- install apps:
|
- install apps:
|
||||||
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
|
- 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/>
|
- shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
|
||||||
|
@ -106,9 +67,8 @@
|
||||||
- offline docs viewer (gtk): <https://github.com/workbenchdev/Biblioteca>
|
- offline docs viewer (gtk): <https://github.com/workbenchdev/Biblioteca>
|
||||||
- some type of games manager/launcher
|
- some type of games manager/launcher
|
||||||
- Gnome Highscore (retro games)?: <https://gitlab.gnome.org/World/highscore>
|
- 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)? Gnome Maps is improved in 45)
|
||||||
- note-taking app: <https://linuxphoneapps.org/categories/note-taking/>
|
- 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
|
- OSK overlay specifically for mobile gaming
|
||||||
- i.e. mock joysticks, for use with SuperTux and SuperTuxKart
|
- i.e. mock joysticks, for use with SuperTux and SuperTuxKart
|
||||||
- install mobile-friendly games:
|
- install mobile-friendly games:
|
||||||
|
@ -119,8 +79,6 @@
|
||||||
- numberlink (generic name for Flow Free). not packaged in Nix
|
- numberlink (generic name for Flow Free). not packaged in Nix
|
||||||
- Neverball (https://neverball.org/screenshots.php). nix: as `neverball`
|
- Neverball (https://neverball.org/screenshots.php). nix: as `neverball`
|
||||||
- blurble (https://linuxphoneapps.org/games/app.drey.blurble/). nix: not as of 2024-02-05
|
- blurble (https://linuxphoneapps.org/games/app.drey.blurble/). nix: not as of 2024-02-05
|
||||||
- Trivia Quiz (https://linuxphoneapps.org/games/io.github.nokse22.trivia-quiz/)
|
|
||||||
- sane-sync-music: remove empty dirs
|
|
||||||
|
|
||||||
#### moby
|
#### moby
|
||||||
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
|
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
|
||||||
|
@ -131,10 +89,11 @@
|
||||||
- add option to change audio output
|
- add option to change audio output
|
||||||
- fix colors (red alert) to match overall theme
|
- fix colors (red alert) to match overall theme
|
||||||
- moby: tune GPS
|
- moby: tune GPS
|
||||||
|
- run only geoclue, and not gpsd, to save power?
|
||||||
- tune QGPS setting in eg25-control, for less jitter?
|
- 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?
|
- 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: show battery state on ssh login
|
||||||
- moby: improve gPodder launch time
|
- moby: improve gPodder launch time
|
||||||
- moby: theme GTK apps (i.e. non-adwaita styles)
|
- moby: theme GTK apps (i.e. non-adwaita styles)
|
||||||
|
@ -163,10 +122,6 @@
|
||||||
|
|
||||||
### perf
|
### perf
|
||||||
- debug nixos-rebuild times
|
- 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
|
- 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
|
- 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!
|
- would be super handy for package prototyping!
|
||||||
|
|
14
default.nix
14
default.nix
|
@ -1,5 +1,9 @@
|
||||||
{ ... }@args:
|
# limited, non-flake interface to this repo.
|
||||||
let
|
# this file exposes the same view into `pkgs` which the flake would see when evaluated.
|
||||||
sane-nix-files = import ./pkgs/additional/sane-nix-files { };
|
#
|
||||||
in
|
# the primary purpose of this file is so i can run `updateScript`s which expect
|
||||||
import "${sane-nix-files}/impure.nix" args
|
# the root to be `default.nix`
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
pkgs.appendOverlays [
|
||||||
|
(import ./overlays/all.nix)
|
||||||
|
]
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
to add a host:
|
|
||||||
- create the new nix targets
|
|
||||||
- hosts/by-name/HOST
|
|
||||||
- let the toplevel (flake.nix) know about HOST
|
|
||||||
- build and flash an image
|
|
||||||
- optionally expand the rootfs
|
|
||||||
- `cfdisk /dev/sda2` -> resize partition
|
|
||||||
- `mount /dev/sda2 boot`
|
|
||||||
- `btrfs filesystem resize max root`
|
|
||||||
- setup required persistent directories
|
|
||||||
- `mkdir -p root/persist/private`
|
|
||||||
- `gocryptfs -init root/persist/private`
|
|
||||||
- then boot the device, and for every dangling symlink in ~/.local/share, ~/.cache, do `mkdir -p` on it
|
|
||||||
- setup host ssh
|
|
||||||
- `mkdir -p root/persist/plaintext/etc/ssh/host_keys`
|
|
||||||
- boot the machine and let it create its own ssh keys
|
|
||||||
- add the pubkey to `hosts/common/hosts.nix`
|
|
||||||
- setup user ssh
|
|
||||||
- `ssh-keygen`. don't enter any password; it's stored in a password-encrypted fs.
|
|
||||||
- add the pubkey to `hosts/common/hosts.nix`
|
|
||||||
- allow the new host to view secrets
|
|
||||||
- instructions in hosts/common/secrets.nix
|
|
||||||
- run `ssh-to-age` on user/host pubkeys
|
|
||||||
- add age key to .sops.yaml
|
|
||||||
- update encrypted secrets: `sops updatekeys path/to/secret.yaml`
|
|
|
@ -1,12 +0,0 @@
|
||||||
## deploying to SD card
|
|
||||||
- build a toplevel config: `nix build '.#hostSystems.moby'`
|
|
||||||
- mount a system:
|
|
||||||
- `mkdir -p root/{nix,boot}`
|
|
||||||
- `mount /dev/sdX1 root/boot`
|
|
||||||
- `mount /dev/sdX2 root/nix`
|
|
||||||
- copy the config:
|
|
||||||
- `sudo nix copy --no-check-sigs --to root/ $(readlink result)`
|
|
||||||
- nix will copy stuff to `root/nix/store`
|
|
||||||
- install the boot files:
|
|
||||||
- `sudo /nix/store/sbwpwngjlgw4f736ay9hgi69pj3fdwk5-extlinux-conf-builder.sh -d ./root/boot -t 5 -c $(readlink ./result)`
|
|
||||||
- extlinux-conf-builder can be found in `/run/current-system/bin/switch-to-configuration`
|
|
330
flake.lock
Normal file
330
flake.lock
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-compat": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1688025799,
|
||||||
|
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"nixpkgs-wayland",
|
||||||
|
"nix-eval-jobs",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1701473968,
|
||||||
|
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lib-aggregate": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710184940,
|
||||||
|
"narHash": "sha256-FzYm4td3FJfzOAuEkCXt3KdUgZuA072OAQXqIq+IAMo=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "lib-aggregate",
|
||||||
|
"rev": "45b75bf534592c0c1c881a1c447f7fdb37a87eaf",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "lib-aggregate",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mobile-nixos": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694749521,
|
||||||
|
"narHash": "sha256-MiVokKlpcJmfoGuWAMeW1En7gZ5hk0rCQArYm6P9XCc=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "mobile-nixos",
|
||||||
|
"rev": "d25d3b87e7f300d8066e31d792337d9cd7ecd23b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "d25d3b87e7f300d8066e31d792337d9cd7ecd23b",
|
||||||
|
"repo": "mobile-nixos",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-eval-jobs": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nix-github-actions": "nix-github-actions",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1705242886,
|
||||||
|
"narHash": "sha256-TLj334vRwFtSym3m+NnKcNCnKKPNoTC/TDZL40vmOso=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-eval-jobs",
|
||||||
|
"rev": "6b03a93296faf174b97546fd573c8b379f523a8d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-eval-jobs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-github-actions": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-wayland",
|
||||||
|
"nix-eval-jobs",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1701208414,
|
||||||
|
"narHash": "sha256-xrQ0FyhwTZK6BwKhahIkUVZhMNk21IEI1nUcWSONtpo=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"rev": "93e39cc1a087d65bcf7a132e75a650c44dd2b734",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703134684,
|
||||||
|
"narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "d6863cbcbbb80e71cecfc03356db1cda38919523",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710031547,
|
||||||
|
"narHash": "sha256-pkUg3hOKuGWMGF9WEMPPN/G4pqqdbNGJQ54yhyQYDVY=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "630ebdc047ca96d8126e16bb664c7730dc52f6e6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-next-unpatched": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710337169,
|
||||||
|
"narHash": "sha256-u2/74bhQuWykUZDWUIhHd6IpZiaQ0hSpTBbx0y9opkE=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4ee0840ba2ecc50458ab1677d108afcd691f4815",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "staging-next",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710033658,
|
||||||
|
"narHash": "sha256-yiZiVKP5Ya813iYLho2+CcFuuHpaqKc/CoxOlANKcqM=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b17375d3bb7c79ffc52f3538028b2ec06eb79ef8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "release-23.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-unpatched": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710339354,
|
||||||
|
"narHash": "sha256-+P5ccUPiLouHexb8aJrUOVOIja9qm+fG57pgAu7uIRs=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "2dbc8f62d8af7a1ab962e4b20d12b25ddcb86ced",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-wayland": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"lib-aggregate": "lib-aggregate",
|
||||||
|
"nix-eval-jobs": "nix-eval-jobs",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-unpatched"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710317949,
|
||||||
|
"narHash": "sha256-bwReMiWPA2wYBvKEMhO8pJcu+o+7ocy5hGkSoawTHu0=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs-wayland",
|
||||||
|
"rev": "771cb198c281db6918829651f194bf4db32e342d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs-wayland",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"mobile-nixos": "mobile-nixos",
|
||||||
|
"nixpkgs-next-unpatched": "nixpkgs-next-unpatched",
|
||||||
|
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||||
|
"nixpkgs-wayland": "nixpkgs-wayland",
|
||||||
|
"sops-nix": "sops-nix",
|
||||||
|
"uninsane-dot-org": "uninsane-dot-org"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sops-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-unpatched"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710195194,
|
||||||
|
"narHash": "sha256-KFxCJp0T6TJOz1IOKlpRdpsCr9xsvlVuWY/VCiAFnTE=",
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"rev": "e52d8117b330f690382f1d16d81ae43daeb4b880",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-wayland",
|
||||||
|
"nix-eval-jobs",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1702979157,
|
||||||
|
"narHash": "sha256-RnFBbLbpqtn4AoJGXKevQMCGhra4h6G2MPcuTSZZQ+g=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "2961375283668d867e64129c22af532de8e77734",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uninsane-dot-org": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-unpatched"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1707981105,
|
||||||
|
"narHash": "sha256-YCU1eNslBHabjP+OCY+BxPycEFO9SRUts10MrN9QORE=",
|
||||||
|
"ref": "refs/heads/master",
|
||||||
|
"rev": "bb10cd8853d05191e4d62947d93687c462e92c30",
|
||||||
|
"revCount": 235,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.uninsane.org/colin/uninsane"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.uninsane.org/colin/uninsane"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
644
flake.nix
Normal file
644
flake.nix
Normal file
|
@ -0,0 +1,644 @@
|
||||||
|
# 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`
|
||||||
|
|
||||||
|
{
|
||||||
|
# XXX: use the `github:` scheme instead of the more readable git+https: because it's *way* more efficient
|
||||||
|
# preferably, i would rewrite the human-readable https URLs to nix-specific github: URLs with a helper,
|
||||||
|
# but `inputs` is required to be a strict attrset: not an expression.
|
||||||
|
inputs = {
|
||||||
|
# branch workflow:
|
||||||
|
# - daily:
|
||||||
|
# - nixos-unstable cut from master after enough packages have been built in caches.
|
||||||
|
# - every 6 hours:
|
||||||
|
# - master auto-merged into staging and staging-next
|
||||||
|
# - staging-next auto-merged into staging.
|
||||||
|
# - manually, approximately once per month:
|
||||||
|
# - staging-next is cut from staging.
|
||||||
|
# - staging-next merged into master.
|
||||||
|
#
|
||||||
|
# which branch to source from?
|
||||||
|
# - nixos-unstable: for everyday development; it provides good caching
|
||||||
|
# - master: temporarily if i'm otherwise cherry-picking lots of already-applied patches
|
||||||
|
# - staging-next: if testing stuff that's been PR'd into staging, i.e. base library updates.
|
||||||
|
# - staging: maybe if no staging-next -> master PR has been cut yet?
|
||||||
|
#
|
||||||
|
# <https://github.com/nixos/nixpkgs/tree/nixos-unstable>
|
||||||
|
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
|
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=master";
|
||||||
|
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-staging";
|
||||||
|
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-staging-next";
|
||||||
|
nixpkgs-next-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
|
||||||
|
|
||||||
|
nixpkgs-wayland = {
|
||||||
|
url = "github:nix-community/nixpkgs-wayland";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
|
};
|
||||||
|
|
||||||
|
mobile-nixos = {
|
||||||
|
# <https://github.com/nixos/mobile-nixos>
|
||||||
|
# only used for building disk images, not relevant after deployment
|
||||||
|
# TODO: replace with something else. commit `0f3ac0bef1aea70254a3bae35e3cc2561623f4c1`
|
||||||
|
# replaces the imageBuilder with a "new implementation from celun" and wildly breaks my use.
|
||||||
|
# pinning to d25d3b... is equivalent to holding at 2023-09-15
|
||||||
|
url = "github:nixos/mobile-nixos?ref=d25d3b87e7f300d8066e31d792337d9cd7ecd23b";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
sops-nix = {
|
||||||
|
# <https://github.com/Mic92/sops-nix>
|
||||||
|
# used to distribute secrets to my hosts
|
||||||
|
url = "github:Mic92/sops-nix";
|
||||||
|
# inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
|
};
|
||||||
|
uninsane-dot-org = {
|
||||||
|
# provides the package to deploy <https://uninsane.org>, used only when building the servo host
|
||||||
|
url = "git+https://git.uninsane.org/colin/uninsane";
|
||||||
|
# inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs-unpatched,
|
||||||
|
nixpkgs-next-unpatched ? nixpkgs-unpatched,
|
||||||
|
nixpkgs-wayland,
|
||||||
|
mobile-nixos,
|
||||||
|
sops-nix,
|
||||||
|
uninsane-dot-org,
|
||||||
|
...
|
||||||
|
}@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);
|
||||||
|
|
||||||
|
# rather than apply our nixpkgs patches as a flake input, do that here instead.
|
||||||
|
# this (temporarily?) resolves the bad UX wherein a subflake residing in the same git
|
||||||
|
# repo as the main flake causes the main flake to have an unstable hash.
|
||||||
|
patchNixpkgs = variant: nixpkgs: (import ./nixpatches/flake.nix).outputs {
|
||||||
|
inherit variant nixpkgs;
|
||||||
|
self = patchNixpkgs variant nixpkgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
nixpkgs' = patchNixpkgs "master" nixpkgs-unpatched;
|
||||||
|
nixpkgsCompiledBy = system: nixpkgs'.legacyPackages."${system}";
|
||||||
|
|
||||||
|
evalHost = { name, local, target, light ? false, nixpkgs ? nixpkgs' }: nixpkgs.lib.nixosSystem {
|
||||||
|
system = target;
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
nixpkgs.buildPlatform.system = local;
|
||||||
|
# nixpkgs.config.replaceStdenv = { pkgs }: pkgs.ccacheStdenv;
|
||||||
|
}
|
||||||
|
(optionalAttrs (local != target) {
|
||||||
|
# XXX(2023/12/11): cache.nixos.org uses `system = ...` instead of `hostPlatform.system`, and that choice impacts the closure of every package.
|
||||||
|
# so avoid specifying hostPlatform.system on non-cross builds, so i can use upstream caches.
|
||||||
|
nixpkgs.hostPlatform.system = target;
|
||||||
|
})
|
||||||
|
(optionalAttrs light {
|
||||||
|
sane.enableSlowPrograms = false;
|
||||||
|
})
|
||||||
|
(import ./hosts/instantiate.nix { hostName = name; })
|
||||||
|
self.nixosModules.default
|
||||||
|
self.nixosModules.passthru
|
||||||
|
{
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
self.overlays.passthru
|
||||||
|
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"; light = true; };
|
||||||
|
lappy = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||||
|
lappy-light = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; light = true; };
|
||||||
|
moby = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
||||||
|
moby-light = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; light = true; };
|
||||||
|
rescue = { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||||
|
};
|
||||||
|
hostsNext = mapAttrs' (h: v: {
|
||||||
|
name = "${h}-next";
|
||||||
|
value = v // { nixpkgs = patchNixpkgs "staging-next" nixpkgs-next-unpatched; };
|
||||||
|
}) hosts;
|
||||||
|
in mapAttrValues evalHost (
|
||||||
|
hosts // hostsNext
|
||||||
|
);
|
||||||
|
|
||||||
|
# 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;
|
||||||
|
|
||||||
|
patched.nixpkgs = nixpkgs';
|
||||||
|
|
||||||
|
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;
|
||||||
|
pins = final: prev: import ./overlays/pins.nix final prev;
|
||||||
|
preferences = final: prev: import ./overlays/preferences.nix final prev;
|
||||||
|
passthru = final: prev:
|
||||||
|
let
|
||||||
|
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||||
|
uninsane = uninsane-dot-org.overlays.default;
|
||||||
|
wayland = final: prev: {
|
||||||
|
# default is to dump the packages into `waylandPkgs` *and* the toplevel.
|
||||||
|
# but i just want the `waylandPkgs` set
|
||||||
|
inherit (nixpkgs-wayland.overlays.default final prev)
|
||||||
|
waylandPkgs
|
||||||
|
new-wayland-protocols #< 2024/03/10: nixpkgs-wayland assumes this will be in the toplevel
|
||||||
|
;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
(mobile final prev)
|
||||||
|
// (uninsane final prev)
|
||||||
|
// (wayland final prev)
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
nixosModules = rec {
|
||||||
|
default = sane;
|
||||||
|
sane = import ./modules;
|
||||||
|
passthru = { ... }: {
|
||||||
|
imports = [
|
||||||
|
sops-nix.nixosModules.sops
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# this includes both our native packages and all the nixpkgs packages.
|
||||||
|
legacyPackages =
|
||||||
|
let
|
||||||
|
allPkgsFor = sys: (nixpkgsCompiledBy sys).appendOverlays [
|
||||||
|
self.overlays.passthru self.overlays.pkgs
|
||||||
|
];
|
||||||
|
in {
|
||||||
|
x86_64-linux = allPkgsFor "x86_64-linux";
|
||||||
|
aarch64-linux = allPkgsFor "aarch64-linux";
|
||||||
|
};
|
||||||
|
|
||||||
|
# extract only our own packages from the full set.
|
||||||
|
# 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)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
# expose sane packages and chosen inputs (uninsane.org)
|
||||||
|
(import ./pkgs { pkgs = passthruPkgs; }) // {
|
||||||
|
inherit (passthruPkgs) uninsane-dot-org;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# self.legacyPackages;
|
||||||
|
{
|
||||||
|
x86_64-linux = (nixpkgsCompiledBy "x86_64-linux").appendOverlays [
|
||||||
|
self.overlays.passthru
|
||||||
|
];
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
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_serve_privkey "$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}" ''
|
||||||
|
export UPDATE_NIX_NAME=${pkg.name}
|
||||||
|
export UPDATE_NIX_PNAME=${pkg.pname}
|
||||||
|
export UPDATE_NIX_OLD_VERSION=${pkg.version}
|
||||||
|
export 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 = {
|
||||||
|
desko = deployApp "desko" "desko" "switch";
|
||||||
|
desko-light = deployApp "desko-light" "desko" "switch";
|
||||||
|
lappy = deployApp "lappy" "lappy" "switch";
|
||||||
|
lappy-light = deployApp "lappy-light" "lappy" "switch";
|
||||||
|
moby = deployApp "moby" "moby" "switch";
|
||||||
|
moby-light = deployApp "moby-light" "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";
|
||||||
|
|
||||||
|
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'
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
preDeploy = {
|
||||||
|
# build the host and copy the runtime closure to that host, but don't activate it.
|
||||||
|
desko = deployApp "desko" "desko" null;
|
||||||
|
desko-light = deployApp "desko-light" "desko" null;
|
||||||
|
lappy = deployApp "lappy" "lappy" null;
|
||||||
|
lappy-light = deployApp "lappy-light" "lappy" null;
|
||||||
|
moby = deployApp "moby" "moby" null;
|
||||||
|
moby-light = deployApp "moby-light" "moby" null;
|
||||||
|
servo = deployApp "servo" "servo" null;
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript "predeploy-all" ''
|
||||||
|
# copy the -light variants first; this might be run while waiting on a full build. or the full build failed.
|
||||||
|
nix run '.#preDeploy.moby-light' -- "$@"
|
||||||
|
nix run '.#preDeploy.lappy-light' -- "$@"
|
||||||
|
nix run '.#preDeploy.desko-light' -- "$@"
|
||||||
|
nix run '.#preDeploy.lappy' -- "$@"
|
||||||
|
nix run '.#preDeploy.servo' -- "$@"
|
||||||
|
nix run '.#preDeploy.moby' -- "$@"
|
||||||
|
nix run '.#preDeploy.desko' -- "$@"
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
${pkgs.rsync}/bin/rsync -arv --exclude servo-macros /mnt/moby/home/Pictures/ /mnt/desko/home/Pictures/moby/
|
||||||
|
# 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=${nixpkgs-unpatched} \
|
||||||
|
-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 "desko-light"}
|
||||||
|
${checkHost "moby-light"}
|
||||||
|
${checkHost "lappy-light"}
|
||||||
|
|
||||||
|
${checkHost "desko"}
|
||||||
|
${checkHost "lappy"}
|
||||||
|
${checkHost "servo"}
|
||||||
|
${checkHost "moby"}
|
||||||
|
${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 "desko-next"}
|
||||||
|
${checkHost "lappy-next"}
|
||||||
|
${checkHost "servo-next"}
|
||||||
|
${checkHost "moby-next"}
|
||||||
|
${checkHost "rescue-next"}
|
||||||
|
|
||||||
|
echo "desko: $RC_desko"
|
||||||
|
echo "lappy: $RC_lappy"
|
||||||
|
echo "servo: $RC_servo"
|
||||||
|
echo "moby: $RC_moby"
|
||||||
|
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 "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_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.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";
|
||||||
|
};
|
||||||
|
pkgs.make = {
|
||||||
|
# initialize with:
|
||||||
|
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.make'`
|
||||||
|
path = ./templates/pkgs/make;
|
||||||
|
description = "default Makefile-based derivation";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
# Samsung chromebook XE303C12
|
|
||||||
# - <https://wiki.postmarketos.org/wiki/Samsung_Chromebook_(google-snow)>
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./fs.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
sane.hal.samsung.enable = true;
|
|
||||||
sane.roles.client = true;
|
|
||||||
# sane.roles.pc = true;
|
|
||||||
|
|
||||||
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."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!
|
|
||||||
# sane.programs.brave.enableFor.user.colin = false; # 2024/06/03: fails eval if enabled on cross
|
|
||||||
# sane.programs.firefox.enableFor.user.colin = false; # 2024/06/03: this triggers an eval error in yarn stuff -- i'm doing IFD somewhere!!?
|
|
||||||
sane.programs.mepo.enableFor.user.colin = false; # 2024/06/04: doesn't cross compile (nodejs)
|
|
||||||
sane.programs.mercurial.enableFor.user.colin = false; # 2024/06/03: does not cross compile
|
|
||||||
sane.programs.nixpkgs-review.enableFor.user.colin = false; # 2024/06/03: OOMs when cross compiling
|
|
||||||
sane.programs.ntfy-sh.enableFor.user.colin = false; # 2024/06/04: doesn't cross compile (nodejs)
|
|
||||||
sane.programs.pwvucontrol.enableFor.user.colin = false; # 2024/06/03: doesn't cross compile (libspa-sys)
|
|
||||||
sane.programs."sane-scripts.bt-search".enableFor.user.colin = false; # 2024/06/03: does not cross compile
|
|
||||||
sane.programs.sequoia.enableFor.user.colin = false; # 2024/06/03: does not cross compile
|
|
||||||
sane.programs.zathura.enableFor.user.colin = false; # 2024/06/03: does not cross compile
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
fileSystems."/nix" = {
|
|
||||||
device = "/dev/disk/by-uuid/55555555-0303-0c12-86df-eda9e9311526";
|
|
||||||
fsType = "btrfs";
|
|
||||||
options = [
|
|
||||||
"compress=zstd"
|
|
||||||
"defaults"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/boot" = {
|
|
||||||
device = "/dev/disk/by-uuid/303C-5A37";
|
|
||||||
fsType = "vfat";
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,50 +1,52 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./fs.nix
|
./fs.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.services.trust-dns.asSystemResolver = false; # TEMPORARY: TODO: re-enable trust-dns
|
|
||||||
# sane.programs.devPkgs.enableFor.user.colin = true;
|
|
||||||
# sane.guest.enable = true;
|
# sane.guest.enable = true;
|
||||||
|
|
||||||
# don't enable wifi by default: it messes with connectivity.
|
# services.distccd.enable = true;
|
||||||
# systemd.services.iwd.enable = false;
|
# sane.programs.distcc.enableFor.user.guest = true;
|
||||||
# systemd.services.wpa_supplicant.enable = false;
|
|
||||||
sane.programs.wpa_supplicant.enableFor.user.colin = lib.mkForce false;
|
# TODO: remove emulation, but need to fix nixos-rebuild to moby for that.
|
||||||
sane.programs.wpa_supplicant.enableFor.system = lib.mkForce false;
|
# sane.roles.build-machine.emulation = true;
|
||||||
|
|
||||||
sops.secrets.colin-passwd.neededForUsers = true;
|
sops.secrets.colin-passwd.neededForUsers = true;
|
||||||
|
|
||||||
|
sane.ports.openFirewall = true; # for e.g. nix-serve
|
||||||
|
|
||||||
sane.roles.build-machine.enable = true;
|
sane.roles.build-machine.enable = true;
|
||||||
sane.roles.client = true;
|
sane.roles.client = true;
|
||||||
sane.roles.dev-machine = true;
|
sane.roles.dev-machine = true;
|
||||||
sane.roles.pc = true;
|
sane.roles.pc = true;
|
||||||
sane.services.wg-home.enable = true;
|
sane.services.wg-home.enable = true;
|
||||||
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
|
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
|
||||||
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.duplicity.enable = true;
|
||||||
|
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
|
||||||
|
|
||||||
|
sane.nixcache.substituters.desko = false;
|
||||||
sane.nixcache.remote-builders.desko = false;
|
sane.nixcache.remote-builders.desko = false;
|
||||||
|
|
||||||
sane.programs.sway.enableFor.user.colin = true;
|
sane.programs.sway.enableFor.user.colin = true;
|
||||||
sane.programs.iphoneUtils.enableFor.user.colin = true;
|
sane.programs.iphoneUtils.enableFor.user.colin = true;
|
||||||
sane.programs.steam.enableFor.user.colin = true;
|
sane.programs.steam.enableFor.user.colin = true;
|
||||||
|
|
||||||
|
# sane.programs.devPkgs.enableFor.user.colin = true;
|
||||||
|
|
||||||
sane.programs."gnome.geary".config.autostart = true;
|
sane.programs."gnome.geary".config.autostart = true;
|
||||||
sane.programs.signal-desktop.config.autostart = true;
|
sane.programs.signal-desktop.config.autostart = true;
|
||||||
|
|
||||||
sane.programs.nwg-panel.config = {
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
battery = false;
|
|
||||||
brightness = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
# needed to use libimobiledevice/ifuse, for iphone sync
|
# needed to use libimobiledevice/ifuse, for iphone sync
|
||||||
services.usbmuxd.enable = true;
|
services.usbmuxd.enable = true;
|
||||||
|
|
||||||
|
# don't enable wifi by default: it messes with connectivity.
|
||||||
|
systemd.services.iwd.enable = false;
|
||||||
|
systemd.services.wpa_supplicant.enable = false;
|
||||||
|
|
||||||
# default config: https://man.archlinux.org/man/snapper-configs.5
|
# default config: https://man.archlinux.org/man/snapper-configs.5
|
||||||
# defaults to something like:
|
# defaults to something like:
|
||||||
# - hourly snapshots
|
# - hourly snapshots
|
||||||
|
@ -56,4 +58,7 @@
|
||||||
# TODO: ALLOW_USERS doesn't seem to work. still need `sudo snapper -c nix list`
|
# TODO: ALLOW_USERS doesn't seem to work. still need `sudo snapper -c nix list`
|
||||||
ALLOW_USERS = [ "colin" ];
|
ALLOW_USERS = [ "colin" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
||||||
|
system.stateVersion = "21.05";
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,15 @@
|
||||||
sane.roles.pc = true;
|
sane.roles.pc = true;
|
||||||
sane.services.wg-home.enable = true;
|
sane.services.wg-home.enable = true;
|
||||||
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
|
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
|
||||||
sane.ovpn.addrV4 = "172.23.119.72";
|
|
||||||
# sane.ovpn.addrV6 = "fd00:0000:1337:cafe:1111:1111:0332:aa96/128";
|
|
||||||
|
|
||||||
# sane.guest.enable = true;
|
# sane.guest.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
sane.programs.stepmania.enableFor.user.colin = true;
|
|
||||||
sane.programs.sway.enableFor.user.colin = true;
|
sane.programs.sway.enableFor.user.colin = true;
|
||||||
|
|
||||||
sane.programs."gnome.geary".config.autostart = true;
|
sane.programs."gnome.geary".config.autostart = true;
|
||||||
sane.programs.signal-desktop.config.autostart = true;
|
sane.programs.signal-desktop.config.autostart = true;
|
||||||
|
sane.programs.stepmania.enableFor.user.colin = true;
|
||||||
|
|
||||||
sops.secrets.colin-passwd.neededForUsers = true;
|
sops.secrets.colin-passwd.neededForUsers = true;
|
||||||
|
|
||||||
|
@ -33,4 +31,7 @@
|
||||||
SUBVOLUME = "/nix";
|
SUBVOLUME = "/nix";
|
||||||
ALLOW_USERS = [ "colin" ];
|
ALLOW_USERS = [ "colin" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
||||||
|
system.stateVersion = "21.05";
|
||||||
}
|
}
|
||||||
|
|
7
hosts/by-name/lappy/xkb_mobile_normal_buttons
Normal file
7
hosts/by-name/lappy/xkb_mobile_normal_buttons
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
xkb_keymap {
|
||||||
|
xkb_keycodes { include "evdev+aliases(qwerty)" };
|
||||||
|
xkb_types { include "complete" };
|
||||||
|
xkb_compat { include "complete" };
|
||||||
|
xkb_symbols { include "pc+us+inet(evdev)" };
|
||||||
|
xkb_geometry { include "pc(pc105)" };
|
||||||
|
};
|
12
hosts/by-name/moby/bootloader.nix
Normal file
12
hosts/by-name/moby/bootloader.nix
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
# we need space in the GPT header to place tow-boot.
|
||||||
|
# only actually need 1 MB, but better to over-allocate than under-allocate
|
||||||
|
sane.image.extraGPTPadding = 16 * 1024 * 1024;
|
||||||
|
sane.image.firstPartGap = 0;
|
||||||
|
system.build.img = pkgs.runCommand "nixos_full-disk-image.img" {} ''
|
||||||
|
cp -v ${config.system.build.img-without-firmware}/nixos.img $out
|
||||||
|
chmod +w $out
|
||||||
|
dd if=${pkgs.tow-boot-pinephone}/Tow-Boot.noenv.bin of=$out bs=1024 seek=8 conv=notrunc
|
||||||
|
'';
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
# Pinephone
|
# Pinephone
|
||||||
|
# other setups to reference:
|
||||||
|
# - <https://hamblingreen.gitlab.io/2022/03/02/my-pinephone-setup.html>
|
||||||
|
# - sxmo Arch user. lots of app recommendations
|
||||||
#
|
#
|
||||||
# wikis, resources, ...:
|
# wikis, resources, ...:
|
||||||
# - Linux Phone Apps: <https://linuxphoneapps.org/>
|
# - Linux Phone Apps: <https://linuxphoneapps.org/>
|
||||||
|
@ -9,16 +12,22 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./bootloader.nix
|
||||||
./fs.nix
|
./fs.nix
|
||||||
|
./gps.nix
|
||||||
|
./kernel.nix
|
||||||
|
./polyfill.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.hal.pine64.enable = true;
|
|
||||||
sane.roles.client = true;
|
sane.roles.client = true;
|
||||||
sane.roles.handheld = true;
|
sane.roles.handheld = true;
|
||||||
|
sane.zsh.showDeadlines = false; # unlikely to act on them when in shell
|
||||||
sane.services.wg-home.enable = true;
|
sane.services.wg-home.enable = true;
|
||||||
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
|
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
|
||||||
sane.ovpn.addrV4 = "172.24.87.255";
|
|
||||||
# sane.ovpn.addrV6 = "fd00:0000:1337:cafe:1111:1111:18cd:a72b";
|
# for some reason desko -> moby deploys are super flaky when desko is also a nixcache (not true of desko -> lappy deploys, though!)
|
||||||
|
# > unable to download 'http://desko:5001/<hash>.narinfo': Server returned nothing (no headers, no data) (52)
|
||||||
|
sane.nixcache.substituters.desko = false;
|
||||||
|
|
||||||
# XXX colin: phosh doesn't work well with passwordless login,
|
# XXX colin: phosh doesn't work well with passwordless login,
|
||||||
# so set this more reliable default password should anything go wrong
|
# so set this more reliable default password should anything go wrong
|
||||||
|
@ -27,8 +36,13 @@
|
||||||
|
|
||||||
sops.secrets.colin-passwd.neededForUsers = true;
|
sops.secrets.colin-passwd.neededForUsers = true;
|
||||||
|
|
||||||
|
# sane.gui.sxmo.enable = true;
|
||||||
sane.programs.sway.enableFor.user.colin = true;
|
sane.programs.sway.enableFor.user.colin = true;
|
||||||
sane.programs.sway.config.mod = "Mod1"; #< alt key instead of Super
|
sane.programs.swaylock.enableFor.user.colin = false; #< not usable on touch
|
||||||
|
sane.programs.schlock.enableFor.user.colin = true;
|
||||||
|
sane.programs.swayidle.config.actions.screenoff.delay = 300;
|
||||||
|
sane.programs.swayidle.config.actions.screenoff.enable = true;
|
||||||
|
sane.programs.sane-input-handler.enableFor.user.colin = true;
|
||||||
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
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.fcitx5.enableFor.user.colin = false; # does not cross compile
|
||||||
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
|
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
|
||||||
|
@ -44,8 +58,22 @@
|
||||||
# sane.programs."gnome.geary".config.autostart = true;
|
# sane.programs."gnome.geary".config.autostart = true;
|
||||||
# sane.programs.calls.config.autostart = true;
|
# sane.programs.calls.config.autostart = true;
|
||||||
|
|
||||||
sane.programs.pipewire.config = {
|
sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
|
||||||
# tune so Dino doesn't drop audio
|
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
|
||||||
|
sane.programs.firefox.env = lib.mkForce {};
|
||||||
|
sane.programs.epiphany.env.BROWSER = "epiphany";
|
||||||
|
|
||||||
|
# note the .conf.d approach: using ~/.config/pipewire/pipewire.conf directly breaks all audio,
|
||||||
|
# presumably because that deletes the defaults entirely whereas the .conf.d approach selectively overrides defaults
|
||||||
|
sane.user.fs.".config/pipewire/pipewire.conf.d/10-fix-dino-mic-cutout.conf".symlink.text = ''
|
||||||
|
# config docs: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire#properties>
|
||||||
|
# useful to run `pw-top` to see that these settings are actually having effect,
|
||||||
|
# and `pw-metadata` to see if any settings conflict (e.g. max-quantum < min-quantum)
|
||||||
|
#
|
||||||
|
# restart pipewire after editing these files:
|
||||||
|
# - `systemctl --user restart pipewire`
|
||||||
|
# - pipewire users will likely stop outputting audio until they are also restarted
|
||||||
|
#
|
||||||
# there's seemingly two buffers for the mic (see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>)
|
# there's seemingly two buffers for the mic (see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>)
|
||||||
# 1. Pipewire buffering out of the driver and into its own member.
|
# 1. Pipewire buffering out of the driver and into its own member.
|
||||||
# 2. Pipewire buffering into Dino.
|
# 2. Pipewire buffering into Dino.
|
||||||
|
@ -56,11 +84,67 @@
|
||||||
# `pw-metadata -n settings 0 clock.force-quantum 1024` reduces to about 1 error per second.
|
# `pw-metadata -n settings 0 clock.force-quantum 1024` reduces to about 1 error per second.
|
||||||
# `pw-metadata -n settings 0 clock.force-quantum 2048` reduces to 1 error every < 10s.
|
# `pw-metadata -n settings 0 clock.force-quantum 2048` reduces to 1 error every < 10s.
|
||||||
# pipewire default config includes `clock.power-of-two-quantum = true`
|
# pipewire default config includes `clock.power-of-two-quantum = true`
|
||||||
min-quantum = 2048;
|
context.properties = {
|
||||||
max-quantum = 8192;
|
default.clock.min-quantum = 2048
|
||||||
};
|
default.clock.max-quantum = 8192
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
# /boot space is at a premium. default was 20.
|
# /boot space is at a premium. default was 20.
|
||||||
# even 10 can be too much
|
# even 10 can be too much
|
||||||
boot.loader.generic-extlinux-compatible.configurationLimit = 8;
|
boot.loader.generic-extlinux-compatible.configurationLimit = 8;
|
||||||
|
# mobile.bootloader.enable = false;
|
||||||
|
# mobile.boot.stage-1.enable = false;
|
||||||
|
# boot.initrd.systemd.enable = false;
|
||||||
|
# boot.initrd.services.swraid.enable = false; # attempt to fix dm_mod stuff
|
||||||
|
|
||||||
|
# hardware.firmware makes the referenced files visible to the kernel, for whenever a driver explicitly asks for them.
|
||||||
|
# these files are visible from userspace by following `/sys/module/firmware_class/parameters/path`
|
||||||
|
#
|
||||||
|
# mobile-nixos' /lib/firmware includes:
|
||||||
|
# rtl_bt (bluetooth)
|
||||||
|
# anx7688-fw.bin (USB-C chip: power negotiation, HDMI/dock)
|
||||||
|
# ov5640_af.bin (camera module)
|
||||||
|
# hardware.firmware = [ config.mobile.device.firmware ];
|
||||||
|
# hardware.firmware = [ pkgs.rtl8723cs-firmware ];
|
||||||
|
hardware.firmware = [
|
||||||
|
(pkgs.linux-firmware-megous.override {
|
||||||
|
# rtl_bt = false probably means no bluetooth connectivity.
|
||||||
|
# N.B.: DON'T RE-ENABLE without first confirming that wake-on-lan works during suspend (rtcwake).
|
||||||
|
# it seems the rtl_bt stuff ("bluetooth coexist") might make wake-on-LAN radically more flaky.
|
||||||
|
rtl_bt = false;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
system.stateVersion = "21.11";
|
||||||
|
|
||||||
|
# defined: https://www.freedesktop.org/software/systemd/man/machine-info.html
|
||||||
|
# XXX colin: not sure which, if any, software makes use of this
|
||||||
|
environment.etc."machine-info".text = ''
|
||||||
|
CHASSIS="handset"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# enable rotation sensor
|
||||||
|
hardware.sensor.iio.enable = true;
|
||||||
|
|
||||||
|
# TODO: move elsewhere...
|
||||||
|
systemd.services.ModemManager.serviceConfig = {
|
||||||
|
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored
|
||||||
|
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ];
|
||||||
|
# --debug sets DEBUG level logging: so reset
|
||||||
|
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.udev.extraRules = let
|
||||||
|
chmod = "${pkgs.coreutils}/bin/chmod";
|
||||||
|
chown = "${pkgs.coreutils}/bin/chown";
|
||||||
|
in ''
|
||||||
|
# make Pinephone flashlight writable by user.
|
||||||
|
# taken from postmarketOS: <repo:postmarketOS/pmaports:device/main/device-pine64-pinephone/60-flashlight.rules>
|
||||||
|
SUBSYSTEM=="leds", DEVPATH=="*/*:flash", RUN+="${chmod} g+w /sys%p/brightness /sys%p/flash_strobe", RUN+="${chown} :video /sys%p/brightness /sys%p/flash_strobe"
|
||||||
|
|
||||||
|
# make Pinephone front LEDs writable by user.
|
||||||
|
SUBSYSTEM=="leds", DEVPATH=="*/*:indicator", RUN+="${chmod} g+w /sys%p/brightness", RUN+="${chown} :video /sys%p/brightness"
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|
69
hosts/by-name/moby/gps.nix
Normal file
69
hosts/by-name/moby/gps.nix
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# 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.services.eg25-control.enable = true;
|
||||||
|
sane.programs.where-am-i.enableFor.user.colin = true;
|
||||||
|
}
|
91
hosts/by-name/moby/kernel.nix
Normal file
91
hosts/by-name/moby/kernel.nix
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
dmesg = "${pkgs.util-linux}/bin/dmesg";
|
||||||
|
grep = "${pkgs.gnugrep}/bin/grep";
|
||||||
|
modprobe = "${pkgs.kmod}/bin/modprobe";
|
||||||
|
ensureHWReady = ''
|
||||||
|
# common boot failure:
|
||||||
|
# blank screen (no backlight even), with the following log:
|
||||||
|
# ```syslog
|
||||||
|
# sun8i-dw-hdmi 1ee0000.hdmi: Couldn't get the HDMI PHY
|
||||||
|
# ...
|
||||||
|
# sun4i-drm display-engine: Couldn't bind all pipelines components
|
||||||
|
# ...
|
||||||
|
# sun8i-dw-hdmi: probe of 1ee0000.hdmi failed with error -17
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# in particular, that `probe ... failed` occurs *only* on failed boots
|
||||||
|
# (the other messages might sometimes occur even on successful runs?)
|
||||||
|
#
|
||||||
|
# reloading the sun8i hdmi driver usually gets the screen on, showing boot text.
|
||||||
|
# then restarting display-manager.service gets us to the login.
|
||||||
|
#
|
||||||
|
# NB: the above log is default level. though less specific, there's a `err` level message that also signals this:
|
||||||
|
# sun4i-drm display-engine: failed to bind 1ee0000.hdmi (ops sun8i_dw_hdmi_ops [sun8i_drm_hdmi]): -17
|
||||||
|
# NB: this is the most common, but not the only, failure mode for `display-manager`.
|
||||||
|
# another error seems characterized by these dmesg logs, in which reprobing sun8i_drm_hdmi does not fix:
|
||||||
|
# ```syslog
|
||||||
|
# sun6i-mipi-dsi 1ca0000.dsi: Couldn't get the MIPI D-PHY
|
||||||
|
# sun4i-drm display-engine: Couldn't bind all pipelines components
|
||||||
|
# sun6i-mipi-dsi 1ca0000.dsi: Couldn't register our component
|
||||||
|
# ```
|
||||||
|
|
||||||
|
if (${dmesg} --kernel --level err --color=never --notime | ${grep} -q 'sun4i-drm display-engine: failed to bind 1ee0000.hdmi')
|
||||||
|
then
|
||||||
|
echo "reprobing sun8i_drm_hdmi"
|
||||||
|
# if a command here fails it errors the whole service, so prefer to log instead
|
||||||
|
${modprobe} -r sun8i_drm_hdmi || echo "failed to unload sun8i_drm_hdmi"
|
||||||
|
${modprobe} sun8i_drm_hdmi || echo "failed to load sub8i_drm_hdmi"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux-megous;
|
||||||
|
# boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux-manjaro;
|
||||||
|
# boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux_latest;
|
||||||
|
|
||||||
|
# alternatively, apply patches directly to stock nixos kernel:
|
||||||
|
# boot.kernelPatches = manjaroPatches ++ [
|
||||||
|
# (patchDefconfig kernelConfig)
|
||||||
|
# ];
|
||||||
|
|
||||||
|
# configure nixos to build a compressed kernel image, since it doesn't usually do that for aarch64 target.
|
||||||
|
# without this i run out of /boot space in < 10 generations
|
||||||
|
nixpkgs.hostPlatform.linux-kernel = {
|
||||||
|
# defaults:
|
||||||
|
name = "aarch64-multiplatform";
|
||||||
|
baseConfig = "defconfig";
|
||||||
|
DTB = true;
|
||||||
|
autoModules = true;
|
||||||
|
preferBuiltin = true;
|
||||||
|
# extraConfig = ...
|
||||||
|
# ^-- raspberry pi stuff: we don't need it.
|
||||||
|
|
||||||
|
# target = "Image"; # <-- default
|
||||||
|
target = "Image.gz"; # <-- compress the kernel image
|
||||||
|
# target = "zImage"; # <-- confuses other parts of nixos :-(
|
||||||
|
};
|
||||||
|
|
||||||
|
# disable proximity sensor.
|
||||||
|
# the filtering/calibration is bad that it causes the screen to go fully dark at times.
|
||||||
|
boot.blacklistedKernelModules = [ "stk3310" ];
|
||||||
|
|
||||||
|
boot.kernelParams = [
|
||||||
|
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
|
||||||
|
# this is because they can't allocate enough video ram.
|
||||||
|
# see related nixpkgs issue: <https://github.com/NixOS/nixpkgs/issues/260222>
|
||||||
|
# TODO(2023/12/03): remove once mesa 23.3.1 lands: <https://github.com/NixOS/nixpkgs/pull/265740>
|
||||||
|
#
|
||||||
|
# the default CMA seems to be 32M.
|
||||||
|
# i was running fine with 256MB from 2022/07-ish through 2022/12-ish, but then the phone quit reliably coming back from sleep (phosh): maybe a memory leak?
|
||||||
|
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
||||||
|
"cma=512M"
|
||||||
|
# 2023/10/20: potential fix for the lima (GPU) timeout bugs:
|
||||||
|
# - <https://gitlab.com/postmarketOS/pmaports/-/issues/805#note_890467824>
|
||||||
|
"lima.sched_timeout_ms=2000"
|
||||||
|
];
|
||||||
|
|
||||||
|
# services.xserver.displayManager.job.preStart = ensureHWReady;
|
||||||
|
# systemd.services.greetd.preStart = ensureHWReady;
|
||||||
|
systemd.services.unl0kr.preStart = ensureHWReady;
|
||||||
|
}
|
45
hosts/by-name/moby/polyfill.nix
Normal file
45
hosts/by-name/moby/polyfill.nix
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# this file configures preferences per program, without actually enabling any programs.
|
||||||
|
# the goal is to separate the place where we decide *what* to use (i.e. `sane.programs.firefox.enable = true` -- at the toplevel)
|
||||||
|
# from where we specific how that thing should behave *if* it's in use.
|
||||||
|
#
|
||||||
|
# NixOS backgrounds:
|
||||||
|
# - <https://github.com/NixOS/nixos-artwork>
|
||||||
|
# - <https://github.com/NixOS/nixos-artwork/issues/50> (colorful; unmerged)
|
||||||
|
# - <https://github.com/NixOS/nixos-artwork/pull/60/files> (desktop-oriented; clean; unmerged)
|
||||||
|
# - <https://itsfoss.com/content/images/2023/04/nixos-tutorials.png>
|
||||||
|
|
||||||
|
{ lib, pkgs, sane-lib, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.firefox.config = {
|
||||||
|
# compromise impermanence for the sake of usability
|
||||||
|
persistCache = "private";
|
||||||
|
persistData = "private";
|
||||||
|
|
||||||
|
# i don't do crypto stuff on moby
|
||||||
|
addons.ether-metamask.enable = false;
|
||||||
|
# sidebery UX doesn't make sense on small screen
|
||||||
|
addons.sidebery.enable = false;
|
||||||
|
};
|
||||||
|
sane.programs.swaynotificationcenter.config = {
|
||||||
|
backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.programs.alacritty.config.fontSize = 9;
|
||||||
|
|
||||||
|
sane.programs.sway.config = {
|
||||||
|
font = "pango:monospace 10";
|
||||||
|
mod = "Mod1"; # prefer Alt
|
||||||
|
workspace_layout = "tabbed";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.programs.waybar.config = {
|
||||||
|
fontSize = 14;
|
||||||
|
height = 26;
|
||||||
|
persistWorkspaces = [ "1" "2" "3" "4" "5" ];
|
||||||
|
modules.media = false;
|
||||||
|
modules.network = false;
|
||||||
|
modules.perf = false;
|
||||||
|
modules.windowTitle = false;
|
||||||
|
# TODO: show modem state
|
||||||
|
};
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
./fs.nix
|
./fs.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
sane.persist.enable = false; # what we mean here is that the image is immutable; `/` is still tmpfs.
|
sane.persist.enable = false; # what we mean here is that the image is immutable; `/` is still tmpfs.
|
||||||
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
|
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
|
||||||
|
@ -11,4 +12,7 @@
|
||||||
# auto-login at shell
|
# auto-login at shell
|
||||||
services.getty.autologinUser = "colin";
|
services.getty.autologinUser = "colin";
|
||||||
# users.users.colin.initialPassword = "colin";
|
# users.users.colin.initialPassword = "colin";
|
||||||
|
|
||||||
|
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
||||||
|
system.stateVersion = "21.05";
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,20 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.roles.build-machine.enable = true;
|
sane.roles.build-machine.enable = true;
|
||||||
sane.programs.zsh.config.showDeadlines = false; # ~/knowledge doesn't always exist
|
sane.zsh.showDeadlines = false; # ~/knowledge doesn't always exist
|
||||||
sane.programs.consoleUtils.suggestedPrograms = [
|
sane.programs.consoleUtils.suggestedPrograms = [
|
||||||
"consoleMediaUtils" # notably, for go2tv / casting
|
"consoleMediaUtils" # notably, for go2tv / casting
|
||||||
"pcConsoleUtils"
|
"pcConsoleUtils"
|
||||||
"sane-scripts.stop-all-servo"
|
"sane-scripts.stop-all-servo"
|
||||||
];
|
];
|
||||||
sane.services.dyn-dns.enable = true;
|
sane.services.dyn-dns.enable = true;
|
||||||
sane.services.trust-dns.asSystemResolver = false; # TODO: enable once it's all working well
|
|
||||||
sane.services.wg-home.enable = true;
|
sane.services.wg-home.enable = true;
|
||||||
sane.services.wg-home.visibleToWan = true;
|
sane.services.wg-home.visibleToWan = true;
|
||||||
sane.services.wg-home.forwardToWan = true;
|
sane.services.wg-home.forwardToWan = true;
|
||||||
sane.services.wg-home.routeThroughServo = false;
|
sane.services.wg-home.routeThroughServo = false;
|
||||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||||
sane.ovpn.addrV4 = "172.23.174.114";
|
sane.nixcache.substituters.servo = false;
|
||||||
# sane.ovpn.addrV6 = "fd00:0000:1337:cafe:1111:1111:8df3:14b0";
|
sane.nixcache.substituters.desko = false;
|
||||||
sane.nixcache.remote-builders.desko = false;
|
sane.nixcache.remote-builders.desko = false;
|
||||||
sane.nixcache.remote-builders.servo = false;
|
sane.nixcache.remote-builders.servo = false;
|
||||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||||
|
@ -38,6 +37,7 @@
|
||||||
# using root here makes sure we always have an escape hatch
|
# using root here makes sure we always have an escape hatch
|
||||||
services.getty.autologinUser = "root";
|
services.getty.autologinUser = "root";
|
||||||
|
|
||||||
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
# both transmission and ipfs try to set different net defaults.
|
# both transmission and ipfs try to set different net defaults.
|
||||||
|
@ -45,5 +45,13 @@
|
||||||
boot.kernel.sysctl = {
|
boot.kernel.sysctl = {
|
||||||
"net.core.rmem_max" = 4194304; # 4MB
|
"net.core.rmem_max" = 4194304; # 4MB
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# This value determines the NixOS release from which the default
|
||||||
|
# settings for stateful data, like file locations and database versions
|
||||||
|
# on your system were taken. It‘s perfectly fine and recommended to leave
|
||||||
|
# this value at the release version of the first install of this system.
|
||||||
|
# Before changing this value read the documentation for this option
|
||||||
|
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
||||||
|
system.stateVersion = "21.11";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
# - 1. identify disk IDs: `ls -l /dev/disk/by-id`
|
# - 1. identify disk IDs: `ls -l /dev/disk/by-id`
|
||||||
# - 2. pool these disks: `zpool create -f -m legacy pool raidz ata-ST4000VN008-2DR166_WDH0VB45 ata-ST4000VN008-2DR166_WDH17616 ata-ST4000VN008-2DR166_WDH0VC8Q ata-ST4000VN008-2DR166_WDH17680`
|
# - 2. pool these disks: `zpool create -f -m legacy pool raidz ata-ST4000VN008-2DR166_WDH0VB45 ata-ST4000VN008-2DR166_WDH17616 ata-ST4000VN008-2DR166_WDH0VC8Q ata-ST4000VN008-2DR166_WDH17680`
|
||||||
# - legacy documented: <https://superuser.com/questions/790036/what-is-a-zfs-legacy-mount-point>
|
# - legacy documented: <https://superuser.com/questions/790036/what-is-a-zfs-legacy-mount-point>
|
||||||
# - 3. enable acl support: `zfs set acltype=posixacl pool`
|
|
||||||
#
|
#
|
||||||
# import pools: `zpool import pool`
|
# import pools: `zpool import pool`
|
||||||
# show zfs datasets: `zfs list` (will be empty if haven't imported)
|
# show zfs datasets: `zfs list` (will be empty if haven't imported)
|
||||||
|
@ -26,7 +25,6 @@
|
||||||
# scrub all zfs pools weekly:
|
# scrub all zfs pools weekly:
|
||||||
services.zfs.autoScrub.enable = true;
|
services.zfs.autoScrub.enable = true;
|
||||||
boot.extraModprobeConfig = ''
|
boot.extraModprobeConfig = ''
|
||||||
### zfs_arc_max tunable:
|
|
||||||
# ZFS likes to use half the ram for its own cache and let the kernel push everything else to swap.
|
# ZFS likes to use half the ram for its own cache and let the kernel push everything else to swap.
|
||||||
# so, reduce its cache size
|
# so, reduce its cache size
|
||||||
# see: <https://askubuntu.com/a/1290387>
|
# see: <https://askubuntu.com/a/1290387>
|
||||||
|
@ -35,13 +33,7 @@
|
||||||
# for all tunables, see: `man 4 zfs`
|
# for all tunables, see: `man 4 zfs`
|
||||||
# to update these parameters without rebooting:
|
# to update these parameters without rebooting:
|
||||||
# - `echo '4294967296' | sane-sudo-redirect /sys/module/zfs/parameters/zfs_arc_max`
|
# - `echo '4294967296' | sane-sudo-redirect /sys/module/zfs/parameters/zfs_arc_max`
|
||||||
### zfs_bclone_enabled tunable
|
options zfs zfs_arc_max=4294967296
|
||||||
# this allows `cp --reflink=always FOO BAR` to work. i.e. shallow copies.
|
|
||||||
# it's unstable as of 2.2.3. led to *actual* corruption in 2.2.1, but hopefully better by now.
|
|
||||||
# - <https://github.com/openzfs/zfs/issues/405>
|
|
||||||
# note that `du -h` won't *always* show the reduced size for reflink'd files (?).
|
|
||||||
# `zpool get all | grep clone` seems to be the way to *actually* see how much data is being deduped
|
|
||||||
options zfs zfs_arc_max=4294967296 zfs_bclone_enabled=1
|
|
||||||
'';
|
'';
|
||||||
# to be able to mount the pool like this, make sure to tell zfs to NOT manage it itself.
|
# to be able to mount the pool like this, make sure to tell zfs to NOT manage it itself.
|
||||||
# otherwise local-fs.target will FAIL and you will be dropped into a rescue shell.
|
# otherwise local-fs.target will FAIL and you will be dropped into a rescue shell.
|
||||||
|
@ -51,7 +43,6 @@
|
||||||
fileSystems."/mnt/pool" = {
|
fileSystems."/mnt/pool" = {
|
||||||
device = "pool";
|
device = "pool";
|
||||||
fsType = "zfs";
|
fsType = "zfs";
|
||||||
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
|
# services.zfs.zed = ... # TODO: zfs can send me emails when disks fail
|
||||||
sane.programs.sysadminUtils.suggestedPrograms = [ "zfs" ];
|
sane.programs.sysadminUtils.suggestedPrograms = [ "zfs" ];
|
||||||
|
@ -91,17 +82,12 @@
|
||||||
};
|
};
|
||||||
sane.fs."/mnt/usb-hdd".mount = {};
|
sane.fs."/mnt/usb-hdd".mount = {};
|
||||||
|
|
||||||
# FIRST TIME SETUP FOR MEDIA DIRECTORY:
|
sane.persist.sys.byStore.plaintext = [{
|
||||||
# - set the group stick bit: `sudo find /var/media -type d -exec chmod g+s {} +`
|
|
||||||
# - this ensures new files/dirs inherit the group of their parent dir (instead of the user who creates them)
|
|
||||||
# - ensure everything under /var/media is mounted with `-o acl`, to support acls
|
|
||||||
# - ensure all files are rwx by group: `setfacl --recursive --modify d:g::rwx /var/media`
|
|
||||||
# - alternatively, `d:g:media:rwx` to grant `media` group even when file has a different owner, but that's a bit complex
|
|
||||||
sane.persist.sys.byStore.ext = [{
|
|
||||||
path = "/var/media";
|
path = "/var/media";
|
||||||
|
method = "bind"; #< this HAS to be `bind` if we're going to persist the whole thing but create subdirs, as below.
|
||||||
user = "colin";
|
user = "colin";
|
||||||
group = "media";
|
group = "media";
|
||||||
mode = "0775";
|
mode = "0755";
|
||||||
}];
|
}];
|
||||||
sane.fs."/var/media/archive".dir = {};
|
sane.fs."/var/media/archive".dir = {};
|
||||||
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
||||||
|
@ -115,7 +101,7 @@
|
||||||
sane.fs."/var/media/Books/Books".dir = {};
|
sane.fs."/var/media/Books/Books".dir = {};
|
||||||
sane.fs."/var/media/Books/Visual".dir = {};
|
sane.fs."/var/media/Books/Visual".dir = {};
|
||||||
sane.fs."/var/media/collections".dir = {};
|
sane.fs."/var/media/collections".dir = {};
|
||||||
# sane.fs."/var/media/datasets".dir = {};
|
sane.fs."/var/media/datasets".dir = {};
|
||||||
sane.fs."/var/media/freeleech".dir = {};
|
sane.fs."/var/media/freeleech".dir = {};
|
||||||
sane.fs."/var/media/Music".dir = {};
|
sane.fs."/var/media/Music".dir = {};
|
||||||
sane.fs."/var/media/Pictures".dir = {};
|
sane.fs."/var/media/Pictures".dir = {};
|
||||||
|
@ -130,6 +116,27 @@
|
||||||
this directory exists on SSD, allowing for speedy access to specific datasets when necessary.
|
this directory exists on SSD, allowing for speedy access to specific datasets when necessary.
|
||||||
the contents should be a subset of what's in ../media/datasets.
|
the contents should be a subset of what's in ../media/datasets.
|
||||||
'';
|
'';
|
||||||
|
# make sure large media is stored to the HDD
|
||||||
|
sane.persist.sys.byStore.ext = [
|
||||||
|
{
|
||||||
|
user = "colin";
|
||||||
|
group = "users";
|
||||||
|
mode = "0777";
|
||||||
|
path = "/var/media/Videos";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
user = "colin";
|
||||||
|
group = "users";
|
||||||
|
mode = "0777";
|
||||||
|
path = "/var/media/freeleech";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
user = "colin";
|
||||||
|
group = "users";
|
||||||
|
mode = "0775";
|
||||||
|
path = "/var/lib/uninsane/datasets";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
# btrfs doesn't easily support swapfiles
|
# btrfs doesn't easily support swapfiles
|
||||||
# swapDevices = [
|
# swapDevices = [
|
||||||
|
|
|
@ -3,19 +3,9 @@
|
||||||
let
|
let
|
||||||
portOpts = with lib; types.submodule {
|
portOpts = with lib; types.submodule {
|
||||||
options = {
|
options = {
|
||||||
visibleTo.ovpns = mkOption {
|
visibleTo.ovpn = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
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,7 +13,7 @@ in
|
||||||
{
|
{
|
||||||
options = with lib; {
|
options = with lib; {
|
||||||
sane.ports.ports = mkOption {
|
sane.ports.ports = mkOption {
|
||||||
# add the `visibleTo.{doof,ovpns}` options
|
# add the `visibleTo.ovpn` option
|
||||||
type = types.attrsOf portOpts;
|
type = types.attrsOf portOpts;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -48,47 +38,37 @@ in
|
||||||
# FallbackDNS=1.1.1.1 9.9.9.9
|
# FallbackDNS=1.1.1.1 9.9.9.9
|
||||||
# '';
|
# '';
|
||||||
|
|
||||||
# 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 = {
|
|
||||||
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
|
|
||||||
];
|
|
||||||
peers = [
|
|
||||||
{
|
|
||||||
publicKey = "nuESyYEJ3YU0hTZZgAd7iHBz1ytWBVM5PjEL1VEoTkU=";
|
|
||||||
# TODO: configure DNS within the doof ns and use tun-sea.doof.net endpoint
|
|
||||||
# endpoint = "tun-sea.doof.net:53263";
|
|
||||||
endpoint = "205.201.63.44:53263";
|
|
||||||
allowedIPs = [ "0.0.0.0/0" "::/0" ];
|
|
||||||
persistentKeepalive = 25; #< keep the NAT alive
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
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):
|
# OVPN CONFIG (https://www.ovpn.com):
|
||||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
# DOCS: https://nixos.wiki/wiki/WireGuard
|
||||||
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
# 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?)
|
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
||||||
networking.wireguard.enable = true;
|
networking.wireguard.enable = true;
|
||||||
networking.wireguard.interfaces.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;
|
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||||
# wg is active only in this namespace.
|
# wg is active only in this namespace.
|
||||||
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
|
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
|
||||||
# sudo ip netns exec ovpns ping www.google.com
|
# sudo ip netns exec ovpns ping www.google.com
|
||||||
interfaceNamespace = "ovpns";
|
interfaceNamespace = "ovpns";
|
||||||
ips = [ "185.157.162.178" ];
|
ips = [
|
||||||
|
"185.157.162.178/32"
|
||||||
|
];
|
||||||
peers = [
|
peers = [
|
||||||
{
|
{
|
||||||
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
||||||
|
@ -106,11 +86,99 @@ in
|
||||||
# dynamicEndpointRefreshRestartSeconds = 5;
|
# dynamicEndpointRefreshRestartSeconds = 5;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
preSetup = ''
|
||||||
|
${ip} netns add ovpns || echo "ovpns already exists"
|
||||||
|
'';
|
||||||
|
postShutdown = ''
|
||||||
|
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||||
|
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||||
|
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||||
|
# restore rules/routes
|
||||||
|
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
|
||||||
|
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
|
||||||
|
${ip} rule add from all lookup local pref 0
|
||||||
|
${ip} rule del from all lookup local pref 100
|
||||||
|
'';
|
||||||
|
postSetup = ''
|
||||||
|
# DOCS:
|
||||||
|
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
||||||
|
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
||||||
|
# create veth pair
|
||||||
|
${ip} link add ovpns-veth-a type veth peer name ovpns-veth-b
|
||||||
|
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
|
||||||
|
${ip} link set ovpns-veth-a up
|
||||||
|
|
||||||
|
# mv veth-b into the ovpns namespace
|
||||||
|
${ip} link set ovpns-veth-b netns ovpns
|
||||||
|
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
|
||||||
|
${in-ns} ip link set ovpns-veth-b up
|
||||||
|
|
||||||
|
# make it so traffic originating from the host side of the veth
|
||||||
|
# is sent over the veth no matter its destination.
|
||||||
|
${ip} rule add from ${veth-host-ip} lookup ovpns pref 50
|
||||||
|
# for traffic originating at the host veth to the WAN, use the veth as our gateway
|
||||||
|
# not sure if the metric 1002 matters.
|
||||||
|
${ip} route add default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
|
||||||
|
# give the default route lower priority
|
||||||
|
${ip} rule add from all lookup local pref 100
|
||||||
|
${ip} rule del from all lookup local pref 0
|
||||||
|
|
||||||
|
# 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";
|
# create a new routing table that we can use to proxy traffic out of the root namespace
|
||||||
sane.netns.ovpns.netnsPubIpv4 = "185.157.162.178";
|
# through the ovpns namespace, and to the WAN via VPN.
|
||||||
sane.netns.ovpns.routeTable = 11;
|
networking.iproute2.rttablesExtraConfig = ''
|
||||||
sane.netns.ovpns.dns = "46.227.67.134"; #< DNS requests inside the namespace are forwarded here
|
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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ lib.mkIf false
|
||||||
# services.calibre-web.options.calibreLibrary = svc-dir;
|
# services.calibre-web.options.calibreLibrary = svc-dir;
|
||||||
|
|
||||||
services.nginx.virtualHosts."calibre.uninsane.org" = {
|
services.nginx.virtualHosts."calibre.uninsane.org" = {
|
||||||
forceSSL = true;
|
addSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://${ip}:${builtins.toString port}";
|
proxyPass = "http://${ip}:${builtins.toString port}";
|
||||||
|
|
|
@ -24,65 +24,50 @@
|
||||||
# that is NOT the case when the STUN server and client A are on the same LAN
|
# that is NOT the case when the STUN server and client A are on the same LAN
|
||||||
# even if client A contacts the STUN server via its WAN address with port reflection enabled.
|
# even if client A contacts the STUN server via its WAN address with port reflection enabled.
|
||||||
# hence, there's no obvious way to put the STUN server on the same LAN as either client and expect the rest to work.
|
# hence, there's no obvious way to put the STUN server on the same LAN as either client and expect the rest to work.
|
||||||
# - there an old version which *half worked*, which is:
|
{ lib, ... }:
|
||||||
# - run the turn server in the root namespace.
|
|
||||||
# - bind the turn server to the veth connecting it to the VPN namespace (so it sends outgoing traffic to the right place).
|
|
||||||
# - NAT the turn port range from VPN into root namespace (so it receives incomming traffic).
|
|
||||||
# - this approach would fail the prosody conversations.im check, but i didn't notice *obvious* call routing errors.
|
|
||||||
#
|
|
||||||
# debugging:
|
|
||||||
# - log messages like 'usage: realm=<turn.uninsane.org>, username=<1715915193>, rp=14, rb=1516, sp=8, sb=684'
|
|
||||||
# - rp = received packets
|
|
||||||
# - rb = received bytes
|
|
||||||
# - sp = sent packets
|
|
||||||
# - sb = sent bytes
|
|
||||||
|
|
||||||
{ config, lib, ... }:
|
|
||||||
let
|
let
|
||||||
# TURN port range (inclusive).
|
# TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward
|
||||||
# default coturn behavior is to use the upper quarter of all ports. i.e. 49152 - 65535.
|
# TURN port range (inclusive)
|
||||||
# i believe TURN allocations expire after either 5 or 10 minutes of inactivity.
|
turnPortLow = 49152;
|
||||||
turnPortLow = 49152; # 49152 = 0xc000
|
turnPortHigh = 49167;
|
||||||
turnPortHigh = turnPortLow + 256;
|
|
||||||
turnPortRange = lib.range turnPortLow turnPortHigh;
|
turnPortRange = lib.range turnPortLow turnPortHigh;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# the port definitions are only needed if running in the root net namespace
|
sane.ports.ports = lib.mkMerge ([
|
||||||
# sane.ports.ports = lib.mkMerge ([
|
{
|
||||||
# {
|
"3478" = {
|
||||||
# "3478" = {
|
# this is the "control" port.
|
||||||
# # this is the "control" port.
|
# i.e. no client data is forwarded through it, but it's where clients request tunnels.
|
||||||
# # i.e. no client data is forwarded through it, but it's where clients request tunnels.
|
protocol = [ "tcp" "udp" ];
|
||||||
# protocol = [ "tcp" "udp" ];
|
# visibleTo.lan = true;
|
||||||
# # visibleTo.lan = true;
|
# visibleTo.wan = true;
|
||||||
# # visibleTo.wan = true;
|
visibleTo.ovpn = true;
|
||||||
# visibleTo.ovpns = true; # forward traffic from the VPN to the root NS
|
description = "colin-stun-turn";
|
||||||
# description = "colin-stun-turn";
|
};
|
||||||
# };
|
"5349" = {
|
||||||
# "5349" = {
|
# the other port 3478 also supports TLS/DTLS, but presumably clients wanting TLS will default 5349
|
||||||
# # the other port 3478 also supports TLS/DTLS, but presumably clients wanting TLS will default 5349
|
protocol = [ "tcp" ];
|
||||||
# protocol = [ "tcp" ];
|
# visibleTo.lan = true;
|
||||||
# # visibleTo.lan = true;
|
# visibleTo.wan = true;
|
||||||
# # visibleTo.wan = true;
|
visibleTo.ovpn = true;
|
||||||
# visibleTo.ovpns = true;
|
description = "colin-stun-turn-over-tls";
|
||||||
# description = "colin-stun-turn-over-tls";
|
};
|
||||||
# };
|
}
|
||||||
# }
|
] ++ (builtins.map
|
||||||
# ] ++ (builtins.map
|
(port: {
|
||||||
# (port: {
|
"${builtins.toString port}" = let
|
||||||
# "${builtins.toString port}" = let
|
count = port - turnPortLow + 1;
|
||||||
# count = port - turnPortLow + 1;
|
numPorts = turnPortHigh - turnPortLow + 1;
|
||||||
# numPorts = turnPortHigh - turnPortLow + 1;
|
in {
|
||||||
# in {
|
protocol = [ "tcp" "udp" ];
|
||||||
# protocol = [ "tcp" "udp" ];
|
# visibleTo.lan = true;
|
||||||
# # visibleTo.lan = true;
|
# visibleTo.wan = true;
|
||||||
# # visibleTo.wan = true;
|
visibleTo.ovpn = true;
|
||||||
# visibleTo.ovpns = true;
|
description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||||
# description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
};
|
||||||
# };
|
})
|
||||||
# })
|
turnPortRange
|
||||||
# turnPortRange
|
));
|
||||||
# ));
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."turn.uninsane.org" = {
|
services.nginx.virtualHosts."turn.uninsane.org" = {
|
||||||
# allow ACME to procure a cert via nginx for this domain
|
# allow ACME to procure a cert via nginx for this domain
|
||||||
|
@ -118,28 +103,22 @@ in
|
||||||
services.coturn.realm = "turn.uninsane.org";
|
services.coturn.realm = "turn.uninsane.org";
|
||||||
services.coturn.cert = "/var/lib/acme/turn.uninsane.org/fullchain.pem";
|
services.coturn.cert = "/var/lib/acme/turn.uninsane.org/fullchain.pem";
|
||||||
services.coturn.pkey = "/var/lib/acme/turn.uninsane.org/key.pem";
|
services.coturn.pkey = "/var/lib/acme/turn.uninsane.org/key.pem";
|
||||||
|
|
||||||
#v disable to allow unauthenticated access (or set `services.coturn.no-auth = true`)
|
|
||||||
services.coturn.use-auth-secret = true;
|
services.coturn.use-auth-secret = true;
|
||||||
services.coturn.static-auth-secret-file = "/var/lib/coturn/shared_secret.bin";
|
services.coturn.static-auth-secret-file = "/var/lib/coturn/shared_secret.bin";
|
||||||
services.coturn.lt-cred-mech = true; #< XXX: use-auth-secret overrides lt-cred-mech
|
services.coturn.lt-cred-mech = true;
|
||||||
|
|
||||||
services.coturn.min-port = turnPortLow;
|
services.coturn.min-port = turnPortLow;
|
||||||
services.coturn.max-port = turnPortHigh;
|
services.coturn.max-port = turnPortHigh;
|
||||||
# services.coturn.secure-stun = true;
|
# services.coturn.secure-stun = true;
|
||||||
services.coturn.extraConfig = lib.concatStringsSep "\n" [
|
services.coturn.extraConfig = lib.concatStringsSep "\n" [
|
||||||
"verbose"
|
"verbose"
|
||||||
# "Verbose" #< even MORE verbosity than "verbose" (it's TOO MUCH verbosity really)
|
# "Verbose" #< even MORE verbosity than "verbose"
|
||||||
"no-multicast-peers" # disables sending to IPv4 broadcast addresses (e.g. 224.0.0.0/3)
|
# "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=10.0.1.5"
|
||||||
"listening-ip=${config.sane.netns.ovpns.netnsPubIpv4}" "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}"
|
# "external-ip=185.157.162.178/10.0.1.5"
|
||||||
|
"external-ip=185.157.162.178"
|
||||||
# old attempts:
|
|
||||||
# "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}/${config.sane.netns.ovpns.hostVethIpv4}"
|
|
||||||
# "listening-ip=10.78.79.51" # can be specified multiple times; omit for *
|
# "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/10.78.79.51"
|
||||||
# "external-ip=97.113.128.229"
|
# "external-ip=97.113.128.229"
|
||||||
# "mobility" # "mobility with ICE (MICE) specs support" (?)
|
# "mobility" # "mobility with ICE (MICE) specs support" (?)
|
||||||
];
|
];
|
||||||
systemd.services.coturn.serviceConfig.NetworkNamespacePath = "/run/netns/ovpns";
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,6 @@
|
||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.pyln-client ])"
|
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.pyln-client ])"
|
||||||
|
|
||||||
"""
|
|
||||||
clightning-sane: helper to perform common Lightning node admin operations:
|
|
||||||
- view channel balances
|
|
||||||
- rebalance channels
|
|
||||||
|
|
||||||
COMMON OPERATIONS:
|
|
||||||
- view channel balances: `clightning-sane status`
|
|
||||||
- rebalance channels to improve routability (without paying any fees): `clightning-sane autobalance`
|
|
||||||
|
|
||||||
FULL OPERATION:
|
|
||||||
- `clightning-sane status --full`
|
|
||||||
- `P$`: represents how many msats i've captured in fees from this channel.
|
|
||||||
- `COST`: rough measure of how much it's "costing" me to let my channel partner hold funds on his side of the channel.
|
|
||||||
this is based on the notion that i only capture fees from outbound transactions, and so the channel partner holding all liquidity means i can't capture fees on that liquidity.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# pyln-client docs: <https://github.com/ElementsProject/lightning/tree/master/contrib/pyln-client>
|
# pyln-client docs: <https://github.com/ElementsProject/lightning/tree/master/contrib/pyln-client>
|
||||||
# terminology:
|
# terminology:
|
||||||
# - "scid": "Short Channel ID", e.g. 123456x7890x0
|
# - "scid": "Short Channel ID", e.g. 123456x7890x0
|
||||||
|
@ -742,7 +726,7 @@ def main():
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description="rebalance lightning channel balances")
|
||||||
parser.add_argument("--verbose", action="store_true", help="more logging")
|
parser.add_argument("--verbose", action="store_true", help="more logging")
|
||||||
parser.add_argument("--min-msat", default="999", help="min transaction size")
|
parser.add_argument("--min-msat", default="999", help="min transaction size")
|
||||||
parser.add_argument("--max-msat", default="1000000", help="max transaction size")
|
parser.add_argument("--max-msat", default="1000000", help="max transaction size")
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
./navidrome.nix
|
./navidrome.nix
|
||||||
./nginx.nix
|
./nginx.nix
|
||||||
./nixos-prebuild.nix
|
./nixos-prebuild.nix
|
||||||
|
./nixserve.nix
|
||||||
./ntfy
|
./ntfy
|
||||||
./pict-rs.nix
|
./pict-rs.nix
|
||||||
./pleroma.nix
|
./pleroma.nix
|
||||||
|
|
|
@ -51,54 +51,54 @@ lib.mkIf false
|
||||||
{
|
{
|
||||||
"3478" = {
|
"3478" = {
|
||||||
protocol = [ "tcp" "udp" ];
|
protocol = [ "tcp" "udp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-stun-turn";
|
description = "colin-xmpp-stun-turn";
|
||||||
};
|
};
|
||||||
"5222" = {
|
"5222" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-client-to-server";
|
description = "colin-xmpp-client-to-server";
|
||||||
};
|
};
|
||||||
"5223" = {
|
"5223" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||||
};
|
};
|
||||||
"5269" = {
|
"5269" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-server-to-server";
|
description = "colin-xmpp-server-to-server";
|
||||||
};
|
};
|
||||||
"5270" = {
|
"5270" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||||
};
|
};
|
||||||
"5280" = {
|
"5280" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-bosh";
|
description = "colin-xmpp-bosh";
|
||||||
};
|
};
|
||||||
"5281" = {
|
"5281" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-bosh-https";
|
description = "colin-xmpp-bosh-https";
|
||||||
};
|
};
|
||||||
"5349" = {
|
"5349" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-stun-turn-over-tls";
|
description = "colin-xmpp-stun-turn-over-tls";
|
||||||
};
|
};
|
||||||
"5443" = {
|
"5443" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -109,8 +109,8 @@ lib.mkIf false
|
||||||
numPorts = turnPortHigh - turnPortLow + 1;
|
numPorts = turnPortHigh - turnPortLow + 1;
|
||||||
in {
|
in {
|
||||||
protocol = [ "tcp" "udp" ];
|
protocol = [ "tcp" "udp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,14 +8,14 @@
|
||||||
{
|
{
|
||||||
sane.ports.ports."143" = {
|
sane.ports.ports."143" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-imap-imap.uninsane.org";
|
description = "colin-imap-imap.uninsane.org";
|
||||||
};
|
};
|
||||||
sane.ports.ports."993" = {
|
sane.ports.ports."993" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-imaps-imap.uninsane.org";
|
description = "colin-imaps-imap.uninsane.org";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# postfix config options: <https://www.postfix.org/postconf.5.html>
|
# postfix config options: <https://www.postfix.org/postconf.5.html>
|
||||||
|
|
||||||
{ config, lib, pkgs, ... }:
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
submissionOptions = {
|
submissionOptions = {
|
||||||
|
@ -56,7 +56,8 @@ in
|
||||||
|
|
||||||
sane.dns.zones."uninsane.org".inet = {
|
sane.dns.zones."uninsane.org".inet = {
|
||||||
MX."@" = "10 mx.uninsane.org.";
|
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:
|
# Sender Policy Framework:
|
||||||
# +mx => mail passes if it originated from the MX
|
# +mx => mail passes if it originated from the MX
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./nfs.nix
|
./nfs.nix
|
||||||
./sftpgo
|
./sftpgo.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
users.groups.export = {};
|
users.groups.export = {};
|
||||||
|
@ -12,10 +12,6 @@
|
||||||
device = "/var/media";
|
device = "/var/media";
|
||||||
options = [ "rbind" ];
|
options = [ "rbind" ];
|
||||||
};
|
};
|
||||||
fileSystems."/var/export/pub" = {
|
|
||||||
device = "/var/www/sites/uninsane.org/share";
|
|
||||||
options = [ "rbind" ];
|
|
||||||
};
|
|
||||||
# fileSystems."/var/export/playground" = {
|
# fileSystems."/var/export/playground" = {
|
||||||
# device = config.fileSystems."/mnt/persist/ext".device;
|
# device = config.fileSystems."/mnt/persist/ext".device;
|
||||||
# fsType = "btrfs";
|
# fsType = "btrfs";
|
||||||
|
@ -41,8 +37,7 @@
|
||||||
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||||
file.text = ''
|
file.text = ''
|
||||||
- media/ read-only: Videos, Music, Books, etc
|
- 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
|
- playground/ read-write: use it to share files with other users of this server
|
||||||
- pub/ read-only: content made to be shared with the www
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
# - could maybe be done with some mount option?
|
# - could maybe be done with some mount option?
|
||||||
|
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
lib.mkIf false #< TODO: remove nfs altogether! it's not exactly the most secure
|
|
||||||
{
|
{
|
||||||
services.nfs.server.enable = true;
|
services.nfs.server.enable = true;
|
||||||
|
|
||||||
|
@ -27,7 +26,7 @@ lib.mkIf false #< TODO: remove nfs altogether! it's not exactly the most secure
|
||||||
description = "NFS server portmapper";
|
description = "NFS server portmapper";
|
||||||
};
|
};
|
||||||
sane.ports.ports."2049" = {
|
sane.ports.ports."2049" = {
|
||||||
protocol = [ "tcp" "udp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
description = "NFS server";
|
description = "NFS server";
|
||||||
};
|
};
|
||||||
|
@ -52,23 +51,6 @@ lib.mkIf false #< TODO: remove nfs altogether! it's not exactly the most secure
|
||||||
services.nfs.server.mountdPort = 4002;
|
services.nfs.server.mountdPort = 4002;
|
||||||
services.nfs.server.statdPort = 4000;
|
services.nfs.server.statdPort = 4000;
|
||||||
|
|
||||||
services.nfs.extraConfig = ''
|
|
||||||
[nfsd]
|
|
||||||
# XXX: NFS over UDP REQUIRES SPECIAL CONFIG TO AVOID DATA LOSS.
|
|
||||||
# see `man 5 nfs`: "Using NFS over UDP on high-speed links".
|
|
||||||
# it's actually just a general property of UDP over IPv4 (IPv6 fixes it).
|
|
||||||
# both the client and the server should configure a shorter-than-default IPv4 fragment reassembly window to mitigate.
|
|
||||||
# OTOH, tunneling NFS over Wireguard also bypasses this weakness, because a mis-assembled packet would not have a valid signature.
|
|
||||||
udp=y
|
|
||||||
|
|
||||||
[exports]
|
|
||||||
# all export paths are relative to rootdir.
|
|
||||||
# for NFSv4, the export with fsid=0 behaves as `/` publicly,
|
|
||||||
# but NFSv3 implements no such feature.
|
|
||||||
# using `rootdir` instead of relying on `fsid=0` allows consistent export paths regardless of NFS proto version
|
|
||||||
rootdir=/var/export
|
|
||||||
'';
|
|
||||||
|
|
||||||
# format:
|
# format:
|
||||||
# fspoint visibility(options)
|
# fspoint visibility(options)
|
||||||
# options:
|
# options:
|
||||||
|
@ -103,20 +85,13 @@ lib.mkIf false #< TODO: remove nfs altogether! it's not exactly the most secure
|
||||||
in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})";
|
in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})";
|
||||||
in lib.concatStringsSep "\n" [
|
in lib.concatStringsSep "\n" [
|
||||||
(fmtExport {
|
(fmtExport {
|
||||||
export = "/";
|
export = "/var/export";
|
||||||
baseOpts = [ "crossmnt" "fsid=root" ];
|
baseOpts = [ "crossmnt" "fsid=root" ];
|
||||||
extraLanOpts = [ "ro" ];
|
extraLanOpts = [ "ro" ];
|
||||||
extraVpnOpts = [ "rw" "no_root_squash" ];
|
extraVpnOpts = [ "rw" "no_root_squash" ];
|
||||||
})
|
})
|
||||||
(fmtExport {
|
(fmtExport {
|
||||||
# provide /media as an explicit export. NFSv4 can transparently mount a subdir of an export, but NFSv3 can only mount paths which are exports.
|
export = "/var/export/playground";
|
||||||
export = "/media";
|
|
||||||
baseOpts = [ "crossmnt" ]; # TODO: is crossmnt needed here?
|
|
||||||
extraLanOpts = [ "ro" ];
|
|
||||||
extraVpnOpts = [ "rw" "no_root_squash" ];
|
|
||||||
})
|
|
||||||
(fmtExport {
|
|
||||||
export = "/playground";
|
|
||||||
baseOpts = [
|
baseOpts = [
|
||||||
"mountpoint"
|
"mountpoint"
|
||||||
"all_squash"
|
"all_squash"
|
||||||
|
|
186
hosts/by-name/servo/services/export/sftpgo.nix
Normal file
186
hosts/by-name/servo/services/export/sftpgo.nix
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
# docs:
|
||||||
|
# - <https://github.com/drakkan/sftpgo>
|
||||||
|
# - config options: <https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md>
|
||||||
|
# - config defaults: <https://github.com/drakkan/sftpgo/blob/main/sftpgo.json>
|
||||||
|
# - nixos options: <repo:nixos/nixpkgs:nixos/modules/services/web-apps/sftpgo.nix>
|
||||||
|
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
|
||||||
|
#
|
||||||
|
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
|
||||||
|
#
|
||||||
|
# TODO: change umask so sftpgo-created files default to 644.
|
||||||
|
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
||||||
|
|
||||||
|
|
||||||
|
{ config, lib, pkgs, sane-lib, ... }:
|
||||||
|
let
|
||||||
|
# user permissions:
|
||||||
|
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
||||||
|
# - "*" = grant all permissions
|
||||||
|
# - read-only perms:
|
||||||
|
# - "list" = list files and directories
|
||||||
|
# - "download"
|
||||||
|
# - rw perms:
|
||||||
|
# - "upload"
|
||||||
|
# - "overwrite" = allow uploads to replace existing files
|
||||||
|
# - "delete" = delete files and directories
|
||||||
|
# - "delete_files"
|
||||||
|
# - "delete_dirs"
|
||||||
|
# - "rename" = rename files and directories
|
||||||
|
# - "rename_files"
|
||||||
|
# - "rename_dirs"
|
||||||
|
# - "create_dirs"
|
||||||
|
# - "create_symlinks"
|
||||||
|
# - "chmod"
|
||||||
|
# - "chown"
|
||||||
|
# - "chtimes" = change atime/mtime (access and modification times)
|
||||||
|
#
|
||||||
|
# home_dir:
|
||||||
|
# - it seems (empirically) that a user can't cd above their home directory.
|
||||||
|
# though i don't have a reference for that in the docs.
|
||||||
|
authResponseSuccess = {
|
||||||
|
status = 1;
|
||||||
|
username = "anonymous";
|
||||||
|
expiration_date = 0;
|
||||||
|
home_dir = "/var/export";
|
||||||
|
# uid/gid 0 means to inherit sftpgo uid.
|
||||||
|
# - i.e. users can't read files which Linux user `sftpgo` can't read
|
||||||
|
# - uploaded files belong to Linux user `sftpgo`
|
||||||
|
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
|
||||||
|
uid = 0;
|
||||||
|
gid = 0;
|
||||||
|
# uid = 65534;
|
||||||
|
# gid = 65534;
|
||||||
|
max_sessions = 0;
|
||||||
|
# quota_*: 0 means to not use SFTP's quota system
|
||||||
|
quota_size = 0;
|
||||||
|
quota_files = 0;
|
||||||
|
permissions = {
|
||||||
|
"/" = [ "list" "download" ];
|
||||||
|
"/playground" = [
|
||||||
|
# read-only:
|
||||||
|
"list"
|
||||||
|
"download"
|
||||||
|
# write:
|
||||||
|
"upload"
|
||||||
|
"overwrite"
|
||||||
|
"delete"
|
||||||
|
"rename"
|
||||||
|
"create_dirs"
|
||||||
|
"create_symlinks"
|
||||||
|
# intentionally omitted:
|
||||||
|
# "chmod"
|
||||||
|
# "chown"
|
||||||
|
# "chtimes"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
upload_bandwidth = 0;
|
||||||
|
download_bandwidth = 0;
|
||||||
|
filters = {
|
||||||
|
allowed_ip = [];
|
||||||
|
denied_ip = [];
|
||||||
|
};
|
||||||
|
public_keys = [];
|
||||||
|
# other fields:
|
||||||
|
# ? groups
|
||||||
|
# ? virtual_folders
|
||||||
|
};
|
||||||
|
authResponseFail = {
|
||||||
|
username = "";
|
||||||
|
};
|
||||||
|
authSuccessJson = pkgs.writeText "sftp-auth-success.json" (builtins.toJSON authResponseSuccess);
|
||||||
|
authFailJson = pkgs.writeText "sftp-auth-fail.json" (builtins.toJSON authResponseFail);
|
||||||
|
unwrappedAuthProgram = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "sftpgo_external_auth_hook";
|
||||||
|
srcRoot = ./.;
|
||||||
|
pkgs = [ "coreutils" ];
|
||||||
|
};
|
||||||
|
authProgram = pkgs.writeShellScript "sftpgo-auth-hook" ''
|
||||||
|
${unwrappedAuthProgram}/bin/sftpgo_external_auth_hook ${authFailJson} ${authSuccessJson}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# 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
|
||||||
|
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
|
||||||
|
# - 50000-50100 is a common port range for this.
|
||||||
|
sane.ports.ports = {
|
||||||
|
"21" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "colin-FTP server";
|
||||||
|
};
|
||||||
|
} // (sane-lib.mapToAttrs
|
||||||
|
(port: {
|
||||||
|
name = builtins.toString port;
|
||||||
|
value = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "colin-FTP server data port range";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.range 50000 50100)
|
||||||
|
);
|
||||||
|
|
||||||
|
services.sftpgo = {
|
||||||
|
enable = true;
|
||||||
|
group = "export";
|
||||||
|
settings = {
|
||||||
|
ftpd = {
|
||||||
|
bindings = [
|
||||||
|
{
|
||||||
|
# binding this means any wireguard client can connect
|
||||||
|
address = "10.0.10.5";
|
||||||
|
port = 21;
|
||||||
|
debug = true;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# binding this means any LAN client can connect
|
||||||
|
address = "10.78.79.51";
|
||||||
|
port = 21;
|
||||||
|
debug = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
|
||||||
|
disable_active_mode = true;
|
||||||
|
hash_support = true;
|
||||||
|
passive_port_range = {
|
||||||
|
start = 50000;
|
||||||
|
end = 50100;
|
||||||
|
};
|
||||||
|
|
||||||
|
banner = ''
|
||||||
|
Welcome, friends, to Colin's read-only FTP server! Also available via NFS on the same host.
|
||||||
|
Username: "anonymous"
|
||||||
|
Password: "anonymous"
|
||||||
|
CONFIGURE YOUR CLIENT FOR "PASSIVE" mode, e.g. `ftp --passive uninsane.org`
|
||||||
|
Please let me know if anything's broken or not as it should be. Otherwise, browse and DL freely :)
|
||||||
|
'';
|
||||||
|
|
||||||
|
};
|
||||||
|
data_provider = {
|
||||||
|
driver = "memory";
|
||||||
|
external_auth_hook = "${authProgram}";
|
||||||
|
# track_quota:
|
||||||
|
# - 0: disable quota tracking
|
||||||
|
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
||||||
|
# - 2: quota is updated on every upload/delete, but only if user/folder has a quota restriction (default, i think)
|
||||||
|
# track_quota = 2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.sftpgo.extraGroups = [ "export" ];
|
||||||
|
|
||||||
|
systemd.services.sftpgo = {
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ReadOnlyPaths = [ "/var/export" ];
|
||||||
|
ReadWritePaths = [ "/var/export/playground" ];
|
||||||
|
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "20s";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,164 +0,0 @@
|
||||||
# docs:
|
|
||||||
# - <https://github.com/drakkan/sftpgo>
|
|
||||||
# - config options: <https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md>
|
|
||||||
# - config defaults: <https://github.com/drakkan/sftpgo/blob/main/sftpgo.json>
|
|
||||||
# - nixos options: <repo:nixos/nixpkgs:nixos/modules/services/web-apps/sftpgo.nix>
|
|
||||||
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
|
|
||||||
#
|
|
||||||
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
|
|
||||||
|
|
||||||
{ config, lib, pkgs, sane-lib, ... }:
|
|
||||||
let
|
|
||||||
external_auth_hook = pkgs.static-nix-shell.mkPython3Bin {
|
|
||||||
pname = "external_auth_hook";
|
|
||||||
srcRoot = ./.;
|
|
||||||
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
|
|
||||||
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
|
|
||||||
# - 50000-50100 is a common port range for this.
|
|
||||||
# 50000 is used by soulseek.
|
|
||||||
passiveStart = 50050;
|
|
||||||
passiveEnd = 50070;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
sane.ports.ports = {
|
|
||||||
"21" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "colin-FTP server";
|
|
||||||
};
|
|
||||||
"990" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "colin-FTPS server";
|
|
||||||
};
|
|
||||||
} // (sane-lib.mapToAttrs
|
|
||||||
(port: {
|
|
||||||
name = builtins.toString port;
|
|
||||||
value = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "colin-FTP server data port range";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
(lib.range passiveStart passiveEnd)
|
|
||||||
);
|
|
||||||
|
|
||||||
# use nginx/acme to produce a cert for FTPS
|
|
||||||
services.nginx.virtualHosts."ftp.uninsane.org" = {
|
|
||||||
addSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
};
|
|
||||||
sane.dns.zones."uninsane.org".inet.CNAME."ftp" = "native";
|
|
||||||
|
|
||||||
services.sftpgo = {
|
|
||||||
enable = true;
|
|
||||||
group = "export";
|
|
||||||
|
|
||||||
package = pkgs.sftpgo.overrideAttrs (upstream: {
|
|
||||||
patches = (upstream.patches or []) ++ [
|
|
||||||
# fix for compatibility with kodi:
|
|
||||||
# ftp LIST operation returns entries over-the-wire like:
|
|
||||||
# - dgrwxrwxr-x 1 ftp ftp 9 Apr 9 15:05 Videos
|
|
||||||
# however not all clients understand all mode bits (like that `g`, indicating SGID / group sticky bit).
|
|
||||||
# instead, only send mode bits which are well-understood.
|
|
||||||
# the full set of bits, from which i filter, is found here: <https://pkg.go.dev/io/fs#FileMode>
|
|
||||||
./safe_fileinfo.patch
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
ftpd = {
|
|
||||||
bindings = [
|
|
||||||
{
|
|
||||||
# binding this means any wireguard client can connect
|
|
||||||
address = "10.0.10.5";
|
|
||||||
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";
|
|
||||||
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 (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.
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
|
|
||||||
disable_active_mode = true;
|
|
||||||
hash_support = true;
|
|
||||||
passive_port_range = {
|
|
||||||
start = passiveStart;
|
|
||||||
end = passiveEnd;
|
|
||||||
};
|
|
||||||
|
|
||||||
certificate_file = "/var/lib/acme/ftp.uninsane.org/full.pem";
|
|
||||||
certificate_key_file = "/var/lib/acme/ftp.uninsane.org/key.pem";
|
|
||||||
|
|
||||||
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):
|
|
||||||
Username: "anonymous"
|
|
||||||
Password: "anonymous"
|
|
||||||
|
|
||||||
CONFIGURE YOUR CLIENT FOR "PASSIVE" MODE, e.g. `ftp --passive ftp.uninsane.org`.
|
|
||||||
Please let me know if anything's broken or not as it should be. Otherwise, browse and transfer freely :)
|
|
||||||
'';
|
|
||||||
|
|
||||||
};
|
|
||||||
data_provider = {
|
|
||||||
driver = "memory";
|
|
||||||
external_auth_hook = "${external_auth_hook}/bin/external_auth_hook";
|
|
||||||
# track_quota:
|
|
||||||
# - 0: disable quota tracking
|
|
||||||
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
|
||||||
# - 2: quota is updated on every upload/delete, but only if user/folder has a quota restriction (default, i think)
|
|
||||||
# track_quota = 2;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.sftpgo.extraGroups = [
|
|
||||||
"export"
|
|
||||||
"media"
|
|
||||||
"nginx" # to access certs
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.services.sftpgo = {
|
|
||||||
after = [ "network-online.target" ];
|
|
||||||
wants = [ "network-online.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
ReadWritePaths = [ "/var/export" ];
|
|
||||||
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "20s";
|
|
||||||
UMask = lib.mkForce "0002";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,171 +0,0 @@
|
||||||
#!/usr/bin/env nix-shell
|
|
||||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.passlib ])"
|
|
||||||
# vim: set filetype=python :
|
|
||||||
#
|
|
||||||
# available environment variables:
|
|
||||||
# - SFTPGO_AUTHD_USERNAME
|
|
||||||
# - SFTPGO_AUTHD_USER
|
|
||||||
# - SFTPGO_AUTHD_IP
|
|
||||||
# - SFTPGO_AUTHD_PROTOCOL = { "DAV", "FTP", "HTTP", "SSH" }
|
|
||||||
# - SFTPGO_AUTHD_PASSWORD
|
|
||||||
# - SFTPGO_AUTHD_PUBLIC_KEY
|
|
||||||
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
|
||||||
# - SFTPGO_AUTHD_TLS_CERT
|
|
||||||
#
|
|
||||||
# user permissions:
|
|
||||||
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
|
||||||
# - "*" = grant all permissions
|
|
||||||
# - read-only perms:
|
|
||||||
# - "list" = list files and directories
|
|
||||||
# - "download"
|
|
||||||
# - rw perms:
|
|
||||||
# - "upload"
|
|
||||||
# - "overwrite" = allow uploads to replace existing files
|
|
||||||
# - "delete" = delete files and directories
|
|
||||||
# - "delete_files"
|
|
||||||
# - "delete_dirs"
|
|
||||||
# - "rename" = rename files and directories
|
|
||||||
# - "rename_files"
|
|
||||||
# - "rename_dirs"
|
|
||||||
# - "create_dirs"
|
|
||||||
# - "create_symlinks"
|
|
||||||
# - "chmod"
|
|
||||||
# - "chown"
|
|
||||||
# - "chtimes" = change atime/mtime (access and modification times)
|
|
||||||
#
|
|
||||||
# home_dir:
|
|
||||||
# - it seems (empirically) that a user can't cd above their home directory.
|
|
||||||
# though i don't have a reference for that in the docs.
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import passlib.hosts
|
|
||||||
|
|
||||||
from hmac import compare_digest
|
|
||||||
|
|
||||||
authFail = dict(username="")
|
|
||||||
|
|
||||||
PERM_DENY = []
|
|
||||||
PERM_LIST = [ "list" ]
|
|
||||||
PERM_RO = [ "list", "download" ]
|
|
||||||
PERM_RW = [
|
|
||||||
# read-only:
|
|
||||||
"list",
|
|
||||||
"download",
|
|
||||||
# write:
|
|
||||||
"upload",
|
|
||||||
"overwrite",
|
|
||||||
"delete",
|
|
||||||
"rename",
|
|
||||||
"create_dirs",
|
|
||||||
"create_symlinks",
|
|
||||||
# intentionally omitted:
|
|
||||||
# "chmod",
|
|
||||||
# "chown",
|
|
||||||
# "chtimes",
|
|
||||||
]
|
|
||||||
|
|
||||||
TRUSTED_CREDS = [
|
|
||||||
# /etc/shadow style creds.
|
|
||||||
# mkpasswd -m sha-512
|
|
||||||
# $<method>$<salt>$<hash>
|
|
||||||
"$6$Zq3c2u4ghUH4S6EP$pOuRt13sEKfX31OqPbbd1LuhS21C9MICMc94iRdTAgdAcJ9h95gQH/6Jf6Ie4Obb0oxQtojRJ1Pd/9QHOlFMW." #< m. rocket boy
|
|
||||||
]
|
|
||||||
|
|
||||||
def mkAuthOk(username: str, permissions: dict[str, list[str]]) -> dict:
|
|
||||||
return dict(
|
|
||||||
status = 1,
|
|
||||||
username = username,
|
|
||||||
expiration_date = 0,
|
|
||||||
home_dir = "/var/export",
|
|
||||||
# uid/gid 0 means to inherit sftpgo uid.
|
|
||||||
# - i.e. users can't read files which Linux user `sftpgo` can't read
|
|
||||||
# - uploaded files belong to Linux user `sftpgo`
|
|
||||||
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
|
|
||||||
uid = 0,
|
|
||||||
gid = 0,
|
|
||||||
# uid = 65534,
|
|
||||||
# gid = 65534,
|
|
||||||
max_sessions = 0,
|
|
||||||
# quota_*: 0 means to not use SFTP's quota system
|
|
||||||
quota_size = 0,
|
|
||||||
quota_files = 0,
|
|
||||||
permissions = permissions,
|
|
||||||
upload_bandwidth = 0,
|
|
||||||
download_bandwidth = 0,
|
|
||||||
filters = dict(
|
|
||||||
allowed_ip = [],
|
|
||||||
denied_ip = [],
|
|
||||||
),
|
|
||||||
public_keys = [],
|
|
||||||
# other fields:
|
|
||||||
# ? groups
|
|
||||||
# ? virtual_folders
|
|
||||||
)
|
|
||||||
|
|
||||||
def isLan(ip: str) -> bool:
|
|
||||||
return ip.startswith("10.78.76.") \
|
|
||||||
or ip.startswith("10.78.77.") \
|
|
||||||
or ip.startswith("10.78.78.") \
|
|
||||||
or ip.startswith("10.78.79.")
|
|
||||||
|
|
||||||
def isWireguard(ip: str) -> bool:
|
|
||||||
return ip.startswith("10.0.10.")
|
|
||||||
|
|
||||||
def isTrustedCred(password: str) -> bool:
|
|
||||||
for cred in TRUSTED_CREDS:
|
|
||||||
if passlib.hosts.linux_context.verify(password, cred):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getAuthResponse(ip: str, username: str, password: str) -> dict:
|
|
||||||
"""
|
|
||||||
return a sftpgo auth response either denying the user or approving them
|
|
||||||
with a set of permissions.
|
|
||||||
"""
|
|
||||||
if isTrustedCred(password) and username != "colin":
|
|
||||||
# allow r/w access from those with a special token
|
|
||||||
return mkAuthOk(username, permissions = {
|
|
||||||
"/": PERM_RW,
|
|
||||||
"/playground": PERM_RW,
|
|
||||||
"/pub": PERM_RO,
|
|
||||||
})
|
|
||||||
if isWireguard(ip):
|
|
||||||
# allow any user from wireguard
|
|
||||||
return mkAuthOk(username, permissions = {
|
|
||||||
"/": PERM_RW,
|
|
||||||
"/playground": PERM_RW,
|
|
||||||
"/pub": PERM_RO,
|
|
||||||
})
|
|
||||||
if isLan(ip):
|
|
||||||
if username == "anonymous":
|
|
||||||
# allow anonymous users on the LAN
|
|
||||||
return mkAuthOk("anonymous", permissions = {
|
|
||||||
"/": PERM_RO,
|
|
||||||
"/playground": PERM_RW,
|
|
||||||
"/pub": 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,
|
|
||||||
"/pub": PERM_RO,
|
|
||||||
# "/README.md": PERM_RO, #< does not work
|
|
||||||
})
|
|
||||||
|
|
||||||
return authFail
|
|
||||||
|
|
||||||
def main():
|
|
||||||
ip = os.environ.get("SFTPGO_AUTHD_IP", "")
|
|
||||||
username = os.environ.get("SFTPGO_AUTHD_USERNAME", "")
|
|
||||||
password = os.environ.get("SFTPGO_AUTHD_PASSWORD", "")
|
|
||||||
resp = getAuthResponse(ip, username, password)
|
|
||||||
print(json.dumps(resp))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,32 +0,0 @@
|
||||||
diff --git a/internal/ftpd/handler.go b/internal/ftpd/handler.go
|
|
||||||
index 036c3977..33211261 100644
|
|
||||||
--- a/internal/ftpd/handler.go
|
|
||||||
+++ b/internal/ftpd/handler.go
|
|
||||||
@@ -169,7 +169,7 @@ func (c *Connection) Stat(name string) (os.FileInfo, error) {
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
- return fi, nil
|
|
||||||
+ return vfs.NewFileInfo(name, fi.IsDir(), fi.Size(), fi.ModTime(), false), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of this connection
|
|
||||||
@@ -315,7 +315,17 @@ func (c *Connection) ReadDir(name string) (ftpserver.DirLister, error) {
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
- return c.ListDir(name)
|
|
||||||
+ lister, err := c.ListDir(name)
|
|
||||||
+ if err != nil {
|
|
||||||
+ return nil, err
|
|
||||||
+ }
|
|
||||||
+ return &patternDirLister{
|
|
||||||
+ DirLister: lister,
|
|
||||||
+ pattern: "*",
|
|
||||||
+ lastCommand: c.clientContext.GetLastCommand(),
|
|
||||||
+ dirName: name,
|
|
||||||
+ connectionPath: c.clientContext.Path(),
|
|
||||||
+ }, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHandle implements ClientDriverExtentionFileTransfer
|
|
23
hosts/by-name/servo/services/export/sftpgo_external_auth_hook
Executable file
23
hosts/by-name/servo/services/export/sftpgo_external_auth_hook
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p coreutils
|
||||||
|
# vim: set filetype=bash :
|
||||||
|
#
|
||||||
|
# available environment variables:
|
||||||
|
# - SFTPGO_AUTHD_USERNAME
|
||||||
|
# - SFTPGO_AUTHD_USER
|
||||||
|
# - SFTPGO_AUTHD_IP
|
||||||
|
# - SFTPGO_AUTHD_PROTOCOL = { "DAV", "FTP", "HTTP", "SSH" }
|
||||||
|
# - SFTPGO_AUTHD_PASSWORD
|
||||||
|
# - SFTPGO_AUTHD_PUBLIC_KEY
|
||||||
|
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
||||||
|
# - SFTPGO_AUTHD_TLS_CERT
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# call with <script_name> /path/to/fail/response.json /path/to/success/response.json
|
||||||
|
|
||||||
|
|
||||||
|
if [ "$SFTPGO_AUTHD_USERNAME" = "anonymous" ]; then
|
||||||
|
cat "$2"
|
||||||
|
else
|
||||||
|
cat "$1"
|
||||||
|
fi
|
|
@ -50,15 +50,9 @@
|
||||||
ENABLE_CAPTCHA = true;
|
ENABLE_CAPTCHA = true;
|
||||||
NOREPLY_ADDRESS = "noreply.anonymous.git@uninsane.org";
|
NOREPLY_ADDRESS = "noreply.anonymous.git@uninsane.org";
|
||||||
};
|
};
|
||||||
session = {
|
session.COOKIE_SECURE = true;
|
||||||
COOKIE_SECURE = true;
|
|
||||||
# keep me logged in for 30 days
|
|
||||||
SESSION_LIFE_TIME = 60 * 60 * 24 * 30;
|
|
||||||
};
|
|
||||||
repository = {
|
repository = {
|
||||||
DEFAULT_BRANCH = "master";
|
DEFAULT_BRANCH = "master";
|
||||||
ENABLE_PUSH_CREATE_USER = true;
|
|
||||||
ENABLE_PUSH_CREATE_ORG = true;
|
|
||||||
};
|
};
|
||||||
other = {
|
other = {
|
||||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
|
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
|
||||||
|
@ -96,8 +90,6 @@
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.openssh.settings.UsePAM = true; #< required for `git` user to authenticate
|
|
||||||
|
|
||||||
# hosted git (web view and for `git <cmd>` use
|
# hosted git (web view and for `git <cmd>` use
|
||||||
# TODO: enable publog?
|
# TODO: enable publog?
|
||||||
services.nginx.virtualHosts."git.uninsane.org" = {
|
services.nginx.virtualHosts."git.uninsane.org" = {
|
||||||
|
@ -133,7 +125,7 @@
|
||||||
sane.ports.ports."22" = {
|
sane.ports.ports."22" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
visibleTo.doof = true;
|
visibleTo.wan = true;
|
||||||
description = "colin-git@git.uninsane.org";
|
description = "colin-git@git.uninsane.org";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
--ignore-panel=HOSTS \
|
--ignore-panel=HOSTS \
|
||||||
--ws-url=wss://sink.uninsane.org:443/ws \
|
--ws-url=wss://sink.uninsane.org:443/ws \
|
||||||
--port=7890 \
|
--port=7890 \
|
||||||
-o /var/lib/goaccess/index.html
|
-o /var/lib/uninsane/sink/index.html
|
||||||
'';
|
'';
|
||||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||||
Type = "simple";
|
Type = "simple";
|
||||||
|
@ -28,19 +28,17 @@
|
||||||
RestartSec = "10s";
|
RestartSec = "10s";
|
||||||
|
|
||||||
# hardening
|
# hardening
|
||||||
# TODO: run as `goaccess` user and add `goaccess` user to group `nginx`.
|
WorkingDirectory = "/tmp";
|
||||||
NoNewPrivileges = true;
|
NoNewPrivileges = true;
|
||||||
PrivateDevices = "yes";
|
|
||||||
PrivateTmp = true;
|
PrivateTmp = true;
|
||||||
ProtectHome = "read-only";
|
ProtectHome = "read-only";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @privileged @reboot @resources @setuid @swap @raw-io";
|
||||||
|
ReadOnlyPaths = "/";
|
||||||
|
ReadWritePaths = [ "/proc/self" "/var/lib/uninsane/sink" ];
|
||||||
|
PrivateDevices = "yes";
|
||||||
ProtectKernelModules = "yes";
|
ProtectKernelModules = "yes";
|
||||||
ProtectKernelTunables = "yes";
|
ProtectKernelTunables = "yes";
|
||||||
ProtectSystem = "strict";
|
|
||||||
ReadOnlyPaths = [ "/var/log/nginx" ];
|
|
||||||
ReadWritePaths = [ "/proc/self" "/var/lib/goaccess" ];
|
|
||||||
StateDirectory = "goaccess";
|
|
||||||
SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @privileged @reboot @resources @setuid @swap @raw-io";
|
|
||||||
WorkingDirectory = "/var/lib/goaccess";
|
|
||||||
};
|
};
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
@ -51,7 +49,7 @@
|
||||||
addSSL = true;
|
addSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
# inherit kTLS;
|
# inherit kTLS;
|
||||||
root = "/var/lib/goaccess";
|
root = "/var/lib/uninsane/sink";
|
||||||
|
|
||||||
locations."/ws" = {
|
locations."/ws" = {
|
||||||
proxyPass = "http://127.0.0.1:7890";
|
proxyPass = "http://127.0.0.1:7890";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.byStore.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
|
@ -12,8 +12,6 @@
|
||||||
systemd.services.jackett.serviceConfig = {
|
systemd.services.jackett.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
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 jackett to listen on the public interfaces
|
# patch jackett to listen on the public interfaces
|
||||||
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
|
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
|
||||||
};
|
};
|
||||||
|
@ -24,7 +22,8 @@
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
# inherit kTLS;
|
# inherit kTLS;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9117";
|
# proxyPass = "http://ovpns.uninsane.org:9117";
|
||||||
|
proxyPass = "http://10.0.1.6:9117";
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
# Jellyfin multimedia server
|
# Jellyfin multimedia server
|
||||||
# this is mostly taken from the official jellfin.org docs
|
# this is mostly taken from the official jellfin.org docs
|
||||||
services.nginx.virtualHosts."jelly.uninsane.org" = {
|
services.nginx.virtualHosts."jelly.uninsane.org" = {
|
||||||
forceSSL = true;
|
addSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
# inherit kTLS;
|
# inherit kTLS;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ in
|
||||||
services.komga.port = 11319; # chosen at random
|
services.komga.port = 11319; # chosen at random
|
||||||
|
|
||||||
services.nginx.virtualHosts."komga.uninsane.org" = {
|
services.nginx.virtualHosts."komga.uninsane.org" = {
|
||||||
forceSSL = true;
|
addSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://127.0.0.1:${builtins.toString port}";
|
proxyPass = "http://127.0.0.1:${builtins.toString port}";
|
||||||
|
|
|
@ -10,21 +10,16 @@ let
|
||||||
uiPort = 1234; # default ui port is 1234
|
uiPort = 1234; # default ui port is 1234
|
||||||
backendPort = 8536; # default backend port is 8536
|
backendPort = 8536; # default backend port is 8536
|
||||||
#^ i guess the "backend" port is used for federation?
|
#^ i guess the "backend" port is used for federation?
|
||||||
pict-rs = pkgs.pict-rs;
|
pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
|
||||||
# pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
|
# as of v 0.4.2, all non-GIF video is forcibly transcoded.
|
||||||
# # as of v0.4.2, all non-GIF video is forcibly transcoded.
|
# that breaks lemmy, because of the request latency.
|
||||||
# # that breaks lemmy, because of the request latency.
|
# and it eats up hella CPU.
|
||||||
# # and it eats up hella CPU.
|
# pict-rs is iffy around video altogether: mp4 seems the best supported.
|
||||||
# # pict-rs is iffy around video altogether: mp4 seems the best supported.
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
# # XXX: this patch no longer applies after 0.5.10 -> 0.5.11 update.
|
substituteInPlace src/validate.rs \
|
||||||
# # git log is hard to parse, but *suggests* that video is natively supported
|
--replace 'if transcode_options.needs_reencode() {' 'if false {'
|
||||||
# # better than in the 0.4.2 days, e.g. 5fd59fc5b42d31559120dc28bfef4e5002fb509e
|
'';
|
||||||
# # "Change commandline flag to allow disabling video, since it is enabled by default"
|
});
|
||||||
# postPatch = (upstream.postPatch or "") + ''
|
|
||||||
# substituteInPlace src/validate.rs \
|
|
||||||
# --replace-fail 'if transcode_options.needs_reencode() {' 'if false {'
|
|
||||||
# '';
|
|
||||||
# });
|
|
||||||
in {
|
in {
|
||||||
services.lemmy = {
|
services.lemmy = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
|
|
||||||
let
|
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;
|
lowerName = lib.toLower name;
|
||||||
in {
|
in {
|
||||||
# XXX sasl: appservice doesn't support NickServ identification (only SASL, or PASS if sasl = false)
|
# 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 = {
|
botConfig = {
|
||||||
# bot has no presence in IRC channel; only real Matrix users
|
# bot has no presence in IRC channel; only real Matrix users
|
||||||
enabled = false;
|
enabled = false;
|
||||||
|
@ -155,10 +156,6 @@ in
|
||||||
# - #sxmo-offtopic
|
# - #sxmo-offtopic
|
||||||
};
|
};
|
||||||
"irc.rizon.net" = ircServer { name = "Rizon"; };
|
"irc.rizon.net" = ircServer { name = "Rizon"; };
|
||||||
"wigle.net" = ircServer {
|
|
||||||
name = "WiGLE";
|
|
||||||
ssl = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,14 +17,14 @@ in
|
||||||
sane.ports.ports."80" = {
|
sane.ports.ports."80" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
visibleTo.ovpns = true; # so that letsencrypt can procure a cert for the mx record
|
visibleTo.wan = true;
|
||||||
visibleTo.doof = true;
|
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
|
||||||
description = "colin-http-uninsane.org";
|
description = "colin-http-uninsane.org";
|
||||||
};
|
};
|
||||||
sane.ports.ports."443" = {
|
sane.ports.ports."443" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
visibleTo.doof = true;
|
visibleTo.wan = true;
|
||||||
description = "colin-https-uninsane.org";
|
description = "colin-https-uninsane.org";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,16 +89,6 @@ in
|
||||||
disable_symlinks on;
|
disable_symlinks on;
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
locations."/share/Milkbags/" = {
|
|
||||||
alias = "/var/media/Videos/Milkbags/";
|
|
||||||
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
|
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
|
||||||
locations."= /.well-known/matrix/server".extraConfig =
|
locations."= /.well-known/matrix/server".extraConfig =
|
||||||
|
|
21
hosts/by-name/servo/services/nixserve.nix
Normal file
21
hosts/by-name/servo/services/nixserve.nix
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.nginx.virtualHosts."nixcache.uninsane.org" = {
|
||||||
|
addSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
# inherit kTLS;
|
||||||
|
# serverAliases = [ "nixcache" ];
|
||||||
|
locations."/".extraConfig = ''
|
||||||
|
proxy_pass http://localhost:${toString config.services.nix-serve.port};
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||||
|
|
||||||
|
sane.services.nixserve.enable = true;
|
||||||
|
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
|
||||||
|
}
|
|
@ -86,7 +86,7 @@ in
|
||||||
sane.ports.ports."${builtins.toString altPort}" = {
|
sane.ports.ports."${builtins.toString altPort}" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
visibleTo.doof = true;
|
visibleTo.wan = true;
|
||||||
description = "colin-ntfy.uninsane.org";
|
description = "colin-ntfy.uninsane.org";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ in
|
||||||
sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: {
|
sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: {
|
||||||
"${builtins.toString port}" = {
|
"${builtins.toString port}" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-notification-waiter-${builtins.toString (port - portLow + 1)}-of-${builtins.toString numPorts}";
|
description = "colin-notification-waiter-${builtins.toString (port - portLow + 1)}-of-${builtins.toString numPorts}";
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -25,7 +25,7 @@ in
|
||||||
|
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
url: [host: "fed.uninsane.org", scheme: "https", port: 443],
|
url: [host: "fed.uninsane.org", scheme: "https", port: 443],
|
||||||
http: [ip: {127, 0, 0, 1}, port: 4040]
|
http: [ip: {127, 0, 0, 1}, port: 4000]
|
||||||
# secret_key_base: "{secrets.pleroma.secret_key_base}",
|
# secret_key_base: "{secrets.pleroma.secret_key_base}",
|
||||||
# signing_salt: "{secrets.pleroma.signing_salt}"
|
# signing_salt: "{secrets.pleroma.signing_salt}"
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ in
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
# inherit kTLS;
|
# inherit kTLS;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://127.0.0.1:4040";
|
proxyPass = "http://127.0.0.1:4000";
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
|
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
|
|
|
@ -61,42 +61,42 @@ in
|
||||||
];
|
];
|
||||||
sane.ports.ports."5000" = {
|
sane.ports.ports."5000" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-prosody-fileshare-proxy65";
|
description = "colin-xmpp-prosody-fileshare-proxy65";
|
||||||
};
|
};
|
||||||
sane.ports.ports."5222" = {
|
sane.ports.ports."5222" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-client-to-server";
|
description = "colin-xmpp-client-to-server";
|
||||||
};
|
};
|
||||||
sane.ports.ports."5223" = {
|
sane.ports.ports."5223" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||||
};
|
};
|
||||||
sane.ports.ports."5269" = {
|
sane.ports.ports."5269" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-server-to-server";
|
description = "colin-xmpp-server-to-server";
|
||||||
};
|
};
|
||||||
sane.ports.ports."5270" = {
|
sane.ports.ports."5270" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||||
};
|
};
|
||||||
sane.ports.ports."5280" = {
|
sane.ports.ports."5280" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-bosh";
|
description = "colin-xmpp-bosh";
|
||||||
};
|
};
|
||||||
sane.ports.ports."5281" = {
|
sane.ports.ports."5281" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.doof = true;
|
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-xmpp-prosody-https"; # necessary?
|
description = "colin-xmpp-prosody-https"; # necessary?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,10 @@
|
||||||
#
|
#
|
||||||
# config precedence (higher precedence overrules lower precedence):
|
# config precedence (higher precedence overrules lower precedence):
|
||||||
# - Default Values < Environment Variables < YAML Configuraiton File < Command Line Arguments
|
# - Default Values < Environment Variables < YAML Configuraiton File < Command Line Arguments
|
||||||
#
|
{ config, lib, ... }:
|
||||||
# debugging:
|
|
||||||
# - soulseek is just *flaky*. if you see e.g. DNS errors, even though you can't replicate them via `dig` or `getent ahostsv4`, just give it 10 minutes to work out:
|
|
||||||
# - "Soulseek.AddressException: Failed to resolve address 'vps.slsknet.org': Resource temporarily unavailable"
|
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.byStore.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "slskd"; group = "media"; path = "/var/lib/slskd"; method = "bind"; }
|
{ user = "slskd"; group = "slskd"; path = "/var/lib/slskd"; method = "bind"; }
|
||||||
];
|
];
|
||||||
sops.secrets."slskd_env" = {
|
sops.secrets."slskd_env" = {
|
||||||
owner = config.users.users.slskd.name;
|
owner = config.users.users.slskd.name;
|
||||||
|
@ -20,9 +15,10 @@
|
||||||
|
|
||||||
users.users.slskd.extraGroups = [ "media" ];
|
users.users.slskd.extraGroups = [ "media" ];
|
||||||
|
|
||||||
sane.ports.ports."50300" = {
|
sane.ports.ports."50000" = {
|
||||||
protocol = [ "tcp" ];
|
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";
|
description = "colin-soulseek";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,14 +28,12 @@
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:5030";
|
proxyPass = "http://10.0.1.6:5001";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.slskd.enable = true;
|
services.slskd.enable = true;
|
||||||
services.slskd.domain = null; # i'll manage nginx for it
|
|
||||||
services.slskd.group = "media";
|
|
||||||
# env file, for auth (SLSKD_SLSK_PASSWORD, SLSKD_SLSK_USERNAME)
|
# env file, for auth (SLSKD_SLSK_PASSWORD, SLSKD_SLSK_USERNAME)
|
||||||
services.slskd.environmentFile = config.sops.secrets.slskd_env.path;
|
services.slskd.environmentFile = config.sops.secrets.slskd_env.path;
|
||||||
services.slskd.settings = {
|
services.slskd.settings = {
|
||||||
|
@ -68,12 +62,13 @@
|
||||||
# flags.volatile = true; # store searches and active transfers in RAM (completed transfers still go to disk). rec for btrfs/zfs
|
# flags.volatile = true; # store searches and active transfers in RAM (completed transfers still go to disk). rec for btrfs/zfs
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.slskd.serviceConfig = {
|
systemd.services.slskd = {
|
||||||
|
serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
|
||||||
|
|
||||||
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
|
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
|
||||||
RestartSec = "60s";
|
RestartSec = "60s";
|
||||||
|
Group = "media";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,65 +22,6 @@ let
|
||||||
--replace-fail 'set(TR_USER_AGENT_PREFIX "''${TR_SEMVER}")' 'set(TR_USER_AGENT_PREFIX "3.00")'
|
--replace-fail 'set(TR_USER_AGENT_PREFIX "''${TR_SEMVER}")' 'set(TR_USER_AGENT_PREFIX "3.00")'
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
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
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.byStore.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
|
@ -105,8 +46,8 @@ in
|
||||||
# DOCUMENTATION/options list: <https://github.com/transmission/transmission/blob/main/docs/Editing-Configuration-Files.md#options>
|
# 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.
|
# 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.
|
# 0.0.0.0 => allow rpc from any host: we gate it via firewall and auth requirement
|
||||||
rpc-bind-address = config.sane.netns.ovpns.netnsVethIpv4;
|
rpc-bind-address = "0.0.0.0";
|
||||||
#rpc-host-whitelist = "bt.uninsane.org";
|
#rpc-host-whitelist = "bt.uninsane.org";
|
||||||
#rpc-whitelist = "*.*.*.*";
|
#rpc-whitelist = "*.*.*.*";
|
||||||
rpc-authentication-required = true;
|
rpc-authentication-required = true;
|
||||||
|
@ -116,10 +57,6 @@ in
|
||||||
rpc-password = "{503fc8928344f495efb8e1f955111ca5c862ce0656SzQnQ5";
|
rpc-password = "{503fc8928344f495efb8e1f955111ca5c862ce0656SzQnQ5";
|
||||||
rpc-whitelist-enabled = false;
|
rpc-whitelist-enabled = false;
|
||||||
|
|
||||||
# force behind ovpns in case the NetworkNamespace fails somehow
|
|
||||||
bind-address-ipv4 = config.sane.netns.ovpns.netnsPubIpv4;
|
|
||||||
port-forwarding-enabled = false;
|
|
||||||
|
|
||||||
# hopefully, make the downloads world-readable
|
# hopefully, make the downloads world-readable
|
||||||
# umask = 0; #< default is 2: i.e. deny writes from world
|
# umask = 0; #< default is 2: i.e. deny writes from world
|
||||||
|
|
||||||
|
@ -135,23 +72,11 @@ in
|
||||||
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
|
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
|
||||||
anti-brute-force-enabled = false;
|
anti-brute-force-enabled = false;
|
||||||
|
|
||||||
inherit download-dir;
|
download-dir = "/var/media";
|
||||||
incomplete-dir = "${download-dir}/incomplete";
|
incomplete-dir = "/var/media/incomplete";
|
||||||
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
|
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
|
||||||
|
# TODO: uncomment this line!
|
||||||
incomplete-dir-enabled = false;
|
incomplete-dir-enabled = false;
|
||||||
|
|
||||||
# env vars available in script:
|
|
||||||
# - TR_APP_VERSION - Transmission's short version string, e.g. `4.0.0`
|
|
||||||
# - TR_TIME_LOCALTIME
|
|
||||||
# - TR_TORRENT_BYTES_DOWNLOADED - Number of bytes that were downloaded for this torrent
|
|
||||||
# - TR_TORRENT_DIR - Location of the downloaded data
|
|
||||||
# - TR_TORRENT_HASH - The torrent's info hash
|
|
||||||
# - TR_TORRENT_ID
|
|
||||||
# - TR_TORRENT_LABELS - A comma-delimited list of the torrent's labels
|
|
||||||
# - TR_TORRENT_NAME - Name of torrent (not filename)
|
|
||||||
# - TR_TORRENT_TRACKERS - A comma-delimited list of the torrent's trackers' announce URLs
|
|
||||||
script-torrent-done-enabled = true;
|
|
||||||
script-torrent-done-filename = "${torrent-done}/bin/torrent-done";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
||||||
|
@ -159,11 +84,8 @@ in
|
||||||
systemd.services.transmission.serviceConfig = {
|
systemd.services.transmission.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
|
||||||
|
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
RestartSec = "30s";
|
RestartSec = "30s";
|
||||||
BindPaths = [ "/var/media" ]; #< so it can move completed torrents into the media library
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# service to automatically backup torrents i add to transmission
|
# service to automatically backup torrents i add to transmission
|
||||||
|
@ -189,14 +111,14 @@ in
|
||||||
# inherit kTLS;
|
# inherit kTLS;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
# proxyPass = "http://ovpns.uninsane.org:9091";
|
# 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.dns.zones."uninsane.org".inet.CNAME."bt" = "native";
|
||||||
sane.ports.ports."51413" = {
|
sane.ports.ports."51413" = {
|
||||||
protocol = [ "tcp" "udp" ];
|
protocol = [ "tcp" "udp" ];
|
||||||
# visibleTo.ovpns = true; #< not needed: it runs in the ovpns namespace
|
visibleTo.ovpn = true;
|
||||||
description = "colin-bittorrent";
|
description = "colin-bittorrent";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,24 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
dyn-dns = config.sane.services.dyn-dns;
|
|
||||||
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
|
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
|
||||||
in
|
bindOvpn = "10.0.1.5";
|
||||||
|
in lib.mkMerge [
|
||||||
{
|
{
|
||||||
|
services.trust-dns.enable = true;
|
||||||
|
|
||||||
|
# don't bind to IPv6 until i explicitly test that stack
|
||||||
|
services.trust-dns.settings.listen_addrs_ipv6 = [];
|
||||||
|
services.trust-dns.quiet = true;
|
||||||
|
# FIXME(2023/11/26): services.trust-dns.debug doesn't log requests: use RUST_LOG=debug env for that.
|
||||||
|
# - see: <https://github.com/hickory-dns/hickory-dns/issues/2082>
|
||||||
|
# services.trust-dns.debug = true;
|
||||||
|
|
||||||
sane.ports.ports."53" = {
|
sane.ports.ports."53" = {
|
||||||
protocol = [ "udp" "tcp" ];
|
protocol = [ "udp" "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
# visibleTo.wan = true;
|
visibleTo.wan = true;
|
||||||
visibleTo.ovpns = true;
|
visibleTo.ovpn = true;
|
||||||
visibleTo.doof = true;
|
|
||||||
description = "colin-dns-hosting";
|
description = "colin-dns-hosting";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,7 +47,6 @@ in
|
||||||
CNAME."native" = "%CNAMENATIVE%";
|
CNAME."native" = "%CNAMENATIVE%";
|
||||||
A."@" = "%ANATIVE%";
|
A."@" = "%ANATIVE%";
|
||||||
A."servo.wan" = "%AWAN%";
|
A."servo.wan" = "%AWAN%";
|
||||||
A."servo.doof" = "%ADOOF%";
|
|
||||||
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
||||||
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip;
|
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||||
|
|
||||||
|
@ -47,9 +54,9 @@ in
|
||||||
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
|
# 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.
|
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
|
||||||
A."ns1" = "%ANATIVE%";
|
A."ns1" = "%ANATIVE%";
|
||||||
A."ns2" = "%ADOOF%";
|
A."ns2" = "185.157.162.178";
|
||||||
A."ns3" = "%AOVPNS%";
|
A."ns3" = "185.157.162.178";
|
||||||
A."ovpns" = "%AOVPNS%";
|
A."ovpns" = "185.157.162.178";
|
||||||
NS."@" = [
|
NS."@" = [
|
||||||
"ns1.uninsane.org."
|
"ns1.uninsane.org."
|
||||||
"ns2.uninsane.org."
|
"ns2.uninsane.org."
|
||||||
|
@ -59,109 +66,145 @@ in
|
||||||
|
|
||||||
services.trust-dns.settings.zones = [ "uninsane.org" ];
|
services.trust-dns.settings.zones = [ "uninsane.org" ];
|
||||||
|
|
||||||
|
# TODO: can i transform this into some sort of service group?
|
||||||
|
# have `systemctl restart trust-dns.service` restart all the individual services?
|
||||||
|
systemd.services.trust-dns.serviceConfig = {
|
||||||
|
DynamicUser = lib.mkForce false;
|
||||||
|
User = "trust-dns";
|
||||||
|
Group = "trust-dns";
|
||||||
|
wantedBy = lib.mkForce [];
|
||||||
|
};
|
||||||
|
systemd.services.trust-dns.enable = false;
|
||||||
|
|
||||||
networking.nat.enable = true; #< TODO: try removing this?
|
users.groups.trust-dns = {};
|
||||||
# networking.nat.extraCommands = ''
|
users.users.trust-dns = {
|
||||||
# # redirect incoming DNS requests from LAN addresses
|
group = "trust-dns";
|
||||||
# # to the LAN-specialized DNS service
|
isSystemUser = true;
|
||||||
# # 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.dyn-dns.restartOnChange = [ "trust-dns.service" ];
|
||||||
|
|
||||||
sane.services.trust-dns.enable = true;
|
networking.nat.enable = true;
|
||||||
sane.services.trust-dns.instances = let
|
networking.nat.extraCommands = ''
|
||||||
mkSubstitutions = flavor: {
|
# redirect incoming DNS requests from LAN addresses
|
||||||
"%ADOOF%" = config.sane.netns.doof.netnsPubIpv4;
|
# to the LAN-specialized DNS service
|
||||||
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
|
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||||
"%AOVPNS%" = config.sane.netns.ovpns.netnsPubIpv4;
|
# because they get cleanly reset across activations or `systemctl restart firewall`
|
||||||
"%AWAN%" = "$(cat '${dyn-dns.ipPath}')";
|
# instead of accumulating cruft
|
||||||
"%CNAMENATIVE%" = "servo.${flavor}";
|
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";
|
||||||
};
|
};
|
||||||
in
|
}
|
||||||
{
|
{
|
||||||
doof = {
|
systemd.services =
|
||||||
substitutions = mkSubstitutions "doof";
|
let
|
||||||
listenAddrsIpv4 = [
|
sed = "${pkgs.gnused}/bin/sed";
|
||||||
config.sane.netns.doof.hostVethIpv4
|
stateDir = "/var/lib/trust-dns";
|
||||||
config.sane.netns.ovpns.hostVethIpv4
|
zoneTemplate = pkgs.writeText "uninsane.org.zone.in" config.sane.dns.zones."uninsane.org".rendered;
|
||||||
];
|
|
||||||
|
zoneDirFor = flavor: "${stateDir}/${flavor}";
|
||||||
|
zoneFor = flavor: "${zoneDirFor flavor}/uninsane.org.zone";
|
||||||
|
mkTrustDnsService = opts: flavor: let
|
||||||
|
flags = let baseCfg = config.services.trust-dns; in
|
||||||
|
(lib.optional baseCfg.debug "--debug") ++ (lib.optional baseCfg.quiet "--quiet");
|
||||||
|
flagsStr = builtins.concatStringsSep " " flags;
|
||||||
|
|
||||||
|
anative = nativeAddrs."servo.${flavor}";
|
||||||
|
|
||||||
|
toml = pkgs.formats.toml { };
|
||||||
|
configTemplate = opts.config or (toml.generate "trust-dns-${flavor}.toml" (
|
||||||
|
(
|
||||||
|
lib.filterAttrsRecursive (_: v: v != null) config.services.trust-dns.settings
|
||||||
|
) // {
|
||||||
|
listen_addrs_ipv4 = opts.listen or [ anative ];
|
||||||
|
}
|
||||||
|
));
|
||||||
|
configFile = "${stateDir}/${flavor}-config.toml";
|
||||||
|
|
||||||
|
port = opts.port or 53;
|
||||||
|
in {
|
||||||
|
description = "trust-dns Domain Name Server (serving ${flavor})";
|
||||||
|
unitConfig.Documentation = "https://trust-dns.org/";
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||||
|
${sed} s/%AWAN%/$wan/ ${configTemplate} > ${configFile}
|
||||||
|
'' + lib.optionalString (!opts ? config) ''
|
||||||
|
mkdir -p ${zoneDirFor flavor}
|
||||||
|
${sed} \
|
||||||
|
-e s/%CNAMENATIVE%/servo.${flavor}/ \
|
||||||
|
-e s/%ANATIVE%/${anative}/ \
|
||||||
|
-e s/%AWAN%/$wan/ \
|
||||||
|
-e s/%AOVPNS%/185.157.162.178/ \
|
||||||
|
${zoneTemplate} > ${zoneFor flavor}
|
||||||
|
'';
|
||||||
|
serviceConfig = config.systemd.services.trust-dns.serviceConfig // {
|
||||||
|
ExecStart = ''
|
||||||
|
${pkgs.trust-dns}/bin/${pkgs.trust-dns.meta.mainProgram} \
|
||||||
|
--port ${builtins.toString port} \
|
||||||
|
--zonedir ${zoneDirFor flavor}/ \
|
||||||
|
--config ${configFile} ${flagsStr}
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
hn = {
|
|
||||||
substitutions = mkSubstitutions "hn";
|
after = [ "network.target" ];
|
||||||
listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
};
|
};
|
||||||
lan = {
|
in {
|
||||||
substitutions = mkSubstitutions "lan";
|
trust-dns-wan = mkTrustDnsService { listen = [ nativeAddrs."servo.lan" bindOvpn ]; } "wan";
|
||||||
listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
|
trust-dns-lan = mkTrustDnsService { port = 1053; } "lan";
|
||||||
# port = 1053;
|
trust-dns-hn = mkTrustDnsService { port = 1053; } "hn";
|
||||||
};
|
trust-dns-hn-resolver = mkTrustDnsService {
|
||||||
# wan = {
|
config = pkgs.writeText "hn-resolver-config.toml" ''
|
||||||
# substitutions = mkSubstitutions "wan";
|
# i host a resolver in the wireguard VPN so that clients can resolve DNS through the VPN.
|
||||||
# listenAddrsIpv4 = [
|
# (that's what this file achieves).
|
||||||
# nativeAddrs."servo.lan"
|
#
|
||||||
# ];
|
# one would expect this resolver could host the authoritative zone for `uninsane.org`, and then forward everything else to the system resolver...
|
||||||
# };
|
# and while that works for `dig`, it breaks for `nslookup` (and so `ssh`, etc).
|
||||||
# hn-resolver = {
|
#
|
||||||
# # don't need %AWAN% here because we forward to the hn instance.
|
# DNS responses include a flag for if the responding server is the authority of the zone queried.
|
||||||
# listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
|
# it seems that default Linux stub resolvers either:
|
||||||
# extraConfig = {
|
# - expect DNSSEC when the response includes that bit, or
|
||||||
# zones = [
|
# - expect A records to be in the `answer` section instead of `additional` section.
|
||||||
# {
|
# or perhaps something more nuanced. but for `nslookup` to be reliable, it has to talk to an
|
||||||
# zone = "uninsane.org";
|
# instance of trust-dns which is strictly a resolver, with no authority.
|
||||||
# zone_type = "Forward";
|
# hence, this config: a resolver which forwards to the actual authority.
|
||||||
# stores = {
|
|
||||||
# type = "forward";
|
listen_addrs_ipv4 = ["${nativeAddrs."servo.hn"}"]
|
||||||
# name_servers = [
|
listen_addrs_ipv6 = []
|
||||||
# {
|
|
||||||
# socket_addr = "${nativeAddrs."servo.hn"}:1053";
|
[[zones]]
|
||||||
# protocol = "udp";
|
zone = "uninsane.org"
|
||||||
# trust_nx_responses = true;
|
zone_type = "Forward"
|
||||||
# }
|
stores = { type = "forward", name_servers = [{ socket_addr = "${nativeAddrs."servo.hn"}:1053", protocol = "udp", trust_nx_responses = true }] }
|
||||||
# ];
|
|
||||||
# };
|
[[zones]]
|
||||||
# }
|
# forward the root zone to the local DNS resolver
|
||||||
# {
|
zone = "."
|
||||||
# # forward the root zone to the local DNS resolver
|
zone_type = "Forward"
|
||||||
# zone = ".";
|
stores = { type = "forward", name_servers = [{ socket_addr = "127.0.0.53:53", protocol = "udp", trust_nx_responses = true }] }
|
||||||
# zone_type = "Forward";
|
'';
|
||||||
# stores = {
|
} "hn-resolver";
|
||||||
# type = "forward";
|
|
||||||
# name_servers = [
|
|
||||||
# {
|
|
||||||
# socket_addr = "127.0.0.53:53";
|
|
||||||
# protocol = "udp";
|
|
||||||
# trust_nx_responses = true;
|
|
||||||
# }
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
# }
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.services.dyn-dns.restartOnChange = [
|
sane.services.dyn-dns.restartOnChange = [
|
||||||
"trust-dns-doof.service"
|
"trust-dns-wan.service"
|
||||||
"trust-dns-hn.service"
|
|
||||||
"trust-dns-lan.service"
|
"trust-dns-lan.service"
|
||||||
# "trust-dns-wan.service"
|
"trust-dns-hn.service"
|
||||||
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
|
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
|
|
@ -1,30 +1,24 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./boot.nix
|
|
||||||
./feeds.nix
|
./feeds.nix
|
||||||
./fs.nix
|
./fs.nix
|
||||||
|
./hardware
|
||||||
./home
|
./home
|
||||||
./hosts.nix
|
./hosts.nix
|
||||||
./ids.nix
|
./ids.nix
|
||||||
./machine-id.nix
|
./machine-id.nix
|
||||||
./net
|
./net
|
||||||
./nix.nix
|
./nix
|
||||||
./persist.nix
|
./persist.nix
|
||||||
./polyunfill.nix
|
./polyunfill.nix
|
||||||
./programs
|
./programs
|
||||||
./quirks.nix
|
|
||||||
./secrets.nix
|
./secrets.nix
|
||||||
./ssh.nix
|
./ssh.nix
|
||||||
./systemd.nix
|
./systemd.nix
|
||||||
./users
|
./users
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
|
||||||
# this affects where nixos modules look for stateful data which might have been migrated across releases.
|
|
||||||
system.stateVersion = "21.11";
|
|
||||||
|
|
||||||
sane.nixcache.enable-trusted-keys = true;
|
sane.nixcache.enable-trusted-keys = true;
|
||||||
sane.nixcache.enable = lib.mkDefault true;
|
sane.nixcache.enable = lib.mkDefault true;
|
||||||
sane.persist.enable = lib.mkDefault true;
|
sane.persist.enable = lib.mkDefault true;
|
||||||
|
@ -32,6 +26,9 @@
|
||||||
sane.programs.sysadminUtils.enableFor.system = lib.mkDefault true;
|
sane.programs.sysadminUtils.enableFor.system = lib.mkDefault true;
|
||||||
sane.programs.consoleUtils.enableFor.user.colin = lib.mkDefault true;
|
sane.programs.consoleUtils.enableFor.user.colin = lib.mkDefault true;
|
||||||
|
|
||||||
|
nixpkgs.config.allowUnfree = true; # NIXPKGS_ALLOW_UNFREE=1
|
||||||
|
nixpkgs.config.allowBroken = true; # NIXPKGS_ALLOW_BROKEN=1
|
||||||
|
|
||||||
# time.timeZone = "America/Los_Angeles";
|
# time.timeZone = "America/Los_Angeles";
|
||||||
time.timeZone = "Etc/UTC"; # DST is too confusing for me => use a stable timezone
|
time.timeZone = "Etc/UTC"; # DST is too confusing for me => use a stable timezone
|
||||||
|
|
||||||
|
|
|
@ -81,15 +81,10 @@ let
|
||||||
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
|
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
|
||||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||||
(fromDb "fulltimenix.com" // tech)
|
(fromDb "fulltimenix.com" // tech)
|
||||||
(fromDb "futureofcoding.org/episodes" // tech)
|
|
||||||
(fromDb "hackerpublicradio.org" // tech)
|
|
||||||
(fromDb "lexfridman.com/podcast" // rat)
|
(fromDb "lexfridman.com/podcast" // rat)
|
||||||
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
|
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
|
||||||
(fromDb "microarch.club" // tech)
|
|
||||||
(fromDb "mintcast.org" // tech)
|
|
||||||
(fromDb "omegataupodcast.net" // tech) # 3/4 German; 1/4 eps are English
|
(fromDb "omegataupodcast.net" // tech) # 3/4 German; 1/4 eps are English
|
||||||
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol) # Maggie Killjoy -- referenced by Cory Doctorow
|
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol) # Maggie Killjoy -- referenced by Cory Doctorow
|
||||||
(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 "omny.fm/shows/the-dollop-with-dave-anthony-and-gareth-reynolds") # The Dollop history/comedy
|
||||||
(fromDb "originstories.libsyn.com" // uncat)
|
(fromDb "originstories.libsyn.com" // uncat)
|
||||||
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
|
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
|
||||||
|
@ -105,12 +100,9 @@ let
|
||||||
(fromDb "sharkbytes.transistor.fm" // tech) # Wireshark Podcast o_0
|
(fromDb "sharkbytes.transistor.fm" // tech) # Wireshark Podcast o_0
|
||||||
(fromDb "sscpodcast.libsyn.com" // rat) # Astral Codex Ten
|
(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 "talesfromthebridge.buzzsprout.com" // tech) # Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
|
||||||
(fromDb "theamphour.com" // tech)
|
|
||||||
(fromDb "techtalesshow.com" // tech) # Corbin Davenport
|
|
||||||
(fromDb "techwontsave.us" // pol) # rec by Cory Doctorow
|
(fromDb "techwontsave.us" // pol) # rec by Cory Doctorow
|
||||||
(fromDb "wakingup.libsyn.com" // pol) # Sam Harris
|
(fromDb "wakingup.libsyn.com" // pol) # Sam Harris
|
||||||
(fromDb "werenotwrong.fireside.fm" // pol)
|
(fromDb "werenotwrong.fireside.fm" // pol)
|
||||||
(mkPod "https://sfconservancy.org/casts/the-corresponding-source/feeds/ogg/" // tech)
|
|
||||||
|
|
||||||
# (fromDb "feeds.libsyn.com/421877" // rat) # Less Wrong Curated
|
# (fromDb "feeds.libsyn.com/421877" // rat) # Less Wrong Curated
|
||||||
# (fromDb "feeds.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
|
# (fromDb "feeds.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
|
||||||
|
@ -130,11 +122,11 @@ let
|
||||||
(fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following>
|
(fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following>
|
||||||
(fromDb "amosbbatto.wordpress.com" // tech)
|
(fromDb "amosbbatto.wordpress.com" // tech)
|
||||||
(fromDb "anish.lakhwara.com" // tech)
|
(fromDb "anish.lakhwara.com" // tech)
|
||||||
(fromDb "apenwarr.ca/log/rss.php" // tech) # CEO of tailscale
|
|
||||||
(fromDb "applieddivinitystudies.com" // rat)
|
(fromDb "applieddivinitystudies.com" // rat)
|
||||||
(fromDb "artemis.sh" // tech)
|
(fromDb "artemis.sh" // tech)
|
||||||
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
||||||
(fromDb "austinvernon.site" // tech)
|
(fromDb "austinvernon.site" // tech)
|
||||||
|
# (fromDb "balajis.com" // pol) # Balaji
|
||||||
(fromDb "ben-evans.com/benedictevans" // pol)
|
(fromDb "ben-evans.com/benedictevans" // pol)
|
||||||
(fromDb "bitbashing.io" // tech)
|
(fromDb "bitbashing.io" // tech)
|
||||||
(fromDb "bitsaboutmoney.com" // uncat)
|
(fromDb "bitsaboutmoney.com" // uncat)
|
||||||
|
@ -145,19 +137,20 @@ let
|
||||||
(fromDb "blog.thalheim.io" // tech) # Mic92
|
(fromDb "blog.thalheim.io" // tech) # Mic92
|
||||||
(fromDb "bunniestudios.com" // tech) # Bunnie Juang
|
(fromDb "bunniestudios.com" // tech) # Bunnie Juang
|
||||||
(fromDb "capitolhillseattle.com" // pol)
|
(fromDb "capitolhillseattle.com" // pol)
|
||||||
|
# (fromDb "drewdevault.com" // tech)
|
||||||
|
# (fromDb "econlib.org" // pol)
|
||||||
(fromDb "edwardsnowden.substack.com" // pol // text)
|
(fromDb "edwardsnowden.substack.com" // pol // text)
|
||||||
(fromDb "fasterthanli.me" // tech)
|
(fromDb "fasterthanli.me" // tech)
|
||||||
(fromDb "gwern.net" // rat)
|
(fromDb "gwern.net" // rat)
|
||||||
(fromDb "hardcoresoftware.learningbyshipping.com" // tech) # Steven Sinofsky
|
|
||||||
(fromDb "harihareswara.net" // tech // pol) # rec by Cory Doctorow
|
(fromDb "harihareswara.net" // tech // pol) # rec by Cory Doctorow
|
||||||
(fromDb "ianthehenry.com" // tech)
|
(fromDb "ianthehenry.com" // tech)
|
||||||
(fromDb "idiomdrottning.org" // uncat)
|
(fromDb "idiomdrottning.org" // uncat)
|
||||||
(fromDb "interconnected.org/home/feed" // rat) # Matt Webb -- engineering-ish, but dreamy
|
(fromDb "interconnected.org/home/feed" // rat) # Matt Webb -- engineering-ish, but dreamy
|
||||||
(fromDb "jeffgeerling.com" // tech)
|
(fromDb "jeffgeerling.com" // tech)
|
||||||
(fromDb "jefftk.com" // tech)
|
(fromDb "jefftk.com" // tech)
|
||||||
(fromDb "jwz.org/blog" // tech // pol) # DNA lounge guy, loooong-time blogger
|
|
||||||
(fromDb "kill-the-newsletter.com/feeds/joh91bv7am2pnznv.xml" // pol) # Matt Levine - Money Stuff
|
(fromDb "kill-the-newsletter.com/feeds/joh91bv7am2pnznv.xml" // pol) # Matt Levine - Money Stuff
|
||||||
(fromDb "kosmosghost.github.io/index.xml" // tech)
|
(fromDb "kosmosghost.github.io/index.xml" // tech)
|
||||||
|
# (fromDb "lesswrong.com" // rat)
|
||||||
(fromDb "linmob.net" // tech)
|
(fromDb "linmob.net" // tech)
|
||||||
(fromDb "lwn.net" // tech)
|
(fromDb "lwn.net" // tech)
|
||||||
(fromDb "lynalden.com" // pol)
|
(fromDb "lynalden.com" // pol)
|
||||||
|
@ -165,20 +158,19 @@ let
|
||||||
(fromDb "mg.lol" // tech)
|
(fromDb "mg.lol" // tech)
|
||||||
(fromDb "mindingourway.com" // rat)
|
(fromDb "mindingourway.com" // rat)
|
||||||
(fromDb "morningbrew.com/feed" // pol)
|
(fromDb "morningbrew.com/feed" // pol)
|
||||||
(fromDb "nixpkgs.news" // tech)
|
|
||||||
(fromDb "overcomingbias.com" // rat) # Robin Hanson
|
(fromDb "overcomingbias.com" // rat) # Robin Hanson
|
||||||
(fromDb "palladiummag.com" // uncat)
|
(fromDb "palladiummag.com" // uncat)
|
||||||
(fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im
|
(fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im
|
||||||
(fromDb "pomeroyb.com" // tech)
|
(fromDb "pomeroyb.com" // tech)
|
||||||
(fromDb "postmarketos.org/blog" // tech)
|
(fromDb "postmarketos.org/blog" // tech)
|
||||||
(fromDb "preposterousuniverse.com" // rat) # Sean Carroll
|
(fromDb "preposterousuniverse.com" // rat) # Sean Carroll
|
||||||
|
(fromDb "profectusmag.com" // uncat)
|
||||||
(fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny
|
(fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny
|
||||||
(fromDb "putanumonit.com" // rat) # mostly dating topics. not advice, or humor, but looking through a social lens
|
(fromDb "putanumonit.com" // rat) # mostly dating topics. not advice, or humor, but looking through a social lens
|
||||||
(fromDb "richardcarrier.info" // rat)
|
(fromDb "richardcarrier.info" // rat)
|
||||||
(fromDb "rifters.com/crawl" // uncat) # No Moods, Ads or Cutesy Fucking Icons
|
(fromDb "rifters.com/crawl" // uncat) # No Moods, Ads or Cutesy Fucking Icons
|
||||||
(fromDb "righto.com" // tech) # Ken Shirriff
|
(fromDb "righto.com" // tech) # Ken Shirriff
|
||||||
(fromDb "rootsofprogress.org" // rat) # Jason Crawford
|
(fromDb "rootsofprogress.org" // rat) # Jason Crawford
|
||||||
(fromDb "samuel.dionne-riel.com" // tech) # SamuelDR
|
|
||||||
(fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary)
|
(fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary)
|
||||||
(fromDb "semiaccurate.com" // tech)
|
(fromDb "semiaccurate.com" // tech)
|
||||||
(fromDb "sideways-view.com" // rat) # Paul Christiano
|
(fromDb "sideways-view.com" // rat) # Paul Christiano
|
||||||
|
@ -187,42 +179,31 @@ let
|
||||||
(fromDb "spectrum.ieee.org" // tech)
|
(fromDb "spectrum.ieee.org" // tech)
|
||||||
(fromDb "stpeter.im/atom.xml" // pol)
|
(fromDb "stpeter.im/atom.xml" // pol)
|
||||||
(fromDb "thediff.co" // pol) # Byrne Hobart
|
(fromDb "thediff.co" // pol) # Byrne Hobart
|
||||||
|
# (fromDb "theregister.com" // tech)
|
||||||
(fromDb "thisweek.gnome.org" // tech)
|
(fromDb "thisweek.gnome.org" // tech)
|
||||||
(fromDb "tuxphones.com" // tech)
|
(fromDb "tuxphones.com" // tech)
|
||||||
(fromDb "uninsane.org" // tech)
|
(fromDb "uninsane.org" // tech)
|
||||||
(fromDb "unintendedconsequenc.es" // rat)
|
(fromDb "unintendedconsequenc.es" // rat)
|
||||||
|
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
|
||||||
(fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin
|
(fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin
|
||||||
(fromDb "weekinethereumnews.com" // tech)
|
# (fromDb "webcurious.co.uk" // uncat) # link aggregator; defunct?
|
||||||
(fromDb "willow.phantoma.online") # wizard@xyzzy.link
|
|
||||||
(fromDb "xn--gckvb8fzb.com" // tech)
|
(fromDb "xn--gckvb8fzb.com" // tech)
|
||||||
(fromDb "xorvoid.com" // tech)
|
|
||||||
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
|
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
|
||||||
|
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
|
||||||
(mkSubstack "eliqian" // rat // weekly)
|
(mkSubstack "eliqian" // rat // weekly)
|
||||||
(mkSubstack "oversharing" // pol // daily)
|
(mkSubstack "oversharing" // pol // daily)
|
||||||
(mkSubstack "samkriss" // humor // infrequent)
|
(mkSubstack "samkriss" // humor // infrequent)
|
||||||
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
||||||
(mkText "http://boginjr.com/feed" // tech // infrequent)
|
(mkText "http://boginjr.com/feed" // tech // infrequent)
|
||||||
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent) #quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
|
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent) #quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
|
||||||
|
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
||||||
(mkText "https://jvns.ca/atom.xml" // tech // weekly) # Julia Evans
|
(mkText "https://jvns.ca/atom.xml" // tech // weekly) # Julia Evans
|
||||||
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
||||||
(mkText "https://nixos.org/blog/announcements-rss.xml" // tech // infrequent) # more nixos stuff here, but unclear how to subscribe: <https://nixos.org/blog/categories.html>
|
(mkText "https://nixos.org/blog/announcements-rss.xml" // tech // infrequent) # more nixos stuff here, but unclear how to subscribe: <https://nixos.org/blog/categories.html>
|
||||||
(mkText "https://nixos.org/blog/stories-rss.xml" // tech // weekly)
|
(mkText "https://nixos.org/blog/stories-rss.xml" // tech // weekly)
|
||||||
(mkText "https://solar.lowtechmagazine.com/posts/index.xml" // tech // weekly)
|
|
||||||
(mkText "https://www.stratechery.com/rss" // pol // weekly) # Ben Thompson
|
|
||||||
|
|
||||||
# (fromDb "balajis.com" // pol) # Balaji
|
|
||||||
# (fromDb "drewdevault.com" // tech)
|
|
||||||
# (fromDb "econlib.org" // pol)
|
|
||||||
# (fromDb "lesswrong.com" // rat)
|
|
||||||
# (fromDb "profectusmag.com" // pol) # some conservative/libertarian think tank
|
|
||||||
# (fromDb "thesideview.co" // uncat) # spiritual journal; RSS items are stubs
|
|
||||||
# (fromDb "theregister.com" // tech)
|
|
||||||
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
|
|
||||||
# (fromDb "webcurious.co.uk" // uncat) # link aggregator; defunct?
|
|
||||||
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
|
|
||||||
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
|
||||||
# (mkText "https://til.simonwillison.net/tils/feed.atom" // tech // weekly)
|
# (mkText "https://til.simonwillison.net/tils/feed.atom" // tech // weekly)
|
||||||
# (mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly) # Matt Levine (preview/paywalled)
|
# (mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly) # Matt Levine (preview/paywalled)
|
||||||
|
(mkText "https://www.stratechery.com/rss" // pol // weekly) # Ben Thompson
|
||||||
];
|
];
|
||||||
|
|
||||||
videos = [
|
videos = [
|
||||||
|
@ -234,20 +215,17 @@ let
|
||||||
(fromDb "youtube.com/@JackStauber")
|
(fromDb "youtube.com/@JackStauber")
|
||||||
(fromDb "youtube.com/@NativLang")
|
(fromDb "youtube.com/@NativLang")
|
||||||
(fromDb "youtube.com/@PolyMatter")
|
(fromDb "youtube.com/@PolyMatter")
|
||||||
|
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
|
||||||
(fromDb "youtube.com/@TechnologyConnections" // tech)
|
(fromDb "youtube.com/@TechnologyConnections" // tech)
|
||||||
(fromDb "youtube.com/@TheB1M")
|
(fromDb "youtube.com/@TheB1M")
|
||||||
(fromDb "youtube.com/@TomScottGo")
|
(fromDb "youtube.com/@TomScottGo")
|
||||||
(fromDb "youtube.com/@Vihart")
|
(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/@Vsauce")
|
||||||
|
|
||||||
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
|
|
||||||
];
|
];
|
||||||
|
|
||||||
images = [
|
images = [
|
||||||
(fromDb "catandgirl.com" // img // humor)
|
(fromDb "catandgirl.com" // img // humor)
|
||||||
(fromDb "davidrevoy.com" // img // art)
|
|
||||||
(fromDb "grumpy.website" // img // humor)
|
|
||||||
(fromDb "miniature-calendar.com" // img // art // daily)
|
(fromDb "miniature-calendar.com" // img // art // daily)
|
||||||
(fromDb "pbfcomics.com" // img // humor)
|
(fromDb "pbfcomics.com" // img // humor)
|
||||||
(fromDb "poorlydrawnlines.com/feed" // img // humor)
|
(fromDb "poorlydrawnlines.com/feed" // img // humor)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
||||||
# - fuse options: `man mount.fuse`
|
# - fuse options: `man mount.fuse`
|
||||||
|
|
||||||
{ config, lib, pkgs, sane-lib, utils, ... }:
|
{ lib, pkgs, sane-lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
fsOpts = rec {
|
fsOpts = rec {
|
||||||
|
@ -23,15 +23,15 @@ let
|
||||||
# N.B.: `remote-fs.target` is a dependency of multi-user.target, itself of graphical.target.
|
# N.B.: `remote-fs.target` is a dependency of multi-user.target, itself of graphical.target.
|
||||||
# hence, omitting `noauto` can slow down boots.
|
# hence, omitting `noauto` can slow down boots.
|
||||||
noauto = [ "noauto" ];
|
noauto = [ "noauto" ];
|
||||||
# lazyMount: defer mounting until first access from userspace.
|
# lazyMount: defer mounting until first access from userspace
|
||||||
# see: `man systemd.automount`, `man automount`, `man autofs`
|
|
||||||
lazyMount = noauto ++ automount;
|
lazyMount = noauto ++ automount;
|
||||||
wg = [
|
wg = [
|
||||||
"x-systemd.requires=wireguard-wg-home.service"
|
"x-systemd.requires=wireguard-wg-home.service"
|
||||||
"x-systemd.after=wireguard-wg-home.service"
|
"x-systemd.after=wireguard-wg-home.service"
|
||||||
];
|
];
|
||||||
|
|
||||||
fuse = [
|
ssh = common ++ [
|
||||||
|
"identityfile=/home/colin/.ssh/id_ed25519"
|
||||||
"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)
|
"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)
|
||||||
# allow_root: allow root to access files on this fs (if mounted by non-root, else it can always access them).
|
# allow_root: allow root to access files on this fs (if mounted by non-root, else it can always access them).
|
||||||
# N.B.: if both allow_root and allow_other are specified, then only allow_root takes effect.
|
# N.B.: if both allow_root and allow_other are specified, then only allow_root takes effect.
|
||||||
|
@ -44,18 +44,7 @@ let
|
||||||
# with default_permissions, sshfs doesn't tunnel file ops from users until checking that said user could perform said op on an equivalent local fs.
|
# with default_permissions, sshfs doesn't tunnel file ops from users until checking that said user could perform said op on an equivalent local fs.
|
||||||
"default_permissions"
|
"default_permissions"
|
||||||
];
|
];
|
||||||
fuseColin = fuse ++ [
|
sshColin = ssh ++ [
|
||||||
"uid=1000"
|
|
||||||
"gid=100"
|
|
||||||
];
|
|
||||||
|
|
||||||
ssh = common ++ fuse ++ [
|
|
||||||
"identityfile=/home/colin/.ssh/id_ed25519"
|
|
||||||
# i *think* idmap=user means that `colin` on `localhost` and `colin` on the remote are actually treated as the same user, even if their uid/gid differs?
|
|
||||||
# i.e., local colin's id is translated to/from remote colin's id on every operation?
|
|
||||||
"idmap=user"
|
|
||||||
];
|
|
||||||
sshColin = ssh ++ fuseColin ++ [
|
|
||||||
# follow_symlinks: remote files which are symlinks are presented to the local system as ordinary files (as the target of the symlink).
|
# follow_symlinks: remote files which are symlinks are presented to the local system as ordinary files (as the target of the symlink).
|
||||||
# if the symlink target does not exist, the presentation is unspecified.
|
# if the symlink target does not exist, the presentation is unspecified.
|
||||||
# symlinks which point outside the mount ARE followed. so this is more capable than `transform_symlinks`
|
# symlinks which point outside the mount ARE followed. so this is more capable than `transform_symlinks`
|
||||||
|
@ -63,6 +52,9 @@ let
|
||||||
# symlinks on the remote fs which are absolute paths are presented to the local system as relative symlinks pointing to the expected data on the remote fs.
|
# symlinks on the remote fs which are absolute paths are presented to the local system as relative symlinks pointing to the expected data on the remote fs.
|
||||||
# only symlinks which would point inside the mountpoint are translated.
|
# only symlinks which would point inside the mountpoint are translated.
|
||||||
"transform_symlinks"
|
"transform_symlinks"
|
||||||
|
"idmap=user"
|
||||||
|
"uid=1000"
|
||||||
|
"gid=100"
|
||||||
];
|
];
|
||||||
# sshRoot = ssh ++ [
|
# sshRoot = ssh ++ [
|
||||||
# # we don't transform_symlinks because that breaks the validity of remote /nix stores
|
# # we don't transform_symlinks because that breaks the validity of remote /nix stores
|
||||||
|
@ -75,44 +67,21 @@ let
|
||||||
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
|
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
|
||||||
# bg = retry failed mounts in the background
|
# bg = retry failed mounts in the background
|
||||||
# retry=n = for how many minutes `mount` will retry NFS mount operation
|
# retry=n = for how many minutes `mount` will retry NFS mount operation
|
||||||
# intr = allow Ctrl+C to abort I/O (it will error with `EINTR`)
|
|
||||||
# soft = on "major timeout", report I/O error to userspace
|
# soft = on "major timeout", report I/O error to userspace
|
||||||
# softreval = on "major timeout", service the request using known-stale cache results instead of erroring -- if such cache data exists
|
|
||||||
# retrans=n = how many times to retry a NFS request before giving userspace a "server not responding" error (default: 3)
|
# retrans=n = how many times to retry a NFS request before giving userspace a "server not responding" error (default: 3)
|
||||||
# timeo=n = number of *deciseconds* to wait for a response before retrying it (default: 600)
|
# timeo=n = number of *deciseconds* to wait for a response before retrying it (default: 600)
|
||||||
# note: client uses a linear backup, so the second request will have double this timeout, then triple, etc.
|
# note: client uses a linear backup, so the second request will have double this timeout, then triple, etc.
|
||||||
# proto=udp = encapsulate protocol ops inside UDP packets instead of a TCP session.
|
|
||||||
# requires `nfsvers=3` and a kernel compiled with `NFS_DISABLE_UDP_SUPPORT=n`.
|
|
||||||
# UDP might be preferable to TCP because the latter is liable to hang for ~100s (kernel TCP timeout) after a link drop.
|
|
||||||
# however, even UDP has issues with `umount` hanging.
|
|
||||||
#
|
|
||||||
# N.B.: don't change these without first testing the behavior of sandboxed apps on a flaky network.
|
|
||||||
nfs = common ++ [
|
nfs = common ++ [
|
||||||
# "actimeo=5"
|
# "actimeo=10"
|
||||||
# "bg"
|
"bg"
|
||||||
"retrans=1"
|
"retrans=4"
|
||||||
"retry=0"
|
"retry=0"
|
||||||
# "intr"
|
|
||||||
"soft"
|
"soft"
|
||||||
"softreval"
|
"timeo=15"
|
||||||
"timeo=30"
|
|
||||||
"nofail" # don't fail remote-fs.target when this mount fails (not an option for sshfs else would be common)
|
"nofail" # don't fail remote-fs.target when this mount fails (not an option for sshfs else would be common)
|
||||||
# "proto=udp" # default kernel config doesn't support NFS over UDP: <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1964093> (see comment 11).
|
|
||||||
# "nfsvers=3" # NFSv4+ doesn't support UDP at *all*. it's ok to omit nfsvers -- server + client will negotiate v3 based on udp requirement. but omitting causes confusing mount errors when the server is *offline*, because the client defaults to v4 and thinks the udp option is a config error.
|
|
||||||
# "x-systemd.idle-timeout=10" # auto-unmount after this much inactivity
|
|
||||||
];
|
|
||||||
|
|
||||||
# manually perform a ftp mount via e.g.
|
|
||||||
# curlftpfs -o ftpfs_debug=2,user=anonymous:anonymous,connect_timeout=10 -f -s ftp://servo-hn /mnt/my-ftp
|
|
||||||
ftp = common ++ fuseColin ++ [
|
|
||||||
# "ftpfs_debug=2"
|
|
||||||
"user=colin:ipauth"
|
|
||||||
# connect_timeout=10: casting shows to T.V. fails partway through about half the time
|
|
||||||
"connect_timeout=20"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
remoteHome = host: {
|
remoteHome = host: {
|
||||||
sane.programs.sshfs-fuse.enableFor.system = true;
|
|
||||||
fileSystems."/mnt/${host}/home" = {
|
fileSystems."/mnt/${host}/home" = {
|
||||||
device = "colin@${host}:/home/colin";
|
device = "colin@${host}:/home/colin";
|
||||||
fsType = "fuse.sshfs";
|
fsType = "fuse.sshfs";
|
||||||
|
@ -125,54 +94,6 @@ let
|
||||||
dir.acl.mode = "0700";
|
dir.acl.mode = "0700";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
remoteServo = subdir: {
|
|
||||||
sane.programs.curlftpfs.enableFor.system = true;
|
|
||||||
sane.fs."/mnt/servo/${subdir}" = sane-lib.fs.wanted {
|
|
||||||
dir.acl.user = "colin";
|
|
||||||
dir.acl.group = "users";
|
|
||||||
dir.acl.mode = "0750";
|
|
||||||
};
|
|
||||||
fileSystems."/mnt/servo/${subdir}" = {
|
|
||||||
device = "ftp://servo-hn:/${subdir}";
|
|
||||||
noCheck = true;
|
|
||||||
fsType = "fuse.curlftpfs";
|
|
||||||
options = fsOpts.ftp ++ fsOpts.noauto ++ fsOpts.wg;
|
|
||||||
# fsType = "nfs";
|
|
||||||
# options = fsOpts.nfs ++ fsOpts.lazyMount ++ fsOpts.wg;
|
|
||||||
};
|
|
||||||
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" ];
|
|
||||||
|
|
||||||
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}"
|
|
||||||
];
|
|
||||||
# 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
|
in
|
||||||
lib.mkMerge [
|
lib.mkMerge [
|
||||||
{
|
{
|
||||||
|
@ -207,6 +128,35 @@ lib.mkMerge [
|
||||||
# but it decreases working memory under the heaviest of loads by however much space the compressed memory occupies (e.g. 50% if 2:1; 25% if 4:1)
|
# but it decreases working memory under the heaviest of loads by however much space the compressed memory occupies (e.g. 50% if 2:1; 25% if 4:1)
|
||||||
zramSwap.memoryPercent = 100;
|
zramSwap.memoryPercent = 100;
|
||||||
|
|
||||||
|
# fileSystems."/mnt/servo-nfs" = {
|
||||||
|
# device = "servo-hn:/";
|
||||||
|
# noCheck = true;
|
||||||
|
# fsType = "nfs";
|
||||||
|
# options = fsOpts.nfs ++ fsOpts.automount ++ fsOpts.wg;
|
||||||
|
# };
|
||||||
|
fileSystems."/mnt/servo/media" = {
|
||||||
|
device = "servo-hn:/media";
|
||||||
|
noCheck = true;
|
||||||
|
fsType = "nfs";
|
||||||
|
options = fsOpts.nfs ++ fsOpts.lazyMount ++ fsOpts.wg;
|
||||||
|
};
|
||||||
|
sane.fs."/mnt/servo/media" = sane-lib.fs.wanted {
|
||||||
|
dir.acl.user = "colin";
|
||||||
|
dir.acl.group = "users";
|
||||||
|
dir.acl.mode = "0750";
|
||||||
|
};
|
||||||
|
fileSystems."/mnt/servo/playground" = {
|
||||||
|
device = "servo-hn:/playground";
|
||||||
|
noCheck = true;
|
||||||
|
fsType = "nfs";
|
||||||
|
options = fsOpts.nfs ++ fsOpts.lazyMount ++ fsOpts.wg;
|
||||||
|
};
|
||||||
|
sane.fs."/mnt/servo/playground" = sane-lib.fs.wanted {
|
||||||
|
dir.acl.user = "colin";
|
||||||
|
dir.acl.group = "users";
|
||||||
|
dir.acl.mode = "0750";
|
||||||
|
};
|
||||||
|
|
||||||
# environment.pathsToLink = [
|
# environment.pathsToLink = [
|
||||||
# # needed to achieve superuser access for user-mounted filesystems (see sshRoot above)
|
# # needed to achieve superuser access for user-mounted filesystems (see sshRoot above)
|
||||||
# # we can only link whole directories here, even though we're only interested in pkgs.openssh
|
# # we can only link whole directories here, even though we're only interested in pkgs.openssh
|
||||||
|
@ -214,24 +164,13 @@ lib.mkMerge [
|
||||||
# ];
|
# ];
|
||||||
|
|
||||||
programs.fuse.userAllowOther = true; #< necessary for `allow_other` or `allow_root` options.
|
programs.fuse.userAllowOther = true; #< necessary for `allow_other` or `allow_root` options.
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.sshfs-fuse
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
(remoteHome "crappy")
|
|
||||||
(remoteHome "desko")
|
(remoteHome "desko")
|
||||||
(remoteHome "lappy")
|
(remoteHome "lappy")
|
||||||
(remoteHome "moby")
|
(remoteHome "moby")
|
||||||
# this granularity of servo media mounts is necessary to support sandboxing:
|
|
||||||
# for flaky mounts, we can only bind the mountpoint itself into the sandbox,
|
|
||||||
# so it's either this or unconditionally bind all of media/.
|
|
||||||
(remoteServo "media/archive")
|
|
||||||
(remoteServo "media/Books")
|
|
||||||
(remoteServo "media/collections")
|
|
||||||
# (remoteServo "media/datasets")
|
|
||||||
(remoteServo "media/games")
|
|
||||||
(remoteServo "media/Music")
|
|
||||||
(remoteServo "media/Pictures/macros")
|
|
||||||
(remoteServo "media/torrents")
|
|
||||||
(remoteServo "media/Videos")
|
|
||||||
(remoteServo "playground")
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
{ lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
./x86_64.nix
|
||||||
|
];
|
||||||
|
|
||||||
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
|
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
|
||||||
# useful emergency utils
|
# useful emergency utils
|
||||||
boot.initrd.extraUtilsCommands = ''
|
boot.initrd.extraUtilsCommands = ''
|
||||||
|
@ -39,12 +44,48 @@
|
||||||
boot.loader.grub.enable = lib.mkDefault false;
|
boot.loader.grub.enable = lib.mkDefault false;
|
||||||
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
|
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
|
||||||
|
|
||||||
hardware.enableAllFirmware = true; # firmware with licenses that don't allow for redistribution. fuck lawyers, fuck IP, give me the goddamn firmware.
|
# non-free firmware
|
||||||
# hardware.enableRedistributableFirmware = true; # proprietary but free-to-distribute firmware (extraneous to `enableAllFirmware` option)
|
hardware.enableRedistributableFirmware = true;
|
||||||
|
|
||||||
# default is 252274, which is too low particularly for servo.
|
# default is 252274, which is too low particularly for servo.
|
||||||
# manifests as spurious "No space left on device" when trying to install watches,
|
# 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`.
|
# 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>
|
# 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" = 1048576;
|
boot.kernel.sysctl."fs.inotify.max_user_watches" = 1048576;
|
||||||
|
|
||||||
|
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
|
||||||
|
powerManagement.powertop.enable = false;
|
||||||
|
# linux CPU governor: <https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt>
|
||||||
|
# - options:
|
||||||
|
# - "powersave" => force CPU to always run at lowest supported frequency
|
||||||
|
# - "performance" => force CPU to always run at highest frequency
|
||||||
|
# - "ondemand" => adjust frequency based on load
|
||||||
|
# - "conservative" (ondemand but slower to adjust)
|
||||||
|
# - "schedutil"
|
||||||
|
# - "userspace"
|
||||||
|
# - not all options are available for all platforms
|
||||||
|
# - intel (intel_pstate) appears to manage scaling w/o intervention/control from the OS.
|
||||||
|
# - AMD (acpi-cpufreq) appears to manage scaling via the OS *or* HW. but the ondemand defaults never put it to max hardware frequency.
|
||||||
|
# - qualcomm (cpufreq-dt) appears to manage scaling *only* via the OS. ondemand governor exercises the full range.
|
||||||
|
# - query details with `sudo cpupower frequency-info`
|
||||||
|
powerManagement.cpuFreqGovernor = "ondemand";
|
||||||
|
|
||||||
|
services.logind.extraConfig = ''
|
||||||
|
# see: `man logind.conf`
|
||||||
|
# don’t shutdown when power button is short-pressed (commonly done an accident, or by cats).
|
||||||
|
# but do on long-press: useful to gracefully power-off server.
|
||||||
|
HandlePowerKey=lock
|
||||||
|
HandlePowerKeyLongPress=poweroff
|
||||||
|
HandleLidSwitch=lock
|
||||||
|
'';
|
||||||
|
|
||||||
|
# services.snapper.configs = {
|
||||||
|
# root = {
|
||||||
|
# subvolume = "/";
|
||||||
|
# extraConfig = {
|
||||||
|
# ALLOW_USERS = "colin";
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
# services.snapper.snapshotInterval = "daily";
|
||||||
}
|
}
|
15
hosts/common/hardware/x86_64.nix
Normal file
15
hosts/common/hardware/x86_64.nix
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
config = lib.mkIf (pkgs.system == "x86_64-linux") {
|
||||||
|
boot.initrd.availableKernelModules = [
|
||||||
|
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
||||||
|
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
||||||
|
"nvme" # to boot from nvme devices
|
||||||
|
# efi_pstore evivars
|
||||||
|
];
|
||||||
|
|
||||||
|
hardware.cpu.amd.updateMicrocode = true; # desktop
|
||||||
|
hardware.cpu.intel.updateMicrocode = true; # laptop
|
||||||
|
};
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ let
|
||||||
localShareApplicationsPkg = (pkgs.symlinkJoin {
|
localShareApplicationsPkg = (pkgs.symlinkJoin {
|
||||||
name = "user-local-share-applications";
|
name = "user-local-share-applications";
|
||||||
paths = builtins.map
|
paths = builtins.map
|
||||||
(p: builtins.toString p.package)
|
(p: "${p.package}")
|
||||||
(enabledProgramsWithPackage ++ [ { package=mimeappsListPkg; } ]);
|
(enabledProgramsWithPackage ++ [ { package=mimeappsListPkg; } ]);
|
||||||
}).overrideAttrs (orig: {
|
}).overrideAttrs (orig: {
|
||||||
# like normal symlinkJoin, but don't error if the path doesn't exist
|
# like normal symlinkJoin, but don't error if the path doesn't exist
|
||||||
|
|
|
@ -3,18 +3,13 @@
|
||||||
{
|
{
|
||||||
# XDG defines things like ~/Desktop, ~/Downloads, etc.
|
# XDG defines things like ~/Desktop, ~/Downloads, etc.
|
||||||
# these clutter the home, so i mostly don't use them.
|
# these clutter the home, so i mostly don't use them.
|
||||||
# note that several of these are not actually standardized anywhere.
|
|
||||||
# some are even non-conventional, like:
|
|
||||||
# - XDG_PHOTOS_DIR: only works because i patch e.g. megapixels
|
|
||||||
sane.user.fs.".config/user-dirs.dirs".symlink.text = ''
|
sane.user.fs.".config/user-dirs.dirs".symlink.text = ''
|
||||||
XDG_DESKTOP_DIR="$HOME/.xdg/Desktop"
|
XDG_DESKTOP_DIR="$HOME/.xdg/Desktop"
|
||||||
XDG_DOCUMENTS_DIR="$HOME/dev"
|
XDG_DOCUMENTS_DIR="$HOME/dev"
|
||||||
XDG_DOWNLOAD_DIR="$HOME/tmp"
|
XDG_DOWNLOAD_DIR="$HOME/tmp"
|
||||||
XDG_MUSIC_DIR="$HOME/Music"
|
XDG_MUSIC_DIR="$HOME/Music"
|
||||||
XDG_PHOTOS_DIR="$HOME/Pictures/Photos"
|
|
||||||
XDG_PICTURES_DIR="$HOME/Pictures"
|
XDG_PICTURES_DIR="$HOME/Pictures"
|
||||||
XDG_PUBLICSHARE_DIR="$HOME/.xdg/Public"
|
XDG_PUBLICSHARE_DIR="$HOME/.xdg/Public"
|
||||||
XDG_SCREENSHOTS_DIR="$HOME/Pictures/Screenshots"
|
|
||||||
XDG_TEMPLATES_DIR="$HOME/.xdg/Templates"
|
XDG_TEMPLATES_DIR="$HOME/.xdg/Templates"
|
||||||
XDG_VIDEOS_DIR="$HOME/Videos"
|
XDG_VIDEOS_DIR="$HOME/Videos"
|
||||||
'';
|
'';
|
||||||
|
@ -22,6 +17,4 @@
|
||||||
# prevent `xdg-user-dirs-update` from overriding/updating our config
|
# prevent `xdg-user-dirs-update` from overriding/updating our config
|
||||||
# see <https://manpages.ubuntu.com/manpages/bionic/man5/user-dirs.conf.5.html>
|
# see <https://manpages.ubuntu.com/manpages/bionic/man5/user-dirs.conf.5.html>
|
||||||
sane.user.fs.".config/user-dirs.conf".symlink.text = "enabled=False";
|
sane.user.fs.".config/user-dirs.conf".symlink.text = "enabled=False";
|
||||||
|
|
||||||
sane.user.fs.".config/environment.d/30-user-dirs.conf".symlink.target = "../user-dirs.dirs";
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,6 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
# TODO: this should be populated per-host
|
# TODO: this should be populated per-host
|
||||||
sane.hosts.by-name."crappy" = {
|
|
||||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMIvSQAGKqmymXIL4La9B00LPxBIqWAr5AsJxk3UQeY5";
|
|
||||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMN0cpRAloCBOE5/2wuzgik35iNDv5KLceWMCVaa7DIQ";
|
|
||||||
# wg-home.pubkey = "TODO";
|
|
||||||
# wg-home.ip = "10.0.10.55";
|
|
||||||
lan-ip = "10.78.79.55";
|
|
||||||
};
|
|
||||||
|
|
||||||
sane.hosts.by-name."desko" = {
|
sane.hosts.by-name."desko" = {
|
||||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
||||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||||
|
|
|
@ -4,9 +4,6 @@
|
||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# partially supported in nixpkgs <repo:nixos/nixpkgs:nixos/modules/misc/ids.nix>
|
|
||||||
sane.ids.networkmanager.uid = 57; #< nixpkgs unofficially reserves this, to match networkmanager's gid
|
|
||||||
|
|
||||||
# legacy servo users, some are inconvenient to migrate
|
# legacy servo users, some are inconvenient to migrate
|
||||||
sane.ids.dhcpcd.gid = 991;
|
sane.ids.dhcpcd.gid = 991;
|
||||||
sane.ids.dhcpcd.uid = 992;
|
sane.ids.dhcpcd.uid = 992;
|
||||||
|
@ -21,7 +18,7 @@
|
||||||
sane.ids.matrix-appservice-irc.uid = 993;
|
sane.ids.matrix-appservice-irc.uid = 993;
|
||||||
sane.ids.matrix-appservice-irc.gid = 992;
|
sane.ids.matrix-appservice-irc.gid = 992;
|
||||||
|
|
||||||
# greetd (legacy)
|
# greetd (used by sway)
|
||||||
sane.ids.greeter.uid = 999;
|
sane.ids.greeter.uid = 999;
|
||||||
sane.ids.greeter.gid = 999;
|
sane.ids.greeter.gid = 999;
|
||||||
|
|
||||||
|
@ -81,7 +78,6 @@
|
||||||
|
|
||||||
# found on graphical hosts
|
# found on graphical hosts
|
||||||
sane.ids.nm-iodine.uid = 2101; # desko/moby/lappy
|
sane.ids.nm-iodine.uid = 2101; # desko/moby/lappy
|
||||||
sane.ids.seat.gid = 2102;
|
|
||||||
|
|
||||||
# found on desko host
|
# found on desko host
|
||||||
# from services.usbmuxd
|
# from services.usbmuxd
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
imports = [
|
imports = [
|
||||||
./dns.nix
|
./dns.nix
|
||||||
./hostnames.nix
|
./hostnames.nix
|
||||||
./modemmanager.nix
|
|
||||||
./networkmanager.nix
|
|
||||||
./upnp.nix
|
./upnp.nix
|
||||||
./vpn.nix
|
./vpn.nix
|
||||||
];
|
];
|
||||||
|
@ -26,4 +24,44 @@
|
||||||
# this is required separately by servo and by any `sane-vpn` users,
|
# this is required separately by servo and by any `sane-vpn` users,
|
||||||
# however Nix requires this be set centrally, in only one location (i.e. here)
|
# however Nix requires this be set centrally, in only one location (i.e. here)
|
||||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||||
|
|
||||||
|
# the default backend is "wpa_supplicant".
|
||||||
|
# wpa_supplicant reliably picks weak APs to connect to.
|
||||||
|
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
|
||||||
|
# iwd is an alternative that shouldn't have this problem
|
||||||
|
# docs:
|
||||||
|
# - <https://nixos.wiki/wiki/Iwd>
|
||||||
|
# - <https://iwd.wiki.kernel.org/networkmanager>
|
||||||
|
# - `man iwd.config` for global config
|
||||||
|
# - `man iwd.network` for per-SSID config
|
||||||
|
# use `iwctl` to control
|
||||||
|
# networking.networkmanager.wifi.backend = "iwd";
|
||||||
|
# networking.wireless.iwd.enable = true;
|
||||||
|
# networking.wireless.iwd.settings = {
|
||||||
|
# # auto-connect to a stronger network if signal drops below this value
|
||||||
|
# # bedroom -> bedroom connection is -35 to -40 dBm
|
||||||
|
# # bedroom -> living room connection is -60 dBm
|
||||||
|
# General.RoamThreshold = "-52"; # default -70
|
||||||
|
# General.RoamThreshold5G = "-52"; # default -76
|
||||||
|
# };
|
||||||
|
|
||||||
|
# plugins mostly add support for establishing different VPN connections.
|
||||||
|
# the default plugin set includes mostly proprietary VPNs:
|
||||||
|
# - fortisslvpn (Fortinet)
|
||||||
|
# - iodine (DNS tunnels)
|
||||||
|
# - l2tp
|
||||||
|
# - openconnect (Cisco Anyconnect / Juniper / ocserv)
|
||||||
|
# - openvpn
|
||||||
|
# - vpnc (Cisco VPN)
|
||||||
|
# - sstp
|
||||||
|
#
|
||||||
|
# i don't use these, and notably they drag in huge dependency sets and don't cross compile well.
|
||||||
|
# e.g. openconnect drags in webkitgtk (for SSO)!
|
||||||
|
networking.networkmanager.plugins = lib.mkForce [];
|
||||||
|
|
||||||
|
# keyfile.path = where networkmanager should look for connection credentials
|
||||||
|
networking.networkmanager.extraConfig = ''
|
||||||
|
[keyfile]
|
||||||
|
path=/var/lib/NetworkManager/system-connections
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# things to consider when changing these parameters:
|
# things to consider when changing these parameters:
|
||||||
# - temporary VPN access (`sane-vpn up ...`)
|
# - temporary VPN access (`sane-vpn up ...`)
|
||||||
# - servo `ovpns` namespace (it *relies* on /etc/resolv.conf mentioning 127.0.0.53)
|
# - servo `ovpns` namespace (it *relies* on /etc/resolv.conf mentioning 127.0.0.53)
|
||||||
|
# - jails: `firejail --net=br-ovpnd-us --noprofile --dns=46.227.67.134 ping 1.1.1.1`
|
||||||
#
|
#
|
||||||
# components:
|
# components:
|
||||||
# - /etc/nsswitch.conf:
|
# - /etc/nsswitch.conf:
|
||||||
|
@ -17,22 +18,17 @@
|
||||||
# - modern implementations hardcodes `127.0.0.53` and then systemd-resolved proxies everything (and caches).
|
# - modern implementations hardcodes `127.0.0.53` and then systemd-resolved proxies everything (and caches).
|
||||||
#
|
#
|
||||||
# namespacing:
|
# namespacing:
|
||||||
# - each namespace may use a different /etc/resolv.conf to specify different DNS servers
|
# - each namespace can use a different /etc/resolv.conf to specify different DNS servers (see `firejail --dns=...`)
|
||||||
# - 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.
|
# - nscd breaks namespacing: the host nscd is unaware of the guest's /etc/resolv.conf, and so direct's 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.
|
# - this is fixed by either `firejail --blacklist=/var/run/nscd/socket`, or disabling nscd altogether.
|
||||||
{ config, lib, ... }:
|
{ lib, ... }:
|
||||||
lib.mkMerge [
|
|
||||||
{
|
{
|
||||||
sane.services.trust-dns.enable = lib.mkDefault config.sane.services.trust-dns.asSystemResolver;
|
|
||||||
sane.services.trust-dns.asSystemResolver = lib.mkDefault true;
|
|
||||||
}
|
|
||||||
(lib.mkIf (!config.sane.services.trust-dns.asSystemResolver) {
|
|
||||||
# use systemd's stub resolver.
|
# use systemd's stub resolver.
|
||||||
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
|
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
|
||||||
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
|
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
|
||||||
# in servo's ovnps namespace to use the provider's DNS resolvers.
|
# in servo's ovnps namespace to use the provider's DNS resolvers.
|
||||||
# a weakness is we can only query 1 NS at a time (unless we were to clone the packets?)
|
# a weakness is we can only query 1 NS at a time (unless we were to clone the packets?)
|
||||||
# TODO: improve trust-dns recursive resolver and then remove this
|
# TODO: rework servo's netns to use `firejail`, which is capable of spoofing /etc/resolv.conf.
|
||||||
services.resolved.enable = true; #< to disable, set ` = lib.mkForce false`, as other systemd features default to enabling `resolved`.
|
services.resolved.enable = true; #< to disable, set ` = lib.mkForce false`, as other systemd features default to enabling `resolved`.
|
||||||
# without DNSSEC:
|
# without DNSSEC:
|
||||||
# - dig matrix.org => works
|
# - dig matrix.org => works
|
||||||
|
@ -48,8 +44,7 @@ lib.mkMerge [
|
||||||
# stub resolver (just forwards upstream) lives on 127.0.0.54
|
# stub resolver (just forwards upstream) lives on 127.0.0.54
|
||||||
"127.0.0.53"
|
"127.0.0.53"
|
||||||
];
|
];
|
||||||
})
|
|
||||||
{
|
|
||||||
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
|
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
|
||||||
# in a way that's unaware of my VPN routing, so routes are frequently poor against
|
# in a way that's unaware of my VPN routing, so routes are frequently poor against
|
||||||
# services which advertise different IPs based on geolocation.
|
# services which advertise different IPs based on geolocation.
|
||||||
|
@ -70,4 +65,3 @@ lib.mkMerge [
|
||||||
services.nscd.enable = false;
|
services.nscd.enable = false;
|
||||||
system.nssModules = lib.mkForce [];
|
system.nssModules = lib.mkForce [];
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
{
|
|
||||||
networking.modemmanager.package = pkgs.modemmanager-split.daemon.overrideAttrs (upstream: {
|
|
||||||
# patch to allow the dbus endpoints to be owned by networkmanager user
|
|
||||||
postInstall = (upstream.postInstall or "") + ''
|
|
||||||
substitute $out/share/dbus-1/system.d/org.freedesktop.ModemManager1.conf \
|
|
||||||
$out/share/dbus-1/system.d/networkmanager-org.freedesktop.ModemManager1.conf \
|
|
||||||
--replace-fail 'user="root"' 'group="networkmanager"'
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
|
|
||||||
systemd.services.ModemManager = {
|
|
||||||
# aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
|
|
||||||
# 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";
|
|
||||||
|
|
||||||
# only if started with `--debug` does mmcli let us issue AT commands like
|
|
||||||
# `mmcli --modem any --command=<AT_CMD>`
|
|
||||||
serviceConfig.ExecStart = [
|
|
||||||
"" # first blank line is to clear the upstream `ExecStart` field.
|
|
||||||
"${lib.getExe' config.networking.modemmanager.package "ModemManager"} --debug"
|
|
||||||
];
|
|
||||||
# --debug sets DEBUG level logging: so reset
|
|
||||||
serviceConfig.ExecStartPost = "${lib.getExe config.sane.programs.mmcli.package} --set-logging=INFO";
|
|
||||||
|
|
||||||
# v this is what upstream ships
|
|
||||||
# serviceConfig.Restart = "on-abort";
|
|
||||||
# serviceConfig.StandardError = "null";
|
|
||||||
# serviceConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_NET_ADMIN";
|
|
||||||
# serviceConfig.ProtectSystem = true; # makes empty: /boot, /usr
|
|
||||||
# serviceConfig.ProtectHome = true; # makes empty: /home, /root, /run/user
|
|
||||||
# serviceConfig.PrivateTmp = true;
|
|
||||||
# serviceConfig.RestrictAddressFamilies = "AF_NETLINK AF_UNIX AF_QIPCRTR";
|
|
||||||
# serviceConfig.NoNewPrivileges = true;
|
|
||||||
|
|
||||||
serviceConfig.CapabilityBoundingSet = [ "CAP_NET_ADMIN" ]; #< TODO: make sure this is *really* taking effect, and isn't supplemental to upstream's `CAP_SYS_ADMIN` setting
|
|
||||||
serviceConfig.LockPersonality = true;
|
|
||||||
# serviceConfig.PrivateUsers = true; #< untried, not likely to work since it needs capabilities
|
|
||||||
serviceConfig.PrivateTmp = true;
|
|
||||||
serviceConfig.ProtectClock = true; # syscall filter to prevent changing the RTC
|
|
||||||
serviceConfig.ProtectControlGroups = true;
|
|
||||||
serviceConfig.ProtectHome = true; # makes empty: /home, /root, /run/user
|
|
||||||
serviceConfig.ProtectHostname = true; # prevents changing hostname
|
|
||||||
serviceConfig.ProtectKernelLogs = true; # disable /proc/kmsg, /dev/kmsg
|
|
||||||
serviceConfig.ProtectKernelModules = true; # syscall filter to prevent module calls
|
|
||||||
serviceConfig.ProtectKernelTunables = true;
|
|
||||||
serviceConfig.ProtectSystem = "strict"; # makes read-only all but /dev, /proc, /sys
|
|
||||||
serviceConfig.RestrictAddressFamilies = [
|
|
||||||
"AF_NETLINK"
|
|
||||||
"AF_QIPCRTR"
|
|
||||||
"AF_UNIX"
|
|
||||||
];
|
|
||||||
serviceConfig.RestrictSUIDSGID = true;
|
|
||||||
serviceConfig.SystemCallArchitectures = "native"; # prevents e.g. aarch64 syscalls in the event that the kernel is multi-architecture.
|
|
||||||
|
|
||||||
# from earlier `landlock` sandboxing, i know it needs these directories:
|
|
||||||
# - # "/"
|
|
||||||
# - "/dev" #v modem-power + net are not enough
|
|
||||||
# - # "/dev/modem-power"
|
|
||||||
# - # "/dev/net"
|
|
||||||
# - "/proc"
|
|
||||||
# - # /run #v can likely be reduced more
|
|
||||||
# - "/run/dbus"
|
|
||||||
# - "/run/NetworkManager"
|
|
||||||
# - "/run/resolvconf"
|
|
||||||
# - "/run/systemd"
|
|
||||||
# - "/run/udev"
|
|
||||||
# - "/sys"
|
|
||||||
};
|
|
||||||
|
|
||||||
# so that ModemManager can discover when the modem appears
|
|
||||||
# services.udev.packages = lib.mkIf cfg.enabled [ cfg.package ];
|
|
||||||
}
|
|
|
@ -1,268 +0,0 @@
|
||||||
{ config, pkgs, ... }:
|
|
||||||
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 = [];
|
|
||||||
});
|
|
||||||
# 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;
|
|
||||||
# plugins mostly add support for establishing different VPN connections.
|
|
||||||
# the default plugin set includes mostly proprietary VPNs:
|
|
||||||
# - fortisslvpn (Fortinet)
|
|
||||||
# - iodine (DNS tunnels)
|
|
||||||
# - l2tp
|
|
||||||
# - openconnect (Cisco Anyconnect / Juniper / ocserv)
|
|
||||||
# - openvpn
|
|
||||||
# - vpnc (Cisco VPN)
|
|
||||||
# - sstp
|
|
||||||
#
|
|
||||||
# i don't use these, and notably they drag in huge dependency sets and don't cross compile well.
|
|
||||||
# e.g. openconnect drags in webkitgtk (for SSO)!
|
|
||||||
# networking.networkmanager.plugins = lib.mkForce [];
|
|
||||||
networking.networkmanager.enableDefaultPlugins = false;
|
|
||||||
|
|
||||||
networking.networkmanager.package = networkmanager-split.daemon.overrideAttrs (upstream: {
|
|
||||||
# postPatch = (upstream.postPatch or "") + ''
|
|
||||||
# substituteInPlace src/{core/org.freedesktop.NetworkManager,nm-dispatcher/nm-dispatcher}.conf --replace-fail \
|
|
||||||
# 'user="root"' 'user="networkmanager"'
|
|
||||||
# '';
|
|
||||||
postInstall = (upstream.postInstall or "") + ''
|
|
||||||
# allow the bus to owned by either root or networkmanager users
|
|
||||||
# use the group here, that way ordinary users can be elevated to control networkmanager
|
|
||||||
# (via e.g. `nmcli`)
|
|
||||||
for f in org.freedesktop.NetworkManager.conf nm-dispatcher.conf ; do
|
|
||||||
substitute $out/share/dbus-1/system.d/$f \
|
|
||||||
$out/share/dbus-1/system.d/networkmanager-$f \
|
|
||||||
--replace-fail 'user="root"' 'group="networkmanager"'
|
|
||||||
done
|
|
||||||
|
|
||||||
# remove unused services to prevent any unexpected interactions
|
|
||||||
rm $out/etc/systemd/system/{nm-cloud-setup.service,nm-cloud-setup.timer,nm-priv-helper.service}
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
|
|
||||||
# fixup the services to run as `networkmanager` and with less permissions
|
|
||||||
systemd.services.NetworkManager = {
|
|
||||||
serviceConfig.RuntimeDirectory = "NetworkManager"; #< tells systemd to create /run/NetworkManager
|
|
||||||
# serviceConfig.StateDirectory = "NetworkManager"; #< tells systemd to create /var/lib/NetworkManager
|
|
||||||
serviceConfig.User = "networkmanager";
|
|
||||||
serviceConfig.Group = "networkmanager";
|
|
||||||
serviceConfig.AmbientCapabilities = [
|
|
||||||
# "CAP_DAC_OVERRIDE"
|
|
||||||
"CAP_NET_ADMIN"
|
|
||||||
"CAP_NET_RAW" #< required, else `libndp: ndp_sock_open: Failed to create ICMP6 socket.`
|
|
||||||
"CAP_NET_BIND_SERVICE" #< this *does* seem to be necessary, though i don't understand why. DHCP?
|
|
||||||
# "CAP_SYS_MODULE"
|
|
||||||
# "CAP_AUDIT_WRITE" #< allow writing to the audit log (optional)
|
|
||||||
# "CAP_KILL"
|
|
||||||
];
|
|
||||||
serviceConfig.LockPersonality = true;
|
|
||||||
serviceConfig.NoNewPrivileges = true;
|
|
||||||
serviceConfig.PrivateDevices = true; # remount /dev with just the basics, syscall filter to block @raw-io
|
|
||||||
serviceConfig.PrivateIPC = true;
|
|
||||||
serviceConfig.PrivateTmp = true;
|
|
||||||
# serviceConfig.PrivateUsers = true; #< BREAKS NetworkManager (presumably, it causes a new user namespace, breaking CAP_NET_ADMIN & others). "platform-linux: do-change-link[3]: failure 1 (Operation not permitted)"
|
|
||||||
serviceConfig.ProtectClock = true; # syscall filter to prevent changing the RTC
|
|
||||||
serviceConfig.ProtectControlGroups = true;
|
|
||||||
serviceConfig.ProtectHome = true; # makes empty: /home, /root, /run/user
|
|
||||||
serviceConfig.ProtectHostname = true; # probably not upstreamable: prevents changing hostname
|
|
||||||
serviceConfig.ProtectKernelLogs = true; # disable /proc/kmsg, /dev/kmsg
|
|
||||||
serviceConfig.ProtectKernelModules = true; # syscall filter to prevent module calls (probably not upstreamable: NM will want to load modules like `ppp`)
|
|
||||||
serviceConfig.ProtectKernelTunables = true; # but NM might need to write /proc/sys/net/...
|
|
||||||
serviceConfig.ProtectSystem = "strict"; # makes read-only: all but /dev, /proc, /sys.
|
|
||||||
serviceConfig.RestrictAddressFamilies = [
|
|
||||||
"AF_INET"
|
|
||||||
"AF_INET6"
|
|
||||||
"AF_NETLINK" # breaks near DHCP without this
|
|
||||||
"AF_PACKET" # for DHCP
|
|
||||||
"AF_UNIX"
|
|
||||||
# AF_ALG ?
|
|
||||||
# AF_BLUETOOTH ?
|
|
||||||
# AF_BRIDGE ?
|
|
||||||
];
|
|
||||||
serviceConfig.RestrictSUIDSGID = true;
|
|
||||||
serviceConfig.SystemCallArchitectures = "native"; # prevents e.g. aarch64 syscalls in the event that the kernel is multi-architecture.
|
|
||||||
# from earlier `landlock` sandboxing, i know it needs these directories:
|
|
||||||
# - "/proc/net"
|
|
||||||
# - "/proc/sys/net"
|
|
||||||
# - "/run/NetworkManager"
|
|
||||||
# - "/run/systemd" # for trust-dns-nmhook
|
|
||||||
# - "/run/udev"
|
|
||||||
# - # "/run/wg-home.priv"
|
|
||||||
# - "/sys/class"
|
|
||||||
# - "/sys/devices"
|
|
||||||
# - "/var/lib/NetworkManager"
|
|
||||||
# - "/var/lib/trust-dns" #< for trust-dns-nmhook
|
|
||||||
# - "/run/systemd"
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.NetworkManager-wait-online = {
|
|
||||||
serviceConfig.User = "networkmanager";
|
|
||||||
serviceConfig.Group = "networkmanager";
|
|
||||||
};
|
|
||||||
|
|
||||||
# fix NetworkManager-dispatcher to actually run as a daemon,
|
|
||||||
# and sandbox it a bit
|
|
||||||
systemd.services.NetworkManager-dispatcher = {
|
|
||||||
after = [ "trust-dns-localhost.service" ]; #< so that /var/lib/trust-dns will exist
|
|
||||||
# serviceConfig.ExecStart = [
|
|
||||||
# "" # first blank line is to clear the upstream `ExecStart` field.
|
|
||||||
# "${cfg.package}/libexec/nm-dispatcher --persist" # --persist is needed for it to actually run as a daemon
|
|
||||||
# ];
|
|
||||||
# serviceConfig.Restart = "always";
|
|
||||||
# serviceConfig.RestartSec = "1s";
|
|
||||||
|
|
||||||
# serviceConfig.DynamicUser = true; #< not possible, else we lose group perms (so can't write to `trust-dns`'s files in the nm hook)
|
|
||||||
serviceConfig.User = "networkmanager"; # TODO: should arguably use `DynamicUser`
|
|
||||||
serviceConfig.Group = "networkmanager";
|
|
||||||
serviceConfig.LockPersonality = true;
|
|
||||||
serviceConfig.NoNewPrivileges = true;
|
|
||||||
serviceConfig.PrivateDevices = true; # remount /dev with just the basics, syscall filter to block @raw-io
|
|
||||||
serviceConfig.PrivateIPC = true;
|
|
||||||
serviceConfig.PrivateTmp = true;
|
|
||||||
serviceConfig.PrivateUsers = true;
|
|
||||||
serviceConfig.ProtectClock = true; # syscall filter to prevent changing the RTC
|
|
||||||
serviceConfig.ProtectControlGroups = true;
|
|
||||||
serviceConfig.ProtectHome = true; # makes empty: /home, /root, /run/user
|
|
||||||
serviceConfig.ProtectHostname = true; # probably not upstreamable: prevents changing hostname
|
|
||||||
serviceConfig.ProtectKernelLogs = true; # disable /proc/kmsg, /dev/kmsg
|
|
||||||
serviceConfig.ProtectKernelModules = true; # syscall filter to prevent module calls
|
|
||||||
serviceConfig.ProtectKernelTunables = true;
|
|
||||||
serviceConfig.ProtectSystem = "full"; # makes read-only: /boot, /etc/, /usr. `strict` isn't possible due to trust-dns hook
|
|
||||||
serviceConfig.RestrictAddressFamilies = [
|
|
||||||
"AF_UNIX" # required, probably for dbus or systemd connectivity
|
|
||||||
];
|
|
||||||
serviceConfig.RestrictSUIDSGID = true;
|
|
||||||
serviceConfig.SystemCallArchitectures = "native"; # prevents e.g. aarch64 syscalls in the event that the kernel is multi-architecture.
|
|
||||||
};
|
|
||||||
|
|
||||||
# harden wpa_supplicant (used by NetworkManager)
|
|
||||||
systemd.services.wpa_supplicant = {
|
|
||||||
serviceConfig.User = "networkmanager";
|
|
||||||
serviceConfig.Group = "networkmanager";
|
|
||||||
serviceConfig.AmbientCapabilities = [
|
|
||||||
"CAP_NET_ADMIN"
|
|
||||||
"CAP_NET_RAW"
|
|
||||||
];
|
|
||||||
serviceConfig.LockPersonality = true;
|
|
||||||
serviceConfig.NoNewPrivileges = true;
|
|
||||||
# serviceConfig.PrivateDevices = true; # untried, not likely to work. remount /dev with just the basics, syscall filter to block @raw-io
|
|
||||||
serviceConfig.PrivateIPC = true;
|
|
||||||
serviceConfig.PrivateTmp = true;
|
|
||||||
# serviceConfig.PrivateUsers = true; #< untried, not likely to work
|
|
||||||
serviceConfig.ProtectClock = true; # syscall filter to prevent changing the RTC
|
|
||||||
serviceConfig.ProtectControlGroups = true;
|
|
||||||
serviceConfig.ProtectHome = true; # makes empty: /home, /root, /run/user
|
|
||||||
serviceConfig.ProtectHostname = true; # prevents changing hostname
|
|
||||||
serviceConfig.ProtectKernelLogs = true; # disable /proc/kmsg, /dev/kmsg
|
|
||||||
serviceConfig.ProtectKernelModules = true; # syscall filter to prevent module calls
|
|
||||||
serviceConfig.ProtectKernelTunables = true; #< N.B.: i think this makes certain /proc writes fail
|
|
||||||
serviceConfig.ProtectSystem = "strict"; # makes read-only: all but /dev, /proc, /sys.
|
|
||||||
serviceConfig.RestrictAddressFamilies = [
|
|
||||||
"AF_INET" #< required
|
|
||||||
"AF_INET6"
|
|
||||||
"AF_NETLINK" #< required
|
|
||||||
"AF_PACKET" #< required
|
|
||||||
"AF_UNIX" #< required (wpa_supplicant wants to use dbus)
|
|
||||||
];
|
|
||||||
serviceConfig.RestrictSUIDSGID = true;
|
|
||||||
serviceConfig.SystemCallArchitectures = "native"; # prevents e.g. aarch64 syscalls in the event that the kernel is multi-architecture.
|
|
||||||
|
|
||||||
# from earlier `landlock` sandboxing, i know it needs only these paths:
|
|
||||||
# - "/dev/net"
|
|
||||||
# - "/dev/rfkill"
|
|
||||||
# - "/proc/sys/net"
|
|
||||||
# - "/sys/class/net"
|
|
||||||
# - "/sys/devices"
|
|
||||||
# - "/run/systemd"
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.networkmanager.settings = {
|
|
||||||
# keyfile.path = where networkmanager should look for connection credentials
|
|
||||||
keyfile.path = "/var/lib/NetworkManager/system-connections";
|
|
||||||
|
|
||||||
# wifi.backend = "wpa_supplicant"; #< default
|
|
||||||
# wifi.scan-rand-mac-address = true; #< default
|
|
||||||
|
|
||||||
# logging.audit = false; #< default
|
|
||||||
logging.level = "INFO";
|
|
||||||
|
|
||||||
# main.dhcp = "internal"; #< default
|
|
||||||
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
|
|
||||||
"none"
|
|
||||||
else
|
|
||||||
"internal"
|
|
||||||
;
|
|
||||||
main.systemd-resolved = false;
|
|
||||||
};
|
|
||||||
environment.etc."NetworkManager/system-connections".source = "/var/lib/NetworkManager/system-connections";
|
|
||||||
|
|
||||||
# the default backend is "wpa_supplicant".
|
|
||||||
# wpa_supplicant reliably picks weak APs to connect to.
|
|
||||||
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
|
|
||||||
# iwd is an alternative that shouldn't have this problem
|
|
||||||
# docs:
|
|
||||||
# - <https://nixos.wiki/wiki/Iwd>
|
|
||||||
# - <https://iwd.wiki.kernel.org/networkmanager>
|
|
||||||
# - `man iwd.config` for global config
|
|
||||||
# - `man iwd.network` for per-SSID config
|
|
||||||
# use `iwctl` to control
|
|
||||||
# networking.networkmanager.wifi.backend = "iwd";
|
|
||||||
# networking.wireless.iwd.enable = true;
|
|
||||||
# networking.wireless.iwd.settings = {
|
|
||||||
# # auto-connect to a stronger network if signal drops below this value
|
|
||||||
# # bedroom -> bedroom connection is -35 to -40 dBm
|
|
||||||
# # bedroom -> living room connection is -60 dBm
|
|
||||||
# General.RoamThreshold = "-52"; # default -70
|
|
||||||
# General.RoamThreshold5G = "-52"; # default -76
|
|
||||||
# };
|
|
||||||
|
|
||||||
# allow networkmanager to control systemd-resolved,
|
|
||||||
# which it needs to do to apply new DNS settings when using systemd-resolved.
|
|
||||||
security.polkit.extraConfig = ''
|
|
||||||
polkit.addRule(function(action, subject) {
|
|
||||||
if (subject.isInGroup("networkmanager") && action.id.indexOf("org.freedesktop.resolve1.") == 0) {
|
|
||||||
return polkit.Result.YES;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
'';
|
|
||||||
|
|
||||||
users.users.networkmanager = {
|
|
||||||
isSystemUser = true;
|
|
||||||
group = "networkmanager";
|
|
||||||
extraGroups = [ "trust-dns" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# there is, unfortunately, no proper interface by which to plumb wpa_supplicant into the NixOS service, except by overlay.
|
|
||||||
nixpkgs.overlays = [(self: super: {
|
|
||||||
wpa_supplicant = super.wpa_supplicant.overrideAttrs (upstream: {
|
|
||||||
# postPatch = (upstream.postPatch or "") + ''
|
|
||||||
# substituteInPlace wpa_supplicant/dbus/dbus-wpa_supplicant.conf --replace-fail \
|
|
||||||
# 'user="root"' 'user="networkmanager"'
|
|
||||||
# '';
|
|
||||||
postInstall = (upstream.postInstall or "") + ''
|
|
||||||
substitute $out/share/dbus-1/system.d/dbus-wpa_supplicant.conf \
|
|
||||||
$out/share/dbus-1/system.d/networkmanager-wpa_supplicant.conf \
|
|
||||||
--replace-fail 'user="root"' 'group="networkmanager"'
|
|
||||||
'';
|
|
||||||
|
|
||||||
postFixup = (upstream.postFixup or "") + ''
|
|
||||||
# remove unused services to avoid unexpected interactions
|
|
||||||
rm $out/etc/systemd/system/{wpa_supplicant-nl80211@,wpa_supplicant-wired@,wpa_supplicant@}.service
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
})];
|
|
||||||
}
|
|
|
@ -7,62 +7,50 @@
|
||||||
|
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
# N.B.: OVPN issues each key (i.e. device) a different IP (addrV4), and requires you use it.
|
def-ovpn = name: { endpoint, publicKey, addrV4, id }: {
|
||||||
# the IP it issues can be used to connect to any of their VPNs.
|
sane.vpn."ovpnd-${name}" = {
|
||||||
# effectively the IP and key map 1-to-1.
|
inherit endpoint publicKey addrV4 id;
|
||||||
# it seems to still be possible to keep two active tunnels on one device, using the same key/IP address, though.
|
privateKeyFile = config.sops.secrets."wg/ovpnd_${name}_privkey".path;
|
||||||
def-ovpn = name: { endpoint, publicKey, id }: let
|
|
||||||
inherit (config.sane.ovpn) addrV4;
|
|
||||||
in {
|
|
||||||
sane.vpn."ovpnd-${name}" = lib.mkIf (addrV4 != null) {
|
|
||||||
inherit addrV4 endpoint publicKey id;
|
|
||||||
privateKeyFile = config.sops.secrets."ovpn_privkey".path;
|
|
||||||
dns = [
|
dns = [
|
||||||
"46.227.67.134"
|
"46.227.67.134"
|
||||||
"192.165.9.158"
|
"192.165.9.158"
|
||||||
# "2a07:a880:4601:10f0:cd45::1"
|
|
||||||
# "2001:67c:750:1:cafe:cd45::1"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
sops.secrets."ovpn_privkey" = lib.mkIf (addrV4 != null) {
|
sops.secrets."wg/ovpnd_${name}_privkey" = {
|
||||||
# needs to be readable by systemd-network or else it says "Ignoring network device" and doesn't expose it to networkctl.
|
# needs to be readable by systemd-network or else it says "Ignoring network device" and doesn't expose it to networkctl.
|
||||||
owner = "systemd-network";
|
owner = "systemd-network";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in lib.mkMerge [
|
||||||
options = with lib; {
|
|
||||||
sane.ovpn.addrV4 = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
ovpn issues one IP address per device.
|
|
||||||
set `null` to disable OVPN for this host.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkMerge [
|
|
||||||
(def-ovpn "us" {
|
(def-ovpn "us" {
|
||||||
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
|
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
|
||||||
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
|
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
|
||||||
id = 1;
|
id = 1;
|
||||||
|
addrV4 = "172.27.237.218";
|
||||||
|
# addrV6 = "fd00:0000:1337:cafe:1111:1111:ab00:4c8f";
|
||||||
})
|
})
|
||||||
|
# TODO: us-atl disabled until i can give it a different link-local address and wireguard key than us-mi
|
||||||
|
# (def-ovpn "us-atl" {
|
||||||
|
# endpoint = "vpn18.prd.atlanta.ovpn.com:9929";
|
||||||
|
# publicKey = "Dpg/4v5s9u0YbrXukfrMpkA+XQqKIFpf8ZFgyw0IkE0=";
|
||||||
|
# address = [
|
||||||
|
# "172.21.182.178/32"
|
||||||
|
# "fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
|
||||||
|
# ];
|
||||||
|
# })
|
||||||
(def-ovpn "us-mi" {
|
(def-ovpn "us-mi" {
|
||||||
endpoint = "vpn34.prd.miami.ovpn.com:9929";
|
endpoint = "vpn34.prd.miami.ovpn.com:9929";
|
||||||
publicKey = "VtJz2irbu8mdkIQvzlsYhU+k9d55or9mx4A2a14t0V0=";
|
publicKey = "VtJz2irbu8mdkIQvzlsYhU+k9d55or9mx4A2a14t0V0=";
|
||||||
id = 2;
|
id = 2;
|
||||||
|
addrV4 = "172.21.182.178";
|
||||||
|
# addrV6 = "fd00:0000:1337:cafe:1111:1111:cfcb:27e3";
|
||||||
})
|
})
|
||||||
(def-ovpn "ukr" {
|
(def-ovpn "ukr" {
|
||||||
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
|
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
|
||||||
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
|
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
|
||||||
id = 3;
|
id = 3;
|
||||||
|
addrV4 = "172.18.180.159";
|
||||||
|
# addrV6 = "fd00:0000:1337:cafe:1111:1111:ec5c:add3";
|
||||||
})
|
})
|
||||||
# TODO: us-atl disabled until i need it again, i guess.
|
]
|
||||||
# (def-ovpn "us-atl" {
|
|
||||||
# endpoint = "vpn18.prd.atlanta.ovpn.com:9929";
|
|
||||||
# publicKey = "Dpg/4v5s9u0YbrXukfrMpkA+XQqKIFpf8ZFgyw0IkE0=";
|
|
||||||
# id = 4;
|
|
||||||
# })
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
|
@ -53,26 +53,22 @@
|
||||||
|
|
||||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages.
|
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages.
|
||||||
# this is actually a no-op, and the real action happens in assigning `nix.settings.nix-path`.
|
# this is actually a no-op, and the real action happens in assigning `nix.settings.nix-path`.
|
||||||
nix.nixPath = (lib.optionals (config.sane.maxBuildCost >= 2) [
|
nix.nixPath = (lib.optionals config.sane.enableSlowPrograms [
|
||||||
"nixpkgs=${pkgs.path}"
|
"nixpkgs=${pkgs.path}"
|
||||||
]) ++ [
|
]) ++ [
|
||||||
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
|
# 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"
|
# "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
|
# 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.
|
# to avoid switching so much during development
|
||||||
# TODO: it would be nice to remove this someday!
|
"nixpkgs-overlays=/home/colin/dev/nixos/hosts/common/nix/overlay"
|
||||||
# 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"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# ensure new deployments have a source of this repo with which they can bootstrap.
|
# 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`.
|
# 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) {
|
environment.etc."nixos" = lib.mkIf config.sane.enableSlowPrograms {
|
||||||
source = pkgs.sane-nix-files;
|
source = ../../..;
|
||||||
};
|
};
|
||||||
environment.etc."nix/registry.json" = lib.mkIf (config.sane.maxBuildCost < 3) {
|
environment.etc."nix/registry.json" = lib.mkIf (!config.sane.enableSlowPrograms) {
|
||||||
enable = false;
|
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
|
|
@ -1,127 +1,7 @@
|
||||||
# strictly *decrease* the scope of the default nixos installation/config
|
# strictly *decrease* the scope of the default nixos installation/config
|
||||||
|
|
||||||
{ lib, pkgs, ... }:
|
{ lib, ... }:
|
||||||
let
|
|
||||||
suidlessPam = pkgs.pam.overrideAttrs (upstream: {
|
|
||||||
# nixpkgs' pam hardcodes unix_chkpwd path to the /run/wrappers one,
|
|
||||||
# but i don't want the wrapper, so undo that.
|
|
||||||
# ideally i would patch this via an overlay, but pam is in the bootstrap so that forces a full rebuild.
|
|
||||||
# TODO: add a `package` option to the nixos' pam module and substitute it that way.
|
|
||||||
postPatch = (if upstream.postPatch != null then upstream.postPatch else "") + ''
|
|
||||||
substituteInPlace modules/pam_unix/Makefile.am --replace-fail \
|
|
||||||
"/run/wrappers/bin/unix_chkpwd" "$out/bin/unix_chkpwd"
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
# remove a few items from /run/wrappers we don't need.
|
|
||||||
options.security.wrappers = lib.mkOption {
|
|
||||||
apply = lib.filterAttrs (name: _: !(builtins.elem name [
|
|
||||||
# from <repo:nixos/nixpkgs:nixos/modules/security/polkit.nix>
|
|
||||||
"pkexec"
|
|
||||||
"polkit-agent-helper-1" #< used by systemd; without this you'll have to `sudo systemctl daemon-reload` instead of unauth'd `systemctl daemon-reload`
|
|
||||||
# from <repo:nixos/nixpkgs:nixos/modules/services/system/dbus.nix>
|
|
||||||
"dbus-daemon-launch-helper"
|
|
||||||
# from <repo:nixos/nixpkgs:nixos/modules/security/wrappers/default.nix>
|
|
||||||
"fusermount" #< only needed if you want to mount entries declared in /etc/fstab or mtab as unprivileged user
|
|
||||||
"fusermount3"
|
|
||||||
"mount" #< only needed if you want to mount entries declared in /etc/fstab or mtab as unprivileged user
|
|
||||||
"umount"
|
|
||||||
# from <repo:nixos/nixpkgs:nixos/modules/programs/shadow.nix>
|
|
||||||
"newgidmap"
|
|
||||||
"newgrp"
|
|
||||||
"newuidmap"
|
|
||||||
"sg"
|
|
||||||
"su"
|
|
||||||
# from: <repo:nixos/nixpkgs:nixos/modules/security/pam.nix>
|
|
||||||
# requires associated `pam` patch to not hardcode unix_chkpwd path
|
|
||||||
"unix_chkpwd"
|
|
||||||
]));
|
|
||||||
};
|
|
||||||
options.security.pam.services = lib.mkOption {
|
|
||||||
apply = services: let
|
|
||||||
filtered = lib.filterAttrs (name: _: !(builtins.elem name [
|
|
||||||
# from <repo:nixos/nixpkgs:nixos/modules/security/pam.nix>
|
|
||||||
"i3lock"
|
|
||||||
"i3lock-color"
|
|
||||||
"vlock"
|
|
||||||
"xlock"
|
|
||||||
"xscreensaver"
|
|
||||||
"runuser"
|
|
||||||
"runuser-l"
|
|
||||||
# from ??
|
|
||||||
"chfn"
|
|
||||||
"chpasswd"
|
|
||||||
"chsh"
|
|
||||||
"groupadd"
|
|
||||||
"groupdel"
|
|
||||||
"groupmems"
|
|
||||||
"groupmod"
|
|
||||||
"useradd"
|
|
||||||
"userdel"
|
|
||||||
"usermod"
|
|
||||||
# from <repo:nixos/nixpkgs:nixos/modules/system/boot/systemd/user.nix>
|
|
||||||
"systemd-user" #< N.B.: this causes the `systemd --user` service manager to not be started!
|
|
||||||
])) services;
|
|
||||||
in lib.mapAttrs (_serviceName: service: service // {
|
|
||||||
# replace references with the old pam_unix, which calls into /run/wrappers/bin/unix_chkpwd,
|
|
||||||
# with a pam_unix that calls into unix_chkpwd via the nix store.
|
|
||||||
# TODO: use `security.pam.package` instead once <https://github.com/NixOS/nixpkgs/pull/314791> lands.
|
|
||||||
text = lib.replaceStrings [" pam_unix.so" ] [ " ${suidlessPam}/lib/security/pam_unix.so" ] service.text;
|
|
||||||
}) filtered;
|
|
||||||
};
|
|
||||||
|
|
||||||
options.environment.systemPackages = lib.mkOption {
|
|
||||||
# see: <repo:nixos/nixpkgs:nixos/modules/config/system-path.nix>
|
|
||||||
# it's 31 "requiredPackages", with no explanation of why they're "required"...
|
|
||||||
# most of these can be safely removed without breaking the *boot*,
|
|
||||||
# but some core system services DO implicitly depend on them.
|
|
||||||
# TODO: see which more of these i can remove (or shadow/sandbox)
|
|
||||||
apply = let
|
|
||||||
requiredPackages = builtins.map (pkg: lib.setPrio ((pkg.meta.priority or 5) + 3) pkg) [
|
|
||||||
# pkgs.acl
|
|
||||||
# pkgs.attr
|
|
||||||
# pkgs.bashInteractive
|
|
||||||
# pkgs.bzip2
|
|
||||||
# pkgs.coreutils-full
|
|
||||||
# pkgs.cpio
|
|
||||||
# pkgs.curl
|
|
||||||
# pkgs.diffutils
|
|
||||||
# pkgs.findutils
|
|
||||||
# pkgs.gawk
|
|
||||||
# pkgs.stdenv.cc.libc
|
|
||||||
# pkgs.getent
|
|
||||||
# pkgs.getconf
|
|
||||||
# pkgs.gnugrep
|
|
||||||
# pkgs.gnupatch
|
|
||||||
# pkgs.gnused
|
|
||||||
# pkgs.gnutar
|
|
||||||
# pkgs.gzip
|
|
||||||
# pkgs.xz
|
|
||||||
pkgs.less
|
|
||||||
# pkgs.libcap #< implicitly required by NetworkManager/wpa_supplicant!
|
|
||||||
# pkgs.ncurses
|
|
||||||
pkgs.netcat
|
|
||||||
# config.programs.ssh.package
|
|
||||||
# pkgs.mkpasswd
|
|
||||||
pkgs.procps
|
|
||||||
# pkgs.su
|
|
||||||
# pkgs.time
|
|
||||||
# pkgs.util-linux
|
|
||||||
# pkgs.which
|
|
||||||
# pkgs.zstd
|
|
||||||
];
|
|
||||||
in lib.filter (p: ! builtins.elem p requiredPackages);
|
|
||||||
};
|
|
||||||
|
|
||||||
options.system.fsPackages = lib.mkOption {
|
|
||||||
# <repo:nixos/nixpkgs:nixos/modules/tasks/filesystems/vfat.nix> adds `mtools` and `dosfstools`
|
|
||||||
# dosfstools actually makes its way into the initrd (`fsck.vfat`).
|
|
||||||
# mtools is like "MS-DOS for Linux", ancient functionality i'll never use.
|
|
||||||
apply = lib.filter (p: p != pkgs.mtools);
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
# disable non-required packages like nano, perl, rsync, strace
|
# disable non-required packages like nano, perl, rsync, strace
|
||||||
environment.defaultPackages = [];
|
environment.defaultPackages = [];
|
||||||
|
|
||||||
|
@ -184,33 +64,4 @@ in
|
||||||
# pkgs.udisks
|
# pkgs.udisks
|
||||||
# pkgs.wpa_supplicant
|
# pkgs.wpa_supplicant
|
||||||
];
|
];
|
||||||
|
|
||||||
# systemd by default forces shitty defaults for e.g. /tmp/.X11-unix.
|
|
||||||
# nixos propagates those in: <nixos/modules/system/boot/systemd/tmpfiles.nix>
|
|
||||||
# by overwriting this with an empty file, we can effectively remove it.
|
|
||||||
environment.etc."tmpfiles.d/x11.conf".text = "# (removed by Colin)";
|
|
||||||
|
|
||||||
# see: <nixos/modules/tasks/swraid.nix>
|
|
||||||
# it was enabled by default before 23.11
|
|
||||||
boot.swraid.enable = lib.mkDefault false;
|
|
||||||
|
|
||||||
# see: <nixos/modules/tasks/bcache.nix>
|
|
||||||
# these allow you to use the Linux block cache (cool! doesn't need to be a default though)
|
|
||||||
boot.bcache.enable = lib.mkDefault false;
|
|
||||||
|
|
||||||
# see: <nixos/modules/system/boot/kernel.nix>
|
|
||||||
# by default, it adds to boot.initrd.availableKernelModules:
|
|
||||||
# - SATA: "ahci" "sata_nv" "sata_via" "sata_sis" "sata_uli" "ata_piix" "pata_marvell"
|
|
||||||
# - "nvme"
|
|
||||||
# - scsi: "sd_mod" "sr_mod"
|
|
||||||
# - SD/eMMC: "mmc_block"
|
|
||||||
# - USB keyboards: "uhci_hcd" "ehci_hcd" "ehci_pci" "ohci_hcd" "ohci_pci" "xhci_hcd" "xhci_pci" "usbhid" "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft" "hid_cherry" "hid_corsair"
|
|
||||||
# - LVM: "dm_mod"
|
|
||||||
# - on x86 only: more keyboard stuff: "pcips2" "atkbd" "i8042"
|
|
||||||
|
|
||||||
boot.initrd.includeDefaultModules = lib.mkDefault false;
|
|
||||||
|
|
||||||
# see: <repo:nixos/nixpkgs:nixos/modules/virtualisation/nixos-containers.nix>
|
|
||||||
boot.enableContainers = lib.mkDefault false;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,13 @@ in
|
||||||
|
|
||||||
services.abaddon = {
|
services.abaddon = {
|
||||||
description = "unofficial Discord chat client";
|
description = "unofficial Discord chat client";
|
||||||
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
||||||
command = "abaddon";
|
serviceConfig = {
|
||||||
|
ExecStart = "${cfg.package}/bin/abaddon";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "20s";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,33 +15,39 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
# upstream alsa ships with PinePhone audio configs, but they don't actually produce sound.
|
# upstream alsa ships with PinePhone audio configs, but they don't actually produce sound.
|
||||||
# - still true as of 2024-05-26
|
# see: <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||||
# - see: <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
# these audio files come from some revision of:
|
||||||
|
# - <https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone>
|
||||||
#
|
#
|
||||||
# we can substitute working UCM conf in two ways:
|
# alternative to patching is to plumb `ALSA_CONFIG_UCM2 = "${./ucm2}"` environment variable into the relevant places
|
||||||
# 1. nixpkgs' override for the `alsa-ucm-conf` package
|
# e.g. `systemd.services.pulseaudio.environment`.
|
||||||
# - that forces a rebuild of ~500 packages (including webkitgtk).
|
# that leaves more opportunity for gaps (i.e. missing a service),
|
||||||
# 2. set ALSA_CONFIG_UCM2 = /path/to/ucm2 in the relevant places
|
# on the other hand this method causes about 500 packages to be rebuilt (including qt5 and webkitgtk).
|
||||||
# - e.g. pulsewire service.
|
|
||||||
# - easy to miss places, though.
|
|
||||||
#
|
#
|
||||||
# alsa-ucm-pinephone-manjaro (2024-05-26):
|
# note that with these files, the following audio device support:
|
||||||
# - headphones work
|
# - headphones work.
|
||||||
# - "internal earpiece" works
|
# - "internal earpiece" works.
|
||||||
# - "internal speaker" is silent (maybe hardware issue)
|
# - "internal speaker" doesn't work (but that's probably because i broke the ribbon cable)
|
||||||
# - 3.5mm connection is flapping when playing to my car, which eventually breaks audio and requires restarting wireplumber
|
# - "analog output" doesn't work.
|
||||||
# packageUnwrapped = pkgs.alsa-ucm-pinephone-manjaro.override {
|
packageUnwrapped = pkgs.alsa-ucm-conf.overrideAttrs (upstream: {
|
||||||
# inherit (cfg.config) preferEarpiece;
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
# };
|
cp ${./ucm2/PinePhone}/* ucm2/Allwinner/A64/PinePhone/
|
||||||
# alsa-ucm-pinephone-pmos (2024-05-26):
|
|
||||||
# - headphones work
|
|
||||||
# - "internal earpiece" works
|
|
||||||
# - "internal speaker" is silent (maybe hardware issue)
|
|
||||||
packageUnwrapped = pkgs.alsa-ucm-pinephone-pmos.override {
|
|
||||||
inherit (cfg.config) preferEarpiece;
|
|
||||||
};
|
|
||||||
|
|
||||||
sandbox.enable = false; #< only provides $out/share/alsa
|
# fix the self-contained ucm files i source from to have correct path within the alsa-ucm-conf source tree
|
||||||
|
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
|
||||||
|
--replace 'HiFi.conf' '/Allwinner/A64/PinePhone/HiFi.conf'
|
||||||
|
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
|
||||||
|
--replace 'VoiceCall.conf' '/Allwinner/A64/PinePhone/VoiceCall.conf'
|
||||||
|
'' + lib.optionalString cfg.config.preferEarpiece ''
|
||||||
|
# decrease the priority of the internal speaker so that sounds are routed
|
||||||
|
# to the earpiece by default.
|
||||||
|
# this is just personal preference.
|
||||||
|
substituteInPlace ucm2/Allwinner/A64/PinePhone/* \
|
||||||
|
--replace 'PlaybackPriority 300' 'PlaybackPriority 100'
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
|
sandbox.enable = false; #< only provides #out/share/alsa
|
||||||
|
|
||||||
# alsa-lib package only looks in its $out/share/alsa to find runtime config data, by default.
|
# alsa-lib package only looks in its $out/share/alsa to find runtime config data, by default.
|
||||||
# but ALSA_CONFIG_UCM2 is an env var that can override that.
|
# but ALSA_CONFIG_UCM2 is an env var that can override that.
|
||||||
|
|
|
@ -30,8 +30,6 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
buildCost = 1;
|
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,6 @@ in
|
||||||
];
|
];
|
||||||
|
|
||||||
sysadminUtils = declPackageSet [
|
sysadminUtils = declPackageSet [
|
||||||
"ausyscall"
|
|
||||||
"bridge-utils" # for brctl; debug linux "bridge" inet devices
|
"bridge-utils" # for brctl; debug linux "bridge" inet devices
|
||||||
"btrfs-progs"
|
"btrfs-progs"
|
||||||
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
||||||
|
@ -44,13 +43,11 @@ in
|
||||||
"dtc" # device tree [de]compiler
|
"dtc" # device tree [de]compiler
|
||||||
"e2fsprogs" # resize2fs
|
"e2fsprogs" # resize2fs
|
||||||
"efibootmgr"
|
"efibootmgr"
|
||||||
"errno"
|
|
||||||
"ethtool"
|
"ethtool"
|
||||||
"fatresize"
|
"fatresize"
|
||||||
"fd"
|
"fd"
|
||||||
"file"
|
"file"
|
||||||
"forkstat" # monitor every spawned/forked process
|
"forkstat" # monitor every spawned/forked process
|
||||||
"free"
|
|
||||||
# "fwupd"
|
# "fwupd"
|
||||||
"gawk"
|
"gawk"
|
||||||
"gdb" # to debug segfaults
|
"gdb" # to debug segfaults
|
||||||
|
@ -66,29 +63,22 @@ in
|
||||||
"jq"
|
"jq"
|
||||||
"killall"
|
"killall"
|
||||||
"less"
|
"less"
|
||||||
"lftp"
|
|
||||||
# "libcap_ng" # for `netcap`
|
# "libcap_ng" # for `netcap`
|
||||||
"lsof"
|
"lsof"
|
||||||
"man-pages"
|
|
||||||
"man-pages-posix"
|
|
||||||
# "miniupnpc"
|
# "miniupnpc"
|
||||||
"mmcli"
|
|
||||||
"nano"
|
"nano"
|
||||||
# "ncdu" # ncurses disk usage. doesn't cross compile (zig)
|
# "ncdu" # ncurses disk usage. doesn't cross compile (zig)
|
||||||
"neovim"
|
"neovim"
|
||||||
"netcat"
|
"netcat"
|
||||||
"nethogs"
|
"nethogs"
|
||||||
"nmap"
|
"nmap"
|
||||||
"nmcli"
|
|
||||||
"nvme-cli" # nvme
|
"nvme-cli" # nvme
|
||||||
# "openssl"
|
# "openssl"
|
||||||
"parted"
|
"parted"
|
||||||
"pciutils"
|
"pciutils"
|
||||||
"powertop"
|
"powertop"
|
||||||
"ps"
|
|
||||||
"pstree"
|
"pstree"
|
||||||
"ripgrep"
|
"ripgrep"
|
||||||
"s6-rc" # service manager
|
|
||||||
"screen"
|
"screen"
|
||||||
"smartmontools" # smartctl
|
"smartmontools" # smartctl
|
||||||
# "socat"
|
# "socat"
|
||||||
|
@ -98,8 +88,6 @@ in
|
||||||
"tree"
|
"tree"
|
||||||
"usbutils" # lsusb
|
"usbutils" # lsusb
|
||||||
"util-linux" # lsblk, lscpu, etc
|
"util-linux" # lsblk, lscpu, etc
|
||||||
"valgrind"
|
|
||||||
"watch"
|
|
||||||
"wget"
|
"wget"
|
||||||
"wirelesstools" # iwlist
|
"wirelesstools" # iwlist
|
||||||
# "xq" # jq for XML
|
# "xq" # jq for XML
|
||||||
|
@ -119,6 +107,7 @@ in
|
||||||
# - debugging?
|
# - debugging?
|
||||||
consoleUtils = declPackageSet [
|
consoleUtils = declPackageSet [
|
||||||
"alsaUtils" # for aplay, speaker-test
|
"alsaUtils" # for aplay, speaker-test
|
||||||
|
"strings"
|
||||||
# "cdrtools"
|
# "cdrtools"
|
||||||
# "clinfo"
|
# "clinfo"
|
||||||
# "dmidecode"
|
# "dmidecode"
|
||||||
|
@ -133,7 +122,6 @@ in
|
||||||
# "gopass"
|
# "gopass"
|
||||||
# "gopass-jsonapi"
|
# "gopass-jsonapi"
|
||||||
# "helix" # text editor
|
# "helix" # text editor
|
||||||
"htop" # needed as a user package, for ~/.config/htop
|
|
||||||
# "libsecret" # for managing user keyrings (secret-tool)
|
# "libsecret" # for managing user keyrings (secret-tool)
|
||||||
# "lm_sensors" # for sensors-detect
|
# "lm_sensors" # for sensors-detect
|
||||||
# "lshw"
|
# "lshw"
|
||||||
|
@ -147,7 +135,6 @@ in
|
||||||
"nmon"
|
"nmon"
|
||||||
# "node2nix"
|
# "node2nix"
|
||||||
# "oathToolkit" # for oathtool
|
# "oathToolkit" # for oathtool
|
||||||
"objdump"
|
|
||||||
# "ponymix"
|
# "ponymix"
|
||||||
"pulsemixer"
|
"pulsemixer"
|
||||||
"python3-repl"
|
"python3-repl"
|
||||||
|
@ -160,7 +147,6 @@ in
|
||||||
"sops" # for manually viewing secrets; outside `sane-secrets` (TODO: improve sane-secrets!)
|
"sops" # for manually viewing secrets; outside `sane-secrets` (TODO: improve sane-secrets!)
|
||||||
"speedtest-cli"
|
"speedtest-cli"
|
||||||
# "ssh-to-age"
|
# "ssh-to-age"
|
||||||
"strings"
|
|
||||||
"sudo"
|
"sudo"
|
||||||
# "tageditor" # music tagging
|
# "tageditor" # music tagging
|
||||||
# "unar"
|
# "unar"
|
||||||
|
@ -185,7 +171,6 @@ in
|
||||||
"ffmpeg"
|
"ffmpeg"
|
||||||
"go2tv" # cast videos to UPNP/DLNA device (i.e. tv).
|
"go2tv" # cast videos to UPNP/DLNA device (i.e. tv).
|
||||||
"imagemagick"
|
"imagemagick"
|
||||||
"sane-cast" # cast videos to UPNP/DLNA, with compatibility
|
|
||||||
"sox"
|
"sox"
|
||||||
"yt-dlp"
|
"yt-dlp"
|
||||||
];
|
];
|
||||||
|
@ -216,176 +201,6 @@ in
|
||||||
# "tree-sitter"
|
# "tree-sitter"
|
||||||
];
|
];
|
||||||
|
|
||||||
gameApps = declPackageSet [
|
|
||||||
"animatch"
|
|
||||||
"gnome-2048"
|
|
||||||
"gnome.hitori" # like sudoku
|
|
||||||
];
|
|
||||||
|
|
||||||
pcGameApps = declPackageSet [
|
|
||||||
# "andyetitmoves" # TODO: fix build!
|
|
||||||
# "armagetronad" # tron/lightcycles; WAN and LAN multiplayer
|
|
||||||
"celeste64"
|
|
||||||
# "cutemaze" # meh: trivial maze game; qt6 and keyboard-only
|
|
||||||
# "cuyo" # trivial puyo-puyo clone
|
|
||||||
"endless-sky" # space merchantilism/exploration
|
|
||||||
# "factorio"
|
|
||||||
"frozen-bubble" # WAN + LAN + 1P/2P bubble bobble
|
|
||||||
"hase" # WAN worms game
|
|
||||||
# "hedgewars" # WAN + LAN worms game (5~10 people online at any moment; <https://hedgewars.org>)
|
|
||||||
# "libremines" # meh: trivial minesweeper; qt6
|
|
||||||
# "mario0" # SMB + portal
|
|
||||||
# "mindustry"
|
|
||||||
# "minesweep-rs" # CLI minesweeper
|
|
||||||
# "nethack"
|
|
||||||
# "osu-lazer"
|
|
||||||
# "pinball" # 3d pinball; kb/mouse. old sourceforge project
|
|
||||||
# "powermanga" # STYLISH space invaders derivative (keyboard-only)
|
|
||||||
"shattered-pixel-dungeon" # doesn't cross compile
|
|
||||||
"space-cadet-pinball" # LMB/RMB controls (bindable though. volume buttons?)
|
|
||||||
"steam"
|
|
||||||
"superTux" # keyboard-only controls
|
|
||||||
"superTuxKart" # poor FPS on pinephone
|
|
||||||
"tumiki-fighters" # keyboard-only
|
|
||||||
"vvvvvv" # keyboard-only controls
|
|
||||||
# "wine"
|
|
||||||
];
|
|
||||||
|
|
||||||
guiApps = declPackageSet [
|
|
||||||
# package sets
|
|
||||||
"gameApps"
|
|
||||||
"guiBaseApps"
|
|
||||||
];
|
|
||||||
|
|
||||||
guiBaseApps = declPackageSet [
|
|
||||||
# "abaddon" # discord client
|
|
||||||
"alacritty" # terminal emulator
|
|
||||||
"calls" # gnome calls (dialer/handler)
|
|
||||||
"dbus"
|
|
||||||
"dconf" # required by many packages, but not well-documented :(
|
|
||||||
# "delfin" # Jellyfin client
|
|
||||||
"dialect" # language translation
|
|
||||||
"dino" # XMPP client
|
|
||||||
"dissent" # Discord client (formerly known as: gtkcord4)
|
|
||||||
# "emote"
|
|
||||||
# "evince" # PDF viewer
|
|
||||||
# "flare-signal" # gtk4 signal client
|
|
||||||
# "foliate" # e-book reader
|
|
||||||
"fractal" # matrix client
|
|
||||||
"g4music" # local music player
|
|
||||||
# "gnome.cheese"
|
|
||||||
# "gnome-feeds" # RSS reader (with claimed mobile support)
|
|
||||||
# "gnome.file-roller"
|
|
||||||
"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"
|
|
||||||
# "gnome.seahorse" # keyring/secret manager
|
|
||||||
"gnome-frog" # OCR/QR decoder
|
|
||||||
"gpodder"
|
|
||||||
"gst-device-monitor" # for debugging audio/video
|
|
||||||
# "gthumb"
|
|
||||||
# "lemoa" # lemmy app
|
|
||||||
"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
|
|
||||||
# "newsflash" # RSS viewer
|
|
||||||
"pavucontrol"
|
|
||||||
"pwvucontrol" # pipewire version of pavu
|
|
||||||
# "picard" # music tagging
|
|
||||||
# "libsForQt5.plasmatube" # Youtube player
|
|
||||||
"signal-desktop"
|
|
||||||
"snapshot" # camera app
|
|
||||||
"spot" # Gnome Spotify client
|
|
||||||
# "sublime-music"
|
|
||||||
# "tdesktop" # broken on phosh
|
|
||||||
# "tokodon"
|
|
||||||
"tuba" # mastodon/pleroma client (stores pw in keyring)
|
|
||||||
"vulkan-tools" # vulkaninfo
|
|
||||||
# "whalebird" # pleroma client (Electron). input is broken on phosh.
|
|
||||||
"xdg-terminal-exec"
|
|
||||||
"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
|
|
||||||
"epiphany" # gnome's web browser
|
|
||||||
# "iotas" # note taking app
|
|
||||||
"komikku"
|
|
||||||
"koreader"
|
|
||||||
"megapixels" # camera app
|
|
||||||
"notejot" # note taking, e.g. shopping list
|
|
||||||
"planify" # todo-tracker/planner
|
|
||||||
"portfolio-filemanager"
|
|
||||||
"tangram" # web browser
|
|
||||||
"wike" # Wikipedia Reader
|
|
||||||
"xarchiver" # archiver, backup option for when engrampa UI overflows screen and is unusale (xarchiver UI fails in different ways)
|
|
||||||
];
|
|
||||||
|
|
||||||
pcGuiApps = declPackageSet [
|
|
||||||
# package sets
|
|
||||||
"pcGameApps"
|
|
||||||
"pcTuiApps"
|
|
||||||
####
|
|
||||||
"audacity"
|
|
||||||
# "blanket" # ambient noise generator
|
|
||||||
"brave" # for the integrated wallet -- as a backup
|
|
||||||
# "cantata" # music player (mpd frontend)
|
|
||||||
# "chromium" # chromium takes hours to build. brave is chromium-based, distributed in binary form, so prefer it.
|
|
||||||
# "cups"
|
|
||||||
"discord" # x86-only
|
|
||||||
"electrum"
|
|
||||||
"element-desktop"
|
|
||||||
"firefox"
|
|
||||||
"font-manager"
|
|
||||||
# "gajim" # XMPP client. cross build tries to import host gobject-introspection types (2023/09/01)
|
|
||||||
"gimp" # broken on phosh
|
|
||||||
# "gnome.dconf-editor"
|
|
||||||
# "gnome.file-roller"
|
|
||||||
"gnome.gnome-disk-utility"
|
|
||||||
"gnome.nautilus" # file browser
|
|
||||||
# "gnome.totem" # video player, supposedly supports UPnP
|
|
||||||
# "handbrake" #< TODO: fix build
|
|
||||||
"inkscape"
|
|
||||||
# "jellyfin-media-player"
|
|
||||||
"kdenlive"
|
|
||||||
# "kid3" # audio tagging
|
|
||||||
"krita"
|
|
||||||
"libreoffice" # TODO: replace with an office suite that uses saner packaging?
|
|
||||||
"losslesscut-bin" # x86-only
|
|
||||||
# "makemkv" # x86-only
|
|
||||||
# "monero-gui" # x86-only
|
|
||||||
# "mumble"
|
|
||||||
# "nheko" # Matrix chat client
|
|
||||||
# "nicotine-plus" # soulseek client. before re-enabling this make sure it's properly sandboxed!
|
|
||||||
# "obsidian"
|
|
||||||
# "openscad" # 3d modeling
|
|
||||||
# "rhythmbox" # local music player
|
|
||||||
# "slic3r"
|
|
||||||
"soundconverter"
|
|
||||||
"spotify" # x86-only
|
|
||||||
"tor-browser" # x86-only
|
|
||||||
# "vlc"
|
|
||||||
"wireshark" # could maybe ship the cli as sysadmin pkg
|
|
||||||
# "xterm" # requires Xwayland
|
|
||||||
# "zecwallet-lite" # x86-only
|
|
||||||
# "zulip"
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
# INDIVIDUAL PACKAGE DEFINITIONS
|
# INDIVIDUAL PACKAGE DEFINITIONS
|
||||||
|
|
||||||
|
@ -394,7 +209,6 @@ in
|
||||||
|
|
||||||
backblaze-b2 = {};
|
backblaze-b2 = {};
|
||||||
|
|
||||||
blanket.buildCost = 1;
|
|
||||||
blanket.sandbox.method = "bwrap";
|
blanket.sandbox.method = "bwrap";
|
||||||
blanket.sandbox.whitelistAudio = true;
|
blanket.sandbox.whitelistAudio = true;
|
||||||
# blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested
|
# blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested
|
||||||
|
@ -413,6 +227,14 @@ in
|
||||||
bridge-utils.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
bridge-utils.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
||||||
bridge-utils.sandbox.net = "all";
|
bridge-utils.sandbox.net = "all";
|
||||||
|
|
||||||
|
brightnessctl.sandbox.method = "landlock"; # also bwrap, but landlock is more responsive
|
||||||
|
brightnessctl.sandbox.extraPaths = [
|
||||||
|
"/sys/class/backlight"
|
||||||
|
"/sys/class/leds"
|
||||||
|
"/sys/devices"
|
||||||
|
];
|
||||||
|
brightnessctl.sandbox.whitelistDbus = [ "system" ];
|
||||||
|
|
||||||
btrfs-progs.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
btrfs-progs.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
||||||
btrfs-progs.sandbox.autodetectCliPaths = "existing"; # e.g. `btrfs filesystem df /my/fs`
|
btrfs-progs.sandbox.autodetectCliPaths = "existing"; # e.g. `btrfs filesystem df /my/fs`
|
||||||
|
|
||||||
|
@ -439,41 +261,33 @@ in
|
||||||
ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested
|
ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested
|
||||||
ddrescue.sandbox.autodetectCliPaths = "existingOrParent";
|
ddrescue.sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
|
|
||||||
delfin.buildCost = 1;
|
# auth token, preferences
|
||||||
delfin.sandbox.method = "bwrap";
|
delfin.sandbox.method = "bwrap";
|
||||||
delfin.sandbox.whitelistAudio = true;
|
delfin.sandbox.whitelistAudio = true;
|
||||||
delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player
|
delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player
|
||||||
delfin.sandbox.whitelistDri = true;
|
delfin.sandbox.whitelistDri = true;
|
||||||
delfin.sandbox.whitelistWayland = true;
|
delfin.sandbox.whitelistWayland = true;
|
||||||
delfin.sandbox.net = "clearnet";
|
delfin.sandbox.net = "clearnet";
|
||||||
# auth token, preferences
|
|
||||||
delfin.persist.byStore.private = [ ".config/delfin" ];
|
delfin.persist.byStore.private = [ ".config/delfin" ];
|
||||||
|
|
||||||
dig.sandbox.method = "bwrap";
|
dig.sandbox.method = "bwrap";
|
||||||
dig.sandbox.net = "all";
|
dig.sandbox.net = "all";
|
||||||
|
|
||||||
# creds, but also 200 MB of node modules, etc
|
# creds, but also 200 MB of node modules, etc
|
||||||
discord.persist.byStore.private = [ ".config/discord" ];
|
|
||||||
discord.suggestedPrograms = [ "xwayland" ];
|
|
||||||
discord.sandbox.method = "bwrap";
|
discord.sandbox.method = "bwrap";
|
||||||
discord.sandbox.wrapperType = "inplace"; #< /opt-style packaging
|
discord.sandbox.wrapperType = "inplace"; #< /opt-style packaging
|
||||||
discord.sandbox.whitelistAudio = true;
|
discord.sandbox.whitelistAudio = true;
|
||||||
discord.sandbox.whitelistDbus = [ "user" ]; # needed for xdg-open
|
discord.sandbox.whitelistDbus = [ "user" ]; # needed for xdg-open
|
||||||
discord.sandbox.whitelistWayland = true;
|
discord.sandbox.whitelistWayland = true;
|
||||||
discord.sandbox.whitelistX = true;
|
|
||||||
discord.sandbox.net = "clearnet";
|
discord.sandbox.net = "clearnet";
|
||||||
discord.sandbox.extraHomePaths = [
|
discord.persist.byStore.private = [ ".config/discord" ];
|
||||||
# still needs these paths despite it using the portal's file-chooser :?
|
|
||||||
"Pictures/cat"
|
|
||||||
"Pictures/Screenshots"
|
|
||||||
"Pictures/servo-macros"
|
|
||||||
"Videos/local"
|
|
||||||
"Videos/servo"
|
|
||||||
"tmp"
|
|
||||||
];
|
|
||||||
|
|
||||||
dtc.sandbox.method = "bwrap";
|
dtc.sandbox.method = "bwrap";
|
||||||
dtc.sandbox.autodetectCliPaths = "existingFile"; # TODO:sandbox: untested
|
dtc.sandbox.autodetectCliPaths = true; # TODO:sandbox: untested
|
||||||
|
|
||||||
|
dtrx.sandbox.method = "bwrap";
|
||||||
|
dtrx.sandbox.whitelistPwd = true;
|
||||||
|
dtrx.sandbox.autodetectCliPaths = "existing"; #< for the archive
|
||||||
|
|
||||||
duplicity = {};
|
duplicity = {};
|
||||||
|
|
||||||
|
@ -487,13 +301,11 @@ in
|
||||||
|
|
||||||
eg25-control = {};
|
eg25-control = {};
|
||||||
|
|
||||||
electrum.buildCost = 1;
|
|
||||||
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
|
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
|
||||||
electrum.sandbox.whitelistWayland = true;
|
electrum.sandbox.whitelistWayland = true;
|
||||||
electrum.persist.byStore.cryptClearOnBoot = [ ".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" ];
|
endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ];
|
||||||
endless-sky.sandbox.method = "bwrap";
|
endless-sky.sandbox.method = "bwrap";
|
||||||
endless-sky.sandbox.whitelistAudio = true;
|
endless-sky.sandbox.whitelistAudio = true;
|
||||||
|
@ -509,9 +321,10 @@ in
|
||||||
ethtool.sandbox.capabilities = [ "net_admin" ];
|
ethtool.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
# eza `ls` replacement
|
# eza `ls` replacement
|
||||||
|
# landlock is OK, only `whitelistPwd` doesn't make the intermediate symlinks traversable, so it breaks on e.g. ~/Videos/servo/Shows/foo
|
||||||
# eza.sandbox.method = "landlock";
|
# 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.method = "bwrap";
|
||||||
eza.sandbox.autodetectCliPaths = "existing";
|
eza.sandbox.autodetectCliPaths = true;
|
||||||
eza.sandbox.whitelistPwd = true;
|
eza.sandbox.whitelistPwd = true;
|
||||||
eza.sandbox.extraHomePaths = [
|
eza.sandbox.extraHomePaths = [
|
||||||
# so that e.g. `eza -l ~` can show which symlink exist
|
# so that e.g. `eza -l ~` can show which symlink exist
|
||||||
|
@ -523,7 +336,7 @@ in
|
||||||
fatresize.sandbox.autodetectCliPaths = "parent"; # /dev/sda1 -> needs /dev/sda
|
fatresize.sandbox.autodetectCliPaths = "parent"; # /dev/sda1 -> needs /dev/sda
|
||||||
|
|
||||||
fd.sandbox.method = "landlock";
|
fd.sandbox.method = "landlock";
|
||||||
fd.sandbox.autodetectCliPaths = "existing";
|
fd.sandbox.autodetectCliPaths = true;
|
||||||
fd.sandbox.whitelistPwd = true;
|
fd.sandbox.whitelistPwd = true;
|
||||||
fd.sandbox.extraHomePaths = [
|
fd.sandbox.extraHomePaths = [
|
||||||
# let it follow symlinks to non-sensitive data
|
# let it follow symlinks to non-sensitive data
|
||||||
|
@ -531,15 +344,14 @@ in
|
||||||
".persist/plaintext"
|
".persist/plaintext"
|
||||||
];
|
];
|
||||||
|
|
||||||
ffmpeg.buildCost = 1;
|
|
||||||
ffmpeg.sandbox.method = "bwrap";
|
ffmpeg.sandbox.method = "bwrap";
|
||||||
ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting
|
ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting
|
||||||
|
|
||||||
file.sandbox.method = "bwrap";
|
file.sandbox.method = "bwrap";
|
||||||
file.sandbox.autodetectCliPaths = "existing"; #< file OR directory, yes
|
file.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
findutils.sandbox.method = "bwrap";
|
findutils.sandbox.method = "bwrap";
|
||||||
findutils.sandbox.autodetectCliPaths = "existing";
|
findutils.sandbox.autodetectCliPaths = true;
|
||||||
findutils.sandbox.whitelistPwd = true;
|
findutils.sandbox.whitelistPwd = true;
|
||||||
findutils.sandbox.extraHomePaths = [
|
findutils.sandbox.extraHomePaths = [
|
||||||
# let it follow symlinks to non-sensitive data
|
# let it follow symlinks to non-sensitive data
|
||||||
|
@ -549,16 +361,16 @@ in
|
||||||
|
|
||||||
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
||||||
|
|
||||||
font-manager.buildCost = 1;
|
|
||||||
font-manager.sandbox.method = "bwrap";
|
font-manager.sandbox.method = "bwrap";
|
||||||
font-manager.sandbox.whitelistWayland = true;
|
|
||||||
font-manager.packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.font-manager.override {
|
font-manager.packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.font-manager.override {
|
||||||
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
|
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
|
||||||
withWebkit = false;
|
withWebkit = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap
|
forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap
|
||||||
forkstat.sandbox.isolatePids = false;
|
forkstat.sandbox.extraConfig = [
|
||||||
|
"--sane-sandbox-keep-namespace" "pid"
|
||||||
|
];
|
||||||
forkstat.sandbox.extraPaths = [
|
forkstat.sandbox.extraPaths = [
|
||||||
"/proc"
|
"/proc"
|
||||||
];
|
];
|
||||||
|
@ -572,7 +384,11 @@ in
|
||||||
|
|
||||||
gawk.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
gawk.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
|
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
|
||||||
gawk.sandbox.autodetectCliPaths = "existingFile";
|
gawk.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
|
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 = {};
|
geoclue2-with-demo-agent = {};
|
||||||
|
|
||||||
|
@ -580,9 +396,8 @@ in
|
||||||
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
||||||
gh.persist.byStore.private = [ ".config/gh" ];
|
gh.persist.byStore.private = [ ".config/gh" ];
|
||||||
|
|
||||||
gimp.buildCost = 1;
|
|
||||||
gimp.sandbox.method = "bwrap";
|
gimp.sandbox.method = "bwrap";
|
||||||
gimp.sandbox.whitelistX = true;
|
gimp.sandbox.net = "clearnet"; #< for Xwayland
|
||||||
gimp.sandbox.whitelistWayland = true;
|
gimp.sandbox.whitelistWayland = true;
|
||||||
gimp.sandbox.extraHomePaths = [
|
gimp.sandbox.extraHomePaths = [
|
||||||
"Pictures/albums"
|
"Pictures/albums"
|
||||||
|
@ -600,39 +415,32 @@ in
|
||||||
"/tmp" # "Cannot open display:" if it can't mount /tmp 👀
|
"/tmp" # "Cannot open display:" if it can't mount /tmp 👀
|
||||||
];
|
];
|
||||||
|
|
||||||
"gnome.gnome-calculator".buildCost = 1;
|
|
||||||
"gnome.gnome-calculator".sandbox.method = "bwrap";
|
"gnome.gnome-calculator".sandbox.method = "bwrap";
|
||||||
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
|
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
"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 surely has data to persist, but i use it strictly to do date math, not track events.
|
||||||
"gnome.gnome-calendar".sandbox.method = "bwrap";
|
"gnome.gnome-calendar".sandbox.method = "bwrap";
|
||||||
"gnome.gnome-calendar".sandbox.whitelistWayland = true;
|
"gnome.gnome-calendar".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
|
"gnome.gnome-clocks".sandbox.method = "bwrap";
|
||||||
|
"gnome.gnome-clocks".sandbox.whitelistWayland = true;
|
||||||
|
"gnome.gnome-clocks".suggestedPrograms = [ "dconf" ];
|
||||||
|
|
||||||
# gnome-disks
|
# gnome-disks
|
||||||
"gnome.gnome-disk-utility".buildCost = 1;
|
|
||||||
"gnome.gnome-disk-utility".sandbox.method = "bwrap";
|
"gnome.gnome-disk-utility".sandbox.method = "bwrap";
|
||||||
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
|
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
|
||||||
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
|
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
|
||||||
"gnome.gnome-disk-utility".sandbox.extraHomePaths = [
|
|
||||||
"tmp"
|
|
||||||
"use/iso"
|
|
||||||
# TODO: probably need /dev and such
|
|
||||||
];
|
|
||||||
|
|
||||||
# seahorse: dump gnome-keyring secrets.
|
# seahorse: dump gnome-keyring secrets.
|
||||||
"gnome.seahorse".buildCost = 1;
|
|
||||||
# N.B.: it can also manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
|
# 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.method = "bwrap";
|
||||||
"gnome.seahorse".sandbox.whitelistDbus = [ "user" ];
|
"gnome.seahorse".sandbox.whitelistDbus = [ "user" ];
|
||||||
"gnome.seahorse".sandbox.whitelistWayland = true;
|
"gnome.seahorse".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
gnome-2048.buildCost = 1;
|
|
||||||
gnome-2048.sandbox.method = "bwrap";
|
gnome-2048.sandbox.method = "bwrap";
|
||||||
gnome-2048.sandbox.whitelistWayland = true;
|
gnome-2048.sandbox.whitelistWayland = true;
|
||||||
gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ];
|
gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ];
|
||||||
|
|
||||||
gnome-frog.buildCost = 1;
|
|
||||||
gnome-frog.sandbox.method = "bwrap";
|
gnome-frog.sandbox.method = "bwrap";
|
||||||
gnome-frog.sandbox.whitelistWayland = true;
|
gnome-frog.sandbox.whitelistWayland = true;
|
||||||
gnome-frog.sandbox.whitelistDbus = [ "user" ];
|
gnome-frog.sandbox.whitelistDbus = [ "user" ];
|
||||||
|
@ -659,12 +467,11 @@ in
|
||||||
# 1. no number may appear unshaded more than once in the same row/column
|
# 1. no number may appear unshaded more than once in the same row/column
|
||||||
# 2. no two shaded tiles can be direct N/S/E/W neighbors
|
# 2. no two shaded tiles can be direct N/S/E/W neighbors
|
||||||
# - win once (1) and (2) are satisfied
|
# - win once (1) and (2) are satisfied
|
||||||
"gnome.hitori".buildCost = 1;
|
|
||||||
"gnome.hitori".sandbox.method = "bwrap";
|
"gnome.hitori".sandbox.method = "bwrap";
|
||||||
"gnome.hitori".sandbox.whitelistWayland = true;
|
"gnome.hitori".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
gnugrep.sandbox.method = "bwrap";
|
gnugrep.sandbox.method = "bwrap";
|
||||||
gnugrep.sandbox.autodetectCliPaths = "existing";
|
gnugrep.sandbox.autodetectCliPaths = true;
|
||||||
gnugrep.sandbox.whitelistPwd = true;
|
gnugrep.sandbox.whitelistPwd = true;
|
||||||
gnugrep.sandbox.extraHomePaths = [
|
gnugrep.sandbox.extraHomePaths = [
|
||||||
# let it follow symlinks to non-sensitive data
|
# let it follow symlinks to non-sensitive data
|
||||||
|
@ -672,6 +479,7 @@ in
|
||||||
".persist/plaintext"
|
".persist/plaintext"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# sed: there is an edgecase of `--file=<foo>`, wherein `foo` won't be whitelisted.
|
||||||
gnused.sandbox.method = "bwrap";
|
gnused.sandbox.method = "bwrap";
|
||||||
gnused.sandbox.autodetectCliPaths = "existingFile";
|
gnused.sandbox.autodetectCliPaths = "existingFile";
|
||||||
gnused.sandbox.whitelistPwd = true; #< `-i` flag creates a temporary file in pwd (?) and then moves it.
|
gnused.sandbox.whitelistPwd = true; #< `-i` flag creates a temporary file in pwd (?) and then moves it.
|
||||||
|
@ -688,7 +496,6 @@ in
|
||||||
grim.sandbox.autodetectCliPaths = "existingOrParent";
|
grim.sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
grim.sandbox.whitelistWayland = true;
|
grim.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
hase.buildCost = 1;
|
|
||||||
hase.sandbox.method = "bwrap";
|
hase.sandbox.method = "bwrap";
|
||||||
hase.sandbox.net = "clearnet";
|
hase.sandbox.net = "clearnet";
|
||||||
hase.sandbox.whitelistAudio = true;
|
hase.sandbox.whitelistAudio = true;
|
||||||
|
@ -697,11 +504,21 @@ in
|
||||||
|
|
||||||
# hdparm: has to be run as sudo. e.g. `sudo hdparm -i /dev/sda`
|
# hdparm: has to be run as sudo. e.g. `sudo hdparm -i /dev/sda`
|
||||||
hdparm.sandbox.method = "bwrap";
|
hdparm.sandbox.method = "bwrap";
|
||||||
hdparm.sandbox.autodetectCliPaths = "existingFile";
|
hdparm.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
host.sandbox.method = "landlock";
|
host.sandbox.method = "landlock";
|
||||||
host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server
|
host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server
|
||||||
|
|
||||||
|
htop.sandbox.method = "landlock";
|
||||||
|
htop.sandbox.extraPaths = [
|
||||||
|
"/proc"
|
||||||
|
"/sys/devices"
|
||||||
|
];
|
||||||
|
htop.persist.byStore.plaintext = [
|
||||||
|
# consider setting `show_program_path=0` and either `hide_userland_threads=1` or `show_thread_names=1`
|
||||||
|
".config/htop"
|
||||||
|
];
|
||||||
|
|
||||||
iftop.sandbox.method = "landlock";
|
iftop.sandbox.method = "landlock";
|
||||||
iftop.sandbox.capabilities = [ "net_raw" ];
|
iftop.sandbox.capabilities = [ "net_raw" ];
|
||||||
|
|
||||||
|
@ -709,7 +526,6 @@ in
|
||||||
# N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally).
|
# N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally).
|
||||||
inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least.
|
inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least.
|
||||||
|
|
||||||
inkscape.buildCost = 1;
|
|
||||||
inkscape.sandbox.method = "bwrap";
|
inkscape.sandbox.method = "bwrap";
|
||||||
inkscape.sandbox.whitelistWayland = true;
|
inkscape.sandbox.whitelistWayland = true;
|
||||||
inkscape.sandbox.extraHomePaths = [
|
inkscape.sandbox.extraHomePaths = [
|
||||||
|
@ -731,17 +547,10 @@ in
|
||||||
];
|
];
|
||||||
iotop.sandbox.capabilities = [ "net_admin" ];
|
iotop.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
# provides `ip`, `routel`, `bridge`, others.
|
# provides `ip`, `routel`, others
|
||||||
# landlock works fine for most of these, but `ip netns exec` wants to attach to an existing namespace
|
iproute2.sandbox.method = "landlock";
|
||||||
# and that means we can't use ANY sandboxer for it.
|
iproute2.sandbox.net = "all";
|
||||||
iproute2.sandbox.enable = false;
|
iproute2.sandbox.capabilities = [ "net_admin" ];
|
||||||
# iproute2.sandbox.net = "all";
|
|
||||||
# iproute2.sandbox.capabilities = [ "net_admin" ];
|
|
||||||
# iproute2.sandbox.extraPaths = [
|
|
||||||
# "/run/netns" # for `ip netns ...` to work, but maybe not needed anymore?
|
|
||||||
# "/sys/class/net" # for `ip netns ...` to work
|
|
||||||
# "/var/run/netns"
|
|
||||||
# ];
|
|
||||||
|
|
||||||
iptables.sandbox.method = "landlock";
|
iptables.sandbox.method = "landlock";
|
||||||
iptables.sandbox.net = "all";
|
iptables.sandbox.net = "all";
|
||||||
|
@ -764,7 +573,6 @@ in
|
||||||
"/proc"
|
"/proc"
|
||||||
];
|
];
|
||||||
|
|
||||||
krita.buildCost = 1;
|
|
||||||
krita.sandbox.method = "bwrap";
|
krita.sandbox.method = "bwrap";
|
||||||
krita.sandbox.whitelistWayland = true;
|
krita.sandbox.whitelistWayland = true;
|
||||||
krita.sandbox.autodetectCliPaths = "existing";
|
krita.sandbox.autodetectCliPaths = "existing";
|
||||||
|
@ -780,15 +588,11 @@ in
|
||||||
"tmp"
|
"tmp"
|
||||||
];
|
];
|
||||||
|
|
||||||
libcamera = {};
|
|
||||||
|
|
||||||
libcap.sandbox.enable = false; #< for `capsh`, which i use as a sandboxer
|
|
||||||
libcap_ng.sandbox.enable = false; # there's something about /proc/$pid/fd which breaks `readlink`/stat with every sandbox technique (except capsh-only)
|
libcap_ng.sandbox.enable = false; # there's something about /proc/$pid/fd which breaks `readlink`/stat with every sandbox technique (except capsh-only)
|
||||||
|
|
||||||
libnotify.sandbox.method = "bwrap";
|
libnotify.sandbox.method = "bwrap";
|
||||||
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
|
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
|
||||||
|
|
||||||
losslesscut-bin.buildCost = 1;
|
|
||||||
losslesscut-bin.sandbox.method = "bwrap";
|
losslesscut-bin.sandbox.method = "bwrap";
|
||||||
losslesscut-bin.sandbox.extraHomePaths = [
|
losslesscut-bin.sandbox.extraHomePaths = [
|
||||||
"Music"
|
"Music"
|
||||||
|
@ -804,21 +608,27 @@ in
|
||||||
losslesscut-bin.sandbox.whitelistX = true;
|
losslesscut-bin.sandbox.whitelistX = true;
|
||||||
|
|
||||||
lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to /
|
lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to /
|
||||||
lsof.sandbox.capabilities = [ "dac_override" "sys_ptrace" ];
|
|
||||||
|
|
||||||
lua = {};
|
lua = {};
|
||||||
|
|
||||||
man-pages.sandbox.enable = false;
|
"mate.engrampa".packageUnwrapped = pkgs.rmDbusServices pkgs.mate.engrampa;
|
||||||
man-pages-posix.sandbox.enable = false;
|
"mate.engrampa".sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
|
"mate.engrampa".sandbox.whitelistWayland = true;
|
||||||
|
"mate.engrampa".sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
|
"mate.engrampa".sandbox.extraHomePaths = [
|
||||||
|
"archive"
|
||||||
|
"Books/local"
|
||||||
|
"Books/servo"
|
||||||
|
"records"
|
||||||
|
"ref"
|
||||||
|
"tmp"
|
||||||
|
];
|
||||||
|
|
||||||
mercurial.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
mercurial.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
mercurial.sandbox.net = "clearnet";
|
mercurial.sandbox.net = "clearnet";
|
||||||
mercurial.sandbox.whitelistPwd = true;
|
mercurial.sandbox.whitelistPwd = true;
|
||||||
|
|
||||||
mesa-demos = {};
|
|
||||||
|
|
||||||
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
||||||
monero-gui.buildCost = 1;
|
|
||||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||||
monero-gui.persist.byStore.plaintext = [ ".bitmonero" ];
|
monero-gui.persist.byStore.plaintext = [ ".bitmonero" ];
|
||||||
monero-gui.sandbox.method = "bwrap";
|
monero-gui.sandbox.method = "bwrap";
|
||||||
|
@ -827,7 +637,6 @@ in
|
||||||
"records/finance/cryptocurrencies/monero"
|
"records/finance/cryptocurrencies/monero"
|
||||||
];
|
];
|
||||||
|
|
||||||
mumble.buildCost = 1;
|
|
||||||
mumble.persist.byStore.private = [ ".local/share/Mumble" ];
|
mumble.persist.byStore.private = [ ".local/share/Mumble" ];
|
||||||
|
|
||||||
nano.sandbox.method = "bwrap";
|
nano.sandbox.method = "bwrap";
|
||||||
|
@ -855,15 +664,9 @@ in
|
||||||
nixpkgs-review.sandbox.wrapperType = "inplace"; #< shell completions use full paths
|
nixpkgs-review.sandbox.wrapperType = "inplace"; #< shell completions use full paths
|
||||||
nixpkgs-review.sandbox.net = "clearnet";
|
nixpkgs-review.sandbox.net = "clearnet";
|
||||||
nixpkgs-review.sandbox.whitelistPwd = true;
|
nixpkgs-review.sandbox.whitelistPwd = true;
|
||||||
nixpkgs-review.sandbox.extraHomePaths = [
|
|
||||||
".config/git" #< it needs to know commiter name/email, even if not posting
|
|
||||||
];
|
|
||||||
nixpkgs-review.sandbox.extraPaths = [
|
nixpkgs-review.sandbox.extraPaths = [
|
||||||
"/nix"
|
"/nix"
|
||||||
];
|
];
|
||||||
nixpkgs-review.persist.byStore.cryptClearOnBoot = [
|
|
||||||
".cache/nixpkgs-review" #< help it not exhaust / tmpfs
|
|
||||||
];
|
|
||||||
|
|
||||||
nmap.sandbox.method = "bwrap";
|
nmap.sandbox.method = "bwrap";
|
||||||
nmap.sandbox.net = "all"; # clearnet and lan
|
nmap.sandbox.net = "all"; # clearnet and lan
|
||||||
|
@ -892,8 +695,6 @@ in
|
||||||
# settings (electron app)
|
# settings (electron app)
|
||||||
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
|
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
|
||||||
|
|
||||||
passt.sandbox.enable = false; #< sandbox helper (netns specifically)
|
|
||||||
|
|
||||||
parted.sandbox.method = "landlock";
|
parted.sandbox.method = "landlock";
|
||||||
parted.sandbox.extraPaths = [
|
parted.sandbox.extraPaths = [
|
||||||
"/dev"
|
"/dev"
|
||||||
|
@ -925,7 +726,9 @@ in
|
||||||
|
|
||||||
# procps: free, pgrep, pidof, pkill, ps, pwait, top, uptime, couple others
|
# procps: free, pgrep, pidof, pkill, ps, pwait, top, uptime, couple others
|
||||||
procps.sandbox.method = "bwrap";
|
procps.sandbox.method = "bwrap";
|
||||||
procps.sandbox.isolatePids = false;
|
procps.sandbox.extraConfig = [
|
||||||
|
"--sane-sandbox-keep-namespace" "pid"
|
||||||
|
];
|
||||||
|
|
||||||
pstree.sandbox.method = "landlock";
|
pstree.sandbox.method = "landlock";
|
||||||
pstree.sandbox.extraPaths = [
|
pstree.sandbox.extraPaths = [
|
||||||
|
@ -937,14 +740,11 @@ in
|
||||||
pulsemixer.sandbox.method = "landlock";
|
pulsemixer.sandbox.method = "landlock";
|
||||||
pulsemixer.sandbox.whitelistAudio = true;
|
pulsemixer.sandbox.whitelistAudio = true;
|
||||||
|
|
||||||
pwvucontrol.buildCost = 1;
|
|
||||||
pwvucontrol.sandbox.method = "bwrap";
|
pwvucontrol.sandbox.method = "bwrap";
|
||||||
pwvucontrol.sandbox.whitelistAudio = true;
|
pwvucontrol.sandbox.whitelistAudio = true;
|
||||||
pwvucontrol.sandbox.whitelistDri = true; # else perf on moby is unusable
|
|
||||||
pwvucontrol.sandbox.whitelistWayland = true;
|
pwvucontrol.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
python3-repl.packageUnwrapped = pkgs.python3.withPackages (ps: with ps; [
|
python3-repl.packageUnwrapped = pkgs.python3.withPackages (ps: with ps; [
|
||||||
psutil
|
|
||||||
requests
|
requests
|
||||||
]);
|
]);
|
||||||
python3-repl.sandbox.method = "bwrap";
|
python3-repl.sandbox.method = "bwrap";
|
||||||
|
@ -955,7 +755,7 @@ in
|
||||||
];
|
];
|
||||||
|
|
||||||
qemu.sandbox.enable = false; #< it's a launcher
|
qemu.sandbox.enable = false; #< it's a launcher
|
||||||
qemu.buildCost = 2;
|
qemu.slowToBuild = true;
|
||||||
|
|
||||||
rsync.sandbox.method = "bwrap";
|
rsync.sandbox.method = "bwrap";
|
||||||
rsync.sandbox.net = "clearnet";
|
rsync.sandbox.net = "clearnet";
|
||||||
|
@ -963,23 +763,17 @@ in
|
||||||
|
|
||||||
rustc = {};
|
rustc = {};
|
||||||
|
|
||||||
sane-cast.sandbox.method = "bwrap";
|
sane-open-desktop.sandbox.enable = false; #< trivial script, and all our deps are sandboxed
|
||||||
sane-cast.sandbox.net = "clearnet";
|
sane-open-desktop.suggestedPrograms = [
|
||||||
sane-cast.sandbox.autodetectCliPaths = "existingFile";
|
"gdbus"
|
||||||
sane-cast.suggestedPrograms = [ "go2tv" ];
|
];
|
||||||
|
|
||||||
sane-die-with-parent.sandbox.enable = false; #< it's a launcher; can't sandbox
|
|
||||||
|
|
||||||
sane-weather.sandbox.method = "bwrap";
|
|
||||||
sane-weather.sandbox.net = "clearnet";
|
|
||||||
|
|
||||||
screen.sandbox.enable = false; #< tty; needs to run anything
|
screen.sandbox.enable = false; #< tty; needs to run anything
|
||||||
|
|
||||||
sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
sequoia.sandbox.whitelistPwd = true;
|
sequoia.sandbox.whitelistPwd = true;
|
||||||
sequoia.sandbox.autodetectCliPaths = "existingFileOrParent"; # supports `-o <file-to-create>`
|
sequoia.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
shattered-pixel-dungeon.buildCost = 1;
|
|
||||||
shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ];
|
shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ];
|
||||||
shattered-pixel-dungeon.sandbox.method = "bwrap";
|
shattered-pixel-dungeon.sandbox.method = "bwrap";
|
||||||
shattered-pixel-dungeon.sandbox.whitelistAudio = true;
|
shattered-pixel-dungeon.sandbox.whitelistAudio = true;
|
||||||
|
@ -987,7 +781,6 @@ in
|
||||||
shattered-pixel-dungeon.sandbox.whitelistWayland = true;
|
shattered-pixel-dungeon.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
# printer/filament settings
|
# printer/filament settings
|
||||||
slic3r.buildCost = 1;
|
|
||||||
slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
|
slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
|
||||||
|
|
||||||
slurp.sandbox.method = "bwrap";
|
slurp.sandbox.method = "bwrap";
|
||||||
|
@ -999,20 +792,15 @@ in
|
||||||
smartmontools.sandbox.autodetectCliPaths = "existing";
|
smartmontools.sandbox.autodetectCliPaths = "existing";
|
||||||
smartmontools.sandbox.capabilities = [ "sys_rawio" ];
|
smartmontools.sandbox.capabilities = [ "sys_rawio" ];
|
||||||
|
|
||||||
# snapshot camera, based on libcamera
|
|
||||||
# TODO: enable dma heaps for more efficient buffer sharing: <https://gitlab.com/postmarketOS/pmaports/-/issues/2789>
|
|
||||||
snapshot = {};
|
|
||||||
|
|
||||||
sops.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
sops.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
sops.sandbox.extraHomePaths = [
|
sops.sandbox.extraHomePaths = [
|
||||||
".config/sops"
|
".config/sops"
|
||||||
"nixos"
|
"dev/nixos"
|
||||||
# TODO: sops should only need access to knowledge/secrets,
|
# TODO: sops should only need access to knowledge/secrets,
|
||||||
# except that i currently put its .sops.yaml config in the root of ~/knowledge
|
# except that i currently put its .sops.yaml config in the root of ~/knowledge
|
||||||
"knowledge"
|
"knowledge"
|
||||||
];
|
];
|
||||||
|
|
||||||
soundconverter.buildCost = 1;
|
|
||||||
soundconverter.sandbox.method = "bwrap";
|
soundconverter.sandbox.method = "bwrap";
|
||||||
soundconverter.sandbox.whitelistWayland = true;
|
soundconverter.sandbox.whitelistWayland = true;
|
||||||
soundconverter.sandbox.extraHomePaths = [
|
soundconverter.sandbox.extraHomePaths = [
|
||||||
|
@ -1030,7 +818,6 @@ in
|
||||||
sox.sandbox.autodetectCliPaths = "existingFileOrParent";
|
sox.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||||
sox.sandbox.whitelistAudio = true;
|
sox.sandbox.whitelistAudio = true;
|
||||||
|
|
||||||
space-cadet-pinball.buildCost = 1;
|
|
||||||
space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ];
|
space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ];
|
||||||
space-cadet-pinball.sandbox.method = "bwrap";
|
space-cadet-pinball.sandbox.method = "bwrap";
|
||||||
space-cadet-pinball.sandbox.whitelistAudio = true;
|
space-cadet-pinball.sandbox.whitelistAudio = true;
|
||||||
|
@ -1042,8 +829,6 @@ in
|
||||||
|
|
||||||
sqlite = {};
|
sqlite = {};
|
||||||
|
|
||||||
sshfs-fuse = {}; # used by fs.nix
|
|
||||||
|
|
||||||
strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything*
|
strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything*
|
||||||
|
|
||||||
subversion.sandbox.method = "bwrap";
|
subversion.sandbox.method = "bwrap";
|
||||||
|
@ -1051,7 +836,6 @@ in
|
||||||
subversion.sandbox.whitelistPwd = true;
|
subversion.sandbox.whitelistPwd = true;
|
||||||
sudo.sandbox.enable = false;
|
sudo.sandbox.enable = false;
|
||||||
|
|
||||||
superTux.buildCost = 1;
|
|
||||||
superTux.sandbox.method = "bwrap";
|
superTux.sandbox.method = "bwrap";
|
||||||
superTux.sandbox.wrapperType = "inplace"; # package Makefile incorrectly installs to $out/games/superTux instead of $out/share/games
|
superTux.sandbox.wrapperType = "inplace"; # package Makefile incorrectly installs to $out/games/superTux instead of $out/share/games
|
||||||
superTux.sandbox.whitelistAudio = true;
|
superTux.sandbox.whitelistAudio = true;
|
||||||
|
@ -1070,14 +854,12 @@ in
|
||||||
|
|
||||||
tdesktop.persist.byStore.private = [ ".local/share/TelegramDesktop" ];
|
tdesktop.persist.byStore.private = [ ".local/share/TelegramDesktop" ];
|
||||||
|
|
||||||
tokodon.buildCost = 1;
|
|
||||||
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
|
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
|
||||||
|
|
||||||
tree.sandbox.method = "landlock";
|
tree.sandbox.method = "landlock";
|
||||||
tree.sandbox.autodetectCliPaths = "existing";
|
tree.sandbox.autodetectCliPaths = true;
|
||||||
tree.sandbox.whitelistPwd = true;
|
tree.sandbox.whitelistPwd = true;
|
||||||
|
|
||||||
tumiki-fighters.buildCost = 1;
|
|
||||||
tumiki-fighters.sandbox.method = "bwrap";
|
tumiki-fighters.sandbox.method = "bwrap";
|
||||||
tumiki-fighters.sandbox.whitelistAudio = true;
|
tumiki-fighters.sandbox.whitelistAudio = true;
|
||||||
tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf
|
tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf
|
||||||
|
@ -1096,16 +878,12 @@ in
|
||||||
"/sys/bus/usb"
|
"/sys/bus/usb"
|
||||||
];
|
];
|
||||||
|
|
||||||
valgrind.buildCost = 1;
|
|
||||||
valgrind.sandbox.enable = false; #< it's a launcher: can't sandbox
|
|
||||||
|
|
||||||
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
visidata.sandbox.autodetectCliPaths = true;
|
visidata.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
# `vulkaninfo`, `vkcube`
|
# `vulkaninfo`, `vkcube`
|
||||||
vulkan-tools.sandbox.method = "landlock";
|
vulkan-tools.sandbox.method = "landlock";
|
||||||
|
|
||||||
vvvvvv.buildCost = 1;
|
|
||||||
vvvvvv.sandbox.method = "bwrap";
|
vvvvvv.sandbox.method = "bwrap";
|
||||||
vvvvvv.sandbox.whitelistAudio = true;
|
vvvvvv.sandbox.whitelistAudio = true;
|
||||||
vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU
|
vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU
|
||||||
|
@ -1119,8 +897,6 @@ in
|
||||||
"tmp"
|
"tmp"
|
||||||
];
|
];
|
||||||
|
|
||||||
watch.sandbox.enable = false; #< it executes the command it's given
|
|
||||||
|
|
||||||
wdisplays.sandbox.method = "bwrap";
|
wdisplays.sandbox.method = "bwrap";
|
||||||
wdisplays.sandbox.whitelistWayland = true;
|
wdisplays.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
|
@ -1128,7 +904,6 @@ in
|
||||||
wget.sandbox.net = "all";
|
wget.sandbox.net = "all";
|
||||||
wget.sandbox.whitelistPwd = true; # saves to pwd by default
|
wget.sandbox.whitelistPwd = true; # saves to pwd by default
|
||||||
|
|
||||||
whalebird.buildCost = 1;
|
|
||||||
whalebird.persist.byStore.private = [ ".config/Whalebird" ];
|
whalebird.persist.byStore.private = [ ".config/Whalebird" ];
|
||||||
|
|
||||||
# `wg`, `wg-quick`
|
# `wg`, `wg-quick`
|
||||||
|
@ -1143,13 +918,11 @@ in
|
||||||
wl-clipboard.sandbox.whitelistWayland = true;
|
wl-clipboard.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
wtype = {};
|
wtype = {};
|
||||||
wtype.sandbox.method = "bwrap";
|
|
||||||
wtype.sandbox.whitelistWayland = true;
|
|
||||||
|
|
||||||
xwayland.sandbox.method = "bwrap";
|
xwayland.sandbox.method = "bwrap";
|
||||||
xwayland.sandbox.wrapperType = "inplace"; #< consumers use it as a library (e.g. wlroots)
|
xwayland.sandbox.wrapperType = "inplace"; #< consumers use it as a library (e.g. wlroots)
|
||||||
xwayland.sandbox.whitelistWayland = true; #< just assuming this is needed
|
xwayland.sandbox.whitelistWayland = true; #< just assuming this is needed
|
||||||
xwayland.sandbox.whitelistX = true;
|
xwayland.sandbox.net = "clearnet"; #< just assuming this is needed (X11 traffic)
|
||||||
xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf
|
xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf
|
||||||
|
|
||||||
xterm.sandbox.enable = false; # need to be able to do everything
|
xterm.sandbox.enable = false; # need to be able to do everything
|
||||||
|
@ -1163,43 +936,7 @@ in
|
||||||
zfs = {};
|
zfs = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.persist.sys.byStore.plaintext = lib.mkIf config.sane.programs.guiApps.enabled [
|
programs.feedbackd = lib.mkIf config.sane.programs.feedbackd.enabled {
|
||||||
# "/var/lib/alsa" # preserve output levels, default devices
|
|
||||||
{ path = "/var/lib/systemd/backlight"; method = "bind"; } # backlight brightness; bind because systemd T_T
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.services."systemd-backlight@" = lib.mkIf config.sane.programs.guiApps.enabled {
|
|
||||||
after = [
|
|
||||||
"ensure-var-lib-systemd-backlight.service"
|
|
||||||
];
|
|
||||||
wants = [
|
|
||||||
"ensure-var-lib-systemd-backlight.service"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
hardware.graphics = lib.mkIf config.sane.programs.guiApps.enabled ({
|
|
||||||
enable = true;
|
enable = 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;
|
|
||||||
}));
|
|
||||||
|
|
||||||
system.activationScripts.notifyActive = lib.mkIf config.sane.programs.guiApps.enabled {
|
|
||||||
text = lib.concatStringsSep "\n" ([
|
|
||||||
''
|
|
||||||
tryNotifyUser() {
|
|
||||||
local user="$1"
|
|
||||||
local new_path="$PATH:${pkgs.sudo}/bin:${pkgs.libnotify}/bin"
|
|
||||||
local version="$(cat $systemConfig/nixos-version)"
|
|
||||||
PATH="$new_path" sudo -u "$user" \
|
|
||||||
env PATH="$new_path" NIXOS_VERSION="$version" /bin/sh -c \
|
|
||||||
'. $HOME/.profile; dbus_file="$XDG_RUNTIME_DIR/bus"; if [ -z "$DBUS_SESSION_BUS_ADDRESS" ] && [ -e "$dbus_file" ]; then export DBUS_SESSION_BUS_ADDRESS="unix:path=$dbus_file"; fi ; if [ -n "$DBUS_SESSION_BUS_ADDRESS" ]; then notify-send "nixos activated" "version: $NIXOS_VERSION" ; fi'
|
|
||||||
}
|
|
||||||
''
|
|
||||||
] ++ lib.mapAttrsToList
|
|
||||||
(user: en: lib.optionalString en "tryNotifyUser ${user}")
|
|
||||||
config.sane.programs.guiApps.enableFor.user
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
# tips/tricks
|
|
||||||
# - audio recording
|
|
||||||
# - default recording input will be silent, on lappy.
|
|
||||||
# - Audio Setup -> Rescan Audio Devices ...
|
|
||||||
# - Audio Setup -> Recording device -> sysdefault
|
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.audacity = {
|
sane.programs.audacity = {
|
||||||
|
@ -14,12 +9,10 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
buildCost = 1;
|
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.autodetectCliPaths = "existingFile";
|
sandbox.autodetectCliPaths = true;
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
# support media imports via file->open dir to some common media directories
|
# support media imports via file->open dir to some common media directories
|
||||||
"tmp"
|
"tmp"
|
||||||
|
@ -27,9 +20,6 @@
|
||||||
# audacity needs the entire config dir mounted if running in a sandbox
|
# audacity needs the entire config dir mounted if running in a sandbox
|
||||||
".config/audacity"
|
".config/audacity"
|
||||||
];
|
];
|
||||||
sandbox.extraPaths = [
|
|
||||||
"/dev/snd" # for recording audio inputs to work
|
|
||||||
];
|
|
||||||
|
|
||||||
# disable first-run splash screen
|
# disable first-run splash screen
|
||||||
fs.".config/audacity/audacity.cfg".file.text = ''
|
fs.".config/audacity/audacity.cfg".file.text = ''
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
# `ausyscall --dump`: lists all syscalls by number and name
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
sane.programs.ausyscall = {
|
|
||||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.audit "bin/ausyscall";
|
|
||||||
|
|
||||||
sandbox.method = "landlock";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.programs.bemenu = {
|
sane.programs.bemenu = {
|
||||||
sandbox.method = "bwrap"; # landlock works, but requires *all* of $XDG_RUNTIME_DIR to be granted.
|
sandbox.method = "bwrap"; # landlock works, but requires *all* of /run/user/$ID to be granted.
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
".cache/fontconfig" #< else it complains, and is *way* slower
|
".cache/fontconfig" #< else it complains, and is *way* slower
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p blast-ugjka
|
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p blast-ugjka
|
||||||
# vim: set filetype=python :
|
# vim: set filetype=python :
|
||||||
|
|
||||||
|
import ctypes
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
@ -13,9 +16,53 @@ logger = logging.getLogger(__name__)
|
||||||
# map from known devices -> required flags
|
# map from known devices -> required flags
|
||||||
DEVICE_MAP = {
|
DEVICE_MAP = {
|
||||||
"Theater TV": [],
|
"Theater TV": [],
|
||||||
"Cuddlevision": [ "-usewav" ],
|
"[LG] webOS TV OLED55C9PUA": [ "-usewav" ],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def set_pdeathsig(sig=signal.SIGTERM):
|
||||||
|
"""
|
||||||
|
helper function to ensure once parent process exits, its children processes will automatically die.
|
||||||
|
see: <https://stackoverflow.com/a/43152455>
|
||||||
|
see: <https://www.man7.org/linux/man-pages/man2/prctl.2.html>
|
||||||
|
"""
|
||||||
|
libc = ctypes.CDLL("libc.so.6")
|
||||||
|
return libc.prctl(1, sig)
|
||||||
|
|
||||||
|
MY_PID = None
|
||||||
|
|
||||||
|
def reap_children(sig=None, frame=None):
|
||||||
|
global MY_PID
|
||||||
|
# reset SIGTERM handler to avoid recursing
|
||||||
|
signal.signal(signal.SIGTERM, signal.Handlers.SIG_DFL)
|
||||||
|
logger.info("killing all children (of pid %d)", MY_PID)
|
||||||
|
os.killpg(MY_PID, signal.SIGTERM)
|
||||||
|
|
||||||
|
def reap_on_exit():
|
||||||
|
"""
|
||||||
|
catch when the parent exits, and map that to SIGTERM for this process.
|
||||||
|
when this process receives SIGTERM, also terminate all descendent processes.
|
||||||
|
|
||||||
|
this is done because:
|
||||||
|
1. mpv invokes this, but (potentially) via the sandbox wrapper.
|
||||||
|
2. when mpv exits, it `SIGKILL`s that sandbox wrapper.
|
||||||
|
3. bwrap does not pass SIGKILL or SIGTERM to its child.
|
||||||
|
4. hence, we neither receive that signal NOR can we pass it on simply by killing our immediate children
|
||||||
|
(since any bwrap'd children wouldn't pass that signal on...)
|
||||||
|
really, the proper fix would be on mpv's side:
|
||||||
|
- mpv should create a new process group when it launches a command, and kill that process group on exit.
|
||||||
|
or fix this in the sandbox wrapper:
|
||||||
|
- why *doesn't* bwrap forward the signals?
|
||||||
|
- there's --die-with-parent, but i can't apply that *system wide* and expect reasonably behavior
|
||||||
|
<https://github.com/containers/bubblewrap/issues/529>
|
||||||
|
"""
|
||||||
|
global MY_PID
|
||||||
|
MY_PID = os.getpid()
|
||||||
|
# create a new process group, pgid = gid
|
||||||
|
os.setpgid(MY_PID, MY_PID)
|
||||||
|
|
||||||
|
set_pdeathsig(signal.SIGTERM)
|
||||||
|
signal.signal(signal.SIGTERM, reap_children)
|
||||||
|
|
||||||
def get_ranked_ip_addrs():
|
def get_ranked_ip_addrs():
|
||||||
"""
|
"""
|
||||||
return the IP addresses most likely to be LAN addresses
|
return the IP addresses most likely to be LAN addresses
|
||||||
|
@ -49,6 +96,8 @@ class BlastDriver:
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
|
# this pdeathsig isn't necessary; seems it might result in leaked pulse outputs
|
||||||
|
# preexec_fn=set_pdeathsig
|
||||||
)
|
)
|
||||||
self.blast_flags = list(blast_flags)
|
self.blast_flags = list(blast_flags)
|
||||||
self.receiver_names = []
|
self.receiver_names = []
|
||||||
|
@ -153,11 +202,15 @@ def main():
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
reap_on_exit()
|
||||||
|
|
||||||
blast = try_blast()
|
blast = try_blast()
|
||||||
|
|
||||||
if blast is not None:
|
if blast is not None:
|
||||||
logger.info("waiting until blast exits")
|
logger.info("waiting until blast exits")
|
||||||
blast.blast.wait()
|
blast.blast.wait()
|
||||||
|
|
||||||
|
reap_children()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -39,10 +39,12 @@ in
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
#v else it fails to reap its children (or, maybe, it fails to hook its parent's death signal?)
|
sandbox.extraConfig = [
|
||||||
#v might be possible to remove this, but kinda hard to see a clean way.
|
# else it fails to reap its children (or, maybe, it fails to hook its parent's death signal?)
|
||||||
sandbox.isolatePids = false;
|
# might be possible to remove this, but kinda hard to see a clean way.
|
||||||
suggestedPrograms = [ "blast-ugjka" "sane-die-with-parent" ];
|
"--sane-sandbox-keep-namespace" "pid"
|
||||||
|
];
|
||||||
|
suggestedPrograms = [ "blast-ugjka" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 9000 ];
|
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 9000 ];
|
||||||
|
|
|
@ -99,41 +99,37 @@ in
|
||||||
type = types.listOf transitionType;
|
type = types.listOf transitionType;
|
||||||
default = [];
|
default = [];
|
||||||
};
|
};
|
||||||
};
|
configFile = mkOption {
|
||||||
};
|
type = types.path;
|
||||||
};
|
default = pkgs.writeText "bonsai_tree.json" (builtins.toJSON cfg.config.transitions);
|
||||||
|
description = ''
|
||||||
packageUnwrapped = pkgs.bonsai.overrideAttrs (upstream: {
|
configuration file to pass to bonsai.
|
||||||
# patch to place the socket in a subdirectory where it can be sandboxed
|
usually auto-generated from the sibling options; exposed mainly for debugging or convenience.
|
||||||
postPatch = (upstream.postPatch or "") + ''
|
|
||||||
substituteInPlace cmd/{bonsaictl,bonsaid}/main.ha \
|
|
||||||
--replace-fail 'path::set(&buf, statedir, "bonsai")' 'path::set(&buf, statedir, "bonsai/bonsai")'
|
|
||||||
'';
|
'';
|
||||||
});
|
};
|
||||||
|
};
|
||||||
fs.".config/bonsai/bonsai_tree.json".symlink.target = pkgs.writers.writeJSON "bonsai_tree.json" cfg.config.transitions;
|
};
|
||||||
|
};
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.extraRuntimePaths = [
|
sandbox.extraRuntimePaths = [
|
||||||
"bonsai"
|
"/" #< just needs "bonsai", but needs to create it first...
|
||||||
];
|
];
|
||||||
|
|
||||||
services.bonsaid = {
|
services.bonsaid = {
|
||||||
description = "bonsai: programmable input dispatcher";
|
description = "bonsai: programmable input dispatcher";
|
||||||
dependencyOf = [ "sway" ]; # to ensure `$XDG_RUNTIME_DIR/bonsai` exists before sway binds it
|
after = [ "graphical-session.target" ];
|
||||||
partOf = [ "graphical-session" ];
|
wantedBy = [ "graphical-session.target" ];
|
||||||
# nice -n -11 chosen arbitrarily. i hope this will allow for faster response to inputs, but without audio underruns (pipewire is -21, dino -15-ish)
|
|
||||||
command = pkgs.writeShellScript "bonsai-start" ''
|
script = ''
|
||||||
# TODO: don't create the sway directory here!
|
${pkgs.coreutils}/bin/rm -f $XDG_RUNTIME_DIR/bonsai
|
||||||
# i do it for now because sway and bonsai call into eachother; circular dependency:
|
exec ${cfg.package}/bin/bonsaid -t ${cfg.config.configFile}
|
||||||
# - sway -> bonsai -> sane-input-handler -> swaymsg
|
|
||||||
mkdir -p $XDG_RUNTIME_DIR/{bonsai,sway}
|
|
||||||
exec nice -n -11 bonsaid -t $HOME/.config/bonsai/bonsai_tree.json
|
|
||||||
'';
|
'';
|
||||||
cleanupCommand = "rm -f $XDG_RUNTIME_DIR/bonsai/bonsai";
|
serviceConfig = {
|
||||||
readiness.waitExists = [
|
Type = "simple";
|
||||||
"$XDG_RUNTIME_DIR/bonsai/bonsai"
|
Restart = "always";
|
||||||
];
|
RestartSec = "5s";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
{ pkgs, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
sane.programs.brave = {
|
sane.programs.brave = {
|
||||||
# convert eval error to build failure
|
|
||||||
packageUnwrapped = if (builtins.tryEval pkgs.brave).success then
|
|
||||||
pkgs.brave
|
|
||||||
else
|
|
||||||
pkgs.runCommandLocal "brave-not-supported" {} "false"
|
|
||||||
;
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "inplace"; # /opt/share/brave.com vendor-style packaging
|
sandbox.wrapperType = "inplace"; # /opt/share/brave.com vendor-style packaging
|
||||||
sandbox.net = "all";
|
sandbox.net = "all";
|
||||||
|
@ -14,9 +8,6 @@
|
||||||
"dev" # for developing anything web-related
|
"dev" # for developing anything web-related
|
||||||
"tmp"
|
"tmp"
|
||||||
];
|
];
|
||||||
sandbox.extraPaths = [
|
|
||||||
"/tmp" # needed particularly if run from `sane-vpn do`
|
|
||||||
];
|
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDri = true;
|
sandbox.whitelistDri = true;
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.sane.programs.brightnessctl;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
sane.programs.brightnessctl = {
|
|
||||||
sandbox.method = "landlock"; # also bwrap, but landlock is more responsive
|
|
||||||
sandbox.extraPaths = [
|
|
||||||
"/sys/class/backlight"
|
|
||||||
"/sys/class/leds"
|
|
||||||
"/sys/devices"
|
|
||||||
];
|
|
||||||
# sandbox.whitelistDbus = [ "system" ]; #< only necessary if not granting udev perms
|
|
||||||
};
|
|
||||||
|
|
||||||
services.udev.extraRules = let
|
|
||||||
chmod = "${pkgs.coreutils}/bin/chmod";
|
|
||||||
chown = "${pkgs.coreutils}/bin/chown";
|
|
||||||
in lib.mkIf cfg.enabled ''
|
|
||||||
# make backlight controllable by members of `video`
|
|
||||||
SUBSYSTEM=="backlight", RUN+="${chown} :video $sys$devpath/brightness", RUN+="${chmod} g+w $sys$devpath/brightness"
|
|
||||||
'';
|
|
||||||
}
|
|
|
@ -21,12 +21,12 @@
|
||||||
# note that invoking bwrap with capabilities in the 'init' namespace does NOT grant the sandboxed process
|
# note that invoking bwrap with capabilities in the 'init' namespace does NOT grant the sandboxed process
|
||||||
# capabilities in the 'init' namespace. it's a limitation of namespaces that namespaced processes can
|
# capabilities in the 'init' namespace. it's a limitation of namespaces that namespaced processes can
|
||||||
# never receive capabilities in their parent namespace.
|
# never receive capabilities in their parent namespace.
|
||||||
substituteInPlace bubblewrap.c --replace-fail \
|
substituteInPlace bubblewrap.c --replace \
|
||||||
'die ("Unexpected capabilities but not setuid, old file caps config?");' \
|
'die ("Unexpected capabilities but not setuid, old file caps config?");' \
|
||||||
'// die ("Unexpected capabilities but not setuid, old file caps config?");'
|
'// die ("Unexpected capabilities but not setuid, old file caps config?");'
|
||||||
|
|
||||||
# enable debug printing
|
# enable debug printing
|
||||||
# substituteInPlace utils.h --replace-fail \
|
# substituteInPlace utils.h --replace \
|
||||||
# '#define __debug__(x)' \
|
# '#define __debug__(x)' \
|
||||||
# '#define __debug__(x) printf x'
|
# '#define __debug__(x) printf x'
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
# https://gitlab.com/mobian1/callaudiod
|
|
||||||
# device support:
|
|
||||||
# - moby:
|
|
||||||
# - mic muting works fine
|
|
||||||
# - speaker seems to have zero volume (maybe it's my alsa profiles?)
|
|
||||||
# - shows some failures when only the modem is online (no wifi)
|
|
||||||
# - gnome-calls doesn't even create an output audio stream, for example; and the other end of the call can't hear any mic.
|
|
||||||
# - desko: unsupported. no mic muting, etc.
|
|
||||||
# - "Card 'alsa_card.pci-0000_0b_00.1' lacks speaker and/or earpiece port, skipping"
|
|
||||||
# - "callaudiod-pulse-CRITICAL **: 07:45:48.092: No suitable card found, stopping here..."
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
sane.programs.callaudiod = {
|
|
||||||
packageUnwrapped = pkgs.rmDbusServices pkgs.callaudiod;
|
|
||||||
|
|
||||||
# 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" ];
|
|
||||||
|
|
||||||
services.callaudiod = {
|
|
||||||
description = "callaudiod: dbus service to switch audio profiles and mute microphone";
|
|
||||||
partOf = [ "default" ];
|
|
||||||
command = "callaudiod";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,14 +1,20 @@
|
||||||
# GNOME calls
|
# GNOME calls
|
||||||
# - <https://gitlab.gnome.org/GNOME/calls>
|
# - <https://gitlab.gnome.org/GNOME/calls>
|
||||||
# - both a dialer and a call handler.
|
# - both a dialer and a call handler.
|
||||||
# - uses callaudiod dbus service.
|
# - uses callaudiod dbus package.
|
||||||
#
|
#
|
||||||
# initial JMP.chat configuration:
|
# initial JMP.chat configuration:
|
||||||
# - message @cheogram.com "reset sip account" (this is not destructive, despite the name)
|
# - message @cheogram.com "reset sip account" (this is not destructive, despite the name)
|
||||||
# - the bot will reply with auto-generated username/password plus a SIP server endpoint.
|
# - the bot will reply with auto-generated username/password plus a SIP server endpoint.
|
||||||
# just copy those into gnome-calls' GUI configurator
|
# just copy those into gnome-calls' GUI configurator
|
||||||
# - now gnome-calls can do outbound calls. inbound calls can be routed by messaging the bot: "configure calls"
|
# - now gnome-calls can do outbound calls. inbound calls requires more chatting with the help bot
|
||||||
{ config, lib, pkgs, ... }:
|
#
|
||||||
|
# my setup here is still very WIP.
|
||||||
|
# open questions:
|
||||||
|
# - can i receive calls even with GUI closed?
|
||||||
|
# - e.g. activated by callaudiod?
|
||||||
|
# - looks like `gnome-calls --daemon` does that?
|
||||||
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.sane.programs.calls;
|
cfg = config.sane.programs.calls;
|
||||||
in
|
in
|
||||||
|
@ -19,52 +25,37 @@ in
|
||||||
type = types.submodule {
|
type = types.submodule {
|
||||||
options.autostart = mkOption {
|
options.autostart = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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!
|
|
||||||
url = "https://git.uninsane.org/colin/gnome-calls/commit/a19166d85927e59662fae189a780eed18bf876ce.patch";
|
|
||||||
name = "exit on close (i.e. never daemonize)";
|
|
||||||
hash = "sha256-NoVQV2TlkCcsBt0uwSyK82hBKySUW4pADrJVfLFvWgU=";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
|
||||||
sandbox.net = "clearnet";
|
|
||||||
sandbox.whitelistAudio = true;
|
|
||||||
sandbox.whitelistDbus = [ "user" ]; # necessary for secrets, at the minimum
|
|
||||||
sandbox.whitelistWayland = true;
|
|
||||||
|
|
||||||
persist.byStore.private = [
|
persist.byStore.private = [
|
||||||
# ".cache/folks" # contact avatars?
|
# ".cache/folks" # contact avatars?
|
||||||
# ".config/calls"
|
# ".config/calls"
|
||||||
".local/share/calls" # call "records"
|
".local/share/calls" # call "records"
|
||||||
# .local/share/folks # contacts?
|
# .local/share/folks # contacts?
|
||||||
];
|
];
|
||||||
# this is only the username/endpoint: the actual password appears to be stored in gnome-keyring
|
|
||||||
secrets.".config/calls/sip-account.cfg" = ../../../secrets/common/gnome_calls_sip-account.cfg.bin;
|
secrets.".config/calls/sip-account.cfg" = ../../../secrets/common/gnome_calls_sip-account.cfg.bin;
|
||||||
suggestedPrograms = [
|
suggestedPrograms = [
|
||||||
"callaudiod" # runtime dependency (optional, but probably needed for mic muting?)
|
|
||||||
"feedbackd" # needs `phone-incoming-call`, in particular
|
"feedbackd" # needs `phone-incoming-call`, in particular
|
||||||
"gnome-keyring" # to remember the password
|
|
||||||
];
|
];
|
||||||
|
|
||||||
services.gnome-calls = {
|
services.gnome-calls = {
|
||||||
|
# TODO: prevent gnome-calls from daemonizing when started manually
|
||||||
description = "gnome-calls daemon to monitor incoming SIP calls";
|
description = "gnome-calls daemon to monitor incoming SIP calls";
|
||||||
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
||||||
|
serviceConfig = {
|
||||||
# add --verbose for more debugging
|
# add --verbose for more debugging
|
||||||
# add --daemon to avoid showing UI on launch.
|
ExecStart = "${cfg.package}/bin/gnome-calls --daemon";
|
||||||
# note that no matter the flags, it returns to being a daemon whenever the UI is manually closed,
|
Type = "simple";
|
||||||
# revealed when launched.
|
Restart = "always";
|
||||||
# default latency is 10ms, which is too low and i get underruns on moby.
|
RestartSec = "10s";
|
||||||
# 50ms is copied from dino, not at all tuned.
|
|
||||||
command = "env G_MESSAGES_DEBUG=all PULSE_LATENCY_MSEC=50 gnome-calls";
|
|
||||||
};
|
};
|
||||||
|
environment.G_MESSAGES_DEBUG = "all";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
programs.calls = lib.mkIf cfg.enabled {
|
||||||
|
enable = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
sane.programs.celeste64 = {
|
sane.programs.celeste64 = {
|
||||||
buildCost = 1;
|
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDri = true;
|
sandbox.whitelistDri = true;
|
||||||
|
|
184
hosts/common/programs/conky/battery_estimate
Executable file
184
hosts/common/programs/conky/battery_estimate
Executable file
|
@ -0,0 +1,184 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#!/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 come from sxmo; they 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
|
|
@ -66,7 +66,6 @@ end
|
||||||
if vars.percent ~= nil then
|
if vars.percent ~= nil then
|
||||||
bat_args = bat_args .. " --percent-suffix '" .. vars.percent .. "'"
|
bat_args = bat_args .. " --percent-suffix '" .. vars.percent .. "'"
|
||||||
end
|
end
|
||||||
bat_args = bat_args .. " {bat}"
|
|
||||||
|
|
||||||
-- N.B.: `[[ <text> ]]` is Lua's multiline string literal
|
-- N.B.: `[[ <text> ]]` is Lua's multiline string literal
|
||||||
conky.text = [[
|
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}
|
${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=22:style=Bold}${alignc}${execp @bat@ ]] .. 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=20:style=Bold}${alignc}${texeci 600 @weather@ }${font}
|
||||||
|
|
||||||
|
|
||||||
${color2}${shadecolor a4d7d0}${font sans-serif:size=16}${alignc}⇅ ${downspeedf wlan0}]] .. vars.kBps .. [[${font}
|
${color2}${shadecolor a4d7d0}${font sans-serif:size=16}${alignc}⇅ ${downspeedf wlan0}]] .. vars.kBps .. [[${font}
|
||||||
|
|
|
@ -1,27 +1,39 @@
|
||||||
{ pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.conky = {
|
sane.programs.conky = {
|
||||||
|
# TODO: non-sandboxed `conky` still ships via `sxmo-utils`, but unused
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.net = "clearnet"; #< for the scripts it calls (weather)
|
sandbox.net = "clearnet"; #< for the scripts it calls (weather)
|
||||||
sandbox.extraPaths = [
|
sandbox.extraPaths = [
|
||||||
"/sys/class/power_supply"
|
"/sys/class/power_supply"
|
||||||
"/sys/devices" # needed by sane-sysload
|
"/sys/devices" # needed by battery_estimate
|
||||||
# "/sys/devices/cpu"
|
# "/sys/devices/cpu"
|
||||||
# "/sys/devices/system"
|
# "/sys/devices/system"
|
||||||
];
|
];
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
suggestedPrograms = [
|
fs.".config/conky/conky.conf".symlink.target =
|
||||||
"sane-sysload"
|
let
|
||||||
"sane-weather"
|
battery_estimate = pkgs.static-nix-shell.mkBash {
|
||||||
];
|
pname = "battery_estimate";
|
||||||
|
srcRoot = ./.;
|
||||||
fs.".config/conky/conky.conf".symlink.target = ./conky.conf;
|
};
|
||||||
|
in pkgs.substituteAll {
|
||||||
|
src = ./conky.conf;
|
||||||
|
bat = "${battery_estimate}/bin/battery_estimate";
|
||||||
|
weather = "timeout 20 ${pkgs.sane-weather}/bin/sane-weather";
|
||||||
|
};
|
||||||
|
|
||||||
services.conky = {
|
services.conky = {
|
||||||
description = "conky dynamic desktop background";
|
description = "conky dynamic desktop background";
|
||||||
partOf = [ "graphical-session" ];
|
after = [ "graphical-session.target" ];
|
||||||
command = "conky";
|
# partOf = [ "graphical-session.target" ]; # propagate stop/restart signal from graphical-session to this unit
|
||||||
|
wantedBy = [ "graphical-session.target" ];
|
||||||
|
|
||||||
|
serviceConfig.ExecStart = "${config.sane.programs.conky.package}/bin/conky";
|
||||||
|
serviceConfig.Type = "simple";
|
||||||
|
serviceConfig.Restart = "on-failure";
|
||||||
|
serviceConfig.RestartSec = "10s";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
|
|
||||||
buildCost = 1;
|
|
||||||
|
|
||||||
sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied
|
sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # mpris
|
sandbox.whitelistDbus = [ "user" ]; # mpris
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
# docs: <https://wiki.nixos.org/wiki/Printing>
|
|
||||||
# to add a printer:
|
|
||||||
# 1. <http://localhost:631/admin/>
|
|
||||||
# 2. click "find new printers" and follow prompts
|
|
||||||
# - prefer to use the "Generic IPP Everywhere Printer" driver
|
|
||||||
# alternatively, add/modify printers by running
|
|
||||||
# - `system-config-printer`
|
|
||||||
{ config, lib, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.sane.programs.cups;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
sane.programs.cups = {
|
|
||||||
suggestedPrograms = [
|
|
||||||
"system-config-printer"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
sane.programs.system-config-printer = {};
|
|
||||||
|
|
||||||
services.printing = lib.mkIf cfg.enabled {
|
|
||||||
enable = true;
|
|
||||||
startWhenNeeded = false; #< a.k.a. socket activated?
|
|
||||||
# webInterface = false;
|
|
||||||
# logLevel = "debug"; # default: "info"
|
|
||||||
# extraConfig = "<lines ... >";
|
|
||||||
# drivers = [ <cups driver packages...> ]
|
|
||||||
};
|
|
||||||
# services.avahi = lib.mkIf cfg.enabled {
|
|
||||||
# # only needed for wireless printing
|
|
||||||
# enable = true;
|
|
||||||
# nssmdns4 = true;
|
|
||||||
# openFirewall = true;
|
|
||||||
# };
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
sane.programs.curlftpfs = {
|
|
||||||
packageUnwrapped = pkgs.curlftpfs.overrideAttrs (upstream: {
|
|
||||||
# my fork includes:
|
|
||||||
# - per-operation timeouts (CURLOPT_TIMEOUT; would use CURLOPT_LOW_SPEED_TIME/CURLOPT_LOW_SPEED_LIMIT but they don't apply)
|
|
||||||
# - exit on timeout (so that one knows to abort the mount, instead of waiting indefinitely)
|
|
||||||
# - support for "meta" keys found in /etc/fstab
|
|
||||||
src = pkgs.fetchFromGitea {
|
|
||||||
domain = "git.uninsane.org";
|
|
||||||
owner = "colin";
|
|
||||||
repo = "curlftpfs";
|
|
||||||
rev = "0890d32e709b5a01153f00d29ed4c00299744f5d";
|
|
||||||
hash = "sha256-M28PzHqEAkezQdtPeL16z56prwl3BfMZqry0dlpXJls=";
|
|
||||||
};
|
|
||||||
# `mount` clears PATH before calling the mount helper (see util-linux/lib/env.c),
|
|
||||||
# so the traditional /etc/fstab approach of fstype=fuse and device = curlftpfs#URI doesn't work.
|
|
||||||
# instead, install a `mount.curlftpfs` mount helper. this is what programs like `gocryptfs` do.
|
|
||||||
postInstall = (upstream.postInstall or "") + ''
|
|
||||||
ln -s curlftpfs $out/bin/mount.fuse.curlftpfs
|
|
||||||
ln -s curlftpfs $out/bin/mount.curlftpfs
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
|
|
||||||
# TODO: try to sandbox this better? maybe i can have fuse (unsandboxed) invoke curlftpfs (sandboxed)?
|
|
||||||
# - landlock gives EPERM
|
|
||||||
# - bwrap just silently doesn't mount it, maybe because of setuid stuff around fuse?
|
|
||||||
# sandbox.method = "capshonly";
|
|
||||||
# sandbox.net = "all";
|
|
||||||
# sandbox.capabilities = [
|
|
||||||
# "sys_admin"
|
|
||||||
# "sys_module"
|
|
||||||
# ];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.sane.programs.dissent;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
sane.programs.dbus = {
|
|
||||||
configOption = with lib; mkOption {
|
|
||||||
default = {};
|
|
||||||
type = types.submodule {
|
|
||||||
options.autostart = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
packageUnwrapped = (pkgs.dbus.override {
|
|
||||||
# remove features i don't want. mostly to avoid undesired interactions, but also it reduces the closure by 55 MB :)
|
|
||||||
enableSystemd = false;
|
|
||||||
x11Support = false;
|
|
||||||
}).overrideAttrs (upstream: {
|
|
||||||
postFixup = (upstream.postFixup or "") + ''
|
|
||||||
# the XML docs have a URI field which points to self,
|
|
||||||
# and that breaks the sandbox checker
|
|
||||||
substituteInPlace $out/share/xml/dbus-1/catalog.xml \
|
|
||||||
--replace-fail "$out" "/run/current-system/sw"
|
|
||||||
|
|
||||||
# conf file points to dbus-daemon-launch-helper by absolute path,
|
|
||||||
# which breaks sandboxing. i don't want dbus auto-launching stuff anyway though.
|
|
||||||
substituteInPlace $out/share/dbus-1/system.conf \
|
|
||||||
--replace-fail "$out/libexec/dbus-daemon-launch-helper" "false"
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
|
||||||
sandbox.extraRuntimePaths = [
|
|
||||||
"/" #< it needs to create a file in the root. TODO: move the bus handle into a sandboxable subdirectory
|
|
||||||
];
|
|
||||||
sandbox.isolatePids = false; #< not actually sure *why* this is necessary, but it is
|
|
||||||
|
|
||||||
env.DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
|
|
||||||
|
|
||||||
# normally systemd would create a dbus session for us, but if you configure it not to do that
|
|
||||||
# then we can create our own. not sure if there's a dependency ordering issue here: lots
|
|
||||||
# of things depend on dbus but i don't do anything special to guarantee this is initialized
|
|
||||||
# before them.
|
|
||||||
services.dbus = {
|
|
||||||
description = "dbus user session";
|
|
||||||
partOf = lib.mkIf cfg.config.autostart [ "default" ];
|
|
||||||
command = "dbus-daemon --session --nofork --address=$DBUS_SESSION_BUS_ADDRESS";
|
|
||||||
readiness.waitExists = [ "$XDG_RUNTIME_DIR/bus" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -34,8 +34,15 @@ in
|
||||||
|
|
||||||
services.dconf = {
|
services.dconf = {
|
||||||
description = "dconf configuration database/server";
|
description = "dconf configuration database/server";
|
||||||
partOf = [ "default" ];
|
after = [ "graphical-session.target" ];
|
||||||
command = "${lib.getLib cfg.package}/libexec/dconf-service";
|
wantedBy = [ "graphical-session.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${lib.getLib cfg.package}/libexec/dconf-service";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "5s";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# supposedly necessary for packages which haven't been wrapped (i.e. wrapGtkApp?),
|
# supposedly necessary for packages which haven't been wrapped (i.e. wrapGtkApp?),
|
||||||
|
|
|
@ -9,14 +9,11 @@
|
||||||
./animatch.nix
|
./animatch.nix
|
||||||
./assorted.nix
|
./assorted.nix
|
||||||
./audacity.nix
|
./audacity.nix
|
||||||
./ausyscall.nix
|
|
||||||
./bemenu.nix
|
./bemenu.nix
|
||||||
./blast-ugjka
|
./blast-ugjka
|
||||||
./bonsai.nix
|
./bonsai.nix
|
||||||
./brave.nix
|
./brave.nix
|
||||||
./brightnessctl.nix
|
|
||||||
./bubblewrap.nix
|
./bubblewrap.nix
|
||||||
./callaudiod.nix
|
|
||||||
./calls.nix
|
./calls.nix
|
||||||
./cantata.nix
|
./cantata.nix
|
||||||
./catt.nix
|
./catt.nix
|
||||||
|
@ -24,60 +21,44 @@
|
||||||
./chatty.nix
|
./chatty.nix
|
||||||
./conky
|
./conky
|
||||||
./cozy.nix
|
./cozy.nix
|
||||||
./cups.nix
|
|
||||||
./curlftpfs.nix
|
|
||||||
./dbus.nix
|
|
||||||
./dconf.nix
|
./dconf.nix
|
||||||
./deadd-notification-center
|
./deadd-notification-center
|
||||||
./dialect.nix
|
./dialect.nix
|
||||||
./dino.nix
|
./dino.nix
|
||||||
./dissent.nix
|
./dissent.nix
|
||||||
./dtrx.nix
|
|
||||||
./eg25-control.nix
|
|
||||||
./element-desktop.nix
|
./element-desktop.nix
|
||||||
./engrampa.nix
|
|
||||||
./epiphany.nix
|
./epiphany.nix
|
||||||
./errno.nix
|
|
||||||
./evince.nix
|
./evince.nix
|
||||||
./fcitx5.nix
|
./fcitx5.nix
|
||||||
./feedbackd.nix
|
./feedbackd.nix
|
||||||
./firefox.nix
|
./firefox.nix
|
||||||
|
./firejail.nix
|
||||||
./flare-signal.nix
|
./flare-signal.nix
|
||||||
./fontconfig.nix
|
./fontconfig.nix
|
||||||
./fractal.nix
|
./fractal.nix
|
||||||
./free.nix
|
|
||||||
./frozen-bubble.nix
|
./frozen-bubble.nix
|
||||||
./fwupd.nix
|
./fwupd.nix
|
||||||
./g4music.nix
|
./g4music.nix
|
||||||
./gajim.nix
|
./gajim.nix
|
||||||
./gdb.nix
|
|
||||||
./gdbus.nix
|
./gdbus.nix
|
||||||
./geary.nix
|
./geary.nix
|
||||||
./geoclue-demo-agent.nix
|
|
||||||
./geoclue2.nix
|
|
||||||
./git.nix
|
./git.nix
|
||||||
./gnome-clocks.nix
|
|
||||||
./gnome-feeds.nix
|
./gnome-feeds.nix
|
||||||
./gnome-keyring
|
./gnome-keyring
|
||||||
./gnome-maps.nix
|
./gnome-maps.nix
|
||||||
./gnome-weather.nix
|
./gnome-weather.nix
|
||||||
./go2tv.nix
|
./go2tv.nix
|
||||||
./gpodder.nix
|
./gpodder.nix
|
||||||
./gpsd.nix
|
|
||||||
./grimshot.nix
|
./grimshot.nix
|
||||||
./gst-device-monitor.nix
|
|
||||||
./gthumb.nix
|
./gthumb.nix
|
||||||
./gvfs.nix
|
|
||||||
./handbrake.nix
|
./handbrake.nix
|
||||||
./helix.nix
|
./helix.nix
|
||||||
./htop
|
|
||||||
./imagemagick.nix
|
./imagemagick.nix
|
||||||
./jellyfin-media-player.nix
|
./jellyfin-media-player.nix
|
||||||
./kdenlive.nix
|
./kdenlive.nix
|
||||||
./komikku.nix
|
./komikku.nix
|
||||||
./koreader
|
./koreader
|
||||||
./less.nix
|
./less.nix
|
||||||
./lftp.nix
|
|
||||||
./libreoffice.nix
|
./libreoffice.nix
|
||||||
./lemoa.nix
|
./lemoa.nix
|
||||||
./loupe.nix
|
./loupe.nix
|
||||||
|
@ -85,50 +66,32 @@
|
||||||
./megapixels.nix
|
./megapixels.nix
|
||||||
./mepo.nix
|
./mepo.nix
|
||||||
./mimeo
|
./mimeo
|
||||||
./mmcli.nix
|
|
||||||
./mopidy.nix
|
./mopidy.nix
|
||||||
./mpv
|
./mpv
|
||||||
./msmtp.nix
|
./msmtp.nix
|
||||||
./nautilus.nix
|
./nautilus.nix
|
||||||
./neovim.nix
|
./neovim.nix
|
||||||
./networkmanager_dmenu
|
|
||||||
./newsflash.nix
|
./newsflash.nix
|
||||||
./nheko.nix
|
./nheko.nix
|
||||||
./nicotine-plus.nix
|
./nicotine-plus.nix
|
||||||
./nix-index.nix
|
./nix-index.nix
|
||||||
./nmcli.nix
|
|
||||||
./notejot.nix
|
./notejot.nix
|
||||||
./ntfy-sh.nix
|
./ntfy-sh.nix
|
||||||
./nwg-panel
|
|
||||||
./objdump.nix
|
|
||||||
./obsidian.nix
|
./obsidian.nix
|
||||||
./offlineimap.nix
|
./offlineimap.nix
|
||||||
./ols.nix
|
|
||||||
./open-in-mpv.nix
|
./open-in-mpv.nix
|
||||||
./pactl.nix
|
|
||||||
./pidof.nix
|
|
||||||
./pipewire.nix
|
./pipewire.nix
|
||||||
./planify.nix
|
./planify.nix
|
||||||
./portfolio-filemanager.nix
|
./portfolio-filemanager.nix
|
||||||
./playerctl.nix
|
./playerctl.nix
|
||||||
./ps.nix
|
|
||||||
./rhythmbox.nix
|
./rhythmbox.nix
|
||||||
./ripgrep.nix
|
./ripgrep.nix
|
||||||
./rofi
|
./rofi
|
||||||
./rtkit.nix
|
|
||||||
./s6-rc.nix
|
|
||||||
./sane-input-handler
|
./sane-input-handler
|
||||||
./sane-open.nix
|
|
||||||
./sane-screenshot.nix
|
./sane-screenshot.nix
|
||||||
./sane-scripts.nix
|
./sane-scripts.nix
|
||||||
./sane-sysload.nix
|
|
||||||
./sane-theme.nix
|
|
||||||
./sanebox.nix
|
|
||||||
./satellite.nix
|
|
||||||
./schlock.nix
|
./schlock.nix
|
||||||
./seatd.nix
|
|
||||||
./sfeed.nix
|
./sfeed.nix
|
||||||
./shadow.nix
|
|
||||||
./signal-desktop.nix
|
./signal-desktop.nix
|
||||||
./splatmoji.nix
|
./splatmoji.nix
|
||||||
./spot.nix
|
./spot.nix
|
||||||
|
@ -143,8 +106,7 @@
|
||||||
./swayidle.nix
|
./swayidle.nix
|
||||||
./swaylock.nix
|
./swaylock.nix
|
||||||
./swaynotificationcenter
|
./swaynotificationcenter
|
||||||
./switchboard.nix
|
./sysvol.nix
|
||||||
./syshud.nix
|
|
||||||
./tangram.nix
|
./tangram.nix
|
||||||
./tor-browser.nix
|
./tor-browser.nix
|
||||||
./tuba.nix
|
./tuba.nix
|
||||||
|
@ -152,11 +114,11 @@
|
||||||
./vlc.nix
|
./vlc.nix
|
||||||
./waybar
|
./waybar
|
||||||
./waylock.nix
|
./waylock.nix
|
||||||
./where-am-i.nix
|
|
||||||
./wike.nix
|
./wike.nix
|
||||||
./wine.nix
|
./wine.nix
|
||||||
./wireplumber.nix
|
./wireplumber.nix
|
||||||
./wireshark.nix
|
./wireshark.nix
|
||||||
|
./wob
|
||||||
./wvkbd.nix
|
./wvkbd.nix
|
||||||
./xarchiver.nix
|
./xarchiver.nix
|
||||||
./xdg-desktop-portal.nix
|
./xdg-desktop-portal.nix
|
||||||
|
@ -167,7 +129,6 @@
|
||||||
./zathura.nix
|
./zathura.nix
|
||||||
./zeal.nix
|
./zeal.nix
|
||||||
./zecwallet-lite.nix
|
./zecwallet-lite.nix
|
||||||
./zulip.nix
|
|
||||||
./zsh
|
./zsh
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.dialect = {
|
sane.programs.dialect = {
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
|
||||||
|
sandbox.whitelistWayland = true;
|
||||||
|
sandbox.net = "clearnet";
|
||||||
|
sandbox.extraHomePaths = [
|
||||||
|
".config/dconf" # won't start without it
|
||||||
|
];
|
||||||
|
suggestedPrograms = [ "dconf" ]; #< to persist settings
|
||||||
|
|
||||||
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {
|
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {
|
||||||
# TODO: send upstream
|
# TODO: send upstream
|
||||||
# TODO: figure out how to get audio working
|
# TODO: figure out how to get audio working
|
||||||
|
@ -9,17 +18,5 @@
|
||||||
pkgs.glib-networking # for TLS
|
pkgs.glib-networking # for TLS
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
suggestedPrograms = [ "dconf" ]; #< to persist settings
|
|
||||||
|
|
||||||
buildCost = 1;
|
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
|
||||||
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
|
|
||||||
sandbox.whitelistWayland = true;
|
|
||||||
sandbox.net = "clearnet";
|
|
||||||
sandbox.extraHomePaths = [
|
|
||||||
".config/dconf" # won't start without it
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,27 +14,22 @@
|
||||||
# but at present it has no "start in tray" type of option: it must render a window.
|
# but at present it has no "start in tray" type of option: it must render a window.
|
||||||
#
|
#
|
||||||
# outstanding bugs:
|
# outstanding bugs:
|
||||||
# - NAT holepunching burns CPU/NET when multiple interfaces are up
|
|
||||||
# - fix by just `ip link set ovpnd-xyz down`
|
|
||||||
# - setting `wg-home` down *seems* to be not necessary
|
|
||||||
# - characterized by UPnP/SOAP error 500/718 in wireshark
|
|
||||||
# - seems it asks router A to open a port mapping for an IP address which belongs to a different subnet...
|
|
||||||
# - mic is sometimes disabled at call start despite presenting as enabled
|
# - mic is sometimes disabled at call start despite presenting as enabled
|
||||||
# - fix is to toggle it off -> on in the Dino UI
|
# - fix is to toggle it off -> on in the Dino UI
|
||||||
# - default mic gain is WAY TOO MUCH (heavily distorted)
|
# - default mic gain is WAY TOO MUCH (heavily distorted)
|
||||||
# - on lappy/desktop, right-clicking the mic button allows to toggle audio devices, but impossible to trigger this on moby/touch screen!
|
# - TODO: dino should have more optimal niceness/priority to ensure it can process its buffers
|
||||||
|
# - possibly this is solved by enabling RealtimeKit (rtkit)
|
||||||
# - TODO: see if Dino calls work better with `echo full > /sys/kernel/debug/sched/preempt`
|
# - TODO: see if Dino calls work better with `echo full > /sys/kernel/debug/sched/preempt`
|
||||||
#
|
#
|
||||||
# probably fixed:
|
# probably fixed:
|
||||||
# - once per 1-2 minutes dino will temporarily drop mic input:
|
# - once per 1-2 minutes dino will temporarily drop mic input:
|
||||||
# - `rtp-WARNING: plugin.vala:148: Warning in pipeline: Can't record audio fast enough
|
# - `rtp-WRNING: plugin.vala:148: Warning in pipeline: Can't record audio fast enough
|
||||||
# - this was *partially* fixed by bumping the pipewire mic buffer to 2048 samples (from ~512)
|
# - this was *partially* fixed by bumping the pipewire mic buffer to 2048 samples (from ~512)
|
||||||
# - this was further fixed by setting PULSE_LATENCY_MSEC=20.
|
# - this was further fixed by setting PULSE_LATENCY_MSEC=20.
|
||||||
# - possibly Dino should be updated internally: `info.rate / 100` -> `info.rate / 50`.
|
# - possibly Dino should be updated internally: `info.rate / 100` -> `info.rate / 50`.
|
||||||
# - i think that affects the batching for echo cancellation, adaptive gain control, etc.
|
# - i think that affects the batching for echo cancellation, adaptive gain control, etc.
|
||||||
# - dino *should* be able to use Pipewire directly for calls instead of going through pulse, but had trouble achieving that in actuality
|
|
||||||
#
|
#
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.sane.programs.dino;
|
cfg = config.sane.programs.dino;
|
||||||
in
|
in
|
||||||
|
@ -50,33 +45,6 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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.method = "bwrap";
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
|
@ -100,34 +68,26 @@ in
|
||||||
|
|
||||||
services.dino = {
|
services.dino = {
|
||||||
description = "dino XMPP client";
|
description = "dino XMPP client";
|
||||||
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
after = [ "graphical-session.target" ];
|
||||||
|
# partOf = [ "graphical-session.target" ];
|
||||||
|
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${cfg.package}/bin/dino";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "20s";
|
||||||
|
};
|
||||||
|
|
||||||
# audio buffering; see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>
|
# audio buffering; see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>
|
||||||
# dino defaults to 10ms mic buffer, which causes underruns, which Dino handles *very* poorly
|
# dino defaults to 10ms mic buffer, which causes underruns, which Dino handles *very* poorly
|
||||||
# as in, the other end of the call will just not receive sound from us for a couple seconds.
|
# as in, the other end of the call will just not receive sound from us for a couple seconds.
|
||||||
# pipewire uses power-of-two buffering for the mic itself (by default), but this env var supports only whole numbers, which isn't quite reconcilable:
|
# pipewire uses power-of-two buffering for the mic itself. that would put us at 21.33 ms, but this env var supports only whole numbers (21ms ends up not power-of-two).
|
||||||
# - 1024/48000 = 21.33ms
|
# also, Dino's likely still doing things in 10ms batches internally anyway.
|
||||||
# - 2048/48000 = 42.67ms
|
environment.PULSE_LATENCY_MSEC = "20";
|
||||||
# - 4096/48000 = 85.33ms
|
|
||||||
# also, Dino's likely still doing things in 10ms batches internally.
|
|
||||||
#
|
|
||||||
# note that this number supposedly is just the buffer size which Dino asks Pulse (pipewire) to share with it.
|
|
||||||
# in theory, it's equivalent to adjusting pipewire's quanta setting, and so isn't additive to the existing pipewire buffers.
|
|
||||||
# (and would also be overriden by pipewire's quanta.min setting).
|
|
||||||
# but in practice, setting this seems to have some more effect beyond just the buffer sizes visible in `pw-top`.
|
|
||||||
#
|
|
||||||
# further: decrease the "niceness" of dino, so that it can take precedence over anything else.
|
|
||||||
# ideally this would target just the audio processing, rather than the whole program.
|
|
||||||
# pipewire is the equivalent of `nice -n -21`, so probably don't want to go any more extreme than that.
|
|
||||||
# nice -n -15 chosen arbitrarily; not optimized (and seems to have very little impact in practice anyway).
|
|
||||||
# buffer size:
|
|
||||||
# - 1024 (PULSE_LATENCY_MSEC=20): `pw-top` shows several underruns per second.
|
|
||||||
# - 2048 (PULSE_LATENCY_MSEC=50): `pw-top` shows very few underruns: maybe 1-5 per minute. with voice processor disabled, this works well. with it enabled, i still get gaps in which the mic "disappears".
|
|
||||||
# - 4096 (PULSE_LATENCY_MSEC=100): `pw-top` shows 0 underruns. with voice processor disabled, i seem to be permanently muted. with it enabled, this works well.
|
|
||||||
#
|
|
||||||
# note that debug logging during calls produces so much journal spam that it pegs the CPU and causes dropped audio
|
# note that debug logging during calls produces so much journal spam that it pegs the CPU and causes dropped audio
|
||||||
# env G_MESSAGES_DEBUG = "all";
|
# environment.G_MESSAGES_DEBUG = "all";
|
||||||
command = "env PULSE_LATENCY_MSEC=50 nice -n -15 dino";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user