Compare commits
240 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc1ec3bf5a | |||
| 823ed83252 | |||
| 300103f2ba | |||
| d33f32ce5b | |||
| 14d9bba4eb | |||
| e63cee7b09 | |||
| a8318d3de2 | |||
| aab4a193ae | |||
| 541102f683 | |||
| 805590a705 | |||
| f8dbd99a7b | |||
| 90cc2d53f1 | |||
| b044504938 | |||
| 98ccc23ef5 | |||
| d7e8ca52a0 | |||
| f1cc0fa25c | |||
| 43828ad34c | |||
| 36d7e4a7e3 | |||
| 2f7d5d3908 | |||
| f3a0ca3f57 | |||
| 023b3f6605 | |||
| ecd3a46591 | |||
| 6bcca599e2 | |||
| 93529c578b | |||
| bb32784962 | |||
| 6ffa35c414 | |||
| 3cc5448410 | |||
| ee4178ee2d | |||
| 2bf18319c9 | |||
| a394b9124a | |||
| 5bc48d33a3 | |||
| 365ef5d49d | |||
| 0206d52fa2 | |||
| 5526e07e65 | |||
| dde682390f | |||
| 4ec59a64ce | |||
| c9c788e261 | |||
| 21c24216b4 | |||
| 2ecd350fcc | |||
| 3aa873d52a | |||
| 0a8ad18de8 | |||
| b6b94fea5a | |||
| de9762b272 | |||
| 8d1f4d51d0 | |||
| f1dc04ec4b | |||
| 7951c777cb | |||
| b031840f81 | |||
| 45861bef08 | |||
| 4433395125 | |||
| 3306aaab5e | |||
| 4480302c65 | |||
| be7fb5a243 | |||
| 25758bae08 | |||
| 7bfe9ad697 | |||
| 7db5e18974 | |||
| 20b7da74bf | |||
| adaf8b6a83 | |||
| 1f145334f3 | |||
| abf9f1b465 | |||
| f0740741a4 | |||
| 0c0b66b8db | |||
| bdf3c04595 | |||
| 02795a6ee4 | |||
| 8fa4a7ee60 | |||
| 773674d879 | |||
| 12c5ca126d | |||
| b38a2a07e2 | |||
| 0dc474887f | |||
| c8bd63ec3e | |||
| d7522f3f97 | |||
| 58c76f822f | |||
| 31bcde23b8 | |||
| fc2fa0666e | |||
| 854cc48479 | |||
| 85a4b124e5 | |||
| f322f3ebac | |||
| bc74fb4968 | |||
| 584abd4991 | |||
| 05074a1fd9 | |||
| 69060dfbff | |||
| 8e288a9e2a | |||
| bb03b6fa76 | |||
| fd92cfae6e | |||
| 25267d09a2 | |||
| f02f538ab2 | |||
| d319657680 | |||
| dff5a4e6d8 | |||
| 2a8ced0fec | |||
| 36c7096120 | |||
| adfcf2f848 | |||
| a3870a4293 | |||
| 8f4b61fc2b | |||
| 44e3a3011a | |||
| 45c972cca9 | |||
| 7bd5b8cbdf | |||
| d1eb9cc981 | |||
| 7a2ebf6872 | |||
| 72b8bd089c | |||
| cff229f487 | |||
| f3ac3cd67f | |||
| 820bb2de5b | |||
| 7d3ad52a44 | |||
| 2cdb98e898 | |||
| b717b1ceb4 | |||
| f31ce61c2b | |||
| aec22942f7 | |||
| fc8676c3bb | |||
| 2915e42a1d | |||
| 5783d3a51e | |||
| 2fe94bba23 | |||
| 4b42960d26 | |||
| 56e9abf945 | |||
| 4e2c2f92f0 | |||
| caa208b288 | |||
| 9e6f885c17 | |||
| d8ca87bfd8 | |||
| e9467e0cc7 | |||
| 6c98ef8944 | |||
| 18981e240b | |||
| df7e5953eb | |||
| 71d1c3f9c2 | |||
| 1453a755c3 | |||
| 970af805e9 | |||
| 383e9a9b1e | |||
| 26a16d0629 | |||
| 208de7654e | |||
| f577e7d58a | |||
| 6130ee73be | |||
| 5d827aa00c | |||
| 173ffc0044 | |||
| b113f2f48d | |||
| 7c67eaff21 | |||
| d1f1b84e82 | |||
| e3cb2adbb6 | |||
| 736c406eb5 | |||
| 8e9b750ac8 | |||
| 51c5578840 | |||
| e174af45f6 | |||
| 198e7188bd | |||
| 571f8f1504 | |||
| 64c3fe682c | |||
| 7c05b6158f | |||
| c9ab90547f | |||
| 63d929c8e8 | |||
| bbb87a2d69 | |||
| e5d5847b89 | |||
| 9e7294e871 | |||
| 69216c6b4c | |||
| 1ea172e690 | |||
| b7be45715e | |||
| 3522a7078b | |||
| b44f0e74e8 | |||
| 7c57f00b27 | |||
| c9d36ec65b | |||
| d8f97b9316 | |||
| d5bb2f6787 | |||
| ced82fc002 | |||
| 3535d2fd90 | |||
| 4e207c3397 | |||
| bc4e75a6a5 | |||
| 2ae922f3e8 | |||
| f263fdca3e | |||
| 1232e9cb30 | |||
| fbb29162ca | |||
| 7ab57a12b7 | |||
| 4e947d4b1e | |||
| b68e82ae03 | |||
| 91489551b9 | |||
| 86c99c2cbb | |||
| 7e2dfc21c6 | |||
| 9ac63220d5 | |||
| ffa5d19854 | |||
| 19fb29213e | |||
| f9870abc9e | |||
| 84ca556c47 | |||
| 9f2651e352 | |||
| bce876ec42 | |||
| bc8adcecad | |||
| 8878ce56c4 | |||
| dd9439b7fa | |||
| bc9f266ef0 | |||
| 1b083d298b | |||
| 83ba26735e | |||
| 50bd96ccdf | |||
| a133cfb189 | |||
| 051e68254e | |||
| 3fa8ab43ef | |||
| c6720f87c1 | |||
| 55ecdddadb | |||
| 9b5173a587 | |||
| 73f5a690bb | |||
| 54db751e23 | |||
| 85299b65dc | |||
| 45bda5b588 | |||
| 9114f5ce74 | |||
| 3925c1090e | |||
| e74538a1a9 | |||
| b8ee21b6e8 | |||
| 41fd54cfad | |||
| 1df34e0515 | |||
| ce0c194761 | |||
| a5e51ddd6b | |||
| 746e0b9dc4 | |||
| 77600a64fc | |||
| c6d5705097 | |||
| 6eefe97764 | |||
| 4bc4fe3ee8 | |||
| 57ec2bfc1b | |||
| d9d1150feb | |||
| 92896d8e52 | |||
| 477ffca33e | |||
| fdc65c544e | |||
| 945302b7c0 | |||
| 5ccf19cab8 | |||
| 7b61dd7f03 | |||
| 682865a0e1 | |||
| a0e4cf2479 | |||
| a5880d66f4 | |||
| 27a4583879 | |||
| fdbf5f8aca | |||
| 40c491aa14 | |||
| 1a8740fb9c | |||
| f857e751b5 | |||
| b420f2377c | |||
| 7d90b5ecb8 | |||
| ace979c226 | |||
| f540edb361 | |||
| 6bc5cd79da | |||
| 5ec77dfde6 | |||
| 52623d458e | |||
| 23b29f0707 | |||
| 338902497f | |||
| 977846991a | |||
| 0e8aec58fb | |||
| 0f1de58917 | |||
| 32183bd331 | |||
| 1813ca1927 | |||
| 51d44e472a | |||
| 44e87aa387 | |||
| f90deabb50 |
+26
-18
@@ -6,11 +6,11 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
name: Check, build and cache Nix flake
|
name: Check, build and cache nixfiles
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: cachix/install-nix-action@v23
|
- uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
# Gitea will supply a token in GITHUB_TOKEN, which this action will
|
# Gitea will supply a token in GITHUB_TOKEN, which this action will
|
||||||
# try to pass to Nix when downloading from GitHub
|
# try to pass to Nix when downloading from GitHub
|
||||||
@@ -18,22 +18,30 @@ jobs:
|
|||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
# Make sure we're using sandbox
|
# Make sure we're using sandbox
|
||||||
sandbox-fallback = false
|
sandbox-fallback = false
|
||||||
|
# Big C++ projects fill up memory...
|
||||||
|
cores = 6
|
||||||
|
|
||||||
extra-substituters = https://nix-cache.nul.ie/main
|
extra-substituters = https://nix-cache.nul.ie
|
||||||
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=
|
extra-trusted-public-keys = nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4=
|
||||||
- name: Set up attic
|
|
||||||
run: |
|
|
||||||
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
|
|
||||||
login --set-default colony https://nix-cache.nul.ie "${{ secrets.NIX_CACHE_TOKEN }}"
|
|
||||||
|
|
||||||
- name: Check flake
|
- name: Check flake
|
||||||
run: nix flake check
|
run: nix flake check --no-build
|
||||||
- name: Build the world
|
|
||||||
|
- name: Build (and cache) the world
|
||||||
id: build
|
id: build
|
||||||
|
env:
|
||||||
|
HARMONIA_SSH_KEY: ${{ secrets.HARMONIA_SSH_KEY }}
|
||||||
run: |
|
run: |
|
||||||
path=$(nix build --no-link .#ci.x86_64-linux --json | jq -r .[0].outputs.out)
|
nix eval --json --apply "builtins.attrNames" .#ci.x86_64-linux | jq -cr '.[]' | while read job; do
|
||||||
echo "path=$path" >> "$GITHUB_OUTPUT"
|
echo "::group::Build $job"
|
||||||
- name: Push to cache
|
nix build --no-link .#ci.x86_64-linux."$job"
|
||||||
run: |
|
echo "::endgroup::"
|
||||||
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
|
|
||||||
push main ${{ steps.build.outputs.path }}
|
echo "::group::Cache $job"
|
||||||
|
ci/push-to-cache.sh "$(nix eval --raw .#ci.x86_64-linux."$job")"
|
||||||
|
echo "::endgroup::"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Building and caching CI derivation"
|
||||||
|
nix build --no-link .#ciDrv.x86_64-linux
|
||||||
|
UPDATE_PROFILE=1 ci/push-to-cache.sh "$(nix eval --raw .#ciDrv.x86_64-linux)"
|
||||||
|
|||||||
@@ -14,22 +14,20 @@ jobs:
|
|||||||
uses: https://github.com/actions/setup-go@v4
|
uses: https://github.com/actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: '>=1.20.1'
|
go-version: '>=1.20.1'
|
||||||
- uses: cachix/install-nix-action@v23
|
- uses: cachix/install-nix-action@v27
|
||||||
with:
|
with:
|
||||||
github_access_token: ${{ secrets.GH_PULL_TOKEN }}
|
github_access_token: ${{ secrets.GH_PULL_TOKEN }}
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
# Make sure we're using sandbox
|
# Make sure we're using sandbox
|
||||||
sandbox-fallback = false
|
sandbox-fallback = false
|
||||||
|
|
||||||
extra-substituters = https://nix-cache.nul.ie/main
|
extra-substituters = https://nix-cache.nul.ie
|
||||||
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=
|
extra-trusted-public-keys = nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4=
|
||||||
- name: Set up attic
|
|
||||||
|
- name: Set up vars
|
||||||
id: setup
|
id: setup
|
||||||
run: |
|
run: |
|
||||||
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
|
|
||||||
login --set-default colony https://nix-cache.nul.ie "${{ secrets.NIX_CACHE_TOKEN }}"
|
|
||||||
echo "short_rev=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
echo "short_rev=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Build installer ISO
|
- name: Build installer ISO
|
||||||
run: |
|
run: |
|
||||||
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.iso
|
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.iso
|
||||||
@@ -39,12 +37,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.netbootArchive
|
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.netbootArchive
|
||||||
ln -s "$(readlink result)" \
|
ln -s "$(readlink result)" \
|
||||||
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar
|
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar.zst
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: https://gitea.com/actions/release-action@main
|
uses: https://gitea.com/actions/release-action@main
|
||||||
with:
|
with:
|
||||||
title: Latest installer
|
title: Latest installer
|
||||||
|
api_key: '${{ secrets.RELEASE_TOKEN }}'
|
||||||
files: |
|
files: |
|
||||||
jackos-installer-${{ steps.setup.outputs.short_rev }}.iso
|
jackos-installer-${{ steps.setup.outputs.short_rev }}.iso
|
||||||
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar
|
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar.zst
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKXRXkYnBf2opIjN+bXE7HmhUpa4hyXJUGmBT+MRccT4 harmonia
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
This file provides guidance to coding agents when working with code in this repository.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Personal Nix flake managing NixOS systems and home-manager configurations for a set of
|
||||||
|
machines — always called **"boxes"**, never "fleet". It is built around a **custom module
|
||||||
|
system** layered on top of NixOS/home-manager, not the stock flake `nixosConfigurations` pattern.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
This repo provides a `numtide/devshell` (entered via `direnv` / `use flake`). The shell defines
|
||||||
|
named commands — prefer them over raw `nix` invocations. Run a bare command name with no args to
|
||||||
|
see its help, or browse `devshell/commands.nix` / `devshell/install.nix` / `devshell/vm-tasks.nix`.
|
||||||
|
|
||||||
|
Common ones:
|
||||||
|
- `fmt` — format Nix with `nixpkgs-fmt` (the canonical formatter here).
|
||||||
|
- `build-system <host> [nix args]` — build a NixOS system's `toplevel`.
|
||||||
|
- `build-n-switch <args>` — wraps `doas nixos-rebuild --flake .`.
|
||||||
|
- `build-home <name>` / `home-switch` — build / switch a home-manager config.
|
||||||
|
- `run-vm <host>` — build & boot a system as a dev VM (installs `.keys/dev.key` into the VM).
|
||||||
|
- `build-iso` / `build-kexec` / `build-netboot <host>` — alternate build outputs via `config.my.buildAs.*`.
|
||||||
|
- `check-system <host> [nix args]` — evaluate a system (catches eval errors without a full build).
|
||||||
|
**Prefer this over `build-system` to validate a config change** — evaluation surfaces module/option
|
||||||
|
errors quickly and cheaply; only do a full build when you specifically need the built artifact.
|
||||||
|
- `deploy <host>` and `deploy-multi <hosts...>` — deploy-rs deployment (uses `.keys/deploy.key`, `--skip-checks`).
|
||||||
|
Pass the flake-qualified node, e.g. `deploy .#git`. The deploy node name is **always** the system
|
||||||
|
name (`deploy-rs.nix` keys nodes directly off `nixos.systems` / `home-manager.homes`); a system is
|
||||||
|
only a deploy target when `config.my.deploy.enable` is true (defaults true; auto-disabled for dev
|
||||||
|
VMs and containers).
|
||||||
|
- `ssh-machine <name> [cmd]` — SSH to a NixOS system or home-manager config by name. Resolves the
|
||||||
|
target and ssh options (identity, port) from its deploy-rs node, so it needs `my.deploy.enable`
|
||||||
|
(same gate as `deploy`). Boxes default to the `fish` login shell, so pipe multi-statement remote
|
||||||
|
scripts through `bash` (e.g. `ssh-machine <name> bash -s < script.sh`) rather than `&&`/`for`.
|
||||||
|
- `ragenix` — edit age secrets using `.keys/dev.key` as identity (see Secrets).
|
||||||
|
- `repl` — `nix repl .#`.
|
||||||
|
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs.
|
||||||
|
|
||||||
|
Check everything (what CI runs): `nix flake check --no-build`.
|
||||||
|
CI builds each attr of `.#ci.x86_64-linux` (systems, homes, packages, shell) and pushes to the
|
||||||
|
Harmonia binary cache; see `.gitea/workflows/ci.yaml` and `ci/push-to-cache.sh`.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### The custom module system
|
||||||
|
`flake.nix` does **not** call `nixosSystem` per host directly. Instead it `evalModules` over
|
||||||
|
`./nixos`, `./home-manager`, `./deploy-rs.nix`, and the per-host files listed in the `configs`
|
||||||
|
list in `flake.nix`. That evaluation produces a top-level config (`self.nixfiles`) from which the
|
||||||
|
real flake outputs are derived:
|
||||||
|
- `nixos.systems.<name>` → `nixosConfigurations.<name>`
|
||||||
|
- `home-manager.homes.<name>` → `homeConfigurations.<name>`
|
||||||
|
- `nixos.modules` / `home-manager.modules` → `nixosModules` / `homeModules`
|
||||||
|
- `deploy-rs.rendered` → `deploy`
|
||||||
|
|
||||||
|
`nixos/default.nix` and `home-manager/default.nix` define the `systemOpts` / `homeOpts` submodules
|
||||||
|
and the `mkSystem` / `mkHome` functions that actually invoke `eval-config.nix` /
|
||||||
|
`homeManagerConfiguration`. **To add a new host:** create a box file that sets
|
||||||
|
`nixos.systems.<name> = { ... }`, then add its path to the `configs` list in `flake.nix`.
|
||||||
|
|
||||||
|
### Multiple nixpkgs channels
|
||||||
|
Four pkgs sets are threaded everywhere as `pkgsFlakes` / `pkgs'` (and `hmFlakes` for home-manager):
|
||||||
|
`unstable`, `stable`, `mine` (a personal nixpkgs fork), `mine-stable`. Each system/home picks its
|
||||||
|
channel via `nixpkgs` / `home-manager` / `hmNixpkgs` options (e.g. `nixpkgs = "mine-stable"`).
|
||||||
|
Modules receive `pkgs'` = an attrset of all channels for the current system.
|
||||||
|
|
||||||
|
### `lib.my` and the `my` option namespace
|
||||||
|
`lib/default.nix` extends `lib` with a `my` attrset (helpers like `mkOpt'`, `mkBoolOpt'`,
|
||||||
|
`mkDefault'`, `inlineModule'`, `mkDefaultSystemsPkgs`, `homeStateVersion`). It also pulls in:
|
||||||
|
- `lib.my.net` — network/CIDR helpers from the `libnetRepo` input. Used heavily for IP math.
|
||||||
|
- `lib.my.c` — shared constants from `lib/constants.nix` (UIDs/GIDs, kernel package selection,
|
||||||
|
nginx snippets, per-network domains/prefixes, etc.). Reuse these rather than hardcoding.
|
||||||
|
- `lib.my.dns` — DNS helpers (`lib/dns.nix`).
|
||||||
|
|
||||||
|
Custom modules add options under the `my.*` namespace (e.g. `my.secrets`, `my.build`,
|
||||||
|
`my.tmproot`, `my.server`). Use `mkOpt'`/`mkBoolOpt'` for option declarations to match style.
|
||||||
|
|
||||||
|
### Modules and module lists
|
||||||
|
Module sets are registered in `nixos/modules/_list.nix` and `home-manager/modules/_list.nix`
|
||||||
|
(name → path), which become `nixos.modules` / `home-manager.modules` and are applied to every
|
||||||
|
system/home. To add a shared module, drop the file in `nixos/modules/` (or `home-manager/modules/`)
|
||||||
|
and add an entry to the relevant `_list.nix`.
|
||||||
|
|
||||||
|
### Network assignments
|
||||||
|
Each system declares `assignments.<name>` (in its `nixos.systems.<host>` block) with IPv4/IPv6
|
||||||
|
addresses, gateways, domains, MTU, etc. These are aggregated into `allAssignments` (passed to every
|
||||||
|
module) and there is an assertion that fails on duplicate IPs. Host networking
|
||||||
|
(`networking.hostName`, `domain`) defaults from the `internal` assignment.
|
||||||
|
|
||||||
|
### Hosts / "boxes"
|
||||||
|
Per-host configs live under `nixos/boxes/<host>` (some are single `.nix` files, some directories
|
||||||
|
with nested VMs/containers under e.g. `colony/vms`). Many "systems" are VMs or containers managed
|
||||||
|
via the `vms` / `containers` modules and the `l2mesh` VXLAN module.
|
||||||
|
|
||||||
|
### Home routers (`nixos/boxes/home/routing-common`)
|
||||||
|
The two home routers, `river` and `stream`, share `routing-common`, which is a **function of an
|
||||||
|
`index`** (`import ../../routing-common 0` for river, `1` for stream). The index derives per-box
|
||||||
|
addresses, keepalived VRRP priorities/state, DNS `ns` numbering, etc., so the two boxes are an
|
||||||
|
active/backup HA pair from one definition. They differ where hardware/uplink differ: `stream` has a
|
||||||
|
DHCP WAN, `river` runs PPPoE (`services.pppd`, Digiweb) — box-specific bits live in the respective
|
||||||
|
box file, not `routing-common`.
|
||||||
|
|
||||||
|
- **HA is VRRP (`keepalived`).** Per-VLAN floating **VIPs** (`lib.my.c.home.vips`) are what clients
|
||||||
|
use as both gateway *and* DNS server. `kea` (DHCP) and `radvd` (RAs; started only on the master)
|
||||||
|
hand out the VIP, and `pdns-recursor` binds the VIPs (with `net.ipv*.ip_nonlocal_bind` so the
|
||||||
|
backup can pre-bind). Point client-facing services at the VIP, not a box's real address, so
|
||||||
|
failover follows the master instead of relying on client resolver timeouts.
|
||||||
|
- **`wan-online.target`** is a shared abstract target meaning "the public WAN/IPv4 route is up".
|
||||||
|
`routing-common` only declares it; each box wires *how it is reached* (`stream`: a oneshot that
|
||||||
|
waits for the DHCP default route; `river`: the pppd `ip-up`/`ip-down` hooks). Services that need
|
||||||
|
the WAN attach **to** it via `wantedBy` + `partOf` + `after` (not `requires`/`wants`), so an empty
|
||||||
|
target is never pulled in and prematurely activated, and they re-load on WAN flap.
|
||||||
|
- networkd helpers used heavily here: `lib.my.networkdAssignment` and `lib.my.mkVLAN` live under
|
||||||
|
**`lib.my`**, while networkd snippet constants like `networkd.noL3` live under **`lib.my.c`** —
|
||||||
|
easy to mix up. Set an interface MTU via the `.network`'s `linkConfig.MTUBytes` (`[Link]`), not
|
||||||
|
`netdevConfig` (`[NetDev]` rejects `MTUBytes`).
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
age-encrypted secrets in `secrets/`, managed with **ragenix**. Each module declares
|
||||||
|
`my.secrets.files.<name>` and `my.secrets.key` (the host pubkey to encrypt for). `secrets.nix`
|
||||||
|
(the ragenix rules file) is generated by reading every system's declared secrets and computing the
|
||||||
|
recipient key list (always including `.keys/dev.pub`). Edit secrets with the `ragenix` devshell
|
||||||
|
command, which supplies `.keys/dev.key` as the identity. The `.keys/` directory (dev + deploy
|
||||||
|
private keys) is required for editing secrets, deploying, and running dev VMs.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- Format with `nixpkgs-fmt` (`fmt`). 2-space indent, `inherit (...)` blocks at the top of `let`.
|
||||||
|
**Ask before running `fmt`** — some files aren't canonically formatted, so `fmt` can reindent a
|
||||||
|
whole file and bury a logical change in whitespace churn. Match the surrounding style by hand and
|
||||||
|
leave formatting to the user unless they ask.
|
||||||
|
- Prefer `lib.my` helpers (`mkOpt'`, `mkBoolOpt'`, `mkDefault'`) and `lib.my.c` constants over
|
||||||
|
reimplementing.
|
||||||
|
- New shared functionality → a module in `*/modules/` + entry in `_list.nix`, options under `my.*`.
|
||||||
|
- New host → box file under `nixos/boxes/` + entry in the `configs` list in `flake.nix`.
|
||||||
|
- Custom packages live in `pkgs/` and are registered in `pkgs/default.nix`; the overlay is exposed
|
||||||
|
as `overlays.default`.
|
||||||
|
- In prose and commit messages, quote code-like identifiers (commands, options, paths, package and
|
||||||
|
attribute names) in backticks.
|
||||||
|
- Call the machines **"boxes"**, never "fleet".
|
||||||
|
- Commit subjects follow `area/scope: Capitalized summary` (e.g. `nixos/home: ...`); keep logically
|
||||||
|
distinct changes in separate commits.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
object-ctr.ams1.int.nul.ie ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFdHbZErWLmTPO/aEWB1Fup/aGMf31Un5Wk66FJwTz/8
|
||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REMOTE_STORE=/var/lib/harmonia
|
||||||
|
SSH_HOST="harmonia@object-ctr.ams1.int.nul.ie"
|
||||||
|
SSH_KEY=/tmp/harmonia.key
|
||||||
|
STORE_URI="ssh-ng://$SSH_HOST?ssh-key=$SSH_KEY&remote-store=$REMOTE_STORE"
|
||||||
|
|
||||||
|
remote_cmd() {
|
||||||
|
ssh -i "$SSH_KEY" "$SSH_HOST" env HOME=/run/harmonia NIX_REMOTE="$REMOTE_STORE" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
umask_old=$(umask)
|
||||||
|
umask 0066
|
||||||
|
echo "$HARMONIA_SSH_KEY" | base64 -d > "$SSH_KEY"
|
||||||
|
umask $umask_old
|
||||||
|
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
cp ci/known_hosts ~/.ssh/
|
||||||
|
path="$1"
|
||||||
|
|
||||||
|
echo "Pushing $path to cache..."
|
||||||
|
nix copy --no-check-sigs --to "$STORE_URI" "$path"
|
||||||
|
|
||||||
|
if [ -n "$UPDATE_PROFILE" ]; then
|
||||||
|
echo "Updating profile..."
|
||||||
|
remote_cmd nix-env -p "$REMOTE_STORE"/nix/var/nix/profiles/nixfiles --set "$path"
|
||||||
|
|
||||||
|
echo "Collecting garbage..."
|
||||||
|
remote_cmd nix-collect-garbage --delete-older-than 60d
|
||||||
|
fi
|
||||||
+51
-19
@@ -20,7 +20,7 @@ in
|
|||||||
[ -e "${homeFlake}" ] && echo "${homeFlake} already exists" && exit 1
|
[ -e "${homeFlake}" ] && echo "${homeFlake} already exists" && exit 1
|
||||||
|
|
||||||
mkdir -p "$(dirname "${homeFlake}")"
|
mkdir -p "$(dirname "${homeFlake}")"
|
||||||
ln -s "$(pwd)/flake.nix" "${homeFlake}"
|
ln -sf "$(pwd)/flake.nix" "${homeFlake}"
|
||||||
echo "Installed link to $(pwd)/flake.nix at ${homeFlake}"
|
echo "Installed link to $(pwd)/flake.nix at ${homeFlake}"
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
@@ -48,11 +48,30 @@ in
|
|||||||
help = "Print the ed25519 pubkey for a host";
|
help = "Print the ed25519 pubkey for a host";
|
||||||
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
|
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "ssh-machine";
|
||||||
|
category = "utilities";
|
||||||
|
help = "SSH to a machine by NixOS system or home-manager config name";
|
||||||
|
command = ''
|
||||||
|
name="$1"
|
||||||
|
shift
|
||||||
|
# Run from the project root so deploy's relative identity path (.keys/deploy.key) resolves
|
||||||
|
cd "$PRJ_ROOT"
|
||||||
|
# deploy-rs node names are the system/home name, with `@` mangled to `-at-`
|
||||||
|
node="''${name//@/-at-}"
|
||||||
|
# Single eval: resolve `user@host` plus merged (global + node) deploy-rs ssh options
|
||||||
|
info="$(nix eval --raw .#deploy --apply 'd: let n = d.nodes."'"$node"'"; in "''${n.sshUser}@''${n.hostname} ''${builtins.concatStringsSep " " (d.sshOpts ++ n.sshOpts)}"')"
|
||||||
|
target="''${info%% *}"
|
||||||
|
opts="''${info#* }"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
exec ${pkgs.openssh}/bin/ssh $opts "$target" "$@"
|
||||||
|
'';
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "json2nix";
|
name = "json2nix";
|
||||||
category = "utilities";
|
category = "utilities";
|
||||||
help = "Convert JSON to formatted Nix";
|
help = "Convert JSON to formatted Nix";
|
||||||
command = "nix eval --impure --expr 'builtins.fromJSON (builtins.readFile /dev/stdin)' | ${pkgs.nixfmt}/bin/nixfmt";
|
command = "nix eval --impure --expr 'builtins.fromJSON (builtins.readFile /dev/stdin)' | ${pkgs.nixfmt}/bin/nixfmt -";
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -73,11 +92,22 @@ in
|
|||||||
help = "Build NixOS configuration";
|
help = "Build NixOS configuration";
|
||||||
command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel"'';
|
command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel"'';
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "check-system";
|
||||||
|
category = "utilities";
|
||||||
|
help = "Evaluate NixOS configuration (check validity without building)";
|
||||||
|
command = ''nix eval "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel.drvPath"'';
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "build-n-switch";
|
name = "build-n-switch";
|
||||||
category = "tasks";
|
category = "tasks";
|
||||||
help = "Shortcut to nixos-rebuild for this flake";
|
help = "Shortcut to nixos-rebuild for this flake";
|
||||||
command = ''doas nixos-rebuild --flake . "$@"'';
|
command = ''
|
||||||
|
# HACK: Upstream changes in Git + Nix makes this necessary
|
||||||
|
# https://github.com/NixOS/nix/issues/10202
|
||||||
|
doas git config --global --add safe.directory "$PWD"
|
||||||
|
doas nixos-rebuild --flake . "$@"
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "run-vm";
|
name = "run-vm";
|
||||||
@@ -106,8 +136,8 @@ in
|
|||||||
{
|
{
|
||||||
name = "build-netboot";
|
name = "build-netboot";
|
||||||
category = "tasks";
|
category = "tasks";
|
||||||
help = "Build NixOS configuration as netboot archive";
|
help = "Build NixOS configuration as netboot tree";
|
||||||
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.netbootArchive"'';
|
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.netbootTree"'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "build-home";
|
name = "build-home";
|
||||||
@@ -115,29 +145,17 @@ in
|
|||||||
help = "Build home-manager configuration";
|
help = "Build home-manager configuration";
|
||||||
command = ''nix build "''${@:2}" ".#homeConfigurations.\"$1\".activationPackage"'';
|
command = ''nix build "''${@:2}" ".#homeConfigurations.\"$1\".activationPackage"'';
|
||||||
}
|
}
|
||||||
{
|
|
||||||
name = "update-inputs";
|
|
||||||
category = "tasks";
|
|
||||||
help = "Update flake inputs";
|
|
||||||
command = ''
|
|
||||||
args=()
|
|
||||||
for f in "$@"; do
|
|
||||||
args+=(--update-input "$f")
|
|
||||||
done
|
|
||||||
nix flake lock "''${args[@]}"
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
name = "update-nixpkgs";
|
name = "update-nixpkgs";
|
||||||
category = "tasks";
|
category = "tasks";
|
||||||
help = "Update nixpkgs flake inputs";
|
help = "Update nixpkgs flake inputs";
|
||||||
command = ''update-inputs nixpkgs-{unstable,stable,mine,mine-stable}'';
|
command = ''nix flake update nixpkgs-{unstable,stable,mine,mine-stable}'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "update-home-manager";
|
name = "update-home-manager";
|
||||||
category = "tasks";
|
category = "tasks";
|
||||||
help = "Update home-manager flake inputs";
|
help = "Update home-manager flake inputs";
|
||||||
command = ''update-inputs home-manager-{unstable,stable}'';
|
command = ''nix flake update home-manager-{unstable,stable}'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "update-installer";
|
name = "update-installer";
|
||||||
@@ -145,5 +163,19 @@ in
|
|||||||
help = "Update installer tag (to trigger new release)";
|
help = "Update installer tag (to trigger new release)";
|
||||||
command = ''git tag -f installer && git push -f origin installer'';
|
command = ''git tag -f installer && git push -f origin installer'';
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "deploy-multi";
|
||||||
|
category = "tasks";
|
||||||
|
help = "Deploy multiple flakes at once";
|
||||||
|
command = ''
|
||||||
|
for f in $@; do
|
||||||
|
if [ -n "''${O:-}" ]; then
|
||||||
|
deploy "$O" $f
|
||||||
|
else
|
||||||
|
deploy $f
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ in
|
|||||||
|
|
||||||
NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf"
|
NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf"
|
||||||
''
|
''
|
||||||
experimental-features = nix-command flakes ca-derivations repl-flake
|
experimental-features = nix-command flakes ca-derivations
|
||||||
connect-timeout = 5
|
connect-timeout = 5
|
||||||
fallback = true
|
fallback = true
|
||||||
${lib.my.c.nix.cache.conf}
|
${lib.my.c.nix.cache.conf}
|
||||||
@@ -24,10 +24,10 @@ in
|
|||||||
coreutils
|
coreutils
|
||||||
nixVersions.stable
|
nixVersions.stable
|
||||||
rage
|
rage
|
||||||
|
wireguard-tools
|
||||||
(pkgs.writeShellScriptBin "deploy" ''
|
(pkgs.writeShellScriptBin "deploy" ''
|
||||||
exec ${deploy-rs.deploy-rs}/bin/deploy --skip-checks "$@"
|
exec ${deploy-rs.deploy-rs}/bin/deploy --skip-checks "$@"
|
||||||
'')
|
'')
|
||||||
home-manager
|
home-manager
|
||||||
attic-client
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+579
-322
File diff suppressed because it is too large
Load Diff
@@ -3,40 +3,52 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
# libnet.url = "github:reo101/nix-lib-net";
|
||||||
|
libnetRepo = {
|
||||||
|
url = "github:oddlama/nixos-extra-modules";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
devshell.url = "github:numtide/devshell";
|
devshell.url = "github:numtide/devshell";
|
||||||
devshell.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
devshell.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
|
||||||
nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
|
nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
|
||||||
nixpkgs-stable.url = "nixpkgs/nixos-23.11";
|
nixpkgs-stable.url = "nixpkgs/nixos-26.05";
|
||||||
nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0";
|
nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0";
|
||||||
nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable";
|
nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable";
|
||||||
|
|
||||||
home-manager-unstable.url = "home-manager";
|
home-manager-unstable.url = "home-manager";
|
||||||
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
home-manager-stable.url = "home-manager/release-23.11";
|
home-manager-stable.url = "home-manager/release-26.05";
|
||||||
home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
|
home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
|
||||||
|
|
||||||
# Stuff used by the flake for build / deployment
|
# Stuff used by the flake for build / deployment
|
||||||
ragenix.url = "github:yaxitech/ragenix";
|
# ragenix.url = "github:yaxitech/ragenix";
|
||||||
|
ragenix.url = "github:devplayer0/ragenix/add-rekey-one-flag";
|
||||||
ragenix.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
ragenix.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
deploy-rs.url = "github:serokell/deploy-rs";
|
deploy-rs.url = "github:serokell/deploy-rs";
|
||||||
deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
|
||||||
# Stuff used by systems
|
# Stuff used by systems
|
||||||
impermanence.url = "github:nix-community/impermanence";
|
impermanence.url = "github:nix-community/impermanence";
|
||||||
|
impermanence.inputs.home-manager.follows = "home-manager-unstable";
|
||||||
boardie.url = "github:devplayer0/boardie";
|
boardie.url = "github:devplayer0/boardie";
|
||||||
boardie.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
boardie.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
nixGL.url = "github:nix-community/nixGL";
|
nixGL.url = "github:nix-community/nixGL";
|
||||||
nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
harmonia.url = "github:nix-community/harmonia";
|
||||||
|
# harmonia.url = "github:devplayer0/harmonia/cache-config-daemon-store";
|
||||||
|
harmonia.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
|
||||||
# Packages not in nixpkgs
|
# Packages not in nixpkgs
|
||||||
sharry.url = "github:eikek/sharry";
|
sharry.url = "github:eikek/sharry";
|
||||||
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
borgthin.url = "github:devplayer0/borg";
|
borgthin.url = "github:devplayer0/borg";
|
||||||
borgthin.inputs.nixpkgs.follows = "nixpkgs-mine";
|
# TODO: Update borgthin so this works
|
||||||
attic.url = "github:zhaofengli/attic";
|
# borgthin.inputs.nixpkgs.follows = "nixpkgs-mine";
|
||||||
attic.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
copyparty.url = "github:9001/copyparty";
|
||||||
attic.inputs.nixpkgs-stable.follows = "nixpkgs-stable";
|
copyparty.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
hass-west-wood.url = "github:devplayer0/hass-west-wood";
|
||||||
|
hass-west-wood.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
@@ -51,7 +63,7 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
inherit (builtins) mapAttrs replaceStrings;
|
inherit (builtins) mapAttrs replaceStrings elem;
|
||||||
inherit (lib) mapAttrs' filterAttrs nameValuePair recurseIntoAttrs evalModules;
|
inherit (lib) mapAttrs' filterAttrs nameValuePair recurseIntoAttrs evalModules;
|
||||||
inherit (lib.flake) flattenTree eachDefaultSystem;
|
inherit (lib.flake) flattenTree eachDefaultSystem;
|
||||||
inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay;
|
inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay;
|
||||||
@@ -59,7 +71,7 @@
|
|||||||
# Extend a lib with extras that _must not_ internally reference private nixpkgs. flake-utils doesn't, but many
|
# Extend a lib with extras that _must not_ internally reference private nixpkgs. flake-utils doesn't, but many
|
||||||
# other flakes (e.g. home-manager) probably do internally.
|
# other flakes (e.g. home-manager) probably do internally.
|
||||||
libOverlay = final: prev: {
|
libOverlay = final: prev: {
|
||||||
my = import ./lib { lib = final; };
|
my = import ./lib { inherit inputs; lib = final; };
|
||||||
flake = flake-utils.lib;
|
flake = flake-utils.lib;
|
||||||
};
|
};
|
||||||
pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; };
|
pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; };
|
||||||
@@ -90,12 +102,12 @@
|
|||||||
(_: path: mkDefaultSystemsPkgs path (system: {
|
(_: path: mkDefaultSystemsPkgs path (system: {
|
||||||
overlays = [
|
overlays = [
|
||||||
pkgsLibOverlay
|
pkgsLibOverlay
|
||||||
|
|
||||||
myPkgsOverlay
|
myPkgsOverlay
|
||||||
inputs.devshell.overlays.default
|
inputs.devshell.overlays.default
|
||||||
inputs.ragenix.overlays.default
|
inputs.ragenix.overlays.default
|
||||||
inputs.deploy-rs.overlay
|
inputs.deploy-rs.overlays.default
|
||||||
(flakePackageOverlay inputs.home-manager-unstable system)
|
(flakePackageOverlay inputs.home-manager-unstable system)
|
||||||
inputs.attic.overlays.default
|
|
||||||
];
|
];
|
||||||
}))
|
}))
|
||||||
pkgsFlakes;
|
pkgsFlakes;
|
||||||
@@ -105,8 +117,19 @@
|
|||||||
(_: path: mkDefaultSystemsPkgs path (_: {
|
(_: path: mkDefaultSystemsPkgs path (_: {
|
||||||
overlays = [
|
overlays = [
|
||||||
pkgsLibOverlay
|
pkgsLibOverlay
|
||||||
|
|
||||||
myPkgsOverlay
|
myPkgsOverlay
|
||||||
];
|
];
|
||||||
|
|
||||||
|
config = {
|
||||||
|
# RMS forgive me...
|
||||||
|
# Normally this is set modularly, but sometimes we need to use other pkgs
|
||||||
|
allowUnfreePredicate = p: elem (lib.getName p) [
|
||||||
|
"widevine-cdm"
|
||||||
|
"chromium-unwrapped"
|
||||||
|
"chromium"
|
||||||
|
];
|
||||||
|
};
|
||||||
}))
|
}))
|
||||||
pkgsFlakes;
|
pkgsFlakes;
|
||||||
|
|
||||||
@@ -115,10 +138,11 @@
|
|||||||
nixos/installer.nix
|
nixos/installer.nix
|
||||||
nixos/boxes/colony
|
nixos/boxes/colony
|
||||||
nixos/boxes/tower
|
nixos/boxes/tower
|
||||||
nixos/boxes/castle
|
|
||||||
nixos/boxes/home/stream.nix
|
nixos/boxes/home/stream.nix
|
||||||
nixos/boxes/home/palace
|
nixos/boxes/home/palace
|
||||||
|
nixos/boxes/home/castle
|
||||||
nixos/boxes/britway
|
nixos/boxes/britway
|
||||||
|
nixos/boxes/britnet.nix
|
||||||
nixos/boxes/kelder
|
nixos/boxes/kelder
|
||||||
|
|
||||||
# Homes
|
# Homes
|
||||||
@@ -149,7 +173,7 @@
|
|||||||
# Platform independent stuff
|
# Platform independent stuff
|
||||||
{
|
{
|
||||||
nixpkgs = pkgs';
|
nixpkgs = pkgs';
|
||||||
inherit lib nixfiles;
|
inherit inputs lib nixfiles;
|
||||||
|
|
||||||
overlays.default = myPkgsOverlay;
|
overlays.default = myPkgsOverlay;
|
||||||
|
|
||||||
@@ -197,8 +221,9 @@
|
|||||||
systems' = mapAttrs' (n: v: nameValuePair "system-${n}" v) systems;
|
systems' = mapAttrs' (n: v: nameValuePair "system-${n}" v) systems;
|
||||||
packages' = mapAttrs' (n: v: nameValuePair "package-${n}" v) packages;
|
packages' = mapAttrs' (n: v: nameValuePair "package-${n}" v) packages;
|
||||||
in
|
in
|
||||||
pkgs.linkFarm "ci" (homes' // systems' // packages' // {
|
homes' // systems' // packages' // {
|
||||||
inherit shell;
|
inherit shell;
|
||||||
});
|
};
|
||||||
|
ciDrv = pkgs.linkFarm "ci" ci;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ in
|
|||||||
|
|
||||||
lsd = {
|
lsd = {
|
||||||
enable = mkDefault true;
|
enable = mkDefault true;
|
||||||
enableAliases = mkDefault true;
|
enableFishIntegration = mkDefault true;
|
||||||
};
|
};
|
||||||
|
|
||||||
starship = {
|
starship = {
|
||||||
@@ -132,38 +132,38 @@ in
|
|||||||
|
|
||||||
ssh = {
|
ssh = {
|
||||||
enable = mkDefault true;
|
enable = mkDefault true;
|
||||||
matchBlocks = {
|
enableDefaultConfig = false;
|
||||||
|
settings = {
|
||||||
nix-dev-vm = {
|
nix-dev-vm = {
|
||||||
user = "dev";
|
User = "dev";
|
||||||
hostname = "localhost";
|
HostName = "localhost";
|
||||||
port = 2222;
|
Port = 2222;
|
||||||
extraOptions = {
|
StrictHostKeyChecking = "no";
|
||||||
StrictHostKeyChecking = "no";
|
UserKnownHostsFile = "/dev/null";
|
||||||
UserKnownHostsFile = "/dev/null";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
"rsync.net" = {
|
"rsync.net" = {
|
||||||
host = "rsyncnet";
|
Host = "rsyncnet";
|
||||||
user = "16413";
|
User = "16413";
|
||||||
hostname = "ch-s010.rsync";
|
HostName = "ch-s010.rsync";
|
||||||
};
|
};
|
||||||
|
|
||||||
shoe = {
|
shoe = {
|
||||||
host = "shoe.netsoc.tcd.ie shoe";
|
Host = "shoe.netsoc.tcd.ie shoe";
|
||||||
user = "netsoc";
|
User = "netsoc";
|
||||||
};
|
};
|
||||||
netsocBoxes = {
|
netsocBoxes = {
|
||||||
host = "cube spoon napalm gandalf saruman";
|
Host = "cube spoon napalm gandalf saruman";
|
||||||
user = "root";
|
User = "root";
|
||||||
|
};
|
||||||
|
|
||||||
|
"*" = {
|
||||||
|
IdentityFile = [
|
||||||
|
"~/.ssh/id_rsa"
|
||||||
|
"~/.ssh/borg"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
extraConfig =
|
|
||||||
''
|
|
||||||
IdentityFile ~/.ssh/id_rsa
|
|
||||||
IdentityFile ~/.ssh/netsoc
|
|
||||||
IdentityFile ~/.ssh/borg
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
direnv = {
|
direnv = {
|
||||||
@@ -199,17 +199,20 @@ in
|
|||||||
file
|
file
|
||||||
tree
|
tree
|
||||||
pwgen
|
pwgen
|
||||||
|
|
||||||
|
minicom
|
||||||
iperf3
|
iperf3
|
||||||
mosh
|
mosh
|
||||||
wget
|
wget
|
||||||
hyx
|
|
||||||
whois
|
whois
|
||||||
ldns
|
ldns
|
||||||
minicom
|
|
||||||
mtr
|
mtr
|
||||||
|
|
||||||
|
hyx
|
||||||
ncdu
|
ncdu
|
||||||
jq
|
jq
|
||||||
yq-go
|
yq-go
|
||||||
|
nix-tree
|
||||||
];
|
];
|
||||||
|
|
||||||
sessionVariables = {
|
sessionVariables = {
|
||||||
@@ -223,7 +226,7 @@ in
|
|||||||
# Note: If globalPkgs mode is on, then these will be overridden by the NixOS equivalents of these options
|
# Note: If globalPkgs mode is on, then these will be overridden by the NixOS equivalents of these options
|
||||||
nixpkgs = {
|
nixpkgs = {
|
||||||
overlays = [
|
overlays = [
|
||||||
inputs.deploy-rs.overlay
|
inputs.deploy-rs.overlays.default
|
||||||
inputs.boardie.overlays.default
|
inputs.boardie.overlays.default
|
||||||
inputs.nixGL.overlays.default
|
inputs.nixGL.overlays.default
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# XTerm's default colors
|
||||||
|
|
||||||
|
# Default colors
|
||||||
|
[colors.primary]
|
||||||
|
background = '#000000'
|
||||||
|
foreground = '#ffffff'
|
||||||
|
|
||||||
|
# Normal colors
|
||||||
|
[colors.normal]
|
||||||
|
black = '#000000'
|
||||||
|
red = '#cd0000'
|
||||||
|
green = '#00cd00'
|
||||||
|
yellow = '#cdcd00'
|
||||||
|
blue = '#0000ee'
|
||||||
|
magenta = '#cd00cd'
|
||||||
|
cyan = '#00cdcd'
|
||||||
|
white = '#e5e5e5'
|
||||||
|
|
||||||
|
# Bright colors
|
||||||
|
[colors.bright]
|
||||||
|
black = '#7f7f7f'
|
||||||
|
red = '#ff0000'
|
||||||
|
green = '#00ff00'
|
||||||
|
yellow = '#ffff00'
|
||||||
|
blue = '#5c5cff'
|
||||||
|
magenta = '#ff00ff'
|
||||||
|
cyan = '#00ffff'
|
||||||
|
white = '#ffffff'
|
||||||
+520
@@ -0,0 +1,520 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Source: https://github.com/daniel3303/ClaudeCodeStatusLine
|
||||||
|
# Single line: Model | tokens | %used | %remain | think | 5h bar @reset | 7d bar @reset | extra
|
||||||
|
|
||||||
|
set -f # disable globbing
|
||||||
|
VERSION="1.4.4"
|
||||||
|
|
||||||
|
input=$(cat)
|
||||||
|
|
||||||
|
if [ -z "$input" ]; then
|
||||||
|
printf "Claude"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ANSI colors matching oh-my-posh theme
|
||||||
|
blue='\033[38;2;0;153;255m'
|
||||||
|
orange='\033[38;2;255;176;85m'
|
||||||
|
green='\033[38;2;0;160;0m'
|
||||||
|
cyan='\033[38;2;46;149;153m'
|
||||||
|
red='\033[38;2;255;85;85m'
|
||||||
|
yellow='\033[38;2;230;200;0m'
|
||||||
|
purple='\033[38;2;167;139;250m'
|
||||||
|
white='\033[38;2;220;220;220m'
|
||||||
|
dim='\033[2m'
|
||||||
|
reset='\033[0m'
|
||||||
|
|
||||||
|
# Format token counts (e.g., 50k / 200k)
|
||||||
|
format_tokens() {
|
||||||
|
local num=$1
|
||||||
|
if [ "$num" -ge 1000000 ]; then
|
||||||
|
awk "BEGIN {v=sprintf(\"%.1f\",$num/1000000)+0; if(v==int(v)) printf \"%dm\",v; else printf \"%.1fm\",v}"
|
||||||
|
elif [ "$num" -ge 1000 ]; then
|
||||||
|
awk "BEGIN {printf \"%.0fk\", $num / 1000}"
|
||||||
|
else
|
||||||
|
printf "%d" "$num"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Format number with commas (e.g., 134,938)
|
||||||
|
format_commas() {
|
||||||
|
printf "%'d" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return color escape based on usage percentage
|
||||||
|
# Usage: usage_color <pct>
|
||||||
|
usage_color() {
|
||||||
|
local pct=$1
|
||||||
|
if [ "$pct" -ge 90 ]; then echo "$red"
|
||||||
|
elif [ "$pct" -ge 70 ]; then echo "$orange"
|
||||||
|
elif [ "$pct" -ge 50 ]; then echo "$yellow"
|
||||||
|
else echo "$green"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve config directory: CLAUDE_CONFIG_DIR (set by alias) or default ~/.claude
|
||||||
|
claude_config_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
||||||
|
|
||||||
|
# Return 0 (true) if $1 > $2 using semantic versioning
|
||||||
|
version_gt() {
|
||||||
|
local a="${1#v}" b="${2#v}"
|
||||||
|
local IFS='.'
|
||||||
|
read -r a1 a2 a3 <<< "$a"
|
||||||
|
read -r b1 b2 b3 <<< "$b"
|
||||||
|
a1=${a1:-0}; a2=${a2:-0}; a3=${a3:-0}
|
||||||
|
b1=${b1:-0}; b2=${b2:-0}; b3=${b3:-0}
|
||||||
|
[ "$a1" -gt "$b1" ] 2>/dev/null && return 0
|
||||||
|
[ "$a1" -lt "$b1" ] 2>/dev/null && return 1
|
||||||
|
[ "$a2" -gt "$b2" ] 2>/dev/null && return 0
|
||||||
|
[ "$a2" -lt "$b2" ] 2>/dev/null && return 1
|
||||||
|
[ "$a3" -gt "$b3" ] 2>/dev/null && return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
# ===== Extract data from JSON =====
|
||||||
|
model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"')
|
||||||
|
model_name=$(echo "$model_name" | sed 's/ *(\([0-9.]*[kKmM]*\) context)/ \1/') # "(1M context)" → "1M"
|
||||||
|
|
||||||
|
# Context window
|
||||||
|
size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
|
||||||
|
[ "$size" -eq 0 ] 2>/dev/null && size=200000
|
||||||
|
|
||||||
|
# Token usage
|
||||||
|
input_tokens=$(echo "$input" | jq -r '.context_window.current_usage.input_tokens // 0')
|
||||||
|
cache_create=$(echo "$input" | jq -r '.context_window.current_usage.cache_creation_input_tokens // 0')
|
||||||
|
cache_read=$(echo "$input" | jq -r '.context_window.current_usage.cache_read_input_tokens // 0')
|
||||||
|
current=$(( input_tokens + cache_create + cache_read ))
|
||||||
|
|
||||||
|
used_tokens=$(format_tokens $current)
|
||||||
|
total_tokens=$(format_tokens $size)
|
||||||
|
|
||||||
|
if [ "$size" -gt 0 ]; then
|
||||||
|
pct_used=$(( current * 100 / size ))
|
||||||
|
else
|
||||||
|
pct_used=0
|
||||||
|
fi
|
||||||
|
pct_remain=$(( 100 - pct_used ))
|
||||||
|
|
||||||
|
used_comma=$(format_commas $current)
|
||||||
|
remain_comma=$(format_commas $(( size - current )))
|
||||||
|
|
||||||
|
settings_path="$claude_config_dir/settings.json"
|
||||||
|
effort_level=""
|
||||||
|
stdin_effort=$(echo "$input" | jq -r '.effort.level // empty' 2>/dev/null)
|
||||||
|
if [ -n "$stdin_effort" ]; then
|
||||||
|
effort_level="$stdin_effort"
|
||||||
|
elif [ -n "$CLAUDE_CODE_EFFORT_LEVEL" ]; then
|
||||||
|
effort_level="$CLAUDE_CODE_EFFORT_LEVEL"
|
||||||
|
elif [ -f "$settings_path" ]; then
|
||||||
|
effort_val=$(jq -r '.effortLevel // empty' "$settings_path" 2>/dev/null)
|
||||||
|
[ -n "$effort_val" ] && effort_level="$effort_val"
|
||||||
|
fi
|
||||||
|
[ -z "$effort_level" ] && effort_level="medium"
|
||||||
|
|
||||||
|
# ===== Claude CLI version (cached, 1h TTL) =====
|
||||||
|
cli_version_cache="/tmp/claude/statusline-cli-version"
|
||||||
|
cli_version=""
|
||||||
|
cli_version_max_age=3600
|
||||||
|
|
||||||
|
if [ -f "$cli_version_cache" ]; then
|
||||||
|
cv_mtime=$(stat -c %Y "$cli_version_cache" 2>/dev/null || stat -f %m "$cli_version_cache" 2>/dev/null)
|
||||||
|
cv_now=$(date +%s)
|
||||||
|
cv_age=$(( cv_now - cv_mtime ))
|
||||||
|
if [ "$cv_age" -lt "$cli_version_max_age" ]; then
|
||||||
|
cli_version=$(cat "$cli_version_cache" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$cli_version" ]; then
|
||||||
|
cli_version=$(claude --version 2>/dev/null | awk '{print $1}')
|
||||||
|
if [ -n "$cli_version" ]; then
|
||||||
|
mkdir -p /tmp/claude 2>/dev/null
|
||||||
|
echo "$cli_version" > "$cli_version_cache"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ===== Build single-line output =====
|
||||||
|
out=""
|
||||||
|
out+="${blue}${model_name}${reset}"
|
||||||
|
|
||||||
|
# Current working directory
|
||||||
|
cwd=$(echo "$input" | jq -r '.cwd // empty')
|
||||||
|
if [ -n "$cwd" ]; then
|
||||||
|
display_dir="${cwd##*/}"
|
||||||
|
git_branch=$(git -C "${cwd}" rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||||
|
out+=" ${dim}|${reset} "
|
||||||
|
out+="${cyan}${display_dir}${reset}"
|
||||||
|
if [ -n "$git_branch" ]; then
|
||||||
|
out+="${dim}@${reset}${green}${git_branch}${reset}"
|
||||||
|
git_stat=$(git -C "${cwd}" diff --numstat 2>/dev/null | awk '{a+=$1; d+=$2} END {if (a+d>0) printf "+%d -%d", a, d}')
|
||||||
|
[ -n "$git_stat" ] && out+=" ${dim}(${reset}${green}${git_stat%% *}${reset} ${red}${git_stat##* }${reset}${dim})${reset}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
out+=" ${dim}|${reset} "
|
||||||
|
out+="${orange}${used_tokens}/${total_tokens}${reset} ${dim}(${reset}${green}${pct_used}%${reset}${dim})${reset}"
|
||||||
|
out+=" ${dim}|${reset} "
|
||||||
|
out+="effort: "
|
||||||
|
case "$effort_level" in
|
||||||
|
low) out+="${dim}${effort_level}${reset}" ;;
|
||||||
|
medium) out+="${orange}med${reset}" ;;
|
||||||
|
high) out+="${green}${effort_level}${reset}" ;;
|
||||||
|
xhigh) out+="${purple}${effort_level}${reset}" ;;
|
||||||
|
max) out+="${red}${effort_level}${reset}" ;;
|
||||||
|
*) out+="${green}${effort_level}${reset}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ===== Cross-platform OAuth token resolution (from statusline.sh) =====
|
||||||
|
# Tries credential sources in order: env var → macOS Keychain → Linux creds file → GNOME Keyring
|
||||||
|
get_oauth_token() {
|
||||||
|
local token=""
|
||||||
|
|
||||||
|
# 1. Explicit env var override
|
||||||
|
if [ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
||||||
|
echo "$CLAUDE_CODE_OAUTH_TOKEN"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. macOS Keychain (Claude Code appends a SHA256 hash of CLAUDE_CONFIG_DIR to the service name)
|
||||||
|
if command -v security >/dev/null 2>&1; then
|
||||||
|
local keychain_svc="Claude Code-credentials"
|
||||||
|
if [ -n "$CLAUDE_CONFIG_DIR" ]; then
|
||||||
|
local dir_hash
|
||||||
|
dir_hash=$(echo -n "$CLAUDE_CONFIG_DIR" | shasum -a 256 | cut -c1-8)
|
||||||
|
keychain_svc="Claude Code-credentials-${dir_hash}"
|
||||||
|
fi
|
||||||
|
local blob
|
||||||
|
blob=$(security find-generic-password -s "$keychain_svc" -w 2>/dev/null)
|
||||||
|
if [ -n "$blob" ]; then
|
||||||
|
token=$(echo "$blob" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
|
||||||
|
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||||
|
echo "$token"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Linux credentials file
|
||||||
|
local creds_file="${claude_config_dir}/.credentials.json"
|
||||||
|
if [ -f "$creds_file" ]; then
|
||||||
|
token=$(jq -r '.claudeAiOauth.accessToken // empty' "$creds_file" 2>/dev/null)
|
||||||
|
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||||
|
echo "$token"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. GNOME Keyring via secret-tool
|
||||||
|
if command -v secret-tool >/dev/null 2>&1; then
|
||||||
|
local blob
|
||||||
|
blob=$(timeout 2 secret-tool lookup service "Claude Code-credentials" 2>/dev/null)
|
||||||
|
if [ -n "$blob" ]; then
|
||||||
|
token=$(echo "$blob" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
|
||||||
|
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||||
|
echo "$token"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== LINE 2 & 3: Usage limits with progress bars =====
|
||||||
|
# First, try to use rate_limits data provided directly by Claude Code in the JSON input.
|
||||||
|
# This is the most reliable source — no OAuth token or API call required.
|
||||||
|
builtin_five_hour_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
|
||||||
|
builtin_five_hour_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
|
||||||
|
builtin_seven_day_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
|
||||||
|
builtin_seven_day_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
|
||||||
|
|
||||||
|
use_builtin=false
|
||||||
|
if [ -n "$builtin_five_hour_pct" ] || [ -n "$builtin_seven_day_pct" ]; then
|
||||||
|
use_builtin=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cache setup — shared across all Claude Code instances to avoid rate limits
|
||||||
|
claude_config_dir_hash=$(echo -n "$claude_config_dir" | shasum -a 256 2>/dev/null || echo -n "$claude_config_dir" | sha256sum 2>/dev/null)
|
||||||
|
claude_config_dir_hash=$(echo "$claude_config_dir_hash" | cut -c1-8)
|
||||||
|
cache_file="/tmp/claude/statusline-usage-cache-${claude_config_dir_hash}.json"
|
||||||
|
cache_max_age=60 # seconds between API calls
|
||||||
|
mkdir -p /tmp/claude
|
||||||
|
|
||||||
|
needs_refresh=true
|
||||||
|
usage_data=""
|
||||||
|
|
||||||
|
# Always load cache — used as primary source for API path, and as fallback when builtin reports zero
|
||||||
|
if [ -f "$cache_file" ] && [ -s "$cache_file" ]; then
|
||||||
|
cache_mtime=$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null)
|
||||||
|
now=$(date +%s)
|
||||||
|
cache_age=$(( now - cache_mtime ))
|
||||||
|
if [ "$cache_age" -lt "$cache_max_age" ]; then
|
||||||
|
needs_refresh=false
|
||||||
|
fi
|
||||||
|
usage_data=$(cat "$cache_file" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# When builtin values are all zero AND reset timestamps are missing, it likely indicates
|
||||||
|
# an API failure on Claude's side — fall through to cached data instead of displaying
|
||||||
|
# misleading 0%. Genuine zero responses (after a billing reset) still include valid
|
||||||
|
# resets_at timestamps, so we trust those.
|
||||||
|
effective_builtin=false
|
||||||
|
if $use_builtin; then
|
||||||
|
# Trust builtin if any percentage is non-zero
|
||||||
|
if { [ -n "$builtin_five_hour_pct" ] && [ "$(printf '%.0f' "$builtin_five_hour_pct" 2>/dev/null)" != "0" ]; } || \
|
||||||
|
{ [ -n "$builtin_seven_day_pct" ] && [ "$(printf '%.0f' "$builtin_seven_day_pct" 2>/dev/null)" != "0" ]; }; then
|
||||||
|
effective_builtin=true
|
||||||
|
fi
|
||||||
|
# Also trust if reset timestamps are present — genuine zero responses include valid reset times
|
||||||
|
if ! $effective_builtin; then
|
||||||
|
if { [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ] && [ "$builtin_five_hour_reset" != "0" ]; } || \
|
||||||
|
{ [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ] && [ "$builtin_seven_day_reset" != "0" ]; }; then
|
||||||
|
effective_builtin=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Refresh API cache when stale — runs regardless of builtin rate_limits because
|
||||||
|
# extra_usage is only exposed through the OAuth usage endpoint (not stdin JSON).
|
||||||
|
# Throttled to cache_max_age and stampede-locked via touch for shared panes.
|
||||||
|
if $needs_refresh; then
|
||||||
|
touch "$cache_file" # stampede lock: prevent parallel panes from fetching simultaneously
|
||||||
|
token=$(get_oauth_token)
|
||||||
|
if [ -n "$token" ] && [ "$token" != "null" ]; then
|
||||||
|
response=$(curl -s --max-time 10 \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $token" \
|
||||||
|
-H "anthropic-beta: oauth-2025-04-20" \
|
||||||
|
-H "User-Agent: claude-code/2.1.34" \
|
||||||
|
"https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
|
||||||
|
# Only cache valid usage responses (not error/rate-limit JSON)
|
||||||
|
if [ -n "$response" ] && echo "$response" | jq -e '.five_hour' >/dev/null 2>&1; then
|
||||||
|
usage_data="$response"
|
||||||
|
echo "$response" > "$cache_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Remove the stampede sentinel if the fetch failed to produce valid JSON —
|
||||||
|
# otherwise an empty cache file would suppress retries for a full cache_max_age window.
|
||||||
|
[ -f "$cache_file" ] && [ ! -s "$cache_file" ] && rm -f "$cache_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cross-platform ISO to epoch conversion
|
||||||
|
# Converts ISO 8601 timestamp (e.g. "2025-06-15T12:30:00Z" or "2025-06-15T12:30:00.123+00:00") to epoch seconds.
|
||||||
|
# Properly handles UTC timestamps and converts to local time.
|
||||||
|
iso_to_epoch() {
|
||||||
|
local iso_str="$1"
|
||||||
|
|
||||||
|
# Try GNU date first (Linux) — handles ISO 8601 format automatically
|
||||||
|
local epoch
|
||||||
|
epoch=$(date -d "${iso_str}" +%s 2>/dev/null)
|
||||||
|
if [ -n "$epoch" ]; then
|
||||||
|
echo "$epoch"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# BSD date (macOS) - handle various ISO 8601 formats
|
||||||
|
local stripped="${iso_str%%.*}" # Remove fractional seconds (.123456)
|
||||||
|
stripped="${stripped%%Z}" # Remove trailing Z
|
||||||
|
stripped="${stripped%%+*}" # Remove timezone offset (+00:00)
|
||||||
|
stripped="${stripped%%-[0-9][0-9]:[0-9][0-9]}" # Remove negative timezone offset
|
||||||
|
|
||||||
|
# Check if timestamp is UTC (has Z or +00:00 or -00:00)
|
||||||
|
if [[ "$iso_str" == *"Z"* ]] || [[ "$iso_str" == *"+00:00"* ]] || [[ "$iso_str" == *"-00:00"* ]]; then
|
||||||
|
# For UTC timestamps, parse with timezone set to UTC
|
||||||
|
epoch=$(env TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$stripped" +%s 2>/dev/null)
|
||||||
|
else
|
||||||
|
epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$stripped" +%s 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$epoch" ]; then
|
||||||
|
echo "$epoch"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Format ISO reset time to compact local time
|
||||||
|
# Usage: format_reset_time <iso_string> <style: time|datetime|date>
|
||||||
|
format_reset_time() {
|
||||||
|
local iso_str="$1"
|
||||||
|
local style="$2"
|
||||||
|
{ [ -z "$iso_str" ] || [ "$iso_str" = "null" ]; } && return
|
||||||
|
|
||||||
|
# Parse ISO datetime and convert to local time (cross-platform)
|
||||||
|
local epoch
|
||||||
|
epoch=$(iso_to_epoch "$iso_str")
|
||||||
|
[ -z "$epoch" ] && return
|
||||||
|
|
||||||
|
# Format based on style
|
||||||
|
# Try GNU date first (Linux), then BSD date (macOS)
|
||||||
|
# Previous implementation piped BSD date through sed/tr, which always returned
|
||||||
|
# exit code 0 from the last pipe stage, preventing the GNU date fallback from
|
||||||
|
# ever executing on Linux.
|
||||||
|
local formatted=""
|
||||||
|
case "$style" in
|
||||||
|
time)
|
||||||
|
formatted=$(date -d "@$epoch" +"%H:%M" 2>/dev/null) || \
|
||||||
|
formatted=$(date -j -r "$epoch" +"%H:%M" 2>/dev/null)
|
||||||
|
;;
|
||||||
|
datetime)
|
||||||
|
formatted=$(date -d "@$epoch" +"%a %b %-d, %H:%M" 2>/dev/null) || \
|
||||||
|
formatted=$(date -j -r "$epoch" +"%a %b %-d, %H:%M" 2>/dev/null)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
formatted=$(date -d "@$epoch" +"%b %-d" 2>/dev/null) || \
|
||||||
|
formatted=$(date -j -r "$epoch" +"%b %-d" 2>/dev/null)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
[ -n "$formatted" ] && echo "$formatted"
|
||||||
|
}
|
||||||
|
|
||||||
|
sep=" ${dim}|${reset} "
|
||||||
|
|
||||||
|
# Render extra_usage segment from API usage data (not available via stdin rate_limits).
|
||||||
|
# Appends to the global $out. No-op when data is missing or is_enabled is false.
|
||||||
|
render_extra_usage() {
|
||||||
|
local data="$1"
|
||||||
|
[ -z "$data" ] && return
|
||||||
|
local enabled
|
||||||
|
enabled=$(echo "$data" | jq -r '.extra_usage.is_enabled // false' 2>/dev/null)
|
||||||
|
[ "$enabled" != "true" ] && return
|
||||||
|
|
||||||
|
local pct used limit
|
||||||
|
pct=$(echo "$data" | jq -r '.extra_usage.utilization // 0' | awk '{printf "%.0f", $1}')
|
||||||
|
used=$(echo "$data" | jq -r '.extra_usage.used_credits // 0' | LC_NUMERIC=C awk '{printf "%.2f", $1/100}')
|
||||||
|
limit=$(echo "$data" | jq -r '.extra_usage.monthly_limit // 0' | LC_NUMERIC=C awk '{printf "%.2f", $1/100}')
|
||||||
|
|
||||||
|
if [ -n "$used" ] && [ -n "$limit" ] && [[ "$used" != *'$'* ]] && [[ "$limit" != *'$'* ]]; then
|
||||||
|
local color
|
||||||
|
color=$(usage_color "$pct")
|
||||||
|
out+="${sep}${white}extra${reset} ${color}\$${used}/\$${limit}${reset}"
|
||||||
|
else
|
||||||
|
out+="${sep}${white}extra${reset} ${green}enabled${reset}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if $effective_builtin; then
|
||||||
|
# ---- Use rate_limits data provided directly by Claude Code in JSON input ----
|
||||||
|
# resets_at values are Unix epoch integers in this source
|
||||||
|
if [ -n "$builtin_five_hour_pct" ]; then
|
||||||
|
five_hour_pct=$(printf "%.0f" "$builtin_five_hour_pct")
|
||||||
|
five_hour_color=$(usage_color "$five_hour_pct")
|
||||||
|
out+="${sep}${white}5h${reset} ${five_hour_color}${five_hour_pct}%${reset}"
|
||||||
|
if [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ]; then
|
||||||
|
five_hour_reset=$(date -j -r "$builtin_five_hour_reset" +"%H:%M" 2>/dev/null || date -d "@$builtin_five_hour_reset" +"%H:%M" 2>/dev/null)
|
||||||
|
[ -n "$five_hour_reset" ] && out+=" ${dim}@${five_hour_reset}${reset}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$builtin_seven_day_pct" ]; then
|
||||||
|
seven_day_pct=$(printf "%.0f" "$builtin_seven_day_pct")
|
||||||
|
seven_day_color=$(usage_color "$seven_day_pct")
|
||||||
|
out+="${sep}${white}7d${reset} ${seven_day_color}${seven_day_pct}%${reset}"
|
||||||
|
if [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ]; then
|
||||||
|
seven_day_reset=$(date -j -r "$builtin_seven_day_reset" +"%a %b %-d, %H:%M" 2>/dev/null || date -d "@$builtin_seven_day_reset" +"%a %b %-d, %H:%M" 2>/dev/null)
|
||||||
|
[ -n "$seven_day_reset" ] && out+=" ${dim}@${seven_day_reset}${reset}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Render extra_usage from API cache (stdin rate_limits doesn't expose it)
|
||||||
|
render_extra_usage "$usage_data"
|
||||||
|
|
||||||
|
# Cache builtin values so they're available as fallback when API is unavailable.
|
||||||
|
# Convert epoch resets_at to ISO 8601 for compatibility with the API-format cache parser.
|
||||||
|
# Preserve extra_usage from prior API response so we don't clobber it.
|
||||||
|
_fh_reset_json="null"
|
||||||
|
if [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ] && [ "$builtin_five_hour_reset" != "0" ]; then
|
||||||
|
_fh_iso=$(date -u -r "$builtin_five_hour_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
|
||||||
|
date -u -d "@$builtin_five_hour_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
|
||||||
|
[ -n "$_fh_iso" ] && _fh_reset_json="\"$_fh_iso\""
|
||||||
|
fi
|
||||||
|
_sd_reset_json="null"
|
||||||
|
if [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ] && [ "$builtin_seven_day_reset" != "0" ]; then
|
||||||
|
_sd_iso=$(date -u -r "$builtin_seven_day_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
|
||||||
|
date -u -d "@$builtin_seven_day_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
|
||||||
|
[ -n "$_sd_iso" ] && _sd_reset_json="\"$_sd_iso\""
|
||||||
|
fi
|
||||||
|
_extra_json=$(echo "$usage_data" | jq -c '.extra_usage // null' 2>/dev/null)
|
||||||
|
[ -z "$_extra_json" ] && _extra_json="null"
|
||||||
|
printf '{"five_hour":{"utilization":%s,"resets_at":%s},"seven_day":{"utilization":%s,"resets_at":%s},"extra_usage":%s}' \
|
||||||
|
"${builtin_five_hour_pct:-0}" "$_fh_reset_json" \
|
||||||
|
"${builtin_seven_day_pct:-0}" "$_sd_reset_json" \
|
||||||
|
"$_extra_json" > "$cache_file" 2>/dev/null
|
||||||
|
elif [ -n "$usage_data" ] && echo "$usage_data" | jq -e '.five_hour' >/dev/null 2>&1; then
|
||||||
|
# ---- Fall back: API-fetched usage data ----
|
||||||
|
# ---- 5-hour (current) ----
|
||||||
|
five_hour_pct=$(echo "$usage_data" | jq -r '.five_hour.utilization // 0' | awk '{printf "%.0f", $1}')
|
||||||
|
five_hour_reset_iso=$(echo "$usage_data" | jq -r '.five_hour.resets_at // empty')
|
||||||
|
five_hour_reset=$(format_reset_time "$five_hour_reset_iso" "time")
|
||||||
|
five_hour_color=$(usage_color "$five_hour_pct")
|
||||||
|
|
||||||
|
out+="${sep}${white}5h${reset} ${five_hour_color}${five_hour_pct}%${reset}"
|
||||||
|
[ -n "$five_hour_reset" ] && out+=" ${dim}@${five_hour_reset}${reset}"
|
||||||
|
|
||||||
|
# ---- 7-day (weekly) ----
|
||||||
|
seven_day_pct=$(echo "$usage_data" | jq -r '.seven_day.utilization // 0' | awk '{printf "%.0f", $1}')
|
||||||
|
seven_day_reset_iso=$(echo "$usage_data" | jq -r '.seven_day.resets_at // empty')
|
||||||
|
seven_day_reset=$(format_reset_time "$seven_day_reset_iso" "datetime")
|
||||||
|
seven_day_color=$(usage_color "$seven_day_pct")
|
||||||
|
|
||||||
|
out+="${sep}${white}7d${reset} ${seven_day_color}${seven_day_pct}%${reset}"
|
||||||
|
[ -n "$seven_day_reset" ] && out+=" ${dim}@${seven_day_reset}${reset}"
|
||||||
|
|
||||||
|
render_extra_usage "$usage_data"
|
||||||
|
else
|
||||||
|
# No valid usage data — show placeholders
|
||||||
|
out+="${sep}${white}5h${reset} ${dim}-${reset}"
|
||||||
|
out+="${sep}${white}7d${reset} ${dim}-${reset}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ===== Update check (cached, 24h TTL) =====
|
||||||
|
# Set STATUSLINE_CHECK_UPDATES=false to disable the update check (no network calls).
|
||||||
|
update_line=""
|
||||||
|
if [ "${STATUSLINE_CHECK_UPDATES:-true}" != "false" ]; then
|
||||||
|
version_cache_file="/tmp/claude/statusline-version-cache.json"
|
||||||
|
version_cache_max_age=86400 # 24 hours
|
||||||
|
|
||||||
|
version_needs_refresh=true
|
||||||
|
version_data=""
|
||||||
|
|
||||||
|
if [ -f "$version_cache_file" ]; then
|
||||||
|
vc_mtime=$(stat -c %Y "$version_cache_file" 2>/dev/null || stat -f %m "$version_cache_file" 2>/dev/null)
|
||||||
|
vc_now=$(date +%s)
|
||||||
|
vc_age=$(( vc_now - vc_mtime ))
|
||||||
|
if [ "$vc_age" -lt "$version_cache_max_age" ]; then
|
||||||
|
version_needs_refresh=false
|
||||||
|
fi
|
||||||
|
version_data=$(cat "$version_cache_file" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $version_needs_refresh; then
|
||||||
|
touch "$version_cache_file" 2>/dev/null
|
||||||
|
vc_response=$(curl -s --max-time 5 \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
"https://api.github.com/repos/daniel3303/ClaudeCodeStatusLine/releases/latest" 2>/dev/null)
|
||||||
|
if [ -n "$vc_response" ] && echo "$vc_response" | jq -e '.tag_name' >/dev/null 2>&1; then
|
||||||
|
version_data="$vc_response"
|
||||||
|
echo "$vc_response" > "$version_cache_file"
|
||||||
|
elif [ ! -s "$version_cache_file" ]; then
|
||||||
|
rm -f "$version_cache_file" 2>/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$version_data" ]; then
|
||||||
|
latest_tag=$(echo "$version_data" | jq -r '.tag_name // empty')
|
||||||
|
if [ -n "$latest_tag" ] && version_gt "$latest_tag" "$VERSION"; then
|
||||||
|
update_line="\n${dim}Update available: ${latest_tag} → Tell Claude: \"Find my installed status bar and update it\"${reset}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Append CLI version as last segment
|
||||||
|
if [ -n "$cli_version" ]; then
|
||||||
|
out+=" ${dim}|${reset} ${orange}v${cli_version}${reset}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Output
|
||||||
|
printf "%b" "$out$update_line"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ lib, pkgs', pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) genAttrs mkIf mkMerge mkForce;
|
inherit (lib) genAttrs mkIf mkMerge mkForce mapAttrs mkOptionDefault mkDefault;
|
||||||
inherit (lib.my) mkBoolOpt';
|
inherit (lib.my) mkOpt' mkBoolOpt';
|
||||||
|
inherit (lib.my.c) pubDomain;
|
||||||
|
|
||||||
cfg = config.my.gui;
|
cfg = config.my.gui;
|
||||||
|
|
||||||
@@ -10,33 +11,80 @@ let
|
|||||||
name = "Monocraft";
|
name = "Monocraft";
|
||||||
size = 10;
|
size = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
doomWad = pkgs.fetchurl {
|
||||||
|
url = "https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad";
|
||||||
|
hash = "sha256-HX1DvlAeZ9kn5BXguPPinDvzMHXoWXIYFvZSpSbKx3E=";
|
||||||
|
};
|
||||||
|
subwaySurfers = pkgs.fetchurl {
|
||||||
|
url = "https://p.${pubDomain}/video/subway-surfers-smol.mkv";
|
||||||
|
hash = "sha256-fMe7TDRNTymRHIJOi7qG3trzu4GP8a3gCDz+FMkX1dY=";
|
||||||
|
};
|
||||||
|
minecraftParkour = pkgs.fetchurl {
|
||||||
|
url = "https://p.${pubDomain}/video/minecraft-parkour-smol.mkv";
|
||||||
|
hash = "sha256-723pRm4AsIjY/WFUyAHzTJp+JvH4Pn5hvzF9wHTnOPA=";
|
||||||
|
};
|
||||||
|
|
||||||
|
genLipsum = pkgs.writeScript "lipsum" ''
|
||||||
|
#!${pkgs.python3.withPackages (ps: [ ps.python-lorem ])}/bin/python
|
||||||
|
import lorem
|
||||||
|
print(lorem.get_paragraph(count=5, sep='\n\n'))
|
||||||
|
'';
|
||||||
|
doomsaver' = brainrotTextCommand: pkgs.runCommand "doomsaver" {
|
||||||
|
inherit (pkgs) windowtolayer tmux terminaltexteffects;
|
||||||
|
chocoDoom = pkgs.chocolate-doom2xx;
|
||||||
|
ffmpeg = pkgs.ffmpeg-full;
|
||||||
|
python = pkgs.python3.withPackages (ps: [ ps.filelock ]);
|
||||||
|
|
||||||
|
inherit doomWad;
|
||||||
|
enojy = ./enojy.jpg;
|
||||||
|
inherit brainrotTextCommand subwaySurfers minecraftParkour;
|
||||||
|
} ''
|
||||||
|
mkdir -p "$out"/bin
|
||||||
|
substituteAll ${./screensaver.py} "$out"/bin/doomsaver
|
||||||
|
chmod +x "$out"/bin/doomsaver
|
||||||
|
'';
|
||||||
|
doomsaver = doomsaver' cfg.screensaver.brainrotTextCommand;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.gui = {
|
options.my.gui = with lib.types; {
|
||||||
enable = mkBoolOpt' true "Enable settings and packages meant for graphical systems";
|
enable = mkBoolOpt' true "Enable settings and packages meant for graphical systems";
|
||||||
manageGraphical = mkBoolOpt' false "Configure the graphical session";
|
manageGraphical = mkBoolOpt' false "Configure the graphical session";
|
||||||
standalone = mkBoolOpt' false "Enable settings for fully Nix managed systems";
|
standalone = mkBoolOpt' false "Enable settings for fully Nix managed systems";
|
||||||
|
screensaver.brainrotTextCommand = mkOpt' (either path str) genLipsum "Command to generate brainrot text.";
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable (mkMerge [
|
config = mkIf cfg.enable (mkMerge [
|
||||||
{
|
{
|
||||||
home = {
|
home = {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
|
xdg-utils
|
||||||
|
|
||||||
font.package
|
font.package
|
||||||
(nerdfonts.override {
|
nerd-fonts.sauce-code-pro
|
||||||
fonts = [ "DroidSansMono" "SourceCodePro" ];
|
nerd-fonts.droid-sans-mono
|
||||||
})
|
noto-fonts-color-emoji
|
||||||
noto-fonts-emoji
|
|
||||||
|
|
||||||
grim
|
grim
|
||||||
slurp
|
slurp
|
||||||
swappy
|
swappy
|
||||||
|
|
||||||
python310Packages.python-lsp-server
|
python3Packages.python-lsp-server
|
||||||
nil # nix language server
|
nil # nix language server
|
||||||
zls # zig language server
|
zls # zig language server
|
||||||
rust-analyzer
|
rust-analyzer
|
||||||
neofetch
|
|
||||||
|
cowsay
|
||||||
|
fortune
|
||||||
|
jp2a
|
||||||
|
terminaltexteffects
|
||||||
|
screenfetch
|
||||||
|
fastfetch
|
||||||
|
cmatrix
|
||||||
|
doomsaver
|
||||||
|
|
||||||
|
ffmpeg-full
|
||||||
|
xournalpp
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +99,15 @@ in
|
|||||||
alacritty = {
|
alacritty = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
font.normal.family = font.name;
|
general.import = [ ./alacritty-xterm.toml ];
|
||||||
|
|
||||||
|
font = {
|
||||||
|
size = font.size;
|
||||||
|
normal = {
|
||||||
|
family = font.name;
|
||||||
|
style = "Regular";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,9 +115,23 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
inherit font;
|
inherit font;
|
||||||
settings = {
|
settings = {
|
||||||
background_opacity = "0.8";
|
background_opacity = "0.65";
|
||||||
tab_bar_edge = "top";
|
tab_bar_edge = "top";
|
||||||
shell_integration = "no-sudo";
|
shell_integration = "no-sudo";
|
||||||
|
font_features = "${font.name} -liga";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
foot = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
main = {
|
||||||
|
font = "${font.name}:size=${toString font.size}";
|
||||||
|
};
|
||||||
|
colors = {
|
||||||
|
alpha = 0.8;
|
||||||
|
background = "000000";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -85,6 +155,19 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
claude-code = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
model = "opus";
|
||||||
|
theme = "auto";
|
||||||
|
statusLine = {
|
||||||
|
type = "command";
|
||||||
|
command = ./claude-statusline.sh;
|
||||||
|
padding = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +191,19 @@ in
|
|||||||
};
|
};
|
||||||
Install.RequiredBy = [ "sway-session.target" ];
|
Install.RequiredBy = [ "sway-session.target" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
activate-linux = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Linux activation watermark";
|
||||||
|
After = "graphical-session.target";
|
||||||
|
PartOf = "graphical-session.target";
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = "${pkgs.activate-linux}/bin/activate-linux";
|
||||||
|
};
|
||||||
|
Install.RequiredBy = [ "graphical-session.target" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,6 +213,7 @@ in
|
|||||||
wl-clipboard
|
wl-clipboard
|
||||||
wev
|
wev
|
||||||
wdisplays
|
wdisplays
|
||||||
|
swaysome
|
||||||
|
|
||||||
pavucontrol
|
pavucontrol
|
||||||
libsecret
|
libsecret
|
||||||
@@ -126,10 +223,11 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
pointerCursor = {
|
pointerCursor = {
|
||||||
package = pkgs.vanilla-dmz;
|
package = pkgs.posy-cursors;
|
||||||
name = "Vanilla-DMZ";
|
name = "Posy_Cursor";
|
||||||
size = 16;
|
size = 32;
|
||||||
gtk.enable = true;
|
gtk.enable = true;
|
||||||
|
x11.enable = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,9 +236,36 @@ in
|
|||||||
xsession.preferStatusNotifierItems = true;
|
xsession.preferStatusNotifierItems = true;
|
||||||
wayland = {
|
wayland = {
|
||||||
windowManager = {
|
windowManager = {
|
||||||
sway = {
|
sway =
|
||||||
|
let
|
||||||
|
cfg = config.wayland.windowManager.sway.config;
|
||||||
|
mod = cfg.modifier;
|
||||||
|
|
||||||
|
renameWs = pkgs.writeShellScript "sway-rename-ws" ''
|
||||||
|
focused_ws="$(swaymsg -t get_workspaces | jq ".[] | select(.focused)")"
|
||||||
|
focused_num="$(jq -r ".num" <<< "$focused_ws")"
|
||||||
|
focused_name="$(jq -r ".name" <<< "$focused_ws")"
|
||||||
|
placeholder="$(sed -E 's/[0-9]+: //' <<< "$focused_name")"
|
||||||
|
|
||||||
|
name="$(rofi -dmenu -p "rename ws $focused_num" -theme+entry+placeholder "\"$placeholder\"")"
|
||||||
|
if [ -n "$name" ]; then
|
||||||
|
swaymsg rename workspace "$focused_name" to "$focused_num: $name"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
clearWsName = pkgs.writeShellScript "sway-clear-ws-name" ''
|
||||||
|
focused_ws="$(swaymsg -t get_workspaces | jq ".[] | select(.focused)")"
|
||||||
|
focused_num="$(jq -r ".num" <<< "$focused_ws")"
|
||||||
|
focused_name="$(jq -r ".name" <<< "$focused_ws")"
|
||||||
|
|
||||||
|
swaymsg rename workspace "$focused_name" to "$focused_num"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
enable = true;
|
enable = true;
|
||||||
xwayland = true;
|
xwayland = true;
|
||||||
|
extraConfigEarly = ''
|
||||||
|
set $mod ${mod}
|
||||||
|
'';
|
||||||
config = {
|
config = {
|
||||||
input = {
|
input = {
|
||||||
"type:touchpad" = {
|
"type:touchpad" = {
|
||||||
@@ -155,31 +280,95 @@ in
|
|||||||
|
|
||||||
modifier = "Mod4";
|
modifier = "Mod4";
|
||||||
terminal = "kitty";
|
terminal = "kitty";
|
||||||
keybindings =
|
keybindings = mapAttrs (k: mkOptionDefault) {
|
||||||
let
|
"${mod}+Left" = "focus left";
|
||||||
cfg = config.wayland.windowManager.sway.config;
|
"${mod}+Down" = "focus down";
|
||||||
mod = cfg.modifier;
|
"${mod}+Up" = "focus up";
|
||||||
in
|
"${mod}+Right" = "focus right";
|
||||||
lib.mkOptionDefault {
|
|
||||||
"${mod}+d" = null;
|
|
||||||
"${mod}+l" = "exec swaylock -i ${./lock.png} -s stretch";
|
|
||||||
"${mod}+x" = "exec ${cfg.menu}";
|
|
||||||
"${mod}+Shift+x" = "exec rofi -show drun";
|
|
||||||
"${mod}+q" = "kill";
|
|
||||||
"${mod}+Shift+q" = "exec swaynag -t warning -m 'bruh you really wanna kill sway?' -b 'ye' 'systemctl --user stop graphical-session.target && swaymsg exit'";
|
|
||||||
"${mod}+Shift+d" = ''exec grim - | swappy -f -'';
|
|
||||||
"${mod}+Shift+s" = ''exec grim -g "$(slurp)" - | swappy -f -'';
|
|
||||||
"${mod}+Shift+e" = "exec rofi -show emoji";
|
|
||||||
# Config for this doesn't seem to work :/
|
|
||||||
"${mod}+c" = ''exec rofi -show calc -calc-command "echo -n '{result}' | ${pkgs.wl-clipboard}/bin/wl-copy"'';
|
|
||||||
|
|
||||||
"XF86AudioRaiseVolume" = "exec ${pkgs.pamixer}/bin/pamixer -i 5";
|
"${mod}+Shift+Left" = "move left";
|
||||||
"XF86AudioLowerVolume" = "exec ${pkgs.pamixer}/bin/pamixer -d 5";
|
"${mod}+Shift+Down" = "move down";
|
||||||
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play";
|
"${mod}+Shift+Up" = "move up";
|
||||||
"XF86AudioPause" = "exec ${pkgs.playerctl}/bin/playerctl pause";
|
"${mod}+Shift+Right" = "move right";
|
||||||
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
|
||||||
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
"${mod}+b" = "splith";
|
||||||
};
|
"${mod}+v" = "splitv";
|
||||||
|
"${mod}+f" = "fullscreen toggle";
|
||||||
|
"${mod}+a" = "focus parent";
|
||||||
|
|
||||||
|
"${mod}+s" = "layout stacking";
|
||||||
|
"${mod}+w" = "layout tabbed";
|
||||||
|
"${mod}+e" = "layout toggle split";
|
||||||
|
|
||||||
|
"${mod}+Shift+space" = "floating toggle";
|
||||||
|
"${mod}+space" = "focus mode_toggle";
|
||||||
|
|
||||||
|
"${mod}+1" = "workspace number 1";
|
||||||
|
"${mod}+2" = "workspace number 2";
|
||||||
|
"${mod}+3" = "workspace number 3";
|
||||||
|
"${mod}+4" = "workspace number 4";
|
||||||
|
"${mod}+5" = "workspace number 5";
|
||||||
|
"${mod}+6" = "workspace number 6";
|
||||||
|
"${mod}+7" = "workspace number 7";
|
||||||
|
"${mod}+8" = "workspace number 8";
|
||||||
|
"${mod}+9" = "workspace number 9";
|
||||||
|
"${mod}+0" = "workspace number 10";
|
||||||
|
|
||||||
|
"${mod}+Shift+1" =
|
||||||
|
"move container to workspace number 1";
|
||||||
|
"${mod}+Shift+2" =
|
||||||
|
"move container to workspace number 2";
|
||||||
|
"${mod}+Shift+3" =
|
||||||
|
"move container to workspace number 3";
|
||||||
|
"${mod}+Shift+4" =
|
||||||
|
"move container to workspace number 4";
|
||||||
|
"${mod}+Shift+5" =
|
||||||
|
"move container to workspace number 5";
|
||||||
|
"${mod}+Shift+6" =
|
||||||
|
"move container to workspace number 6";
|
||||||
|
"${mod}+Shift+7" =
|
||||||
|
"move container to workspace number 7";
|
||||||
|
"${mod}+Shift+8" =
|
||||||
|
"move container to workspace number 8";
|
||||||
|
"${mod}+Shift+9" =
|
||||||
|
"move container to workspace number 9";
|
||||||
|
"${mod}+Shift+0" =
|
||||||
|
"move container to workspace number 10";
|
||||||
|
|
||||||
|
"${mod}+Shift+minus" = "move scratchpad";
|
||||||
|
"${mod}+minus" = "scratchpad show";
|
||||||
|
|
||||||
|
"${mod}+Return" = "exec ${cfg.terminal}";
|
||||||
|
"${mod}+r" = "mode resize";
|
||||||
|
"${mod}+d" = null;
|
||||||
|
"${mod}+l" = "exec ${doomsaver}/bin/doomsaver";
|
||||||
|
"${mod}+q" = "kill";
|
||||||
|
"${mod}+Shift+c" = "reload";
|
||||||
|
"${mod}+Shift+q" = "exec swaynag -t warning -m 'bruh you really wanna kill sway?' -b 'ye' 'systemctl --user stop graphical-session.target && swaymsg exit'";
|
||||||
|
|
||||||
|
# rofi
|
||||||
|
"${mod}+x" = "exec ${cfg.menu}";
|
||||||
|
"${mod}+Shift+x" = "exec rofi -show drun";
|
||||||
|
"${mod}+Shift+e" = "exec rofi -show emoji";
|
||||||
|
# Config for this doesn't seem to work :/
|
||||||
|
"${mod}+c" = ''exec rofi -show calc -calc-command "echo -n '{result}' | ${pkgs.wl-clipboard}/bin/wl-copy"'';
|
||||||
|
"${mod}+Shift+r" = "exec ${renameWs}";
|
||||||
|
"${mod}+Shift+n" = "exec ${clearWsName}";
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
"${mod}+Shift+d" = ''exec grim - | swappy -f -'';
|
||||||
|
"${mod}+Shift+s" = ''exec grim -g "$(slurp)" - | swappy -f -'';
|
||||||
|
|
||||||
|
"XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-";
|
||||||
|
"XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set +5%";
|
||||||
|
|
||||||
|
"XF86AudioRaiseVolume" = "exec ${pkgs.pamixer}/bin/pamixer -i 5";
|
||||||
|
"XF86AudioLowerVolume" = "exec ${pkgs.pamixer}/bin/pamixer -d 5";
|
||||||
|
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play";
|
||||||
|
"XF86AudioPause" = "exec ${pkgs.playerctl}/bin/playerctl pause";
|
||||||
|
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
||||||
|
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
||||||
|
};
|
||||||
keycodebindings = {
|
keycodebindings = {
|
||||||
# keycode for XF86AudioPlayPause (no sym for some reason)
|
# keycode for XF86AudioPlayPause (no sym for some reason)
|
||||||
"172" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
|
"172" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
|
||||||
@@ -188,6 +377,9 @@ in
|
|||||||
menu = "rofi -show run";
|
menu = "rofi -show run";
|
||||||
bars = mkForce [ ];
|
bars = mkForce [ ];
|
||||||
};
|
};
|
||||||
|
extraConfig = ''
|
||||||
|
include ${./swaysome.conf}
|
||||||
|
'';
|
||||||
|
|
||||||
swaynag = {
|
swaynag = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -202,6 +394,10 @@ in
|
|||||||
name = "Numix";
|
name = "Numix";
|
||||||
package = pkgs.numix-gtk-theme;
|
package = pkgs.numix-gtk-theme;
|
||||||
};
|
};
|
||||||
|
gtk4.theme = {
|
||||||
|
name = "Numix";
|
||||||
|
package = pkgs.numix-gtk-theme;
|
||||||
|
};
|
||||||
iconTheme = {
|
iconTheme = {
|
||||||
name = "Numix";
|
name = "Numix";
|
||||||
package = pkgs.numix-icon-theme;
|
package = pkgs.numix-icon-theme;
|
||||||
@@ -210,17 +406,10 @@ in
|
|||||||
};
|
};
|
||||||
qt = {
|
qt = {
|
||||||
enable = true;
|
enable = true;
|
||||||
platformTheme = "gtk";
|
platformTheme.name = "gtk";
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
swaync = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
widgets = [ "title" "dnd" "mpris" "notifications" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
playerctld.enable = true;
|
playerctld.enable = true;
|
||||||
spotifyd = {
|
spotifyd = {
|
||||||
enable = false;
|
enable = false;
|
||||||
@@ -237,18 +426,45 @@ in
|
|||||||
device_type = "computer";
|
device_type = "computer";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
easyeffects = {
|
||||||
|
enable = mkDefault false;
|
||||||
|
preset = mkDefault "moar-bass";
|
||||||
|
extraPresets = {
|
||||||
|
moar-bass = {
|
||||||
|
output = {
|
||||||
|
"bass_enhancer#0" = {
|
||||||
|
amount = 3;
|
||||||
|
blend = 0;
|
||||||
|
bypass = false;
|
||||||
|
floor = 20;
|
||||||
|
floor-active = false;
|
||||||
|
harmonics = 8.5;
|
||||||
|
input-gain = 0;
|
||||||
|
output-gain = 0;
|
||||||
|
scope = 100;
|
||||||
|
};
|
||||||
|
blocklist = [ ];
|
||||||
|
plugins_order = [ "bass_enhancer#0" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
git = {
|
git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
diff-so-fancy.enable = true;
|
settings = {
|
||||||
userEmail = "jackos1998@gmail.com";
|
user = {
|
||||||
userName = "Jack O'Sullivan";
|
email = "jackos1998@gmail.com";
|
||||||
extraConfig = {
|
name = "Jack O'Sullivan";
|
||||||
|
};
|
||||||
pull.rebase = true;
|
pull.rebase = true;
|
||||||
};
|
};
|
||||||
|
lfs.enable = true;
|
||||||
};
|
};
|
||||||
|
diff-so-fancy.enable = true;
|
||||||
|
|
||||||
waybar = import ./waybar.nix { inherit lib pkgs config font; };
|
waybar = import ./waybar.nix { inherit lib pkgs config font; };
|
||||||
rofi = {
|
rofi = {
|
||||||
@@ -271,7 +487,7 @@ in
|
|||||||
|
|
||||||
chromium = {
|
chromium = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = (pkgs.chromium.override { enableWideVine = true; }).overrideAttrs (old: {
|
package = (pkgs'.unstable.chromium.override { enableWideVine = true; }).overrideAttrs (old: {
|
||||||
buildCommand = ''
|
buildCommand = ''
|
||||||
${old.buildCommand}
|
${old.buildCommand}
|
||||||
|
|
||||||
@@ -297,6 +513,15 @@ in
|
|||||||
] (_: "chromium-browser.desktop");
|
] (_: "chromium-browser.desktop");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
swaync = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
widgets = [ "title" "dnd" "mpris" "notifications" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
(mkIf (cfg.standalone && !pkgs.stdenv.isDarwin) {
|
(mkIf (cfg.standalone && !pkgs.stdenv.isDarwin) {
|
||||||
@@ -304,6 +529,7 @@ in
|
|||||||
userDirs = {
|
userDirs = {
|
||||||
enable = true;
|
enable = true;
|
||||||
createDirectories = true;
|
createDirectories = true;
|
||||||
|
setSessionVariables = true;
|
||||||
desktop = "$HOME/desktop";
|
desktop = "$HOME/desktop";
|
||||||
documents = "$HOME/documents";
|
documents = "$HOME/documents";
|
||||||
download = "$HOME/downloads";
|
download = "$HOME/downloads";
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
Executable
+257
@@ -0,0 +1,257 @@
|
|||||||
|
#!@python@/bin/python
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import filelock
|
||||||
|
|
||||||
|
class Screensaver:
|
||||||
|
def __init__(self, cmd, env=None, weight=1):
|
||||||
|
self.cmd = cmd
|
||||||
|
self.weight = weight
|
||||||
|
|
||||||
|
if env is not None:
|
||||||
|
self.env = os.environ.copy()
|
||||||
|
for k, v in env.items():
|
||||||
|
self.env[k] = v
|
||||||
|
else:
|
||||||
|
self.env = None
|
||||||
|
self.proc = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
assert self.proc is None
|
||||||
|
self.proc = subprocess.Popen(self.cmd, env=self.env)
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
assert self.proc is not None
|
||||||
|
self.proc.wait()
|
||||||
|
|
||||||
|
def stop(self, kill=False):
|
||||||
|
assert self.proc is not None
|
||||||
|
if kill:
|
||||||
|
self.proc.kill()
|
||||||
|
else:
|
||||||
|
self.proc.terminate()
|
||||||
|
|
||||||
|
class DoomSaver(Screensaver):
|
||||||
|
wad = '@doomWad@'
|
||||||
|
|
||||||
|
def __init__(self, demo_index, weight=1.5):
|
||||||
|
super().__init__(
|
||||||
|
['@chocoDoom@/bin/chocolate-doom',
|
||||||
|
'-iwad', self.wad,
|
||||||
|
'-demoloopi', str(demo_index)],
|
||||||
|
env={
|
||||||
|
'SDL_AUDIODRIVER': 'null',
|
||||||
|
'SDL_VIDEODRIVER': 'caca',
|
||||||
|
'CACA_DRIVER': 'ncurses',
|
||||||
|
},
|
||||||
|
weight=weight,
|
||||||
|
)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
super().stop(kill=True)
|
||||||
|
|
||||||
|
class TTESaver(Screensaver):
|
||||||
|
effects = (
|
||||||
|
'beams,binarypath,blackhole,bouncyballs,bubbles,burn,colorshift,crumble,'
|
||||||
|
'decrypt,errorcorrect,expand,fireworks,middleout,orbittingvolley,overflow,'
|
||||||
|
'pour,print,rain,randomsequence,rings,scattered,slice,slide,spotlights,'
|
||||||
|
'spray,swarm,synthgrid,unstable,vhstape,waves,wipe'
|
||||||
|
).split(',')
|
||||||
|
|
||||||
|
def __init__(self, cmd, env=None, weight=1):
|
||||||
|
super().__init__(cmd, env=env, weight=weight)
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
while self.running:
|
||||||
|
effect_cmd = ['@terminaltexteffects@/bin/tte', random.choice(self.effects)]
|
||||||
|
print(f"$ {self.cmd} | {' '.join(effect_cmd)}")
|
||||||
|
content = subprocess.check_output(self.cmd, shell=True, env=self.env, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
self.proc = subprocess.Popen(effect_cmd, stdin=subprocess.PIPE)
|
||||||
|
self.proc.stdin.write(content)
|
||||||
|
self.proc.stdin.close()
|
||||||
|
self.proc.wait()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.running = False
|
||||||
|
self.proc.terminate()
|
||||||
|
|
||||||
|
class FFmpegCACASaver(Screensaver):
|
||||||
|
@staticmethod
|
||||||
|
def command(video, size):
|
||||||
|
return ['@ffmpeg@/bin/ffmpeg', '-hide_banner', '-loglevel', 'error',
|
||||||
|
'-stream_loop', '-1', '-i', video,
|
||||||
|
'-pix_fmt', 'rgb24', '-window_size', f'{size}x{size}',
|
||||||
|
'-f', 'caca', '-']
|
||||||
|
|
||||||
|
def __init__(self, video, weight=2):
|
||||||
|
cols, lines = os.get_terminal_size()
|
||||||
|
# IDK if it's reasonable to do this as "1:1"
|
||||||
|
size = lines - 4
|
||||||
|
super().__init__(
|
||||||
|
self.command(video, size),
|
||||||
|
env={'CACA_DRIVER': 'ncurses'},
|
||||||
|
weight=weight,
|
||||||
|
)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
super().stop(kill=True)
|
||||||
|
|
||||||
|
class BrainrotStorySaver(Screensaver):
|
||||||
|
def __init__(self, video, text_command, weight=2):
|
||||||
|
cols, lines = os.get_terminal_size()
|
||||||
|
video_size = lines - 1
|
||||||
|
video_command = ' '.join(FFmpegCACASaver.command(video, video_size))
|
||||||
|
text_command = (
|
||||||
|
f'while true; do {text_command} | '
|
||||||
|
f'@terminaltexteffects@/bin/tte --wrap-text --canvas-width=80 --canvas-height={video_size//2} --anchor-canvas=c '
|
||||||
|
'print --final-gradient-stops=ffffff; clear; done' )
|
||||||
|
self.tmux_session = f'screensaver-{os.urandom(4).hex()}'
|
||||||
|
super().__init__(
|
||||||
|
['@tmux@/bin/tmux', 'new-session', '-s', self.tmux_session, '-n', 'brainrot',
|
||||||
|
text_command, ';', 'split-window', '-hbl', str(lines), video_command],
|
||||||
|
# ['sh', '-c', text_command],
|
||||||
|
env={
|
||||||
|
'CACA_DRIVER': 'ncurses',
|
||||||
|
'SHELL': '/bin/sh',
|
||||||
|
},
|
||||||
|
weight=weight,
|
||||||
|
)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
subprocess.check_call(['@tmux@/bin/tmux', 'kill-session', '-t', self.tmux_session])
|
||||||
|
|
||||||
|
class MultiSaver:
|
||||||
|
savers = [
|
||||||
|
DoomSaver(0),
|
||||||
|
DoomSaver(1),
|
||||||
|
DoomSaver(2),
|
||||||
|
|
||||||
|
Screensaver(['cmatrix']),
|
||||||
|
|
||||||
|
TTESaver('screenfetch -N'),
|
||||||
|
TTESaver('fortune | cowsay'),
|
||||||
|
TTESaver('top -bn1 | head -n50'),
|
||||||
|
TTESaver('ss -nltu'),
|
||||||
|
TTESaver('ss -ntu'),
|
||||||
|
TTESaver('jp2a --width=100 @enojy@'),
|
||||||
|
|
||||||
|
BrainrotStorySaver('@subwaySurfers@', '@brainrotTextCommand@'),
|
||||||
|
BrainrotStorySaver('@minecraftParkour@', '@brainrotTextCommand@'),
|
||||||
|
]
|
||||||
|
state_filename = 'screensaver.json'
|
||||||
|
|
||||||
|
def __init__(self, select=None):
|
||||||
|
self.state_path = os.path.join(f'/run/user/{os.geteuid()}', self.state_filename)
|
||||||
|
self.lock = filelock.FileLock(f'{self.state_path}.lock')
|
||||||
|
|
||||||
|
if select is not None:
|
||||||
|
assert select >= 0 and select < len(self.savers), 'Invalid screensaver index'
|
||||||
|
self.selected = self.savers[select]
|
||||||
|
else:
|
||||||
|
self.selected = None
|
||||||
|
self.cleaned_up = False
|
||||||
|
|
||||||
|
def select(self):
|
||||||
|
with self.lock:
|
||||||
|
if not os.path.exists(self.state_path):
|
||||||
|
state = {'instances': []}
|
||||||
|
else:
|
||||||
|
with open(self.state_path) as f:
|
||||||
|
state = json.load(f)
|
||||||
|
|
||||||
|
if self.selected is None:
|
||||||
|
available = set(range(len(self.savers)))
|
||||||
|
new_instances = []
|
||||||
|
for instance in state['instances']:
|
||||||
|
if not os.path.exists(f"/proc/{instance['pid']}"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_instances.append(instance)
|
||||||
|
i = instance['saver']
|
||||||
|
assert i in available
|
||||||
|
available.remove(i)
|
||||||
|
assert available, 'No screensavers left'
|
||||||
|
available = list(available)
|
||||||
|
|
||||||
|
weights = []
|
||||||
|
for i in available:
|
||||||
|
weights.append(self.savers[i].weight)
|
||||||
|
selected_i = random.choices(available, weights=weights)[0]
|
||||||
|
|
||||||
|
new_instances.append({'pid': os.getpid(), 'saver': selected_i})
|
||||||
|
state['instances'] = new_instances
|
||||||
|
|
||||||
|
# print(f'Selected saver {selected_i}')
|
||||||
|
self.selected = self.savers[selected_i]
|
||||||
|
|
||||||
|
with open(self.state_path, 'w') as f:
|
||||||
|
json.dump(state, f)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
if self.cleaned_up:
|
||||||
|
return
|
||||||
|
self.cleaned_up = True
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
with open(self.state_path) as f:
|
||||||
|
state = json.load(f)
|
||||||
|
|
||||||
|
for i, instance in enumerate(state['instances']):
|
||||||
|
if instance['pid'] == os.getpid():
|
||||||
|
del state['instances'][i]
|
||||||
|
|
||||||
|
with open(self.state_path, 'w') as f:
|
||||||
|
json.dump(state, f)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
assert self.selected is not None
|
||||||
|
self.selected.start()
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, self._sighandler)
|
||||||
|
signal.signal(signal.SIGTERM, self._sighandler)
|
||||||
|
signal.signal(signal.SIGHUP, self._sighandler)
|
||||||
|
self.selected.wait()
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
assert self.selected is not None
|
||||||
|
print('Shutting down')
|
||||||
|
self.selected.stop()
|
||||||
|
self.cleanup()
|
||||||
|
def _sighandler(self, signum, frame):
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Wayland terminal-based lock screen')
|
||||||
|
parser.add_argument('-l', '--locker-cmd', default='swaylock-plugin', help='swaylock-plugin command to use')
|
||||||
|
parser.add_argument('-t', '--terminal', default='alacritty', help='Terminal emulator to use')
|
||||||
|
parser.add_argument('-i', '--instance', action='store_true', help='Run as instance')
|
||||||
|
parser.add_argument('-s', '--screensaver', type=int, help='Force use of specific screensaver')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
if not args.instance:
|
||||||
|
cmd = [
|
||||||
|
args.locker_cmd, '--command-each',
|
||||||
|
f'@windowtolayer@/bin/windowtolayer -- {args.terminal} -e {sys.argv[0]} --instance']
|
||||||
|
if args.screensaver is not None:
|
||||||
|
cmd[-1] += f' --screensaver {args.screensaver}'
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
return
|
||||||
|
|
||||||
|
ms = MultiSaver(select=args.screensaver)
|
||||||
|
ms.select()
|
||||||
|
ms.run()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 251 KiB |
@@ -0,0 +1,66 @@
|
|||||||
|
# Use (un)bindcode or (un)bindsym, depending on what you used in your main sway config file.
|
||||||
|
# The `--no-warn` setting is only added to shortcuts that exist in the default config. You may want to add or remove
|
||||||
|
# that flag on some bindings depending on your config.
|
||||||
|
|
||||||
|
|
||||||
|
# Change focus between workspaces
|
||||||
|
bindsym $mod+Alt+1 exec "swaysome focus 1"
|
||||||
|
bindsym $mod+Alt+2 exec "swaysome focus 2"
|
||||||
|
bindsym $mod+Alt+3 exec "swaysome focus 3"
|
||||||
|
bindsym $mod+Alt+4 exec "swaysome focus 4"
|
||||||
|
bindsym $mod+Alt+5 exec "swaysome focus 5"
|
||||||
|
bindsym $mod+Alt+6 exec "swaysome focus 6"
|
||||||
|
bindsym $mod+Alt+7 exec "swaysome focus 7"
|
||||||
|
bindsym $mod+Alt+8 exec "swaysome focus 8"
|
||||||
|
bindsym $mod+Alt+9 exec "swaysome focus 9"
|
||||||
|
bindsym $mod+Alt+0 exec "swaysome focus 0"
|
||||||
|
|
||||||
|
# Focus workspace groups
|
||||||
|
bindsym --no-warn $mod+1 exec "swaysome focus-group 1"
|
||||||
|
bindsym --no-warn $mod+2 exec "swaysome focus-group 2"
|
||||||
|
bindsym --no-warn $mod+3 exec "swaysome focus-group 3"
|
||||||
|
bindsym --no-warn $mod+4 exec "swaysome focus-group 4"
|
||||||
|
bindsym --no-warn $mod+5 exec "swaysome focus-group 5"
|
||||||
|
bindsym --no-warn $mod+6 exec "swaysome focus-group 6"
|
||||||
|
bindsym --no-warn $mod+7 exec "swaysome focus-group 7"
|
||||||
|
bindsym --no-warn $mod+8 exec "swaysome focus-group 8"
|
||||||
|
bindsym --no-warn $mod+9 exec "swaysome focus-group 9"
|
||||||
|
bindsym --no-warn $mod+0 exec "swaysome focus-group 0"
|
||||||
|
|
||||||
|
# Move containers between workspaces
|
||||||
|
bindsym $mod+Alt+Shift+1 exec "swaysome move 1"
|
||||||
|
bindsym $mod+Alt+Shift+2 exec "swaysome move 2"
|
||||||
|
bindsym $mod+Alt+Shift+3 exec "swaysome move 3"
|
||||||
|
bindsym $mod+Alt+Shift+4 exec "swaysome move 4"
|
||||||
|
bindsym $mod+Alt+Shift+5 exec "swaysome move 5"
|
||||||
|
bindsym $mod+Alt+Shift+6 exec "swaysome move 6"
|
||||||
|
bindsym $mod+Alt+Shift+7 exec "swaysome move 7"
|
||||||
|
bindsym $mod+Alt+Shift+8 exec "swaysome move 8"
|
||||||
|
bindsym $mod+Alt+Shift+9 exec "swaysome move 9"
|
||||||
|
bindsym $mod+Alt+Shift+0 exec "swaysome move 0"
|
||||||
|
|
||||||
|
# Move containers to other workspace groups
|
||||||
|
bindsym --no-warn $mod+Shift+1 exec "swaysome move-to-group 1"
|
||||||
|
bindsym --no-warn $mod+Shift+2 exec "swaysome move-to-group 2"
|
||||||
|
bindsym --no-warn $mod+Shift+3 exec "swaysome move-to-group 3"
|
||||||
|
bindsym --no-warn $mod+Shift+4 exec "swaysome move-to-group 4"
|
||||||
|
bindsym --no-warn $mod+Shift+5 exec "swaysome move-to-group 5"
|
||||||
|
bindsym --no-warn $mod+Shift+6 exec "swaysome move-to-group 6"
|
||||||
|
bindsym --no-warn $mod+Shift+7 exec "swaysome move-to-group 7"
|
||||||
|
bindsym --no-warn $mod+Shift+8 exec "swaysome move-to-group 8"
|
||||||
|
bindsym --no-warn $mod+Shift+9 exec "swaysome move-to-group 9"
|
||||||
|
bindsym --no-warn $mod+Shift+0 exec "swaysome move-to-group 0"
|
||||||
|
|
||||||
|
# Move focused container to next output
|
||||||
|
bindsym $mod+Alt+Right exec "swaysome next-output"
|
||||||
|
# Move focused container to previous output
|
||||||
|
bindsym $mod+Alt+Left exec "swaysome prev-output"
|
||||||
|
|
||||||
|
# Move focused workspace group to next output
|
||||||
|
bindsym $mod+Shift+Alt+Right exec "swaysome workspace-group-next-output"
|
||||||
|
# Move focused workspace group to previous output
|
||||||
|
bindsym $mod+Shift+Alt+Left exec "swaysome workspace-group-prev-output"
|
||||||
|
|
||||||
|
# Init workspaces for every screen
|
||||||
|
exec "swaysome init 1"
|
||||||
|
|
||||||
@@ -146,9 +146,9 @@ in
|
|||||||
dnd-none = "";
|
dnd-none = "";
|
||||||
};
|
};
|
||||||
return-type = "json";
|
return-type = "json";
|
||||||
exec = "${config.services.swaync.package}/bin/swaync-client -swb";
|
exec = "${config.my.swaync.package}/bin/swaync-client -swb";
|
||||||
on-click = "${config.services.swaync.package}/bin/swaync-client -t -sw";
|
on-click = "${config.my.swaync.package}/bin/swaync-client -t -sw";
|
||||||
on-click-right = "${config.services.swaync.package}/bin/swaync-client -d -sw";
|
on-click-right = "${config.my.swaync.package}/bin/swaync-client -d -sw";
|
||||||
escape = true;
|
escape = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
cfg = config.services.swaync;
|
cfg = config.my.swaync;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.services.swaync = with lib.types; {
|
options.my.swaync = with lib.types; {
|
||||||
enable = mkEnableOption "Sway Notification Center";
|
enable = mkEnableOption "Sway Notification Center";
|
||||||
package = mkOption {
|
package = mkOption {
|
||||||
type = package;
|
type = package;
|
||||||
|
|||||||
+131
-9
@@ -11,6 +11,9 @@ rec {
|
|||||||
jellyseerr = 402;
|
jellyseerr = 402;
|
||||||
atticd = 403;
|
atticd = 403;
|
||||||
kea = 404;
|
kea = 404;
|
||||||
|
keepalived_script = 405;
|
||||||
|
photoprism = 406;
|
||||||
|
copyparty = 408;
|
||||||
};
|
};
|
||||||
gids = {
|
gids = {
|
||||||
matrix-syncv3 = 400;
|
matrix-syncv3 = 400;
|
||||||
@@ -18,12 +21,16 @@ rec {
|
|||||||
jellyseerr = 402;
|
jellyseerr = 402;
|
||||||
atticd = 403;
|
atticd = 403;
|
||||||
kea = 404;
|
kea = 404;
|
||||||
|
keepalived_script = 405;
|
||||||
|
photoprism = 406;
|
||||||
|
adbusers = 407;
|
||||||
|
copyparty = 408;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
kernel = {
|
kernel = {
|
||||||
lts = pkgs: pkgs.linuxKernel.packages.linux_6_1;
|
lts = pkgs: pkgs.linuxKernel.packages.linux_6_18;
|
||||||
latest = pkgs: pkgs.linuxKernel.packages.linux_6_6;
|
latest = pkgs: pkgs.linuxKernel.packages.linux_7_0;
|
||||||
};
|
};
|
||||||
|
|
||||||
nginx = rec {
|
nginx = rec {
|
||||||
@@ -94,10 +101,10 @@ rec {
|
|||||||
nix = {
|
nix = {
|
||||||
cache = rec {
|
cache = rec {
|
||||||
substituters = [
|
substituters = [
|
||||||
"https://nix-cache.${pubDomain}/main"
|
"https://nix-cache.${pubDomain}"
|
||||||
];
|
];
|
||||||
keys = [
|
keys = [
|
||||||
"main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8="
|
"nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4="
|
||||||
];
|
];
|
||||||
conf = ''
|
conf = ''
|
||||||
extra-substituters = ${concatStringsSep " " substituters}
|
extra-substituters = ${concatStringsSep " " substituters}
|
||||||
@@ -107,7 +114,7 @@ rec {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pubDomain = "nul.ie";
|
pubDomain = "nul.ie";
|
||||||
colony = {
|
colony = rec {
|
||||||
domain = "ams1.int.${pubDomain}";
|
domain = "ams1.int.${pubDomain}";
|
||||||
pubV4 = "94.142.240.44";
|
pubV4 = "94.142.240.44";
|
||||||
prefixes = with lib.my.net.cidr; rec {
|
prefixes = with lib.my.net.cidr; rec {
|
||||||
@@ -131,6 +138,19 @@ rec {
|
|||||||
v4 = subnet 8 3 all.v4;
|
v4 = subnet 8 3 all.v4;
|
||||||
v6 = subnet 4 3 all.v6;
|
v6 = subnet 4 3 all.v6;
|
||||||
};
|
};
|
||||||
|
qclk = {
|
||||||
|
v4 = subnet 8 4 all.v4;
|
||||||
|
};
|
||||||
|
|
||||||
|
p2pTunnels = {
|
||||||
|
v4 = subnet 8 5 all.v4;
|
||||||
|
};
|
||||||
|
hillcrest = {
|
||||||
|
v4 = subnet 6 0 p2pTunnels.v4;
|
||||||
|
};
|
||||||
|
john-valorant = {
|
||||||
|
v4 = subnet 6 1 p2pTunnels.v4;
|
||||||
|
};
|
||||||
|
|
||||||
cust = {
|
cust = {
|
||||||
v4 = subnet 8 100 all.v4; # single ip for routing only
|
v4 = subnet 8 100 all.v4; # single ip for routing only
|
||||||
@@ -144,6 +164,10 @@ rec {
|
|||||||
v4 = "94.142.242.255/32";
|
v4 = "94.142.242.255/32";
|
||||||
v6 = subnet 8 1 cust.v6;
|
v6 = subnet 8 1 cust.v6;
|
||||||
};
|
};
|
||||||
|
jam = {
|
||||||
|
v4 = subnet 8 4 cust.v4;
|
||||||
|
v6 = subnet 8 2 cust.v6;
|
||||||
|
};
|
||||||
|
|
||||||
vip1 = "94.142.241.224/30";
|
vip1 = "94.142.241.224/30";
|
||||||
vip2 = "94.142.242.254/31";
|
vip2 = "94.142.242.254/31";
|
||||||
@@ -156,6 +180,16 @@ rec {
|
|||||||
home.v6 = "2a0e:97c0:4d0::/48";
|
home.v6 = "2a0e:97c0:4d0::/48";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
custRouting = with lib.my.net.cidr; {
|
||||||
|
mail-vm = host 1 prefixes.cust.v4;
|
||||||
|
darts-vm = host 2 prefixes.cust.v4;
|
||||||
|
jam-ctr = host 3 prefixes.cust.v4;
|
||||||
|
};
|
||||||
|
|
||||||
|
qclk = {
|
||||||
|
wgPort = 51821;
|
||||||
|
};
|
||||||
|
|
||||||
firewallForwards = aa: [
|
firewallForwards = aa: [
|
||||||
{
|
{
|
||||||
port = "http";
|
port = "http";
|
||||||
@@ -169,6 +203,7 @@ rec {
|
|||||||
port = 8448;
|
port = 8448;
|
||||||
dst = aa.middleman.internal.ipv4.address;
|
dst = aa.middleman.internal.ipv4.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
port = 25565;
|
port = 25565;
|
||||||
dst = aa.simpcraft-oci.internal.ipv4.address;
|
dst = aa.simpcraft-oci.internal.ipv4.address;
|
||||||
@@ -178,8 +213,27 @@ rec {
|
|||||||
dst = aa.simpcraft-staging-oci.internal.ipv4.address;
|
dst = aa.simpcraft-staging-oci.internal.ipv4.address;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
port = 25575;
|
port = 25567;
|
||||||
dst = aa.simpcraft-oci.internal.ipv4.address;
|
dst = aa.kevcraft-oci.internal.ipv4.address;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 25568;
|
||||||
|
dst = aa.kinkcraft-oci.internal.ipv4.address;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 25569;
|
||||||
|
dst = aa.graeme-oci.internal.ipv4.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
# RCON... unsafe?
|
||||||
|
# {
|
||||||
|
# port = 25575;
|
||||||
|
# dst = aa.simpcraft-oci.internal.ipv4.address;
|
||||||
|
# }
|
||||||
|
|
||||||
|
{
|
||||||
|
port = 7777;
|
||||||
|
dst = aa.gam.internal.ipv4.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -204,6 +258,44 @@ rec {
|
|||||||
dst = aa.simpcraft-oci.internal.ipv4.address;
|
dst = aa.simpcraft-oci.internal.ipv4.address;
|
||||||
proto = "udp";
|
proto = "udp";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
port = 25567;
|
||||||
|
dst = aa.kevcraft-oci.internal.ipv4.address;
|
||||||
|
proto = "udp";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 25568;
|
||||||
|
dst = aa.kinkcraft-oci.internal.ipv4.address;
|
||||||
|
proto = "udp";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 25569;
|
||||||
|
dst = aa.graeme-oci.internal.ipv4.address;
|
||||||
|
proto = "udp";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
port = 15636;
|
||||||
|
dst = aa.enshrouded-oci.internal.ipv4.address;
|
||||||
|
proto = "udp";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 15637;
|
||||||
|
dst = aa.enshrouded-oci.internal.ipv4.address;
|
||||||
|
proto = "udp";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
port = qclk.wgPort;
|
||||||
|
dst = aa.qclk.internal.ipv4.address;
|
||||||
|
proto = "udp";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
port = 7777;
|
||||||
|
dst = aa.gam.internal.ipv4.address;
|
||||||
|
proto = "udp";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
fstrimConfig = {
|
fstrimConfig = {
|
||||||
@@ -220,6 +312,9 @@ rec {
|
|||||||
lo = 110;
|
lo = 110;
|
||||||
untrusted = 120;
|
untrusted = 120;
|
||||||
wan = 130;
|
wan = 130;
|
||||||
|
wan-pon = 131;
|
||||||
|
|
||||||
|
pon-isp = 10;
|
||||||
};
|
};
|
||||||
hiMTU = 9000;
|
hiMTU = 9000;
|
||||||
routers = [
|
routers = [
|
||||||
@@ -227,8 +322,8 @@ rec {
|
|||||||
"stream"
|
"stream"
|
||||||
];
|
];
|
||||||
routersPubV4 = [
|
routersPubV4 = [
|
||||||
"109.255.1.246"
|
"109.255.108.88"
|
||||||
"109.255.252.63"
|
"109.255.108.121"
|
||||||
];
|
];
|
||||||
|
|
||||||
prefixes = with lib.my.net.cidr; rec {
|
prefixes = with lib.my.net.cidr; rec {
|
||||||
@@ -277,6 +372,8 @@ rec {
|
|||||||
v6 = host ((1*65536*65536*65536) + 65535) prefixes.as211024.v6;
|
v6 = host ((1*65536*65536*65536) + 65535) prefixes.as211024.v6;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
roceBootModules = [ "ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
britway = {
|
britway = {
|
||||||
@@ -292,6 +389,20 @@ rec {
|
|||||||
assignedV6 = "2001:19f0:7402:128b:5400:04ff:feac:6e06";
|
assignedV6 = "2001:19f0:7402:128b:5400:04ff:feac:6e06";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
britnet = {
|
||||||
|
domain = "bhx1.int.${pubDomain}";
|
||||||
|
pubV4 = "77.74.199.67";
|
||||||
|
vpn = {
|
||||||
|
port = 51820;
|
||||||
|
};
|
||||||
|
prefixes = with lib.my.net.cidr; rec {
|
||||||
|
vpn = {
|
||||||
|
v4 = "10.200.0.0/24";
|
||||||
|
v6 = "fdfb:5ebf:6e84::/64";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
tailscale = {
|
tailscale = {
|
||||||
prefix = {
|
prefix = {
|
||||||
v4 = "100.64.0.0/10";
|
v4 = "100.64.0.0/10";
|
||||||
@@ -327,6 +438,7 @@ rec {
|
|||||||
};
|
};
|
||||||
|
|
||||||
domain = "hentai.engineer";
|
domain = "hentai.engineer";
|
||||||
|
ipv4MTU = 1460;
|
||||||
vpn = {
|
vpn = {
|
||||||
port = 51820;
|
port = 51820;
|
||||||
};
|
};
|
||||||
@@ -335,11 +447,21 @@ rec {
|
|||||||
ctrs.v4 = subnet 4 0 all.v4;
|
ctrs.v4 = subnet 4 0 all.v4;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hillcrest = {
|
||||||
|
vpn.port = 51822;
|
||||||
|
};
|
||||||
|
|
||||||
|
john-valorant = {
|
||||||
|
vpn.port = 51823;
|
||||||
|
};
|
||||||
|
|
||||||
sshKeyFiles = {
|
sshKeyFiles = {
|
||||||
me = ../.keys/me.pub;
|
me = ../.keys/me.pub;
|
||||||
deploy = ../.keys/deploy.pub;
|
deploy = ../.keys/deploy.pub;
|
||||||
rsyncNet = ../.keys/zh2855.rsync.net.pub;
|
rsyncNet = ../.keys/zh2855.rsync.net.pub;
|
||||||
mailcowAcme = ../.keys/mailcow-acme.pub;
|
mailcowAcme = ../.keys/mailcow-acme.pub;
|
||||||
|
harmonia = ../.keys/harmonia.pub;
|
||||||
};
|
};
|
||||||
sshHostKeys = {
|
sshHostKeys = {
|
||||||
mail-vm = ../.keys/mail-vm-host.pub;
|
mail-vm = ../.keys/mail-vm-host.pub;
|
||||||
|
|||||||
+38
-9
@@ -1,11 +1,11 @@
|
|||||||
{ lib }:
|
{ inputs, lib }:
|
||||||
let
|
let
|
||||||
inherit (builtins) length match elemAt filter replaceStrings substring;
|
inherit (builtins) length match elemAt filter replaceStrings substring;
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types
|
genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types
|
||||||
mkOption mkOverride mkForce mkIf mergeEqualOption optional
|
mkOption mkOverride mkForce mkIf mergeEqualOption optional
|
||||||
showWarnings concatStringsSep flatten unique optionalAttrs
|
showWarnings concatStringsSep flatten unique optionalAttrs
|
||||||
mkBefore;
|
mkBefore toLower splitString last;
|
||||||
inherit (lib.flake) defaultSystems;
|
inherit (lib.flake) defaultSystems;
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
@@ -23,7 +23,7 @@ rec {
|
|||||||
|
|
||||||
attrsToNVList = mapAttrsToList nameValuePair;
|
attrsToNVList = mapAttrsToList nameValuePair;
|
||||||
|
|
||||||
inherit (import ./net.nix { inherit lib; }) net;
|
inherit ((import "${inputs.libnetRepo}/lib/netu.nix" { inherit lib; }).lib) net;
|
||||||
dns = import ./dns.nix { inherit lib; };
|
dns = import ./dns.nix { inherit lib; };
|
||||||
c = import ./constants.nix { inherit lib; };
|
c = import ./constants.nix { inherit lib; };
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ rec {
|
|||||||
in mkApp "${app}/bin/${app.meta.mainProgram}";
|
in mkApp "${app}/bin/${app.meta.mainProgram}";
|
||||||
flakePackageOverlay' = flake: pkg: system: (final: prev:
|
flakePackageOverlay' = flake: pkg: system: (final: prev:
|
||||||
let
|
let
|
||||||
pkg' = if pkg != null then flake.packages.${system}.${pkg} else flake.defaultPackage.${system};
|
pkg' = if pkg != null then flake.packages.${system}.${pkg} else flake.packages.${system}.default;
|
||||||
name = if pkg != null then pkg else pkg'.name;
|
name = if pkg != null then pkg else pkg'.name;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -175,11 +175,11 @@ rec {
|
|||||||
};
|
};
|
||||||
|
|
||||||
vm = rec {
|
vm = rec {
|
||||||
lvmDisk' = name: lv: {
|
lvmDisk'' = name: vg: lv: {
|
||||||
inherit name;
|
inherit name;
|
||||||
backend = {
|
backend = {
|
||||||
driver = "host_device";
|
driver = "host_device";
|
||||||
filename = "/dev/main/${lv}";
|
filename = "/dev/${vg}/${lv}";
|
||||||
# It appears this needs to be set on the backend _and_ the format
|
# It appears this needs to be set on the backend _and_ the format
|
||||||
discard = "unmap";
|
discard = "unmap";
|
||||||
};
|
};
|
||||||
@@ -189,7 +189,8 @@ rec {
|
|||||||
};
|
};
|
||||||
frontend = "virtio-blk";
|
frontend = "virtio-blk";
|
||||||
};
|
};
|
||||||
lvmDisk = lv: lvmDisk' lv lv;
|
lvmDisk' = vg: lv: lvmDisk'' lv vg lv;
|
||||||
|
lvmDisk = lvmDisk' "main";
|
||||||
disk = vm: lv: lvmDisk' lv "vm-${vm}-${lv}";
|
disk = vm: lv: lvmDisk' lv "vm-${vm}-${lv}";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -248,10 +249,38 @@ rec {
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
trivial = prev.trivial // {
|
trivial = prev.trivial // {
|
||||||
release = "23.12:u-${prev.trivial.release}";
|
release = "26.06:u-${prev.trivial.release}";
|
||||||
codeName = "Amogus";
|
codeName = "Irritating";
|
||||||
revisionWithDefault = default: self.rev or default;
|
revisionWithDefault = default: self.rev or default;
|
||||||
versionSuffix = ".${date}.${revCode self}:u-${revCode pkgsFlake}";
|
versionSuffix = ".${date}.${revCode self}:u-${revCode pkgsFlake}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
upstreamRelease = last (splitString "-" lib.trivial.release);
|
||||||
|
|
||||||
|
netbootKeaClientClasses = { tftpIP, hostname, systems }:
|
||||||
|
let
|
||||||
|
testIPXE = "substring(option[user-class].hex, 0, 4) == 'iPXE'";
|
||||||
|
in
|
||||||
|
(mapAttrsToList (name: mac: {
|
||||||
|
name = "nixos-${name}";
|
||||||
|
test = "(${testIPXE}) and (hexstring(pkt4.mac, ':') == '${toLower mac}')";
|
||||||
|
next-server = tftpIP;
|
||||||
|
server-hostname = hostname;
|
||||||
|
boot-file-name = "http://${hostname}/systems/${name}/menu.ipxe";
|
||||||
|
}) systems) ++ [
|
||||||
|
{
|
||||||
|
name = "ipxe";
|
||||||
|
test = testIPXE;
|
||||||
|
next-server = tftpIP;
|
||||||
|
server-hostname = hostname;
|
||||||
|
boot-file-name = "http://${hostname}/boot.ipxe";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "efi-x86_64";
|
||||||
|
test = "option[client-system].hex == 0x0007";
|
||||||
|
next-server = tftpIP;
|
||||||
|
server-hostname = hostname;
|
||||||
|
boot-file-name = "ipxe-x86_64.efi";
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
-1322
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,191 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c) pubDomain;
|
||||||
|
inherit (lib.my.c.britnet) domain pubV4 prefixes;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixos.systems.britnet = {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
nixpkgs = "mine";
|
||||||
|
|
||||||
|
assignments = {
|
||||||
|
allhost = {
|
||||||
|
inherit domain;
|
||||||
|
ipv4 = {
|
||||||
|
address = pubV4;
|
||||||
|
mask = 24;
|
||||||
|
gateway = "77.74.199.1";
|
||||||
|
};
|
||||||
|
ipv6 = {
|
||||||
|
address = "2a12:ab46:5344:99::a";
|
||||||
|
gateway = "2a12:ab46:5344::1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
vpn = {
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 1 prefixes.vpn.v4;
|
||||||
|
gateway = null;
|
||||||
|
};
|
||||||
|
ipv6.address = net.cidr.host 1 prefixes.vpn.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkMerge mkForce;
|
||||||
|
inherit (lib.my) networkdAssignment;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
"${modulesPath}/profiles/qemu-guest.nix"
|
||||||
|
];
|
||||||
|
|
||||||
|
config = mkMerge [
|
||||||
|
{
|
||||||
|
boot = {
|
||||||
|
initrd.availableKernelModules = [
|
||||||
|
"ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "ahci" "sr_mod" "virtio_blk"
|
||||||
|
];
|
||||||
|
loader = {
|
||||||
|
systemd-boot.enable = false;
|
||||||
|
grub = {
|
||||||
|
enable = true;
|
||||||
|
device = "/dev/vda";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems = {
|
||||||
|
"/boot" = {
|
||||||
|
device = "/dev/disk/by-uuid/457444a1-81dd-4934-960c-650ad16c92b5";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
"/nix" = {
|
||||||
|
device = "/dev/disk/by-uuid/992c0c79-5be6-45b6-bc30-dc82e3ec082a";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
"/persist" = {
|
||||||
|
device = "/dev/disk/by-uuid/f020a955-54d5-4098-98ba-d3615781d96a";
|
||||||
|
fsType = "ext4";
|
||||||
|
neededForBoot = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
systemPackages = with pkgs; [
|
||||||
|
wireguard-tools
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
iperf3 = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
tailscale = {
|
||||||
|
enable = true;
|
||||||
|
authKeyFile = config.age.secrets."tailscale-auth.key".path;
|
||||||
|
openFirewall = true;
|
||||||
|
interfaceName = "tailscale0";
|
||||||
|
extraUpFlags = [
|
||||||
|
"--operator=${config.my.user.config.name}"
|
||||||
|
"--login-server=https://hs.nul.ie"
|
||||||
|
"--netfilter-mode=off"
|
||||||
|
"--advertise-exit-node"
|
||||||
|
"--accept-routes=false"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking = { inherit domain; };
|
||||||
|
|
||||||
|
systemd.network = {
|
||||||
|
netdevs = {
|
||||||
|
"30-wg0" = {
|
||||||
|
netdevConfig = {
|
||||||
|
Name = "wg0";
|
||||||
|
Kind = "wireguard";
|
||||||
|
};
|
||||||
|
wireguardConfig = {
|
||||||
|
PrivateKeyFile = config.age.secrets."britnet/wg.key".path;
|
||||||
|
ListenPort = lib.my.c.britnet.vpn.port;
|
||||||
|
};
|
||||||
|
wireguardPeers = [
|
||||||
|
{
|
||||||
|
PublicKey = "EfPwREfZ/q3ogHXBIqFZh4k/1NRJRyq4gBkBXtegNkE=";
|
||||||
|
AllowedIPs = [
|
||||||
|
(net.cidr.host 10 prefixes.vpn.v4)
|
||||||
|
(net.cidr.host 10 prefixes.vpn.v6)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
links = {
|
||||||
|
"10-veth0" = {
|
||||||
|
matchConfig.PermanentMACAddress = "00:db:d9:62:68:1a";
|
||||||
|
linkConfig.Name = "veth0";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networks = {
|
||||||
|
"20-veth0" = mkMerge [
|
||||||
|
(networkdAssignment "veth0" assignments.allhost)
|
||||||
|
{
|
||||||
|
dns = [ "1.1.1.1" "1.0.0.1" ];
|
||||||
|
routes = [
|
||||||
|
{
|
||||||
|
# Gateway is on a different network for some reason...
|
||||||
|
Destination = "2a12:ab46:5344::1";
|
||||||
|
Scope = "link";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
"30-wg0" = mkMerge [
|
||||||
|
(networkdAssignment "wg0" assignments.vpn)
|
||||||
|
{
|
||||||
|
networkConfig.IPv6AcceptRA = mkForce false;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
server.enable = true;
|
||||||
|
secrets = {
|
||||||
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJIEx+1EC/lN6WKIaOB+O5LJgVHRK962YpZEPQg/m78O";
|
||||||
|
files = {
|
||||||
|
"tailscale-auth.key" = {};
|
||||||
|
"britnet/wg.key" = {
|
||||||
|
owner = "systemd-network";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
firewall = {
|
||||||
|
udp.allowed = [ lib.my.c.britnet.vpn.port ];
|
||||||
|
trustedInterfaces = [ "tailscale0" ];
|
||||||
|
extraRules = ''
|
||||||
|
table inet filter {
|
||||||
|
chain forward {
|
||||||
|
iifname wg0 oifname veth0 accept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table inet nat {
|
||||||
|
chain postrouting {
|
||||||
|
iifname { tailscale0, wg0 } oifname veth0 snat ip to ${assignments.allhost.ipv4.address}
|
||||||
|
iifname { tailscale0, wg0 } oifname veth0 snat ip6 to ${assignments.allhost.ipv6.address}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,23 +11,24 @@ in
|
|||||||
config = {
|
config = {
|
||||||
my = {
|
my = {
|
||||||
secrets.files."britway/bgp-password-vultr.conf" = {
|
secrets.files."britway/bgp-password-vultr.conf" = {
|
||||||
owner = "bird2";
|
owner = "bird";
|
||||||
group = "bird2";
|
group = "bird";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.etc."bird/vultr-password.conf".source = config.age.secrets."britway/bgp-password-vultr.conf".path;
|
environment.etc."bird/vultr-password.conf".source = config.age.secrets."britway/bgp-password-vultr.conf".path;
|
||||||
|
|
||||||
systemd = {
|
systemd = {
|
||||||
services.bird2.after = [ "systemd-networkd-wait-online@veth0.service" ];
|
services.bird.after = [ "systemd-networkd-wait-online@veth0.service" ];
|
||||||
network = {
|
network = {
|
||||||
config.networkConfig.ManageForeignRoutes = false;
|
config.networkConfig.ManageForeignRoutes = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
bird2 = {
|
bird = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = pkgs.bird2;
|
||||||
preCheckConfig = ''
|
preCheckConfig = ''
|
||||||
echo '"dummy"' > vultr-password.conf
|
echo '"dummy"' > vultr-password.conf
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ in
|
|||||||
{
|
{
|
||||||
matchConfig.Name = "as211024";
|
matchConfig.Name = "as211024";
|
||||||
networkConfig.IPv6AcceptRA = mkForce false;
|
networkConfig.IPv6AcceptRA = mkForce false;
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = lib.my.c.colony.prefixes.all.v4;
|
Destination = lib.my.c.colony.prefixes.all.v4;
|
||||||
Gateway = allAssignments.estuary.as211024.ipv4.address;
|
Gateway = allAssignments.estuary.as211024.ipv4.address;
|
||||||
@@ -123,7 +123,7 @@ in
|
|||||||
Table = "ts-extra";
|
Table = "ts-extra";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [
|
routingPolicyRules = [
|
||||||
{
|
{
|
||||||
IncomingInterface = "tailscale0";
|
IncomingInterface = "tailscale0";
|
||||||
To = lib.my.c.colony.prefixes.all.v6;
|
To = lib.my.c.colony.prefixes.all.v6;
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ in
|
|||||||
config = {
|
config = {
|
||||||
my = {
|
my = {
|
||||||
secrets.files = {
|
secrets.files = {
|
||||||
"dhparams.pem" = {
|
|
||||||
owner = "acme";
|
|
||||||
group = "acme";
|
|
||||||
mode = "440";
|
|
||||||
};
|
|
||||||
"britway/cloudflare-credentials.conf" = {
|
"britway/cloudflare-credentials.conf" = {
|
||||||
owner = "acme";
|
owner = "acme";
|
||||||
group = "acme";
|
group = "acme";
|
||||||
@@ -45,7 +40,7 @@ in
|
|||||||
"*.${pubDomain}"
|
"*.${pubDomain}"
|
||||||
];
|
];
|
||||||
dnsProvider = "cloudflare";
|
dnsProvider = "cloudflare";
|
||||||
credentialsFile = config.age.secrets."britway/cloudflare-credentials.conf".path;
|
environmentFile = config.age.secrets."britway/cloudflare-credentials.conf".path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -58,7 +53,6 @@ in
|
|||||||
logError = "stderr info";
|
logError = "stderr info";
|
||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
serverTokens = true;
|
serverTokens = true;
|
||||||
sslDhparam = config.age.secrets."dhparams.pem".path;
|
|
||||||
|
|
||||||
# Based on recommended*Settings, but probably better to be explicit about these
|
# Based on recommended*Settings, but probably better to be explicit about these
|
||||||
appendHttpConfig = ''
|
appendHttpConfig = ''
|
||||||
@@ -80,7 +74,7 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
"ts.${pubDomain}" = {
|
"hs.${pubDomain}" = {
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://localhost:${toString config.services.headscale.port}";
|
proxyPass = "http://localhost:${toString config.services.headscale.port}";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
|
|||||||
@@ -4,20 +4,6 @@ let
|
|||||||
inherit (lib.my.c) pubDomain;
|
inherit (lib.my.c) pubDomain;
|
||||||
inherit (lib.my.c.britway) prefixes domain;
|
inherit (lib.my.c.britway) prefixes domain;
|
||||||
|
|
||||||
# Can't use overrideAttrs because we need to override `vendorHash` within `buildGoModule`
|
|
||||||
headscale = pkgs.headscale.override {
|
|
||||||
buildGoModule = args: pkgs.buildGoModule (args // rec {
|
|
||||||
version = "0.23.0-alpha2";
|
|
||||||
src = pkgs.fetchFromGitHub {
|
|
||||||
owner = "juanfont";
|
|
||||||
repo = "headscale";
|
|
||||||
rev = "v${version}";
|
|
||||||
hash = "sha256-sz+uQyyq/5YYDe5I44x5x2nvd48swAhNlInB8KZYvDo=";
|
|
||||||
};
|
|
||||||
vendorHash = "sha256-u9AmJguQ5dnJpfhOeLN43apvMHuraOrJhvlEIp9RoIc=";
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
advRoutes = concatStringsSep "," [
|
advRoutes = concatStringsSep "," [
|
||||||
lib.my.c.home.prefixes.all.v4
|
lib.my.c.home.prefixes.all.v4
|
||||||
lib.my.c.home.prefixes.all.v6
|
lib.my.c.home.prefixes.all.v6
|
||||||
@@ -39,19 +25,21 @@ in
|
|||||||
services = {
|
services = {
|
||||||
headscale = {
|
headscale = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = headscale;
|
|
||||||
settings = {
|
settings = {
|
||||||
disable_check_updates = true;
|
disable_check_updates = true;
|
||||||
unix_socket_permission = "0770";
|
unix_socket_permission = "0770";
|
||||||
server_url = "https://ts.${pubDomain}";
|
server_url = "https://hs.${pubDomain}";
|
||||||
db_type = "sqlite3";
|
database = {
|
||||||
db_path = "/var/lib/headscale/db.sqlite3";
|
type = "sqlite3";
|
||||||
|
sqlite.path = "/var/lib/headscale/db.sqlite3";
|
||||||
|
};
|
||||||
noise.private_key_path = "/var/lib/headscale/noise_private.key";
|
noise.private_key_path = "/var/lib/headscale/noise_private.key";
|
||||||
ip_prefixes = with lib.my.c.tailscale.prefix; [ v4 v6 ];
|
prefixes = with lib.my.c.tailscale.prefix; { inherit v4 v6; };
|
||||||
dns_config = {
|
dns = {
|
||||||
|
override_local_dns = false;
|
||||||
# Use IPs that will route inside the VPN to prevent interception
|
# Use IPs that will route inside the VPN to prevent interception
|
||||||
# (e.g. DNS rebinding filtering)
|
# (e.g. DNS rebinding filtering)
|
||||||
restricted_nameservers = {
|
nameservers.split = {
|
||||||
"${domain}" = pubNameservers;
|
"${domain}" = pubNameservers;
|
||||||
"${lib.my.c.colony.domain}" = with allAssignments.estuary.base; [
|
"${lib.my.c.colony.domain}" = with allAssignments.estuary.base; [
|
||||||
ipv4.address ipv6.address
|
ipv4.address ipv6.address
|
||||||
@@ -65,7 +53,6 @@ in
|
|||||||
};
|
};
|
||||||
magic_dns = true;
|
magic_dns = true;
|
||||||
base_domain = "ts.${pubDomain}";
|
base_domain = "ts.${pubDomain}";
|
||||||
override_local_dns = false;
|
|
||||||
};
|
};
|
||||||
oidc = {
|
oidc = {
|
||||||
only_start_if_oidc_is_available = true;
|
only_start_if_oidc_is_available = true;
|
||||||
@@ -85,7 +72,7 @@ in
|
|||||||
interfaceName = "tailscale0";
|
interfaceName = "tailscale0";
|
||||||
extraUpFlags = [
|
extraUpFlags = [
|
||||||
"--operator=${config.my.user.config.name}"
|
"--operator=${config.my.user.config.name}"
|
||||||
"--login-server=https://ts.nul.ie"
|
"--login-server=https://hs.nul.ie"
|
||||||
"--netfilter-mode=off"
|
"--netfilter-mode=off"
|
||||||
"--advertise-exit-node"
|
"--advertise-exit-node"
|
||||||
"--advertise-routes=${advRoutes}"
|
"--advertise-routes=${advRoutes}"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c.colony) domain prefixes firewallForwards;
|
inherit (lib.my.c.colony) domain prefixes custRouting firewallForwards;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./vms ];
|
imports = [ ./vms ];
|
||||||
@@ -60,8 +60,8 @@ in
|
|||||||
kernelPackages = (lib.my.c.kernel.lts pkgs).extend (self: super: {
|
kernelPackages = (lib.my.c.kernel.lts pkgs).extend (self: super: {
|
||||||
kernel = super.kernel.override {
|
kernel = super.kernel.override {
|
||||||
structuredExtraConfig = with lib.kernel; {
|
structuredExtraConfig = with lib.kernel; {
|
||||||
#SOME_OPT = yes;
|
ACPI_APEI_PCIEAER = yes;
|
||||||
#A_MOD = module;
|
PCIEAER = yes;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -150,12 +150,12 @@ in
|
|||||||
"serial-getty@ttyS1".enable = true;
|
"serial-getty@ttyS1".enable = true;
|
||||||
lvm-activate-main = {
|
lvm-activate-main = {
|
||||||
description = "Activate remaining LVs";
|
description = "Activate remaining LVs";
|
||||||
before = [ "local-fs-pre.target" ];
|
unitConfig.DefaultDependencies = false;
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
ExecStart = "${pkgs.lvm2.bin}/bin/vgchange -aay main";
|
ExecStart = "${pkgs.lvm2.bin}/bin/vgchange -aay main";
|
||||||
};
|
};
|
||||||
wantedBy = [ "sysinit.target" ];
|
wantedBy = [ "local-fs-pre.target" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
rsync-lvm-meta = {
|
rsync-lvm-meta = {
|
||||||
@@ -252,10 +252,10 @@ in
|
|||||||
};
|
};
|
||||||
ipv6Prefixes = [
|
ipv6Prefixes = [
|
||||||
{
|
{
|
||||||
ipv6PrefixConfig.Prefix = prefixes.vms.v6;
|
Prefix = prefixes.vms.v6;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = prefixes.ctrs.v4;
|
Destination = prefixes.ctrs.v4;
|
||||||
Gateway = allAssignments.shill.routing.ipv4.address;
|
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||||
@@ -264,10 +264,12 @@ in
|
|||||||
Destination = prefixes.ctrs.v6;
|
Destination = prefixes.ctrs.v6;
|
||||||
Gateway = allAssignments.shill.internal.ipv6.address;
|
Gateway = allAssignments.shill.internal.ipv6.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Destination = allAssignments.shill.internal.ipv4.address;
|
Destination = allAssignments.shill.internal.ipv4.address;
|
||||||
Gateway = allAssignments.shill.routing.ipv4.address;
|
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Destination = lib.my.c.tailscale.prefix.v4;
|
Destination = lib.my.c.tailscale.prefix.v4;
|
||||||
Gateway = allAssignments.shill.routing.ipv4.address;
|
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||||
@@ -276,6 +278,15 @@ in
|
|||||||
Destination = lib.my.c.tailscale.prefix.v6;
|
Destination = lib.my.c.tailscale.prefix.v6;
|
||||||
Gateway = allAssignments.shill.internal.ipv6.address;
|
Gateway = allAssignments.shill.internal.ipv6.address;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Destination = prefixes.qclk.v4;
|
||||||
|
Gateway = allAssignments.shill.routing.ipv4.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Destination = prefixes.jam.v6;
|
||||||
|
Gateway = allAssignments.shill.internal.ipv6.address;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Destination = prefixes.oci.v4;
|
Destination = prefixes.oci.v4;
|
||||||
@@ -307,7 +318,7 @@ in
|
|||||||
"90-vm-mail" = {
|
"90-vm-mail" = {
|
||||||
matchConfig.Name = "vm-mail";
|
matchConfig.Name = "vm-mail";
|
||||||
address = [
|
address = [
|
||||||
(net.cidr.subnet 8 1 prefixes.cust.v4)
|
"${custRouting.mail-vm}/32"
|
||||||
prefixes.mail.v6
|
prefixes.mail.v6
|
||||||
];
|
];
|
||||||
networkConfig = {
|
networkConfig = {
|
||||||
@@ -316,10 +327,10 @@ in
|
|||||||
};
|
};
|
||||||
ipv6Prefixes = [
|
ipv6Prefixes = [
|
||||||
{
|
{
|
||||||
ipv6PrefixConfig.Prefix = prefixes.mail.v6;
|
Prefix = prefixes.mail.v6;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = prefixes.mail.v4;
|
Destination = prefixes.mail.v4;
|
||||||
Scope = "link";
|
Scope = "link";
|
||||||
@@ -330,7 +341,7 @@ in
|
|||||||
"90-vm-darts" = {
|
"90-vm-darts" = {
|
||||||
matchConfig.Name = "vm-darts";
|
matchConfig.Name = "vm-darts";
|
||||||
address = [
|
address = [
|
||||||
(net.cidr.subnet 8 2 prefixes.cust.v4)
|
"${custRouting.darts-vm}/32"
|
||||||
prefixes.darts.v6
|
prefixes.darts.v6
|
||||||
];
|
];
|
||||||
networkConfig = {
|
networkConfig = {
|
||||||
@@ -339,10 +350,10 @@ in
|
|||||||
};
|
};
|
||||||
ipv6Prefixes = [
|
ipv6Prefixes = [
|
||||||
{
|
{
|
||||||
ipv6PrefixConfig.Prefix = prefixes.darts.v6;
|
Prefix = prefixes.darts.v6;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = prefixes.darts.v4;
|
Destination = prefixes.darts.v4;
|
||||||
Scope = "link";
|
Scope = "link";
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
# Kernel Same-Page Merging to attempt memory usage reduction
|
||||||
|
hardware.ksm.enable = false;
|
||||||
|
|
||||||
systemd = {
|
systemd = {
|
||||||
network = {
|
network = {
|
||||||
links = {
|
links = {
|
||||||
@@ -130,7 +133,8 @@
|
|||||||
|
|
||||||
(vm.lvmDisk "media")
|
(vm.lvmDisk "media")
|
||||||
(vm.lvmDisk "minio")
|
(vm.lvmDisk "minio")
|
||||||
(vm.lvmDisk "nix-atticd")
|
(vm.lvmDisk "nix-cache")
|
||||||
|
(vm.lvmDisk "jam")
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -209,6 +213,7 @@
|
|||||||
drives = [
|
drives = [
|
||||||
(mkMerge [ (vm.disk "darts" "root") { frontendOpts.bootindex = 0; } ])
|
(mkMerge [ (vm.disk "darts" "root") { frontendOpts.bootindex = 0; } ])
|
||||||
(vm.lvmDisk' "media" "darts-media")
|
(vm.lvmDisk' "media" "darts-media")
|
||||||
|
(vm.lvmDisk' "ext" "darts-ext")
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ in
|
|||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
services = {
|
services = {
|
||||||
bird2 = {
|
bird = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = pkgs.bird2;
|
||||||
# TODO: Clean up and modularise
|
# TODO: Clean up and modularise
|
||||||
config = ''
|
config = ''
|
||||||
define OWNAS = 211024;
|
define OWNAS = 211024;
|
||||||
@@ -232,59 +233,88 @@ in
|
|||||||
neighbor 2a07:cd40:1::9 as 202413;
|
neighbor 2a07:cd40:1::9 as 202413;
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol bgp ixp4_frysix_rs1 from ixp_bgp4 {
|
protocol bgp ixp4_frysix_rs3 from ixp_bgp4 {
|
||||||
description "Frys-IX route server 1 (IPv4)";
|
description "Frys-IX route server 3 (IPv4)";
|
||||||
neighbor 185.1.203.253 as 56393;
|
neighbor 185.1.160.255 as 56393;
|
||||||
}
|
}
|
||||||
protocol bgp ixp6_frysix_rs1 from ixp_bgp6 {
|
protocol bgp ixp6_frysix_rs3 from ixp_bgp6 {
|
||||||
description "Frys-IX route server 1 (IPv6)";
|
description "Frys-IX route server 3 (IPv6)";
|
||||||
neighbor 2001:7f8:10f::dc49:253 as 56393;
|
neighbor 2001:7f8:10f::dc49:1 as 56393;
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol bgp ixp4_frysix_rs2 from ixp_bgp4 {
|
protocol bgp ixp4_frysix_rs4 from ixp_bgp4 {
|
||||||
description "Frys-IX route server 2 (IPv4)";
|
description "Frys-IX route server 4 (IPv4)";
|
||||||
neighbor 185.1.203.254 as 56393;
|
neighbor 185.1.161.0 as 56393;
|
||||||
}
|
}
|
||||||
protocol bgp ixp6_frysix_rs2 from ixp_bgp6 {
|
protocol bgp ixp6_frysix_rs4 from ixp_bgp6 {
|
||||||
description "Frys-IX route server 2 (IPv6)";
|
description "Frys-IX route server 4 (IPv6)";
|
||||||
neighbor 2001:7f8:10f::dc49:254 as 56393;
|
neighbor 2001:7f8:10f::dc49:2 as 56393;
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol bgp peer4_frysix_luje from peer_bgp4 {
|
protocol bgp peer4_frysix_luje from peer_bgp4 {
|
||||||
description "LUJE.net (on Frys-IX, IPv4)";
|
description "LUJE.net (on Frys-IX, IPv4)";
|
||||||
neighbor 185.1.203.152 as 212855;
|
neighbor 185.1.160.152 as 212855;
|
||||||
}
|
}
|
||||||
protocol bgp peer6_frysix_luje from peer_bgp6 {
|
protocol bgp peer6_frysix_luje from peer_bgp6 {
|
||||||
description "LUJE.net (on Frys-IX, IPv6)";
|
description "LUJE.net (on Frys-IX, IPv6)";
|
||||||
neighbor 2001:7f8:10f::3:3f95:152 as 212855;
|
neighbor 2001:7f8:10f::3:3f95:152 as 212855;
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol bgp peer4_frysix_he from peer_bgp4 {
|
protocol bgp peer4_frysix_he from peer_bgp4 {
|
||||||
description "Hurricane Electric (on Frys-IX, IPv4)";
|
description "Hurricane Electric (on Frys-IX, IPv4)";
|
||||||
neighbor 185.1.203.154 as 6939;
|
neighbor 185.1.160.154 as 6939;
|
||||||
}
|
}
|
||||||
protocol bgp peer4_frysix_cloudflare from peer_bgp4 {
|
|
||||||
description "Cloudflare (on Frys-IX, IPv4)";
|
protocol bgp peer4_frysix_cloudflare1 from peer_bgp4 {
|
||||||
neighbor 185.1.203.217 as 13335;
|
description "Cloudflare 1 (on Frys-IX, IPv4)";
|
||||||
|
neighbor 185.1.160.217 as 13335;
|
||||||
}
|
}
|
||||||
protocol bgp peer6_frysix_cloudflare from peer_bgp6 {
|
protocol bgp peer4_frysix_cloudflare2 from peer_bgp4 {
|
||||||
description "Cloudflare (on Frys-IX, IPv6)";
|
description "Cloudflare 2 (on Frys-IX, IPv4)";
|
||||||
|
neighbor 185.1.160.109 as 13335;
|
||||||
|
}
|
||||||
|
protocol bgp peer6_frysix_cloudflare1 from peer_bgp6 {
|
||||||
|
description "Cloudflare 1 (on Frys-IX, IPv6)";
|
||||||
neighbor 2001:7f8:10f::3417:217 as 13335;
|
neighbor 2001:7f8:10f::3417:217 as 13335;
|
||||||
}
|
}
|
||||||
|
protocol bgp peer6_frysix_cloudflare2 from peer_bgp6 {
|
||||||
|
description "Cloudflare 2 (on Frys-IX, IPv6)";
|
||||||
|
neighbor 2001:7f8:10f::3417:109 as 13335;
|
||||||
|
}
|
||||||
|
|
||||||
protocol bgp peer4_frysix_jurrian from peer_bgp4 {
|
protocol bgp peer4_frysix_jurrian from peer_bgp4 {
|
||||||
description "AS212635 aka jurrian (on Frys-IX, IPv4)";
|
description "AS212635 aka jurrian (on Frys-IX, IPv4)";
|
||||||
neighbor 185.1.203.134 as 212635;
|
neighbor 185.1.160.134 as 212635;
|
||||||
}
|
}
|
||||||
protocol bgp peer6_frysix_jurrian from peer_bgp6 {
|
protocol bgp peer6_frysix_jurrian from peer_bgp6 {
|
||||||
description "AS212635 aka jurrian (on Frys-IX, IPv6)";
|
description "AS212635 aka jurrian (on Frys-IX, IPv6)";
|
||||||
neighbor 2001:7f8:10f::3:3e9b:134 as 212635;
|
neighbor 2001:7f8:10f::3:3e9b:134 as 212635;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol bgp peer4_nlix_meta1 from peer_bgp4 {
|
||||||
|
description "Meta 1 (on NL-ix, IPv4)";
|
||||||
|
neighbor 193.239.116.147 as 32934;
|
||||||
|
}
|
||||||
|
protocol bgp peer4_nlix_meta2 from peer_bgp4 {
|
||||||
|
description "Meta 2 (on NL-ix, IPv4)";
|
||||||
|
neighbor 193.239.116.148 as 32934;
|
||||||
|
}
|
||||||
|
protocol bgp peer6_nlix_meta1 from peer_bgp6 {
|
||||||
|
description "Meta 1 (on NL-ix, IPv6)";
|
||||||
|
neighbor 2001:7f8:13::a503:2934:1 as 32934;
|
||||||
|
}
|
||||||
|
protocol bgp peer6_nlix_meta2 from peer_bgp6 {
|
||||||
|
description "Meta 2 (on NL-ix, IPv6)";
|
||||||
|
neighbor 2001:7f8:13::a503:2934:2 as 32934;
|
||||||
|
}
|
||||||
|
|
||||||
protocol bgp peer4_frysix_meta1 from peer_bgp4 {
|
protocol bgp peer4_frysix_meta1 from peer_bgp4 {
|
||||||
description "Meta 1 (on Frys-IX, IPv4)";
|
description "Meta 1 (on Frys-IX, IPv4)";
|
||||||
neighbor 185.1.203.225 as 32934;
|
neighbor 185.1.160.225 as 32934;
|
||||||
}
|
}
|
||||||
protocol bgp peer4_frysix_meta2 from peer_bgp4 {
|
protocol bgp peer4_frysix_meta2 from peer_bgp4 {
|
||||||
description "Meta 2 (on Frys-IX, IPv4)";
|
description "Meta 2 (on Frys-IX, IPv4)";
|
||||||
neighbor 185.1.203.226 as 32934;
|
neighbor 185.1.160.226 as 32934;
|
||||||
}
|
}
|
||||||
protocol bgp peer6_frysix_meta1 from peer_bgp6 {
|
protocol bgp peer6_frysix_meta1 from peer_bgp6 {
|
||||||
description "Meta 1 (on Frys-IX, IPv6)";
|
description "Meta 1 (on Frys-IX, IPv6)";
|
||||||
@@ -317,36 +347,36 @@ in
|
|||||||
ipv6 { preference (PREFIXP-1); };
|
ipv6 { preference (PREFIXP-1); };
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol bgp peer4_nlix_cloudflare1 from peer_bgp4 {
|
# protocol bgp peer4_nlix_cloudflare1 from peer_bgp4 {
|
||||||
description "Cloudflare NL-ix 1 (IPv4)";
|
# description "Cloudflare NL-ix 1 (IPv4)";
|
||||||
neighbor 193.239.117.14 as 13335;
|
# neighbor 193.239.117.14 as 13335;
|
||||||
ipv4 { preference (PREFPEER-1); };
|
# ipv4 { preference (PREFPEER-1); };
|
||||||
}
|
# }
|
||||||
protocol bgp peer4_nlix_cloudflare2 from peer_bgp4 {
|
# protocol bgp peer4_nlix_cloudflare2 from peer_bgp4 {
|
||||||
description "Cloudflare NL-ix 2 (IPv4)";
|
# description "Cloudflare NL-ix 2 (IPv4)";
|
||||||
neighbor 193.239.117.114 as 13335;
|
# neighbor 193.239.117.114 as 13335;
|
||||||
ipv4 { preference (PREFPEER-1); };
|
# ipv4 { preference (PREFPEER-1); };
|
||||||
}
|
# }
|
||||||
protocol bgp peer4_nlix_cloudflare3 from peer_bgp4 {
|
# protocol bgp peer4_nlix_cloudflare3 from peer_bgp4 {
|
||||||
description "Cloudflare NL-ix 3 (IPv4)";
|
# description "Cloudflare NL-ix 3 (IPv4)";
|
||||||
neighbor 193.239.118.138 as 13335;
|
# neighbor 193.239.118.138 as 13335;
|
||||||
ipv4 { preference (PREFPEER-1); };
|
# ipv4 { preference (PREFPEER-1); };
|
||||||
}
|
# }
|
||||||
protocol bgp peer6_nlix_cloudflare1 from peer_bgp6 {
|
# protocol bgp peer6_nlix_cloudflare1 from peer_bgp6 {
|
||||||
description "Cloudflare NL-ix 1 (IPv6)";
|
# description "Cloudflare NL-ix 1 (IPv6)";
|
||||||
neighbor 2001:7f8:13::a501:3335:1 as 13335;
|
# neighbor 2001:7f8:13::a501:3335:1 as 13335;
|
||||||
ipv6 { preference (PREFPEER-1); };
|
# ipv6 { preference (PREFPEER-1); };
|
||||||
}
|
# }
|
||||||
protocol bgp peer6_nlix_cloudflare2 from peer_bgp6 {
|
# protocol bgp peer6_nlix_cloudflare2 from peer_bgp6 {
|
||||||
description "Cloudflare NL-ix 2 (IPv6)";
|
# description "Cloudflare NL-ix 2 (IPv6)";
|
||||||
neighbor 2001:7f8:13::a501:3335:2 as 13335;
|
# neighbor 2001:7f8:13::a501:3335:2 as 13335;
|
||||||
ipv6 { preference (PREFPEER-1); };
|
# ipv6 { preference (PREFPEER-1); };
|
||||||
}
|
# }
|
||||||
protocol bgp peer6_nlix_cloudflare3 from peer_bgp6 {
|
# protocol bgp peer6_nlix_cloudflare3 from peer_bgp6 {
|
||||||
description "Cloudflare NL-ix 3 (IPv6)";
|
# description "Cloudflare NL-ix 3 (IPv6)";
|
||||||
neighbor 2001:7f8:13::a501:3335:3 as 13335;
|
# neighbor 2001:7f8:13::a501:3335:3 as 13335;
|
||||||
ipv6 { preference (PREFPEER-1); };
|
# ipv6 { preference (PREFPEER-1); };
|
||||||
}
|
# }
|
||||||
protocol bgp peer4_nlix_jurrian from peer_bgp4 {
|
protocol bgp peer4_nlix_jurrian from peer_bgp4 {
|
||||||
description "AS212635 aka jurrian (on NL-ix, IPv4)";
|
description "AS212635 aka jurrian (on NL-ix, IPv4)";
|
||||||
neighbor 193.239.117.55 as 212635;
|
neighbor 193.239.117.55 as 212635;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ in
|
|||||||
vpns = {
|
vpns = {
|
||||||
l2 = {
|
l2 = {
|
||||||
as211024 = {
|
as211024 = {
|
||||||
|
udpEncapsulation = true;
|
||||||
vni = 211024;
|
vni = 211024;
|
||||||
security.enable = true;
|
security.enable = true;
|
||||||
peers = {
|
peers = {
|
||||||
@@ -103,11 +104,9 @@ in
|
|||||||
lvm = {
|
lvm = {
|
||||||
dmeventd.enable = true;
|
dmeventd.enable = true;
|
||||||
};
|
};
|
||||||
resolved = {
|
resolved.settings.Resolve = {
|
||||||
llmnr = "false";
|
LLMNR = false;
|
||||||
extraConfig = ''
|
MulticastDNS = false;
|
||||||
MulticastDNS=false
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
netdata.enable = true;
|
netdata.enable = true;
|
||||||
|
|
||||||
@@ -163,11 +162,47 @@ in
|
|||||||
};
|
};
|
||||||
wireguardPeers = [
|
wireguardPeers = [
|
||||||
{
|
{
|
||||||
wireguardPeerConfig = {
|
PublicKey = "7N9YdQaCMWWIwAnW37vrthm9ZpbnG4Lx3gheHeRYz2E=";
|
||||||
PublicKey = "7N9YdQaCMWWIwAnW37vrthm9ZpbnG4Lx3gheHeRYz2E=";
|
AllowedIPs = [ allAssignments.kelder.estuary.ipv4.address ];
|
||||||
AllowedIPs = [ allAssignments.kelder.estuary.ipv4.address ];
|
PersistentKeepalive = 25;
|
||||||
PersistentKeepalive = 25;
|
}
|
||||||
};
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"30-hillcrest" = {
|
||||||
|
netdevConfig = {
|
||||||
|
Name = "hillcrest";
|
||||||
|
Kind = "wireguard";
|
||||||
|
};
|
||||||
|
wireguardConfig = {
|
||||||
|
PrivateKeyFile = config.age.secrets."estuary/hillcrest-wg.key".path;
|
||||||
|
ListenPort = lib.my.c.hillcrest.vpn.port;
|
||||||
|
};
|
||||||
|
wireguardPeers = [
|
||||||
|
{
|
||||||
|
PublicKey = "+67Ks+ZRk1ssNCfg5BFKmIE9NtLasAxRE6XMqufx5GY=";
|
||||||
|
AllowedIPs = [ (net.cidr.host 2 prefixes.hillcrest.v4) ];
|
||||||
|
PersistentKeepalive = 25;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"30-john-valorant" = {
|
||||||
|
netdevConfig = {
|
||||||
|
Name = "john-valorant";
|
||||||
|
Kind = "wireguard";
|
||||||
|
};
|
||||||
|
wireguardConfig = {
|
||||||
|
PrivateKeyFile = config.age.secrets."estuary/john-valorant-wg.key".path;
|
||||||
|
ListenPort = lib.my.c.john-valorant.vpn.port;
|
||||||
|
};
|
||||||
|
wireguardPeers = [
|
||||||
|
{
|
||||||
|
PublicKey = "xyqKF0yOAv1bObN1paL2vATFh77pdFfvN+JmuAxaTCk=";
|
||||||
|
AllowedIPs = [ (net.cidr.host 2 prefixes.john-valorant.v4) ];
|
||||||
|
PersistentKeepalive = 25;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -219,7 +254,7 @@ in
|
|||||||
in
|
in
|
||||||
mkMerge
|
mkMerge
|
||||||
[
|
[
|
||||||
(mkIXPConfig "frys-ix" "185.1.203.196/24" "2001:7f8:10f::3:3850:196/64")
|
(mkIXPConfig "frys-ix" "185.1.160.196/23" "2001:7f8:10f::3:3850:196/64")
|
||||||
(mkIXPConfig "nl-ix" "193.239.116.145/22" "2001:7f8:13::a521:1024:1/64")
|
(mkIXPConfig "nl-ix" "193.239.116.145/22" "2001:7f8:13::a521:1024:1/64")
|
||||||
(mkIXPConfig "fogixp" "185.1.147.159/24" "2001:7f8:ca:1::159/64")
|
(mkIXPConfig "fogixp" "185.1.147.159/24" "2001:7f8:ca:1::159/64")
|
||||||
{
|
{
|
||||||
@@ -277,47 +312,51 @@ in
|
|||||||
};
|
};
|
||||||
ipv6Prefixes = [
|
ipv6Prefixes = [
|
||||||
{
|
{
|
||||||
ipv6PrefixConfig.Prefix = prefixes.base.v6;
|
Prefix = prefixes.base.v6;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
routes = map (r: { routeConfig = r; }) (flatten
|
routes = flatten ([
|
||||||
([
|
{
|
||||||
{
|
Destination = prefixes.vip1;
|
||||||
Destination = prefixes.vip1;
|
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
}
|
||||||
}
|
{
|
||||||
{
|
Destination = prefixes.vip3;
|
||||||
Destination = prefixes.vip3;
|
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
}
|
||||||
}
|
{
|
||||||
{
|
Destination = prefixes.darts.v4;
|
||||||
Destination = prefixes.darts.v4;
|
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
}
|
||||||
}
|
{
|
||||||
{
|
Destination = prefixes.cust.v6;
|
||||||
Destination = prefixes.cust.v6;
|
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Destination = lib.my.c.tailscale.prefix.v4;
|
Destination = lib.my.c.tailscale.prefix.v4;
|
||||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Destination = lib.my.c.tailscale.prefix.v6;
|
Destination = lib.my.c.tailscale.prefix.v6;
|
||||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||||
}
|
}
|
||||||
] ++
|
|
||||||
(map (pName: [
|
{
|
||||||
{
|
Destination = prefixes.qclk.v4;
|
||||||
Gateway = allAssignments.colony.routing.ipv4.address;
|
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||||
Destination = prefixes."${pName}".v4;
|
}
|
||||||
}
|
] ++
|
||||||
{
|
(map (pName: [
|
||||||
Destination = prefixes."${pName}".v6;
|
{
|
||||||
Gateway = allAssignments.colony.internal.ipv6.address;
|
Gateway = allAssignments.colony.routing.ipv4.address;
|
||||||
}
|
Destination = prefixes."${pName}".v4;
|
||||||
]) [ "vms" "ctrs" "oci" ])));
|
}
|
||||||
|
{
|
||||||
|
Destination = prefixes."${pName}".v6;
|
||||||
|
Gateway = allAssignments.colony.internal.ipv6.address;
|
||||||
|
}
|
||||||
|
]) [ "vms" "ctrs" "oci" ]));
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -326,7 +365,7 @@ in
|
|||||||
{
|
{
|
||||||
matchConfig.Name = "as211024";
|
matchConfig.Name = "as211024";
|
||||||
networkConfig.IPv6AcceptRA = mkForce false;
|
networkConfig.IPv6AcceptRA = mkForce false;
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = lib.my.c.home.prefixes.all.v4;
|
Destination = lib.my.c.home.prefixes.all.v4;
|
||||||
Gateway = lib.my.c.home.vips.as211024.v4;
|
Gateway = lib.my.c.home.vips.as211024.v4;
|
||||||
@@ -338,10 +377,28 @@ in
|
|||||||
matchConfig.Name = "kelder";
|
matchConfig.Name = "kelder";
|
||||||
routes = [
|
routes = [
|
||||||
{
|
{
|
||||||
routeConfig = {
|
Destination = allAssignments.kelder.estuary.ipv4.address;
|
||||||
Destination = allAssignments.kelder.estuary.ipv4.address;
|
Scope = "link";
|
||||||
Scope = "link";
|
}
|
||||||
};
|
];
|
||||||
|
};
|
||||||
|
"95-hillcrest" = {
|
||||||
|
matchConfig.Name = "hillcrest";
|
||||||
|
address = [ "${net.cidr.host 1 prefixes.hillcrest.v4}/32" ];
|
||||||
|
routes = [
|
||||||
|
{
|
||||||
|
Destination = net.cidr.host 2 prefixes.hillcrest.v4;
|
||||||
|
Scope = "link";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"95-john-valorant" = {
|
||||||
|
matchConfig.Name = "john-valorant";
|
||||||
|
address = [ "${net.cidr.host 1 prefixes.john-valorant.v4}/32" ];
|
||||||
|
routes = [
|
||||||
|
{
|
||||||
|
Destination = net.cidr.host 2 prefixes.john-valorant.v4;
|
||||||
|
Scope = "link";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -355,6 +412,12 @@ in
|
|||||||
"estuary/kelder-wg.key" = {
|
"estuary/kelder-wg.key" = {
|
||||||
owner = "systemd-network";
|
owner = "systemd-network";
|
||||||
};
|
};
|
||||||
|
"estuary/hillcrest-wg.key" = {
|
||||||
|
owner = "systemd-network";
|
||||||
|
};
|
||||||
|
"estuary/john-valorant-wg.key" = {
|
||||||
|
owner = "systemd-network";
|
||||||
|
};
|
||||||
"l2mesh/as211024.key" = {};
|
"l2mesh/as211024.key" = {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -366,7 +429,13 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
firewall = {
|
firewall = {
|
||||||
udp.allowed = [ 5353 lib.my.c.kelder.vpn.port ];
|
udp.allowed = [
|
||||||
|
5353
|
||||||
|
|
||||||
|
lib.my.c.kelder.vpn.port
|
||||||
|
lib.my.c.hillcrest.vpn.port
|
||||||
|
lib.my.c.john-valorant.vpn.port
|
||||||
|
];
|
||||||
tcp.allowed = [ 5353 "bgp" ];
|
tcp.allowed = [ 5353 "bgp" ];
|
||||||
nat = {
|
nat = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -393,16 +462,28 @@ in
|
|||||||
# Safe enough to allow all SSH
|
# Safe enough to allow all SSH
|
||||||
tcp dport ssh accept
|
tcp dport ssh accept
|
||||||
|
|
||||||
|
# jam-ctr forwards
|
||||||
|
ip daddr ${aa.shill.internal.ipv4.address} tcp dport 60022 accept
|
||||||
|
|
||||||
ip6 daddr ${aa.middleman.internal.ipv6.address} tcp dport { http, https, 8448 } accept
|
ip6 daddr ${aa.middleman.internal.ipv6.address} tcp dport { http, https, 8448 } accept
|
||||||
${matchInet "tcp dport { http, https } accept" "git"}
|
${matchInet "tcp dport { http, https } accept" "git"}
|
||||||
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} tcp dport { 25565, 25575 } accept
|
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} tcp dport 25565 accept
|
||||||
ip6 daddr ${aa.simpcraft-staging-oci.internal.ipv6.address} tcp dport 25565 accept
|
ip6 daddr ${aa.simpcraft-staging-oci.internal.ipv6.address} tcp dport 25565 accept
|
||||||
|
ip6 daddr ${aa.kevcraft-oci.internal.ipv6.address} tcp dport 25567 accept
|
||||||
|
ip6 daddr ${aa.kinkcraft-oci.internal.ipv6.address} tcp dport 25568 accept
|
||||||
|
ip6 daddr ${aa.graeme-oci.internal.ipv6.address} tcp dport 25569 accept
|
||||||
|
ip6 daddr ${aa.gam.internal.ipv6.address} tcp dport 7777 accept
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
chain routing-udp {
|
chain routing-udp {
|
||||||
ip6 daddr ${aa.valheim-oci.internal.ipv6.address} udp dport { 2456-2457 } accept
|
ip6 daddr ${aa.valheim-oci.internal.ipv6.address} udp dport { 2456-2457 } accept
|
||||||
ip6 daddr ${aa.waffletail.internal.ipv6.address} udp dport 41641 accept
|
ip6 daddr ${aa.waffletail.internal.ipv6.address} udp dport 41641 accept
|
||||||
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} udp dport 25565 accept
|
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} udp dport 25565 accept
|
||||||
|
ip6 daddr ${aa.enshrouded-oci.internal.ipv6.address} udp dport { 15636-15637 } accept
|
||||||
|
ip6 daddr ${aa.kevcraft-oci.internal.ipv6.address} udp dport 25567 accept
|
||||||
|
ip6 daddr ${aa.kinkcraft-oci.internal.ipv6.address} udp dport 25568 accept
|
||||||
|
ip6 daddr ${aa.graeme-oci.internal.ipv6.address} udp dport 25569 accept
|
||||||
|
ip6 daddr ${aa.gam.internal.ipv6.address} udp dport 7777 accept
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
chain filter-routing {
|
chain filter-routing {
|
||||||
@@ -423,7 +504,7 @@ in
|
|||||||
iifname { wan, as211024, $ixps } oifname base jump filter-routing
|
iifname { wan, as211024, $ixps } oifname base jump filter-routing
|
||||||
oifname $ixps jump ixp
|
oifname $ixps jump ixp
|
||||||
iifname base oifname { base, wan, $ixps } accept
|
iifname base oifname { base, wan, $ixps } accept
|
||||||
oifname { as211024, kelder } accept
|
oifname { as211024, kelder, hillcrest, john-valorant } accept
|
||||||
}
|
}
|
||||||
chain output {
|
chain output {
|
||||||
oifname ifog ether type != vlan reject
|
oifname ifog ether type != vlan reject
|
||||||
@@ -435,6 +516,8 @@ in
|
|||||||
${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"}
|
${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"}
|
||||||
}
|
}
|
||||||
chain postrouting {
|
chain postrouting {
|
||||||
|
oifname hillcrest snat ip to ${net.cidr.host 1 prefixes.hillcrest.v4}
|
||||||
|
oifname john-valorant snat ip to ${net.cidr.host 1 prefixes.john-valorant.v4}
|
||||||
ip saddr ${prefixes.all.v4} oifname != as211024 snat to ${assignments.internal.ipv4.address}
|
ip saddr ${prefixes.all.v4} oifname != as211024 snat to ${assignments.internal.ipv4.address}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
let
|
let
|
||||||
inherit (builtins) attrNames;
|
inherit (builtins) attrNames;
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c.colony) prefixes;
|
inherit (lib.my.c.colony) prefixes custRouting;
|
||||||
|
|
||||||
authZones = attrNames config.my.pdns.auth.bind.zones;
|
authZones = attrNames config.my.pdns.auth.bind.zones;
|
||||||
in
|
in
|
||||||
@@ -14,7 +14,7 @@ in
|
|||||||
owner = "pdns";
|
owner = "pdns";
|
||||||
group = "pdns";
|
group = "pdns";
|
||||||
};
|
};
|
||||||
"estuary/pdns/recursor.conf" = {
|
"estuary/pdns/recursor.yml" = {
|
||||||
owner = "pdns-recursor";
|
owner = "pdns-recursor";
|
||||||
group = "pdns-recursor";
|
group = "pdns-recursor";
|
||||||
};
|
};
|
||||||
@@ -31,7 +31,7 @@ in
|
|||||||
|
|
||||||
pdns.recursor = {
|
pdns.recursor = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraSettingsFile = config.age.secrets."estuary/pdns/recursor.conf".path;
|
extraSettingsFile = config.age.secrets."estuary/pdns/recursor.yml".path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,45 +44,55 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
pdns-recursor = {
|
pdns-recursor = {
|
||||||
dns = {
|
|
||||||
address = [
|
|
||||||
"127.0.0.1" "::1"
|
|
||||||
assignments.base.ipv4.address assignments.base.ipv6.address
|
|
||||||
];
|
|
||||||
allowFrom = [
|
|
||||||
"127.0.0.0/8" "::1/128"
|
|
||||||
prefixes.all.v4 prefixes.all.v6
|
|
||||||
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
|
|
||||||
};
|
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
query-local-address = [
|
incoming = {
|
||||||
assignments.internal.ipv4.address
|
listen = [
|
||||||
assignments.internal.ipv6.address
|
"127.0.0.1" "::1"
|
||||||
assignments.base.ipv6.address
|
assignments.base.ipv4.address assignments.base.ipv6.address
|
||||||
];
|
];
|
||||||
forward-zones = map (z: "${z}=127.0.0.1:5353") authZones;
|
allow_from = [
|
||||||
|
"127.0.0.0/8" "::1/128"
|
||||||
|
prefixes.all.v4 prefixes.all.v6
|
||||||
|
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
|
||||||
|
|
||||||
# DNS NOTIFY messages override TTL
|
# DNS NOTIFY messages override TTL
|
||||||
allow-notify-for = authZones;
|
allow_notify_for = authZones;
|
||||||
allow-notify-from = [ "127.0.0.0/8" "::1/128" ];
|
allow_notify_from = [ "127.0.0.0/8" "::1/128" ];
|
||||||
|
};
|
||||||
|
|
||||||
webserver = true;
|
outgoing = {
|
||||||
webserver-address = "::";
|
source_address = [
|
||||||
webserver-allow-from = [ "127.0.0.1" "::1" ];
|
assignments.internal.ipv4.address
|
||||||
|
assignments.internal.ipv6.address
|
||||||
|
assignments.base.ipv6.address
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
lua-dns-script = pkgs.writeText "pdns-script.lua" ''
|
recursor = {
|
||||||
function preresolve(dq)
|
forward_zones = map (z: {
|
||||||
if dq.qname:equal("nix-cache.nul.ie") then
|
zone = z;
|
||||||
dq:addAnswer(pdns.CNAME, "http.${config.networking.domain}.")
|
forwarders = [ "127.0.0.1:5353" ];
|
||||||
dq.rcode = 0
|
}) authZones;
|
||||||
dq.followupFunction = "followCNAMERecords"
|
|
||||||
return true
|
lua_dns_script = pkgs.writeText "pdns-script.lua" ''
|
||||||
|
function preresolve(dq)
|
||||||
|
if dq.qname:equal("nix-cache.nul.ie") then
|
||||||
|
dq:addAnswer(pdns.CNAME, "http.${config.networking.domain}.")
|
||||||
|
dq.rcode = 0
|
||||||
|
dq.followupFunction = "followCNAMERecords"
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
return false
|
webservice = {
|
||||||
end
|
webserver = true;
|
||||||
'';
|
address = "::";
|
||||||
|
allow_from = [ "127.0.0.1" "::1" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -153,6 +163,15 @@ in
|
|||||||
simpcraft IN AAAA ${allAssignments.simpcraft-oci.internal.ipv6.address}
|
simpcraft IN AAAA ${allAssignments.simpcraft-oci.internal.ipv6.address}
|
||||||
simpcraft-staging IN A ${assignments.internal.ipv4.address}
|
simpcraft-staging IN A ${assignments.internal.ipv4.address}
|
||||||
simpcraft-staging IN AAAA ${allAssignments.simpcraft-staging-oci.internal.ipv6.address}
|
simpcraft-staging IN AAAA ${allAssignments.simpcraft-staging-oci.internal.ipv6.address}
|
||||||
|
enshrouded IN A ${assignments.internal.ipv4.address}
|
||||||
|
kevcraft IN A ${assignments.internal.ipv4.address}
|
||||||
|
kevcraft IN AAAA ${allAssignments.kevcraft-oci.internal.ipv6.address}
|
||||||
|
kinkcraft IN A ${assignments.internal.ipv4.address}
|
||||||
|
kinkcraft IN AAAA ${allAssignments.kinkcraft-oci.internal.ipv6.address}
|
||||||
|
graeme IN A ${assignments.internal.ipv4.address}
|
||||||
|
graeme IN AAAA ${allAssignments.graeme-oci.internal.ipv6.address}
|
||||||
|
terraria IN A ${assignments.internal.ipv4.address}
|
||||||
|
terraria IN AAAA ${allAssignments.gam.internal.ipv6.address}
|
||||||
|
|
||||||
mail-vm IN A ${net.cidr.host 0 prefixes.mail.v4}
|
mail-vm IN A ${net.cidr.host 0 prefixes.mail.v4}
|
||||||
mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6}
|
mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6}
|
||||||
@@ -162,6 +181,13 @@ in
|
|||||||
|
|
||||||
andrey-cust IN A ${allAssignments.kelder.estuary.ipv4.address}
|
andrey-cust IN A ${allAssignments.kelder.estuary.ipv4.address}
|
||||||
|
|
||||||
|
jam-cust IN A ${net.cidr.host 0 prefixes.jam.v4}
|
||||||
|
jam-fwd IN A ${allAssignments.shill.internal.ipv4.address}
|
||||||
|
jam-cust IN AAAA ${net.cidr.host 1 prefixes.jam.v6}
|
||||||
|
|
||||||
|
hillcrest-tun IN A ${net.cidr.host 2 prefixes.hillcrest.v4}
|
||||||
|
john-valorant-tun IN A ${net.cidr.host 2 prefixes.john-valorant.v4}
|
||||||
|
|
||||||
$TTL 3
|
$TTL 3
|
||||||
_acme-challenge IN LUA TXT @@FILE@@
|
_acme-challenge IN LUA TXT @@FILE@@
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ let
|
|||||||
inherit (lib) mkMerge mkDefault;
|
inherit (lib) mkMerge mkDefault;
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c) pubDomain;
|
inherit (lib.my.c) pubDomain;
|
||||||
inherit (lib.my.c.colony) domain prefixes;
|
inherit (lib.my.c.colony) domain prefixes firewallForwards;
|
||||||
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
|
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -95,41 +95,13 @@ in
|
|||||||
"*.${pubDomain}"
|
"*.${pubDomain}"
|
||||||
];
|
];
|
||||||
dnsProvider = "cloudflare";
|
dnsProvider = "cloudflare";
|
||||||
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
environmentFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
fstrim = lib.my.c.colony.fstrimConfig;
|
fstrim = lib.my.c.colony.fstrimConfig;
|
||||||
# Hacks for Jsch (Minecraft FastBack) to work
|
|
||||||
openssh = {
|
|
||||||
hostKeys = [
|
|
||||||
{
|
|
||||||
bits = 4096;
|
|
||||||
path = "/etc/ssh/ssh_host_rsa_key";
|
|
||||||
type = "rsa";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
path = "/etc/ssh/ssh_host_ed25519_key";
|
|
||||||
type = "ed25519";
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
type = "ecdsa-sha2-nistp256";
|
|
||||||
path = "/etc/ssh/ssh_host_ecdsa_key";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
settings = {
|
|
||||||
Macs = [
|
|
||||||
"hmac-sha2-512-etm@openssh.com"
|
|
||||||
"hmac-sha2-256-etm@openssh.com"
|
|
||||||
"umac-128-etm@openssh.com"
|
|
||||||
|
|
||||||
"hmac-sha2-256"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
netdata.enable = true;
|
netdata.enable = true;
|
||||||
nginx = {
|
nginx = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -139,7 +111,6 @@ in
|
|||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
clientMaxBodySize = "0";
|
clientMaxBodySize = "0";
|
||||||
serverTokens = true;
|
serverTokens = true;
|
||||||
sslDhparam = config.age.secrets."dhparams.pem".path;
|
|
||||||
|
|
||||||
# Based on recommended*Settings, but probably better to be explicit about these
|
# Based on recommended*Settings, but probably better to be explicit about these
|
||||||
appendHttpConfig = ''
|
appendHttpConfig = ''
|
||||||
@@ -210,11 +181,6 @@ in
|
|||||||
secrets = {
|
secrets = {
|
||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP+KINpHLMduBuW96JzfSRDLUzkI+XaCBghu5/wHiW5R";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP+KINpHLMduBuW96JzfSRDLUzkI+XaCBghu5/wHiW5R";
|
||||||
files = {
|
files = {
|
||||||
"dhparams.pem" = {
|
|
||||||
owner = "acme";
|
|
||||||
group = "acme";
|
|
||||||
mode = "440";
|
|
||||||
};
|
|
||||||
"middleman/cloudflare-credentials.conf" = {
|
"middleman/cloudflare-credentials.conf" = {
|
||||||
owner = "acme";
|
owner = "acme";
|
||||||
group = "acme";
|
group = "acme";
|
||||||
@@ -225,6 +191,7 @@ in
|
|||||||
|
|
||||||
firewall = {
|
firewall = {
|
||||||
tcp.allowed = [ 19999 "http" "https" ];
|
tcp.allowed = [ 19999 "http" "https" ];
|
||||||
|
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
|
||||||
extraRules = ''
|
extraRules = ''
|
||||||
table inet filter {
|
table inet filter {
|
||||||
chain forward {
|
chain forward {
|
||||||
|
|||||||
@@ -1,19 +1,7 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) toJSON;
|
|
||||||
inherit (lib) mkForce;
|
inherit (lib) mkForce;
|
||||||
inherit (lib.my.c) pubDomain;
|
inherit (lib.my.c) pubDomain;
|
||||||
|
|
||||||
cfgFile = pkgs.writeText "gitea-actions-runner.yaml" (toJSON {
|
|
||||||
container = {
|
|
||||||
network = "podman";
|
|
||||||
privileged = true;
|
|
||||||
};
|
|
||||||
cache = {
|
|
||||||
enabled = true;
|
|
||||||
dir = "/var/cache/gitea-runner";
|
|
||||||
};
|
|
||||||
});
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
@@ -30,11 +18,24 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
name = "main-docker";
|
name = "main-docker";
|
||||||
labels = [
|
labels = [
|
||||||
"debian-node-bullseye:docker://node:18-bullseye"
|
"debian-node-trixie:docker://node:24-trixie"
|
||||||
"ubuntu-22.04:docker://git.nul.ie/dev/actions-ubuntu:22.04"
|
"ubuntu-26.04:docker://git.nul.ie/dev/actions-ubuntu:26.04"
|
||||||
];
|
];
|
||||||
url = "https://git.${pubDomain}";
|
url = "https://git.${pubDomain}";
|
||||||
tokenFile = config.age.secrets."gitea/actions-runner.env".path;
|
tokenFile = config.age.secrets."gitea/actions-runner.env".path;
|
||||||
|
settings = {
|
||||||
|
runner = {
|
||||||
|
timeout = "8h";
|
||||||
|
};
|
||||||
|
container = {
|
||||||
|
network = "podman";
|
||||||
|
privileged = true;
|
||||||
|
};
|
||||||
|
cache = {
|
||||||
|
enabled = true;
|
||||||
|
dir = "/var/cache/gitea-runner";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -61,7 +62,6 @@ in
|
|||||||
DynamicUser = mkForce false;
|
DynamicUser = mkForce false;
|
||||||
User = "gitea-runner";
|
User = "gitea-runner";
|
||||||
Group = "gitea-runner";
|
Group = "gitea-runner";
|
||||||
ExecStart = mkForce "${config.services.gitea-actions-runner.package}/bin/act_runner -c ${cfgFile} daemon";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
{ lib, pkgs, assignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c.colony) prefixes custRouting;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
fileSystems = {
|
||||||
|
"/mnt/jam" = {
|
||||||
|
device = "/dev/disk/by-label/jam";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
"/var/lib/machines/jam" = {
|
||||||
|
device = "/mnt/jam";
|
||||||
|
options = [ "bind" ];
|
||||||
|
fsType = "none";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
nspawn = {
|
||||||
|
jam = {
|
||||||
|
enable = true;
|
||||||
|
execConfig = {
|
||||||
|
Boot = true;
|
||||||
|
PrivateUsers = "pick";
|
||||||
|
LinkJournal = false;
|
||||||
|
};
|
||||||
|
networkConfig = {
|
||||||
|
Private = true;
|
||||||
|
VirtualEthernet = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
network.networks = {
|
||||||
|
"50-ve-jam" = {
|
||||||
|
matchConfig = {
|
||||||
|
Kind = "veth";
|
||||||
|
Name = "ve-jam";
|
||||||
|
};
|
||||||
|
address = [
|
||||||
|
custRouting.jam-ctr
|
||||||
|
prefixes.jam.v6
|
||||||
|
];
|
||||||
|
networkConfig = {
|
||||||
|
IPv6AcceptRA = false;
|
||||||
|
IPv6SendRA = true;
|
||||||
|
};
|
||||||
|
ipv6Prefixes = [
|
||||||
|
{
|
||||||
|
Prefix = prefixes.jam.v6;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
routes = [
|
||||||
|
{
|
||||||
|
Destination = prefixes.jam.v4;
|
||||||
|
Scope = "link";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services = {
|
||||||
|
"systemd-nspawn@jam" = {
|
||||||
|
overrideStrategy = "asDropin";
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
CPUQuota = "400%";
|
||||||
|
MemoryHigh = "infinity";
|
||||||
|
MemoryMax = "4G";
|
||||||
|
};
|
||||||
|
|
||||||
|
wantedBy = [ "machines.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
firewall =
|
||||||
|
let
|
||||||
|
jamIP = net.cidr.host 0 prefixes.jam.v4;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nat.forwardPorts."${assignments.internal.ipv4.address}" = [
|
||||||
|
{
|
||||||
|
port = 60022;
|
||||||
|
dst = jamIP;
|
||||||
|
dstPort = "ssh";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
extraRules = ''
|
||||||
|
table inet filter {
|
||||||
|
chain forward {
|
||||||
|
iifname { ve-jam } oifname vms accept
|
||||||
|
iifname vms oifname { ve-jam } accept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table inet nat {
|
||||||
|
chain postrouting {
|
||||||
|
ip saddr ${jamIP} snat to ${assignments.internal.ipv4.address}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ in
|
|||||||
|
|
||||||
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkMerge mkIf mkForce;
|
inherit (lib) genAttrs mkMerge mkIf mkForce;
|
||||||
inherit (lib.my) networkdAssignment;
|
inherit (lib.my) networkdAssignment;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -45,9 +45,22 @@ in
|
|||||||
owner = "matrix-synapse";
|
owner = "matrix-synapse";
|
||||||
group = "matrix-synapse";
|
group = "matrix-synapse";
|
||||||
};
|
};
|
||||||
"chatterbox/syncv3.env" = {
|
"chatterbox/doublepuppet.yaml" = {
|
||||||
owner = "matrix-syncv3";
|
owner = "matrix-synapse";
|
||||||
group = "matrix-syncv3";
|
group = "matrix-synapse";
|
||||||
|
};
|
||||||
|
|
||||||
|
"chatterbox/mautrix-whatsapp.env" = {
|
||||||
|
owner = "mautrix-whatsapp";
|
||||||
|
group = "mautrix-whatsapp";
|
||||||
|
};
|
||||||
|
"chatterbox/mautrix-messenger.env" = {
|
||||||
|
owner = "mautrix-meta-messenger";
|
||||||
|
group = "mautrix-meta";
|
||||||
|
};
|
||||||
|
"chatterbox/mautrix-instagram.env" = {
|
||||||
|
owner = "mautrix-meta-instagram";
|
||||||
|
group = "mautrix-meta";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -59,29 +72,24 @@ in
|
|||||||
|
|
||||||
users = with lib.my.c.ids; {
|
users = with lib.my.c.ids; {
|
||||||
users = {
|
users = {
|
||||||
matrix-syncv3 = {
|
matrix-synapse.extraGroups = [
|
||||||
isSystemUser = true;
|
"mautrix-whatsapp"
|
||||||
uid = uids.matrix-syncv3;
|
];
|
||||||
group = "matrix-syncv3";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
groups = {
|
|
||||||
matrix-syncv3.gid = gids.matrix-syncv3;
|
|
||||||
};
|
};
|
||||||
|
groups = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd = {
|
systemd = {
|
||||||
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||||
services = {
|
services = { } // (genAttrs [ "mautrix-whatsapp" "mautrix-meta-messenger" "mautrix-meta-instagram" ] (_: {
|
||||||
matrix-sliding-sync.serviceConfig = {
|
# ffmpeg needed to convert GIFs to video
|
||||||
# Needs to be able to read its secrets
|
path = with pkgs; [ ffmpeg ];
|
||||||
DynamicUser = mkForce false;
|
}));
|
||||||
User = "matrix-syncv3";
|
|
||||||
Group = "matrix-syncv3";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# TODO/FIXME: https://github.com/NixOS/nixpkgs/issues/336052
|
||||||
|
nixpkgs.config.permittedInsecurePackages = [ "olm-3.2.16" ];
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
netdata.enable = true;
|
netdata.enable = true;
|
||||||
matrix-synapse = {
|
matrix-synapse = {
|
||||||
@@ -168,18 +176,10 @@ in
|
|||||||
|
|
||||||
app_service_config_files = [
|
app_service_config_files = [
|
||||||
"/var/lib/heisenbridge/registration.yml"
|
"/var/lib/heisenbridge/registration.yml"
|
||||||
|
config.age.secrets."chatterbox/doublepuppet.yaml".path
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
sliding-sync = {
|
|
||||||
enable = true;
|
|
||||||
createDatabase = false;
|
|
||||||
environmentFile = config.age.secrets."chatterbox/syncv3.env".path;
|
|
||||||
settings = {
|
|
||||||
SYNCV3_BINDADDR = "[::]:8009";
|
|
||||||
SYNCV3_SERVER = "http://localhost:8008";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
heisenbridge = {
|
heisenbridge = {
|
||||||
@@ -195,6 +195,181 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mautrix-whatsapp = {
|
||||||
|
enable = true;
|
||||||
|
# package = pkgs.mautrix-whatsapp.overrideAttrs (o: rec {
|
||||||
|
# version = "26.05";
|
||||||
|
# tag = "v0.2605.0";
|
||||||
|
# src = pkgs.fetchFromGitHub {
|
||||||
|
# owner = "mautrix";
|
||||||
|
# repo = "whatsapp";
|
||||||
|
# inherit tag;
|
||||||
|
# hash = "sha256-WlVfGQoP9e/wl98hUJei8O2JMcOKijoEY8XuU/z69Qk=";
|
||||||
|
# };
|
||||||
|
# vendorHash = "sha256-Hi/dZHJHoTTCnxLXgbkcYzuzis4fl5kxb5wMd9fKTY8=";
|
||||||
|
# });
|
||||||
|
environmentFile = config.age.secrets."chatterbox/mautrix-whatsapp.env".path;
|
||||||
|
settings = {
|
||||||
|
database = {
|
||||||
|
type = "postgres";
|
||||||
|
uri = "$MAU_WAPP_PSQL_URI";
|
||||||
|
};
|
||||||
|
homeserver = {
|
||||||
|
address = "http://localhost:8008";
|
||||||
|
domain = "nul.ie";
|
||||||
|
};
|
||||||
|
appservice = {
|
||||||
|
id = "whatsapp2";
|
||||||
|
bot = {
|
||||||
|
username = "whatsapp2";
|
||||||
|
displayname = "WhatsApp Bridge Bot";
|
||||||
|
};
|
||||||
|
username_template = "wapp2_{{.}}";
|
||||||
|
};
|
||||||
|
bridge = {
|
||||||
|
personal_filtering_spaces = true;
|
||||||
|
command_prefix = "!wa";
|
||||||
|
permissions = {
|
||||||
|
"@dev:nul.ie" = "admin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
double_puppet = {
|
||||||
|
secrets."nul.ie" = "$MAU_WAPP_DOUBLE_PUPPET_TOKEN";
|
||||||
|
};
|
||||||
|
encryption = {
|
||||||
|
allow = true;
|
||||||
|
default = true;
|
||||||
|
require = true;
|
||||||
|
pickle_key = "maunium.net/go/mautrix-whatsapp";
|
||||||
|
};
|
||||||
|
matrix = {
|
||||||
|
delivery_receipts = true;
|
||||||
|
};
|
||||||
|
network = {
|
||||||
|
displayname_template = ''{{or .BusinessName .PushName .FullName .Phone "Unknown user"}} (WA)'';
|
||||||
|
url_previews = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# mautrix-meta.package = pkgs.mautrix-meta.overrideAttrs (o: rec {
|
||||||
|
# version = "26.05.1";
|
||||||
|
# tag = "v0.2605.1";
|
||||||
|
# src = pkgs.fetchFromGitHub {
|
||||||
|
# owner = "mautrix";
|
||||||
|
# repo = "meta";
|
||||||
|
# inherit tag;
|
||||||
|
# hash = "sha256-zpolDtwGulDTiojJPnkj9O0D5b4rgPYQX6A28rvuvM0=";
|
||||||
|
# };
|
||||||
|
# vendorHash = "sha256-+i45bXBhlXPXX24VMS9IJLLX+i4VPnqy5RAH4j88sTA=";
|
||||||
|
# });
|
||||||
|
mautrix-meta.instances = {
|
||||||
|
messenger = {
|
||||||
|
enable = true;
|
||||||
|
registerToSynapse = true;
|
||||||
|
dataDir = "mautrix-messenger";
|
||||||
|
environmentFile = config.age.secrets."chatterbox/mautrix-messenger.env".path;
|
||||||
|
settings = {
|
||||||
|
database = {
|
||||||
|
type = "postgres";
|
||||||
|
uri = "$MAU_FBM_PSQL_URI";
|
||||||
|
};
|
||||||
|
homeserver = {
|
||||||
|
address = "http://localhost:8008";
|
||||||
|
domain = "nul.ie";
|
||||||
|
};
|
||||||
|
appservice = {
|
||||||
|
id = "fbm2";
|
||||||
|
bot = {
|
||||||
|
username = "messenger2";
|
||||||
|
displayname = "Messenger Bridge Bot";
|
||||||
|
avatar = "mxc://maunium.net/ygtkteZsXnGJLJHRchUwYWak";
|
||||||
|
};
|
||||||
|
username_template = "fbm2_{{.}}";
|
||||||
|
};
|
||||||
|
network = {
|
||||||
|
mode = "messenger";
|
||||||
|
displayname_template = ''{{or .DisplayName .Username "Unknown user"}} (FBM)'';
|
||||||
|
};
|
||||||
|
bridge = {
|
||||||
|
personal_filtering_spaces = true;
|
||||||
|
# management_room_text.welcome = "Hello, I'm a Messenger bridge bot.";
|
||||||
|
command_prefix = "!fbm";
|
||||||
|
backfill = {
|
||||||
|
enabled = true;
|
||||||
|
};
|
||||||
|
permissions = {
|
||||||
|
"@dev:nul.ie" = "admin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
double_puppet = {
|
||||||
|
secrets."nul.ie" = "$MAU_FBM_DOUBLE_PUPPET_TOKEN";
|
||||||
|
};
|
||||||
|
encryption = {
|
||||||
|
allow = true;
|
||||||
|
default = true;
|
||||||
|
require = true;
|
||||||
|
};
|
||||||
|
matrix = {
|
||||||
|
delivery_receipts = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
instagram = {
|
||||||
|
enable = true;
|
||||||
|
registerToSynapse = true;
|
||||||
|
dataDir = "mautrix-instagram";
|
||||||
|
environmentFile = config.age.secrets."chatterbox/mautrix-instagram.env".path;
|
||||||
|
settings = {
|
||||||
|
database = {
|
||||||
|
type = "postgres";
|
||||||
|
uri = "$MAU_IG_PSQL_URI";
|
||||||
|
};
|
||||||
|
homeserver = {
|
||||||
|
address = "http://localhost:8008";
|
||||||
|
domain = "nul.ie";
|
||||||
|
};
|
||||||
|
appservice = {
|
||||||
|
id = "instagram";
|
||||||
|
bot = {
|
||||||
|
username = "instagram";
|
||||||
|
displayname = "Instagram Bridge Bot";
|
||||||
|
avatar = "mxc://maunium.net/JxjlbZUlCPULEeHZSwleUXQv";
|
||||||
|
};
|
||||||
|
username_template = "ig_{{.}}";
|
||||||
|
};
|
||||||
|
network = {
|
||||||
|
mode = "instagram";
|
||||||
|
displayname_template = ''{{or .DisplayName .Username "Unknown user"}} (IG)'';
|
||||||
|
};
|
||||||
|
bridge = {
|
||||||
|
personal_filtering_spaces = true;
|
||||||
|
# management_room_text.welcome = "Hello, I'm an Instagram bridge bot.";
|
||||||
|
command_prefix = "!ig";
|
||||||
|
backfill = {
|
||||||
|
enabled = true;
|
||||||
|
};
|
||||||
|
permissions = {
|
||||||
|
"@dev:nul.ie" = "admin";
|
||||||
|
"@adzerq:nul.ie" = "user";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
double_puppet = {
|
||||||
|
secrets."nul.ie" = "$MAU_IG_DOUBLE_PUPPET_TOKEN";
|
||||||
|
};
|
||||||
|
encryption = {
|
||||||
|
allow = true;
|
||||||
|
default = true;
|
||||||
|
require = true;
|
||||||
|
};
|
||||||
|
matrix = {
|
||||||
|
delivery_receipts = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(mkIf config.my.build.isDevVM {
|
(mkIf config.my.build.isDevVM {
|
||||||
|
|||||||
@@ -8,5 +8,7 @@
|
|||||||
./object.nix
|
./object.nix
|
||||||
./toot.nix
|
./toot.nix
|
||||||
./waffletail.nix
|
./waffletail.nix
|
||||||
|
./qclk
|
||||||
|
./gam.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c) pubDomain;
|
||||||
|
inherit (lib.my.c.colony) domain prefixes;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixos.systems.gam = { config, ... }: {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
nixpkgs = "mine";
|
||||||
|
rendered = config.configuration.config.my.asContainer;
|
||||||
|
|
||||||
|
assignments = {
|
||||||
|
internal = {
|
||||||
|
name = "gam-ctr";
|
||||||
|
inherit domain;
|
||||||
|
ipv4.address = net.cidr.host 11 prefixes.ctrs.v4;
|
||||||
|
ipv6 = {
|
||||||
|
iid = "::11";
|
||||||
|
address = net.cidr.host 11 prefixes.ctrs.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkMerge mkIf mkForce;
|
||||||
|
inherit (lib.my) networkdAssignment;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = mkMerge [
|
||||||
|
{
|
||||||
|
my = {
|
||||||
|
deploy.enable = false;
|
||||||
|
server.enable = true;
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAvDlH3nT1kve741gBluYmn5KQs8yz7FAEt8qLt+f0K6";
|
||||||
|
files = {
|
||||||
|
"gam/terraria.conf" = {
|
||||||
|
owner = "terraria";
|
||||||
|
group = "terraria";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
terraria = {
|
||||||
|
enable = true;
|
||||||
|
noUPnP = true;
|
||||||
|
messageOfTheDay = "sup gamers";
|
||||||
|
autoCreatedWorldSize = "large";
|
||||||
|
worldPath = "/var/lib/terraria/NotWorld.wld";
|
||||||
|
configFile = config.age.secrets."gam/terraria.conf".path;
|
||||||
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(mkIf config.my.build.isDevVM {
|
||||||
|
virtualisation = {
|
||||||
|
forwardPorts = [ ];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
|
inherit (lib) concatStringsSep;
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c) pubDomain;
|
||||||
inherit (lib.my.c.colony) domain prefixes;
|
inherit (lib.my.c.colony) domain prefixes;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -21,7 +23,7 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
configuration = { lib, pkgs, config, ... }:
|
configuration = { lib, pkgs, config, allAssignments, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkForce;
|
inherit (lib) mkForce;
|
||||||
in
|
in
|
||||||
@@ -35,6 +37,19 @@ in
|
|||||||
|
|
||||||
secrets = {
|
secrets = {
|
||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUv1ntVrZv5ripsKpcOAnyDQX2PHjowzyhqWK10Ml53";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUv1ntVrZv5ripsKpcOAnyDQX2PHjowzyhqWK10Ml53";
|
||||||
|
files = {
|
||||||
|
"jackflix/photoprism-pass.txt" = {};
|
||||||
|
"jackflix/copyparty-pass.txt" = {
|
||||||
|
owner = "copyparty";
|
||||||
|
group = "copyparty";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
firewall = {
|
||||||
|
tcp.allowed = [
|
||||||
|
3923
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,15 +60,26 @@ in
|
|||||||
transmission.extraGroups = [ "media" ];
|
transmission.extraGroups = [ "media" ];
|
||||||
radarr.extraGroups = [ "media" ];
|
radarr.extraGroups = [ "media" ];
|
||||||
sonarr.extraGroups = [ "media" ];
|
sonarr.extraGroups = [ "media" ];
|
||||||
jellyseerr = {
|
seerr = {
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
uid = uids.jellyseerr;
|
uid = uids.jellyseerr;
|
||||||
group = "jellyseerr";
|
group = "seerr";
|
||||||
|
};
|
||||||
|
photoprism = {
|
||||||
|
isSystemUser = true;
|
||||||
|
uid = uids.photoprism;
|
||||||
|
group = "photoprism";
|
||||||
|
};
|
||||||
|
copyparty = {
|
||||||
|
uid = uids.copyparty;
|
||||||
|
extraGroups = [ "media" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
groups = {
|
groups = {
|
||||||
media.gid = 2000;
|
media.gid = 2000;
|
||||||
jellyseerr.gid = gids.jellyseerr;
|
seerr.gid = gids.jellyseerr;
|
||||||
|
photoprism.gid = gids.photoprism;
|
||||||
|
copyparty.gid = gids.copyparty;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,13 +88,15 @@ in
|
|||||||
jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
||||||
transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
||||||
|
|
||||||
radarr.serviceConfig.UMask = "0002";
|
radarr.serviceConfig.UMask = mkForce "0002";
|
||||||
sonarr.serviceConfig.UMask = "0002";
|
radarr.path = with pkgs; [ ffmpeg ];
|
||||||
jellyseerr.serviceConfig = {
|
sonarr.serviceConfig.UMask = mkForce "0002";
|
||||||
|
sonarr.path = with pkgs; [ ffmpeg ];
|
||||||
|
seerr.serviceConfig = {
|
||||||
# Needs to be able to read its secrets
|
# Needs to be able to read its secrets
|
||||||
DynamicUser = mkForce false;
|
DynamicUser = mkForce false;
|
||||||
User = "jellyseerr";
|
User = "seerr";
|
||||||
Group = "jellyseerr";
|
Group = "seerr";
|
||||||
};
|
};
|
||||||
|
|
||||||
# https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
|
# https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
|
||||||
@@ -76,6 +104,10 @@ in
|
|||||||
RootDirectoryStartOnly = lib.mkForce false;
|
RootDirectoryStartOnly = lib.mkForce false;
|
||||||
RootDirectory = lib.mkForce "";
|
RootDirectory = lib.mkForce "";
|
||||||
};
|
};
|
||||||
|
photoprism.serviceConfig = {
|
||||||
|
# Needs to be able to access its data
|
||||||
|
DynamicUser = mkForce false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,6 +116,7 @@ in
|
|||||||
|
|
||||||
transmission = {
|
transmission = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = pkgs.transmission_4;
|
||||||
downloadDirPermissions = null;
|
downloadDirPermissions = null;
|
||||||
performanceNetParameters = true;
|
performanceNetParameters = true;
|
||||||
settings = {
|
settings = {
|
||||||
@@ -108,15 +141,78 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
flaresolverr.enable = true;
|
||||||
jackett.enable = true;
|
jackett.enable = true;
|
||||||
radarr.enable = true;
|
radarr.enable = true;
|
||||||
sonarr.enable = true;
|
sonarr.enable = true;
|
||||||
jellyseerr = {
|
seerr = {
|
||||||
enable = true;
|
enable = true;
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
jellyfin.enable = true;
|
jellyfin.enable = true;
|
||||||
|
|
||||||
|
photoprism = {
|
||||||
|
enable = true;
|
||||||
|
address = "[::]";
|
||||||
|
port = 2342;
|
||||||
|
originalsPath = "/mnt/media/photoprism/originals";
|
||||||
|
importPath = "/mnt/media/photoprism/import";
|
||||||
|
passwordFile = config.age.secrets."jackflix/photoprism-pass.txt".path;
|
||||||
|
settings = {
|
||||||
|
PHOTOPRISM_AUTH_MODE = "password";
|
||||||
|
PHOTOPRISM_ADMIN_USER = "dev";
|
||||||
|
PHOTOPRISM_APP_NAME = "/dev/player0 Photos";
|
||||||
|
PHOTOPRISM_SITE_URL = "https://photos.${pubDomain}/";
|
||||||
|
PHOTOPRISM_SITE_TITLE = "/dev/player0 Photos";
|
||||||
|
PHOTOPRISM_TRUSTED_PROXY = concatStringsSep "," (with prefixes.ctrs; [ v4 v6 ]);
|
||||||
|
PHOTOPRISM_DATABASE_DRIVER = "sqlite";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
copyparty = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.copyparty.override {
|
||||||
|
withMagic = true;
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
name = "dev-stuff";
|
||||||
|
no-reload = true;
|
||||||
|
j = 8; # cores
|
||||||
|
http-only = true;
|
||||||
|
xff-src =
|
||||||
|
with allAssignments.middleman.internal;
|
||||||
|
[ "${ipv4.address}/32" prefixes.ctrs.v6 ];
|
||||||
|
rproxy = 1; # get if from x-forwarded-for
|
||||||
|
magic = true; # enable checking file magic on upload
|
||||||
|
hist = "/var/cache/copyparty";
|
||||||
|
shr = "/share"; # enable share creation
|
||||||
|
ed = true; # enable dotfiles
|
||||||
|
chmod-f = "664";
|
||||||
|
chmod-d = "775";
|
||||||
|
e2dsa = true; # file indexing
|
||||||
|
e2t = true; # metadata indexing
|
||||||
|
og-ua = "(Discord|Twitter|Slack)bot"; # embeds
|
||||||
|
theme = 6;
|
||||||
|
};
|
||||||
|
accounts.dev.passwordFile = config.age.secrets."jackflix/copyparty-pass.txt".path;
|
||||||
|
volumes = {
|
||||||
|
"/" = {
|
||||||
|
path = "/mnt/media/public";
|
||||||
|
access = {
|
||||||
|
A = "dev";
|
||||||
|
"r." = "*";
|
||||||
|
};
|
||||||
|
flags = {
|
||||||
|
shr_who = "no"; # no reason to have shares here
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"/priv" = {
|
||||||
|
path = "/mnt/media/stuff";
|
||||||
|
access.A = "dev"; # dev has admin access
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
# Forwarded in AirVPN config
|
# Forwarded in AirVPN config
|
||||||
transmissionPeerPort = 47016;
|
transmissionPeerPort = 28457;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
@@ -37,7 +37,7 @@ in
|
|||||||
tcp dport ${toString transmissionPeerPort} accept
|
tcp dport ${toString transmissionPeerPort} accept
|
||||||
iifname vpn return
|
iifname vpn return
|
||||||
|
|
||||||
tcp dport { 19999, 9091, 9117, 7878, 8989, 8096 } accept
|
tcp dport { 19999, 9091, 9117, 7878, 8989, 8096, 2342 } accept
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
chain input {
|
chain input {
|
||||||
@@ -71,14 +71,12 @@ in
|
|||||||
RouteTable = routeTable;
|
RouteTable = routeTable;
|
||||||
};
|
};
|
||||||
wireguardPeers = [
|
wireguardPeers = [
|
||||||
|
# AirVPN NL
|
||||||
{
|
{
|
||||||
# AirVPN NL
|
Endpoint = "2a00:1678:1337:2329:e5f:35d4:4404:ef9f:1637";
|
||||||
wireguardPeerConfig = {
|
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
|
||||||
Endpoint = "2a00:1678:1337:2329:e5f:35d4:4404:ef9f:1637";
|
PresharedKeyFile = config.age.secrets."${pskFile}".path;
|
||||||
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
|
AllowedIPs = [ "0.0.0.0/0" "::/0" ];
|
||||||
PresharedKeyFile = config.age.secrets."${pskFile}".path;
|
|
||||||
AllowedIPs = [ "0.0.0.0/0" "::/0" ];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -94,7 +92,7 @@ in
|
|||||||
matchConfig.Name = "vpn";
|
matchConfig.Name = "vpn";
|
||||||
address = [ "10.182.97.37/32" "fd7d:76ee:e68f:a993:735d:ef5e:6907:b122/128" ];
|
address = [ "10.182.97.37/32" "fd7d:76ee:e68f:a993:735d:ef5e:6907:b122/128" ];
|
||||||
dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ];
|
dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ];
|
||||||
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [
|
routingPolicyRules = [
|
||||||
{
|
{
|
||||||
Family = "both";
|
Family = "both";
|
||||||
SuppressPrefixLength = 0;
|
SuppressPrefixLength = 0;
|
||||||
|
|||||||
@@ -40,11 +40,6 @@ in
|
|||||||
secrets = {
|
secrets = {
|
||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAQM9U1e/XcUCyMJITrpAHjAGahpqkZCmtX6pJkYzuks";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAQM9U1e/XcUCyMJITrpAHjAGahpqkZCmtX6pJkYzuks";
|
||||||
files = {
|
files = {
|
||||||
"dhparams.pem" = {
|
|
||||||
owner = "acme";
|
|
||||||
group = "acme";
|
|
||||||
mode = "440";
|
|
||||||
};
|
|
||||||
"pdns-file-records.key" = {
|
"pdns-file-records.key" = {
|
||||||
owner = "acme";
|
owner = "acme";
|
||||||
group = "acme";
|
group = "acme";
|
||||||
@@ -72,6 +67,13 @@ in
|
|||||||
|
|
||||||
firewall = {
|
firewall = {
|
||||||
tcp.allowed = [ "http" "https" 8448 ];
|
tcp.allowed = [ "http" "https" 8448 ];
|
||||||
|
extraRules = ''
|
||||||
|
table inet nat {
|
||||||
|
chain postrouting {
|
||||||
|
oifname host0 snat ip6 to ${assignments.internal.ipv6.address}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
nginx-sso = {
|
nginx-sso = {
|
||||||
@@ -169,7 +171,7 @@ in
|
|||||||
"*.${config.networking.domain}"
|
"*.${config.networking.domain}"
|
||||||
];
|
];
|
||||||
dnsProvider = "exec";
|
dnsProvider = "exec";
|
||||||
credentialsFile =
|
environmentFile =
|
||||||
let
|
let
|
||||||
script = pkgs.writeShellScript "lego-update-int.sh" ''
|
script = pkgs.writeShellScript "lego-update-int.sh" ''
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -200,7 +202,7 @@ in
|
|||||||
"*.s3.${pubDomain}"
|
"*.s3.${pubDomain}"
|
||||||
];
|
];
|
||||||
dnsProvider = "cloudflare";
|
dnsProvider = "cloudflare";
|
||||||
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
environmentFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
|
||||||
postRun =
|
postRun =
|
||||||
let
|
let
|
||||||
sshKey = config.age.secrets."middleman/mailcow-ssh.key".path;
|
sshKey = config.age.secrets."middleman/mailcow-ssh.key".path;
|
||||||
@@ -239,6 +241,9 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
|
recommendedBrotliSettings = true;
|
||||||
|
# Uh so nginx is hanging with zstd enabled... maybe let's not for now
|
||||||
|
# recommendedZstdSettings = true;
|
||||||
clientMaxBodySize = "0";
|
clientMaxBodySize = "0";
|
||||||
serverTokens = true;
|
serverTokens = true;
|
||||||
resolver = {
|
resolver = {
|
||||||
@@ -246,8 +251,10 @@ in
|
|||||||
valid = "5s";
|
valid = "5s";
|
||||||
};
|
};
|
||||||
proxyResolveWhileRunning = true;
|
proxyResolveWhileRunning = true;
|
||||||
sslDhparam = config.age.secrets."dhparams.pem".path;
|
|
||||||
|
|
||||||
|
appendConfig = ''
|
||||||
|
worker_processes auto;
|
||||||
|
'';
|
||||||
# Based on recommended*Settings, but probably better to be explicit about these
|
# Based on recommended*Settings, but probably better to be explicit about these
|
||||||
appendHttpConfig = ''
|
appendHttpConfig = ''
|
||||||
${baseHttpConfig}
|
${baseHttpConfig}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
let
|
let
|
||||||
inherit (builtins) mapAttrs toJSON;
|
inherit (builtins) mapAttrs toJSON;
|
||||||
inherit (lib) mkMerge mkDefault genAttrs flatten concatStringsSep;
|
inherit (lib) mkMerge mkDefault genAttrs flatten concatStringsSep;
|
||||||
inherit (lib.my.c) pubDomain;
|
inherit (lib.my.c) pubDomain home;
|
||||||
inherit (lib.my.c.nginx) proxyHeaders;
|
inherit (lib.my.c.nginx) proxyHeaders;
|
||||||
inherit (config.networking) domain;
|
inherit (config.networking) domain;
|
||||||
|
|
||||||
@@ -35,7 +35,6 @@ let
|
|||||||
# For clients
|
# For clients
|
||||||
(mkWellKnown "matrix/client" (toJSON {
|
(mkWellKnown "matrix/client" (toJSON {
|
||||||
"m.homeserver".base_url = "https://matrix.nul.ie";
|
"m.homeserver".base_url = "https://matrix.nul.ie";
|
||||||
"org.matrix.msc3575.proxy".url = "https://matrix-syncv3.nul.ie";
|
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -50,6 +49,7 @@ let
|
|||||||
"/.well-known/webfinger".return = "301 https://toot.nul.ie$request_uri";
|
"/.well-known/webfinger".return = "301 https://toot.nul.ie$request_uri";
|
||||||
"/.well-known/nodeinfo".return = "301 https://toot.nul.ie$request_uri";
|
"/.well-known/nodeinfo".return = "301 https://toot.nul.ie$request_uri";
|
||||||
"/.well-known/host-meta".return = "301 https://toot.nul.ie$request_uri";
|
"/.well-known/host-meta".return = "301 https://toot.nul.ie$request_uri";
|
||||||
|
"/.well-known/atproto-did".return = "301 https://pds.nul.ie$request_uri";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -80,6 +80,10 @@ in
|
|||||||
sha256 = "018wh6ps19n7323fi44njzj9yd4wqslc90dykbwfyscv7bgxhlar";
|
sha256 = "018wh6ps19n7323fi44njzj9yd4wqslc90dykbwfyscv7bgxhlar";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "ssh.pub";
|
||||||
|
path = lib.my.c.sshKeyFiles.me;
|
||||||
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
wellKnown
|
wellKnown
|
||||||
@@ -145,7 +149,7 @@ in
|
|||||||
|
|
||||||
"pass.${pubDomain}" =
|
"pass.${pubDomain}" =
|
||||||
let
|
let
|
||||||
upstream = "http://vaultwarden-ctr.${domain}";
|
upstream = "http://vaultwarden-ctr.${domain}:8080";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
locations = {
|
locations = {
|
||||||
@@ -182,10 +186,6 @@ in
|
|||||||
];
|
];
|
||||||
useACMEHost = pubDomain;
|
useACMEHost = pubDomain;
|
||||||
};
|
};
|
||||||
"matrix-syncv3.${pubDomain}" = {
|
|
||||||
locations."/".proxyPass = "http://chatterbox-ctr.${domain}:8009";
|
|
||||||
useACMEHost = pubDomain;
|
|
||||||
};
|
|
||||||
|
|
||||||
"element.${pubDomain}" =
|
"element.${pubDomain}" =
|
||||||
let
|
let
|
||||||
@@ -206,7 +206,8 @@ in
|
|||||||
# Currently it seems like single quotes aren't escaped like they should be...
|
# Currently it seems like single quotes aren't escaped like they should be...
|
||||||
conf = {
|
conf = {
|
||||||
brand = "/dev/player0 Matrix";
|
brand = "/dev/player0 Matrix";
|
||||||
showLabsSettings = true;
|
show_labs_settings = true;
|
||||||
|
default_country_code = "IE";
|
||||||
disable_guests = true;
|
disable_guests = true;
|
||||||
default_server_config = {
|
default_server_config = {
|
||||||
"m.homeserver" = {
|
"m.homeserver" = {
|
||||||
@@ -214,9 +215,8 @@ in
|
|||||||
server_name = "nul.ie";
|
server_name = "nul.ie";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
roomDirectory.servers = [
|
room_directory.servers = [
|
||||||
"nul.ie"
|
"nul.ie"
|
||||||
"netsoc.ie"
|
|
||||||
"matrix.org"
|
"matrix.org"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -327,6 +327,15 @@ in
|
|||||||
useACMEHost = pubDomain;
|
useACMEHost = pubDomain;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"pds.nul.ie" = {
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://toot-ctr.${domain}:3000";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = proxyHeaders;
|
||||||
|
};
|
||||||
|
useACMEHost = pubDomain;
|
||||||
|
};
|
||||||
|
|
||||||
"share.${pubDomain}" = {
|
"share.${pubDomain}" = {
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://object-ctr.${domain}:9090";
|
proxyPass = "http://object-ctr.${domain}:9090";
|
||||||
@@ -338,16 +347,13 @@ in
|
|||||||
|
|
||||||
"stuff.${pubDomain}" = {
|
"stuff.${pubDomain}" = {
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
basicAuthFile = config.age.secrets."middleman/htpasswd".path;
|
proxyPass = "http://jackflix-ctr.${domain}:3923";
|
||||||
root = "/mnt/media/stuff";
|
|
||||||
extraConfig = ''
|
|
||||||
fancyindex on;
|
|
||||||
fancyindex_show_dotfiles on;
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
useACMEHost = pubDomain;
|
useACMEHost = pubDomain;
|
||||||
};
|
};
|
||||||
"public.${pubDomain}" = {
|
"public.${pubDomain}" = {
|
||||||
|
onlySSL = false;
|
||||||
|
addSSL = true;
|
||||||
serverAliases = [ "p.${pubDomain}" ];
|
serverAliases = [ "p.${pubDomain}" ];
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
root = "/mnt/media/public";
|
root = "/mnt/media/public";
|
||||||
@@ -364,7 +370,12 @@ in
|
|||||||
useACMEHost = pubDomain;
|
useACMEHost = pubDomain;
|
||||||
};
|
};
|
||||||
"mc-rail.${pubDomain}" = {
|
"mc-rail.${pubDomain}" = {
|
||||||
locations."/".proxyPass = "http://simpcraft-staging-oci.${domain}:3876";
|
locations."/".proxyPass = "http://simpcraft-oci.${domain}:3876";
|
||||||
|
useACMEHost = pubDomain;
|
||||||
|
};
|
||||||
|
|
||||||
|
"mc-map-kink.${pubDomain}" = {
|
||||||
|
locations."/".proxyPass = "http://kinkcraft-oci.${domain}:8100";
|
||||||
useACMEHost = pubDomain;
|
useACMEHost = pubDomain;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -384,6 +395,56 @@ in
|
|||||||
};
|
};
|
||||||
useACMEHost = pubDomain;
|
useACMEHost = pubDomain;
|
||||||
};
|
};
|
||||||
|
"pb.${pubDomain}" = {
|
||||||
|
locations."/".proxyPass = "http://object-ctr.${domain}:8088";
|
||||||
|
useACMEHost = pubDomain;
|
||||||
|
};
|
||||||
|
"photos.${pubDomain}" = {
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://jackflix-ctr.${domain}:2342";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = proxyHeaders;
|
||||||
|
};
|
||||||
|
useACMEHost = pubDomain;
|
||||||
|
};
|
||||||
|
|
||||||
|
"pront.${pubDomain}" = mkMerge [
|
||||||
|
{
|
||||||
|
locations."/" = mkMerge [
|
||||||
|
{
|
||||||
|
proxyPass = "http://stream-hi.${home.domain}:5000";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = proxyHeaders;
|
||||||
|
}
|
||||||
|
(ssoLoc "generic")
|
||||||
|
];
|
||||||
|
locations."~* ^/webcam/(.*)" = mkMerge [
|
||||||
|
{
|
||||||
|
proxyPass = "http://stream-hi.${home.domain}:5050/$1$is_args$args";
|
||||||
|
extraConfig = proxyHeaders;
|
||||||
|
}
|
||||||
|
(ssoLoc "generic")
|
||||||
|
];
|
||||||
|
useACMEHost = pubDomain;
|
||||||
|
}
|
||||||
|
(ssoServer "generic")
|
||||||
|
];
|
||||||
|
"hass.${pubDomain}" = {
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://hass-ctr.${home.domain}:8123";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = proxyHeaders;
|
||||||
|
};
|
||||||
|
useACMEHost = pubDomain;
|
||||||
|
};
|
||||||
|
"hass-john.${pubDomain}" = {
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://john-valorant-tun.${domain}:8123";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = proxyHeaders;
|
||||||
|
};
|
||||||
|
useACMEHost = pubDomain;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
minio =
|
minio =
|
||||||
@@ -395,10 +456,13 @@ in
|
|||||||
ignore_invalid_headers off;
|
ignore_invalid_headers off;
|
||||||
'';
|
'';
|
||||||
|
|
||||||
nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar\.\S+)$'';
|
nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar.*|serve\/.+)$'';
|
||||||
nixCacheHeaders = ''
|
nixCacheHeaders = ''
|
||||||
add_header Cache-Control $nix_cache_control;
|
add_header Cache-Control $nix_cache_control;
|
||||||
add_header Expires $nix_expires;
|
add_header Expires $nix_expires;
|
||||||
|
|
||||||
|
brotli on;
|
||||||
|
brotli_types application/x-nix-archive;
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -440,9 +504,11 @@ in
|
|||||||
|
|
||||||
"nix-cache.${pubDomain}" = {
|
"nix-cache.${pubDomain}" = {
|
||||||
locations = {
|
locations = {
|
||||||
"/".proxyPass = "http://${host}:8069";
|
"/" = {
|
||||||
|
proxyPass = "http://${host}:5000";
|
||||||
|
};
|
||||||
"~ ${nixCacheableRegex}" = {
|
"~ ${nixCacheableRegex}" = {
|
||||||
proxyPass = "http://${host}:8069";
|
proxyPass = "http://${host}:5000";
|
||||||
extraConfig = nixCacheHeaders;
|
extraConfig = nixCacheHeaders;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ in
|
|||||||
{
|
{
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
{
|
{
|
||||||
|
fileSystems = {
|
||||||
|
"/var/lib/harmonia" = {
|
||||||
|
device = "/mnt/nix-cache";
|
||||||
|
options = [ "bind" ];
|
||||||
|
fsType = "none";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
my = {
|
my = {
|
||||||
deploy.enable = false;
|
deploy.enable = false;
|
||||||
server.enable = true;
|
server.enable = true;
|
||||||
@@ -48,7 +56,9 @@ in
|
|||||||
group = config.my.user.config.group;
|
group = config.my.user.config.group;
|
||||||
};
|
};
|
||||||
"object/atticd.env" = {};
|
"object/atticd.env" = {};
|
||||||
|
"nix-cache.key" = {};
|
||||||
"object/hedgedoc.env" = {};
|
"object/hedgedoc.env" = {};
|
||||||
|
"object/wastebin.env" = {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,7 +67,9 @@ in
|
|||||||
9000 9001
|
9000 9001
|
||||||
config.services.sharry.config.bind.port
|
config.services.sharry.config.bind.port
|
||||||
8069
|
8069
|
||||||
|
5000
|
||||||
config.services.hedgedoc.settings.port
|
config.services.hedgedoc.settings.port
|
||||||
|
8088
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,14 +78,32 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users = with lib.my.c.ids; let inherit (config.services.atticd) user group; in {
|
users = with lib.my.c.ids; mkMerge [
|
||||||
users."${user}" = {
|
(let inherit (config.services.atticd) user group; in {
|
||||||
isSystemUser = true;
|
users."${user}" = {
|
||||||
uid = uids.atticd;
|
isSystemUser = true;
|
||||||
group = group;
|
uid = uids.atticd;
|
||||||
};
|
group = group;
|
||||||
groups."${user}".gid = gids.atticd;
|
};
|
||||||
};
|
groups."${user}".gid = gids.atticd;
|
||||||
|
})
|
||||||
|
{
|
||||||
|
users = {
|
||||||
|
harmonia = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "harmonia";
|
||||||
|
shell = pkgs.bashInteractive;
|
||||||
|
openssh.authorizedKeys.keyFiles = [
|
||||||
|
lib.my.c.sshKeyFiles.harmonia
|
||||||
|
lib.my.c.sshKeyFiles.me
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
groups = {
|
||||||
|
harmonia = { };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
systemd = {
|
systemd = {
|
||||||
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||||
@@ -91,7 +121,9 @@ in
|
|||||||
MINIO_BROWSER_REDIRECT_URL = "https://minio.nul.ie";
|
MINIO_BROWSER_REDIRECT_URL = "https://minio.nul.ie";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
sharry = awaitPostgres;
|
sharry = awaitPostgres;
|
||||||
|
|
||||||
atticd = mkMerge [
|
atticd = mkMerge [
|
||||||
awaitPostgres
|
awaitPostgres
|
||||||
{
|
{
|
||||||
@@ -102,6 +134,26 @@ in
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
harmonia-dev = {
|
||||||
|
# environment.RUST_LOG = mkForce "trace";
|
||||||
|
# serviceConfig = {
|
||||||
|
# StateDirectory = "harmonia";
|
||||||
|
# DynamicUser = mkForce false;
|
||||||
|
# };
|
||||||
|
};
|
||||||
|
harmonia-daemon = {
|
||||||
|
# environment.RUST_LOG = mkForce "trace";
|
||||||
|
preStart = ''
|
||||||
|
${config.nix.package}/bin/nix store info --store /var/lib/harmonia
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
User = "harmonia";
|
||||||
|
Group = "harmonia";
|
||||||
|
StateDirectory = "harmonia";
|
||||||
|
DynamicUser = mkForce false;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -111,6 +163,9 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# TODO/FIXME: this is bad...
|
||||||
|
nixpkgs.config.permittedInsecurePackages = [ "minio-2025-10-15T17-29-55Z" ];
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
minio = {
|
minio = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -181,8 +236,8 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
atticd = {
|
atticd = {
|
||||||
enable = true;
|
enable = false;
|
||||||
credentialsFile = config.age.secrets."object/atticd.env".path;
|
environmentFile = config.age.secrets."object/atticd.env".path;
|
||||||
settings = {
|
settings = {
|
||||||
listen = "[::]:8069";
|
listen = "[::]:8069";
|
||||||
allowed-hosts = [ "nix-cache.${pubDomain}" ];
|
allowed-hosts = [ "nix-cache.${pubDomain}" ];
|
||||||
@@ -201,6 +256,23 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
harmonia-dev = {
|
||||||
|
daemon = {
|
||||||
|
enable = true;
|
||||||
|
storeDir = "/nix/store";
|
||||||
|
dbPath = "/var/lib/harmonia/nix/var/nix/db/db.sqlite";
|
||||||
|
};
|
||||||
|
cache = {
|
||||||
|
enable = true;
|
||||||
|
signKeyPaths = [ config.age.secrets."nix-cache.key".path ];
|
||||||
|
settings = {
|
||||||
|
priority = 30;
|
||||||
|
virtual_nix_store = "/nix/store";
|
||||||
|
real_nix_store = "/var/lib/harmonia/nix/store";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
hedgedoc = {
|
hedgedoc = {
|
||||||
enable = true;
|
enable = true;
|
||||||
environmentFile = config.age.secrets."object/hedgedoc.env".path;
|
environmentFile = config.age.secrets."object/hedgedoc.env".path;
|
||||||
@@ -220,6 +292,15 @@ in
|
|||||||
allowEmailRegister = false;
|
allowEmailRegister = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wastebin = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
WASTEBIN_MAX_BODY_SIZE = 67108864; # 16 MiB
|
||||||
|
WASTEBIN_PASSWORD_SALT = "TeGhaemeer0Siez3";
|
||||||
|
};
|
||||||
|
secretFile = config.age.secrets."object/wastebin.env".path;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(mkIf config.my.build.isDevVM {
|
(mkIf config.my.build.isDevVM {
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c.colony) domain prefixes qclk;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixos.systems.qclk = { config, ... }: {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
nixpkgs = "mine";
|
||||||
|
rendered = config.configuration.config.my.asContainer;
|
||||||
|
|
||||||
|
assignments = {
|
||||||
|
internal = {
|
||||||
|
name = "qclk-ctr";
|
||||||
|
inherit domain;
|
||||||
|
ipv4.address = net.cidr.host 10 prefixes.ctrs.v4;
|
||||||
|
ipv6 = {
|
||||||
|
iid = "::a";
|
||||||
|
address = net.cidr.host 10 prefixes.ctrs.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
qclk = {
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 1 prefixes.qclk.v4;
|
||||||
|
gateway = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = { lib, pkgs, config, assignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) concatStringsSep mkMerge mkIf mkForce;
|
||||||
|
inherit (lib.my) networkdAssignment;
|
||||||
|
|
||||||
|
apiPort = 8080;
|
||||||
|
|
||||||
|
instances = [
|
||||||
|
{
|
||||||
|
host = 2;
|
||||||
|
wgKey = "D7z1FhcdxpnrGCE0wBW5PZb5BKuhCu6tcZ/5ZaYxdwQ=";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
ipFor = i: net.cidr.host i.host prefixes.qclk.v4;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
environment = {
|
||||||
|
systemPackages = with pkgs; [
|
||||||
|
wireguard-tools
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
deploy.enable = false;
|
||||||
|
server.enable = true;
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC1kcfvahYmSk8IJKaUIcGkhxf/8Yse2XnU7Qqgcglyq";
|
||||||
|
files = {
|
||||||
|
"qclk/wg.key" = {
|
||||||
|
group = "systemd-network";
|
||||||
|
mode = "440";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
firewall = {
|
||||||
|
udp.allowed = [ qclk.wgPort ];
|
||||||
|
extraRules = ''
|
||||||
|
table inet filter {
|
||||||
|
chain input {
|
||||||
|
iifname management tcp dport ${toString apiPort} accept
|
||||||
|
}
|
||||||
|
chain forward {
|
||||||
|
iifname host0 oifname management ip saddr { ${concatStringsSep ", " lib.my.c.as211024.trusted.v4} } accept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table inet nat {
|
||||||
|
chain postrouting {
|
||||||
|
iifname host0 oifname management snat ip to ${assignments.qclk.ipv4.address}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
network = {
|
||||||
|
netdevs."30-management" = {
|
||||||
|
netdevConfig = {
|
||||||
|
Name = "management";
|
||||||
|
Kind = "wireguard";
|
||||||
|
};
|
||||||
|
wireguardConfig = {
|
||||||
|
PrivateKeyFile = config.age.secrets."qclk/wg.key".path;
|
||||||
|
ListenPort = qclk.wgPort;
|
||||||
|
};
|
||||||
|
wireguardPeers = map (i: {
|
||||||
|
PublicKey = i.wgKey;
|
||||||
|
AllowedIPs = [ (ipFor i) ];
|
||||||
|
}) instances;
|
||||||
|
};
|
||||||
|
networks = {
|
||||||
|
"30-container-host0" = networkdAssignment "host0" assignments.internal;
|
||||||
|
|
||||||
|
"30-management" = networkdAssignment "management" assignments.qclk;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -26,6 +26,8 @@ in
|
|||||||
let
|
let
|
||||||
inherit (lib) mkMerge mkIf genAttrs;
|
inherit (lib) mkMerge mkIf genAttrs;
|
||||||
inherit (lib.my) networkdAssignment systemdAwaitPostgres;
|
inherit (lib.my) networkdAssignment systemdAwaitPostgres;
|
||||||
|
|
||||||
|
pdsPort = 3000;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
@@ -36,7 +38,7 @@ in
|
|||||||
|
|
||||||
secrets = {
|
secrets = {
|
||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSslLkDe54AKYzxdtKD70zcU72W0EpYsfbdJ6UFq0QK";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSslLkDe54AKYzxdtKD70zcU72W0EpYsfbdJ6UFq0QK";
|
||||||
files = genAttrs
|
files = (genAttrs
|
||||||
(map (f: "toot/${f}") [
|
(map (f: "toot/${f}") [
|
||||||
"postgres-password.txt"
|
"postgres-password.txt"
|
||||||
"secret-key.txt"
|
"secret-key.txt"
|
||||||
@@ -48,7 +50,12 @@ in
|
|||||||
(_: with config.services.mastodon; {
|
(_: with config.services.mastodon; {
|
||||||
owner = user;
|
owner = user;
|
||||||
inherit group;
|
inherit group;
|
||||||
});
|
})) // {
|
||||||
|
"toot/pds.env" = {
|
||||||
|
owner = "pds";
|
||||||
|
group = "pds";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
firewall = {
|
firewall = {
|
||||||
@@ -56,6 +63,7 @@ in
|
|||||||
19999
|
19999
|
||||||
|
|
||||||
"http"
|
"http"
|
||||||
|
pdsPort
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -79,7 +87,7 @@ in
|
|||||||
netdata.enable = true;
|
netdata.enable = true;
|
||||||
mastodon = mkMerge [
|
mastodon = mkMerge [
|
||||||
rec {
|
rec {
|
||||||
enable = true;
|
enable = false;
|
||||||
localDomain = extraConfig.WEB_DOMAIN; # for nginx config
|
localDomain = extraConfig.WEB_DOMAIN; # for nginx config
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
LOCAL_DOMAIN = "nul.ie";
|
LOCAL_DOMAIN = "nul.ie";
|
||||||
@@ -87,7 +95,9 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path;
|
secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path;
|
||||||
otpSecretFile = config.age.secrets."toot/otp-secret.txt".path;
|
# TODO: This was removed at some point.
|
||||||
|
# If we want to bring Mastodon back, this will probably need to be addressd.
|
||||||
|
# otpSecretFile = config.age.secrets."toot/otp-secret.txt".path;
|
||||||
vapidPrivateKeyFile = config.age.secrets."toot/vapid-key.txt".path;
|
vapidPrivateKeyFile = config.age.secrets."toot/vapid-key.txt".path;
|
||||||
vapidPublicKeyFile = toString (pkgs.writeText
|
vapidPublicKeyFile = toString (pkgs.writeText
|
||||||
"vapid-pubkey.txt"
|
"vapid-pubkey.txt"
|
||||||
@@ -155,6 +165,32 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bluesky-pds = {
|
||||||
|
enable = true;
|
||||||
|
environmentFiles = [ config.age.secrets."toot/pds.env".path ];
|
||||||
|
settings = {
|
||||||
|
PDS_HOSTNAME = "pds.nul.ie";
|
||||||
|
PDS_PORT = pdsPort;
|
||||||
|
|
||||||
|
PDS_BLOBSTORE_DISK_LOCATION = null;
|
||||||
|
PDS_BLOBSTORE_S3_BUCKET = "pds";
|
||||||
|
PDS_BLOBSTORE_S3_ENDPOINT = "https://s3.nul.ie/";
|
||||||
|
PDS_BLOBSTORE_S3_REGION = "eu-central-1";
|
||||||
|
PDS_BLOBSTORE_S3_ACCESS_KEY_ID = "pds";
|
||||||
|
PDS_BLOB_UPLOAD_LIMIT = "52428800";
|
||||||
|
|
||||||
|
PDS_EMAIL_FROM_ADDRESS = "pds@nul.ie";
|
||||||
|
|
||||||
|
PDS_DID_PLC_URL = "https://plc.directory";
|
||||||
|
PDS_INVITE_REQUIRED = "true";
|
||||||
|
PDS_BSKY_APP_VIEW_URL = "https://api.bsky.app";
|
||||||
|
PDS_BSKY_APP_VIEW_DID = "did:web:api.bsky.app";
|
||||||
|
PDS_REPORT_SERVICE_URL = "https://mod.bsky.app";
|
||||||
|
PDS_REPORT_SERVICE_DID = "did:plc:ar7c4by46qjdydhdevvrndac";
|
||||||
|
PDS_CRAWLERS = "https://bsky.network";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(mkIf config.my.build.isDevVM {
|
(mkIf config.my.build.isDevVM {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ in
|
|||||||
DOMAIN = "https://pass.${lib.my.c.pubDomain}";
|
DOMAIN = "https://pass.${lib.my.c.pubDomain}";
|
||||||
|
|
||||||
ROCKET_ADDRESS = "::";
|
ROCKET_ADDRESS = "::";
|
||||||
ROCKET_PORT = 80;
|
ROCKET_PORT = 8080;
|
||||||
|
|
||||||
SMTP_HOST = "mail.nul.ie";
|
SMTP_HOST = "mail.nul.ie";
|
||||||
SMTP_FROM = "pass@nul.ie";
|
SMTP_FROM = "pass@nul.ie";
|
||||||
@@ -99,6 +99,8 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
borgbackup.jobs.vaultwarden = {
|
borgbackup.jobs.vaultwarden = {
|
||||||
|
readWritePaths = [ "/var/lib/borgbackup" "/var/cache/borgbackup" ];
|
||||||
|
|
||||||
paths = [ vwData ];
|
paths = [ vwData ];
|
||||||
repo = "zh2855@zh2855.rsync.net:borg/vaultwarden2";
|
repo = "zh2855@zh2855.rsync.net:borg/vaultwarden2";
|
||||||
doInit = true;
|
doInit = true;
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ in
|
|||||||
interfaceName = "tailscale0";
|
interfaceName = "tailscale0";
|
||||||
extraUpFlags = [
|
extraUpFlags = [
|
||||||
"--operator=${config.my.user.config.name}"
|
"--operator=${config.my.user.config.name}"
|
||||||
"--login-server=https://ts.nul.ie"
|
"--login-server=https://hs.nul.ie"
|
||||||
"--netfilter-mode=off"
|
"--netfilter-mode=off"
|
||||||
"--advertise-exit-node"
|
"--advertise-exit-node"
|
||||||
"--advertise-routes=${advRoutes}"
|
"--advertise-routes=${advRoutes}"
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ in
|
|||||||
inherit (lib.my) networkdAssignment;
|
inherit (lib.my) networkdAssignment;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ];
|
imports = [
|
||||||
|
"${modulesPath}/profiles/qemu-guest.nix"
|
||||||
|
|
||||||
|
./containers-ext.nix
|
||||||
|
];
|
||||||
|
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
{
|
{
|
||||||
@@ -90,8 +94,8 @@ in
|
|||||||
device = "/dev/disk/by-label/minio";
|
device = "/dev/disk/by-label/minio";
|
||||||
fsType = "xfs";
|
fsType = "xfs";
|
||||||
};
|
};
|
||||||
"/mnt/atticd" = {
|
"/mnt/nix-cache" = {
|
||||||
device = "/dev/disk/by-label/atticd";
|
device = "/dev/disk/by-label/nix-cache";
|
||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -136,10 +140,10 @@ in
|
|||||||
};
|
};
|
||||||
ipv6Prefixes = [
|
ipv6Prefixes = [
|
||||||
{
|
{
|
||||||
ipv6PrefixConfig.Prefix = prefixes.ctrs.v6;
|
Prefix = prefixes.ctrs.v6;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = lib.my.c.tailscale.prefix.v4;
|
Destination = lib.my.c.tailscale.prefix.v4;
|
||||||
Gateway = allAssignments.waffletail.internal.ipv4.address;
|
Gateway = allAssignments.waffletail.internal.ipv4.address;
|
||||||
@@ -148,6 +152,11 @@ in
|
|||||||
Destination = lib.my.c.tailscale.prefix.v6;
|
Destination = lib.my.c.tailscale.prefix.v6;
|
||||||
Gateway = allAssignments.waffletail.internal.ipv6.address;
|
Gateway = allAssignments.waffletail.internal.ipv6.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Destination = prefixes.qclk.v4;
|
||||||
|
Gateway = allAssignments.qclk.internal.ipv4.address;
|
||||||
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -202,11 +211,13 @@ in
|
|||||||
object = {
|
object = {
|
||||||
bindMounts = {
|
bindMounts = {
|
||||||
"/mnt/minio".readOnly = false;
|
"/mnt/minio".readOnly = false;
|
||||||
"/mnt/atticd".readOnly = false;
|
"/mnt/nix-cache".readOnly = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
toot = {};
|
toot = {};
|
||||||
waffletail = {};
|
waffletail = {};
|
||||||
|
qclk = {};
|
||||||
|
gam = {};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
mkMerge [
|
mkMerge [
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ in
|
|||||||
valheim-oci = 2;
|
valheim-oci = 2;
|
||||||
simpcraft-oci = 3;
|
simpcraft-oci = 3;
|
||||||
simpcraft-staging-oci = 4;
|
simpcraft-staging-oci = 4;
|
||||||
|
enshrouded-oci = 5;
|
||||||
|
kevcraft-oci = 6;
|
||||||
|
kinkcraft-oci = 7;
|
||||||
|
graeme-oci = 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
|
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
|
||||||
@@ -66,6 +70,7 @@ in
|
|||||||
|
|
||||||
./valheim.nix
|
./valheim.nix
|
||||||
./minecraft
|
./minecraft
|
||||||
|
# ./enshrouded.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
@@ -108,45 +113,30 @@ in
|
|||||||
oci-containers = {
|
oci-containers = {
|
||||||
backend = "podman";
|
backend = "podman";
|
||||||
};
|
};
|
||||||
# NixOS has switched to using netavark, which is native to podman. It's currently missing an option to
|
containers.containersConf.settings.network = {
|
||||||
# disable iptables rules generation, which is very annoying.
|
network_backend = "netavark";
|
||||||
containers.containersConf.settings.network.network_backend = mkForce "cni";
|
firewall_driver = mkForce "none";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
etc = {
|
etc = {
|
||||||
"cni/net.d/90-colony.conflist".text = toJSON {
|
"containers/networks/colony.json".text = toJSON {
|
||||||
cniVersion = "0.4.0";
|
|
||||||
name = "colony";
|
name = "colony";
|
||||||
plugins = [
|
id = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||||
|
driver = "bridge";
|
||||||
|
network_interface = "oci";
|
||||||
|
ipv6_enabled = true;
|
||||||
|
internal = false;
|
||||||
|
dns_enabled = false;
|
||||||
|
subnets = [
|
||||||
{
|
{
|
||||||
type = "bridge";
|
subnet = prefixes.oci.v4;
|
||||||
bridge = "oci";
|
gateway = net.cidr.host 1 prefixes.oci.v4;
|
||||||
isGateway = true;
|
}
|
||||||
ipMasq = false;
|
{
|
||||||
hairpinMode = true;
|
subnet = prefixes.oci.v6;
|
||||||
ipam = {
|
gateway = net.cidr.host 1 prefixes.oci.v6;
|
||||||
type = "host-local";
|
|
||||||
routes = [
|
|
||||||
{ dst = "0.0.0.0/0"; }
|
|
||||||
{ dst = "::/0"; }
|
|
||||||
];
|
|
||||||
ranges = [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
subnet = prefixes.oci.v4;
|
|
||||||
gateway = net.cidr.host 1 prefixes.oci.v4;
|
|
||||||
}
|
|
||||||
]
|
|
||||||
[
|
|
||||||
{
|
|
||||||
subnet = prefixes.oci.v6;
|
|
||||||
gateway = net.cidr.host 1 prefixes.oci.v6;
|
|
||||||
}
|
|
||||||
]
|
|
||||||
];
|
|
||||||
};
|
|
||||||
capabilities.ips = true;
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{ lib, config, allAssignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) concatStringsSep;
|
||||||
|
inherit (lib.my) dockerNetAssignment;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
virtualisation.oci-containers.containers = {
|
||||||
|
enshrouded = {
|
||||||
|
image = "sknnr/enshrouded-dedicated-server@sha256:f163e8ba9caa2115d8a0a7b16c3696968242fb6fba82706d9a77a882df083497";
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
SERVER_NAME = "UWUshrouded";
|
||||||
|
# SERVER_IP = "::"; # no IPv6?? :(
|
||||||
|
TZ = "Europe/Dublin";
|
||||||
|
};
|
||||||
|
environmentFiles = [ config.age.secrets."whale2/enshrouded.env".path ];
|
||||||
|
|
||||||
|
volumes = [
|
||||||
|
"enshrouded:/home/steam/enshrouded/savegame"
|
||||||
|
];
|
||||||
|
|
||||||
|
extraOptions = [
|
||||||
|
''--network=colony:${dockerNetAssignment allAssignments "enshrouded-oci"}''
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
secrets.files = {
|
||||||
|
"whale2/enshrouded.env" = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -5,12 +5,13 @@ let
|
|||||||
|
|
||||||
# devplayer0
|
# devplayer0
|
||||||
op = "6d7d971b-ce10-435b-85c5-c99c0d8d288c";
|
op = "6d7d971b-ce10-435b-85c5-c99c0d8d288c";
|
||||||
|
kev = "703b378a-09f9-4c1d-9876-1c9305728c49";
|
||||||
whitelist = concatStringsSep "," [
|
whitelist = concatStringsSep "," [
|
||||||
op
|
op
|
||||||
"dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56" # Elderlypug
|
"dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56" # Elderlypug
|
||||||
"fcb26db2-c3ce-41aa-b588-efec79d37a8a" # Jesthral_
|
"fcb26db2-c3ce-41aa-b588-efec79d37a8a" # Jesthral_
|
||||||
"1d366062-12c0-4e29-aba7-6ab5d8c6bb05" # shr3kas0ras
|
"1d366062-12c0-4e29-aba7-6ab5d8c6bb05" # shr3kas0ras
|
||||||
"703b378a-09f9-4c1d-9876-1c9305728c49" # OROURKEIRE
|
kev
|
||||||
"f105bbe6-eda6-4a13-a8cf-894e77cab77b" # Adzerq
|
"f105bbe6-eda6-4a13-a8cf-894e77cab77b" # Adzerq
|
||||||
"1fc94979-41fb-497a-81e9-34ae24ca537a" # johnnyscrims
|
"1fc94979-41fb-497a-81e9-34ae24ca537a" # johnnyscrims
|
||||||
"d53c91df-b6e6-4463-b106-e8427d7a8d01" # BossLonus
|
"d53c91df-b6e6-4463-b106-e8427d7a8d01" # BossLonus
|
||||||
@@ -25,22 +26,20 @@ let
|
|||||||
email = "simpcraft@nul.ie"
|
email = "simpcraft@nul.ie"
|
||||||
name = "Simpcraft bot"
|
name = "Simpcraft bot"
|
||||||
'';
|
'';
|
||||||
knownHosts = pkgs.writeText "known_hosts" ''
|
|
||||||
git.nul.ie ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD023ECzYmLeXIpcGVaciPjq6UN/Sjmsys5HP/Nei5GkrUZqPa3OJ2uSXKLUSKGYdeNhxaFTPJe8Yx3TsZxMme8=
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
virtualisation.oci-containers.containers = {
|
virtualisation.oci-containers.containers = {
|
||||||
simpcraft = {
|
simpcraft = {
|
||||||
image = "ghcr.io/itzg/minecraft-server:2023.12.2-java17-alpine";
|
image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
TYPE = "MODRINTH";
|
TYPE = "MODRINTH";
|
||||||
|
|
||||||
EULA = "true";
|
EULA = "true";
|
||||||
ENABLE_QUERY = "true";
|
ENABLE_QUERY = "true";
|
||||||
|
ENABLE_RCON = "true";
|
||||||
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t §4§k-----";
|
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t §4§k-----";
|
||||||
ICON = "/ext/icon.png";
|
ICON = "/ext/icon.png";
|
||||||
|
|
||||||
@@ -52,15 +51,17 @@ in
|
|||||||
SPAWN_PROTECTION = "0";
|
SPAWN_PROTECTION = "0";
|
||||||
VIEW_DISTANCE = "20";
|
VIEW_DISTANCE = "20";
|
||||||
|
|
||||||
MAX_MEMORY = "6G";
|
MAX_MEMORY = "8G";
|
||||||
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/cdj2bSKg/Simpcraft-0.1.2.mrpack";
|
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/NGutsQSd/Simpcraft-0.2.1.mrpack";
|
||||||
|
|
||||||
TZ = "Europe/Dublin";
|
TZ = "Europe/Dublin";
|
||||||
};
|
};
|
||||||
|
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||||
|
|
||||||
volumes = [
|
volumes = [
|
||||||
"minecraft_data:/data"
|
"minecraft_data:/data"
|
||||||
"${./icon.png}:/ext/icon.png:ro"
|
"${./icon.png}:/ext/icon.png:ro"
|
||||||
|
"${fastback.gitConfig}:/data/.config/git/config:ro"
|
||||||
];
|
];
|
||||||
|
|
||||||
extraOptions = [
|
extraOptions = [
|
||||||
@@ -68,16 +69,96 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
simpcraft-staging = {
|
# simpcraft-staging = {
|
||||||
image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
|
# image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
|
||||||
|
|
||||||
|
# environment = {
|
||||||
|
# TYPE = "MODRINTH";
|
||||||
|
|
||||||
|
# EULA = "true";
|
||||||
|
# ENABLE_QUERY = "true";
|
||||||
|
# ENABLE_RCON = "true";
|
||||||
|
# MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t [staging] §4§k-----";
|
||||||
|
# ICON = "/ext/icon.png";
|
||||||
|
|
||||||
|
# EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||||
|
# WHITELIST = whitelist;
|
||||||
|
# EXISTING_OPS_FILE = "SYNCHRONIZE";
|
||||||
|
# OPS = op;
|
||||||
|
# DIFFICULTY = "normal";
|
||||||
|
# SPAWN_PROTECTION = "0";
|
||||||
|
# VIEW_DISTANCE = "20";
|
||||||
|
|
||||||
|
# MAX_MEMORY = "4G";
|
||||||
|
# MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/Ym3sIi6H/Simpcraft-0.2.0.mrpack";
|
||||||
|
|
||||||
|
# TZ = "Europe/Dublin";
|
||||||
|
# };
|
||||||
|
# environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||||
|
|
||||||
|
# volumes = [
|
||||||
|
# "minecraft_staging_data:/data"
|
||||||
|
# "${./icon.png}:/ext/icon.png:ro"
|
||||||
|
# ];
|
||||||
|
|
||||||
|
# extraOptions = [
|
||||||
|
# ''--network=colony:${dockerNetAssignment allAssignments "simpcraft-staging-oci"}''
|
||||||
|
# ];
|
||||||
|
# };
|
||||||
|
|
||||||
|
kevcraft = {
|
||||||
|
# 2025.2.1-java21-alpine
|
||||||
|
image = "itzg/minecraft-server@sha256:57e319c15e9fee63f61029a65a33acc3de85118b21a2b4bb29f351cf4a915027";
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
TYPE = "MODRINTH";
|
TYPE = "VANILLA";
|
||||||
|
VERSION = "1.20.1";
|
||||||
|
SERVER_PORT = "25567";
|
||||||
|
QUERY_PORT = "25567";
|
||||||
|
|
||||||
EULA = "true";
|
EULA = "true";
|
||||||
ENABLE_QUERY = "true";
|
ENABLE_QUERY = "true";
|
||||||
ENABLE_RCON = "true";
|
ENABLE_RCON = "true";
|
||||||
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t [staging] §4§k-----";
|
MOTD = "§4§k----- §9K§ae§bv§cc§dr§ea§ff§6t §4§k-----";
|
||||||
|
ICON = "/ext/icon.png";
|
||||||
|
|
||||||
|
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||||
|
WHITELIST = whitelist;
|
||||||
|
EXISTING_OPS_FILE = "SYNCHRONIZE";
|
||||||
|
OPS = concatStringsSep "," [ op kev ];
|
||||||
|
DIFFICULTY = "normal";
|
||||||
|
SPAWN_PROTECTION = "0";
|
||||||
|
# VIEW_DISTANCE = "20";
|
||||||
|
|
||||||
|
MAX_MEMORY = "4G";
|
||||||
|
|
||||||
|
TZ = "Europe/Dublin";
|
||||||
|
};
|
||||||
|
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||||
|
|
||||||
|
volumes = [
|
||||||
|
"kevcraft_data:/data"
|
||||||
|
"${./kev.png}:/ext/icon.png:ro"
|
||||||
|
];
|
||||||
|
|
||||||
|
extraOptions = [
|
||||||
|
''--network=colony:${dockerNetAssignment allAssignments "kevcraft-oci"}''
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
kinkcraft = {
|
||||||
|
# 2025.5.1-java21-alpine
|
||||||
|
image = "itzg/minecraft-server@sha256:de26c7128e3935f3be48fd30283f0b5a6da1b3d9f1a10c9f92502ee1ba072f7b";
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
TYPE = "MODRINTH";
|
||||||
|
SERVER_PORT = "25568";
|
||||||
|
QUERY_PORT = "25568";
|
||||||
|
|
||||||
|
EULA = "true";
|
||||||
|
ENABLE_QUERY = "true";
|
||||||
|
ENABLE_RCON = "true";
|
||||||
|
MOTD = "§4§k----- §9K§ai§bn§ck§dc§er§fa§6f§5t §4§k-----";
|
||||||
ICON = "/ext/icon.png";
|
ICON = "/ext/icon.png";
|
||||||
|
|
||||||
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||||
@@ -88,33 +169,102 @@ in
|
|||||||
SPAWN_PROTECTION = "0";
|
SPAWN_PROTECTION = "0";
|
||||||
VIEW_DISTANCE = "20";
|
VIEW_DISTANCE = "20";
|
||||||
|
|
||||||
MAX_MEMORY = "4G";
|
MAX_MEMORY = "6G";
|
||||||
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/Ym3sIi6H/Simpcraft-0.2.0.mrpack";
|
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/NGutsQSd/Simpcraft-0.2.1.mrpack";
|
||||||
|
|
||||||
TZ = "Europe/Dublin";
|
TZ = "Europe/Dublin";
|
||||||
};
|
};
|
||||||
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||||
|
|
||||||
volumes = [
|
volumes = [
|
||||||
"minecraft_staging_data:/data"
|
"kinkcraft_data:/data"
|
||||||
"${./icon.png}:/ext/icon.png:ro"
|
"${./icon.png}:/ext/icon.png:ro"
|
||||||
"${fastback.gitConfig}:/data/.config/git/config:ro"
|
|
||||||
"${fastback.knownHosts}:/data/.ssh/known_hosts:ro"
|
|
||||||
"${config.age.secrets."whale2/simpcraft-git.key".path}:/data/.ssh/id_rsa"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
extraOptions = [
|
extraOptions = [
|
||||||
''--network=colony:${dockerNetAssignment allAssignments "simpcraft-staging-oci"}''
|
''--network=colony:${dockerNetAssignment allAssignments "kinkcraft-oci"}''
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
graeme = {
|
||||||
|
# 2026.2.1-java21-alpine
|
||||||
|
image = "itzg/minecraft-server@sha256:82adaddfe0156f07c34228f1c1065cdbd298abc174de0a9961abb068b11beebb";
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
TYPE = "VANILLA";
|
||||||
|
SERVER_PORT = "25569";
|
||||||
|
QUERY_PORT = "25569";
|
||||||
|
|
||||||
|
EULA = "true";
|
||||||
|
ENABLE_QUERY = "true";
|
||||||
|
ENABLE_RCON = "false";
|
||||||
|
MOTD = "§4§k----- §9G§ar§ba§ce§dm§ee §4§k-----";
|
||||||
|
ICON = "/ext/icon.png";
|
||||||
|
|
||||||
|
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
|
||||||
|
WHITELIST = concatStringsSep "," [
|
||||||
|
op
|
||||||
|
"fffa146c-0bc8-421c-9e3a-3635c0aca2ea" # Scarlehh
|
||||||
|
"1ea05f48-76cc-4034-bcd3-2fa1fc5a7375" # Dario
|
||||||
|
"4bf837b1-01db-4491-a0e0-700d98542833" # JoeSpencer
|
||||||
|
"d07a9554-1b05-4b0b-b558-27e4a86e1f53" # AmyClover
|
||||||
|
];
|
||||||
|
EXISTING_OPS_FILE = "SYNCHRONIZE";
|
||||||
|
OPS = op;
|
||||||
|
DIFFICULTY = "hard";
|
||||||
|
SPAWN_PROTECTION = "0";
|
||||||
|
VIEW_DISTANCE = "20";
|
||||||
|
|
||||||
|
MAX_MEMORY = "4G";
|
||||||
|
|
||||||
|
TZ = "Europe/Dublin";
|
||||||
|
};
|
||||||
|
|
||||||
|
volumes = [
|
||||||
|
"graeme_data:/data"
|
||||||
|
"${./graeme.png}:/ext/icon.png:ro"
|
||||||
|
];
|
||||||
|
|
||||||
|
extraOptions = [
|
||||||
|
''--network=colony:${dockerNetAssignment allAssignments "graeme-oci"}''
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
borgbackup.jobs.simpcraft =
|
||||||
|
let
|
||||||
|
rconCommand = cmd: ''${pkgs.mcrcon}/bin/mcrcon -H simpcraft-oci -p "$RCON_PASSWORD" "${cmd}"'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
paths = [ "/var/lib/containers/storage/volumes/minecraft_data/_data/world" ];
|
||||||
|
repo = "/var/lib/containers/backup/simpcraft";
|
||||||
|
doInit = true;
|
||||||
|
encryption.mode = "none";
|
||||||
|
compression = "zstd,10";
|
||||||
|
# every ~15 minutes offset from 5 minute intervals (Minecraft seems to save at precise times?)
|
||||||
|
startAt = "*:03,17,33,47";
|
||||||
|
prune.keep = {
|
||||||
|
within = "12H";
|
||||||
|
hourly = 48;
|
||||||
|
};
|
||||||
|
readWritePaths = [ "/var/lib/borgbackup" "/var/cache/borgbackup" ];
|
||||||
|
|
||||||
|
# Avoid Minecraft poking the files while we back up
|
||||||
|
preHook = rconCommand "save-off";
|
||||||
|
postHook = rconCommand "save-on";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
services = {
|
||||||
|
borgbackup-job-simpcraft.serviceConfig.EnvironmentFile = [ config.age.secrets."whale2/simpcraft.env".path ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
my = {
|
my = {
|
||||||
secrets.files = {
|
secrets.files = {
|
||||||
"whale2/simpcraft.env" = {};
|
"whale2/simpcraft.env" = {};
|
||||||
"whale2/simpcraft-git.key" = {
|
|
||||||
owner = "1000";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -1,7 +1,8 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c.home) domain vlans prefixes;
|
inherit (lib.my.c) networkd;
|
||||||
|
inherit (lib.my.c.home) domain vlans prefixes vips roceBootModules;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
nixos.systems.castle = {
|
nixos.systems.castle = {
|
||||||
@@ -15,7 +16,7 @@ in
|
|||||||
ipv4 = {
|
ipv4 = {
|
||||||
address = net.cidr.host 40 prefixes.hi.v4;
|
address = net.cidr.host 40 prefixes.hi.v4;
|
||||||
mask = 22;
|
mask = 22;
|
||||||
gateway = null;
|
gateway = vips.hi.v4;
|
||||||
};
|
};
|
||||||
ipv6 = {
|
ipv6 = {
|
||||||
iid = "::3:1";
|
iid = "::3:1";
|
||||||
@@ -35,7 +36,7 @@ in
|
|||||||
cpu = {
|
cpu = {
|
||||||
amd.updateMicrocode = true;
|
amd.updateMicrocode = true;
|
||||||
};
|
};
|
||||||
opengl.extraPackages = with pkgs; [
|
graphics.extraPackages = with pkgs; [
|
||||||
intel-media-driver
|
intel-media-driver
|
||||||
];
|
];
|
||||||
bluetooth.enable = true;
|
bluetooth.enable = true;
|
||||||
@@ -47,7 +48,7 @@ in
|
|||||||
timeout = 10;
|
timeout = 10;
|
||||||
};
|
};
|
||||||
kernelPackages = lib.my.c.kernel.latest pkgs;
|
kernelPackages = lib.my.c.kernel.latest pkgs;
|
||||||
kernelModules = [ "kvm-amd" ];
|
kernelModules = [ "kvm-amd" "dm-snapshot" ];
|
||||||
kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ];
|
kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ];
|
||||||
kernelPatches = [
|
kernelPatches = [
|
||||||
# {
|
# {
|
||||||
@@ -57,27 +58,40 @@ in
|
|||||||
# }
|
# }
|
||||||
];
|
];
|
||||||
initrd = {
|
initrd = {
|
||||||
availableKernelModules = [ "thunderbolt" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sd_mod" ];
|
availableKernelModules = [
|
||||||
|
"thunderbolt" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sd_mod"
|
||||||
|
"8021q"
|
||||||
|
] ++ roceBootModules;
|
||||||
|
systemd.network = {
|
||||||
|
netdevs = mkVLAN "lan-hi" vlans.hi;
|
||||||
|
networks = {
|
||||||
|
"10-et100g" = {
|
||||||
|
matchConfig.Name = "et100g";
|
||||||
|
vlan = [ "lan-hi" ];
|
||||||
|
linkConfig.RequiredForOnline = "no";
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
};
|
||||||
|
"20-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
binfmt.emulatedSystems = [ "aarch64-linux" "armv7l-linux" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
fileSystems = {
|
fileSystems = {
|
||||||
"/boot" = {
|
|
||||||
device = "/dev/disk/by-partuuid/8ce4248a-3ee4-f44f-801f-064a628b4d6e";
|
|
||||||
fsType = "vfat";
|
|
||||||
};
|
|
||||||
"/nix" = {
|
"/nix" = {
|
||||||
device = "/dev/disk/by-partuuid/2da23a1d-2daf-d943-b91e-fc175f3dad07";
|
device = "/dev/nvmeof/nix";
|
||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
};
|
};
|
||||||
|
|
||||||
"/persist" = {
|
"/persist" = {
|
||||||
device = "/dev/disk/by-partuuid/f4c80d4f-a022-e941-b5d1-fe2e65e444b9";
|
device = "/dev/nvmeof/persist";
|
||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
neededForBoot = true;
|
neededForBoot = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
"/home" = {
|
"/home" = {
|
||||||
device = "/dev/disk/by-partuuid/992a93cf-6c9c-324b-b0ce-f8eb2d1ce10d";
|
device = "/dev/nvmeof/home";
|
||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -95,26 +109,29 @@ in
|
|||||||
};
|
};
|
||||||
fstrim.enable = true;
|
fstrim.enable = true;
|
||||||
|
|
||||||
resolved = {
|
resolved.settings.Resolve.LLMNR = mkForce true;
|
||||||
enable = true;
|
|
||||||
extraConfig = mkForce "";
|
|
||||||
dnssec = "false";
|
|
||||||
};
|
|
||||||
|
|
||||||
|
pipewire.extraConfig.pipewire = {
|
||||||
|
"10-buffer"."context.properties" = {
|
||||||
|
"default.clock.quantum" = 128;
|
||||||
|
"default.clock.max-quantum" = 128;
|
||||||
|
};
|
||||||
|
};
|
||||||
blueman.enable = true;
|
blueman.enable = true;
|
||||||
|
avahi.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
virt-manager.enable = true;
|
virt-manager.enable = true;
|
||||||
wireshark = {
|
wireshark = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs.wireshark-qt;
|
package = pkgs.wireshark;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
virtualisation.libvirtd.enable = true;
|
virtualisation.libvirtd.enable = true;
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
domain = "h.${lib.my.c.pubDomain}";
|
inherit domain;
|
||||||
firewall.enable = false;
|
firewall.enable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,25 +147,22 @@ in
|
|||||||
mstflint
|
mstflint
|
||||||
qperf
|
qperf
|
||||||
ethtool
|
ethtool
|
||||||
|
android-tools
|
||||||
];
|
];
|
||||||
environment.etc = {
|
|
||||||
"pipewire/pipewire.conf.d/sample-size.conf".text = ''
|
|
||||||
context.properties = {
|
|
||||||
default.clock.quantum = 128
|
|
||||||
default.clock.max-quantum = 128
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
nix = {
|
nix = {
|
||||||
gc.automatic = false;
|
gc.automatic = false;
|
||||||
|
settings = {
|
||||||
|
experimental-features = [ "recursive-nix" ];
|
||||||
|
system-features = [ "nixos-test" "benchmark" "big-parallel" "kvm" "recursive-nix" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd = {
|
systemd = {
|
||||||
network = {
|
network = {
|
||||||
wait-online.enable = false;
|
|
||||||
netdevs = mkMerge [
|
netdevs = mkMerge [
|
||||||
(mkVLAN "lan-hi" vlans.hi)
|
(mkVLAN "lan-hi" vlans.hi)
|
||||||
|
(mkVLAN "lan-lo" vlans.lo)
|
||||||
];
|
];
|
||||||
links = {
|
links = {
|
||||||
"10-et2.5g" = {
|
"10-et2.5g" = {
|
||||||
@@ -163,30 +177,37 @@ in
|
|||||||
matchConfig.PermanentMACAddress = "24:8a:07:a8:fe:3a";
|
matchConfig.PermanentMACAddress = "24:8a:07:a8:fe:3a";
|
||||||
linkConfig = {
|
linkConfig = {
|
||||||
Name = "et100g";
|
Name = "et100g";
|
||||||
MTUBytes = "9000";
|
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
networks = {
|
networks = {
|
||||||
"50-lan" = {
|
"30-et100g" = {
|
||||||
matchConfig.Name = "et2.5g";
|
|
||||||
DHCP = "no";
|
|
||||||
address = [ "10.16.7.1/16" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
"50-et100g" = {
|
|
||||||
matchConfig.Name = "et100g";
|
matchConfig.Name = "et100g";
|
||||||
vlan = [ "lan-hi" ];
|
vlan = [ "lan-hi" "lan-lo" ];
|
||||||
networkConfig.IPv6AcceptRA = false;
|
networkConfig.IPv6AcceptRA = false;
|
||||||
};
|
};
|
||||||
"60-lan-hi" = mkMerge [
|
"40-lan-hi" = mkMerge [
|
||||||
(networkdAssignment "lan-hi" assignments.hi)
|
(networkdAssignment "lan-hi" assignments.hi)
|
||||||
{
|
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||||
DHCP = "yes";
|
{ networkConfig.KeepConfiguration = "static"; }
|
||||||
matchConfig.Name = "lan-hi";
|
|
||||||
linkConfig.MTUBytes = "9000";
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
"45-lan-lo" = {
|
||||||
|
matchConfig.Name = "lan-lo";
|
||||||
|
networkConfig = {
|
||||||
|
DHCP = "ipv4";
|
||||||
|
IPv6AcceptRA = true;
|
||||||
|
UseDomains = false;
|
||||||
|
};
|
||||||
|
dhcpV4Config = {
|
||||||
|
UseDNS = false;
|
||||||
|
UseGateway = false;
|
||||||
|
};
|
||||||
|
ipv6AcceptRAConfig = {
|
||||||
|
UseDNS = false;
|
||||||
|
UseGateway = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -205,15 +226,19 @@ in
|
|||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
jacktrip
|
jacktrip
|
||||||
qpwgraph
|
qpwgraph
|
||||||
# TODO: seems to be borked (infinite recursion???)
|
boardie
|
||||||
# (writeShellScriptBin "boardie" ''
|
|
||||||
# exec pw-jack ${boardie}/bin/boardie "$@"
|
|
||||||
# '')
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
programs = {
|
||||||
|
claude-code = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
blueman-applet.enable = true;
|
blueman-applet.enable = true;
|
||||||
|
easyeffects.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
wayland.windowManager.sway = {
|
wayland.windowManager.sway = {
|
||||||
@@ -222,6 +247,7 @@ in
|
|||||||
HDMI-A-1 = {
|
HDMI-A-1 = {
|
||||||
transform = "270";
|
transform = "270";
|
||||||
position = "0 0";
|
position = "0 0";
|
||||||
|
bg = "${./his-team-player.jpg} fill";
|
||||||
};
|
};
|
||||||
DP-1 = {
|
DP-1 = {
|
||||||
mode = "2560x1440@170Hz";
|
mode = "2560x1440@170Hz";
|
||||||
@@ -243,11 +269,19 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
#deploy.generate.system.mode = "boot";
|
#deploy.generate.system.mode = "boot";
|
||||||
deploy.node.hostname = "castle.box.${config.networking.domain}";
|
|
||||||
secrets = {
|
secrets = {
|
||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr";
|
||||||
};
|
};
|
||||||
nvme.uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b";
|
netboot.client = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
nvme = {
|
||||||
|
uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b";
|
||||||
|
boot = {
|
||||||
|
nqn = "nqn.2016-06.io.spdk:castle";
|
||||||
|
address = "192.168.68.80";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
firewall = {
|
firewall = {
|
||||||
enable = false;
|
enable = false;
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 MiB |
@@ -53,7 +53,7 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
kernelModules = [ "kvm-amd" ];
|
kernelModules = [ "dm-raid" "kvm-amd" ];
|
||||||
kernelParams = [ "amd_iommu=on" ];
|
kernelParams = [ "amd_iommu=on" ];
|
||||||
initrd = {
|
initrd = {
|
||||||
availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
|
availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
|
||||||
@@ -94,7 +94,7 @@ in
|
|||||||
extraOptions = [ "-A /var/log/smartd/" "--interval=600" ];
|
extraOptions = [ "-A /var/log/smartd/" "--interval=600" ];
|
||||||
};
|
};
|
||||||
udev.extraRules = ''
|
udev.extraRules = ''
|
||||||
ACTION=="add", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="mlx5_core", ENV{ID_PATH}=="pci-0000:44:00.0", ATTR{device/sriov_numvfs}="3"
|
ACTION=="add", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="mlx5_core", ENV{ID_PATH}=="pci-0000:44:00.0", ATTR{device/sriov_numvfs}="4"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,9 +126,9 @@ in
|
|||||||
};
|
};
|
||||||
linkConfig.Name = "et1g0";
|
linkConfig.Name = "et1g0";
|
||||||
};
|
};
|
||||||
"10-lan-core" = {
|
"10-lan-core-phy" = {
|
||||||
matchConfig.PermanentMACAddress = "e0:d5:5e:68:0c:70";
|
matchConfig.PermanentMACAddress = "e0:d5:5e:68:0c:70";
|
||||||
linkConfig.Name = "lan-core";
|
linkConfig.Name = "lan-core-phy";
|
||||||
};
|
};
|
||||||
"10-et100g" = {
|
"10-et100g" = {
|
||||||
matchConfig = {
|
matchConfig = {
|
||||||
@@ -145,6 +145,12 @@ in
|
|||||||
netdevs = mkMerge [
|
netdevs = mkMerge [
|
||||||
(mkVLAN "lan-hi" vlans.hi)
|
(mkVLAN "lan-hi" vlans.hi)
|
||||||
(mkVLAN "lan-lo-phy" vlans.lo)
|
(mkVLAN "lan-lo-phy" vlans.lo)
|
||||||
|
{
|
||||||
|
"25-lan-core".netdevConfig = {
|
||||||
|
Name = "lan-core";
|
||||||
|
Kind = "bridge";
|
||||||
|
};
|
||||||
|
}
|
||||||
{
|
{
|
||||||
"25-lan-lo".netdevConfig = {
|
"25-lan-lo".netdevConfig = {
|
||||||
Name = "lan-lo";
|
Name = "lan-lo";
|
||||||
@@ -188,10 +194,23 @@ in
|
|||||||
VLANId=${toString vlans.hi}
|
VLANId=${toString vlans.hi}
|
||||||
LinkState=yes
|
LinkState=yes
|
||||||
MACAddress=52:54:00:ac:15:a9
|
MACAddress=52:54:00:ac:15:a9
|
||||||
|
|
||||||
|
# sfh bridge
|
||||||
|
[SR-IOV]
|
||||||
|
VirtualFunction=3
|
||||||
|
VLANId=${toString vlans.hi}
|
||||||
|
LinkState=yes
|
||||||
|
MACAddress=52:54:00:90:34:95
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
"60-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
"60-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||||
|
|
||||||
|
"50-lan-core-phy" = {
|
||||||
|
matchConfig.Name = "lan-core-phy";
|
||||||
|
networkConfig = {
|
||||||
|
Bridge = "lan-core";
|
||||||
|
} // networkd.noL3;
|
||||||
|
};
|
||||||
"50-lan-lo-phy" = {
|
"50-lan-lo-phy" = {
|
||||||
matchConfig.Name = "lan-lo-phy";
|
matchConfig.Name = "lan-lo-phy";
|
||||||
networkConfig = {
|
networkConfig = {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
|
fstrim.enable = true;
|
||||||
netdata.enable = true;
|
netdata.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,12 @@ in
|
|||||||
hostnqn =
|
hostnqn =
|
||||||
"nqn.2014-08.org.nvmexpress:uuid:2230b066-a674-4f45-a1dc-f7727b3a9e7b";
|
"nqn.2014-08.org.nvmexpress:uuid:2230b066-a674-4f45-a1dc-f7727b3a9e7b";
|
||||||
serial = "SPDK00000000000002";
|
serial = "SPDK00000000000002";
|
||||||
|
}) ++ (nvmfBdev {
|
||||||
|
bdev = "NVMeRaidp3";
|
||||||
|
nqn = "nqn.2016-06.io.spdk:sfh";
|
||||||
|
hostnqn =
|
||||||
|
"nqn.2014-08.org.nvmexpress:uuid:85d7df36-0de0-431b-b06e-51f7c0a455b4";
|
||||||
|
serial = "SPDK00000000000003";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./cellar
|
./cellar
|
||||||
./river.nix
|
./river.nix
|
||||||
|
./sfh
|
||||||
];
|
];
|
||||||
|
|
||||||
nixos.systems.palace.configuration = { lib, pkgs, config, systems, allAssignments, ... }:
|
nixos.systems.palace.configuration = { lib, pkgs, config, systems, allAssignments, ... }:
|
||||||
@@ -57,11 +58,11 @@
|
|||||||
|
|
||||||
systemd.services =
|
systemd.services =
|
||||||
let
|
let
|
||||||
awaitCellar = {
|
awaitVM = system: {
|
||||||
after = [ "vm@cellar.service" ];
|
after = [ "vm@${system}.service" ];
|
||||||
bindsTo = [ "vm@cellar.service" ];
|
bindsTo = [ "vm@${system}.service" ];
|
||||||
preStart = ''
|
preStart = ''
|
||||||
until ${pkgs.netcat}/bin/nc -w1 -z ${allAssignments.cellar.hi.ipv4.address} 22; do
|
until ${pkgs.netcat}/bin/nc -w1 -z ${allAssignments.${system}.hi.ipv4.address} 22; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
@@ -81,13 +82,13 @@
|
|||||||
vtapUnit = "sys-subsystem-net-devices-vm\\x2det1g0.device";
|
vtapUnit = "sys-subsystem-net-devices-vm\\x2det1g0.device";
|
||||||
in
|
in
|
||||||
mkMerge [
|
mkMerge [
|
||||||
awaitCellar
|
(awaitVM "cellar")
|
||||||
{
|
{
|
||||||
requires = [ vtapUnit ];
|
requires = [ vtapUnit ];
|
||||||
after = [ vtapUnit ];
|
after = [ vtapUnit ];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
"vm@sfh" = awaitCellar;
|
"vm@sfh" = (awaitVM "river");
|
||||||
};
|
};
|
||||||
|
|
||||||
my = {
|
my = {
|
||||||
@@ -171,18 +172,40 @@
|
|||||||
};
|
};
|
||||||
memory = 32768;
|
memory = 32768;
|
||||||
cleanShutdown.timeout = 120;
|
cleanShutdown.timeout = 120;
|
||||||
networks.netboot = {
|
networks = {
|
||||||
bridge = "lan-lo";
|
netboot = {
|
||||||
waitOnline = "carrier";
|
bridge = "lan-lo";
|
||||||
mac = "52:54:00:a5:7e:93";
|
waitOnline = "carrier";
|
||||||
extraOptions.bootindex = 1;
|
mac = "52:54:00:a5:7e:93";
|
||||||
|
extraOptions.bootindex = 1;
|
||||||
|
};
|
||||||
|
core = {
|
||||||
|
bridge = "lan-core";
|
||||||
|
ifname = "vm-sfh-core";
|
||||||
|
waitOnline = "carrier";
|
||||||
|
mac = "52:54:00:72:67:51";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
drives = [
|
||||||
|
(vm.lvmDisk' "hdds" "frigate")
|
||||||
|
];
|
||||||
hostDevices = {
|
hostDevices = {
|
||||||
et100g0vf2 = {
|
et100g0vf2 = {
|
||||||
index = 0;
|
index = 0;
|
||||||
hostBDF = "44:00.3";
|
hostBDF = "44:00.3";
|
||||||
};
|
};
|
||||||
|
et100g0vf3 = {
|
||||||
|
index = 1;
|
||||||
|
hostBDF = "44:00.4";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
qemuFlags = [
|
||||||
|
"device qemu-xhci,id=xhci"
|
||||||
|
# Front-right port?
|
||||||
|
"device usb-host,hostbus=1,hostport=4"
|
||||||
|
# Front-left port
|
||||||
|
"device usb-host,hostbus=1,hostport=3"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,20 +8,16 @@
|
|||||||
|
|
||||||
configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }:
|
configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }:
|
||||||
let
|
let
|
||||||
|
inherit (lib) mkForce mkMerge mkIf;
|
||||||
inherit (lib.my) networkdAssignment mkVLAN;
|
inherit (lib.my) networkdAssignment mkVLAN;
|
||||||
inherit (lib.my.c) networkd;
|
inherit (lib.my.c) networkd;
|
||||||
inherit (lib.my.c.home) vlans;
|
inherit (lib.my.c.home) vlans domain prefixes roceBootModules;
|
||||||
|
|
||||||
lanLink = {
|
# Digiweb currently delivers the ISP VLAN (pon-isp, 10) single-tagged, so PPPoE runs on a
|
||||||
matchConfig = {
|
# VLAN 10 sitting directly on the physical WAN link. Flip this to true to nest it back
|
||||||
Driver = "mlx5_core";
|
# inside the wan-pon (131) transport VLAN — double-stacking also needs QinQ (tag-stacking)
|
||||||
PermanentMACAddress = "52:54:00:8a:8a:f2";
|
# on the switch feeding the ONT, or the BRAS never answers PADI.
|
||||||
};
|
wanStacked = false;
|
||||||
linkConfig = {
|
|
||||||
Name = "lan";
|
|
||||||
MTUBytes = toString lib.my.c.home.hiMTU;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
@@ -30,29 +26,17 @@
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
boot = {
|
boot = {
|
||||||
kernelModules = [ "kvm-intel" ];
|
kernelModules = [ "kvm-amd" ];
|
||||||
kernelParams = [ "console=ttyS0,115200n8" ];
|
kernelParams = [ "console=ttyS0,115200n8" ];
|
||||||
initrd = {
|
initrd = {
|
||||||
availableKernelModules = [
|
availableKernelModules = [
|
||||||
"virtio_pci" "ahci" "sr_mod" "virtio_blk"
|
"virtio_pci" "ahci" "sr_mod" "virtio_blk"
|
||||||
"ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" "8021q"
|
"8021q"
|
||||||
"rdma_cm" "iw_cm" "ib_cm" "nvme_core" "nvme_rdma"
|
] ++ roceBootModules;
|
||||||
];
|
kernelModules = [ "dm-snapshot" ];
|
||||||
kernelModules = [ "dm-snapshot" "nvme-fabrics" ];
|
|
||||||
systemd = {
|
systemd = {
|
||||||
extraBin = with pkgs; {
|
|
||||||
dmesg = "${util-linux}/bin/dmesg";
|
|
||||||
ip = "${iproute2}/bin/ip";
|
|
||||||
};
|
|
||||||
extraConfig = ''
|
|
||||||
DefaultTimeoutStartSec=50
|
|
||||||
DefaultDeviceTimeoutSec=50
|
|
||||||
'';
|
|
||||||
network = {
|
network = {
|
||||||
enable = true;
|
# Don't need to put the link config here, they're copied from main config
|
||||||
wait-online.enable = true;
|
|
||||||
|
|
||||||
links."10-lan" = lanLink;
|
|
||||||
netdevs = mkVLAN "lan-hi" vlans.hi;
|
netdevs = mkVLAN "lan-hi" vlans.hi;
|
||||||
networks = {
|
networks = {
|
||||||
"20-lan" = {
|
"20-lan" = {
|
||||||
@@ -70,9 +54,6 @@
|
|||||||
|
|
||||||
hardware = {
|
hardware = {
|
||||||
enableRedistributableFirmware = true;
|
enableRedistributableFirmware = true;
|
||||||
cpu = {
|
|
||||||
intel.updateMicrocode = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fileSystems = {
|
fileSystems = {
|
||||||
@@ -96,11 +77,73 @@
|
|||||||
boot.thin.enable = true;
|
boot.thin.enable = true;
|
||||||
dmeventd.enable = true;
|
dmeventd.enable = true;
|
||||||
};
|
};
|
||||||
|
fstrim.enable = true;
|
||||||
|
|
||||||
|
# TODO: re-enable once scheduling is tested
|
||||||
|
networkd-dispatcher.enable = mkForce false;
|
||||||
|
|
||||||
|
pppd = {
|
||||||
|
enable = true;
|
||||||
|
peers.digiweb = {
|
||||||
|
autostart = true;
|
||||||
|
enable = true;
|
||||||
|
# Password is shared across all Digiweb customers, so no need for a secret
|
||||||
|
config = ''
|
||||||
|
plugin pppoe.so wan-vlan-inner
|
||||||
|
name "digiweb@nga.digiweb.ie"
|
||||||
|
password "digiweb"
|
||||||
|
noipdefault
|
||||||
|
# no usepeerdns: we ignore Digiweb's resolvers and use the local recursive resolver
|
||||||
|
lcp-echo-interval 1
|
||||||
|
lcp-echo-failure 4
|
||||||
|
noauth
|
||||||
|
persist
|
||||||
|
maxfail 0
|
||||||
|
holdoff 5
|
||||||
|
mtu 1500
|
||||||
|
mru 1500
|
||||||
|
noaccomp
|
||||||
|
default-asyncmap
|
||||||
|
ifname wan
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# PPPoE WAN (Digiweb): pppd owns the `wan` interface on top of VLAN 10, and its
|
||||||
|
# ip-up/ip-down hooks toggle the shared wan-online.target. Nothing else Wants the
|
||||||
|
# target, so it stays inactive until the link is actually up.
|
||||||
|
systemd.targets.wan-online.unitConfig.DefaultDependencies = false;
|
||||||
|
|
||||||
|
environment.etc = {
|
||||||
|
ppp-up = {
|
||||||
|
target = "ppp/ip-up";
|
||||||
|
mode = "0755";
|
||||||
|
text = ''
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
${pkgs.iproute2}/bin/ip route add default dev wan scope link metric 100
|
||||||
|
${config.systemd.package}/bin/systemctl --no-block start wan-online.target
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
ppp-down = {
|
||||||
|
target = "ppp/ip-down";
|
||||||
|
mode = "0755";
|
||||||
|
text = ''
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
${config.systemd.package}/bin/systemctl --no-block stop wan-online.target
|
||||||
|
${pkgs.iproute2}/bin/ip route del default dev wan scope link metric 100
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.network = {
|
systemd.network = {
|
||||||
|
netdevs = mkMerge [
|
||||||
|
(mkIf wanStacked (mkVLAN "wan-vlan-outer" vlans.wan-pon))
|
||||||
|
(mkVLAN "wan-vlan-inner" vlans.pon-isp)
|
||||||
|
];
|
||||||
|
|
||||||
links = {
|
links = {
|
||||||
"10-wan" = {
|
"10-wan-old" = {
|
||||||
matchConfig = {
|
matchConfig = {
|
||||||
# Matching against MAC address seems to break VLAN interfaces
|
# Matching against MAC address seems to break VLAN interfaces
|
||||||
# (since they share the same MAC address)
|
# (since they share the same MAC address)
|
||||||
@@ -108,17 +151,52 @@
|
|||||||
PermanentMACAddress = "e0:d5:5e:68:0c:6e";
|
PermanentMACAddress = "e0:d5:5e:68:0c:6e";
|
||||||
};
|
};
|
||||||
linkConfig = {
|
linkConfig = {
|
||||||
Name = "wan";
|
Name = "wan-old";
|
||||||
RxBufferSize = 4096;
|
RxBufferSize = 4096;
|
||||||
TxBufferSize = 4096;
|
TxBufferSize = 4096;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
"10-lan" = lanLink;
|
"10-lan" = {
|
||||||
|
matchConfig = {
|
||||||
|
Driver = "mlx5_core";
|
||||||
|
PermanentMACAddress = "52:54:00:8a:8a:f2";
|
||||||
|
};
|
||||||
|
linkConfig = {
|
||||||
|
Name = "lan";
|
||||||
|
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# So we don't drop the IP we use to connect to NVMe-oF!
|
networks = {
|
||||||
networks."60-lan-hi".networkConfig.KeepConfiguration = "static";
|
"55-lan" = {
|
||||||
|
# outer transport VLAN when stacked, otherwise the ISP VLAN directly on lan
|
||||||
|
vlan = [ (if wanStacked then "wan-vlan-outer" else "wan-vlan-inner") ];
|
||||||
|
};
|
||||||
|
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||||
|
"60-lan-hi".networkConfig.KeepConfiguration = "static";
|
||||||
|
|
||||||
|
"70-wan-vlan-outer" = mkIf wanStacked {
|
||||||
|
matchConfig.Name = "wan-vlan-outer";
|
||||||
|
vlan = [ "wan-vlan-inner" ];
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
# baby jumbo: carries the inner VLAN's frames, whose 4B tag counts as payload
|
||||||
|
# at this layer, so it needs 1512 (inner's 1508B payload + the inner 802.1Q tag)
|
||||||
|
linkConfig.MTUBytes = "1512";
|
||||||
|
};
|
||||||
|
# pppd attaches PPPoE to this; just needs to be up with no L3. Hangs off
|
||||||
|
# wan-vlan-outer when stacked, otherwise directly off lan (see "55-lan").
|
||||||
|
"71-wan-vlan-inner" = {
|
||||||
|
matchConfig.Name = "wan-vlan-inner";
|
||||||
|
linkConfig = {
|
||||||
|
RequiredForOnline = "no";
|
||||||
|
# baby jumbo: PPPoE's 8B overhead leaves a clean 1500 on ppp
|
||||||
|
MTUBytes = "1508";
|
||||||
|
};
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
my = {
|
my = {
|
||||||
@@ -134,6 +212,14 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
netboot.server = {
|
||||||
|
enable = true;
|
||||||
|
ip = assignments.lo.ipv4.address;
|
||||||
|
host = "boot.${domain}";
|
||||||
|
allowedPrefixes = with prefixes; [ hi.v4 hi.v6 lo.v4 lo.v6 ];
|
||||||
|
instances = [ "sfh" "castle" ];
|
||||||
|
};
|
||||||
|
|
||||||
deploy.node.hostname = "192.168.68.1";
|
deploy.node.hostname = "192.168.68.1";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./unifi.nix
|
||||||
|
./hass.nix
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c) pubDomain;
|
||||||
|
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixos.systems.hass = { config, ... }: {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
nixpkgs = "mine";
|
||||||
|
rendered = config.configuration.config.my.asContainer;
|
||||||
|
|
||||||
|
assignments = {
|
||||||
|
hi = {
|
||||||
|
name = "hass-ctr";
|
||||||
|
altNames = [ "frigate" ];
|
||||||
|
inherit domain;
|
||||||
|
mtu = hiMTU;
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 103 prefixes.hi.v4;
|
||||||
|
mask = 22;
|
||||||
|
gateway = vips.hi.v4;
|
||||||
|
};
|
||||||
|
ipv6 = {
|
||||||
|
iid = "::5:3";
|
||||||
|
address = net.cidr.host (65536*5+3) prefixes.hi.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
lo = {
|
||||||
|
name = "hass-ctr-lo";
|
||||||
|
inherit domain;
|
||||||
|
mtu = 1500;
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 103 prefixes.lo.v4;
|
||||||
|
mask = 21;
|
||||||
|
gateway = null;
|
||||||
|
};
|
||||||
|
ipv6 = {
|
||||||
|
iid = "::5:3";
|
||||||
|
address = net.cidr.host (65536*5+3) prefixes.lo.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = { lib, config, pkgs, assignments, allAssignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkMerge mkIf mkForce;
|
||||||
|
inherit (lib.my) networkdAssignment;
|
||||||
|
|
||||||
|
hassCli = pkgs.writeShellScriptBin "hass-cli" ''
|
||||||
|
export HASS_SERVER="http://localhost:${toString config.services.home-assistant.config.http.server_port}"
|
||||||
|
export HASS_TOKEN="$(< ${config.age.secrets."hass/cli-token.txt".path})"
|
||||||
|
exec ${pkgs.home-assistant-cli}/bin/hass-cli "$@"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
my = {
|
||||||
|
deploy.enable = false;
|
||||||
|
server.enable = true;
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpYX2WbYwUqHp8bFFf0eHFrqrR8xp8IheguA054F8V4";
|
||||||
|
files = {
|
||||||
|
"hass/cli-token.txt" = {
|
||||||
|
owner = config.my.user.config.name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
firewall = {
|
||||||
|
tcp.allowed = [ "http" 1883 ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
systemPackages = with pkgs; [
|
||||||
|
usbutils
|
||||||
|
hassCli
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
network.networks = {
|
||||||
|
"80-container-host0" = networkdAssignment "host0" assignments.hi;
|
||||||
|
"80-container-lan-lo" = networkdAssignment "lan-lo" assignments.lo;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
mosquitto = {
|
||||||
|
enable = true;
|
||||||
|
listeners = [
|
||||||
|
{
|
||||||
|
omitPasswordAuth = true;
|
||||||
|
settings = {
|
||||||
|
allow_anonymous = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
go2rtc = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
streams = {
|
||||||
|
reolink_living_room = [
|
||||||
|
# "http://reolink-living-room.${domain}/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=admin#video=copy#audio=copy#audio=opus"
|
||||||
|
"rtsp://admin:@reolink-living-room:554/h264Preview_01_main"
|
||||||
|
];
|
||||||
|
webcam_office = [
|
||||||
|
"ffmpeg:device?video=/dev/video0&video_size=1024x576#video=h264"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
frigate = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "frigate.${domain}";
|
||||||
|
settings = {
|
||||||
|
mqtt = {
|
||||||
|
enabled = true;
|
||||||
|
host = "localhost";
|
||||||
|
topic_prefix = "frigate";
|
||||||
|
};
|
||||||
|
|
||||||
|
cameras = {
|
||||||
|
reolink_living_room = {
|
||||||
|
ffmpeg.inputs = [
|
||||||
|
{
|
||||||
|
path = "rtsp://127.0.0.1:8554/reolink_living_room";
|
||||||
|
input_args = "preset-rtsp-restream";
|
||||||
|
roles = [ "record" "detect" ];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
detect = {
|
||||||
|
enabled = false;
|
||||||
|
};
|
||||||
|
record = {
|
||||||
|
enabled = true;
|
||||||
|
retain.days = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
webcam_office = {
|
||||||
|
ffmpeg.inputs = [
|
||||||
|
{
|
||||||
|
path = "rtsp://127.0.0.1:8554/webcam_office";
|
||||||
|
input_args = "preset-rtsp-restream";
|
||||||
|
roles = [ "record" "detect" ];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
detect.enabled = false;
|
||||||
|
record = {
|
||||||
|
enabled = true;
|
||||||
|
retain.days = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
home-assistant =
|
||||||
|
let
|
||||||
|
cfg = config.services.home-assistant;
|
||||||
|
|
||||||
|
pyirishrail = ps: ps.buildPythonPackage rec {
|
||||||
|
pname = "pyirishrail";
|
||||||
|
version = "0.0.2";
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "ttroy50";
|
||||||
|
repo = "pyirishrail";
|
||||||
|
tag = version;
|
||||||
|
hash = "sha256-NgARqhcXP0lgGpgBRiNtQaSn9JcRNtCcZPljcL7t3Xc=";
|
||||||
|
};
|
||||||
|
|
||||||
|
dependencies = with ps; [
|
||||||
|
requests
|
||||||
|
];
|
||||||
|
|
||||||
|
pyproject = true;
|
||||||
|
build-system = [ ps.setuptools ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
extraComponents = [
|
||||||
|
"default_config"
|
||||||
|
"esphome"
|
||||||
|
"google_translate"
|
||||||
|
|
||||||
|
"met"
|
||||||
|
"zha"
|
||||||
|
"denonavr"
|
||||||
|
"webostv"
|
||||||
|
"androidtv_remote"
|
||||||
|
"heos"
|
||||||
|
"mqtt"
|
||||||
|
"wled"
|
||||||
|
];
|
||||||
|
extraPackages = python3Packages: with python3Packages; [
|
||||||
|
zlib-ng
|
||||||
|
isal
|
||||||
|
|
||||||
|
gtts
|
||||||
|
(pyirishrail python3Packages)
|
||||||
|
];
|
||||||
|
customComponents = with pkgs.home-assistant-custom-components; [
|
||||||
|
alarmo
|
||||||
|
frigate
|
||||||
|
west_wood_club
|
||||||
|
];
|
||||||
|
|
||||||
|
configWritable = false;
|
||||||
|
openFirewall = true;
|
||||||
|
config = {
|
||||||
|
default_config = {};
|
||||||
|
homeassistant = {
|
||||||
|
name = "Home";
|
||||||
|
unit_system = "metric";
|
||||||
|
currency = "EUR";
|
||||||
|
country = "IE";
|
||||||
|
time_zone = "Europe/Dublin";
|
||||||
|
external_url = "https://hass.${pubDomain}";
|
||||||
|
internal_url = "http://hass-ctr.${domain}:${toString cfg.config.http.server_port}";
|
||||||
|
};
|
||||||
|
http = {
|
||||||
|
use_x_forwarded_for = true;
|
||||||
|
trusted_proxies = with allAssignments.middleman.internal; [
|
||||||
|
ipv4.address
|
||||||
|
ipv6.address
|
||||||
|
];
|
||||||
|
ip_ban_enabled = false;
|
||||||
|
};
|
||||||
|
automation = "!include automations.yaml";
|
||||||
|
script = "!include scripts.yaml";
|
||||||
|
scene = "!include scenes.yaml";
|
||||||
|
|
||||||
|
sensor = [
|
||||||
|
{
|
||||||
|
platform = "irish_rail_transport";
|
||||||
|
name = "To Work from Home";
|
||||||
|
station = "Glenageary";
|
||||||
|
stops_at = "Dublin Connolly";
|
||||||
|
direction = "Northbound";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
platform = "irish_rail_transport";
|
||||||
|
name = "To Home from Work";
|
||||||
|
station = "Dublin Connolly";
|
||||||
|
stops_at = "Glenageary";
|
||||||
|
direction = "Southbound";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixos.systems.unifi = { config, ... }: {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
nixpkgs = "mine";
|
||||||
|
rendered = config.configuration.config.my.asContainer;
|
||||||
|
|
||||||
|
assignments = {
|
||||||
|
hi = {
|
||||||
|
name = "unifi-ctr";
|
||||||
|
inherit domain;
|
||||||
|
mtu = hiMTU;
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 100 prefixes.hi.v4;
|
||||||
|
mask = 22;
|
||||||
|
gateway = vips.hi.v4;
|
||||||
|
};
|
||||||
|
ipv6 = {
|
||||||
|
iid = "::5:1";
|
||||||
|
address = net.cidr.host (65536*5+1) prefixes.hi.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
core = {
|
||||||
|
inherit domain;
|
||||||
|
name = "unifi-ctr-core";
|
||||||
|
mtu = 1500;
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 21 prefixes.core.v4;
|
||||||
|
gateway = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = { lib, config, pkgs, assignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkMerge mkIf mkForce;
|
||||||
|
inherit (lib.my) networkdAssignment;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
my = {
|
||||||
|
deploy.enable = false;
|
||||||
|
server.enable = true;
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKdgcziQki/RH7E+NH2bYnzSVKaJ27905Yo5TcOjSh/U";
|
||||||
|
files = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
firewall = {
|
||||||
|
tcp.allowed = [ 8443 ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
network.networks = {
|
||||||
|
"80-container-host0" = networkdAssignment "host0" assignments.hi;
|
||||||
|
"80-lan-core" = networkdAssignment "lan-core" assignments.core;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
unifi = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
unifiPackage = pkgs.unifi;
|
||||||
|
mongodbPackage = pkgs.mongodb-7_0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c.home) domain prefixes vips hiMTU roceBootModules;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./containers ];
|
||||||
|
|
||||||
|
config.nixos.systems.sfh = {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
nixpkgs = "mine";
|
||||||
|
home-manager = "mine";
|
||||||
|
|
||||||
|
assignments = {
|
||||||
|
hi = {
|
||||||
|
inherit domain;
|
||||||
|
mtu = hiMTU;
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 81 prefixes.hi.v4;
|
||||||
|
mask = 22;
|
||||||
|
gateway = vips.hi.v4;
|
||||||
|
};
|
||||||
|
ipv6 = {
|
||||||
|
iid = "::4:2";
|
||||||
|
address = net.cidr.host (65536*4+2) prefixes.hi.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mapAttrs mkMerge mkForce;
|
||||||
|
inherit (lib.my) networkdAssignment;
|
||||||
|
inherit (lib.my.c) networkd;
|
||||||
|
inherit (lib.my.c.home) domain;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
"${modulesPath}/profiles/qemu-guest.nix"
|
||||||
|
];
|
||||||
|
|
||||||
|
config = {
|
||||||
|
boot = {
|
||||||
|
kernelModules = [ "kvm-amd" ];
|
||||||
|
kernelParams = [ "console=ttyS0,115200n8" ];
|
||||||
|
initrd = {
|
||||||
|
availableKernelModules = [
|
||||||
|
"virtio_pci" "ahci" "sr_mod" "virtio_blk"
|
||||||
|
] ++ roceBootModules;
|
||||||
|
kernelModules = [ "dm-snapshot" ];
|
||||||
|
systemd = {
|
||||||
|
network = {
|
||||||
|
networks = {
|
||||||
|
"20-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware = {
|
||||||
|
enableRedistributableFirmware = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems = {
|
||||||
|
"/nix" = {
|
||||||
|
device = "/dev/main/nix";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
"/persist" = {
|
||||||
|
device = "/dev/main/persist";
|
||||||
|
fsType = "ext4";
|
||||||
|
neededForBoot = true;
|
||||||
|
};
|
||||||
|
"/mnt/frigate" = {
|
||||||
|
device = "/dev/disk/by-label/frigate";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking = { inherit domain; };
|
||||||
|
|
||||||
|
services = {
|
||||||
|
lvm = {
|
||||||
|
boot.thin.enable = true;
|
||||||
|
dmeventd.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
systemPackages = with pkgs; [
|
||||||
|
usbutils
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.network = {
|
||||||
|
links = {
|
||||||
|
"10-lan-hi" = {
|
||||||
|
matchConfig = {
|
||||||
|
Driver = "mlx5_core";
|
||||||
|
PermanentMACAddress = "52:54:00:ac:15:a9";
|
||||||
|
};
|
||||||
|
linkConfig = {
|
||||||
|
Name = "lan-hi";
|
||||||
|
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"10-lan-hi-ctrs" = {
|
||||||
|
matchConfig = {
|
||||||
|
Driver = "mlx5_core";
|
||||||
|
PermanentMACAddress = "52:54:00:90:34:95";
|
||||||
|
};
|
||||||
|
linkConfig = {
|
||||||
|
Name = "lan-hi-ctrs";
|
||||||
|
MTUBytes = toString lib.my.c.home.hiMTU;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"10-lan-core-ctrs" = {
|
||||||
|
matchConfig = {
|
||||||
|
Driver = "virtio_net";
|
||||||
|
PermanentMACAddress = "52:54:00:72:67:51";
|
||||||
|
};
|
||||||
|
linkConfig.Name = "lan-core-ctrs";
|
||||||
|
};
|
||||||
|
"10-lan-lo-ctrs" = {
|
||||||
|
matchConfig = {
|
||||||
|
Driver = "virtio_net";
|
||||||
|
PermanentMACAddress = "52:54:00:a5:7e:93";
|
||||||
|
};
|
||||||
|
linkConfig.Name = "lan-lo-ctrs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networks = {
|
||||||
|
"30-lan-hi" = mkMerge [
|
||||||
|
(networkdAssignment "lan-hi" assignments.hi)
|
||||||
|
# So we don't drop the IP we use to connect to NVMe-oF!
|
||||||
|
{ networkConfig.KeepConfiguration = "static"; }
|
||||||
|
];
|
||||||
|
"30-lan-hi-ctrs" = {
|
||||||
|
matchConfig.Name = "lan-hi-ctrs";
|
||||||
|
linkConfig.RequiredForOnline = "no";
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
};
|
||||||
|
"30-lan-core-ctrs" = {
|
||||||
|
matchConfig.Name = "lan-core-ctrs";
|
||||||
|
linkConfig.RequiredForOnline = "no";
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
};
|
||||||
|
"30-lan-lo-ctrs" = {
|
||||||
|
matchConfig.Name = "lan-lo-ctrs";
|
||||||
|
linkConfig.RequiredForOnline = "no";
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.nspawn = {
|
||||||
|
hass = {
|
||||||
|
networkConfig = {
|
||||||
|
MACVLAN = mkForce "lan-hi-ctrs:host0 lan-lo-ctrs:lan-lo";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
unifi = {
|
||||||
|
networkConfig = {
|
||||||
|
MACVLAN = mkForce "lan-hi-ctrs:host0 lan-core-ctrs:lan-core";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services = {
|
||||||
|
"systemd-nspawn@hass".serviceConfig.DeviceAllow = [
|
||||||
|
"char-ttyUSB rw"
|
||||||
|
"char-video4linux rw"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
secrets = {
|
||||||
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAAaav5Se1E/AbqEXmADryVszYfNDscyP6jrWioN57R7";
|
||||||
|
};
|
||||||
|
server.enable = true;
|
||||||
|
|
||||||
|
netboot.client = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
nvme = {
|
||||||
|
uuid = "85d7df36-0de0-431b-b06e-51f7c0a455b4";
|
||||||
|
boot = {
|
||||||
|
nqn = "nqn.2016-06.io.spdk:sfh";
|
||||||
|
address = "192.168.68.80";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
containers.instances =
|
||||||
|
let
|
||||||
|
instances = {
|
||||||
|
unifi = {};
|
||||||
|
hass = {
|
||||||
|
bindMounts = {
|
||||||
|
"/mnt/frigate" = {
|
||||||
|
mountPoint = "/var/lib/frigate";
|
||||||
|
readOnly = false;
|
||||||
|
};
|
||||||
|
"/dev/bus/usb/001/002".readOnly = false;
|
||||||
|
"/dev/video0".readOnly = false;
|
||||||
|
"/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_ce549704fe38ef11a2c2e5d154516304-if00-port0" = {
|
||||||
|
readOnly = false;
|
||||||
|
mountPoint = "/dev/ttyUSB0";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
mkMerge [
|
||||||
|
instances
|
||||||
|
(mapAttrs (n: i: {
|
||||||
|
networking.macVLAN = "lan-hi-ctrs";
|
||||||
|
}) instances)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -121,11 +121,9 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
resolved = {
|
resolved.settings.Resolve = {
|
||||||
llmnr = "false";
|
LLMNR = false;
|
||||||
extraConfig = ''
|
MulticastDNS = false;
|
||||||
MulticastDNS=false
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
iperf3 = {
|
iperf3 = {
|
||||||
@@ -141,25 +139,47 @@ in
|
|||||||
onState = [ "configured" ];
|
onState = [ "configured" ];
|
||||||
script = ''
|
script = ''
|
||||||
#!${pkgs.runtimeShell}
|
#!${pkgs.runtimeShell}
|
||||||
if [ $IFACE = "wan-ifb" ]; then
|
if [ "$IFACE" = "wan-ifb" ]; then
|
||||||
${pkgs.iproute2}/bin/tc filter add dev wan parent ffff: matchall action mirred egress redirect dev $IFACE
|
${pkgs.iproute2}/bin/tc filter add dev wan parent ffff: matchall action mirred egress redirect dev "$IFACE"
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nginx.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.domain = "h.${pubDomain}";
|
networking = { inherit domain; };
|
||||||
|
|
||||||
|
# Uniform "WAN is up" gate. Consumers attach to this target (via wantedBy +
|
||||||
|
# partOf) rather than depending on it, so it is never pulled in / prematurely
|
||||||
|
# activated. Each box wires up how the target actually gets reached: stream
|
||||||
|
# gates it on networkd's wait-online, river drives it from the pppd hooks.
|
||||||
|
systemd.targets.wan-online.description = "WAN is online";
|
||||||
|
|
||||||
systemd.services = {
|
systemd.services = {
|
||||||
ipsec =
|
ipsec = {
|
||||||
let
|
after = [ "wan-online.target" ];
|
||||||
waitOnline = "systemd-networkd-wait-online@wan.service";
|
# strongswan/libreswan force wantedBy=multi-user.target; drop it so the
|
||||||
in
|
# target is a true gate rather than mere ordering. This matters most on
|
||||||
{
|
# river, where the target is hook-driven and not in the boot transaction,
|
||||||
after = [ waitOnline ];
|
# so plain ordering wouldn't hold ipsec back at all. partOf re-loads ipsec
|
||||||
requires = [ waitOnline ];
|
# (re-orienting its connections) whenever the WAN drops and returns.
|
||||||
|
wantedBy = mkForce [ "wan-online.target" ];
|
||||||
|
partOf = [ "wan-online.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
ipv6-clear-default-route = {
|
||||||
|
description = "Clear IPv6 RA default route";
|
||||||
|
after = [ "wan-online.target" ];
|
||||||
|
wantedBy = [ "wan-online.target" ];
|
||||||
|
partOf = [ "wan-online.target" ];
|
||||||
|
script = ''
|
||||||
|
# Seems like we can sometimes pick up a default route somehow...
|
||||||
|
${pkgs.iproute2}/bin/ip -6 route del default via fe80::1 || true
|
||||||
|
'';
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -208,41 +228,6 @@ in
|
|||||||
in
|
in
|
||||||
mkMerge [
|
mkMerge [
|
||||||
{
|
{
|
||||||
"50-wan-ifb" = {
|
|
||||||
matchConfig.Name = "wan-ifb";
|
|
||||||
networkConfig = networkd.noL3;
|
|
||||||
extraConfig = ''
|
|
||||||
[CAKE]
|
|
||||||
Bandwidth=235M
|
|
||||||
RTTSec=10ms
|
|
||||||
PriorityQueueingPreset=besteffort
|
|
||||||
# DOCSIS preset
|
|
||||||
OverheadBytes=18
|
|
||||||
MPUBytes=64
|
|
||||||
CompensationMode=none
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"50-wan" = mkMerge [
|
|
||||||
(networkdAssignment "wan" assignments.modem)
|
|
||||||
{
|
|
||||||
matchConfig.Name = "wan";
|
|
||||||
DHCP = "ipv4";
|
|
||||||
dns = [ "127.0.0.1" "::1" ];
|
|
||||||
dhcpV4Config.UseDNS = false;
|
|
||||||
|
|
||||||
qdiscConfig = {
|
|
||||||
Parent = "ingress";
|
|
||||||
Handle = "0xffff";
|
|
||||||
};
|
|
||||||
extraConfig = ''
|
|
||||||
[CAKE]
|
|
||||||
Parent=root
|
|
||||||
Bandwidth=24M
|
|
||||||
RTTSec=1ms
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
"55-lan" = {
|
"55-lan" = {
|
||||||
matchConfig.Name = "lan";
|
matchConfig.Name = "lan";
|
||||||
vlan = [ "lan-hi" "lan-lo" "lan-untrusted" "wan-tunnel" ];
|
vlan = [ "lan-hi" "lan-lo" "lan-untrusted" "wan-tunnel" ];
|
||||||
@@ -262,7 +247,7 @@ in
|
|||||||
{
|
{
|
||||||
matchConfig.Name = "as211024";
|
matchConfig.Name = "as211024";
|
||||||
networkConfig.IPv6AcceptRA = mkForce false;
|
networkConfig.IPv6AcceptRA = mkForce false;
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = lib.my.c.colony.prefixes.all.v4;
|
Destination = lib.my.c.colony.prefixes.all.v4;
|
||||||
Gateway = allAssignments.estuary.as211024.ipv4.address;
|
Gateway = allAssignments.estuary.as211024.ipv4.address;
|
||||||
@@ -287,7 +272,7 @@ in
|
|||||||
|
|
||||||
{
|
{
|
||||||
"60-lan-hi" = {
|
"60-lan-hi" = {
|
||||||
routes = map (r: { routeConfig = r; }) [
|
routes = [
|
||||||
{
|
{
|
||||||
Destination = elemAt routersPubV4 otherIndex;
|
Destination = elemAt routersPubV4 otherIndex;
|
||||||
Gateway = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
|
Gateway = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
|
||||||
@@ -358,6 +343,16 @@ in
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chain forward-early {
|
||||||
|
type filter hook forward priority -1; policy accept;
|
||||||
|
|
||||||
|
# MSS clamping to workaround IPv6 PMTUD being broken...
|
||||||
|
tcp flags syn tcp option maxseg size set rt mtu counter
|
||||||
|
|
||||||
|
# More Disney+ discrimination...
|
||||||
|
# TODO: This prefix could change (random AWS block)
|
||||||
|
ip6 daddr 2600:9000:2245::/48 drop
|
||||||
|
}
|
||||||
chain forward {
|
chain forward {
|
||||||
${lib.my.c.as211024.nftTrust}
|
${lib.my.c.as211024.nftTrust}
|
||||||
iifname lan-untrusted jump filter-untrusted
|
iifname lan-untrusted jump filter-untrusted
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Blocklist for LG WebOS Services (US)
|
||||||
|
ad.lgappstv.com
|
||||||
|
ibis.lgappstv.com
|
||||||
|
info.lgsmartad.com
|
||||||
|
lgtvsdp.com
|
||||||
|
ngfts.lge.com
|
||||||
|
rdx2.lgtvsdp.com
|
||||||
|
smartshare.lgtvsdp.com
|
||||||
|
lgappstv.com
|
||||||
|
us.ad.lgsmartad.com
|
||||||
|
us.ibs.lgappstv.com
|
||||||
|
us.info.lgsmartad.com
|
||||||
|
us.lgtvsdp.com
|
||||||
|
|
||||||
|
# Community Contributions
|
||||||
|
lgad.cjpowercast.com
|
||||||
|
edgesuite.net
|
||||||
|
yumenetworks.com
|
||||||
|
smartclip.net
|
||||||
|
smartclip.com
|
||||||
|
|
||||||
|
# Non-US Entries
|
||||||
|
rdx2.lgtvsdp.com
|
||||||
|
info.lgsmartad.com
|
||||||
|
ibs.lgappstv.com
|
||||||
|
lgtvsdp.com
|
||||||
|
lgappstv.com
|
||||||
|
smartshare.lgtvsdp.com
|
||||||
|
|
||||||
|
# Full Block for Europe and Other Regions
|
||||||
|
de.ad.lgsmartad.com
|
||||||
|
de.emp.lgsmartplatform.com
|
||||||
|
de.ibs.lgappstv.com
|
||||||
|
de.info.lgsmartad.com
|
||||||
|
de.lgeapi.com
|
||||||
|
de.lgtvsdp.com
|
||||||
|
de.rdx2.lgtvsdp.com
|
||||||
|
eu.ad.lgsmartad.com
|
||||||
|
eu.ibs.lgappstv.com
|
||||||
|
eu.info.lgsmartad.com
|
||||||
|
app-lgwebos.pluto.tv
|
||||||
|
it.lgtvsdp.com
|
||||||
|
it.lgeapi.com
|
||||||
|
it.emp.lgsmartplatform.com
|
||||||
|
|
||||||
|
# LG ThinQ Services
|
||||||
|
eic.common.lgthinq.com
|
||||||
|
eic.iotservice.lgthinq.com
|
||||||
|
eic.service.lgthinq.com
|
||||||
|
eic.ngfts.lge.com
|
||||||
|
eic.svc-lgthinq-com.aws-thinq-prd.net
|
||||||
|
eic.cdpsvc.lgtvcommon.com
|
||||||
|
eic.cdpbeacon.lgtvcommon.com
|
||||||
|
eic.cdplauncher.lgtvcommon.com
|
||||||
|
eic.homeprv.lgtvcommon.com
|
||||||
|
eic.lgtviot.com
|
||||||
|
eic.nudge.lgtvcommon.com
|
||||||
|
eic.rdl.lgtvcommon.com
|
||||||
|
eic.recommend.lgtvcommon.com
|
||||||
|
eic.service.lgtvcommon.com
|
||||||
|
gb-lgeapi-com.esi-prd.net
|
||||||
|
gb.lgeapi.com
|
||||||
|
lgtvonline.lge.com
|
||||||
|
lg-channelplus-de-beacons.xumo.com
|
||||||
|
lg-channelplus-de-mds.xumo.com
|
||||||
|
lg-channelplus-eu-beacons.xumo.com
|
||||||
|
lg-channelplus-eu-mds.xumo.com
|
||||||
|
kr-op-v2.lgthinqhome.com
|
||||||
|
ngfts.lge.com
|
||||||
|
noti.lgthinq.com
|
||||||
|
objectcontent.lgthinq.com
|
||||||
|
|
||||||
|
# Update Server Block
|
||||||
|
#snu.lge.com
|
||||||
@@ -13,13 +13,20 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
|
# Let pdns-recursor bind the VRRP VIPs even on the backup, where the addresses
|
||||||
|
# aren't present locally
|
||||||
|
boot.kernel.sysctl = {
|
||||||
|
"net.ipv4.ip_nonlocal_bind" = 1;
|
||||||
|
"net.ipv6.ip_nonlocal_bind" = 1;
|
||||||
|
};
|
||||||
|
|
||||||
my = {
|
my = {
|
||||||
secrets.files = {
|
secrets.files = {
|
||||||
"home/pdns/auth.conf" = {
|
"home/pdns/auth.conf" = {
|
||||||
owner = "pdns";
|
owner = "pdns";
|
||||||
group = "pdns";
|
group = "pdns";
|
||||||
};
|
};
|
||||||
"home/pdns/recursor.conf" = {
|
"home/pdns/recursor.yml" = {
|
||||||
owner = "pdns-recursor";
|
owner = "pdns-recursor";
|
||||||
group = "pdns-recursor";
|
group = "pdns-recursor";
|
||||||
};
|
};
|
||||||
@@ -28,39 +35,82 @@ in
|
|||||||
|
|
||||||
pdns.recursor = {
|
pdns.recursor = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraSettingsFile = config.age.secrets."home/pdns/recursor.conf".path;
|
extraSettingsFile = config.age.secrets."home/pdns/recursor.yml".path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
pdns-recursor = {
|
pdns-recursor = {
|
||||||
dns = {
|
|
||||||
address = [
|
|
||||||
"127.0.0.1" "::1"
|
|
||||||
assignments.hi.ipv4.address assignments.hi.ipv6.address
|
|
||||||
assignments.lo.ipv4.address assignments.lo.ipv6.address
|
|
||||||
];
|
|
||||||
allowFrom = [
|
|
||||||
"127.0.0.0/8" "::1/128"
|
|
||||||
prefixes.hi.v4 prefixes.hi.v6
|
|
||||||
prefixes.lo.v4 prefixes.lo.v6
|
|
||||||
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
|
|
||||||
};
|
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
query-local-address = [
|
incoming = {
|
||||||
"0.0.0.0"
|
listen = [
|
||||||
"::"
|
"127.0.0.1" "::1"
|
||||||
];
|
assignments.hi.ipv4.address assignments.hi.ipv6.address
|
||||||
forward-zones = map (z: "${z}=127.0.0.1:5353") authZones;
|
assignments.lo.ipv4.address assignments.lo.ipv6.address
|
||||||
|
# VRRP VIPs: DNS follows the master, so clients only ever have one
|
||||||
|
# (always-live) resolver address and never hang on a dead router
|
||||||
|
vips.hi.v4 vips.hi.v6
|
||||||
|
vips.lo.v4 vips.lo.v6
|
||||||
|
];
|
||||||
|
allow_from = [
|
||||||
|
"127.0.0.0/8" "::1/128"
|
||||||
|
prefixes.hi.v4 prefixes.hi.v6
|
||||||
|
prefixes.lo.v4 prefixes.lo.v6
|
||||||
|
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
|
||||||
|
|
||||||
# DNS NOTIFY messages override TTL
|
# DNS NOTIFY messages override TTL
|
||||||
allow-notify-for = authZones;
|
allow_notify_for = authZones;
|
||||||
allow-notify-from = [ "127.0.0.0/8" "::1/128" ];
|
allow_notify_from = [ "127.0.0.0/8" "::1/128" ];
|
||||||
|
};
|
||||||
|
|
||||||
webserver = true;
|
outgoing = {
|
||||||
webserver-address = "::";
|
source_address = [ "0.0.0.0" "::" ];
|
||||||
webserver-allow-from = [ "127.0.0.1" "::1" ];
|
};
|
||||||
|
|
||||||
|
recursor = {
|
||||||
|
forward_zones = map (z: {
|
||||||
|
zone = z;
|
||||||
|
forwarders = [ "127.0.0.1:5353" ];
|
||||||
|
}) authZones;
|
||||||
|
|
||||||
|
lua_dns_script = pkgs.writeText "pdns-script.lua" ''
|
||||||
|
blocklist = newDS()
|
||||||
|
|
||||||
|
function preresolve(dq)
|
||||||
|
local name = dq.qname:toString()
|
||||||
|
|
||||||
|
-- Disney+ doesn't like our IP space...
|
||||||
|
if dq.qtype == pdns.AAAA and (string.find(name, "disneyplus") or string.find(name, "disney-plus") or string.find(name , "disney.api")) then
|
||||||
|
dq.rcode = 0
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if blocklist:check(dq.qname) then
|
||||||
|
if dq.qtype == pdns.A then
|
||||||
|
dq:addAnswer(dq.qtype, "127.0.0.1")
|
||||||
|
elseif dq.qtype == pdns.AAAA then
|
||||||
|
dq:addAnswer(dq.qtype, "::1")
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in io.lines("${./dns-blocklist.txt}") do
|
||||||
|
entry = line:gsub("%s+", "")
|
||||||
|
if entry ~= "" and string.sub(entry, 1, 1) ~= "#" then
|
||||||
|
blocklist:add(entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
webservice = {
|
||||||
|
webserver = true;
|
||||||
|
address = "::";
|
||||||
|
allow_from = [ "127.0.0.1" "::1" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -157,8 +207,9 @@ in
|
|||||||
hostname = "${otherName}.${config.networking.domain}";
|
hostname = "${otherName}.${config.networking.domain}";
|
||||||
server = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
|
server = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
|
||||||
}}
|
}}
|
||||||
${elemAt routers 0} IN AAAA ${net.cidr.host 1 prefixes.hi.v6}
|
${elemAt routers 0} IN AAAA ${allAssignments."${elemAt routers 0}".as211024.ipv6.address}
|
||||||
${elemAt routers 1} IN AAAA ${net.cidr.host 2 prefixes.hi.v6}
|
${elemAt routers 1} IN AAAA ${allAssignments."${elemAt routers 1}".as211024.ipv6.address}
|
||||||
|
boot IN CNAME river-hi.${config.networking.domain}.
|
||||||
|
|
||||||
@ IN NS ns1
|
@ IN NS ns1
|
||||||
@ IN NS ns2
|
@ IN NS ns2
|
||||||
@@ -182,14 +233,20 @@ in
|
|||||||
dave-lo IN A ${net.cidr.host 11 prefixes.lo.v4}
|
dave-lo IN A ${net.cidr.host 11 prefixes.lo.v4}
|
||||||
dave-lo IN AAAA ${net.cidr.host (65536+2) prefixes.lo.v6}
|
dave-lo IN AAAA ${net.cidr.host (65536+2) prefixes.lo.v6}
|
||||||
|
|
||||||
;ap0 IN A ${net.cidr.host 12 prefixes.hi.v4}
|
shytzel IN A ${net.cidr.host 12 prefixes.core.v4}
|
||||||
;ap0 IN AAAA ${net.cidr.host (65536+3) prefixes.hi.v6}
|
brian IN A ${net.cidr.host 13 prefixes.core.v4}
|
||||||
|
|
||||||
|
wave IN A ${net.cidr.host 12 prefixes.hi.v4}
|
||||||
|
; wave IN AAAA ${net.cidr.host (65536+3) prefixes.hi.v6}
|
||||||
vibe IN A ${net.cidr.host 13 prefixes.hi.v4}
|
vibe IN A ${net.cidr.host 13 prefixes.hi.v4}
|
||||||
vibe IN AAAA ${net.cidr.host (65536+4) prefixes.hi.v6}
|
vibe IN AAAA ${net.cidr.host (65536+4) prefixes.hi.v6}
|
||||||
|
|
||||||
ups IN A ${net.cidr.host 20 prefixes.lo.v4}
|
ups IN A ${net.cidr.host 20 prefixes.lo.v4}
|
||||||
palace-kvm IN A ${net.cidr.host 21 prefixes.lo.v4}
|
palace-kvm IN A ${net.cidr.host 21 prefixes.lo.v4}
|
||||||
|
|
||||||
|
reolink-living-room IN A ${net.cidr.host 45 prefixes.lo.v4}
|
||||||
|
nixlight IN A ${net.cidr.host 46 prefixes.lo.v4}
|
||||||
|
|
||||||
${lib.my.dns.fwdRecords {
|
${lib.my.dns.fwdRecords {
|
||||||
inherit allAssignments names;
|
inherit allAssignments names;
|
||||||
domain = config.networking.domain;
|
domain = config.networking.domain;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import CloudFlare
|
import cloudflare
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description='Cloudflare DNS update script')
|
parser = argparse.ArgumentParser(description='Cloudflare DNS update script')
|
||||||
@@ -19,17 +19,22 @@ def main():
|
|||||||
if args.api_token_file:
|
if args.api_token_file:
|
||||||
with open(args.api_token_file) as f:
|
with open(args.api_token_file) as f:
|
||||||
cf_token = f.readline().strip()
|
cf_token = f.readline().strip()
|
||||||
|
cf = cloudflare.Cloudflare(api_token=cf_token)
|
||||||
|
|
||||||
cf = CloudFlare.CloudFlare(token=cf_token)
|
zones = list(cf.zones.list(name=args.zone))
|
||||||
zones = cf.zones.get(params={'name': args.zone})
|
|
||||||
assert zones, f'Zone {args.zone} not found'
|
assert zones, f'Zone {args.zone} not found'
|
||||||
records = cf.zones.dns_records.get(zones[0]['id'], params={'name': args.record})
|
assert len(zones) == 1, f'More than one zone found for {args.zone}'
|
||||||
|
zone = zones[0]
|
||||||
|
|
||||||
|
records = list(cf.dns.records.list(zone_id=zone.id, name=args.record, type='A'))
|
||||||
assert records, f'Record {args.record} not found in zone {args.zone}'
|
assert records, f'Record {args.record} not found in zone {args.zone}'
|
||||||
|
assert len(records) == 1, f'More than one record found for {args.record}'
|
||||||
|
record = records[0]
|
||||||
|
|
||||||
print(f'Updating {args.record} -> {address}')
|
print(f'Updating {args.record} -> {address}')
|
||||||
cf.zones.dns_records.patch(
|
cf.dns.records.edit(
|
||||||
zones[0]['id'], records[0]['id'],
|
zone_id=zone.id, dns_record_id=record.id, name=args.record, ttl=60,
|
||||||
data={'type': 'A', 'name': args.record, 'content': address})
|
type='A', content=address)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
index: { lib, pkgs, assignments, ... }:
|
index: { lib, pkgs, config, assignments, allAssignments, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkForce;
|
inherit (lib) mkForce;
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net netbootKeaClientClasses;
|
||||||
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
inherit (lib.my.c.home) domain prefixes vips hiMTU;
|
||||||
|
|
||||||
dns-servers = [
|
dns-servers = [
|
||||||
@@ -26,7 +26,11 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
systemd.services = {
|
systemd.services = {
|
||||||
kea-dhcp4-server.serviceConfig.DynamicUser = mkForce false;
|
kea-dhcp4-server.serviceConfig = {
|
||||||
|
# Sometimes interfaces might not be ready in time and Kea doesn't like that
|
||||||
|
Restart = "on-failure";
|
||||||
|
DynamicUser = mkForce false;
|
||||||
|
};
|
||||||
kea-dhcp-ddns-server.serviceConfig.DynamicUser = mkForce false;
|
kea-dhcp-ddns-server.serviceConfig.DynamicUser = mkForce false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,6 +63,14 @@ in
|
|||||||
always-send = true;
|
always-send = true;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
client-classes = netbootKeaClientClasses {
|
||||||
|
tftpIP = allAssignments.river.lo.ipv4.address;
|
||||||
|
hostname = "boot.${domain}";
|
||||||
|
systems = {
|
||||||
|
sfh = "52:54:00:a5:7e:93";
|
||||||
|
castle = "c8:7f:54:6e:17:0f";
|
||||||
|
};
|
||||||
|
};
|
||||||
subnet4 = [
|
subnet4 = [
|
||||||
{
|
{
|
||||||
id = 1;
|
id = 1;
|
||||||
@@ -71,7 +83,8 @@ in
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "domain-name-servers";
|
name = "domain-name-servers";
|
||||||
data = "${net.cidr.host 1 prefixes.hi.v4}, ${net.cidr.host 2 prefixes.hi.v4}";
|
# VRRP VIP so DNS follows the master and clients never hit a dead router
|
||||||
|
data = vips.hi.v4;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "interface-mtu";
|
name = "interface-mtu";
|
||||||
@@ -104,7 +117,8 @@ in
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "domain-name-servers";
|
name = "domain-name-servers";
|
||||||
data = "${net.cidr.host 1 prefixes.lo.v4}, ${net.cidr.host 2 prefixes.lo.v4}";
|
# VRRP VIP so DNS follows the master and clients never hit a dead router
|
||||||
|
data = vips.lo.v4;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
pools = [
|
pools = [
|
||||||
@@ -120,6 +134,59 @@ in
|
|||||||
hw-address = "24:8a:07:a8:fe:3a";
|
hw-address = "24:8a:07:a8:fe:3a";
|
||||||
ip-address = net.cidr.host 40 prefixes.lo.v4;
|
ip-address = net.cidr.host 40 prefixes.lo.v4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
# avr
|
||||||
|
hw-address = "8c:a9:6f:30:03:6b";
|
||||||
|
ip-address = net.cidr.host 41 prefixes.lo.v4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# tv
|
||||||
|
hw-address = "00:a1:59:b8:4d:86";
|
||||||
|
ip-address = net.cidr.host 42 prefixes.lo.v4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# android tv
|
||||||
|
hw-address = "b8:7b:d4:95:c6:74";
|
||||||
|
ip-address = net.cidr.host 43 prefixes.lo.v4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# hass-panel
|
||||||
|
hw-address = "80:30:49:cd:d7:51";
|
||||||
|
ip-address = net.cidr.host 44 prefixes.lo.v4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# reolink-living-room
|
||||||
|
hw-address = "ec:71:db:30:69:a4";
|
||||||
|
ip-address = net.cidr.host 45 prefixes.lo.v4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# nixlight
|
||||||
|
hw-address = "00:4b:12:3b:d3:14";
|
||||||
|
ip-address = net.cidr.host 46 prefixes.lo.v4;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
id = 3;
|
||||||
|
subnet = prefixes.untrusted.v4;
|
||||||
|
interface = "lan-untrusted";
|
||||||
|
option-data = [
|
||||||
|
{
|
||||||
|
name = "routers";
|
||||||
|
data = vips.untrusted.v4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "domain-name-servers";
|
||||||
|
data = "1.1.1.1, 1.0.0.1";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
pools = [
|
||||||
|
{
|
||||||
|
pool = if index == 0
|
||||||
|
then "192.168.80.10 - 192.168.80.127"
|
||||||
|
else "192.168.80.128 - 192.168.80.250";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,52 +1,83 @@
|
|||||||
index: { lib, pkgs, config, ... }:
|
index: { lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) attrNames concatMap;
|
inherit (builtins) attrNames concatMap length;
|
||||||
inherit (lib) optional;
|
inherit (lib) optional concatMapStringsSep;
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c.home) prefixes vips;
|
inherit (lib.my.c.home) prefixes vips;
|
||||||
|
|
||||||
|
pingScriptFor = name: ips:
|
||||||
|
let
|
||||||
|
script' = pkgs.writeShellScript
|
||||||
|
"keepalived-ping-${name}"
|
||||||
|
(concatMapStringsSep " || " (ip: "${pkgs.iputils}/bin/ping -qnc 1 -W 1 ${ip}") ips);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
script = toString script';
|
||||||
|
interval = 1;
|
||||||
|
timeout = (length ips) + 1;
|
||||||
|
rise = 3;
|
||||||
|
fall = 3;
|
||||||
|
};
|
||||||
|
|
||||||
vlanIface = vlan: if vlan == "as211024" then vlan else "lan-${vlan}";
|
vlanIface = vlan: if vlan == "as211024" then vlan else "lan-${vlan}";
|
||||||
vrrpIPs = family: concatMap (vlan: [
|
vrrpIPs = family: concatMap (vlan: [
|
||||||
{
|
{
|
||||||
addr = "${vips.${vlan}.${family}}/${toString (net.cidr.length prefixes.${vlan}.${family})}";
|
addr = "${vips.${vlan}.${family}}/${toString (net.cidr.length prefixes.${vlan}.${family})}";
|
||||||
dev = vlanIface vlan;
|
dev = vlanIface vlan;
|
||||||
}
|
}
|
||||||
] ++ (optional (family == "v6") {
|
]) (attrNames vips);
|
||||||
addr = "fe80::1/64";
|
|
||||||
dev = vlanIface vlan;
|
|
||||||
})) (attrNames vips);
|
|
||||||
mkVRRP = family: routerId: {
|
mkVRRP = family: routerId: {
|
||||||
state = if index == 0 then "MASTER" else "BACKUP";
|
state = if index == 0 then "MASTER" else "BACKUP";
|
||||||
interface = "lan-core";
|
interface = "lan-core";
|
||||||
priority = 255 - index;
|
priority = 255 - index;
|
||||||
virtualRouterId = routerId;
|
virtualRouterId = routerId;
|
||||||
virtualIps = vrrpIPs family;
|
virtualIps = vrrpIPs family;
|
||||||
extraConfig = ''
|
trackScripts = [ "${family}Alive" ];
|
||||||
notify_master "${config.systemd.package}/bin/systemctl start radvd.service"
|
|
||||||
notify_backup "${config.systemd.package}/bin/systemctl stop radvd.service"
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
users = with lib.my.c.ids; {
|
||||||
|
users.keepalived_script = {
|
||||||
|
uid = uids.keepalived_script;
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "keepalived_script";
|
||||||
|
};
|
||||||
|
groups.keepalived_script.gid = gids.keepalived_script;
|
||||||
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
keepalived = {
|
keepalived = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
enableScriptSecurity = true;
|
||||||
extraGlobalDefs = ''
|
extraGlobalDefs = ''
|
||||||
vrrp_version 3
|
vrrp_version 3
|
||||||
nftables keepalived
|
nftables keepalived
|
||||||
'';
|
'';
|
||||||
|
vrrpScripts = {
|
||||||
|
v4Alive = pingScriptFor "v4" [ "1.1.1.1" "8.8.8.8" "216.218.236.2" ];
|
||||||
|
v6Alive = pingScriptFor "v6" [ "2606:4700:4700::1111" "2001:4860:4860::8888" "2600::" ];
|
||||||
|
};
|
||||||
vrrpInstances = {
|
vrrpInstances = {
|
||||||
v4 = mkVRRP "v4" 51;
|
v4 = mkVRRP "v4" 51;
|
||||||
v6 = mkVRRP "v6" 52;
|
v6 = (mkVRRP "v6" 52) // {
|
||||||
|
extraConfig = ''
|
||||||
|
virtual_ipaddress_excluded {
|
||||||
|
${concatMapStringsSep "\n" (vlan: "fe80::1/64 dev ${vlanIface vlan}") (attrNames vips)}
|
||||||
|
}
|
||||||
|
notify_master "${config.systemd.package}/bin/systemctl start radvd.service" root
|
||||||
|
notify_backup "${config.systemd.package}/bin/systemctl stop radvd.service" root
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
# Actually disable this for now, don't want to fault IPv4 just because IPv6 is broken...
|
||||||
vrrp_sync_group main {
|
# extraConfig = ''
|
||||||
group {
|
# vrrp_sync_group main {
|
||||||
v4
|
# group {
|
||||||
v6
|
# v4
|
||||||
}
|
# v6
|
||||||
}
|
# }
|
||||||
'';
|
# }
|
||||||
|
# '';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ let
|
|||||||
# TODO: Move into nixpkgs
|
# TODO: Move into nixpkgs
|
||||||
mstpd = pkgs.mstpd.overrideAttrs {
|
mstpd = pkgs.mstpd.overrideAttrs {
|
||||||
patches = [ ./mstpd.patch ];
|
patches = [ ./mstpd.patch ];
|
||||||
|
# Delete postInstall since it nukes the bridge-stp script we need
|
||||||
|
postInstall = "";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
environment = {
|
environment = {
|
||||||
systemPackages = [
|
systemPackages = [
|
||||||
|
# For kernel to call bridge-stp (see ./pkgs/os-specific/linux/kernel/bridge-stp-helper.patch)
|
||||||
mstpd
|
mstpd
|
||||||
];
|
];
|
||||||
etc = {
|
etc = {
|
||||||
@@ -24,8 +27,8 @@ in
|
|||||||
onState = [ "routable" ];
|
onState = [ "routable" ];
|
||||||
script = ''
|
script = ''
|
||||||
#!${pkgs.runtimeShell}
|
#!${pkgs.runtimeShell}
|
||||||
if [ $IFACE = "lan" ]; then
|
if [ "$IFACE" = "lan" ]; then
|
||||||
${mstpd}/sbin/mstpctl setforcevers $IFACE rstp
|
${mstpd}/sbin/mstpctl setforcevers "$IFACE" rstp
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@@ -39,8 +42,8 @@ in
|
|||||||
before = [ "network-pre.target" ];
|
before = [ "network-pre.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "forking";
|
Type = "forking";
|
||||||
ExecStart = "${mstpd}/sbin/bridge-stp restart";
|
ExecStart = "${mstpd}/bin/bridge-stp restart";
|
||||||
ExecReload = "${mstpd}/sbin/bridge-stp restart_config";
|
ExecReload = "${mstpd}/bin/bridge-stp restart_config";
|
||||||
PIDFile = "/run/mstpd.pid";
|
PIDFile = "/run/mstpd.pid";
|
||||||
Restart = "always";
|
Restart = "always";
|
||||||
PrivateTmp = true;
|
PrivateTmp = true;
|
||||||
|
|||||||
@@ -2,7 +2,14 @@ index: { lib, pkgs, ... }:
|
|||||||
let
|
let
|
||||||
inherit (lib) mkForce concatMapStringsSep;
|
inherit (lib) mkForce concatMapStringsSep;
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c.home) domain prefixes;
|
inherit (lib.my.c.home) domain prefixes vips;
|
||||||
|
|
||||||
|
# untrusted uses external (Cloudflare) resolvers, matching the v4 kea config;
|
||||||
|
# trusted VLANs use the internal recursor via its floating VRRP VIP
|
||||||
|
rdnss = name:
|
||||||
|
if name == "untrusted"
|
||||||
|
then "2606:4700:4700::1111 2606:4700:4700::1001"
|
||||||
|
else vips."${name}".v6;
|
||||||
|
|
||||||
mkInterface = name: ''
|
mkInterface = name: ''
|
||||||
interface lan-${name} {
|
interface lan-${name} {
|
||||||
@@ -10,7 +17,7 @@ let
|
|||||||
AdvRASrcAddress { fe80::1; };
|
AdvRASrcAddress { fe80::1; };
|
||||||
AdvLinkMTU ${toString prefixes."${name}".mtu};
|
AdvLinkMTU ${toString prefixes."${name}".mtu};
|
||||||
prefix ${prefixes."${name}".v6} {};
|
prefix ${prefixes."${name}".v6} {};
|
||||||
RDNSS ${net.cidr.host 1 prefixes."${name}".v6} ${net.cidr.host 2 prefixes."${name}".v6} {};
|
RDNSS ${rdnss name} {};
|
||||||
DNSSL ${domain} dyn.${domain} ${lib.my.c.colony.domain} ${lib.my.c.britway.domain} {};
|
DNSSL ${domain} dyn.${domain} ${lib.my.c.colony.domain} ${lib.my.c.britway.domain} {};
|
||||||
};
|
};
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
nixpkgs = "mine";
|
nixpkgs = "mine";
|
||||||
home-manager = "mine";
|
home-manager = "mine";
|
||||||
|
|
||||||
configuration = { lib, pkgs, config, ... }:
|
configuration = { lib, pkgs, config, assignments, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib);
|
inherit (lib) mkMerge;
|
||||||
|
inherit (lib.my) networkdAssignment;
|
||||||
|
inherit (lib.my.c) networkd;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./routing-common/mstpd.nix ];
|
imports = [ ./routing-common/mstpd.nix ];
|
||||||
@@ -43,6 +45,64 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
mjpg-streamer = {
|
||||||
|
enable = false;
|
||||||
|
inputPlugin = "input_uvc.so";
|
||||||
|
outputPlugin = "output_http.so -w @www@ -n -p 5050";
|
||||||
|
};
|
||||||
|
octoprint = {
|
||||||
|
enable = false;
|
||||||
|
host = "::";
|
||||||
|
extraConfig = {
|
||||||
|
plugins = {
|
||||||
|
classicwebcam = {
|
||||||
|
snapshot = "/webcam/?action=snapshot";
|
||||||
|
stream = "/webcam/?action=stream";
|
||||||
|
streamRatio = "4:3";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
serial = {
|
||||||
|
port = "/dev/ttyACM0";
|
||||||
|
baudrate = 115200;
|
||||||
|
};
|
||||||
|
temperature.profiles = [
|
||||||
|
{
|
||||||
|
bed = 60;
|
||||||
|
extruder = 215;
|
||||||
|
name = "PLA";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# wan carries a permanent static modem-management address (assignments.modem)
|
||||||
|
# alongside the DHCP public IP, so wait-online@wan reports "online" as soon as
|
||||||
|
# the static address is up - before the DHCP lease arrives. ipsec's left= is the
|
||||||
|
# public IP, so gating on wait-online lets it start unoriented and never connect.
|
||||||
|
# Gate instead on the DHCP default route, which only exists once the public lease
|
||||||
|
# is up (the static modem address has no gateway).
|
||||||
|
systemd.services.wan-wait-online = {
|
||||||
|
description = "Wait for the wan default route (public DHCP lease)";
|
||||||
|
after = [ "systemd-networkd.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
TimeoutStartSec = "300";
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
until [ -n "$(${pkgs.iproute2}/bin/ip -4 route show default dev wan)" ]; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
systemd.targets.wan-online = {
|
||||||
|
requires = [ "wan-wait-online.service" ];
|
||||||
|
after = [ "wan-wait-online.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
systemd.network = {
|
systemd.network = {
|
||||||
netdevs = {
|
netdevs = {
|
||||||
"25-lan" = {
|
"25-lan" = {
|
||||||
@@ -115,6 +175,41 @@
|
|||||||
matchConfig.Name = "lan-dave";
|
matchConfig.Name = "lan-dave";
|
||||||
networkConfig.Bridge = "lan";
|
networkConfig.Bridge = "lan";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"50-wan-ifb" = {
|
||||||
|
matchConfig.Name = "wan-ifb";
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
extraConfig = ''
|
||||||
|
[CAKE]
|
||||||
|
Bandwidth=490M
|
||||||
|
RTTSec=50ms
|
||||||
|
PriorityQueueingPreset=besteffort
|
||||||
|
# DOCSIS preset
|
||||||
|
OverheadBytes=18
|
||||||
|
MPUBytes=64
|
||||||
|
CompensationMode=none
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"50-wan" = mkMerge [
|
||||||
|
(networkdAssignment "wan" assignments.modem)
|
||||||
|
{
|
||||||
|
matchConfig.Name = "wan";
|
||||||
|
DHCP = "ipv4";
|
||||||
|
dns = [ "127.0.0.1" "::1" ];
|
||||||
|
dhcpV4Config.UseDNS = false;
|
||||||
|
|
||||||
|
qdiscConfig = {
|
||||||
|
Parent = "ingress";
|
||||||
|
Handle = "0xffff";
|
||||||
|
};
|
||||||
|
extraConfig = ''
|
||||||
|
[CAKE]
|
||||||
|
Parent=root
|
||||||
|
Bandwidth=48M
|
||||||
|
RTTSec=50ms
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,7 +218,7 @@
|
|||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
|
||||||
};
|
};
|
||||||
server.enable = true;
|
server.enable = true;
|
||||||
deploy.node.hostname = "192.168.68.2";
|
# deploy.node.hostname = "192.168.68.2";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ in
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
# Hardware acceleration for Jellyfin
|
# Hardware acceleration for Jellyfin
|
||||||
hardware.opengl = {
|
hardware.graphics = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraPackages = with pkgs; [
|
extraPackages = with pkgs; [
|
||||||
vaapiIntel
|
intel-vaapi-driver
|
||||||
intel-ocl
|
intel-ocl
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -65,16 +65,31 @@ in
|
|||||||
systemd = {
|
systemd = {
|
||||||
services = {
|
services = {
|
||||||
jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
||||||
transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
|
||||||
|
|
||||||
radarr.serviceConfig.UMask = "0002";
|
transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
|
||||||
sonarr.serviceConfig.UMask = "0002";
|
# https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
|
||||||
|
transmission.serviceConfig = {
|
||||||
|
RootDirectoryStartOnly = lib.mkForce false;
|
||||||
|
RootDirectory = lib.mkForce "";
|
||||||
|
};
|
||||||
|
|
||||||
|
radarr.serviceConfig.UMask = lib.mkForce "0002";
|
||||||
|
sonarr.serviceConfig.UMask = lib.mkForce "0002";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nixpkgs.config.permittedInsecurePackages = [
|
||||||
|
# FIXME: This is needed for Sonarr
|
||||||
|
"aspnetcore-runtime-wrapped-6.0.36"
|
||||||
|
"aspnetcore-runtime-6.0.36"
|
||||||
|
"dotnet-sdk-wrapped-6.0.428"
|
||||||
|
"dotnet-sdk-6.0.428"
|
||||||
|
];
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
transmission = {
|
transmission = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = pkgs.transmission_4;
|
||||||
downloadDirPermissions = null;
|
downloadDirPermissions = null;
|
||||||
performanceNetParameters = true;
|
performanceNetParameters = true;
|
||||||
settings = {
|
settings = {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
let
|
let
|
||||||
inherit (lib) mkMerge mkIf;
|
inherit (lib) mkMerge mkIf;
|
||||||
inherit (lib.my) networkdAssignment;
|
inherit (lib.my) networkdAssignment;
|
||||||
|
inherit (lib.my.c.kelder) ipv4MTU;
|
||||||
|
|
||||||
wg = {
|
wg = {
|
||||||
keyFile = "kelder/acquisition/airvpn-privkey";
|
keyFile = "kelder/acquisition/airvpn-privkey";
|
||||||
@@ -72,14 +73,12 @@ in
|
|||||||
RouteTable = routeTable;
|
RouteTable = routeTable;
|
||||||
};
|
};
|
||||||
wireguardPeers = [
|
wireguardPeers = [
|
||||||
|
# AirVPN IE
|
||||||
{
|
{
|
||||||
# AirVPN IE
|
Endpoint = "146.70.94.2:1637";
|
||||||
wireguardPeerConfig = {
|
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
|
||||||
Endpoint = "146.70.94.2:1637";
|
PresharedKeyFile = config.age.secrets."${pskFile}".path;
|
||||||
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
|
AllowedIPs = [ "0.0.0.0/0" "::/0" ];
|
||||||
PresharedKeyFile = config.age.secrets."${pskFile}".path;
|
|
||||||
AllowedIPs = [ "0.0.0.0/0" "::/0" ];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -89,13 +88,14 @@ in
|
|||||||
(networkdAssignment "host0" assignments.internal)
|
(networkdAssignment "host0" assignments.internal)
|
||||||
{
|
{
|
||||||
networkConfig.DNSDefaultRoute = false;
|
networkConfig.DNSDefaultRoute = false;
|
||||||
|
linkConfig.MTUBytes = toString ipv4MTU;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
"90-vpn" = with wg; {
|
"90-vpn" = with wg; {
|
||||||
matchConfig.Name = "vpn";
|
matchConfig.Name = "vpn";
|
||||||
address = [ "10.161.170.28/32" "fd7d:76ee:e68f:a993:b12d:6d15:c80a:9516/128" ];
|
address = [ "10.161.170.28/32" "fd7d:76ee:e68f:a993:b12d:6d15:c80a:9516/128" ];
|
||||||
dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ];
|
dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ];
|
||||||
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [
|
routingPolicyRules = [
|
||||||
{
|
{
|
||||||
Family = "both";
|
Family = "both";
|
||||||
SuppressPrefixLength = 0;
|
SuppressPrefixLength = 0;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ in
|
|||||||
"*.${domain}"
|
"*.${domain}"
|
||||||
];
|
];
|
||||||
dnsProvider = "cloudflare";
|
dnsProvider = "cloudflare";
|
||||||
credentialsFile = config.age.secrets."kelder/cloudflare-credentials.conf".path;
|
environmentFile = config.age.secrets."kelder/cloudflare-credentials.conf".path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -88,21 +88,23 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
resolved.extraConfig = mkForce "";
|
resolved.settings.Resolve = mkForce { };
|
||||||
|
|
||||||
nextcloud = {
|
nextcloud = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs.nextcloud28;
|
# TODO: Might need to do some bullshit to go from Nextcloud 28 (?) to 32
|
||||||
|
package = pkgs.nextcloud32;
|
||||||
datadir = "/mnt/storage/nextcloud";
|
datadir = "/mnt/storage/nextcloud";
|
||||||
hostName = "cloud.${domain}";
|
hostName = "cloud.${domain}";
|
||||||
https = true;
|
https = true;
|
||||||
config = {
|
config = {
|
||||||
extraTrustedDomains = [ "cloud-local.${domain}" ];
|
|
||||||
adminpassFile = config.age.secrets."kelder/nextcloud-root.txt".path;
|
adminpassFile = config.age.secrets."kelder/nextcloud-root.txt".path;
|
||||||
defaultPhoneRegion = "IE";
|
dbtype = "sqlite";
|
||||||
};
|
};
|
||||||
extraOptions = {
|
settings = {
|
||||||
updatechecker = false;
|
updatechecker = false;
|
||||||
|
trusted_domains = [ "cloud-local.${domain}" ];
|
||||||
|
default_phone_region = "IE";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,11 +13,6 @@ in
|
|||||||
owner = "nginx";
|
owner = "nginx";
|
||||||
group = "nginx";
|
group = "nginx";
|
||||||
};
|
};
|
||||||
"dhparams.pem" = {
|
|
||||||
owner = "acme";
|
|
||||||
group = "acme";
|
|
||||||
mode = "440";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
firewall = {
|
firewall = {
|
||||||
@@ -35,7 +30,6 @@ in
|
|||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
clientMaxBodySize = "0";
|
clientMaxBodySize = "0";
|
||||||
serverTokens = true;
|
serverTokens = true;
|
||||||
sslDhparam = config.age.secrets."dhparams.pem".path;
|
|
||||||
|
|
||||||
# Based on recommended*Settings, but probably better to be explicit about these
|
# Based on recommended*Settings, but probably better to be explicit about these
|
||||||
appendHttpConfig = ''
|
appendHttpConfig = ''
|
||||||
@@ -84,6 +78,7 @@ in
|
|||||||
c
|
c
|
||||||
];
|
];
|
||||||
acquisition = "http://${allAssignments.kelder-acquisition.internal.ipv4.address}";
|
acquisition = "http://${allAssignments.kelder-acquisition.internal.ipv4.address}";
|
||||||
|
# This is kinda borked because Virgin Media filters DNS responses with local IPs...
|
||||||
localRedirect = to: ''
|
localRedirect = to: ''
|
||||||
rewrite_by_lua_block {
|
rewrite_by_lua_block {
|
||||||
if ngx.var.remote_addr == pub_ip then
|
if ngx.var.remote_addr == pub_ip then
|
||||||
@@ -103,7 +98,7 @@ in
|
|||||||
|
|
||||||
"monitor.${domain}" = withAuth {
|
"monitor.${domain}" = withAuth {
|
||||||
serverAliases = [ "monitor-local.${domain}" ];
|
serverAliases = [ "monitor-local.${domain}" ];
|
||||||
extraConfig = localRedirect "monitor-local.${domain}";
|
# extraConfig = localRedirect "monitor-local.${domain}";
|
||||||
locations = {
|
locations = {
|
||||||
"/" = {
|
"/" = {
|
||||||
proxyPass = "http://${allAssignments.kelder.ctrs.ipv4.address}:19999";
|
proxyPass = "http://${allAssignments.kelder.ctrs.ipv4.address}:19999";
|
||||||
@@ -136,17 +131,17 @@ in
|
|||||||
};
|
};
|
||||||
"torrents.${domain}" = withAuth {
|
"torrents.${domain}" = withAuth {
|
||||||
serverAliases = [ "torrents-local.${domain}" ];
|
serverAliases = [ "torrents-local.${domain}" ];
|
||||||
extraConfig = localRedirect "torrents-local.${domain}";
|
# extraConfig = localRedirect "torrents-local.${domain}";
|
||||||
locations."/".proxyPass = "${acquisition}:9091";
|
locations."/".proxyPass = "${acquisition}:9091";
|
||||||
};
|
};
|
||||||
"jackett.${domain}" = withAuth {
|
"jackett.${domain}" = withAuth {
|
||||||
serverAliases = [ "jackett-local.${domain}" ];
|
serverAliases = [ "jackett-local.${domain}" ];
|
||||||
extraConfig = localRedirect "jackett-local.${domain}";
|
# extraConfig = localRedirect "jackett-local.${domain}";
|
||||||
locations."/".proxyPass = "${acquisition}:9117";
|
locations."/".proxyPass = "${acquisition}:9117";
|
||||||
};
|
};
|
||||||
"radarr.${domain}" = withAuth {
|
"radarr.${domain}" = withAuth {
|
||||||
serverAliases = [ "radarr-local.${domain}" ];
|
serverAliases = [ "radarr-local.${domain}" ];
|
||||||
extraConfig = localRedirect "radarr-local.${domain}";
|
# extraConfig = localRedirect "radarr-local.${domain}";
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "${acquisition}:7878";
|
proxyPass = "${acquisition}:7878";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
@@ -155,7 +150,7 @@ in
|
|||||||
};
|
};
|
||||||
"sonarr.${domain}" = withAuth {
|
"sonarr.${domain}" = withAuth {
|
||||||
serverAliases = [ "sonarr-local.${domain}" ];
|
serverAliases = [ "sonarr-local.${domain}" ];
|
||||||
extraConfig = localRedirect "sonarr-local.${domain}";
|
# extraConfig = localRedirect "sonarr-local.${domain}";
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "${acquisition}:8989";
|
proxyPass = "${acquisition}:8989";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib.my) net;
|
inherit (lib.my) net;
|
||||||
inherit (lib.my.c.kelder) domain prefixes;
|
inherit (lib.my.c.kelder) domain prefixes ipv4MTU;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./containers ];
|
imports = [ ./containers ];
|
||||||
@@ -121,8 +121,7 @@ in
|
|||||||
|
|
||||||
samba = {
|
samba = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableNmbd = true;
|
settings = {
|
||||||
shares = {
|
|
||||||
storage = {
|
storage = {
|
||||||
path = "/mnt/storage";
|
path = "/mnt/storage";
|
||||||
browseable = "yes";
|
browseable = "yes";
|
||||||
@@ -131,6 +130,8 @@ in
|
|||||||
"directory mask" = "0775";
|
"directory mask" = "0775";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nmbd.enable = true;
|
||||||
};
|
};
|
||||||
samba-wsdd.enable = true;
|
samba-wsdd.enable = true;
|
||||||
|
|
||||||
@@ -180,12 +181,10 @@ in
|
|||||||
};
|
};
|
||||||
wireguardPeers = [
|
wireguardPeers = [
|
||||||
{
|
{
|
||||||
wireguardPeerConfig = {
|
PublicKey = "bP1XUNxp9i8NLOXhgPaIaRzRwi5APbam44/xjvYcyjU=";
|
||||||
PublicKey = "bP1XUNxp9i8NLOXhgPaIaRzRwi5APbam44/xjvYcyjU=";
|
Endpoint = "${allAssignments.estuary.internal.ipv4.address}:${toString lib.my.c.kelder.vpn.port}";
|
||||||
Endpoint = "estuary-vm.${lib.my.c.colony.domain}:${toString lib.my.c.kelder.vpn.port}";
|
AllowedIPs = [ "0.0.0.0/0" ];
|
||||||
AllowedIPs = [ "0.0.0.0/0" ];
|
PersistentKeepalive = 25;
|
||||||
PersistentKeepalive = 25;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -200,6 +199,7 @@ in
|
|||||||
"50-lan" = {
|
"50-lan" = {
|
||||||
matchConfig.Name = "et1g0";
|
matchConfig.Name = "et1g0";
|
||||||
DHCP = "yes";
|
DHCP = "yes";
|
||||||
|
linkConfig.MTUBytes = toString ipv4MTU;
|
||||||
};
|
};
|
||||||
"80-ctrs" = mkMerge [
|
"80-ctrs" = mkMerge [
|
||||||
(networkdAssignment "ctrs" assignments.ctrs)
|
(networkdAssignment "ctrs" assignments.ctrs)
|
||||||
@@ -212,7 +212,7 @@ in
|
|||||||
address = with assignments.estuary; [
|
address = with assignments.estuary; [
|
||||||
(with ipv4; "${address}/${toString mask}")
|
(with ipv4; "${address}/${toString mask}")
|
||||||
];
|
];
|
||||||
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [
|
routingPolicyRules = [
|
||||||
{
|
{
|
||||||
Family = "both";
|
Family = "both";
|
||||||
SuppressPrefixLength = 0;
|
SuppressPrefixLength = 0;
|
||||||
@@ -272,7 +272,7 @@ in
|
|||||||
config.name = "kontent";
|
config.name = "kontent";
|
||||||
};
|
};
|
||||||
|
|
||||||
#deploy.node.hostname = "10.16.9.21";
|
# deploy.node.hostname = "192.168.0.69";
|
||||||
secrets = {
|
secrets = {
|
||||||
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFvUdJshXkqmchEgkZDn5rgtZ1NO9vbd6Px+S6YioWi";
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFvUdJshXkqmchEgkZDn5rgtZ1NO9vbd6Px+S6YioWi";
|
||||||
files = {
|
files = {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
cpu = {
|
cpu = {
|
||||||
intel.updateMicrocode = true;
|
intel.updateMicrocode = true;
|
||||||
};
|
};
|
||||||
opengl.extraPackages = with pkgs; [
|
graphics.extraPackages = with pkgs; [
|
||||||
intel-media-driver
|
intel-media-driver
|
||||||
];
|
];
|
||||||
bluetooth.enable = true;
|
bluetooth.enable = true;
|
||||||
@@ -99,12 +99,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
resolved = {
|
|
||||||
enable = true;
|
|
||||||
extraConfig = mkForce "";
|
|
||||||
dnssec = "false";
|
|
||||||
};
|
|
||||||
|
|
||||||
fprintd.enable = true;
|
fprintd.enable = true;
|
||||||
blueman.enable = true;
|
blueman.enable = true;
|
||||||
|
|
||||||
@@ -118,7 +112,7 @@
|
|||||||
steam.enable = true;
|
steam.enable = true;
|
||||||
wireshark = {
|
wireshark = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs.wireshark-qt;
|
package = pkgs.wireshark;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -129,10 +123,9 @@
|
|||||||
wifi = {
|
wifi = {
|
||||||
backend = "wpa_supplicant";
|
backend = "wpa_supplicant";
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
settings = {
|
||||||
[main]
|
main.no-auto-default = "*";
|
||||||
no-auto-default=*
|
};
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -178,7 +171,7 @@
|
|||||||
programs = {
|
programs = {
|
||||||
fish = {
|
fish = {
|
||||||
shellAbbrs = {
|
shellAbbrs = {
|
||||||
tsup = "doas tailscale up --login-server=https://ts.nul.ie --accept-routes";
|
tsup = "doas tailscale up --login-server=https://hs.nul.ie --accept-routes";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-2
@@ -23,7 +23,7 @@ let
|
|||||||
pkgs = pkgs'.${config'.nixpkgs}.${config'.system};
|
pkgs = pkgs'.${config'.nixpkgs}.${config'.system};
|
||||||
allPkgs = mapAttrs (_: p: p.${config'.system}) pkgs';
|
allPkgs = mapAttrs (_: p: p.${config'.system}) pkgs';
|
||||||
|
|
||||||
modules' = [ hmFlakes.${config'.home-manager}.nixosModule ] ++ (attrValues cfg.modules);
|
modules' = [ hmFlakes.${config'.home-manager}.nixosModules.default ] ++ (attrValues cfg.modules);
|
||||||
in
|
in
|
||||||
# Import eval-config ourselves since the flake now force-sets lib
|
# Import eval-config ourselves since the flake now force-sets lib
|
||||||
import "${pkgsFlake}/nixos/lib/eval-config.nix" {
|
import "${pkgsFlake}/nixos/lib/eval-config.nix" {
|
||||||
@@ -35,7 +35,7 @@ let
|
|||||||
system = null;
|
system = null;
|
||||||
|
|
||||||
# Put the inputs in specialArgs to avoid infinite recursion when modules try to do imports
|
# Put the inputs in specialArgs to avoid infinite recursion when modules try to do imports
|
||||||
specialArgs = { inherit inputs pkgsFlakes pkgsFlake allAssignments; inherit (cfg) systems; };
|
specialArgs = { inherit self inputs pkgsFlakes pkgsFlake allAssignments; inherit (cfg) systems; };
|
||||||
|
|
||||||
# `baseModules` informs the manual which modules to document
|
# `baseModules` informs the manual which modules to document
|
||||||
baseModules =
|
baseModules =
|
||||||
@@ -135,6 +135,7 @@ let
|
|||||||
ipv6 = mkBoolOpt' false "Whether this mesh's underlay operates over IPv6.";
|
ipv6 = mkBoolOpt' false "Whether this mesh's underlay operates over IPv6.";
|
||||||
baseMTU = mkOpt' ints.unsigned 1500 "Base MTU to calculate VXLAN MTU with.";
|
baseMTU = mkOpt' ints.unsigned 1500 "Base MTU to calculate VXLAN MTU with.";
|
||||||
l3Overhead = mkOpt' ints.unsigned 40 "Overhead of L3 header (to calculate MTU).";
|
l3Overhead = mkOpt' ints.unsigned 40 "Overhead of L3 header (to calculate MTU).";
|
||||||
|
udpEncapsulation = mkBoolOpt' false "Whether to encapsulate ESP frames in UDP.";
|
||||||
firewall = mkBoolOpt' true "Whether to generate firewall rules.";
|
firewall = mkBoolOpt' true "Whether to generate firewall rules.";
|
||||||
vni = mkOpt' ints.unsigned 1 "VXLAN VNI.";
|
vni = mkOpt' ints.unsigned 1 "VXLAN VNI.";
|
||||||
peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers.";
|
peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers.";
|
||||||
|
|||||||
+11
-5
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
nixos.systems.installer = { config, ... }: {
|
nixos.systems.installer = { config, ... }: {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
nixpkgs = "unstable";
|
nixpkgs = "mine";
|
||||||
docCustom = false;
|
docCustom = false;
|
||||||
rendered = config.configuration.config.my.asISO;
|
rendered = config.configuration.config.my.asISO;
|
||||||
|
|
||||||
@@ -31,8 +31,10 @@
|
|||||||
server.enable = true;
|
server.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
image = {
|
||||||
|
baseName = "jackos-installer";
|
||||||
|
};
|
||||||
isoImage = {
|
isoImage = {
|
||||||
isoBaseName = "jackos-installer";
|
|
||||||
volumeID = "jackos-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}";
|
volumeID = "jackos-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}";
|
||||||
edition = "devplayer0";
|
edition = "devplayer0";
|
||||||
appendToMenuLabel = " /dev/player0 Installer";
|
appendToMenuLabel = " /dev/player0 Installer";
|
||||||
@@ -52,6 +54,8 @@
|
|||||||
home.shellAliases = {
|
home.shellAliases = {
|
||||||
show-hw-config = "nixos-generate-config --show-hardware-config --root $INSTALL_ROOT";
|
show-hw-config = "nixos-generate-config --show-hardware-config --root $INSTALL_ROOT";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my.gui.enable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
@@ -59,8 +63,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
# Will be set dynamically
|
# Will be set dynamically, but need something to satisfy `/etc/os-release` stuff
|
||||||
hostName = "";
|
hostName = "installer";
|
||||||
useNetworkd = false;
|
useNetworkd = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,6 +84,8 @@
|
|||||||
${pkgs.gawk}/bin/awk '{ print $1 }')"
|
${pkgs.gawk}/bin/awk '{ print $1 }')"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
boot.supportedFilesystems.nfs = true;
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
dhcpcd
|
dhcpcd
|
||||||
lm_sensors
|
lm_sensors
|
||||||
@@ -93,7 +99,7 @@
|
|||||||
|
|
||||||
# Enable wpa_supplicant, but don't start it by default.
|
# Enable wpa_supplicant, but don't start it by default.
|
||||||
networking.wireless.enable = mkDefault true;
|
networking.wireless.enable = mkDefault true;
|
||||||
networking.wireless.userControlled.enable = true;
|
networking.wireless.userControlled = true;
|
||||||
systemd.services.wpa_supplicant.wantedBy = mkForce [];
|
systemd.services.wpa_supplicant.wantedBy = mkForce [];
|
||||||
|
|
||||||
# Tell the Nix evaluator to garbage collect more aggressively.
|
# Tell the Nix evaluator to garbage collect more aggressively.
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
network = ./network.nix;
|
network = ./network.nix;
|
||||||
pdns = ./pdns.nix;
|
pdns = ./pdns.nix;
|
||||||
nginx-sso = ./nginx-sso.nix;
|
nginx-sso = ./nginx-sso.nix;
|
||||||
gui = ./gui.nix;
|
gui = ./gui;
|
||||||
l2mesh = ./l2mesh.nix;
|
l2mesh = ./l2mesh.nix;
|
||||||
borgthin = ./borgthin.nix;
|
borgthin = ./borgthin.nix;
|
||||||
nvme = ./nvme;
|
nvme = ./nvme;
|
||||||
spdk = ./spdk.nix;
|
spdk = ./spdk.nix;
|
||||||
librespeed = ./librespeed;
|
librespeed = ./librespeed;
|
||||||
|
netboot = ./netboot;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ inputs, lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) substring match;
|
inherit (builtins) substring match;
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
@@ -127,7 +127,9 @@ in
|
|||||||
enable = mkBoolOpt' false "Whether to enable borgthin jobs";
|
enable = mkBoolOpt' false "Whether to enable borgthin jobs";
|
||||||
lvmPackage = mkOpt' package pkgs.lvm2 "Packge containing LVM tools";
|
lvmPackage = mkOpt' package pkgs.lvm2 "Packge containing LVM tools";
|
||||||
thinToolsPackage = mkOpt' package pkgs.thin-provisioning-tools "Package containing thin-provisioning-tools";
|
thinToolsPackage = mkOpt' package pkgs.thin-provisioning-tools "Package containing thin-provisioning-tools";
|
||||||
package = mkOpt' package pkgs.borgthin "borgthin package";
|
# Really we should use the version from the overlay, but the package is quite far behind...
|
||||||
|
# Not bothering to update until Borg 2.0 releases
|
||||||
|
package = mkOpt' package inputs.borgthin.packages.${config.nixpkgs.system}.borgthin "borgthin package";
|
||||||
jobs = mkOpt' (attrsOf jobType) { } "borgthin jobs";
|
jobs = mkOpt' (attrsOf jobType) { } "borgthin jobs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+143
-11
@@ -1,6 +1,6 @@
|
|||||||
{ lib, pkgs, extendModules, modulesPath, options, config, ... }:
|
{ lib, pkgs, extendModules, modulesPath, options, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) recursiveUpdate mkOption mkDefault mkIf mkMerge flatten optional;
|
inherit (lib) recursiveUpdate mkOption mkDefault mkIf mkMerge mkForce flatten optional;
|
||||||
inherit (lib.my) mkBoolOpt' dummyOption;
|
inherit (lib.my) mkBoolOpt' dummyOption;
|
||||||
|
|
||||||
cfg = config.my.build;
|
cfg = config.my.build;
|
||||||
@@ -43,15 +43,145 @@ let
|
|||||||
modules = flatten [
|
modules = flatten [
|
||||||
"${modulesPath}/installer/netboot/netboot.nix"
|
"${modulesPath}/installer/netboot/netboot.nix"
|
||||||
allHardware
|
allHardware
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
asNetboot = extendModules {
|
||||||
|
modules = flatten [
|
||||||
|
allHardware
|
||||||
({ pkgs, config, ... }: {
|
({ pkgs, config, ... }: {
|
||||||
system.build.netbootArchive = pkgs.runCommand "netboot-${config.system.name}-archive.tar" { } ''
|
boot = {
|
||||||
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.kernel}" \
|
loader.grub.enable = false;
|
||||||
-f "$out" "${config.system.boot.loader.kernelFile}"
|
initrd = {
|
||||||
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.netbootRamdisk}" \
|
kernelModules = [ "nbd" ];
|
||||||
-f "$out" initrd
|
availableKernelModules = [ "igb" "igc" ];
|
||||||
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.netbootIpxeScript}" \
|
|
||||||
-f "$out" netboot.ipxe
|
systemd = {
|
||||||
'';
|
storePaths = with pkgs; [
|
||||||
|
gnused
|
||||||
|
nbd
|
||||||
|
netcat
|
||||||
|
];
|
||||||
|
extraBin = with pkgs; {
|
||||||
|
dmesg = "${util-linux}/bin/dmesg";
|
||||||
|
ip = "${iproute2}/bin/ip";
|
||||||
|
nbd-client = "${nbd}/bin/nbd-client";
|
||||||
|
};
|
||||||
|
extraConfig = ''
|
||||||
|
DefaultTimeoutStartSec=20
|
||||||
|
DefaultDeviceTimeoutSec=20
|
||||||
|
'';
|
||||||
|
|
||||||
|
network = {
|
||||||
|
enable = true;
|
||||||
|
wait-online.enable = true;
|
||||||
|
|
||||||
|
networks."10-netboot" = {
|
||||||
|
matchConfig.Name = "et-boot";
|
||||||
|
DHCP = "yes";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
nbd = {
|
||||||
|
description = "NBD Root FS";
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
get_cmdline() {
|
||||||
|
${pkgs.gnused}/bin/sed -rn "s/^.*$1=(\\S+).*\$/\\1/p" < /proc/cmdline
|
||||||
|
}
|
||||||
|
|
||||||
|
s="$(get_cmdline nbd_server)"
|
||||||
|
until ${pkgs.netcat}/bin/nc -zv "$s" 22; do
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
exec ${pkgs.nbd}/bin/nbd-client -systemd-mark -N "$(get_cmdline nbd_export)" "$s" /dev/nbd0
|
||||||
|
'';
|
||||||
|
unitConfig = {
|
||||||
|
IgnoreOnIsolate = "yes";
|
||||||
|
DefaultDependencies = "no";
|
||||||
|
};
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "forking";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
wantedBy = [ "initrd-root-device.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
postBootCommands = ''
|
||||||
|
# After booting, register the contents of the Nix store
|
||||||
|
# in the Nix database in the COW root.
|
||||||
|
${config.nix.package}/bin/nix-store --load-db < /nix-path-registration
|
||||||
|
|
||||||
|
# nixos-rebuild also requires a "system" profile and an
|
||||||
|
# /etc/NIXOS tag.
|
||||||
|
touch /etc/NIXOS
|
||||||
|
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.nbd.enable = true;
|
||||||
|
|
||||||
|
fileSystems = {
|
||||||
|
"/" = {
|
||||||
|
fsType = "ext4";
|
||||||
|
device = "/dev/nbd0";
|
||||||
|
noCheck = true;
|
||||||
|
autoResize = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.useNetworkd = mkForce true;
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
network.networks."10-boot" = {
|
||||||
|
matchConfig.Name = "et-boot";
|
||||||
|
DHCP = "yes";
|
||||||
|
networkConfig.KeepConfiguration = "yes";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
system.build = {
|
||||||
|
rootImage = pkgs.callPackage "${modulesPath}/../lib/make-ext4-fs.nix" {
|
||||||
|
storePaths = [ config.system.build.toplevel ];
|
||||||
|
volumeLabel = "netboot-root";
|
||||||
|
};
|
||||||
|
netbootScript = pkgs.writeText "boot.ipxe" ''
|
||||||
|
#!ipxe
|
||||||
|
kernel ${pkgs.stdenv.hostPlatform.linux-kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ifname=et-boot:''${mac} nbd_server=''${next-server} ${toString config.boot.kernelParams} ''${cmdline}
|
||||||
|
initrd initrd
|
||||||
|
boot
|
||||||
|
'';
|
||||||
|
|
||||||
|
netbootTree = pkgs.linkFarm "netboot-${config.system.name}" [
|
||||||
|
{
|
||||||
|
name = config.system.boot.loader.kernelFile;
|
||||||
|
path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "initrd";
|
||||||
|
path = "${config.system.build.initialRamdisk}/initrd";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "rootfs.ext4";
|
||||||
|
path = config.system.build.rootImage;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "boot.ipxe";
|
||||||
|
path = config.system.build.netbootScript;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
netbootArchive = pkgs.runCommand "netboot-${config.system.name}.tar.zst" { } ''
|
||||||
|
export PATH=${pkgs.zstd}/bin:$PATH
|
||||||
|
${pkgs.gnutar}/bin/tar --dereference --zstd -cvC ${config.system.build.netbootTree} -f "$out" .
|
||||||
|
'';
|
||||||
|
};
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -77,6 +207,7 @@ in
|
|||||||
asISO = mkAsOpt asISO "a bootable .iso image";
|
asISO = mkAsOpt asISO "a bootable .iso image";
|
||||||
asContainer = mkAsOpt asContainer "a container";
|
asContainer = mkAsOpt asContainer "a container";
|
||||||
asKexecTree = mkAsOpt asKexecTree "a kexec-able kernel and initrd";
|
asKexecTree = mkAsOpt asKexecTree "a kexec-able kernel and initrd";
|
||||||
|
asNetboot = mkAsOpt asNetboot "a netboot-able kernel initrd, and iPXE script";
|
||||||
|
|
||||||
buildAs = options.system.build;
|
buildAs = options.system.build;
|
||||||
};
|
};
|
||||||
@@ -90,8 +221,8 @@ in
|
|||||||
memorySize = dummyOption;
|
memorySize = dummyOption;
|
||||||
qemu.options = dummyOption;
|
qemu.options = dummyOption;
|
||||||
};
|
};
|
||||||
|
image.baseName = dummyOption;
|
||||||
isoImage = {
|
isoImage = {
|
||||||
isoBaseName = dummyOption;
|
|
||||||
volumeID = dummyOption;
|
volumeID = dummyOption;
|
||||||
edition = dummyOption;
|
edition = dummyOption;
|
||||||
appendToMenuLabel = dummyOption;
|
appendToMenuLabel = dummyOption;
|
||||||
@@ -110,7 +241,8 @@ in
|
|||||||
iso = config.my.asISO.config.system.build.isoImage;
|
iso = config.my.asISO.config.system.build.isoImage;
|
||||||
container = config.my.asContainer.config.system.build.toplevel;
|
container = config.my.asContainer.config.system.build.toplevel;
|
||||||
kexecTree = config.my.asKexecTree.config.system.build.kexecTree;
|
kexecTree = config.my.asKexecTree.config.system.build.kexecTree;
|
||||||
netbootArchive = config.my.asKexecTree.config.system.build.netbootArchive;
|
netbootTree = config.my.asNetboot.config.system.build.netbootTree;
|
||||||
|
netbootArchive = config.my.asNetboot.config.system.build.netbootArchive;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+60
-20
@@ -1,4 +1,4 @@
|
|||||||
{ lib, pkgs, pkgs', inputs, config, ... }:
|
{ lib, pkgsFlake, pkgs, pkgs', self, inputs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkIf mkDefault mkMerge;
|
inherit (lib) mkIf mkDefault mkMerge;
|
||||||
inherit (lib.my) mkDefault';
|
inherit (lib.my) mkDefault';
|
||||||
@@ -9,10 +9,11 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
imports = [
|
imports = [
|
||||||
inputs.impermanence.nixosModule
|
inputs.impermanence.nixosModules.default
|
||||||
inputs.ragenix.nixosModules.age
|
inputs.ragenix.nixosModules.age
|
||||||
inputs.sharry.nixosModules.default
|
inputs.sharry.nixosModules.default
|
||||||
inputs.attic.nixosModules.atticd
|
inputs.copyparty.nixosModules.default
|
||||||
|
inputs.harmonia.nixosModules.harmonia
|
||||||
];
|
];
|
||||||
|
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
@@ -37,10 +38,20 @@ in
|
|||||||
enable = mkDefault true;
|
enable = mkDefault true;
|
||||||
wheelNeedsPassword = mkDefault false;
|
wheelNeedsPassword = mkDefault false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# TODO: Add this to fix login
|
||||||
|
# pam = {
|
||||||
|
# services = {
|
||||||
|
# kmscon.rules. = mkIf config.services.kmscon.config.libseat {
|
||||||
|
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
# };
|
||||||
};
|
};
|
||||||
|
|
||||||
nix = {
|
nix = {
|
||||||
package = pkgs'.mine.nix;
|
package = pkgs'.mine.nix;
|
||||||
|
channel.enable = false;
|
||||||
settings = with lib.my.c.nix; {
|
settings = with lib.my.c.nix; {
|
||||||
trusted-users = [ "@wheel" ];
|
trusted-users = [ "@wheel" ];
|
||||||
experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
|
experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
|
||||||
@@ -53,7 +64,7 @@ in
|
|||||||
pkgs = {
|
pkgs = {
|
||||||
to = {
|
to = {
|
||||||
type = "path";
|
type = "path";
|
||||||
path = "${pkgs.path}";
|
path = "${pkgsFlake}";
|
||||||
};
|
};
|
||||||
exact = true;
|
exact = true;
|
||||||
};
|
};
|
||||||
@@ -65,10 +76,13 @@ in
|
|||||||
};
|
};
|
||||||
nixpkgs = {
|
nixpkgs = {
|
||||||
overlays = [
|
overlays = [
|
||||||
inputs.deploy-rs.overlay
|
inputs.deploy-rs.overlays.default
|
||||||
inputs.sharry.overlays.default
|
inputs.sharry.overlays.default
|
||||||
inputs.borgthin.overlays.default
|
# TODO: Re-enable when borgthin is updated
|
||||||
|
# inputs.borgthin.overlays.default
|
||||||
inputs.boardie.overlays.default
|
inputs.boardie.overlays.default
|
||||||
|
inputs.copyparty.overlays.default
|
||||||
|
inputs.hass-west-wood.overlays.default
|
||||||
];
|
];
|
||||||
config = {
|
config = {
|
||||||
allowUnfree = true;
|
allowUnfree = true;
|
||||||
@@ -127,11 +141,15 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
environment.etc = {
|
||||||
|
"nixos/flake.nix".source = "/run/nixfiles/flake.nix";
|
||||||
|
};
|
||||||
environment.systemPackages = with pkgs; mkMerge [
|
environment.systemPackages = with pkgs; mkMerge [
|
||||||
[
|
[
|
||||||
bash-completion
|
bash-completion
|
||||||
git
|
git
|
||||||
unzip
|
unzip
|
||||||
|
tcpdump
|
||||||
]
|
]
|
||||||
(mkIf config.services.netdata.enable [ netdata ])
|
(mkIf config.services.netdata.enable [ netdata ])
|
||||||
];
|
];
|
||||||
@@ -142,20 +160,21 @@ in
|
|||||||
fish.enable = mkDefault true;
|
fish.enable = mkDefault true;
|
||||||
# TODO: This is expecting to look up the channel for the database...
|
# TODO: This is expecting to look up the channel for the database...
|
||||||
command-not-found.enable = mkDefault false;
|
command-not-found.enable = mkDefault false;
|
||||||
vim.defaultEditor = true;
|
vim = {
|
||||||
|
enable = true;
|
||||||
|
defaultEditor = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
kmscon = {
|
# TODO: Remove if-else when 26.11 releases
|
||||||
# As it turns out, kmscon hasn't been updated in years and has some bugs...
|
kmscon = if (config.system.nixos.release == "26.06:u-26.11") then {
|
||||||
enable = mkDefault false;
|
enable = mkDefault false;
|
||||||
hwRender = mkDefault true;
|
config = {
|
||||||
extraOptions = "--verbose";
|
hwaccel = config.hardware.graphics.enable;
|
||||||
extraConfig =
|
font-name = "SauceCodePro Nerd Font Mono";
|
||||||
''
|
};
|
||||||
font-name=SauceCodePro Nerd Font Mono
|
} else { };
|
||||||
'';
|
|
||||||
};
|
|
||||||
getty.greetingLine = mkDefault' ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
|
getty.greetingLine = mkDefault' ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
|
||||||
|
|
||||||
openssh = {
|
openssh = {
|
||||||
@@ -209,14 +228,35 @@ in
|
|||||||
# python.d plugin script does #!/usr/bin/env bash
|
# python.d plugin script does #!/usr/bin/env bash
|
||||||
path = with pkgs; [ bash ];
|
path = with pkgs; [ bash ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nixfiles-mutable = {
|
||||||
|
description = "Mutable nixfiles";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
path = with pkgs; [ util-linux ];
|
||||||
|
script = ''
|
||||||
|
nixfilesDir="${self}"
|
||||||
|
|
||||||
|
mkdir -p /run/nixfiles{,/.rw,/.work}
|
||||||
|
mount -t overlay overlay -o lowerdir="$nixfilesDir",upperdir=/run/nixfiles/.rw,workdir=/run/nixfiles/.work /run/nixfiles
|
||||||
|
chmod -R u+w /run/nixfiles
|
||||||
|
'';
|
||||||
|
preStop = ''
|
||||||
|
umount /run/nixfiles
|
||||||
|
rm -rf /run/nixfiles
|
||||||
|
'';
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(mkIf config.services.kmscon.enable {
|
(mkIf config.services.kmscon.enable {
|
||||||
fonts.fonts = with pkgs; [
|
fonts.packages = with pkgs; [
|
||||||
(nerdfonts.override {
|
nerd-fonts.sauce-code-pro
|
||||||
fonts = [ "SourceCodePro" ];
|
|
||||||
})
|
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{ lib, pkgs, options, config, systems, ... }:
|
{ lib, pkgs, options, config, systems, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) attrNames attrValues all hashString toJSON;
|
inherit (builtins) attrNames attrValues all hashString toJSON any;
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
groupBy' mapAttrsToList optionalString optional concatMapStringsSep filterAttrs mkOption mkDefault mkIf mkMerge;
|
groupBy' mapAttrsToList optionalString optional concatMapStringsSep filterAttrs mkOption mkDefault mkIf mkMerge;
|
||||||
inherit (lib.my) mkOpt' mkBoolOpt';
|
inherit (lib.my) mkOpt' mkBoolOpt';
|
||||||
@@ -15,6 +15,7 @@ let
|
|||||||
passAsFile = [ "code" ];
|
passAsFile = [ "code" ];
|
||||||
code = ''
|
code = ''
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <systemd/sd-daemon.h>
|
#include <systemd/sd-daemon.h>
|
||||||
@@ -98,6 +99,7 @@ let
|
|||||||
};
|
};
|
||||||
networking = {
|
networking = {
|
||||||
bridge = mkOpt' (nullOr str) null "Network bridge to connect to.";
|
bridge = mkOpt' (nullOr str) null "Network bridge to connect to.";
|
||||||
|
macVLAN = mkOpt' (nullOr str) null "Network interface to make MACVLAN interface from.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -115,26 +117,19 @@ in
|
|||||||
assertion = config.systemd.network.enable;
|
assertion = config.systemd.network.enable;
|
||||||
message = "Containers currently require systemd-networkd!";
|
message = "Containers currently require systemd-networkd!";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
assertion = all (i: i.networking.bridge == null || i.networking.macVLAN == null) (attrValues cfg.instances);
|
||||||
|
message = "Only bridge OR MACVLAN can be set";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
# TODO: Better security
|
# TODO: Better security
|
||||||
my.firewall.trustedInterfaces =
|
my.firewall.trustedInterfaces =
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
(n: _: "ve-${n}")
|
(n: _: "ve-${n}")
|
||||||
(filterAttrs (_: c: c.networking.bridge == null) cfg.instances);
|
(filterAttrs (_: c: c.networking.bridge == null && c.networking.macVLAN == null) cfg.instances);
|
||||||
|
|
||||||
systemd = mkMerge ([
|
systemd = mkMerge (mapAttrsToList (n: c: {
|
||||||
{
|
|
||||||
# By symlinking to the original systemd-nspawn@.service for every instance we force the unit generator to
|
|
||||||
# create overrides instead of replacing the unit entirely
|
|
||||||
packages = [
|
|
||||||
(pkgs.linkFarm "systemd-nspawn-containers" (map (n: {
|
|
||||||
name = "etc/systemd/system/systemd-nspawn@${n}.service";
|
|
||||||
path = "${pkgs.systemd}/example/systemd/system/systemd-nspawn@.service";
|
|
||||||
}) (attrNames cfg.instances)))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
] ++ (mapAttrsToList (n: c: {
|
|
||||||
nspawn."${n}" = {
|
nspawn."${n}" = {
|
||||||
execConfig = {
|
execConfig = {
|
||||||
Boot = true;
|
Boot = true;
|
||||||
@@ -165,6 +160,8 @@ in
|
|||||||
};
|
};
|
||||||
networkConfig = if (c.networking.bridge != null) then {
|
networkConfig = if (c.networking.bridge != null) then {
|
||||||
Bridge = c.networking.bridge;
|
Bridge = c.networking.bridge;
|
||||||
|
} else if (c.networking.macVLAN != null) then {
|
||||||
|
MACVLAN = "${c.networking.macVLAN}:host0";
|
||||||
} else {
|
} else {
|
||||||
VirtualEthernet = true;
|
VirtualEthernet = true;
|
||||||
};
|
};
|
||||||
@@ -182,6 +179,9 @@ in
|
|||||||
c.containerSystem;
|
c.containerSystem;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
# To prevent creating a whole new unit file
|
||||||
|
overrideStrategy = "asDropin";
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
# systemd.nspawn units can't set the root directory directly, but /run/machines/${n} is one of the search paths
|
# systemd.nspawn units can't set the root directory directly, but /run/machines/${n} is one of the search paths
|
||||||
root = "/run/machines/${n}";
|
root = "/run/machines/${n}";
|
||||||
@@ -247,7 +247,7 @@ in
|
|||||||
Bridge = c.networking.bridge;
|
Bridge = c.networking.bridge;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}) cfg.instances));
|
}) cfg.instances);
|
||||||
})
|
})
|
||||||
|
|
||||||
# Inside container
|
# Inside container
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ let
|
|||||||
|
|
||||||
journalctl -o cat --no-pager -n 0 -f -u "$unit" &
|
journalctl -o cat --no-pager -n 0 -f -u "$unit" &
|
||||||
jPid=$!
|
jPid=$!
|
||||||
|
|
||||||
|
# shellcheck disable=SC2329
|
||||||
cleanup() {
|
cleanup() {
|
||||||
# shellcheck disable=SC2317
|
|
||||||
kill "$jPid"
|
kill "$jPid"
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,15 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) optional mkIf mkDefault mkMerge;
|
inherit (lib) optional mkIf mkDefault mkMerge mkOverride;
|
||||||
inherit (lib.my) mkBoolOpt';
|
inherit (lib.my) mkBoolOpt';
|
||||||
|
|
||||||
cfg = config.my.gui;
|
cfg = config.my.gui;
|
||||||
|
|
||||||
|
androidUdevRules = pkgs.runCommand "udev-rules-android" {
|
||||||
|
rulesFile = ./android-udev.rules;
|
||||||
|
} ''
|
||||||
|
install -D "$rulesFile" "$out"/lib/udev/rules.d/51-android.rules
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.gui = with lib.types; {
|
options.my.gui = with lib.types; {
|
||||||
@@ -12,7 +18,7 @@ in
|
|||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
hardware = {
|
hardware = {
|
||||||
opengl.enable = mkDefault true;
|
graphics.enable = mkDefault true;
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd = {
|
systemd = {
|
||||||
@@ -23,15 +29,32 @@ in
|
|||||||
|
|
||||||
security = {
|
security = {
|
||||||
polkit.enable = true;
|
polkit.enable = true;
|
||||||
pam.services.swaylock = {};
|
pam.services.swaylock-plugin = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
users = {
|
||||||
|
groups = {
|
||||||
|
adbusers.gid = lib.my.c.ids.gids.adbusers;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
# for pw-jack
|
# for pw-jack
|
||||||
pipewire.jack
|
pipewire.jack
|
||||||
swaylock
|
swaylock-plugin
|
||||||
];
|
];
|
||||||
services = {
|
services = {
|
||||||
|
resolved = {
|
||||||
|
settings.Resolve = {
|
||||||
|
FallbackDNS = mkOverride 99 (
|
||||||
|
"1.1.1.1#cloudflare-dns.com 8.8.8.8#dns.google " +
|
||||||
|
"1.0.0.1#cloudflare-dns.com 8.8.4.4#dns.google " +
|
||||||
|
"2606:4700:4700::1111#cloudflare-dns.com 2001:4860:4860::8888#dns.google " +
|
||||||
|
"2606:4700:4700::1001#cloudflare-dns.com 2001:4860:4860::8844#dns.google" );
|
||||||
|
LLMNR = "resolve";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
pipewire = {
|
pipewire = {
|
||||||
enable = true;
|
enable = true;
|
||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
@@ -44,13 +67,21 @@ in
|
|||||||
gnome = {
|
gnome = {
|
||||||
gnome-keyring.enable = true;
|
gnome-keyring.enable = true;
|
||||||
};
|
};
|
||||||
|
udisks2.enable = true;
|
||||||
|
|
||||||
udev = {
|
udev = {
|
||||||
|
packages = [
|
||||||
|
androidUdevRules
|
||||||
|
];
|
||||||
extraRules = ''
|
extraRules = ''
|
||||||
# Nvidia
|
# Nvidia
|
||||||
SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0664", GROUP="wheel"
|
SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0664", GROUP="wheel"
|
||||||
# Nintendo
|
# Nintendo
|
||||||
SUBSYSTEM=="usb", ATTR{idVendor}=="057e", MODE="0664", GROUP="wheel"
|
SUBSYSTEM=="usb", ATTR{idVendor}=="057e", MODE="0664", GROUP="wheel"
|
||||||
|
# FT
|
||||||
|
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", MODE="0664", GROUP="wheel"
|
||||||
|
# /dev/player0
|
||||||
|
SUBSYSTEM=="usb", ATTR{idVendor}=="6969", MODE="0664", GROUP="wheel"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -63,7 +94,7 @@ in
|
|||||||
gyre-fonts # TrueType substitutes for standard PostScript fonts
|
gyre-fonts # TrueType substitutes for standard PostScript fonts
|
||||||
liberation_ttf
|
liberation_ttf
|
||||||
unifont
|
unifont
|
||||||
noto-fonts-emoji
|
noto-fonts-color-emoji
|
||||||
];
|
];
|
||||||
|
|
||||||
xdg = {
|
xdg = {
|
||||||
@@ -84,5 +115,13 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
user = {
|
||||||
|
config = {
|
||||||
|
extraGroups = [ "adbusers" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -36,18 +36,16 @@ let
|
|||||||
espOverhead =
|
espOverhead =
|
||||||
if (!mesh.security.enable) then 0
|
if (!mesh.security.enable) then 0
|
||||||
else
|
else
|
||||||
# SPI + seq + IV + pad / header + ICV
|
# UDP encap + SPI + seq + IV + pad / header + ICV
|
||||||
4 + 4 + (if mesh.security.encrypt then 8 else 0) + 2 + 16;
|
(if mesh.udpEncapsulation then 8 else 0) + 4 + 4 + (if mesh.security.encrypt then 8 else 0) + 2 + 16;
|
||||||
# UDP + VXLAN + Ethernet + L3 (IPv4/IPv6)
|
# UDP + VXLAN + Ethernet + L3 (IPv4/IPv6)
|
||||||
overhead = espOverhead + 8 + 8 + 14 + mesh.l3Overhead;
|
overhead = espOverhead + 8 + 8 + 14 + mesh.l3Overhead;
|
||||||
in
|
in
|
||||||
toString (mesh.baseMTU - overhead);
|
toString (mesh.baseMTU - overhead);
|
||||||
|
|
||||||
bridgeFDBs = mapAttrsToList (n: peer: {
|
bridgeFDBs = mapAttrsToList (n: peer: {
|
||||||
bridgeFDBConfig = {
|
MACAddress = "00:00:00:00:00:00";
|
||||||
MACAddress = "00:00:00:00:00:00";
|
Destination = peer.addr;
|
||||||
Destination = peer.addr;
|
|
||||||
};
|
|
||||||
}) otherPeers;
|
}) otherPeers;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -62,7 +60,11 @@ let
|
|||||||
chain l2mesh-${name} {
|
chain l2mesh-${name} {
|
||||||
${optionalString mesh.security.enable ''
|
${optionalString mesh.security.enable ''
|
||||||
udp dport isakmp accept
|
udp dport isakmp accept
|
||||||
meta l4proto esp accept
|
${if mesh.udpEncapsulation then ''
|
||||||
|
udp dport ipsec-nat-t accept
|
||||||
|
'' else ''
|
||||||
|
meta l4proto esp accept
|
||||||
|
''}
|
||||||
''}
|
''}
|
||||||
${optionalString (!mesh.security.enable) (vxlanAllow mesh.vni)}
|
${optionalString (!mesh.security.enable) (vxlanAllow mesh.vni)}
|
||||||
return
|
return
|
||||||
@@ -94,6 +96,7 @@ let
|
|||||||
esp=${if mesh.security.encrypt then "aes_gcm256" else "null-sha256"}
|
esp=${if mesh.security.encrypt then "aes_gcm256" else "null-sha256"}
|
||||||
ikev2=yes
|
ikev2=yes
|
||||||
modecfgpull=no
|
modecfgpull=no
|
||||||
|
encapsulation=${if mesh.udpEncapsulation then "yes" else "no"}
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
otherPeers);
|
otherPeers);
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
{ lib, pkgs, config, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkMerge mkIf mkForce genAttrs concatMapStringsSep;
|
||||||
|
inherit (lib.my) mkOpt' mkBoolOpt';
|
||||||
|
|
||||||
|
cfg = config.my.netboot;
|
||||||
|
|
||||||
|
tftpRoot = pkgs.linkFarm "tftp-root" [
|
||||||
|
{
|
||||||
|
name = "ipxe-x86_64.efi";
|
||||||
|
path = "${pkgs.ipxe}/snp.efi";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
menuFile = pkgs.runCommand "menu.ipxe" {
|
||||||
|
bootHost = cfg.server.host;
|
||||||
|
} ''
|
||||||
|
substituteAll ${./menu.ipxe} "$out"
|
||||||
|
'';
|
||||||
|
|
||||||
|
bootBuilder = pkgs.replaceVarsWith {
|
||||||
|
src = ./netboot-loader-builder.py;
|
||||||
|
isExecutable = true;
|
||||||
|
|
||||||
|
replacements = {
|
||||||
|
inherit (pkgs) python3;
|
||||||
|
bootspecTools = pkgs.bootspec;
|
||||||
|
nix = config.nix.package.out;
|
||||||
|
|
||||||
|
inherit (config.system.nixos) distroName;
|
||||||
|
systemName = config.system.name;
|
||||||
|
inherit (cfg.client) configurationLimit;
|
||||||
|
checkMountpoints = pkgs.writeShellScript "check-mountpoints" ''
|
||||||
|
if ! ${pkgs.util-linuxMinimal}/bin/findmnt /boot > /dev/null; then
|
||||||
|
echo "/boot is not a mounted partition. Is the path configured correctly?" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.my.netboot = with lib.types; {
|
||||||
|
client = {
|
||||||
|
enable = mkBoolOpt' false "Whether network booting should be enabled.";
|
||||||
|
configurationLimit = mkOpt' ints.unsigned 10 "Max generations to show in boot menu.";
|
||||||
|
};
|
||||||
|
server = {
|
||||||
|
enable = mkBoolOpt' false "Whether a netboot server should be enabled.";
|
||||||
|
ip = mkOpt' str null "IP clients should connect to via TFTP.";
|
||||||
|
host = mkOpt' str config.networking.fqdn "Hostname clients should connect to over HTTP / NFS.";
|
||||||
|
allowedPrefixes = mkOpt' (listOf str) null "Prefixes clients should be allowed to connect from (NFS).";
|
||||||
|
installer = {
|
||||||
|
storeSize = mkOpt' str "16GiB" "Total allowed writable size of store.";
|
||||||
|
};
|
||||||
|
instances = mkOpt' (listOf str) [ ] "Systems to hold boot files for.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkMerge [
|
||||||
|
(mkIf cfg.client.enable {
|
||||||
|
systemd = {
|
||||||
|
services = {
|
||||||
|
mount-boot = {
|
||||||
|
description = "Mount /boot";
|
||||||
|
after = [ "systemd-networkd-wait-online.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
path = with pkgs; [ gnused ldns nfs-utils ];
|
||||||
|
script = ''
|
||||||
|
get_cmdline() {
|
||||||
|
sed -rn "s/^.*$1=(\\S+).*\$/\\1/p" < /proc/cmdline
|
||||||
|
}
|
||||||
|
|
||||||
|
host="$(get_cmdline boothost)"
|
||||||
|
if [ -z "$host" ]; then
|
||||||
|
echo "boothost kernel parameter not found!" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
until [ -n "$(drill -Q $host)" ]; do
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p /boot
|
||||||
|
mount.nfs $host:/srv/netboot/systems/${config.system.name} /boot
|
||||||
|
'';
|
||||||
|
|
||||||
|
wantedBy = [ "remote-fs.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
boot.supportedFilesystems.nfs = true;
|
||||||
|
boot.loader = {
|
||||||
|
grub.enable = false;
|
||||||
|
systemd-boot.enable = false;
|
||||||
|
};
|
||||||
|
system = {
|
||||||
|
build.installBootLoader = bootBuilder;
|
||||||
|
boot.loader.id = "ipxe-netboot";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(mkIf cfg.server.enable {
|
||||||
|
environment = {
|
||||||
|
etc = {
|
||||||
|
"netboot/menu.ipxe".source = menuFile;
|
||||||
|
"netboot/shell.efi".source = "${pkgs.edk2-uefi-shell}/shell.efi";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
tmpfiles.settings."10-netboot" = genAttrs
|
||||||
|
(map (i: "/srv/netboot/systems/${i}") cfg.server.instances)
|
||||||
|
(p: {
|
||||||
|
d = {
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
mode = "0777";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
services = {
|
||||||
|
netboot-update = {
|
||||||
|
description = "Update netboot images";
|
||||||
|
after = [ "systemd-networkd-wait-online.service" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
path = with pkgs; [
|
||||||
|
coreutils curl jq zstd gnutar
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
update_nixos() {
|
||||||
|
latestShort="$(curl -s https://git.nul.ie/api/v1/repos/dev/nixfiles/tags/installer \
|
||||||
|
| jq -r .commit.sha | cut -c -7)"
|
||||||
|
if [ -f nixos-installer/tag.txt ] && [ "$(< nixos-installer/tag.txt)" = "$latestShort" ]; then
|
||||||
|
echo "NixOS installer is up to date"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating NixOS installer to $latestShort"
|
||||||
|
mkdir -p nixos-installer
|
||||||
|
fname="jackos-installer-netboot-$latestShort.tar.zst"
|
||||||
|
downloadUrl="$(curl -s https://git.nul.ie/api/v1/repos/dev/nixfiles/releases/tags/installer | \
|
||||||
|
jq -r ".assets[] | select(.name == \"$fname\").browser_download_url")"
|
||||||
|
curl -Lo /tmp/nixos-installer-netboot.tar.zst "$downloadUrl"
|
||||||
|
tar -C nixos-installer --zstd -xf /tmp/nixos-installer-netboot.tar.zst
|
||||||
|
truncate -s "${cfg.server.installer.storeSize}" nixos-installer/rootfs.ext4
|
||||||
|
rm /tmp/nixos-installer-netboot.tar.zst
|
||||||
|
echo "$latestShort" > nixos-installer/tag.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p /srv/netboot
|
||||||
|
cd /srv/netboot
|
||||||
|
|
||||||
|
ln -sf ${menuFile} boot.ipxe
|
||||||
|
ln -sf "${pkgs.edk2-uefi-shell}/shell.efi" "efi-shell-${config.nixpkgs.localSystem.linuxArch}.efi"
|
||||||
|
update_nixos
|
||||||
|
'';
|
||||||
|
startAt = "06:00";
|
||||||
|
wantedBy = [ "network-online.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nbd-server = {
|
||||||
|
serviceConfig = {
|
||||||
|
PrivateUsers = mkForce false;
|
||||||
|
CacheDirectory = "netboot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
atftpd = {
|
||||||
|
enable = true;
|
||||||
|
root = tftpRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
nginx = {
|
||||||
|
virtualHosts."${cfg.server.host}" = {
|
||||||
|
locations."/" = {
|
||||||
|
root = "/srv/netboot";
|
||||||
|
extraConfig = ''
|
||||||
|
autoindex on;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nbd.server = {
|
||||||
|
enable = true;
|
||||||
|
extraOptions = {
|
||||||
|
allowlist = true;
|
||||||
|
};
|
||||||
|
exports = {
|
||||||
|
nixos-installer = {
|
||||||
|
path = "/srv/netboot/nixos-installer/rootfs.ext4";
|
||||||
|
extraOptions = {
|
||||||
|
copyonwrite = true;
|
||||||
|
cowdir = "/var/cache/netboot";
|
||||||
|
sparse_cow = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nfs = {
|
||||||
|
server = {
|
||||||
|
enable = true;
|
||||||
|
exports = ''
|
||||||
|
/srv/netboot/systems ${concatMapStringsSep " " (p: "${p}(rw,all_squash)") cfg.server.allowedPrefixes}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
tmproot.persistence.config.directories = [
|
||||||
|
"/srv/netboot"
|
||||||
|
{ directory = "/var/cache/netboot"; mode = "0700"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
From 7f75d320f6d8ac7ec5185b2145da87f698aec273 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Michael Brown <mcb30@ipxe.org>
|
||||||
|
Date: Mon, 2 Sep 2024 12:24:57 +0100
|
||||||
|
Subject: [PATCH] [etherfabric] Fix use of uninitialised variable in
|
||||||
|
falcon_xaui_link_ok()
|
||||||
|
|
||||||
|
The link status check in falcon_xaui_link_ok() reads from the
|
||||||
|
FCN_XX_CORE_STAT_REG_MAC register only on production hardware (where
|
||||||
|
the FPGA version reads as zero), but modifies the value and writes
|
||||||
|
back to this register unconditionally. This triggers an uninitialised
|
||||||
|
variable warning on newer versions of gcc.
|
||||||
|
|
||||||
|
Fix by assuming that the register exists only on production hardware,
|
||||||
|
and so moving the "modify-write" portion of the "read-modify-write"
|
||||||
|
operation to also be covered by the same conditional check.
|
||||||
|
|
||||||
|
Signed-off-by: Michael Brown <mcb30@ipxe.org>
|
||||||
|
---
|
||||||
|
src/drivers/net/etherfabric.c | 15 +++++++++------
|
||||||
|
1 file changed, 9 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c
|
||||||
|
index b40596beae7..be30b71f79f 100644
|
||||||
|
--- a/src/drivers/net/etherfabric.c
|
||||||
|
+++ b/src/drivers/net/etherfabric.c
|
||||||
|
@@ -2225,13 +2225,16 @@ falcon_xaui_link_ok ( struct efab_nic *efab )
|
||||||
|
sync = ( sync == FCN_XX_SYNC_STAT_DECODE_SYNCED );
|
||||||
|
|
||||||
|
link_ok = align_done && sync;
|
||||||
|
- }
|
||||||
|
|
||||||
|
- /* Clear link status ready for next read */
|
||||||
|
- EFAB_SET_DWORD_FIELD ( reg, FCN_XX_COMMA_DET, FCN_XX_COMMA_DET_RESET );
|
||||||
|
- EFAB_SET_DWORD_FIELD ( reg, FCN_XX_CHARERR, FCN_XX_CHARERR_RESET);
|
||||||
|
- EFAB_SET_DWORD_FIELD ( reg, FCN_XX_DISPERR, FCN_XX_DISPERR_RESET);
|
||||||
|
- falcon_xmac_writel ( efab, ®, FCN_XX_CORE_STAT_REG_MAC );
|
||||||
|
+ /* Clear link status ready for next read */
|
||||||
|
+ EFAB_SET_DWORD_FIELD ( reg, FCN_XX_COMMA_DET,
|
||||||
|
+ FCN_XX_COMMA_DET_RESET );
|
||||||
|
+ EFAB_SET_DWORD_FIELD ( reg, FCN_XX_CHARERR,
|
||||||
|
+ FCN_XX_CHARERR_RESET );
|
||||||
|
+ EFAB_SET_DWORD_FIELD ( reg, FCN_XX_DISPERR,
|
||||||
|
+ FCN_XX_DISPERR_RESET );
|
||||||
|
+ falcon_xmac_writel ( efab, ®, FCN_XX_CORE_STAT_REG_MAC );
|
||||||
|
+ }
|
||||||
|
|
||||||
|
has_phyxs = ( efab->phy_op->mmds & ( 1 << MDIO_MMD_PHYXS ) );
|
||||||
|
if ( link_ok && has_phyxs ) {
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#!ipxe
|
||||||
|
|
||||||
|
set server http://@bootHost@
|
||||||
|
|
||||||
|
# Figure out if client is 64-bit capable
|
||||||
|
cpuid --ext 29 && set arch x86_64 || set arch i386
|
||||||
|
|
||||||
|
isset ${menu-default} || set menu-default exit
|
||||||
|
|
||||||
|
:start
|
||||||
|
menu Welcome to /dev/player0's humble iPXE boot menu
|
||||||
|
item --gap -- Operating Systems
|
||||||
|
iseq ${arch} x86_64 &&
|
||||||
|
item --key n nixos NixOS installer
|
||||||
|
# iseq ${arch} x86_64 &&
|
||||||
|
# item --key a archlinux Arch Linux (archiso x86_64)
|
||||||
|
# iseq ${arch} x86_64 &&
|
||||||
|
# item --key p alpine Alpine Linux
|
||||||
|
item --gap -- Other Options
|
||||||
|
item --key e efi_shell UEFI Shell
|
||||||
|
item --key x xyz netboot.xyz
|
||||||
|
item --key c config iPXE settings
|
||||||
|
item --key s shell Drop to iPXE shell
|
||||||
|
item --key r reboot Reboot
|
||||||
|
item --key q exit Exit (and continue to next boot device)
|
||||||
|
choose --timeout 0 --default ${menu-default} selected || goto cancel
|
||||||
|
goto ${selected}
|
||||||
|
|
||||||
|
:cancel
|
||||||
|
echo You cancelled the menu, dropping you to an iPXE shell
|
||||||
|
|
||||||
|
:shell
|
||||||
|
echo Type 'exit' to go back to the menu
|
||||||
|
shell
|
||||||
|
set menu-default nixos
|
||||||
|
goto start
|
||||||
|
|
||||||
|
:failed
|
||||||
|
echo Booting failed, dropping to shell
|
||||||
|
goto shell
|
||||||
|
|
||||||
|
:reboot
|
||||||
|
reboot
|
||||||
|
|
||||||
|
:exit
|
||||||
|
exit
|
||||||
|
|
||||||
|
:config
|
||||||
|
config
|
||||||
|
set menu-default config
|
||||||
|
goto start
|
||||||
|
|
||||||
|
:efi_shell
|
||||||
|
chain ${server}/efi-shell-${arch}.efi || goto failed
|
||||||
|
|
||||||
|
:xyz
|
||||||
|
chain --autofree https://boot.netboot.xyz || goto failed
|
||||||
|
|
||||||
|
:nixos
|
||||||
|
set cmdline nbd_export=nixos-installer
|
||||||
|
chain ${server}/nixos-installer/boot.ipxe || goto failed
|
||||||
|
|
||||||
|
:archlinux
|
||||||
|
# set mirrorurl https://arch.nul.ie/
|
||||||
|
chain ${server}/arch.ipxe || goto failed
|
||||||
|
|
||||||
|
:alpine
|
||||||
|
chain ${server}/alpine.ipxe || goto failed
|
||||||
+280
@@ -0,0 +1,280 @@
|
|||||||
|
#! @python3@/bin/python3 -B
|
||||||
|
# Based on `nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py`
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from typing import NamedTuple, Dict, List
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
BOOT_MOUNT_POINT = '/boot'
|
||||||
|
STORE_DIR = 'nix'
|
||||||
|
|
||||||
|
# These values will be replaced with actual values during the package build
|
||||||
|
BOOTSPEC_TOOLS = '@bootspecTools@'
|
||||||
|
NIX = '@nix@'
|
||||||
|
DISTRO_NAME = '@distroName@'
|
||||||
|
SYSTEM_NAME = '@systemName@'
|
||||||
|
CONFIGURATION_LIMIT = int('@configurationLimit@')
|
||||||
|
CHECK_MOUNTPOINTS = "@checkMountpoints@"
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BootSpec:
|
||||||
|
init: str
|
||||||
|
initrd: str
|
||||||
|
kernel: str
|
||||||
|
kernelParams: List[str]
|
||||||
|
label: str
|
||||||
|
system: str
|
||||||
|
toplevel: str
|
||||||
|
specialisations: Dict[str, 'BootSpec']
|
||||||
|
sortKey: str
|
||||||
|
initrdSecrets: str | None = None
|
||||||
|
|
||||||
|
class SystemIdentifier(NamedTuple):
|
||||||
|
profile: str | None
|
||||||
|
generation: int
|
||||||
|
specialisation: str | None
|
||||||
|
|
||||||
|
def copy_if_not_exists(source: str, dest: str) -> None:
|
||||||
|
if not os.path.exists(dest):
|
||||||
|
shutil.copyfile(source, dest)
|
||||||
|
|
||||||
|
def generation_dir(profile: str | None, generation: int) -> str:
|
||||||
|
if profile:
|
||||||
|
return f'/nix/var/nix/profiles/system-profiles/{profile}-{generation}-link'
|
||||||
|
else:
|
||||||
|
return f'/nix/var/nix/profiles/system-{generation}-link'
|
||||||
|
|
||||||
|
def system_dir(i: SystemIdentifier) -> str:
|
||||||
|
d = generation_dir(i.profile, i.generation)
|
||||||
|
if i.specialisation:
|
||||||
|
return os.path.join(d, 'specialisation', i.specialisation)
|
||||||
|
else:
|
||||||
|
return d
|
||||||
|
|
||||||
|
def entry_key(i: SystemIdentifier) -> str:
|
||||||
|
pieces = [
|
||||||
|
'nixos',
|
||||||
|
i.profile or None,
|
||||||
|
'generation',
|
||||||
|
str(i.generation),
|
||||||
|
f'specialisation-{i.specialisation}' if i.specialisation else None,
|
||||||
|
]
|
||||||
|
return '-'.join(p for p in pieces if p)
|
||||||
|
|
||||||
|
def bootspec_from_json(bootspec_json: Dict) -> BootSpec:
|
||||||
|
specialisations = bootspec_json['org.nixos.specialisation.v1']
|
||||||
|
specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()}
|
||||||
|
systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {})
|
||||||
|
sortKey = systemdBootExtension.get('sortKey', 'nixos')
|
||||||
|
return BootSpec(
|
||||||
|
**bootspec_json['org.nixos.bootspec.v1'],
|
||||||
|
specialisations=specialisations,
|
||||||
|
sortKey=sortKey
|
||||||
|
)
|
||||||
|
|
||||||
|
bootspecs = {}
|
||||||
|
def get_bootspec(profile: str | None, generation: int) -> BootSpec:
|
||||||
|
k = (profile, generation)
|
||||||
|
if k in bootspecs:
|
||||||
|
return bootspecs[k]
|
||||||
|
|
||||||
|
system_directory = system_dir(SystemIdentifier(profile, generation, None))
|
||||||
|
boot_json_path = os.path.realpath(f'{system_directory}/boot.json')
|
||||||
|
if os.path.isfile(boot_json_path):
|
||||||
|
boot_json_f = open(boot_json_path, 'r')
|
||||||
|
bootspec_json = json.load(boot_json_f)
|
||||||
|
else:
|
||||||
|
boot_json_str = subprocess.check_output([
|
||||||
|
f'{BOOTSPEC_TOOLS}/bin/synthesize',
|
||||||
|
'--version',
|
||||||
|
'1',
|
||||||
|
system_directory,
|
||||||
|
'/dev/stdout',
|
||||||
|
],
|
||||||
|
universal_newlines=True)
|
||||||
|
bootspec_json = json.loads(boot_json_str)
|
||||||
|
|
||||||
|
bs = bootspec_from_json(bootspec_json)
|
||||||
|
bootspecs[k] = bs
|
||||||
|
return bs
|
||||||
|
|
||||||
|
def copy_from_file(file: str, dry_run: bool = False) -> str:
|
||||||
|
store_file_path = os.path.realpath(file)
|
||||||
|
suffix = os.path.basename(store_file_path)
|
||||||
|
store_dir = os.path.basename(os.path.dirname(store_file_path))
|
||||||
|
dst_path = f'/{STORE_DIR}/{store_dir}-{suffix}'
|
||||||
|
if not dry_run:
|
||||||
|
copy_if_not_exists(store_file_path, f'{BOOT_MOUNT_POINT}{dst_path}')
|
||||||
|
return dst_path
|
||||||
|
|
||||||
|
MENU_ITEM = 'item {gen_key} {title} Generation {generation} {description}'
|
||||||
|
|
||||||
|
BOOT_ENTRY = ''':{gen_key}
|
||||||
|
kernel ${{server}}/systems/{system_name}{kernel} {kernel_params} boothost=${{boothost}}
|
||||||
|
initrd ${{server}}/systems/{system_name}{initrd}
|
||||||
|
boot
|
||||||
|
'''
|
||||||
|
|
||||||
|
def gen_entry(i: SystemIdentifier) -> (str, str):
|
||||||
|
bootspec = get_bootspec(i.profile, i.generation)
|
||||||
|
if i.specialisation:
|
||||||
|
bootspec = bootspec.specialisations[i.specialisation]
|
||||||
|
kernel = copy_from_file(bootspec.kernel)
|
||||||
|
initrd = copy_from_file(bootspec.initrd)
|
||||||
|
|
||||||
|
gen_key = entry_key(i)
|
||||||
|
title = '{name}{profile}{specialisation}'.format(
|
||||||
|
name=DISTRO_NAME,
|
||||||
|
profile=' [' + i.profile + ']' if i.profile else '',
|
||||||
|
specialisation=f' ({i.specialisation})' if i.specialisation else '')
|
||||||
|
|
||||||
|
kernel_params = f'init={bootspec.init} '
|
||||||
|
|
||||||
|
kernel_params = kernel_params + ' '.join(bootspec.kernelParams)
|
||||||
|
build_time = int(os.path.getctime(system_dir(i)))
|
||||||
|
build_date = datetime.datetime.fromtimestamp(build_time).strftime('%F')
|
||||||
|
|
||||||
|
return MENU_ITEM.format(
|
||||||
|
gen_key=gen_key,
|
||||||
|
title=title,
|
||||||
|
description=f'{bootspec.label}, built on {build_date}',
|
||||||
|
generation=i.generation,
|
||||||
|
), BOOT_ENTRY.format(
|
||||||
|
gen_key=gen_key,
|
||||||
|
generation=i.generation,
|
||||||
|
system_name=SYSTEM_NAME,
|
||||||
|
kernel=kernel,
|
||||||
|
kernel_params=kernel_params,
|
||||||
|
initrd=initrd,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
|
||||||
|
gen_list = subprocess.check_output([
|
||||||
|
f'{NIX}/bin/nix-env',
|
||||||
|
'--list-generations',
|
||||||
|
'-p',
|
||||||
|
'/nix/var/nix/profiles/' + ('system-profiles/' + profile if profile else 'system')],
|
||||||
|
universal_newlines=True)
|
||||||
|
gen_lines = gen_list.split('\n')
|
||||||
|
gen_lines.pop()
|
||||||
|
|
||||||
|
configurationLimit = CONFIGURATION_LIMIT
|
||||||
|
configurations = [
|
||||||
|
SystemIdentifier(
|
||||||
|
profile=profile,
|
||||||
|
generation=int(line.split()[0]),
|
||||||
|
specialisation=None
|
||||||
|
)
|
||||||
|
for line in gen_lines
|
||||||
|
]
|
||||||
|
return configurations[-configurationLimit:]
|
||||||
|
|
||||||
|
def remove_old_files(gens: list[SystemIdentifier]) -> None:
|
||||||
|
known_paths = []
|
||||||
|
for gen in gens:
|
||||||
|
bootspec = get_bootspec(gen.profile, gen.generation)
|
||||||
|
known_paths.append(copy_from_file(bootspec.kernel, True))
|
||||||
|
known_paths.append(copy_from_file(bootspec.initrd, True))
|
||||||
|
for path in glob.iglob(f'{BOOT_MOUNT_POINT}/{STORE_DIR}/*'):
|
||||||
|
if not path in known_paths and not os.path.isdir(path):
|
||||||
|
os.unlink(path)
|
||||||
|
|
||||||
|
def get_profiles() -> list[str]:
|
||||||
|
if os.path.isdir('/nix/var/nix/profiles/system-profiles/'):
|
||||||
|
return [x
|
||||||
|
for x in os.listdir('/nix/var/nix/profiles/system-profiles/')
|
||||||
|
if not x.endswith('-link')]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
MENU = '''#!ipxe
|
||||||
|
# Server hostname option
|
||||||
|
set boothost ${{66:string}}
|
||||||
|
set server http://${{boothost}}
|
||||||
|
|
||||||
|
:start
|
||||||
|
menu {distro} boot menu
|
||||||
|
item --gap -- Generations
|
||||||
|
{generation_items}
|
||||||
|
item --gap -- Other
|
||||||
|
item --key m main Main netboot menu
|
||||||
|
choose --timeout 5000 --default {menu_default} selected || goto cancel
|
||||||
|
goto ${{selected}}
|
||||||
|
|
||||||
|
:cancel
|
||||||
|
shell
|
||||||
|
goto start
|
||||||
|
|
||||||
|
:error
|
||||||
|
echo Booting failed, dropping to shell
|
||||||
|
shell
|
||||||
|
goto start
|
||||||
|
|
||||||
|
:main
|
||||||
|
chain ${{server}}/boot.ipxe || goto error
|
||||||
|
'''
|
||||||
|
|
||||||
|
def write_menu(gens: list[SystemIdentifier], default: SystemIdentifier) -> None:
|
||||||
|
gen_menu_items = []
|
||||||
|
gen_cmds = []
|
||||||
|
|
||||||
|
for g in gens:
|
||||||
|
bootspec = get_bootspec(g.profile, g.generation)
|
||||||
|
specialisations = [
|
||||||
|
SystemIdentifier(profile=g.profile, generation=g.generation, specialisation=s) for s in bootspec.specialisations]
|
||||||
|
for i in [g] + specialisations:
|
||||||
|
mi, cmds = gen_entry(i)
|
||||||
|
gen_menu_items.append(mi)
|
||||||
|
gen_cmds.append(cmds)
|
||||||
|
|
||||||
|
menu_file = f'{BOOT_MOUNT_POINT}/menu.ipxe'
|
||||||
|
with open(f'{menu_file}.tmp', 'w') as f:
|
||||||
|
f.write(MENU.format(
|
||||||
|
distro=DISTRO_NAME,
|
||||||
|
generation_items='\n'.join(gen_menu_items),
|
||||||
|
menu_default=entry_key(default),
|
||||||
|
))
|
||||||
|
|
||||||
|
print(file=f)
|
||||||
|
print('\n\n'.join(gen_cmds), file=f)
|
||||||
|
|
||||||
|
os.rename(f'{menu_file}.tmp', menu_file)
|
||||||
|
|
||||||
|
def install_bootloader(args: argparse.Namespace) -> None:
|
||||||
|
os.makedirs(f'{BOOT_MOUNT_POINT}/{STORE_DIR}', exist_ok=True)
|
||||||
|
|
||||||
|
gens = get_generations()
|
||||||
|
for profile in get_profiles():
|
||||||
|
gens += get_generations(profile)
|
||||||
|
|
||||||
|
gens = sorted(gens, key=lambda g: entry_key(g), reverse=True)
|
||||||
|
|
||||||
|
remove_old_files(gens)
|
||||||
|
|
||||||
|
for g in gens:
|
||||||
|
if os.path.dirname(get_bootspec(g.profile, g.generation).init) == os.path.realpath(args.default_config):
|
||||||
|
default = g
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False, 'No default generation found'
|
||||||
|
|
||||||
|
write_menu(gens, default)
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description=f'Update {DISTRO_NAME}-related netboot files')
|
||||||
|
parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f'The default {DISTRO_NAME} config to boot')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
subprocess.check_call(CHECK_MOUNTPOINTS)
|
||||||
|
|
||||||
|
install_bootloader(args)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) flatten optional mkIf mkDefault mkMerge;
|
inherit (lib) flatten optional mkIf mkDefault mkMerge versionAtLeast;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
@@ -12,21 +12,13 @@ in
|
|||||||
useNetworkd = mkDefault true;
|
useNetworkd = mkDefault true;
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd = {
|
|
||||||
additionalUpstreamSystemUnits = [
|
|
||||||
# TODO: NixOS has its own version of this, but with `network` instead of `networkd`. Is this just a typo? It
|
|
||||||
# hasn't been updated in 2 years...
|
|
||||||
"systemd-networkd-wait-online@.service"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
services.resolved = {
|
services.resolved = {
|
||||||
domains = [ config.networking.domain ];
|
settings.Resolve = {
|
||||||
# Explicitly unset fallback DNS (Nix module will not allow for a blank config)
|
Domains = [ config.networking.domain ];
|
||||||
extraConfig = ''
|
# Explicitly unset fallback DNS (Nix module will not allow for a blank config)
|
||||||
FallbackDNS=
|
FallbackDNS = "";
|
||||||
Cache=no-negative
|
Cache = "no-negative";
|
||||||
'';
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ let
|
|||||||
inherit (lib.my) mkOpt';
|
inherit (lib.my) mkOpt';
|
||||||
|
|
||||||
cfg = config.my.nvme;
|
cfg = config.my.nvme;
|
||||||
nvme-cli = pkgs.nvme-cli.override {
|
|
||||||
libnvme = pkgs.libnvme.overrideAttrs (o: {
|
|
||||||
patches = o.patches ++ [ ./libnvme-hostconf.patch ];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
hostNQN = "nqn.2014-08.org.nvmexpress:uuid:${cfg.uuid}";
|
hostNQN = "nqn.2014-08.org.nvmexpress:uuid:${cfg.uuid}";
|
||||||
etc = prefix: {
|
etc = prefix: {
|
||||||
@@ -28,29 +23,49 @@ in
|
|||||||
config = mkIf (cfg.uuid != null) {
|
config = mkIf (cfg.uuid != null) {
|
||||||
environment = {
|
environment = {
|
||||||
systemPackages = [
|
systemPackages = [
|
||||||
nvme-cli
|
pkgs.nvme-cli
|
||||||
];
|
];
|
||||||
etc = etc "";
|
etc = etc "";
|
||||||
};
|
};
|
||||||
|
|
||||||
boot.initrd.systemd = mkIf (cfg.boot.nqn != null) {
|
boot = mkIf (cfg.boot.nqn != null) {
|
||||||
contents = etc "/etc/";
|
initrd = {
|
||||||
extraBin.nvme = "${nvme-cli}/bin/nvme";
|
availableKernelModules = [ "rdma_cm" "iw_cm" "ib_cm" "nvme_core" "nvme_rdma" ];
|
||||||
|
kernelModules = [ "nvme-fabrics" ];
|
||||||
|
systemd = {
|
||||||
|
contents = etc "/etc/";
|
||||||
|
extraBin = with pkgs; {
|
||||||
|
dmesg = "${util-linux}/bin/dmesg";
|
||||||
|
ip = "${iproute2}/bin/ip";
|
||||||
|
nvme = "${nvme-cli}/bin/nvme";
|
||||||
|
};
|
||||||
|
|
||||||
services.connect-nvme = {
|
network = {
|
||||||
description = "Connect NVMe-oF";
|
enable = true;
|
||||||
before = [ "initrd-root-device.target" ];
|
wait-online.enable = true;
|
||||||
after = [ "systemd-networkd-wait-online.service" ];
|
};
|
||||||
requires = [ "systemd-networkd-wait-online.service" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
settings.Manager = {
|
||||||
Type = "oneshot";
|
DefaultTimeoutStartSec = 20;
|
||||||
ExecStart = "${nvme-cli}/bin/nvme connect -t rdma -a ${cfg.boot.address} -n ${cfg.boot.nqn}";
|
DefaultDeviceTimeoutSec = 20;
|
||||||
Restart = "on-failure";
|
};
|
||||||
RestartSec = 10;
|
|
||||||
|
services.connect-nvme = {
|
||||||
|
description = "Connect NVMe-oF";
|
||||||
|
before = [ "initrd-root-device.target" ];
|
||||||
|
after = [ "systemd-networkd-wait-online.service" ];
|
||||||
|
requires = [ "systemd-networkd-wait-online.service" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${pkgs.nvme-cli}/bin/nvme connect -t rdma -a ${cfg.boot.address} -n ${cfg.boot.nqn} -q ${hostNQN}";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
wantedBy = [ "initrd-root-device.target" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
wantedBy = [ "initrd-root-device.target" ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+12
-4
@@ -1,7 +1,7 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) isList;
|
inherit (builtins) isList;
|
||||||
inherit (lib) mkMerge mkIf mkDefault mapAttrsToList concatMapStringsSep concatStringsSep;
|
inherit (lib) mkMerge mkIf mkDefault mapAttrsToList concatMapStringsSep concatStringsSep getExe;
|
||||||
inherit (lib.my) mkBoolOpt' mkOpt';
|
inherit (lib.my) mkBoolOpt' mkOpt';
|
||||||
|
|
||||||
# Yoinked from nixos/modules/services/networking/pdns-recursor.nix
|
# Yoinked from nixos/modules/services/networking/pdns-recursor.nix
|
||||||
@@ -165,7 +165,7 @@ let
|
|||||||
|
|
||||||
extraSettingsOpt = with lib.types; mkOpt' (nullOr str) null "Path to extra settings (e.g. for secrets).";
|
extraSettingsOpt = with lib.types; mkOpt' (nullOr str) null "Path to extra settings (e.g. for secrets).";
|
||||||
baseAuthSettings = pkgs.writeText "pdns.conf" (settingsToLines cfg.auth.settings);
|
baseAuthSettings = pkgs.writeText "pdns.conf" (settingsToLines cfg.auth.settings);
|
||||||
baseRecursorSettings = pkgs.writeText "pdns-recursor.conf" (settingsToLines config.services.pdns-recursor.settings);
|
baseRecursorSettings = (pkgs.formats.yaml { }).generate "pdns-recursor.yaml" config.services.pdns-recursor.settings;
|
||||||
generateSettings = type: base: dst: if (cfg."${type}".extraSettingsFile != null) then ''
|
generateSettings = type: base: dst: if (cfg."${type}".extraSettingsFile != null) then ''
|
||||||
oldUmask="$(umask)"
|
oldUmask="$(umask)"
|
||||||
umask 006
|
umask 006
|
||||||
@@ -174,6 +174,14 @@ let
|
|||||||
'' else ''
|
'' else ''
|
||||||
cp "${base}" "${dst}"
|
cp "${base}" "${dst}"
|
||||||
'';
|
'';
|
||||||
|
generateYamlSettings = type: base: dst: if (cfg."${type}".extraSettingsFile != null) then ''
|
||||||
|
oldUmask="$(umask)"
|
||||||
|
umask 006
|
||||||
|
${getExe pkgs.yaml-merge} "${base}" "${cfg."${type}".extraSettingsFile}" > "${dst}"
|
||||||
|
umask "$oldUmask"
|
||||||
|
'' else ''
|
||||||
|
cp "${base}" "${dst}"
|
||||||
|
'';
|
||||||
|
|
||||||
namedConf = pkgs.writeText "pdns-named.conf" ''
|
namedConf = pkgs.writeText "pdns-named.conf" ''
|
||||||
options {
|
options {
|
||||||
@@ -315,9 +323,9 @@ in
|
|||||||
(mkIf cfg.recursor.enable {
|
(mkIf cfg.recursor.enable {
|
||||||
systemd.services.pdns-recursor = {
|
systemd.services.pdns-recursor = {
|
||||||
preStart = ''
|
preStart = ''
|
||||||
${generateSettings "recursor" baseRecursorSettings "/run/pdns-recursor/recursor.conf"}
|
${generateYamlSettings "recursor" baseRecursorSettings "/run/pdns-recursor/recursor.yml"}
|
||||||
'';
|
'';
|
||||||
serviceConfig.ExecStart = [ "" "${pkgs.pdns-recursor}/bin/pdns_recursor --config-dir=/run/pdns-recursor" ];
|
serviceConfig.ExecStart = [ "" "${pkgs.pdns-recursor}/bin/pdns_recursor --config-dir=/run/pdns-recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.pdns-recursor = {
|
services.pdns-recursor = {
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ in
|
|||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
services = {
|
services = {
|
||||||
getty.autologinUser = mkDefault uname;
|
getty.autologinUser = mkDefault uname;
|
||||||
kmscon.autologinUser = mkDefault uname;
|
resolved.settings.Resolve.LLMNR = mkDefault "false";
|
||||||
resolved.llmnr = mkDefault "false";
|
|
||||||
};
|
};
|
||||||
systemd = {
|
systemd = {
|
||||||
timers = {
|
timers = {
|
||||||
@@ -35,10 +34,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
documentation.nixos.enable = mkDefault' false;
|
documentation.nixos.enable = mkDefault' false;
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
tcpdump
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.buildDocsInSandbox = false;
|
meta.buildDocsInSandbox = false;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user