Compare commits

...

132 Commits

Author SHA1 Message Date
ab25c07f69 An attempt was made 2024-01-09 21:56:10 +00:00
aad8adf5da nixos/middleman: Add hack for working Gitea Docker image pull
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 26m52s
2024-01-09 18:50:11 +00:00
205a948486 nixos/middleman: Fix HedgeDoc websockets 2024-01-09 17:35:34 +00:00
39e7c703ba pkgs: Add modrinth-app
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 34m35s
2024-01-09 17:19:13 +00:00
d07ef96d28 envrc: Use watch_file instead of nix_direnv_watch_file 2024-01-09 13:33:00 +00:00
1a29a7d589 nixos/simpcraft: Staging server running
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 26m57s
2024-01-09 00:22:17 +00:00
ca6fe534dc nixos/git: Use separate nginx 2024-01-08 23:31:06 +00:00
e277cce3bc nixos/object: Add HedgeDoc
Some checks failed
CI / Check, build and cache Nix flake (push) Has been cancelled
2024-01-08 21:40:20 +00:00
c9ce57e2c5 nixos/middleman: Add public directory
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 24m53s
2024-01-07 03:40:33 +00:00
04dfc89f07 nixos/simpcraft: Add ToTheMoonStar to whitelist
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 24m53s
2024-01-06 14:59:57 +00:00
cfbbed8285 nixos/simpcraft: Upgrade to 0.1.1 2024-01-05 20:07:48 +00:00
066c87d3d6 nixos/simpcraft: Add mods
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 23m39s
2024-01-05 02:13:15 +00:00
e24ac05bb2 nixos/home/routing-common: Add vibe DNS
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 23m7s
2024-01-03 03:10:44 +00:00
a2b146e8ba nixos: Add librespeed
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 22m56s
2024-01-03 01:07:12 +00:00
a03fdbdbdd nixos/simpcraft: Add Eefah98 to whitelist
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 23m44s
2024-01-02 22:13:06 +00:00
5915f664cc nixos/simpcraft: Add hynge_ to whitelist 2024-01-02 20:49:08 +00:00
42111c530e nixos/colony: Reduce memory for mail VM
Some checks failed
CI / Check, build and cache Nix flake (push) Failing after 35m28s
2024-01-01 21:45:54 +00:00
a741e3eea2 nixos/whale2: Minecraft server updates
Some checks failed
CI / Check, build and cache Nix flake (push) Failing after 1h14m44s
2024-01-01 20:32:15 +00:00
7a4372dfe7 nixos/whale2: Add Minecraft server
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 25m30s
2024-01-01 16:28:04 +00:00
65917bad5c nixos/kelder: Disable minecraft-server 2024-01-01 16:26:45 +00:00
16c7fd7659 nixos/kelder: Update Minecraft and Nextcloud
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 23m55s
2023-12-31 20:21:02 +00:00
2fffefd22d Update river public IP
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 23m24s
2023-12-31 15:15:22 +00:00
c14aebf4a3 nixos/colony: Only start needed LVs in initrd to prevent race
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 23m2s
2023-12-30 21:07:12 +00:00
677f3f26ab home-manager/common: Use vi bindings for tmux 2023-12-29 21:04:07 +00:00
c55600c5af lib: Make awaitPostgres code early in preStart 2023-12-29 19:05:28 +00:00
64c3e2d720 nixos/colony: Give 8GiB more RAM to git
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 53m46s
Installer / Build installer (push) Successful in 4m43s
2023-12-28 18:54:43 +00:00
20d5fa29ae nixos/deploy: Minor improvements 2023-12-28 18:33:55 +00:00
046937de27 Update inputs and add custom NixOS branding
Some checks failed
CI / Check, build and cache Nix flake (push) Failing after 34m39s
2023-12-28 17:39:14 +00:00
bba87ef73b nixos/home/routing-common: Add trusted AS211024 to input chain
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 20m12s
2023-12-23 12:33:10 +00:00
4e3ff0a466 nixos/home/routing-common: Add dynamic DNS update script
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 20m48s
2023-12-23 01:22:41 +00:00
b1af3dbf18 nixos/tower: Add wireshark and Tailscale shell abbrev 2023-12-23 00:49:24 +00:00
f58b71e8d3 nixos/britway: Use internal addresses for DNS 2023-12-23 00:49:02 +00:00
0a86a649a6 nixos: Add SFH VM config
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 21m29s
2023-12-22 01:34:28 +00:00
85189e74f8 Add missing trusted AS211024 prefixes
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 20m12s
2023-12-20 23:43:04 +00:00
e760569b3e Don't blindly trust as211024
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 20m22s
2023-12-20 23:06:27 +00:00
0fe863844f nixos/estuary: Don't announce home prefix for now (MTU issues) 2023-12-20 22:51:01 +00:00
d44fdcfe6a nixos/home/routing-common: Restrict SSH access 2023-12-20 20:41:19 +00:00
b48e7b1c33 nixos: Initial waffletail
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 20m52s
2023-12-20 19:21:31 +00:00
d0b155d786 nixos/middleman: Wait for psql before starting nginx 2023-12-20 18:38:22 +00:00
01cb95de6d nixos/containers: Fix initial dummy deploy 2023-12-20 18:29:08 +00:00
46df9b8aa8 nixos/britway: Setup split DNS
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m27s
2023-12-20 16:43:20 +00:00
b2342c7a05 nixos/tower: Add tailscale
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m54s
2023-12-20 10:16:07 +00:00
a572be0708 nixos/britway: Use AS211024 source address for SNAT
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m26s
2023-12-20 01:44:24 +00:00
68bf705e85 Intra-AS211024 routing
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m5s
2023-12-20 01:30:27 +00:00
21136e98b2 nixos/britway: Add tailscale
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m49s
2023-12-20 00:21:39 +00:00
b537524e5a nixos/britway: Add headscale 2023-12-19 23:40:54 +00:00
10769a4441 nixos/britway: Export route to Dublin
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m2s
2023-12-19 16:59:45 +00:00
9b05e7cd8d nixos/britway: Add BGP
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m10s
2023-12-19 15:51:16 +00:00
eda0cdbe0e nixos: Add initial britway
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 19m34s
2023-12-19 14:30:27 +00:00
f321a039f3 nixos/home/routing-common: Send MTU DHCP option
Some checks reported warnings
CI / Check, build and cache Nix flake (push) Has been cancelled
Installer / Build installer (push) Successful in 4m36s
2023-12-17 15:19:14 +00:00
fc6c4f461f Add netboot archive (including to installer workflow)
Some checks reported warnings
CI / Check, build and cache Nix flake (push) Has been cancelled
Installer / Build installer (push) Successful in 4m31s
2023-12-17 14:56:39 +00:00
22bf75d0a0 deploy-rs: Default to skipping checks and disabling auto / magic rollback 2023-12-17 13:40:25 +00:00
318972a086 nixos/home/routing-common: Split DHCP pools 2023-12-17 13:39:55 +00:00
9fa8299b71 nixos/home/routing-common: Add missing radvd search domain
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m45s
2023-12-16 19:33:53 +00:00
490413c24b nixos/routing-common: Working DHCP
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 18m48s
2023-12-16 18:50:51 +00:00
9cec5051bf nixos/estuary: Remvoe efero upstream 2023-12-16 16:53:55 +00:00
70f49c8438 nixos/home/routing-common: Working IPv6 router 2023-12-16 15:59:33 +00:00
8b0db3ac7f nixos/home/routing-common: Add route to other router's public IPv4 2023-12-16 13:00:10 +00:00
cc07964fac nixos/palace: Add BindsTo= dependency for river on cellar 2023-12-16 12:54:10 +00:00
4624480c8b home-manager/common: Fix Nix cache config for users
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 18m32s
2023-12-13 22:57:54 +00:00
3bc8befb7d nixos/cellar: Enable SPDK dynamic scheduler
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m37s
2023-12-13 21:53:10 +00:00
1b853d405c nixos/palace: Set up virtual IOMMU for cellar
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m49s
2023-12-13 11:23:47 +00:00
82b24c3c55 nixos/cellar: Move SPDK config to separate module
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m56s
2023-12-13 02:19:04 +00:00
2f2764a364 Add json2nix util 2023-12-13 00:40:36 +00:00
4b48d7e788 nixos/nvme: Add module
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m30s
2023-12-12 01:37:14 +00:00
5686aa1a01 nixos/shill: Replicate port forwards for internal routing
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m23s
2023-12-11 16:53:09 +00:00
20a3873d25 nixos/colony: Replicate port forwards for internal routing 2023-12-11 16:51:43 +00:00
d9d7a714cd nixos/firewall: Add ability to forward per external IP 2023-12-11 14:59:40 +00:00
93892224b7 nixos/home/routing-common: Allow VRRP traffic
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m34s
2023-12-11 02:31:26 +00:00
5e5f70501c nixos/river: Initial NVMe-oF booting river :)
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m32s
2023-12-11 01:55:02 +00:00
33eded0626 nixos/cellar: Working NVMe-oF
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 16m40s
2023-12-10 02:29:53 +00:00
9268256309 nixos/vms: Extract vfio-pci-bind to separate package 2023-12-09 21:17:51 +00:00
027cf2af6b nixos/git: Fix container network access
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 30m54s
2023-12-09 17:22:17 +00:00
54f628d3a5 nixos/git: Fix for local access to git.nul.ie
Some checks failed
CI / Check, build and cache Nix flake (push) Failing after 1m43s
2023-12-09 16:55:21 +00:00
56704821b8 nixos/palace: Enable AER
Some checks failed
CI / Check, build and cache Nix flake (push) Failing after 1m2s
2023-12-09 15:22:49 +00:00
ca3547b27a nixos/user: Use impermanence's users options 2023-12-09 15:22:15 +00:00
88b6e00f93 nixos: Add Gitea VM 2023-12-09 15:22:01 +00:00
b4d0d9aff9 nixos/castle: Add libvirt
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m33s
Installer ISO / Build installer ISO (push) Successful in 3m11s
2023-12-05 23:27:16 +00:00
4ee66cdca7 nixos/build: Disable systemd initrd in ISO
Some checks reported warnings
CI / Check, build and cache Nix flake (push) Has been cancelled
2023-12-05 23:26:51 +00:00
1b72739000 devshell: Add update-installer command
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 16m49s
2023-12-05 16:58:20 +00:00
c28acb339b Installer workflow: Create ISO instead of build artifact
Some checks reported warnings
CI / Check, build and cache Nix flake (push) Has been cancelled
Installer ISO / Build installer ISO (push) Successful in 3m13s
2023-12-05 15:36:08 +00:00
3d7a1b8e6c Add workflow to build installer ISO
Some checks reported warnings
CI / Check, build and cache Nix flake (push) Has been cancelled
Installer ISO / Build installer ISO (push) Successful in 6m14s
2023-12-05 14:22:03 +00:00
373fb45831 nixos: Switch to systemd initrd
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m17s
2023-12-04 23:00:25 +00:00
c4e6896a0c nixos: Add initial palace
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 16m35s
2023-12-03 22:58:28 +00:00
a1778e0f1e Upgrade nixpkgs and NixOS stable to 23.11
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 26m58s
2023-12-03 15:06:11 +00:00
0cc35547f2 nixos: Working l2mesh with IPsec
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m15s
2023-11-26 01:29:44 +00:00
7404779c6d nixos/home/routing-common: Add initial VRRP 2023-11-25 17:18:34 +00:00
63f36fabbb nixos/home/routing-common: Set up CAKE qdisc for wan
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m0s
2023-11-25 16:28:43 +00:00
c8b65092be nixos/home/routing-common: Dynamically return WAN IP DNS
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m8s
2023-11-25 15:07:58 +00:00
d347234e82 nixos/home/routing-common: Move mstpd config to separate file
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m10s
2023-11-21 11:18:17 +00:00
0e115544e4 nixos/home/routing-common: Put lan-core on a 1500 byte MACVLAN
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m13s
2023-11-21 09:54:09 +00:00
e6ad150865 nixos/home/routing: Initial working RSTP
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 15m33s
2023-11-20 01:51:46 +00:00
afe124a726 nixos/stream: Remove public IPv4
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 15m21s
2023-11-19 23:50:24 +00:00
655a44a28b nixos/stream: Initial working config
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 15m5s
2023-11-19 22:05:24 +00:00
7330b8f832 nixos/home/routing-common: Add starting DNS server
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 14m21s
2023-11-19 14:32:23 +00:00
aa18ebcb3b nixos/oxbow: Rename to stream 2023-11-19 13:47:23 +00:00
23db7e6c66 Initial oxbow setup
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 14m56s
2023-11-19 00:27:29 +00:00
dfca7fccaa CI: Use pre-installed jq
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 14m1s
2023-11-18 15:59:48 +00:00
edb5767770 Add binary cache timeout and fallback
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 14m19s
2023-11-18 00:10:05 +00:00
a9a6409034 CI: Split check and build into separate steps
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 14m34s
2023-11-17 23:47:11 +00:00
4d345f467f Add devshell and packages to CI
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 16m45s
2023-11-17 23:29:18 +00:00
38e7251300 Set up homes and systems to use cache
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 14m11s
2023-11-17 23:08:28 +00:00
5766bdda99 nixos/object: Use local storage instead of s3
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 14m46s
2023-11-17 22:14:19 +00:00
4133ed48c5 CI: Update Nix cache key
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 28m0s
2023-11-17 21:45:29 +00:00
01897ef0bb Remove CI ragenix key
Some checks reported warnings
CI / Check, build and cache Nix flake (push) Has been cancelled
2023-11-17 20:25:58 +00:00
82eab34f4b nixos/estuary: Fix hairpinned NAT 2023-11-17 20:25:43 +00:00
241abd02ac Full CI 2023-11-17 20:25:33 +00:00
30d07385e6 CI with attic cache
All checks were successful
CI / Check Nix flake (push) Successful in 1m30s
2023-11-17 15:12:13 +00:00
36cd77c697 nixos/object: Initial working atticd cache 2023-11-17 15:05:12 +00:00
f72713410a nixos/gitea: Maybe working CI setup
All checks were successful
CI / Check Nix flake (push) Successful in 21m49s
2023-11-17 02:36:32 +00:00
6f800cd8ef Overwrite runner arch for magic Nix cache
All checks were successful
CI / Check Nix flake (push) Successful in 19m5s
2023-11-17 02:35:33 +00:00
f13f990e5b Add initial Gitea CI
Some checks failed
CI / Check Nix flake (push) Failing after 20s
2023-11-16 22:03:02 +00:00
aa15afa650 nixos/kelder-spoder: Remove deprecated Nextcloud option 2023-11-16 21:42:55 +00:00
f202f8c12c Update nixosConfigurations to reference a preferred default config 2023-11-16 21:42:30 +00:00
3ae6725f0e nixos/whale2: Move actions cache to a separate disk 2023-11-16 19:39:53 +00:00
5ddcf927e3 nixos/gitea: Enable MinIO storage 2023-11-16 14:30:20 +00:00
cc00c7d20b nixos/chatterbox: Add mail config 2023-11-16 13:28:32 +00:00
ca7b6fcd81 home-manager/gui: Add shortcut to capture whole desktop 2023-11-15 16:26:21 +00:00
9d30d76b18 nixos/colony: Add media disk to darts VM 2023-11-13 22:00:02 +00:00
ddc2ed2919 nixos/jackflix: Add Jellyseerr 2023-11-13 20:49:59 +00:00
fe4a24ab17 nixos/colony: Add Gitea LV to borgthin 2023-11-13 18:33:26 +00:00
d5a32419eb Add attic 2023-11-13 14:45:13 +01:00
f8c7183594 nixos/gitea: Set up Gitea Actions 2023-11-13 14:24:08 +01:00
17324455de nixos/shill: Add Gitea incoming mail support 2023-11-13 13:19:44 +01:00
a94c778e10 nixos/shill: Add Gitea 2023-11-13 08:14:16 +00:00
4f8bdc1219 nixos/tmproot: Persist rasdaemon DB 2023-11-12 19:35:41 +01:00
02eb96fe46 nixos/colony: Backup LVM metadata 2023-11-12 19:30:06 +01:00
14f1f5b575 nixos/colony: Add darts VM 2023-11-11 15:56:27 +00:00
9df8a450e8 nixos/colony: Enable rasdaemon 2023-11-11 14:09:45 +00:00
7b9045586f nixos/chatterbox: Add Matrix sliding sync proxy
Also fix nginx upstream definitions
2023-11-07 19:20:27 +00:00
3f01baae38 nixos/jackflix: Fix transmission not starting 2023-11-03 12:58:55 +00:00
2dbc8b398b Split constants into separate lib file 2023-11-02 13:41:50 +00:00
143 changed files with 13274 additions and 1578 deletions

2
.envrc
View File

@@ -1,2 +1,2 @@
nix_direnv_watch_file devshell/{default,commands,install,vm-tasks}.nix
watch_file devshell/{default,commands,install,vm-tasks}.nix
use flake

39
.gitea/workflows/ci.yaml Normal file
View File

@@ -0,0 +1,39 @@
name: CI
on:
push:
branches: [master]
jobs:
check:
name: Check, build and cache Nix flake
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v23
with:
# Gitea will supply a token in GITHUB_TOKEN, which this action will
# try to pass to Nix when downloading from GitHub
github_access_token: ${{ secrets.GH_PULL_TOKEN }}
extra_nix_config: |
# Make sure we're using sandbox
sandbox-fallback = false
extra-substituters = https://nix-cache.nul.ie/main
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=
- 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
run: nix flake check
- name: Build the world
id: build
run: |
path=$(nix build --no-link .#ci.x86_64-linux --json | jq -r .[0].outputs.out)
echo "path=$path" >> "$GITHUB_OUTPUT"
- name: Push to cache
run: |
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
push main ${{ steps.build.outputs.path }}

View File

@@ -0,0 +1,50 @@
name: Installer
on:
push:
tags: [installer]
jobs:
installer:
name: Build installer
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: https://github.com/actions/setup-go@v4
with:
go-version: '>=1.20.1'
- uses: cachix/install-nix-action@v23
with:
github_access_token: ${{ secrets.GH_PULL_TOKEN }}
extra_nix_config: |
# Make sure we're using sandbox
sandbox-fallback = false
extra-substituters = https://nix-cache.nul.ie/main
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=
- name: Set up attic
id: setup
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"
- name: Build installer ISO
run: |
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.iso
ln -s "$(readlink result)"/iso/jackos-installer.iso \
jackos-installer-${{ steps.setup.outputs.short_rev }}.iso
- name: Build installer netboot archive
run: |
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.netbootArchive
ln -s "$(readlink result)" \
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar
- name: Create release
uses: https://gitea.com/actions/release-action@main
with:
title: Latest installer
files: |
jackos-installer-${{ steps.setup.outputs.short_rev }}.iso
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar

View File

@@ -47,8 +47,8 @@ in
(nodesFor homes)
);
autoRollback = true;
magicRollback = true;
autoRollback = false;
magicRollback = false;
};
# Filter out null values so deploy merges overriding options correctly

View File

@@ -48,6 +48,12 @@ in
help = "Print the ed25519 pubkey for a host";
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
}
{
name = "json2nix";
category = "utilities";
help = "Convert JSON to formatted Nix";
command = "nix eval --impure --expr 'builtins.fromJSON (builtins.readFile /dev/stdin)' | ${pkgs.nixfmt}/bin/nixfmt";
}
{
name = "fmt";
@@ -89,7 +95,19 @@ in
name = "build-iso";
category = "tasks";
help = "Build NixOS configuration into an ISO";
command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.my.buildAs.iso"'';
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.iso"'';
}
{
name = "build-kexec";
category = "tasks";
help = "Build NixOS configuration as kexec tree";
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.kexecTree"'';
}
{
name = "build-netboot";
category = "tasks";
help = "Build NixOS configuration as netboot archive";
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.netbootArchive"'';
}
{
name = "build-home";
@@ -121,5 +139,11 @@ in
help = "Update home-manager flake inputs";
command = ''update-inputs home-manager-{unstable,stable}'';
}
{
name = "update-installer";
category = "tasks";
help = "Update installer tag (to trigger new release)";
command = ''git tag -f installer && git push -f origin installer'';
}
];
}

View File

@@ -1,6 +1,5 @@
{ lib, pkgs, ... }:
let
inherit (lib) concatStringsSep;
inherit (lib.my) attrsToNVList;
in
{
@@ -13,9 +12,9 @@ in
NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf"
''
experimental-features = nix-command flakes ca-derivations repl-flake
#substituters = https://nix-cache.nul.ie https://cache.nixos.org
substituters = https://cache.nixos.org
trusted-public-keys = ${concatStringsSep " " lib.my.nix.cacheKeys}
connect-timeout = 5
fallback = true
${lib.my.c.nix.cache.conf}
'');
INSTALLER_SSH_OPTS = "-i .keys/deploy.key";
@@ -25,7 +24,10 @@ in
coreutils
nixVersions.stable
rage
deploy-rs.deploy-rs
(pkgs.writeShellScriptBin "deploy" ''
exec ${deploy-rs.deploy-rs}/bin/deploy --skip-checks "$@"
'')
home-manager
attic-client
];
}

253
flake.lock generated
View File

@@ -22,10 +22,36 @@
"type": "github"
}
},
"attic": {
"inputs": {
"crane": "crane",
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs-unstable"
],
"nixpkgs-stable": [
"nixpkgs-stable"
]
},
"locked": {
"lastModified": 1702969472,
"narHash": "sha256-IJP9sC+/gLUdWhm6TsnWpw6A1zQWUfn53ym63KeLXvU=",
"owner": "zhaofengli",
"repo": "attic",
"rev": "bdafd64910bb2b861cf90fa15f1fc93318b6fbf6",
"type": "github"
},
"original": {
"owner": "zhaofengli",
"repo": "attic",
"type": "github"
}
},
"boardie": {
"inputs": {
"devshell": "devshell",
"flake-utils": "flake-utils_2",
"flake-utils": "flake-utils_3",
"nixpkgs": [
"nixpkgs-unstable"
],
@@ -48,7 +74,7 @@
"borgthin": {
"inputs": {
"devshell": "devshell_2",
"flake-utils": "flake-utils_5",
"flake-utils": "flake-utils_6",
"nixpkgs": [
"nixpkgs-mine"
]
@@ -69,7 +95,28 @@
},
"crane": {
"inputs": {
"flake-compat": "flake-compat_2",
"nixpkgs": [
"attic",
"nixpkgs"
]
},
"locked": {
"lastModified": 1702918879,
"narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=",
"owner": "ipetkov",
"repo": "crane",
"rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"crane_2": {
"inputs": {
"flake-compat": "flake-compat_3",
"flake-utils": [
"ragenix",
"flake-utils"
@@ -122,18 +169,18 @@
},
"deploy-rs": {
"inputs": {
"flake-compat": "flake-compat",
"flake-compat": "flake-compat_2",
"nixpkgs": [
"nixpkgs-unstable"
],
"utils": "utils"
},
"locked": {
"lastModified": 1695052866,
"narHash": "sha256-agn7F9Oww4oU6nPiw+YiYI9Xb4vOOE73w8PAoBRP4AA=",
"lastModified": 1703087360,
"narHash": "sha256-0VUbWBW8VyiDRuimMuLsEO4elGuUw/nc2WDeuO1eN1M=",
"owner": "serokell",
"repo": "deploy-rs",
"rev": "e3f41832680801d0ee9e2ed33eb63af398b090e9",
"rev": "b709d63debafce9f5645a5ba550c9e0983b3d1f7",
"type": "github"
},
"original": {
@@ -144,7 +191,7 @@
},
"devshell": {
"inputs": {
"flake-utils": "flake-utils",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs"
},
"locked": {
@@ -163,7 +210,7 @@
},
"devshell_2": {
"inputs": {
"flake-utils": "flake-utils_4",
"flake-utils": "flake-utils_5",
"nixpkgs": "nixpkgs_3"
},
"locked": {
@@ -185,14 +232,14 @@
"nixpkgs": [
"nixpkgs-unstable"
],
"systems": "systems_3"
"systems": "systems_4"
},
"locked": {
"lastModified": 1698410321,
"narHash": "sha256-MphuSlgpmKwtJncGMohryHiK55J1n6WzVQ/OAfmfoMc=",
"lastModified": 1701787589,
"narHash": "sha256-ce+oQR4Zq9VOsLoh9bZT8Ip9PaMLcjjBUHVPzW5d7Cw=",
"owner": "numtide",
"repo": "devshell",
"rev": "1aed986e3c81a4f6698e85a7452cbfcc4b31a36e",
"rev": "44ddedcbcfc2d52a76b64fb6122f209881bd3e1e",
"type": "github"
},
"original": {
@@ -204,11 +251,11 @@
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1668681692,
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
@@ -218,6 +265,22 @@
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false,
"locked": {
"lastModified": 1673956053,
@@ -234,6 +297,36 @@
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_10": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1642700792,
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
@@ -248,7 +341,7 @@
"type": "github"
}
},
"flake-utils_2": {
"flake-utils_3": {
"inputs": {
"systems": "systems"
},
@@ -266,7 +359,7 @@
"type": "github"
}
},
"flake-utils_3": {
"flake-utils_4": {
"inputs": {
"systems": "systems_2"
},
@@ -284,7 +377,7 @@
"type": "github"
}
},
"flake-utils_4": {
"flake-utils_5": {
"locked": {
"lastModified": 1642700792,
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
@@ -299,7 +392,7 @@
"type": "github"
}
},
"flake-utils_5": {
"flake-utils_6": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
@@ -314,16 +407,16 @@
"type": "github"
}
},
"flake-utils_6": {
"flake-utils_7": {
"inputs": {
"systems": "systems_4"
"systems": "systems_5"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
@@ -332,7 +425,7 @@
"type": "github"
}
},
"flake-utils_7": {
"flake-utils_8": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
@@ -347,9 +440,9 @@
"type": "github"
}
},
"flake-utils_8": {
"flake-utils_9": {
"inputs": {
"systems": "systems_5"
"systems": "systems_6"
},
"locked": {
"lastModified": 1681202837,
@@ -365,21 +458,6 @@
"type": "github"
}
},
"flake-utils_9": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager-stable": {
"inputs": {
"nixpkgs": [
@@ -387,16 +465,16 @@
]
},
"locked": {
"lastModified": 1695108154,
"narHash": "sha256-gSg7UTVtls2yO9lKtP0yb66XBHT1Fx5qZSZbGMpSn2c=",
"lastModified": 1703367386,
"narHash": "sha256-FMbm48UGrBfOWGt8+opuS+uLBLQlRfhiYXhHNcYMS5k=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "07682fff75d41f18327a871088d20af2710d4744",
"rev": "d5824a76bc6bb93d1dce9ebbbcb09a9b6abcc224",
"type": "github"
},
"original": {
"id": "home-manager",
"ref": "release-23.05",
"ref": "release-23.11",
"type": "indirect"
}
},
@@ -407,11 +485,11 @@
]
},
"locked": {
"lastModified": 1698670511,
"narHash": "sha256-jQIu3UhBMPHXzVkHQO1O2gg8SVo5lqAVoC6mOaLQcLQ=",
"lastModified": 1703754036,
"narHash": "sha256-JpJdcj9Tg4lMuYikXDpajA8wOp+rHyn9RD2rKBEM4cQ=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "8e5416b478e465985eec274bc3a018024435c106",
"rev": "c24c298562fe41b39909f632c5a7151bbf6b4628",
"type": "github"
},
"original": {
@@ -421,11 +499,11 @@
},
"impermanence": {
"locked": {
"lastModified": 1697303681,
"narHash": "sha256-caJ0rXeagaih+xTgRduYtYKL1rZ9ylh06CIrt1w5B4g=",
"lastModified": 1703656108,
"narHash": "sha256-hCSUqdFJKHHbER8Cenf5JRzjMlBjIdwdftGQsO0xoJs=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "0f317c2e9e56550ce12323eb39302d251618f5b5",
"rev": "033643a45a4a920660ef91caa391fbffb14da466",
"type": "github"
},
"original": {
@@ -436,7 +514,7 @@
},
"nixGL": {
"inputs": {
"flake-utils": "flake-utils_7",
"flake-utils": "flake-utils_8",
"nixpkgs": [
"nixpkgs-unstable"
]
@@ -444,13 +522,13 @@
"locked": {
"lastModified": 1685908677,
"narHash": "sha256-E4zUPEUFyVWjVm45zICaHRpfGepfkE9Z2OECV9HXfA4=",
"owner": "guibou",
"owner": "nix-community",
"repo": "nixGL",
"rev": "489d6b095ab9d289fe11af0219a9ff00fe87c7c5",
"type": "github"
},
"original": {
"owner": "guibou",
"owner": "nix-community",
"repo": "nixGL",
"type": "github"
}
@@ -473,11 +551,11 @@
},
"nixpkgs-mine": {
"locked": {
"lastModified": 1698758299,
"narHash": "sha256-J5Ljnna3fmtSRXvYOo0fm+65+lsP6FO1DXNp+fnSFA8=",
"lastModified": 1703756459,
"narHash": "sha256-ztEMyPQZh3Pb+LOoWl5lbIK2LenP59sOUBC86CDmLio=",
"owner": "devplayer0",
"repo": "nixpkgs",
"rev": "aebb3f35c0cb5270052dd4a1ac511cca5607a65e",
"rev": "e80160eb2ac3a7111d07cc43a15c16b9edca01ea",
"type": "github"
},
"original": {
@@ -489,11 +567,11 @@
},
"nixpkgs-mine-stable": {
"locked": {
"lastModified": 1698758314,
"narHash": "sha256-n5lkK0deuU/yHN6c+d1IeGFWW0DIOXDe2ZQK0OdMyY4=",
"lastModified": 1703756491,
"narHash": "sha256-9VL34e0gzomwqRnryRn23V2ImYcaZIQdp7CsWg5TmlE=",
"owner": "devplayer0",
"repo": "nixpkgs",
"rev": "2e412711696ca846a039eafcc7a23e80d85de1e3",
"rev": "36611f5f7cfd401f51ad4ca76fd6ee85a714bb74",
"type": "github"
},
"original": {
@@ -505,26 +583,26 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1698562188,
"narHash": "sha256-9nkxGnA/T+jLhHAMFRW157Qi/zfbf5dF1q7HfKROl3o=",
"lastModified": 1703467016,
"narHash": "sha256-/5A/dNPhbQx/Oa2d+Get174eNI3LERQ7u6WTWOlR1eQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3e10c80821dedb93592682379f476745f370a58e",
"rev": "d02d818f22c777aa4e854efc3242ec451e5d462a",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-23.05",
"ref": "nixos-23.11",
"type": "indirect"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1698611440,
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"lastModified": 1703438236,
"narHash": "sha256-aqVBq1u09yFhL7bj1/xyUeJjzr92fXVvQSSEx6AdB1M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"rev": "5f64a12a728902226210bf01d25ec6cbb9d9265b",
"type": "github"
},
"original": {
@@ -566,7 +644,7 @@
},
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils_3",
"flake-utils": "flake-utils_4",
"nixpkgs": "nixpkgs_2"
},
"locked": {
@@ -586,8 +664,8 @@
"ragenix": {
"inputs": {
"agenix": "agenix",
"crane": "crane",
"flake-utils": "flake-utils_8",
"crane": "crane_2",
"flake-utils": "flake-utils_9",
"nixpkgs": [
"nixpkgs-unstable"
],
@@ -609,11 +687,12 @@
},
"root": {
"inputs": {
"attic": "attic",
"boardie": "boardie",
"borgthin": "borgthin",
"deploy-rs": "deploy-rs",
"devshell": "devshell_3",
"flake-utils": "flake-utils_6",
"flake-utils": "flake-utils_7",
"home-manager-stable": "home-manager-stable",
"home-manager-unstable": "home-manager-unstable",
"impermanence": "impermanence",
@@ -653,7 +732,7 @@
},
"sharry": {
"inputs": {
"flake-utils": "flake-utils_9",
"flake-utils": "flake-utils_10",
"nixpkgs": [
"nixpkgs-unstable"
]
@@ -747,13 +826,31 @@
"type": "github"
}
},
"utils": {
"systems_6": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {

View File

@@ -7,13 +7,13 @@
devshell.inputs.nixpkgs.follows = "nixpkgs-unstable";
nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
nixpkgs-stable.url = "nixpkgs/nixos-23.05";
nixpkgs-stable.url = "nixpkgs/nixos-23.11";
nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0";
nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable";
home-manager-unstable.url = "home-manager";
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
home-manager-stable.url = "home-manager/release-23.05";
home-manager-stable.url = "home-manager/release-23.11";
home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
# Stuff used by the flake for build / deployment
@@ -26,7 +26,7 @@
impermanence.url = "github:nix-community/impermanence";
boardie.url = "github:devplayer0/boardie";
boardie.inputs.nixpkgs.follows = "nixpkgs-unstable";
nixGL.url = "github:guibou/nixGL";
nixGL.url = "github:nix-community/nixGL";
nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable";
# Packages not in nixpkgs
@@ -34,6 +34,9 @@
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
borgthin.url = "github:devplayer0/borg";
borgthin.inputs.nixpkgs.follows = "nixpkgs-mine";
attic.url = "github:zhaofengli/attic";
attic.inputs.nixpkgs.follows = "nixpkgs-unstable";
attic.inputs.nixpkgs-stable.follows = "nixpkgs-stable";
};
outputs =
@@ -48,8 +51,8 @@
...
}:
let
inherit (builtins) mapAttrs;
inherit (lib) genAttrs recurseIntoAttrs evalModules;
inherit (builtins) mapAttrs replaceStrings;
inherit (lib) mapAttrs' filterAttrs nameValuePair recurseIntoAttrs evalModules;
inherit (lib.flake) flattenTree eachDefaultSystem;
inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay;
@@ -60,7 +63,7 @@
flake = flake-utils.lib;
};
pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; };
myPkgsOverlay = final: prev: import ./pkgs { lib = prev.lib; pkgs = prev; };
myPkgsOverlay = final: prev: import ./pkgs { lib = final.lib; pkgs = prev; };
# Override the flake-level lib since we're going to use it for non-config specific stuff
pkgsFlakes = mapAttrs (_: pkgsFlake: pkgsFlake // { lib = pkgsFlake.lib.extend libOverlay; }) {
@@ -92,6 +95,7 @@
inputs.ragenix.overlays.default
inputs.deploy-rs.overlay
(flakePackageOverlay inputs.home-manager-unstable system)
inputs.attic.overlays.default
];
}))
pkgsFlakes;
@@ -112,18 +116,20 @@
nixos/boxes/colony
nixos/boxes/tower
nixos/boxes/castle
nixos/boxes/home/stream.nix
nixos/boxes/home/palace
nixos/boxes/britway
nixos/boxes/kelder
# Homes
# home-manager/configs/castle.nix
home-manager/configs/macsimum.nix
# home-manager/configs/macsimum.nix
];
nixfiles = evalModules {
modules = [
{
_module.args = {
inherit lib pkgsFlakes hmFlakes inputs;
inherit lib pkgsFlakes hmFlakes self inputs;
pkgs' = configPkgs';
};
@@ -150,52 +156,49 @@
nixosModules = nixfiles.config.nixos.modules;
homeModules = nixfiles.config.home-manager.modules;
nixosConfigurations = mapAttrs (_: s: s.configuration) nixfiles.config.nixos.systems;
nixosConfigurations = mapAttrs (_: s: s.rendered) nixfiles.config.nixos.systems;
homeConfigurations = mapAttrs (_: s: s.configuration) nixfiles.config.home-manager.homes;
deploy = nixfiles.config.deploy-rs.rendered;
# TODO: Modularise?
herculesCI =
let
system = n: self.nixosConfigurations."${n}".config.system.build.toplevel;
container = n: self.nixosConfigurations."${n}".config.my.buildAs.container;
home = n: self.homeConfigurations."${n}".activationPackage;
in
{
onPush = {
default.outputs = {
shell = self.devShells.x86_64-linux.default;
};
systems.outputs = {
colony = system "colony";
vms = genAttrs [ "estuary" "shill" ] system;
containers = genAttrs [ "jackflix" "middleman" "chatterbox" ] container;
};
homes.outputs = {
castle = home "dev@castle";
};
};
};
} //
(eachDefaultSystem (system:
let
pkgs = pkgs'.mine.${system};
lib = pkgs.lib;
filterSystem = filterAttrs (_: c: c.config.nixpkgs.system == system);
homes =
mapAttrs
(_: h: h.activationPackage)
(filterSystem self.homeConfigurations);
systems =
mapAttrs
(_: h: h.config.system.build.toplevel)
(filterSystem self.nixosConfigurations);
shell = pkgs.devshell.mkShell ./devshell;
in
# Stuff for each platform
{
rec {
checks = flattenTree {
homeConfigurations = recurseIntoAttrs (mapAttrs (_: h: h.activationPackage)
(lib.filterAttrs (_: h: h.config.nixpkgs.system == system) self.homeConfigurations));
homeConfigurations = recurseIntoAttrs homes;
deploy = recurseIntoAttrs (pkgs.deploy-rs.lib.deployChecks self.deploy);
};
packages = flattenTree (import ./pkgs { inherit lib pkgs; });
devShells.default = shell;
devShell = shell;
ci =
let
homes' =
mapAttrs'
(n: v: nameValuePair ''home-${replaceStrings ["@"] ["-at-"] n}'' v)
homes;
systems' = mapAttrs' (n: v: nameValuePair "system-${n}" v) systems;
packages' = mapAttrs' (n: v: nameValuePair "package-${n}" v) packages;
in
pkgs.linkFarm "ci" (homes' // systems' // packages' // {
inherit shell;
});
}));
}

View File

@@ -47,9 +47,14 @@ in
nix = {
package = mkIf (!(versionAtLeast config.home.stateVersion "22.11")) pkgs.nix;
settings = {
settings = with lib.my.c.nix; {
experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
max-jobs = mkDefault "auto";
extra-substituters = cache.substituters;
extra-trusted-public-keys = cache.keys;
connect-timeout = 5;
fallback = true;
};
};
@@ -75,6 +80,7 @@ in
tmux = {
enable = true;
keyMode = "vi";
};
bash = {
@@ -235,18 +241,11 @@ in
exact = true;
};
};
settings = {
substituters = [
#"https://nix-cache.nul.ie"
"https://cache.nixos.org"
];
trusted-public-keys = lib.my.nix.cacheKeys;
};
};
})
(mkIf config.my.isStandalone {
my = {
ssh.authKeys.files = [ lib.my.sshKeyFiles.me ];
ssh.authKeys.files = [ lib.my.c.sshKeyFiles.me ];
};
nix.package = mkIf (versionAtLeast config.home.stateVersion "22.05") pkgs.nix;

View File

@@ -61,6 +61,7 @@ in
settings = {
background_opacity = "0.8";
tab_bar_edge = "top";
shell_integration = "no-sudo";
};
};
@@ -166,7 +167,7 @@ in
"${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+s" = "exec flameshot gui";
"${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 :/
@@ -220,17 +221,6 @@ in
};
};
flameshot = {
enable = false;
settings = {
General = {
disabledTrayIcon = true;
savePath = "/tmp/screenshots";
savePathFixed = false;
};
};
};
playerctld.enable = true;
spotifyd = {
enable = false;

View File

@@ -70,13 +70,17 @@ in
"swaync/config.json" = mkIf (cfg.settings != { }) {
source = configSource;
onChange = ''
${cfg.package}/bin/swaync-client --reload-config
if ${pkgs.systemd}/bin/systemctl --user is-active --quiet swaync; then
${cfg.package}/bin/swaync-client --reload-config
fi
'';
};
"swaync/style.css" = mkIf (cfg.style != null) {
source = styleSource;
onChange = ''
${cfg.package}/bin/swaync-client --reload-css
if ${pkgs.systemd}/bin/systemctl --user is-active --quiet swaync; then
${cfg.package}/bin/swaync-client --reload-css
fi
'';
};
};

347
lib/constants.nix Normal file
View File

@@ -0,0 +1,347 @@
{ lib }:
let
inherit (lib) concatStringsSep;
in
rec {
# See https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/misc/ids.nix
ids = {
uids = {
matrix-syncv3 = 400;
gitea-runner = 401;
jellyseerr = 402;
atticd = 403;
kea = 404;
};
gids = {
matrix-syncv3 = 400;
gitea-runner = 401;
jellyseerr = 402;
atticd = 403;
kea = 404;
};
};
kernel = {
lts = pkgs: pkgs.linuxKernel.packages.linux_6_1;
latest = pkgs: pkgs.linuxKernel.packages.linux_6_6;
};
nginx = rec {
proxyHeaders = ''
# Setting any proxy_header in a child (e.g. location) will nuke the parents...
proxy_set_header X-Origin-URI $request_uri;
proxy_set_header Host $host;
proxy_set_header X-Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Scheme $scheme;
'';
baseHttpConfig = ''
# NixOS provides a logrotate config that auto-compresses :)
log_format main
'$remote_addr - $remote_user [$time_local] $scheme "$host" "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
# optimisation
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# gzip
gzip on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types
application/atom+xml
application/javascript
application/json
application/xml
application/xml+rss
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
gzip_vary on;
# proxying
proxy_buffering off;
proxy_redirect off;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_http_version 1.1;
${proxyHeaders}
'';
};
networkd = {
noL3 = {
LinkLocalAddressing = "no";
DHCP = "no";
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
};
};
nix = {
cache = rec {
substituters = [
"https://nix-cache.${pubDomain}/main"
];
keys = [
"main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8="
];
conf = ''
extra-substituters = ${concatStringsSep " " substituters}
extra-trusted-public-keys = ${concatStringsSep " " keys}
'';
};
};
pubDomain = "nul.ie";
colony = {
domain = "ams1.int.${pubDomain}";
pubV4 = "94.142.240.44";
prefixes = with lib.my.net.cidr; rec {
all = {
v4 = "10.100.0.0/16";
v6 = "2a0e:97c0:4d2:10::/60";
};
base = {
v4 = subnet 8 0 all.v4;
v6 = subnet 4 0 all.v6;
};
vms = {
v4 = subnet 8 1 all.v4;
v6 = subnet 4 1 all.v6;
};
ctrs = {
v4 = subnet 8 2 all.v4;
v6 = subnet 4 2 all.v6;
};
oci = {
v4 = subnet 8 3 all.v4;
v6 = subnet 4 3 all.v6;
};
cust = {
v4 = subnet 8 100 all.v4; # single ip for routing only
v6 = "2a0e:97c0:4d2:2000::/56";
};
mail = {
v4 = "94.142.241.227/32";
v6 = subnet 8 0 cust.v6;
};
darts = {
v4 = "94.142.242.255/32";
v6 = subnet 8 1 cust.v6;
};
vip1 = "94.142.241.224/30";
vip2 = "94.142.242.254/31";
vip3 = "94.142.241.117/32";
as211024 = {
v4 = subnet 8 50 all.v4;
v6 = "2a0e:97c0:4df::/64";
};
home.v6 = "2a0e:97c0:4d0::/48";
};
firewallForwards = aa: [
{
port = "http";
dst = aa.middleman.internal.ipv4.address;
}
{
port = "https";
dst = aa.middleman.internal.ipv4.address;
}
{
port = 8448;
dst = aa.middleman.internal.ipv4.address;
}
{
port = 25565;
dst = aa.simpcraft-oci.internal.ipv4.address;
}
{
port = 25566;
dst = aa.simpcraft-staging-oci.internal.ipv4.address;
}
{
port = 25575;
dst = aa.simpcraft-oci.internal.ipv4.address;
}
{
port = 2456;
dst = aa.valheim-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 2457;
dst = aa.valheim-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 41641;
dst = aa.waffletail.internal.ipv4.address;
proto = "udp";
}
{
port = 25565;
dst = aa.simpcraft-oci.internal.ipv4.address;
proto = "udp";
}
];
fstrimConfig = {
enable = true;
# backup happens at 05:00
interval = "04:45";
};
};
home = rec {
domain = "h.${pubDomain}";
vlans = {
hi = 100;
lo = 110;
untrusted = 120;
wan = 130;
};
hiMTU = 9000;
routers = [
"river"
"stream"
];
routersPubV4 = [
"109.255.1.246"
"109.255.252.63"
];
prefixes = with lib.my.net.cidr; rec {
modem = {
v4 = "192.168.0.0/24";
};
all = {
v4 = "192.168.64.0/18";
v6 = "2a0e:97c0:4d0::/60";
};
core = {
v4 = subnet 6 0 all.v4;
};
hi = {
v4 = subnet 4 1 all.v4;
v6 = subnet 4 1 all.v6;
mtu = hiMTU;
};
lo = {
v4 = subnet 3 1 all.v4;
v6 = subnet 4 2 all.v6;
mtu = 1500;
};
untrusted = {
v4 = subnet 6 16 all.v4;
v6 = subnet 4 3 all.v6;
mtu = 1500;
};
inherit (colony.prefixes) as211024;
};
vips = with lib.my.net.cidr; {
hi = {
v4 = host (4*256-2) prefixes.hi.v4;
v6 = host 65535 prefixes.hi.v6;
};
lo = {
v4 = host (8*256-2) prefixes.lo.v4;
v6 = host 65535 prefixes.lo.v6;
};
untrusted = {
v4 = host 254 prefixes.untrusted.v4;
v6 = host 65535 prefixes.untrusted.v6;
};
as211024 = {
v4 = host 4 prefixes.as211024.v4;
v6 = host ((1*65536*65536*65536) + 65535) prefixes.as211024.v6;
};
};
};
britway = {
domain = "lon1.int.${pubDomain}";
pubV4 = "45.76.141.188";
prefixes = {
vultr = {
v6 = "2001:19f0:7402:128b::/64";
};
inherit (colony.prefixes) as211024;
};
# Need to use this IP as the source address for BGP
assignedV6 = "2001:19f0:7402:128b:5400:04ff:feac:6e06";
};
tailscale = {
prefix = {
v4 = "100.64.0.0/10";
v6 = "fd7a:115c:a1e0::/48";
};
};
as211024 = rec {
trusted = {
v4 = [
colony.prefixes.as211024.v4
colony.prefixes.all.v4
home.prefixes.all.v4
tailscale.prefix.v4
];
v6 = [
colony.prefixes.as211024.v6
colony.prefixes.all.v6
home.prefixes.all.v6
tailscale.prefix.v6
];
};
nftTrust = ''
iifname as211024 ip saddr { ${concatStringsSep ", " trusted.v4} } accept
iifname as211024 ip6 saddr { ${concatStringsSep ", " trusted.v6} } accept
'';
};
kelder = {
groups = {
storage = 2000;
media = 2010;
};
domain = "hentai.engineer";
vpn = {
port = 51820;
};
prefixes = with lib.my.net.cidr; rec {
all.v4 = "172.16.64.0/20";
ctrs.v4 = subnet 4 0 all.v4;
};
};
sshKeyFiles = {
me = ../.keys/me.pub;
deploy = ../.keys/deploy.pub;
rsyncNet = ../.keys/zh2855.rsync.net.pub;
mailcowAcme = ../.keys/mailcow-acme.pub;
};
sshHostKeys = {
mail-vm = ../.keys/mail-vm-host.pub;
};
}

View File

@@ -1,17 +1,31 @@
{ lib }:
let
inherit (builtins) length match replaceStrings elemAt mapAttrs head split filter;
inherit (builtins) length match elemAt filter replaceStrings substring;
inherit (lib)
genAttrs mapAttrs' mapAttrsToList filterAttrsRecursive nameValuePair types
mkOption mkOverride mkForce mkIf mergeEqualOption optional hasPrefix
showWarnings concatStringsSep flatten unique;
genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types
mkOption mkOverride mkForce mkIf mergeEqualOption optional
showWarnings concatStringsSep flatten unique optionalAttrs
mkBefore;
inherit (lib.flake) defaultSystems;
in
rec {
pow =
let
pow' = base: exponent: value:
# FIXME: It will silently overflow on values > 2**62 :(
# The value will become negative or zero in this case
if exponent == 0
then 1
else if exponent <= 1
then value
else (pow' base (exponent - 1) (value * base));
in base: exponent: pow' base exponent base;
attrsToNVList = mapAttrsToList nameValuePair;
inherit (import ./net.nix { inherit lib; }) net;
dns = import ./dns.nix { inherit lib; };
c = import ./constants.nix { inherit lib; };
# Yoinked from nixpkgs/nixos/modules/services/networking/nat.nix
isIPv6 = ip: length (lib.splitString ":" ip) > 2;
@@ -28,6 +42,8 @@ rec {
ports = checked (elemAt m 1);
};
netBroadcast = net': net.cidr.host ((pow 2 (net.cidr.size net')) - 1) net';
mkDefaultSystemsPkgs = path: args': genAttrs defaultSystems (system: import path ((args' system) // { inherit system; }));
mkApp = program: { type = "app"; inherit program; };
mkShellApp = pkgs: name: text: mkApp (pkgs.writeShellScript name text).outPath;
@@ -108,6 +124,21 @@ rec {
home-manager = mkOpt' (enum [ "unstable" "stable" "mine" "mine-stable" ]) "unstable" "Branch of home-manager to use.";
};
nft = rec {
ipEscape = replaceStrings ["." ":"] ["-" "-"];
natFilterChain = ip: "filter-fwd-${ipEscape ip}";
dnatChain = ip: "fwd-${ipEscape ip}";
};
mkVLAN = name: vid: {
"25-${name}" = {
netdevConfig = {
Name = name;
Kind = "vlan";
};
vlanConfig.Id = vid;
};
};
networkdAssignment = iface: a: {
matchConfig.Name = iface;
address =
@@ -122,22 +153,46 @@ rec {
LLDP = true;
EmitLLDP = "customer-bridge";
};
linkConfig = optionalAttrs (a.mtu != null) {
MTUBytes = toString a.mtu;
};
ipv6AcceptRAConfig = {
Token = mkIf (a.ipv6.iid != null) "static:${a.ipv6.iid}";
UseDNS = true;
UseDomains = true;
};
};
dockerNetAssignment =
assignments: name: with assignments."${name}".internal; "ip=${ipv4.address},ip=${ipv6.address}";
systemdAwaitPostgres = pkg: host: {
after = [ "systemd-networkd-wait-online.service" ];
preStart = ''
preStart = mkBefore ''
until ${pkg}/bin/pg_isready -h ${host}; do
sleep 0.5
done
'';
};
vm = rec {
lvmDisk' = name: lv: {
inherit name;
backend = {
driver = "host_device";
filename = "/dev/main/${lv}";
# It appears this needs to be set on the backend _and_ the format
discard = "unmap";
};
format = {
driver = "raw";
discard = "unmap";
};
frontend = "virtio-blk";
};
lvmDisk = lv: lvmDisk' lv lv;
disk = vm: lv: lvmDisk' lv "vm-${vm}-${lv}";
};
deploy-rs =
with types;
let
@@ -186,96 +241,17 @@ rec {
filterOpts = filterAttrsRecursive (_: v: v != null);
};
nginx = {
proxyHeaders = ''
# Setting any proxy_header in a child (e.g. location) will nuke the parents...
proxy_set_header X-Origin-URI $request_uri;
proxy_set_header Host $host;
proxy_set_header X-Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Scheme $scheme;
'';
};
nix = {
cacheKeys = [
"nix-cache.nul.ie-1:XofkqdHQSGFoPjB6aRohQbCU2ILKFqhNjWfoOdQgF5Y="
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
];
};
pubDomain = "nul.ie";
dockerNetAssignment =
assignments: name: with assignments."${name}".internal; "ip=${ipv4.address},ip=${ipv6.address}";
colony = {
domain = "ams1.int.${pubDomain}";
prefixes = with net.cidr; rec {
all = {
v4 = "10.100.0.0/16";
v6 = "2a0e:97c0:4d2:10::/60";
};
base = {
v4 = subnet 8 0 all.v4;
v6 = subnet 4 0 all.v6;
};
vms = {
v4 = subnet 8 1 all.v4;
v6 = subnet 4 1 all.v6;
};
ctrs = {
v4 = subnet 8 2 all.v4;
v6 = subnet 4 2 all.v6;
};
oci = {
v4 = subnet 8 3 all.v4;
v6 = subnet 4 3 all.v6;
};
cust = {
v4 = subnet 8 100 all.v4; # single ip for routing only
v6 = "2a0e:97c0:4d2:2000::/56";
};
mail = {
v4 = "94.142.241.227/32";
v6 = subnet 8 0 cust.v6;
};
vip1 = "94.142.241.224/30";
vip2 = "94.142.242.254/31";
versionOverlay = { self, pkgsFlake }: final: prev:
let
date = substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101");
revCode = flake: flake.shortRev or "dirty";
in
{
trivial = prev.trivial // {
release = "23.12:u-${prev.trivial.release}";
codeName = "Amogus";
revisionWithDefault = default: self.rev or default;
versionSuffix = ".${date}.${revCode self}:u-${revCode pkgsFlake}";
};
fstrimConfig = {
enable = true;
# backup happens at 05:00
interval = "04:45";
};
};
kelder = {
groups = {
storage = 2000;
media = 2010;
};
domain = "hentai.engineer";
vpn = {
port = 51820;
};
prefixes = with net.cidr; rec {
all.v4 = "172.16.64.0/20";
ctrs.v4 = subnet 4 0 all.v4;
};
};
sshKeyFiles = {
me = ../.keys/me.pub;
deploy = ../.keys/deploy.pub;
rsyncNet = ../.keys/zh2855.rsync.net.pub;
mailcowAcme = ../.keys/mailcow-acme.pub;
};
sshHostKeys = {
mail-vm = ../.keys/mail-vm-host.pub;
};
}

View File

@@ -67,4 +67,35 @@ rec {
(a.ipv6.address != null && a.ipv6.genPTR)
''@@PTR:${a.ipv6.address}:${toString ndots}@@ IN PTR ${a.name}.${domain}.'';
};
ifaceA = { pkgs, iface, skipBroadcasts ? [] }:
let
extraFilters = concatMapStringsSep " " (b: ''and .broadcast != \"${b}\"'') skipBroadcasts;
script = pkgs.writeText "if-${iface}-a.lua" ''
local proc = io.popen("${pkgs.iproute2}/bin/ip -j addr show dev ${iface} | ${pkgs.jq}/bin/jq -r '.[0].addr_info[] | select(.family == \"inet\" and .scope == \"global\" ${extraFilters}).local'", "r")
assert(proc, "failed to popen")
local addr_line = proc:read("*l")
assert(proc:close(), "command failed")
assert(addr_line, "no output from command")
return addr_line
'';
in
''A "dofile('${script}')"'';
lookupIP = { pkgs, hostname, server, type ? "A" }:
let
script = pkgs.writeScript "drill-${hostname}-${server}.lua" ''
local proc = io.popen("${pkgs.ldns}/bin/drill -Q @${server} ${hostname} ${type}", "r")
assert(proc, "failed to popen")
local addr_line = proc:read("*l")
assert(proc:close(), "command failed")
assert(addr_line, "no output from command")
return addr_line
'';
in
''${type} "dofile('${script}')"'';
}

197
nixos/boxes/britway/bgp.nix Normal file
View File

@@ -0,0 +1,197 @@
{ lib, pkgs, config, assignments, ... }:
let
inherit (lib.my.c.britway) assignedV6;
securebitSpace = "2a0e:97c0:4d0::/44";
intnet6 = "2a0e:97c0:4df::/48";
amsnet6 = "2a0e:97c0:4d2::/48";
homenet6 = "2a0e:97c0:4d0::/48";
in
{
config = {
my = {
secrets.files."britway/bgp-password-vultr.conf" = {
owner = "bird2";
group = "bird2";
};
};
environment.etc."bird/vultr-password.conf".source = config.age.secrets."britway/bgp-password-vultr.conf".path;
systemd = {
services.bird2.after = [ "systemd-networkd-wait-online@veth0.service" ];
network = {
config.networkConfig.ManageForeignRoutes = false;
};
};
services = {
bird2 = {
enable = true;
preCheckConfig = ''
echo '"dummy"' > vultr-password.conf
'';
# TODO: Clean up and modularise
config = ''
define OWNAS = 211024;
define OWNIP4 = ${assignments.vultr.ipv4.address};
define OWNNETSET4 = [ ${assignments.vultr.ipv4.address}/32 ];
define INTNET6 = ${intnet6};
define AMSNET6 = ${amsnet6};
define HOMENET6 = ${homenet6};
define OWNIP6 = ${assignments.vultr.ipv6.address};
define OWNNETSET6 = [ ${intnet6}, ${amsnet6}, ${homenet6} ];
#define TRANSSET6 = [ ::1/128 ];
define DUB1IP6 = ${lib.my.c.home.vips.as211024.v6};
define PREFIXP = 110;
define PREFPEER = 120;
filter bgp_import {
if net !~ OWNNETSET4 && net !~ OWNNETSET6 then accept; else reject;
}
filter bgp_export {
if net ~ OWNNETSET4 || net ~ OWNNETSET6 then accept; else reject;
}
router id from "veth0";
protocol device {}
protocol direct {
interface "veth0";
ipv4;
ipv6;
}
protocol static static4 {
ipv4 {
import all;
export none;
};
}
protocol static static6 {
# Special case: We have to do the routing on behalf of this _internal_ next-hop
route INTNET6 via "as211024";
route HOMENET6 via DUB1IP6;
ipv6 {
import all;
export none;
};
}
protocol kernel kernel4 {
ipv4 {
import none;
export none;
};
}
protocol kernel kernel6 {
ipv6 {
import none;
export filter {
if net = HOMENET6 then accept;
reject;
};
};
}
protocol bgp bgptools {
local as OWNAS;
multihop;
description "bgp.tools monitoring";
neighbor 2a0c:2f07:9459::b11 as 212232;
source address OWNIP6;
ipv4 {
import none;
export all;
add paths tx;
};
ipv6 {
import none;
export all;
add paths tx;
};
}
template bgp base_bgp4 {
local as OWNAS;
direct;
allow local as;
ipv4 {
import keep filtered;
export none;
};
}
template bgp upstream_bgp4 from base_bgp4 {
ipv4 {
#import none;
import filter bgp_import;
};
}
template bgp peer_bgp4 from base_bgp4 {
ipv4 {
import filter bgp_import;
preference PREFPEER;
};
}
template bgp ixp_bgp4 from base_bgp4 {
ipv4 {
import filter bgp_import;
preference PREFIXP;
};
}
template bgp base_bgp6 {
local ${assignedV6} as OWNAS;
direct;
# So we can see routes we announce from other routers
allow local as;
ipv6 {
import keep filtered;
export filter bgp_export;
};
}
template bgp upstream_bgp6 from base_bgp6 {
ipv6 {
#import none;
import filter bgp_import;
};
}
template bgp peer_bgp6 from base_bgp6 {
ipv6 {
import filter bgp_import;
preference PREFPEER;
};
}
template bgp ixp_bgp6 from base_bgp6 {
ipv6 {
import filter bgp_import;
preference PREFIXP;
};
}
protocol bgp upstream4_vultr from upstream_bgp4 {
description "Vultr transit (IPv4)";
neighbor 169.254.169.254 as 64515;
multihop 2;
password
include "vultr-password.conf";;
}
protocol bgp upstream6_vultr from upstream_bgp6 {
description "Vultr transit (IPv6)";
neighbor 2001:19f0:ffff::1 as 64515;
multihop 2;
password
include "vultr-password.conf";;
}
'';
};
};
};
}

View File

@@ -0,0 +1,174 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.britway) prefixes domain pubV4 assignedV6;
in
{
nixos.systems.britway = {
system = "x86_64-linux";
nixpkgs = "mine";
assignments = {
vultr = {
inherit domain;
ipv4 = {
address = pubV4;
mask = 23;
gateway = "45.76.140.1";
};
ipv6 = {
iid = "::1";
address = "2001:19f0:7402:128b::1";
};
};
as211024 = {
ipv4 = {
address = net.cidr.host 5 prefixes.as211024.v4;
gateway = null;
};
ipv6.address = net.cidr.host ((2*65536*65536*65536) + 1) prefixes.as211024.v6;
};
};
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge mkForce;
inherit (lib.my) networkdAssignment;
in
{
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
./bgp.nix
./nginx.nix
./tailscale.nix
];
config = mkMerge [
{
boot = {
initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "sr_mod" ];
loader = {
systemd-boot.enable = false;
grub = {
enable = true;
device = "/dev/vda";
};
};
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-partuuid/c557ef12-da44-41d1-84f5-d32a711feefd";
fsType = "ext4";
};
"/nix" = {
device = "/dev/disk/by-partuuid/d42d0853-b054-4104-8afd-6d36287c7ca3";
fsType = "ext4";
};
"/persist" = {
device = "/dev/disk/by-partuuid/f14fbcf4-5242-456b-a4db-ef15d053d62e";
fsType = "ext4";
neededForBoot = true;
};
};
services = {
iperf3 = {
enable = true;
openFirewall = true;
};
};
networking = { inherit domain; };
systemd.network = {
config = {
routeTables.ts-extra = 1337;
};
links = {
"10-veth0" = {
matchConfig.PermanentMACAddress = "56:00:04:ac:6e:06";
linkConfig.Name = "veth0";
};
};
networks = {
"20-veth0" = mkMerge [
(networkdAssignment "veth0" assignments.vultr)
{
address = [ "${assignedV6}/64" ];
}
];
"90-l2mesh-as211024" = mkMerge [
(networkdAssignment "as211024" assignments.as211024)
{
matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false;
routes = map (r: { routeConfig = r; }) [
{
Destination = lib.my.c.colony.prefixes.all.v4;
Gateway = allAssignments.estuary.as211024.ipv4.address;
}
{
Destination = lib.my.c.home.prefixes.all.v4;
Gateway = lib.my.c.home.vips.as211024.v4;
}
{
# Just when routing traffic from Tailscale nodes, otherwise use WAN
Destination = lib.my.c.colony.prefixes.all.v6;
Gateway = allAssignments.estuary.as211024.ipv6.address;
Table = "ts-extra";
}
];
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [
{
IncomingInterface = "tailscale0";
To = lib.my.c.colony.prefixes.all.v6;
Table = "ts-extra";
}
];
}
];
};
};
my = {
server.enable = true;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAmXC9egI46Qtaiifhq2I+rv2s1yPyzTlO4BHzUb+3Su";
files = {
"l2mesh/as211024.key" = {};
};
};
vpns = {
l2.pskFiles = {
as211024 = config.age.secrets."l2mesh/as211024.key".path;
};
};
firewall = {
trustedInterfaces = [ "tailscale0" ];
extraRules = ''
table inet filter {
chain forward {
${lib.my.c.as211024.nftTrust}
oifname as211024 accept
}
}
table inet nat {
chain postrouting {
iifname tailscale0 oifname veth0 snat ip to ${assignments.vultr.ipv4.address}
iifname tailscale0 oifname veth0 snat ip6 to ${assignments.as211024.ipv6.address}
}
}
'';
};
};
}
];
};
};
}

View File

@@ -0,0 +1,109 @@
{ lib, pkgs, config, ... }:
let
inherit (builtins) mapAttrs;
inherit (lib) mkMerge mkDefault;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
in
{
config = {
my = {
secrets.files = {
"dhparams.pem" = {
owner = "acme";
group = "acme";
mode = "440";
};
"britway/cloudflare-credentials.conf" = {
owner = "acme";
group = "acme";
};
};
firewall = {
tcp.allowed = [ "http" "https" ];
};
};
users = {
users = {
nginx.extraGroups = [ "acme" ];
};
};
security.acme = {
acceptTerms = true;
defaults = {
email = "dev@nul.ie";
server = "https://acme-v02.api.letsencrypt.org/directory";
reloadServices = [ "nginx" ];
dnsResolver = "8.8.8.8";
};
certs = {
"${pubDomain}" = {
extraDomainNames = [
"*.${pubDomain}"
];
dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."britway/cloudflare-credentials.conf".path;
};
};
};
services = {
nginx = {
enable = true;
enableReload = true;
logError = "stderr info";
recommendedTlsSettings = true;
serverTokens = true;
sslDhparam = config.age.secrets."dhparams.pem".path;
# Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = ''
${baseHttpConfig}
# caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=512m;
'';
virtualHosts =
let
hosts = {
"_" = {
default = true;
forceSSL = true;
onlySSL = false;
locations = {
"/".root = "${pkgs.nginx}/html";
};
};
"ts.${pubDomain}" = {
locations."/" = {
proxyPass = "http://localhost:${toString config.services.headscale.port}";
proxyWebsockets = true;
extraConfig = ''
proxy_buffering off;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
'';
};
};
};
defaultsFor = mapAttrs (n: _: {
onlySSL = mkDefault true;
useACMEHost = mkDefault pubDomain;
kTLS = mkDefault true;
http2 = mkDefault true;
});
in
mkMerge [
hosts
(defaultsFor hosts)
];
};
};
};
}

View File

@@ -0,0 +1,110 @@
{ lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib) concatStringsSep;
inherit (lib.my.c) pubDomain;
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 "," [
lib.my.c.home.prefixes.all.v4
lib.my.c.home.prefixes.all.v6
];
pubNameservers = [
"1.1.1.1"
"1.0.0.1"
"2606:4700:4700::1111"
"2606:4700:4700::1001"
];
in
{
config = {
environment.systemPackages = [
# For CLI
config.services.headscale.package
];
services = {
headscale = {
enable = true;
package = headscale;
settings = {
disable_check_updates = true;
unix_socket_permission = "0770";
server_url = "https://ts.${pubDomain}";
db_type = "sqlite3";
db_path = "/var/lib/headscale/db.sqlite3";
noise.private_key_path = "/var/lib/headscale/noise_private.key";
ip_prefixes = with lib.my.c.tailscale.prefix; [ v4 v6 ];
dns_config = {
# Use IPs that will route inside the VPN to prevent interception
# (e.g. DNS rebinding filtering)
restricted_nameservers = {
"${domain}" = pubNameservers;
"${lib.my.c.colony.domain}" = with allAssignments.estuary.base; [
ipv4.address ipv6.address
];
"${lib.my.c.home.domain}" = with allAssignments; [
river.hi.ipv4.address
river.hi.ipv6.address
stream.hi.ipv4.address
stream.hi.ipv6.address
];
};
magic_dns = true;
base_domain = "ts.${pubDomain}";
override_local_dns = false;
};
oidc = {
only_start_if_oidc_is_available = true;
issuer = "https://accounts.google.com";
client_id = "545475967061-l45cln081mp8t4li2c34v7t7b8la6f4f.apps.googleusercontent.com";
client_secret_path = config.age.secrets."britway/oidc-secret.txt".path;
scope = [ "openid" "profile" "email" ];
allowed_users = [ "jackos1998@gmail.com" ];
};
};
};
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://ts.nul.ie"
"--netfilter-mode=off"
"--advertise-exit-node"
"--advertise-routes=${advRoutes}"
"--accept-routes=false"
];
};
};
my = {
secrets = {
files = {
"britway/oidc-secret.txt" = {
owner = "headscale";
group = "headscale";
mode = "440";
};
"tailscale-auth.key" = {};
};
};
};
};
}

View File

@@ -1,12 +1,33 @@
{ lib, ... }: {
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c.home) domain vlans prefixes;
in
{
nixos.systems.castle = {
system = "x86_64-linux";
nixpkgs = "mine";
home-manager = "mine";
assignments = {
hi = {
inherit domain;
ipv4 = {
address = net.cidr.host 40 prefixes.hi.v4;
mask = 22;
gateway = null;
};
ipv6 = {
iid = "::3:1";
address = net.cidr.host (65536*3+1) prefixes.hi.v6;
};
};
};
configuration = { lib, pkgs, modulesPath, config, systems, assignments, allAssignments, ... }:
let
inherit (lib) mkIf mkMerge mkForce;
inherit (lib.my) mkVLAN networkdAssignment;
in
{
hardware = {
@@ -25,7 +46,7 @@
efi.canTouchEfiVariables = false;
timeout = 10;
};
kernelPackages = pkgs.linuxKernel.packages.linux_6_5;
kernelPackages = lib.my.c.kernel.latest pkgs;
kernelModules = [ "kvm-amd" ];
kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ];
kernelPatches = [
@@ -83,8 +104,17 @@
blueman.enable = true;
};
programs = {
virt-manager.enable = true;
wireshark = {
enable = true;
package = pkgs.wireshark-qt;
};
};
virtualisation.libvirtd.enable = true;
networking = {
domain = "h.${lib.my.pubDomain}";
domain = "h.${lib.my.c.pubDomain}";
firewall.enable = false;
};
@@ -117,6 +147,9 @@
systemd = {
network = {
wait-online.enable = false;
netdevs = mkMerge [
(mkVLAN "lan-hi" vlans.hi)
];
links = {
"10-et2.5g" = {
matchConfig.MACAddress = "c8:7f:54:6e:17:0f";
@@ -127,15 +160,33 @@
linkConfig.Name = "et10g";
};
"12-et100g" = {
matchConfig.MACAddress = "24:8a:07:a8:fe:3a";
linkConfig.Name = "et100g";
matchConfig.PermanentMACAddress = "24:8a:07:a8:fe:3a";
linkConfig = {
Name = "et100g";
MTUBytes = "9000";
};
};
};
networks = {
"50-lan" = {
matchConfig.Name = "et2.5g";
DHCP = "yes";
DHCP = "no";
address = [ "10.16.7.1/16" ];
};
"50-et100g" = {
matchConfig.Name = "et100g";
vlan = [ "lan-hi" ];
networkConfig.IPv6AcceptRA = false;
};
"60-lan-hi" = mkMerge [
(networkdAssignment "lan-hi" assignments.hi)
{
DHCP = "yes";
matchConfig.Name = "lan-hi";
linkConfig.MTUBytes = "9000";
}
];
};
};
};
@@ -196,6 +247,7 @@
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr";
};
nvme.uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b";
firewall = {
enable = false;

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c.colony) domain prefixes firewallForwards;
in
{
imports = [ ./vms ];
@@ -53,10 +53,11 @@ in
cpu = {
amd.updateMicrocode = true;
};
rasdaemon.enable = true;
};
boot = {
kernelPackages = pkgs.linuxKernel.packages.linux_6_1.extend (self: super: {
kernelPackages = (lib.my.c.kernel.lts pkgs).extend (self: super: {
kernel = super.kernel.override {
structuredExtraConfig = with lib.kernel; {
#SOME_OPT = yes;
@@ -65,10 +66,21 @@ in
};
});
kernelModules = [ "kvm-amd" ];
kernelParams = [ "amd_iommu=on" "console=ttyS0,115200n8" "console=ttyS1,115200n8" "console=tty0" ];
kernelParams = [
"amd_iommu=on"
"console=ttyS0,115200n8" "console=ttyS1,115200n8" "console=tty0"
"systemd.setenv=SYSTEMD_SULOGIN_FORCE=1"
];
initrd = {
kernelModules = [ "dm-raid" ];
availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
systemd = {
enable = true;
# Onlu activate volumes needed for boot to prevent thin check from getting killed while switching root
contents."/etc/lvm/lvm.conf".text = ''
activation/auto_activation_volume_list = [ "main/colony-nix" "main/colony-persist" ]
'';
};
};
};
@@ -93,11 +105,11 @@ in
};
programs.ssh.knownHostsFiles = [
lib.my.sshKeyFiles.rsyncNet
lib.my.c.sshKeyFiles.rsyncNet
];
services = {
fstrim = lib.my.colony.fstrimConfig;
fstrim = lib.my.c.colony.fstrimConfig;
lvm = {
boot.thin.enable = true;
dmeventd.enable = true;
@@ -136,7 +148,33 @@ in
services = {
"serial-getty@ttyS0".enable = true;
"serial-getty@ttyS1".enable = true;
lvm-activate-main = {
description = "Activate remaining LVs";
before = [ "local-fs-pre.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.lvm2.bin}/bin/vgchange -aay main";
};
wantedBy = [ "sysinit.target" ];
};
rsync-lvm-meta = {
description = "rsync lvm metadata backups / archives to rsync.net";
serviceConfig = {
Type = "oneshot";
# Only run when no other process is using CPU or disk
CPUSchedulingPolicy = "idle";
IOSchedulingClass = "idle";
};
script = ''
${pkgs.rsync}/bin/rsync -av --delete --delete-after \
-e "${pkgs.openssh}/bin/ssh -i ${config.age.secrets."colony/rsync.key".path}" \
/etc/lvm/{archive,backup} zh2855@zh2855.rsync.net:colony/lvm/
'';
wantedBy = [ "borgthin-job-main.service" ];
after = [ "borgthin-job-main.service" ];
};
borgthin-rsync = {
description = "rsync borgthin backups to rsync.net";
serviceConfig = {
@@ -214,35 +252,48 @@ in
};
ipv6Prefixes = [
{
ipv6PrefixConfig.Prefix = lib.my.colony.prefixes.vms.v6;
ipv6PrefixConfig.Prefix = prefixes.vms.v6;
}
];
routes = map (r: { routeConfig = r; }) [
{
Destination = lib.my.colony.prefixes.ctrs.v4;
Destination = prefixes.ctrs.v4;
Gateway = allAssignments.shill.routing.ipv4.address;
}
{
Destination = lib.my.colony.prefixes.ctrs.v6;
Destination = prefixes.ctrs.v6;
Gateway = allAssignments.shill.internal.ipv6.address;
}
{
Destination = allAssignments.shill.internal.ipv4.address;
Gateway = allAssignments.shill.routing.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.shill.routing.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.shill.internal.ipv6.address;
}
{
Destination = lib.my.colony.prefixes.oci.v4;
Destination = prefixes.oci.v4;
Gateway = allAssignments.whale2.routing.ipv4.address;
}
{
Destination = lib.my.colony.prefixes.oci.v6;
Destination = prefixes.oci.v6;
Gateway = allAssignments.whale2.internal.ipv6.address;
}
{
Destination = allAssignments.whale2.internal.ipv4.address;
Gateway = allAssignments.whale2.routing.ipv4.address;
}
{
Destination = allAssignments.git.internal.ipv4.address;
Gateway = allAssignments.git.routing.ipv4.address;
}
];
}
];
@@ -275,6 +326,29 @@ in
}
];
};
"90-vm-darts" = {
matchConfig.Name = "vm-darts";
address = [
(net.cidr.subnet 8 2 prefixes.cust.v4)
prefixes.darts.v6
];
networkConfig = {
IPv6AcceptRA = false;
IPv6SendRA = true;
};
ipv6Prefixes = [
{
ipv6PrefixConfig.Prefix = prefixes.darts.v6;
}
];
routes = map (r: { routeConfig = r; }) [
{
Destination = prefixes.darts.v4;
Scope = "link";
}
];
};
};
};
};
@@ -305,8 +379,9 @@ in
firewall = {
trustedInterfaces = [ "vms" ];
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
extraRules = ''
define cust = { vm-mail }
define cust = { vm-mail, vm-darts }
table inet filter {
chain forward {
# Trust that the outer firewall has done the filtering!
@@ -331,6 +406,8 @@ in
"vm-estuary-persist"
"vm-whale2-persist"
"vm-mail-data"
"vm-git-persist"
"git"
];
compression = "zstd,5";
extraCreateArgs = [ "--stats" ];

View File

@@ -3,35 +3,22 @@
./estuary
./shill
./whale2
./git
];
nixos.systems.colony.configuration = { lib, pkgs, config, systems, ... }:
let
inherit (builtins) listToAttrs;
inherit (lib) mkIf mkMerge optionals;
vmLVM = vm: lv: {
name = lv;
backend = {
driver = "host_device";
filename = "/dev/main/vm-${vm}-${lv}";
# It appears this needs to be set on the backend _and_ the format
discard = "unmap";
};
format = {
driver = "raw";
discard = "unmap";
};
frontend = "virtio-blk";
};
inherit (lib.my) vm;
installerDisk = {
name = "installer";
backend = {
driver = "file";
#filename = "${systems.installer.configuration.config.my.buildAs.iso}/iso/nixos-installer-devplayer0.iso";
#filename = "/persist/home/dev/nixos-installer-devplayer0.iso";
filename = "/persist/home/dev/debian-12.1.0-amd64-netinst.iso";
filename = "/persist/home/dev/nixos-installer-devplayer0-b4d0d9a.iso";
#filename = "/persist/home/dev/debian-12.1.0-amd64-netinst.iso";
# filename = "/persist/home/dev/ubuntu-22.04.3-live-server-amd64.iso";
read-only = "on";
};
format.driver = "raw";
@@ -114,9 +101,9 @@
};
};
drives = [ ] ++ (optionals (!config.my.build.isDevVM) [
(mkMerge [ (vmLVM "estuary" "esp") { frontendOpts.bootindex = 0; } ])
(vmLVM "estuary" "nix")
(vmLVM "estuary" "persist")
(mkMerge [ (vm.disk "estuary" "esp") { frontendOpts.bootindex = 0; } ])
(vm.disk "estuary" "nix")
(vm.disk "estuary" "persist")
]);
hostDevices = {
net-wan0 = {
@@ -133,39 +120,17 @@
cpus = 12;
threads = 2;
};
memory = 65536;
memory = 40960;
networks.vms.mac = "52:54:00:27:3d:5c";
cleanShutdown.timeout = 120;
drives = [ ] ++ (optionals (!config.my.build.isDevVM) [
(mkMerge [ (vmLVM "shill" "esp") { frontendOpts.bootindex = 0; } ])
(vmLVM "shill" "nix")
(vmLVM "shill" "persist")
{
name = "media";
backend = {
driver = "host_device";
filename = "/dev/main/media";
discard = "unmap";
};
format = {
driver = "raw";
discard = "unmap";
};
frontend = "virtio-blk";
}
{
name = "minio";
backend = {
driver = "host_device";
filename = "/dev/main/minio";
discard = "unmap";
};
format = {
driver = "raw";
discard = "unmap";
};
frontend = "virtio-blk";
}
(mkMerge [ (vm.disk "shill" "esp") { frontendOpts.bootindex = 0; } ])
(vm.disk "shill" "nix")
(vm.disk "shill" "persist")
(vm.lvmDisk "media")
(vm.lvmDisk "minio")
(vm.lvmDisk "nix-atticd")
]);
};
@@ -180,25 +145,35 @@
networks.vms.mac = "52:54:00:d5:d9:c6";
cleanShutdown.timeout = 120;
drives = [ ] ++ (optionals (!config.my.build.isDevVM) [
(mkMerge [ (vmLVM "whale2" "esp") { frontendOpts.bootindex = 0; } ])
(vmLVM "whale2" "nix")
(vmLVM "whale2" "persist")
{
name = "oci";
backend = {
driver = "host_device";
filename = "/dev/main/oci";
discard = "unmap";
};
format = {
driver = "raw";
discard = "unmap";
};
frontend = "virtio-blk";
}
(mkMerge [ (vm.disk "whale2" "esp") { frontendOpts.bootindex = 0; } ])
(vm.disk "whale2" "nix")
(vm.disk "whale2" "persist")
(vm.lvmDisk "oci")
]);
};
git = {
uuid = "c0659fdc-3356-4717-a6a1-5f289ef03c4a";
cpu = "host,topoext";
smp = {
cpus = 12;
threads = 2;
};
memory = 40960;
networks.vms.mac = "52:54:00:75:78:a8";
cleanShutdown.timeout = 120;
drives = [
(mkMerge [ (vm.disk "git" "esp") { frontendOpts.bootindex = 0; } ])
(vm.disk "git" "nix")
(vm.disk "git" "persist")
(vm.disk "git" "oci")
(vm.lvmDisk "git")
(vm.lvmDisk "gitea-actions-cache")
];
};
mail = {
uuid = "fd95fe0f-c204-4dd5-b16f-2b808e14a43a";
cpu = "host,topoext";
@@ -206,15 +181,34 @@
cpus = 3;
threads = 2;
};
memory = 8192;
memory = 6144;
networks.public = {
bridge = null;
mac = "52:54:00:a8:d1:03";
};
cleanShutdown.timeout = 120;
drives = [
(mkMerge [ (vmLVM "mail" "root") { frontendOpts.bootindex = 0; } ])
(vmLVM "mail" "data")
(mkMerge [ (vm.disk "mail" "root") { frontendOpts.bootindex = 0; } ])
(vm.disk "mail" "data")
];
};
darts = {
uuid = "ee3882a9-5616-4fcb-83d7-89eb41a84d28";
cpu = "host,topoext";
smp = {
cpus = 4;
threads = 2;
};
memory = 16384;
networks.public = {
bridge = null;
mac = "52:54:00:a8:29:cd";
};
cleanShutdown.timeout = 120;
drives = [
(mkMerge [ (vm.disk "darts" "root") { frontendOpts.bootindex = 0; } ])
(vm.lvmDisk' "media" "darts-media")
];
};
};

View File

@@ -14,22 +14,25 @@ in
config = ''
define OWNAS = 211024;
define CCVIP1 = ${lib.my.colony.prefixes.vip1};
define CCVIP2 = ${lib.my.colony.prefixes.vip2};
define CCVIP1 = ${lib.my.c.colony.prefixes.vip1};
define CCVIP2 = ${lib.my.c.colony.prefixes.vip2};
define CCVIP3 = ${lib.my.c.colony.prefixes.vip3};
define OWNIP4 = ${assignments.internal.ipv4.address};
define OWNNETSET4 = [ ${assignments.internal.ipv4.address}/32 ];
define CCNETSET4 = [ ${lib.my.colony.prefixes.vip1}, ${lib.my.colony.prefixes.vip2} ];
define CCNETSET4 = [ ${lib.my.c.colony.prefixes.vip1}, ${lib.my.c.colony.prefixes.vip2}, ${lib.my.c.colony.prefixes.vip3} ];
define INTNET6 = ${intnet6};
define AMSNET6 = ${amsnet6};
define HOMENET6 = ${homenet6};
define OWNIP6 = ${assignments.base.ipv6.address};
define OWNNETSET6 = [ ${intnet6}, ${amsnet6}, ${homenet6} ];
# we have issues with sending ICMPv6 too big back on the wrong interface right now...
define OWNNETSET6 = [ ${intnet6}, ${amsnet6} ];
define CCNETSET6 = [ ];
#define TRANSSET6 = [ ::1/128 ];
define DUB1IP6 = 2a0e:97c0:4df:0:2::1;
define DUB1IP6 = ${lib.my.c.home.vips.as211024.v6};
define PREFIXP = 110;
define PREFPEER = 120;
@@ -41,7 +44,7 @@ in
if net ~ OWNNETSET4 || net ~ OWNNETSET6 then accept; else reject;
}
filter bgp_export_cc {
if net ~ OWNNETSET4 || net ~ OWNNETSET6 || net ~ CCNETSET4 then accept; else reject;
if net ~ OWNNETSET4 || net ~ OWNNETSET6 || net ~ CCNETSET4 || net ~ CCNETSET6 then accept; else reject;
}
router id from "wan";
@@ -55,6 +58,7 @@ in
protocol static static4 {
route CCVIP1 via "base";
route CCVIP2 via "base";
route CCVIP3 via "base";
ipv4 {
import all;
@@ -186,10 +190,12 @@ in
protocol bgp upstream6_coloclue_eun2 from upstream_bgp6 {
description "ColoClue euNetworks 2 (IPv6)";
neighbor 2a02:898:0:20::e2 as 8283;
ipv6 { export filter bgp_export_cc; };
}
protocol bgp upstream6_coloclue_eun3 from upstream_bgp6 {
description "ColoClue euNetworks 3 (IPv6)";
neighbor 2a02:898:0:20::e1 as 8283;
ipv6 { export filter bgp_export_cc; };
}
protocol bgp upstream6_ifog from upstream_bgp6 {
@@ -202,14 +208,15 @@ in
neighbor 2001:7f8:10f::1b1b:154 as 6939;
}
protocol bgp upstream4_fogixp_efero from upstream_bgp4 {
description "efero transit (on FogIXP, IPv4)";
neighbor 185.1.147.107 as 208431;
}
protocol bgp upstream6_fogixp_efero from upstream_bgp6 {
description "efero transit (on FogIXP, IPv6)";
neighbor 2001:7f8:ca:1::107 as 208431;
}
# Not working so well lately...
# protocol bgp upstream4_fogixp_efero from upstream_bgp4 {
# description "efero transit (on FogIXP, IPv4)";
# neighbor 185.1.147.107 as 208431;
# }
# protocol bgp upstream6_fogixp_efero from upstream_bgp6 {
# description "efero transit (on FogIXP, IPv6)";
# neighbor 2001:7f8:ca:1::107 as 208431;
# }
protocol bgp peer4_cc_luje from peer_bgp4 {
description "LUJE.net (on ColoClue, IPv4)";

View File

@@ -1,9 +1,8 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
pubV4 = "94.142.240.44";
inherit (builtins) elemAt;
inherit (lib.my) net mkVLAN;
inherit (lib.my.c.colony) pubV4 domain prefixes firewallForwards;
in
{
nixos = {
@@ -11,9 +10,12 @@ in
l2 = {
as211024 = {
vni = 211024;
security.enable = true;
peers = {
estuary.addr = pubV4;
home.addr = "188.141.75.2";
river.addr = elemAt lib.my.c.home.routersPubV4 0;
stream.addr = elemAt lib.my.c.home.routersPubV4 1;
britway.addr = lib.my.c.britway.pubV4;
};
};
};
@@ -53,10 +55,10 @@ in
};
as211024 = {
ipv4 = {
address = "10.255.3.1";
address = net.cidr.host 1 prefixes.as211024.v4;
gateway = null;
};
ipv6.address = "2a0e:97c0:4df:0:3::1";
ipv6.address = net.cidr.host 1 prefixes.as211024.v6;
};
};
@@ -64,6 +66,7 @@ in
let
inherit (lib) flatten mkIf mkMerge mkForce;
inherit (lib.my) networkdAssignment;
inherit (lib.my.c) networkd;
in
{
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ./dns.nix ./bgp.nix ];
@@ -90,12 +93,13 @@ in
environment = {
systemPackages = with pkgs; [
ethtool
conntrack-tools
wireguard-tools
];
};
services = {
fstrim = lib.my.colony.fstrimConfig;
fstrim = lib.my.c.colony.fstrimConfig;
lvm = {
dmeventd.enable = true;
};
@@ -114,34 +118,19 @@ in
};
systemd = {
services = {
# Use this as a way to make sure the router always knows we're here (NDP seems kindy funky)
ipv6-neigh-keepalive =
let
waitOnline = "systemd-networkd-wait-online@wan.service";
in
{
description = "Frequent ICMP6 neighbour solicitations";
enable = false;
requires = [ waitOnline ];
after = [ waitOnline ];
script = ''
while true; do
${pkgs.ndisc6}/bin/ndisc6 ${assignments.internal.ipv6.gateway} wan
sleep 10
done
'';
wantedBy = [ "multi-user.target" ];
};
bird2 =
let
waitOnline = "systemd-networkd-wait-online@wan.service";
in
{
services =
let
waitOnline = "systemd-networkd-wait-online@wan.service";
in
{
bird2 = {
after = [ waitOnline ];
# requires = [ waitOnline ];
};
ipsec = {
after = [ waitOnline ];
requires = [ waitOnline ];
};
};
};
@@ -154,19 +143,7 @@ in
};
};
netdevs =
let
mkVLAN = name: vid: {
"25-${name}" = {
netdevConfig = {
Name = name;
Kind = "vlan";
};
vlanConfig.Id = vid;
};
};
in
mkMerge [
netdevs = mkMerge [
(mkVLAN "ifog" 409)
(mkVLAN "frys-ix" 701)
@@ -182,7 +159,7 @@ in
};
wireguardConfig = {
PrivateKeyFile = config.age.secrets."estuary/kelder-wg.key".path;
ListenPort = lib.my.kelder.vpn.port;
ListenPort = lib.my.c.kelder.vpn.port;
};
wireguardPeers = [
{
@@ -271,13 +248,7 @@ in
Kind = "vlan";
};
vlan = [ "frys-ix" "nl-ix" "fogixp" "ifog-transit" ];
networkConfig = {
LinkLocalAddressing = "no";
DHCP = "no";
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
};
networkConfig = networkd.noL3;
};
"85-ifog-transit" = {
matchConfig.Name = "ifog-transit";
@@ -306,41 +277,63 @@ in
};
ipv6Prefixes = [
{
ipv6PrefixConfig.Prefix = lib.my.colony.prefixes.base.v6;
ipv6PrefixConfig.Prefix = prefixes.base.v6;
}
];
routes = map (r: { routeConfig = r; }) (flatten
([
{
Destination = lib.my.colony.prefixes.vip1;
Destination = prefixes.vip1;
Gateway = allAssignments.colony.routing.ipv4.address;
}
{
Destination = lib.my.colony.prefixes.cust.v6;
Destination = prefixes.vip3;
Gateway = allAssignments.colony.routing.ipv4.address;
}
{
Destination = prefixes.darts.v4;
Gateway = allAssignments.colony.routing.ipv4.address;
}
{
Destination = prefixes.cust.v6;
Gateway = allAssignments.colony.internal.ipv6.address;
}
{
Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.colony.routing.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.colony.internal.ipv6.address;
}
] ++
(map (pName: [
{
Gateway = allAssignments.colony.routing.ipv4.address;
Destination = lib.my.colony.prefixes."${pName}".v4;
Destination = prefixes."${pName}".v4;
}
{
Destination = lib.my.colony.prefixes."${pName}".v6;
Destination = prefixes."${pName}".v6;
Gateway = allAssignments.colony.internal.ipv6.address;
}
]) [ "vms" "ctrs" "oci" ])));
}
];
"90-l2mesh-as211024" = {
matchConfig.Name = "as211024";
address = with assignments.as211024; [
(with ipv4; "${address}/${toString mask}")
(with ipv6; "${address}/${toString mask}")
];
networkConfig.IPv6AcceptRA = false;
};
"90-l2mesh-as211024" = mkMerge [
(networkdAssignment "as211024" assignments.as211024)
{
matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false;
routes = map (r: { routeConfig = r; }) [
{
Destination = lib.my.c.home.prefixes.all.v4;
Gateway = lib.my.c.home.vips.as211024.v4;
}
];
}
];
"95-kelder" = {
matchConfig.Name = "kelder";
routes = [
@@ -356,50 +349,29 @@ in
};
my = {
#deploy.generate.system.mode = "boot";
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF9up7pXu6M/OWCKufTOfSiGcxMUk4VqUe7fLuatNFFA";
files = {
"estuary/kelder-wg.key" = {
owner = "systemd-network";
};
"l2mesh/as211024.key" = {};
};
};
server.enable = true;
vpns = {
l2.pskFiles = {
as211024 = config.age.secrets."l2mesh/as211024.key".path;
};
};
firewall = {
trustedInterfaces = [ "as211024" ];
udp.allowed = [ 5353 lib.my.kelder.vpn.port ];
udp.allowed = [ 5353 lib.my.c.kelder.vpn.port ];
tcp.allowed = [ 5353 "bgp" ];
nat = {
enable = true;
externalInterface = "wan";
externalIP = assignments.internal.ipv4.address;
forwardPorts = [
{
port = "http";
dst = allAssignments.middleman.internal.ipv4.address;
}
{
port = "https";
dst = allAssignments.middleman.internal.ipv4.address;
}
{
port = 8448;
dst = allAssignments.middleman.internal.ipv4.address;
}
{
port = 2456;
dst = allAssignments.valheim-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 2457;
dst = allAssignments.valheim-oci.internal.ipv4.address;
proto = "udp";
}
];
forwardPorts."${assignments.internal.ipv4.address}" = firewallForwards allAssignments;
};
extraRules =
let
@@ -421,16 +393,20 @@ in
# Safe enough to allow all SSH
tcp dport ssh accept
${matchInet "tcp dport { http, https, 8448 } accept" "middleman"}
${matchInet "udp dport { 2456-2457 } accept" "valheim-oci"}
ip6 daddr ${aa.middleman.internal.ipv6.address} tcp dport { http, https, 8448 } accept
${matchInet "tcp dport { http, https } accept" "git"}
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} tcp dport { 25565, 25575 } accept
ip6 daddr ${aa.simpcraft-staging-oci.internal.ipv6.address} tcp dport 25565 accept
return
}
chain routing-udp {
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.simpcraft-oci.internal.ipv6.address} udp dport 25565 accept
return
}
chain filter-routing {
ip daddr ${prefixes.mail.v4} accept
ip daddr { ${prefixes.mail.v4}, ${prefixes.darts.v4} } accept
ip6 daddr ${prefixes.cust.v6} accept
tcp flags & (fin|syn|rst|ack) == syn ct state new jump routing-tcp
@@ -443,9 +419,10 @@ in
}
chain forward {
iifname { wan, $ixps } oifname base jump filter-routing
${lib.my.c.as211024.nftTrust}
iifname { wan, as211024, $ixps } oifname base jump filter-routing
oifname $ixps jump ixp
iifname base oifname { wan, $ixps } accept
iifname base oifname { base, wan, $ixps } accept
oifname { as211024, kelder } accept
}
chain output {
@@ -458,7 +435,7 @@ in
${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"}
}
chain postrouting {
ip saddr ${lib.my.colony.prefixes.all.v4} snat to ${assignments.internal.ipv4.address}
ip saddr ${prefixes.all.v4} oifname != as211024 snat to ${assignments.internal.ipv4.address}
}
}
'';

View File

@@ -1,6 +1,8 @@
{ lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (builtins) attrNames;
inherit (lib.my) net;
inherit (lib.my.c.colony) prefixes;
authZones = attrNames config.my.pdns.auth.bind.zones;
in
@@ -49,8 +51,8 @@ in
];
allowFrom = [
"127.0.0.0/8" "::1/128"
lib.my.colony.prefixes.all.v4 lib.my.colony.prefixes.all.v6
];
prefixes.all.v4 prefixes.all.v6
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
};
settings = {
@@ -143,12 +145,20 @@ in
http IN A ${assignments.internal.ipv4.address}
http IN AAAA ${allAssignments.middleman.internal.ipv6.address}
librespeed IN CNAME http.${config.networking.domain}.
valheim IN A ${assignments.internal.ipv4.address}
valheim IN AAAA ${allAssignments.valheim-oci.internal.ipv6.address}
simpcraft IN A ${assignments.internal.ipv4.address}
simpcraft IN AAAA ${allAssignments.simpcraft-oci.internal.ipv6.address}
simpcraft-staging IN A ${assignments.internal.ipv4.address}
simpcraft-staging IN AAAA ${allAssignments.simpcraft-staging-oci.internal.ipv6.address}
mail-vm IN A ${lib.my.net.cidr.host 0 lib.my.colony.prefixes.mail.v4}
mail-vm IN AAAA ${lib.my.net.cidr.host 1 lib.my.colony.prefixes.mail.v6}
mail-vm IN A ${net.cidr.host 0 prefixes.mail.v4}
mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6}
darts-cust IN A ${net.cidr.host 0 prefixes.darts.v4}
darts-cust IN AAAA ${net.cidr.host 1 prefixes.darts.v6}
andrey-cust IN A ${allAssignments.kelder.estuary.ipv4.address}
@@ -200,6 +210,8 @@ in
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2 IN PTR mail.nul.ie.
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2 IN PTR darts-cust.${config.networking.domain}.
${lib.my.dns.ptr6Records {
inherit allAssignments names;
domain = config.networking.domain;

View File

@@ -0,0 +1,241 @@
{ lib, ... }:
let
inherit (builtins) mapAttrs;
inherit (lib) mkMerge mkDefault;
inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes;
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
in
{
nixos.systems.git = {
system = "x86_64-linux";
nixpkgs = "mine";
assignments = {
routing = {
name = "git-vm-routing";
inherit domain;
ipv4.address = net.cidr.host 4 prefixes.vms.v4;
};
internal = {
name = "git-vm";
inherit domain;
ipv4 = {
address = net.cidr.host 0 prefixes.vip3;
mask = 32;
gateway = null;
genPTR = false;
};
ipv6 = {
iid = "::4";
address = net.cidr.host 4 prefixes.vms.v6;
};
};
};
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge;
inherit (lib.my) networkdAssignment;
in
{
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
./gitea.nix
./gitea-actions.nix
];
config = mkMerge [
{
boot = {
kernelParams = [ "console=ttyS0,115200n8" ];
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-label/ESP";
fsType = "vfat";
};
"/nix" = {
device = "/dev/disk/by-label/nix";
fsType = "ext4";
};
"/persist" = {
device = "/dev/disk/by-label/persist";
fsType = "ext4";
neededForBoot = true;
};
"/var/lib/containers" = {
device = "/dev/disk/by-label/oci";
fsType = "xfs";
options = [ "pquota" ];
};
};
users = {
users = {
nginx.extraGroups = [ "acme" ];
};
};
security.acme = {
acceptTerms = true;
defaults = {
email = "dev@nul.ie";
server = "https://acme-v02.api.letsencrypt.org/directory";
reloadServices = [ "nginx" ];
dnsResolver = "8.8.8.8";
};
certs = {
"${pubDomain}" = {
extraDomainNames = [
"*.${pubDomain}"
];
dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
};
};
};
services = {
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;
nginx = {
enable = true;
enableReload = true;
logError = "stderr info";
recommendedTlsSettings = true;
clientMaxBodySize = "0";
serverTokens = true;
sslDhparam = config.age.secrets."dhparams.pem".path;
# Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = ''
${baseHttpConfig}
# caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=512m;
'';
virtualHosts =
let
hosts = {
"_" = {
default = true;
forceSSL = true;
onlySSL = false;
locations = {
"/".root = "${pkgs.nginx}/html";
};
};
"git.${pubDomain}" = {
locations."/".proxyPass = "http://localhost:3000";
};
};
defaultsFor = mapAttrs (n: _: {
onlySSL = mkDefault true;
useACMEHost = mkDefault pubDomain;
kTLS = mkDefault true;
http2 = mkDefault true;
});
in
mkMerge [
hosts
(defaultsFor hosts)
];
};
};
virtualisation = {
podman = {
enable = true;
};
oci-containers = {
backend = "podman";
};
containers.containersConf.settings.network.default_subnet = "10.88.0.0/16";
};
systemd.network = {
links = {
"10-vms" = {
matchConfig.MACAddress = "52:54:00:75:78:a8";
linkConfig.Name = "vms";
};
};
networks = {
"80-vms" = mkMerge [
(networkdAssignment "vms" assignments.routing)
(networkdAssignment "vms" assignments.internal)
];
};
};
my = {
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP+KINpHLMduBuW96JzfSRDLUzkI+XaCBghu5/wHiW5R";
files = {
"dhparams.pem" = {
owner = "acme";
group = "acme";
mode = "440";
};
"middleman/cloudflare-credentials.conf" = {
owner = "acme";
group = "acme";
};
};
};
server.enable = true;
firewall = {
tcp.allowed = [ 19999 "http" "https" ];
extraRules = ''
table inet filter {
chain forward {
ip saddr 10.88.0.0/16 accept
}
}
'';
};
};
}
];
};
};
}

View File

@@ -0,0 +1,78 @@
{ lib, pkgs, config, ... }:
let
inherit (builtins) toJSON;
inherit (lib) mkForce;
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
{
config = {
fileSystems = {
"/var/cache/gitea-runner" = {
device = "/dev/disk/by-label/actions-cache";
fsType = "ext4";
};
};
services = {
gitea-actions-runner.instances = {
main = {
enable = true;
name = "main-docker";
labels = [
"debian-node-bullseye:docker://node:18-bullseye"
"ubuntu-22.04:docker://git.nul.ie/dev/actions-ubuntu:22.04"
];
url = "https://git.${pubDomain}";
tokenFile = config.age.secrets."gitea/actions-runner.env".path;
};
};
};
users = with lib.my.c.ids; {
users = {
gitea-runner = {
isSystemUser = true;
uid = uids.gitea-runner;
group = "gitea-runner";
home = "/var/lib/gitea-runner";
};
};
groups = {
gitea-runner.gid = gids.gitea-runner;
};
};
systemd = {
services = {
gitea-runner-main.serviceConfig = {
# Needs to be able to read its secrets
CacheDirectory = "gitea-runner";
DynamicUser = mkForce false;
User = "gitea-runner";
Group = "gitea-runner";
ExecStart = mkForce "${config.services.gitea-actions-runner.package}/bin/act_runner -c ${cfgFile} daemon";
};
};
};
my = {
secrets.files = {
"gitea/actions-runner.env" = {
owner = "gitea-runner";
group = "gitea-runner";
};
};
};
};
}

View File

@@ -0,0 +1,134 @@
{ lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) prefixes;
in
{
config = {
fileSystems = {
"/var/lib/gitea" = {
device = "/dev/disk/by-label/git";
fsType = "ext4";
};
};
users = {
users.git = {
description = "Gitea Service";
home = config.services.gitea.stateDir;
useDefaultShell = true;
group = config.services.gitea.group;
isSystemUser = true;
};
groups.git = {};
};
systemd = {
services = {
gitea = mkMerge [
(lib.my.systemdAwaitPostgres pkgs.postgresql "colony-psql")
{
preStart =
let
repSec = "${pkgs.replace-secret}/bin/replace-secret";
confPath = "${config.services.gitea.customDir}/conf/app.ini";
in
''
gitea_extra_setup() {
chmod u+w '${confPath}'
${repSec} '#miniosecret#' '${config.age.secrets."gitea/minio.txt".path}' '${confPath}'
chmod u-w '${confPath}'
}
(umask 027; gitea_extra_setup)
'';
}
];
};
};
services = {
gitea = {
enable = true;
user = "git";
group = "git";
appName = "/dev/player0 git";
stateDir = "/var/lib/gitea";
lfs.enable = true;
database = {
type = "postgres";
createDatabase = false;
host = "colony-psql";
user = "gitea";
passwordFile = config.age.secrets."gitea/db.txt".path;
};
mailerPasswordFile = config.age.secrets."gitea/mail.txt".path;
settings = {
server = {
DOMAIN = "git.${pubDomain}";
HTTP_ADDR = "::";
ROOT_URL = "https://git.${pubDomain}";
};
service = {
DISABLE_REGISTRATION = true;
ENABLE_NOTIFY_MAIL = true;
};
session = {
COOKIE_SECURE = true;
};
repository = {
DEFAULT_BRANCH = "master";
};
mailer = {
ENABLED = true;
PROTOCOL = "smtp+starttls";
SMTP_ADDR = "mail.nul.ie";
SMTP_PORT = 587;
USER = "git@nul.ie";
FROM = "Gitea <git@nul.ie>";
};
"email.incoming" = {
ENABLED = true;
HOST = "mail.nul.ie";
PORT = 993;
USE_TLS = true;
USERNAME = "git@nul.ie";
PASSWORD = "#mailerpass#";
REPLY_TO_ADDRESS = "git+%{token}@nul.ie";
};
storage = {
STORAGE_TYPE = "minio";
SERVE_DIRECT = true;
MINIO_ENDPOINT = "s3.${pubDomain}";
MINIO_ACCESS_KEY_ID = "gitea";
MINIO_SECRET_ACCESS_KEY = "#miniosecret#";
MINIO_BUCKET = "gitea";
MINIO_LOCATION = "eu-central-1";
MINIO_USE_SSL = true;
};
actions = {
ENABLED = true;
};
};
};
};
my = {
secrets = {
files =
let
ownedByGit = {
owner = "git";
group = "git";
};
in
{
"gitea/db.txt" = ownedByGit;
"gitea/mail.txt" = ownedByGit;
"gitea/minio.txt" = ownedByGit;
};
};
};
};
}

View File

@@ -1,12 +1,14 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.chatterbox = {
nixos.systems.chatterbox = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -22,7 +24,7 @@ in
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge mkIf;
inherit (lib) mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment;
in
{
@@ -43,16 +45,41 @@ in
owner = "matrix-synapse";
group = "matrix-synapse";
};
"chatterbox/syncv3.env" = {
owner = "matrix-syncv3";
group = "matrix-syncv3";
};
};
};
firewall = {
tcp.allowed = [ 19999 8008 ];
tcp.allowed = [ 19999 8008 8009 ];
};
};
users = with lib.my.c.ids; {
users = {
matrix-syncv3 = {
isSystemUser = true;
uid = uids.matrix-syncv3;
group = "matrix-syncv3";
};
};
groups = {
matrix-syncv3.gid = gids.matrix-syncv3;
};
};
systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
services = {
matrix-sliding-sync.serviceConfig = {
# Needs to be able to read its secrets
DynamicUser = mkForce false;
User = "matrix-syncv3";
Group = "matrix-syncv3";
};
};
};
services = {
@@ -68,6 +95,7 @@ in
settings = {
server_name = "nul.ie";
public_baseurl = "https://matrix.nul.ie";
web_client_location = "https://element.${pubDomain}";
admin_contact = "dev@nul.ie";
prescence.enabled = true;
@@ -128,7 +156,7 @@ in
"2001:db8::/32"
"ff00::/8"
"fec0::/10"
] ++ (with lib.my.colony.prefixes; [ all.v4 all.v6 ]);
] ++ (with lib.my.c.colony.prefixes; [ all.v4 all.v6 ]);
url_preview_ip_range_whitelist =
with allAssignments.middleman.internal;
[ ipv4.address ipv6.address ];
@@ -142,6 +170,16 @@ in
"/var/lib/heisenbridge/registration.yml"
];
};
sliding-sync = {
enable = true;
createDatabase = false;
environmentFile = config.age.secrets."chatterbox/syncv3.env".path;
settings = {
SYNCV3_BINDADDR = "[::]:8009";
SYNCV3_SERVER = "http://localhost:8008";
};
};
};
heisenbridge = {

View File

@@ -1,12 +1,13 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.colony-psql = {
nixos.systems.colony-psql = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -66,7 +67,7 @@ in
enable = true;
enableTCPIP = true;
authentication = with lib.my.colony.prefixes; ''
authentication = with lib.my.c.colony.prefixes; ''
local all postgres peer map=local
host all all ${all.v4} md5

View File

@@ -7,5 +7,6 @@
./jackflix
./object.nix
./toot.nix
./waffletail.nix
];
}

View File

@@ -1,12 +1,13 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.jackflix = {
nixos.systems.jackflix = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -22,7 +23,7 @@ in
configuration = { lib, pkgs, config, ... }:
let
inherit (lib);
inherit (lib) mkForce;
in
{
imports = [ ./networking.nix ];
@@ -37,14 +38,22 @@ in
};
};
users = {
groups.media.gid = 2000;
users = with lib.my.c.ids; {
users = {
"${config.my.user.config.name}".extraGroups = [ "media" ];
transmission.extraGroups = [ "media" ];
radarr.extraGroups = [ "media" ];
sonarr.extraGroups = [ "media" ];
jellyseerr = {
isSystemUser = true;
uid = uids.jellyseerr;
group = "jellyseerr";
};
};
groups = {
media.gid = 2000;
jellyseerr.gid = gids.jellyseerr;
};
};
@@ -55,6 +64,18 @@ in
radarr.serviceConfig.UMask = "0002";
sonarr.serviceConfig.UMask = "0002";
jellyseerr.serviceConfig = {
# Needs to be able to read its secrets
DynamicUser = mkForce false;
User = "jellyseerr";
Group = "jellyseerr";
};
# https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
transmission.serviceConfig = {
RootDirectoryStartOnly = lib.mkForce false;
RootDirectory = lib.mkForce "";
};
};
};
@@ -90,6 +111,10 @@ in
jackett.enable = true;
radarr.enable = true;
sonarr.enable = true;
jellyseerr = {
enable = true;
openFirewall = true;
};
jellyfin.enable = true;
};

View File

@@ -2,6 +2,7 @@
let
inherit (lib) mkMerge mkIf;
inherit (lib.my) networkdAssignment;
inherit (lib.my.c.colony) prefixes;
wg = {
keyFile = "jackflix/airvpn-privkey";
@@ -102,23 +103,23 @@ in
}
{
From = lib.my.colony.prefixes.all.v4;
From = prefixes.all.v4;
Table = "main";
Priority = 100;
}
{
To = lib.my.colony.prefixes.all.v4;
To = prefixes.all.v4;
Table = "main";
Priority = 100;
}
{
From = lib.my.colony.prefixes.all.v6;
From = prefixes.all.v6;
Table = "main";
Priority = 100;
}
{
To = lib.my.colony.prefixes.all.v6;
To = prefixes.all.v6;
Table = "main";
Priority = 100;
}

View File

@@ -1,12 +1,15 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.nginx) baseHttpConfig;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.middleman = {
nixos.systems.middleman = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -63,6 +66,7 @@ in
owner = "nginx";
group = "nginx";
};
"librespeed.toml" = { };
};
};
@@ -79,8 +83,8 @@ in
port = 8082;
};
login = {
title = "${lib.my.pubDomain} login";
default_redirect = "https://${lib.my.pubDomain}";
title = "${pubDomain} login";
default_redirect = "https://${pubDomain}";
default_method = "google_oauth";
names = {
google_oauth = "Google account";
@@ -88,7 +92,7 @@ in
};
};
cookie = {
domain = ".${lib.my.pubDomain}";
domain = ".${pubDomain}";
secure = true;
};
audit_log = {
@@ -109,14 +113,27 @@ in
};
google_oauth = {
client_id = "545475967061-cag4g1qf0pk33g3pdbom4v69562vboc8.apps.googleusercontent.com";
redirect_url = "https://sso.${lib.my.pubDomain}/login";
redirect_url = "https://sso.${pubDomain}/login";
user_id_method = "user-id";
};
};
};
includes = {
endpoint = "http://localhost:8082";
baseURL = "https://sso.${lib.my.pubDomain}";
baseURL = "https://sso.${pubDomain}";
};
};
librespeed = {
frontend.servers = [
{
name = "Amsterdam, Netherlands";
server = "//librespeed.${domain}";
}
];
backend = {
enable = true;
extraSettingsFile = config.age.secrets."librespeed.toml".path;
};
};
};
@@ -129,6 +146,10 @@ in
systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
services = {
# HACK: nginx seems to get stuck not being able to DNS early...
nginx = lib.my.systemdAwaitPostgres pkgs.postgresql "colony-psql";
};
};
security = {
@@ -173,10 +194,10 @@ in
EXEC_PATH=${script}
'';
};
"${lib.my.pubDomain}" = {
"${pubDomain}" = {
extraDomainNames = [
"*.${lib.my.pubDomain}"
"*.s3.${lib.my.pubDomain}"
"*.${pubDomain}"
"*.s3.${pubDomain}"
];
dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
@@ -194,7 +215,7 @@ in
};
programs = {
ssh.knownHostsFiles = [ lib.my.sshHostKeys.mail-vm ];
ssh.knownHostsFiles = [ lib.my.c.sshHostKeys.mail-vm ];
};
services = {
@@ -229,43 +250,9 @@ in
# Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = ''
# NixOS provides a logrotate config that auto-compresses :)
log_format main
'$remote_addr - $remote_user [$time_local] $scheme "$host" "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
${baseHttpConfig}
# optimisation
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# gzip
gzip on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types
application/atom+xml
application/javascript
application/json
application/xml
application/xml+rss
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
gzip_vary on;
# proxying
proxy_buffering off;
proxy_redirect off;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_http_version 1.1;
${lib.my.nginx.proxyHeaders}
resolver_timeout 5s;
# caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=4g;

View File

@@ -2,6 +2,9 @@
let
inherit (builtins) mapAttrs toJSON;
inherit (lib) mkMerge mkDefault genAttrs flatten concatStringsSep;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.nginx) proxyHeaders;
inherit (config.networking) domain;
dualStackListen' = l: map (addr: l // { inherit addr; }) [ "0.0.0.0" "[::]" ];
dualStackListen = ll: flatten (map dualStackListen' ll);
@@ -32,6 +35,7 @@ let
# For clients
(mkWellKnown "matrix/client" (toJSON {
"m.homeserver".base_url = "https://matrix.nul.ie";
"org.matrix.msc3575.proxy".url = "https://matrix-syncv3.nul.ie";
}))
];
};
@@ -80,7 +84,7 @@ in
}
wellKnown
];
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"localhost" = {
forceSSL = false;
@@ -98,12 +102,12 @@ in
};
};
"sso.${lib.my.pubDomain}" = {
"sso.${pubDomain}" = {
locations."/".proxyPass = config.my.nginx-sso.includes.endpoint;
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"netdata-colony.${lib.my.pubDomain}" =
"netdata-colony.${pubDomain}" =
let
hosts = [
"vm"
@@ -119,10 +123,10 @@ in
"~ /(?<behost>${matchHosts})$".return = "301 https://$host/$behost/";
"~ /(?<behost>${matchHosts})/(?<ndpath>.*)" = mkMerge [
{
proxyPass = "http://$behost.${config.networking.domain}:19999/$ndpath$is_args$args";
proxyPass = "http://$behost.${domain}:19999/$ndpath$is_args$args";
extraConfig = ''
proxy_pass_request_headers on;
${lib.my.nginx.proxyHeaders}
${proxyHeaders}
proxy_set_header Connection "keep-alive";
proxy_store off;
@@ -134,14 +138,14 @@ in
(ssoLoc "generic")
];
};
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
}
(ssoServer "generic")
];
"pass.${lib.my.pubDomain}" =
"pass.${pubDomain}" =
let
upstream = "http://vaultwarden-ctr.${config.networking.domain}";
upstream = "http://vaultwarden-ctr.${domain}";
in
{
locations = {
@@ -149,11 +153,11 @@ in
"/notifications/hub" = {
proxyPass = upstream;
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
};
"/notifications/hub/negotiate".proxyPass = upstream;
};
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"matrix.nul.ie" = {
@@ -171,15 +175,19 @@ in
];
locations = mkMerge [
{
"/".proxyPass = "http://chatterbox-ctr.${config.networking.domain}:8008";
"= /".return = "301 https://element.${lib.my.pubDomain}";
"/".proxyPass = "http://chatterbox-ctr.${domain}:8008";
"= /".return = "301 https://element.${pubDomain}";
}
wellKnown
];
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"matrix-syncv3.${pubDomain}" = {
locations."/".proxyPass = "http://chatterbox-ctr.${domain}:8009";
useACMEHost = pubDomain;
};
"element.${lib.my.pubDomain}" =
"element.${pubDomain}" =
let
headers = ''
# TODO: why are these here?
@@ -224,66 +232,70 @@ in
'';
}))
];
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"torrents.${lib.my.pubDomain}" = mkMerge [
"torrents.${pubDomain}" = mkMerge [
{
locations."/" = mkMerge [
{
proxyPass = "http://jackflix-ctr.${config.networking.domain}:9091";
proxyPass = "http://jackflix-ctr.${domain}:9091";
}
(ssoLoc "generic")
];
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
}
(ssoServer "generic")
];
"jackett.${lib.my.pubDomain}" = mkMerge [
"jackett.${pubDomain}" = mkMerge [
{
locations."/" = mkMerge [
{
proxyPass = "http://jackflix-ctr.${config.networking.domain}:9117";
proxyPass = "http://jackflix-ctr.${domain}:9117";
}
(ssoLoc "generic")
];
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
}
(ssoServer "generic")
];
"radarr.${lib.my.pubDomain}" = mkMerge [
"radarr.${pubDomain}" = mkMerge [
{
locations."/" = mkMerge [
{
proxyPass = "http://jackflix-ctr.${config.networking.domain}:7878";
proxyPass = "http://jackflix-ctr.${domain}:7878";
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
}
(ssoLoc "generic")
];
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
}
(ssoServer "generic")
];
"sonarr.${lib.my.pubDomain}" = mkMerge [
"sonarr.${pubDomain}" = mkMerge [
{
locations."/" = mkMerge [
{
proxyPass = "http://jackflix-ctr.${config.networking.domain}:8989";
proxyPass = "http://jackflix-ctr.${domain}:8989";
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
}
(ssoLoc "generic")
];
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
}
(ssoServer "generic")
];
"gib.${pubDomain}" = {
locations."/".proxyPass = "http://jackflix-ctr.${domain}:5055";
useACMEHost = pubDomain;
};
"jackflix.${lib.my.pubDomain}" =
"jackflix.${pubDomain}" =
let
upstream = "http://jackflix-ctr.${config.networking.domain}:8096";
upstream = "http://jackflix-ctr.${domain}:8096";
in
{
extraConfig = ''
@@ -300,78 +312,31 @@ in
"/socket" = {
proxyPass = upstream;
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
};
};
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"toot.nul.ie" =
let
mkAssetLoc = name: {
tryFiles = "$uri =404";
extraConfig = ''
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
'';
};
in
{
root = "${pkgs.mastodon}/public";
locations = mkMerge [
(genAttrs [
"= /sw.js"
"~ ^/assets/"
"~ ^/avatars/"
"~ ^/emoji/"
"~ ^/headers/"
"~ ^/packs/"
"~ ^/shortcuts/"
"~ ^/sounds/"
] mkAssetLoc)
{
"/".tryFiles = "$uri @proxy";
"^~ /api/v1/streaming" = {
proxyPass = "http://toot-ctr.${config.networking.domain}:55000";
proxyWebsockets = true;
extraConfig = ''
${lib.my.nginx.proxyHeaders}
proxy_set_header Proxy "";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
'';
};
"@proxy" = {
proxyPass = "http://toot-ctr.${config.networking.domain}:55001";
proxyWebsockets = true;
extraConfig = ''
${lib.my.nginx.proxyHeaders}
proxy_set_header Proxy "";
proxy_pass_header Server;
proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;
'';
};
}
];
useACMEHost = lib.my.pubDomain;
};
"share.${lib.my.pubDomain}" = {
"toot.nul.ie" = {
locations."/" = {
proxyPass = "http://object-ctr.${config.networking.domain}:9090";
proxyPass = "http://toot-ctr.${domain}:80";
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
};
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"stuff.${lib.my.pubDomain}" = {
"share.${pubDomain}" = {
locations."/" = {
proxyPass = "http://object-ctr.${domain}:9090";
proxyWebsockets = true;
extraConfig = proxyHeaders;
};
useACMEHost = pubDomain;
};
"stuff.${pubDomain}" = {
locations."/" = {
basicAuthFile = config.age.secrets."middleman/htpasswd".path;
root = "/mnt/media/stuff";
@@ -380,13 +345,50 @@ in
fancyindex_show_dotfiles on;
'';
};
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"public.${pubDomain}" = {
serverAliases = [ "p.${pubDomain}" ];
locations."/" = {
root = "/mnt/media/public";
extraConfig = ''
fancyindex on;
fancyindex_show_dotfiles on;
'';
};
useACMEHost = pubDomain;
};
"mc-map.${pubDomain}" = {
locations."/".proxyPass = "http://simpcraft-oci.${domain}:8100";
useACMEHost = pubDomain;
};
"mc-rail.${pubDomain}" = {
locations."/".proxyPass = "http://simpcraft-staging-oci.${domain}:3876";
useACMEHost = pubDomain;
};
"librespeed.${domain}" = {
locations."/".proxyPass = "http://localhost:8989";
};
"speed.${pubDomain}" = {
locations."/".proxyPass = "http://localhost:8989";
useACMEHost = pubDomain;
};
"md.${pubDomain}" = {
locations."/" = {
proxyPass = "http://object-ctr.${domain}:3000";
proxyWebsockets = true;
extraConfig = proxyHeaders;
};
useACMEHost = pubDomain;
};
};
minio =
let
host = "object-ctr.${config.networking.domain}";
host = "object-ctr.${domain}";
s3Upstream = "http://${host}:9000";
extraConfig = ''
chunked_transfer_encoding off;
@@ -395,13 +397,12 @@ in
nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar\.\S+)$'';
nixCacheHeaders = ''
proxy_hide_header "X-Amz-Request-Id";
add_header Cache-Control $nix_cache_control;
add_header Expires $nix_expires;
'';
in
{
"minio.${lib.my.pubDomain}" = {
"minio.${pubDomain}" = {
inherit extraConfig;
locations = {
"/" = {
@@ -410,38 +411,48 @@ in
"/ws" = {
proxyPass = "http://${host}:9001";
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
};
};
useACMEHost = lib.my.pubDomain;
useACMEHost = pubDomain;
};
"s3.${lib.my.pubDomain}" = {
serverAliases = [ "*.s3.${lib.my.pubDomain}" ];
"s3.${pubDomain}" = {
serverAliases = [ "*.s3.${pubDomain}" ];
inherit extraConfig;
locations."/".proxyPass = s3Upstream;
useACMEHost = lib.my.pubDomain;
};
"nix-cache.${lib.my.pubDomain}" = {
extraConfig = ''
${extraConfig}
proxy_set_header Host "nix-cache.s3.nul.ie";
'';
locations = {
"/".proxyPass = s3Upstream;
"~ ${nixCacheableRegex}" = {
"/gitea/packages/" = {
proxyPass = s3Upstream;
# HACK: Docker images need the MIME type to be correct for the manifest but Gitea
# doesn't tell S3... By hiding the header we can use add_header to set Content-Type
# (normally can't be set directly)
extraConfig = ''
proxy_hide_header Content-Type;
add_header Content-Type $upstream_http_content_type always;
if ($args ~ "response-content-disposition=.+filename%3D%22manifest\.json%22") {
add_header Content-Type "application/vnd.docker.distribution.manifest.v2+json";
}
'';
};
};
useACMEHost = pubDomain;
};
"nix-cache.${pubDomain}" = {
locations = {
"/".proxyPass = "http://${host}:8069";
"~ ${nixCacheableRegex}" = {
proxyPass = "http://${host}:8069";
extraConfig = nixCacheHeaders;
};
};
useACMEHost = lib.my.pubDomain;
onlySSL = false;
useACMEHost = pubDomain;
};
};
defaultsFor = mapAttrs (n: _: {
onlySSL = mkDefault true;
useACMEHost = mkDefault "${config.networking.domain}";
useACMEHost = mkDefault "${domain}";
kTLS = mkDefault true;
http2 = mkDefault true;
});

View File

@@ -1,12 +1,14 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.object = {
nixos.systems.object = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -22,7 +24,7 @@ in
configuration = { lib, pkgs, config, assignments, ... }:
let
inherit (lib) mkMerge mkIf;
inherit (lib) mkMerge mkIf mkForce;
inherit (config.my.user.homeConfig.lib.file) mkOutOfStoreSymlink;
inherit (lib.my) networkdAssignment systemdAwaitPostgres;
in
@@ -45,11 +47,18 @@ in
owner = config.my.user.config.name;
group = config.my.user.config.group;
};
"object/atticd.env" = {};
"object/hedgedoc.env" = {};
};
};
firewall = {
tcp.allowed = [ 9000 9001 config.services.sharry.config.bind.port ];
tcp.allowed = [
9000 9001
config.services.sharry.config.bind.port
8069
config.services.hedgedoc.settings.port
];
};
user.homeConfig = {
@@ -57,9 +66,23 @@ in
};
};
users = with lib.my.c.ids; let inherit (config.services.atticd) user group; in {
users."${user}" = {
isSystemUser = true;
uid = uids.atticd;
group = group;
};
groups."${user}".gid = gids.atticd;
};
systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
services = {
services =
let
awaitPostgres = systemdAwaitPostgres pkgs.postgresql "colony-psql";
in
{
minio = {
environment = {
MINIO_ROOT_USER = "minioadmin";
@@ -68,7 +91,17 @@ in
MINIO_BROWSER_REDIRECT_URL = "https://minio.nul.ie";
};
};
sharry = systemdAwaitPostgres pkgs.postgresql "colony-psql";
sharry = awaitPostgres;
atticd = mkMerge [
awaitPostgres
{
serviceConfig = {
# Needs to be able to access its data
DynamicUser = mkForce false;
BindPaths = [ "/mnt/atticd:/var/lib/atticd/storage" ];
};
}
];
};
};
@@ -92,7 +125,7 @@ in
configOverridesFile = config.age.secrets."object/sharry.conf".path;
config = {
base-url = "https://share.${lib.my.pubDomain}";
base-url = "https://share.${lib.my.c.pubDomain}";
bind.address = "::";
alias-member-enabled = true;
webapp = {
@@ -146,6 +179,47 @@ in
};
};
};
atticd = {
enable = true;
credentialsFile = config.age.secrets."object/atticd.env".path;
settings = {
listen = "[::]:8069";
allowed-hosts = [ "nix-cache.${pubDomain}" ];
api-endpoint = "https://nix-cache.${pubDomain}/";
database = mkForce {}; # blank to pull from env
storage = {
type = "local";
path = "/var/lib/atticd/storage";
};
chunking = {
nar-size-threshold = 65536;
min-size = 16384;
avg-size = 65536;
max-size = 262144;
};
};
};
hedgedoc = {
enable = true;
environmentFile = config.age.secrets."object/hedgedoc.env".path;
settings = {
domain = "md.${pubDomain}";
protocolUseSSL = true;
db = {
dialect = "postgresql";
username = "hedgedoc";
database = "hedgedoc";
host = "colony-psql";
};
host = "::";
allowAnonymous = false;
allowAnonymousEdits = true;
email = true;
allowEmailRegister = false;
};
};
};
}
(mkIf config.my.build.isDevVM {

View File

@@ -1,12 +1,14 @@
{ lib, ... }:
let
inherit (lib) mkForce;
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.toot = {
nixos.systems.toot = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -53,8 +55,7 @@ in
tcp.allowed = [
19999
config.services.mastodon.webPort
config.services.mastodon.streamingPort
"http"
];
};
};
@@ -77,10 +78,13 @@ in
services = {
netdata.enable = true;
mastodon = mkMerge [
{
rec {
enable = true;
localDomain = "nul.ie";
extraConfig.WEB_DOMAIN = "toot.nul.ie";
localDomain = extraConfig.WEB_DOMAIN; # for nginx config
extraConfig = {
LOCAL_DOMAIN = "nul.ie";
WEB_DOMAIN = "toot.nul.ie";
};
secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path;
otpSecretFile = config.age.secrets."toot/otp-secret.txt".path;
@@ -89,9 +93,8 @@ in
"vapid-pubkey.txt"
"BAyRyD2pnLQtMHr3J5AzjNMll_HDC6ra1ilOLAUmKyhkEdbm7_OwKZUgw1UefY4CHEcv4OOX9TnnN2DOYYuPZu8=");
enableUnixSocket = false;
configureNginx = false;
trustedProxy = allAssignments.middleman.internal.ipv6.address;
streamingProcesses = 4;
configureNginx = true;
database = {
createLocally = false;
@@ -133,13 +136,31 @@ in
};
}
];
# Override some stuff since we are proxying upstream
nginx = {
recommendedProxySettings = mkForce false;
virtualHosts."${config.services.mastodon.localDomain}" =
let
extraConfig = ''
proxy_set_header Host $host;
'';
in
{
forceSSL = false;
enableACME = false;
locations = {
"@proxy" = { inherit extraConfig; };
"/api/v1/streaming/" = { inherit extraConfig; };
};
};
};
};
}
(mkIf config.my.build.isDevVM {
virtualisation = {
forwardPorts = with config.services.mastodon; [
{ from = "host"; guest.port = webPort; }
{ from = "host"; guest.port = streamingPort; }
];
};
})

View File

@@ -1,12 +1,13 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.vaultwarden = {
nixos.systems.vaultwarden = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -62,7 +63,7 @@ in
};
programs.ssh.knownHostsFiles = [
lib.my.sshKeyFiles.rsyncNet
lib.my.c.sshKeyFiles.rsyncNet
];
services = {
@@ -79,7 +80,7 @@ in
SIGNUPS_ALLOWED = false;
DOMAIN = "https://pass.${lib.my.pubDomain}";
DOMAIN = "https://pass.${lib.my.c.pubDomain}";
ROCKET_ADDRESS = "::";
ROCKET_PORT = 80;

View File

@@ -0,0 +1,100 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.waffletail = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
name = "waffletail-ctr";
inherit domain;
ipv4.address = net.cidr.host 9 prefixes.ctrs.v4;
ipv6 = {
iid = "::9";
address = net.cidr.host 9 prefixes.ctrs.v6;
};
};
tailscale = with lib.my.c.tailscale; {
ipv4 = {
address = net.cidr.host 5 prefix.v4;
mask = 32;
gateway = null;
};
ipv6 = {
address = net.cidr.host 5 prefix.v6;
mask = 128;
};
};
};
configuration = { lib, config, assignments, ... }:
let
inherit (lib) concatStringsSep mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment;
in
{
config = {
my = {
deploy.enable = false;
server.enable = true;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICZc88lcSQ9zzQzDITdE/T5ty++TxFQUAED7p9YfFBiR";
files = {
"tailscale-auth.key" = {};
};
};
firewall = {
trustedInterfaces = [ "tailscale0" ];
extraRules = ''
table inet filter {
chain forward {
iifname host0 oifname tailscale0 ip saddr ${prefixes.all.v4} accept
iifname host0 oifname tailscale0 ip6 saddr ${prefixes.all.v6} accept
}
}
table inet nat {
chain postrouting {
iifname tailscale0 ip daddr != ${prefixes.all.v4} snat to ${assignments.internal.ipv4.address}
iifname tailscale0 ip6 daddr != ${prefixes.all.v6} snat ip6 to ${assignments.internal.ipv6.address}
}
}
'';
};
};
systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
};
services = {
tailscale =
let
advRoutes = concatStringsSep "," (with prefixes.all; [ v4 v6 ]);
in
{
enable = true;
authKeyFile = config.age.secrets."tailscale-auth.key".path;
port = 41641;
openFirewall = true;
interfaceName = "tailscale0";
extraUpFlags = [
"--operator=${config.my.user.config.name}"
"--login-server=https://ts.nul.ie"
"--netfilter-mode=off"
"--advertise-exit-node"
"--advertise-routes=${advRoutes}"
"--accept-routes=false"
];
};
};
};
};
};
}

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my) net nft;
inherit (lib.my.c.colony) domain prefixes firewallForwards;
in
{
imports = [ ./containers ];
@@ -49,7 +49,7 @@ in
inherit (lib.my) networkdAssignment;
in
{
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ./hercules.nix ];
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ];
config = mkMerge [
{
@@ -81,6 +81,7 @@ in
fsType = "ext4";
neededForBoot = true;
};
"/mnt/media" = {
device = "/dev/disk/by-label/media";
fsType = "ext4";
@@ -89,6 +90,10 @@ in
device = "/dev/disk/by-label/minio";
fsType = "xfs";
};
"/mnt/atticd" = {
device = "/dev/disk/by-label/atticd";
fsType = "ext4";
};
};
nix.settings = {
@@ -97,7 +102,7 @@ in
};
services = {
fstrim = lib.my.colony.fstrimConfig;
fstrim = lib.my.c.colony.fstrimConfig;
netdata.enable = true;
};
@@ -131,7 +136,17 @@ in
};
ipv6Prefixes = [
{
ipv6PrefixConfig.Prefix = lib.my.colony.prefixes.ctrs.v6;
ipv6PrefixConfig.Prefix = prefixes.ctrs.v6;
}
];
routes = map (r: { routeConfig = r; }) [
{
Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.waffletail.internal.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.waffletail.internal.ipv6.address;
}
];
}
@@ -146,6 +161,7 @@ in
firewall = {
tcp.allowed = [ 19999 ];
trustedInterfaces = [ "ctrs" ];
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
extraRules = ''
table inet filter {
chain forward {
@@ -153,6 +169,17 @@ in
iifname vms oifname ctrs accept
}
}
table inet nat {
# Hack to fix our NAT situation with internal routing
# We need to snat to our public IP, otherwise on the return path from e.g. middleman it will
# try to forward packet directly with its own IP, bypassing our carefully crafted DNAT...
chain ${nft.dnatChain allAssignments.estuary.internal.ipv4.address} {
ct mark set 0x1337
}
chain postrouting {
ct mark 0x1337 snat ip to ${assignments.internal.ipv4.address}
}
}
'';
};
@@ -175,9 +202,11 @@ in
object = {
bindMounts = {
"/mnt/minio".readOnly = false;
"/mnt/atticd".readOnly = false;
};
};
toot = {};
waffletail = {};
};
in
mkMerge [

View File

@@ -2,7 +2,7 @@
let
inherit (builtins) mapAttrs;
inherit (lib.my) net;
inherit (lib.my.colony) domain prefixes;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.whale2 = {
@@ -50,6 +50,8 @@ in
};
}) {
valheim-oci = 2;
simpcraft-oci = 3;
simpcraft-staging-oci = 4;
};
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
@@ -63,6 +65,7 @@ in
"${modulesPath}/profiles/qemu-guest.nix"
./valheim.nix
./minecraft
];
config = mkMerge [
@@ -94,7 +97,7 @@ in
};
services = {
fstrim = lib.my.colony.fstrimConfig;
fstrim = lib.my.c.colony.fstrimConfig;
netdata.enable = true;
};

View File

@@ -0,0 +1,121 @@
{ lib, pkgs, config, allAssignments, ... }:
let
inherit (lib) concatStringsSep;
inherit (lib.my) dockerNetAssignment;
# devplayer0
op = "6d7d971b-ce10-435b-85c5-c99c0d8d288c";
whitelist = concatStringsSep "," [
op
"dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56" # Elderlypug
"fcb26db2-c3ce-41aa-b588-efec79d37a8a" # Jesthral_
"1d366062-12c0-4e29-aba7-6ab5d8c6bb05" # shr3kas0ras
"703b378a-09f9-4c1d-9876-1c9305728c49" # OROURKEIRE
"f105bbe6-eda6-4a13-a8cf-894e77cab77b" # Adzerq
"1fc94979-41fb-497a-81e9-34ae24ca537a" # johnnyscrims
"d53c91df-b6e6-4463-b106-e8427d7a8d01" # BossLonus
"f439f64d-91c9-4c74-9ce5-df4d24cd8e05" # hynge_
"d6ec4c91-5da2-44eb-b89d-71dc8fe017a0" # Eefah98
"096a7348-fabe-4b2d-93fc-fd1fd5608fb0" # ToTheMoonStar
];
fastback = {
gitConfig = pkgs.writeText "git-config" ''
[user]
email = "simpcraft@nul.ie"
name = "Simpcraft bot"
'';
knownHosts = pkgs.writeText "known_hosts" ''
git.nul.ie ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD023ECzYmLeXIpcGVaciPjq6UN/Sjmsys5HP/Nei5GkrUZqPa3OJ2uSXKLUSKGYdeNhxaFTPJe8Yx3TsZxMme8=
'';
};
in
{
config = {
virtualisation.oci-containers.containers = {
simpcraft = {
image = "ghcr.io/itzg/minecraft-server:2023.12.2-java17-alpine";
environment = {
TYPE = "MODRINTH";
EULA = "true";
ENABLE_QUERY = "true";
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t §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 = "6G";
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/cdj2bSKg/Simpcraft-0.1.2.mrpack";
TZ = "Europe/Dublin";
};
volumes = [
"minecraft_data:/data"
"${./icon.png}:/ext/icon.png:ro"
];
extraOptions = [
''--network=colony:${dockerNetAssignment allAssignments "simpcraft-oci"}''
];
};
simpcraft-staging = {
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"
"${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 = [
''--network=colony:${dockerNetAssignment allAssignments "simpcraft-staging-oci"}''
];
};
};
my = {
secrets.files = {
"whale2/simpcraft.env" = {};
"whale2/simpcraft-git.key" = {
owner = "1000";
};
};
};
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,220 @@
{ lib, ... }:
let
inherit (lib.my) net mkVLAN;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) domain vlans prefixes vips hiMTU;
in
{
imports = [ ./vms ];
nixos.systems.palace = {
system = "x86_64-linux";
nixpkgs = "mine-stable";
home-manager = "mine-stable";
assignments = {
hi = {
inherit domain;
mtu = hiMTU;
ipv4 = {
address = net.cidr.host 22 prefixes.hi.v4;
mask = 22;
gateway = vips.hi.v4;
};
ipv6 = {
iid = "::2:1";
address = net.cidr.host (65536*2+1) prefixes.hi.v6;
};
};
core = {
inherit domain;
name = "palace-core";
mtu = 1500;
ipv4 = {
address = net.cidr.host 20 prefixes.core.v4;
gateway = null;
};
};
};
configuration = { lib, pkgs, modulesPath, config, systems, assignments, allAssignments, ... }:
let
inherit (lib) mkForce mkMerge;
inherit (lib.my) networkdAssignment;
inherit (lib.my.c) networkd;
in
{
boot = {
kernelPackages = (lib.my.c.kernel.lts pkgs).extend (self: super: {
kernel = super.kernel.override {
structuredExtraConfig = with lib.kernel; {
ACPI_APEI_PCIEAER = yes;
PCIEAER = yes;
};
};
});
kernelModules = [ "kvm-amd" ];
kernelParams = [ "amd_iommu=on" ];
initrd = {
availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
};
};
hardware = {
enableRedistributableFirmware = true;
cpu = {
amd.updateMicrocode = true;
};
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-partuuid/c06a8d24-2af9-4416-bf5e-cfe6defdbd47";
fsType = "vfat";
};
"/nix" = {
device = "/dev/disk/by-uuid/450e1f72-238a-4160-98b8-b5e6d0d6fdf6";
fsType = "ext4";
};
"/persist" = {
device = "/dev/disk/by-uuid/9d6d53a8-dff8-49e0-9bc3-fb5f7c6760d0";
fsType = "ext4";
neededForBoot = true;
};
};
services = {
lvm = {
boot.thin.enable = true;
dmeventd.enable = true;
};
smartd = {
enable = true;
autodetect = true;
extraOptions = [ "-A /var/log/smartd/" "--interval=600" ];
};
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"
'';
};
environment.systemPackages = with pkgs; [
pciutils
usbutils
partclone
lm_sensors
linuxPackages.cpupower
smartmontools
mstflint
ethtool
hwloc
];
networking = { inherit domain; };
systemd = {
tmpfiles.rules = [
"d /var/log/smartd 0755 root root"
];
network = {
links = {
"10-et1g0" = {
matchConfig = {
PermanentMACAddress = "e0:d5:5e:68:0c:6e";
Driver = "igb";
};
linkConfig.Name = "et1g0";
};
"10-lan-core" = {
matchConfig.PermanentMACAddress = "e0:d5:5e:68:0c:70";
linkConfig.Name = "lan-core";
};
"10-et100g" = {
matchConfig = {
PermanentMACAddress = "24:8a:07:ac:59:c0";
Driver = "mlx5_core";
};
linkConfig = {
Name = "et100g";
MTUBytes = toString hiMTU;
};
};
};
netdevs = mkMerge [
(mkVLAN "lan-hi" vlans.hi)
(mkVLAN "lan-lo-phy" vlans.lo)
{
"25-lan-lo".netdevConfig = {
Name = "lan-lo";
Kind = "bridge";
};
}
];
networks = {
"50-lan-core" = mkMerge [
(networkdAssignment "lan-core" assignments.core)
{
matchConfig.Name = "lan-core";
vlan = [ "lan-lo-phy" ];
networkConfig.IPv6AcceptRA = mkForce false;
}
];
"50-et100g" = {
matchConfig.Name = "et100g";
vlan = [ "lan-hi" ];
networkConfig = networkd.noL3;
linkConfig.RequiredForOnline = "no";
extraConfig = ''
# cellar
[SR-IOV]
VirtualFunction=0
VLANId=${toString vlans.hi}
LinkState=yes
MACAddress=52:54:00:cc:3e:70
# river
[SR-IOV]
VirtualFunction=1
LinkState=yes
MACAddress=52:54:00:8a:8a:f2
# sfh
[SR-IOV]
VirtualFunction=2
VLANId=${toString vlans.hi}
LinkState=yes
MACAddress=52:54:00:ac:15:a9
'';
};
"60-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
"50-lan-lo-phy" = {
matchConfig.Name = "lan-lo-phy";
networkConfig = {
Bridge = "lan-lo";
} // networkd.noL3;
};
"60-lan-lo" = {
matchConfig.Name = "lan-lo";
linkConfig.RequiredForOnline = "no";
networkConfig = networkd.noL3;
};
};
};
};
my = {
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHzVJpoDz/AAYLJGzU8t6DgZ2sY3oehRqrlSO7C+GWiK";
};
server.enable = true;
deploy.node.hostname = "192.168.68.22";
};
};
};
}

View File

@@ -0,0 +1,98 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) domain prefixes vips hiMTU;
in
{
nixos.systems.cellar = {
system = "x86_64-linux";
nixpkgs = "mine";
assignments = {
hi = {
inherit domain;
mtu = hiMTU;
ipv4 = {
address = net.cidr.host 80 prefixes.hi.v4;
mask = 22;
gateway = vips.hi.v4;
};
ipv6 = {
iid = "::4:1";
address = net.cidr.host (65536*4+1) prefixes.hi.v6;
};
};
};
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge;
inherit (lib.my) networkdAssignment;
in
{
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
./spdk.nix
];
config = mkMerge [
{
boot = {
kernelParams = [ "console=ttyS0,115200n8" "intel_iommu=on" ];
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-partuuid/f7562ee6-34c1-4e94-8ae7-c6e71794d563";
fsType = "vfat";
};
"/nix" = {
device = "/dev/disk/by-uuid/f31f6abd-0832-4014-a761-f3c3126d5739";
fsType = "ext4";
};
"/persist" = {
device = "/dev/disk/by-uuid/620364e3-3a30-4704-be80-8593516e7482";
fsType = "ext4";
neededForBoot = true;
};
};
networking = { inherit domain; };
environment.systemPackages = with pkgs; [
pciutils
partclone
];
services = {
netdata.enable = true;
};
systemd.network = {
links = {
"10-lan-hi" = {
matchConfig.PermanentMACAddress = "52:54:00:cc:3e:70";
linkConfig.Name = "lan-hi";
};
};
networks = {
"80-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
};
};
my = {
secrets.key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDcklmJp8xVRddNDU1DruKV+Ipim3Jtl6nE1oCWmpmZH";
server.enable = true;
deploy.node.hostname = "192.168.68.80";
firewall = {
tcp.allowed = [ 19999 ];
};
};
}
];
};
};
}

View File

@@ -0,0 +1,137 @@
{ lib, pkgs, config, assignments, ... }:
let
inherit (lib) mapAttrsToList;
in
{
config = {
boot.blacklistedKernelModules = [ "nvme" ];
systemd.services = {
spdk-tgt.after = [ "systemd-networkd-wait-online@lan-hi.service" ];
};
my = {
spdk = {
enable = true;
extraArgs = "--mem-channels 2 --cpumask 0xffff";
debugCommands = ''
spdk-rpc bdev_nvme_attach_controller -t pcie -a 02:00.0 -b NVMe0
spdk-rpc bdev_nvme_attach_controller -t pcie -a 03:00.0 -b NVMe1
spdk-rpc bdev_nvme_attach_controller -t pcie -a 04:00.0 -b NVMe2
spdk-rpc bdev_raid_create -n NVMeRaid -z 64 -r 0 -b 'NVMe0n1 NVMe1n1 NVMe2n1'
spdk-rpc ublk_create_target
spdk-rpc ublk_start_disk NVMeRaid 1
'';
config.subsystems =
let
nvmeAttaches = mapAttrsToList (name: bdf: {
method = "bdev_nvme_attach_controller";
params = {
hostnqn =
"nqn.2014-08.org.nvmexpress:uuid:2b16606f-b82c-49f8-9b20-a589dac8b775";
trtype = "PCIe";
inherit name;
traddr = bdf;
};
}) {
"NVMe0" = "02:00.0";
"NVMe1" = "03:00.0";
"NVMe2" = "04:00.0";
};
nvmfListener = nqn: {
method = "nvmf_subsystem_add_listener";
params = {
inherit nqn;
listen_address = {
adrfam = "IPv4";
traddr = assignments.hi.ipv4.address;
trsvcid = "4420";
trtype = "RDMA";
};
secure_channel = false;
};
};
nvmfBdev = { nqn, hostnqn, bdev, serial }: [
{
method = "nvmf_create_subsystem";
params = {
inherit nqn;
serial_number = serial;
};
}
(nvmfListener nqn)
{
method = "nvmf_subsystem_add_host";
params = {
inherit nqn;
host = hostnqn;
};
}
{
method = "nvmf_subsystem_add_ns";
params = {
inherit nqn;
namespace = {
bdev_name = bdev;
nsid = 1;
};
};
}
];
in
{
scheduler = [
{
method = "framework_set_scheduler";
params.name = "dynamic";
}
];
bdev = [
{
method = "bdev_set_options";
params.bdev_auto_examine = false;
}
] ++ nvmeAttaches ++ [
{
method = "bdev_raid_create";
params = {
base_bdevs = [ "NVMe0n1" "NVMe1n1" "NVMe2n1" ];
name = "NVMeRaid";
raid_level = "raid0";
strip_size_kb = 64;
};
}
{
method = "bdev_examine";
params.name = "NVMeRaid";
}
{ method = "bdev_wait_for_examine"; }
];
nvmf = [
{
method = "nvmf_create_transport";
params.trtype = "RDMA";
}
(nvmfListener "nqn.2014-08.org.nvmexpress.discovery")
] ++ (nvmfBdev {
bdev = "NVMeRaidp1";
nqn = "nqn.2016-06.io.spdk:river";
hostnqn =
"nqn.2014-08.org.nvmexpress:uuid:12b52d80-ccb6-418d-9b2e-2be34bff3cd9";
serial = "SPDK00000000000001";
}) ++ (nvmfBdev {
bdev = "NVMeRaidp2";
nqn = "nqn.2016-06.io.spdk:castle";
hostnqn =
"nqn.2014-08.org.nvmexpress:uuid:2230b066-a674-4f45-a1dc-f7727b3a9e7b";
serial = "SPDK00000000000002";
});
};
};
};
};
}

View File

@@ -0,0 +1,191 @@
{
imports = [
./cellar
./river.nix
];
nixos.systems.palace.configuration = { lib, pkgs, config, systems, allAssignments, ... }:
let
inherit (lib) mkMerge;
inherit (lib.my) vm;
inherit (lib.my.c) networkd;
installerDisk = {
name = "installer";
backend = {
driver = "file";
filename = "/persist/home/dev/nixos-installer-devplayer0.iso";
read-only = "on";
};
format.driver = "raw";
frontend = "ide-cd";
frontendOpts = {
bootindex = 1;
};
};
in
{
systemd.network = {
netdevs = {
"25-vm-et1g0" = {
netdevConfig = {
Name = "vm-et1g0";
Kind = "macvtap";
};
# TODO: Upstream this missing section
extraConfig = ''
[MACVTAP]
Mode=passthru
'';
};
};
networks = {
"75-et1g0" = {
matchConfig.Name = "et1g0";
linkConfig.RequiredForOnline = "no";
networkConfig = {
MACVTAP = "vm-et1g0";
} // networkd.noL3;
};
"75-vm-et1g0" = {
matchConfig.Name = "vm-et1g0";
linkConfig.RequiredForOnline = "no";
networkConfig = networkd.noL3;
};
};
};
systemd.services =
let
awaitCellar = {
after = [ "vm@cellar.service" ];
bindsTo = [ "vm@cellar.service" ];
preStart = ''
until ${pkgs.netcat}/bin/nc -w1 -z ${allAssignments.cellar.hi.ipv4.address} 22; do
sleep 1
done
'';
};
in
{
"vm@cellar" = {
serviceConfig = {
CPUAffinity = "numa";
NUMAPolicy = "bind";
NUMAMask = "1";
};
};
"vm@river" =
let
vtapUnit = "sys-subsystem-net-devices-vm\\x2det1g0.device";
in
mkMerge [
awaitCellar
{
requires = [ vtapUnit ];
after = [ vtapUnit ];
}
];
"vm@sfh" = awaitCellar;
};
my = {
vms = {
instances = {
cellar = {
uuid = "b126d135-9fc1-415a-b675-aaf727bf2f38";
cpu = "host,topoext";
smp = {
cpus = 8;
threads = 2;
};
memory = 16384;
cleanShutdown.timeout = 120;
drives = [
(mkMerge [ (vm.disk "cellar" "esp") { frontendOpts.bootindex = 0; } ])
(vm.disk "cellar" "nix")
(vm.disk "cellar" "persist")
];
hostDevices = {
et100g0vf0 = {
index = 0;
hostBDF = "44:00.1";
};
nvme0 = {
index = 1;
hostBDF = "41:00.0";
};
nvme1 = {
index = 2;
hostBDF = "42:00.0";
};
nvme2 = {
index = 3;
hostBDF = "43:00.0";
};
};
qemuFlags = [
"machine kernel-irqchip=split"
"device intel-iommu,caching-mode=on,device-iotlb=on,intremap=on"
];
};
river = {
uuid = "12b52d80-ccb6-418d-9b2e-2be34bff3cd9";
cpu = "host,topoext";
smp = {
cpus = 3;
threads = 2;
};
memory = 4096;
cleanShutdown.timeout = 60;
networks = {
et1g0 = {
ifname = "vm-et1g0";
bridge = null;
tapFD = 100;
# Real hardware MAC
mac = "e0:d5:5e:68:0c:6e";
waitOnline = false;
};
};
drives = [
installerDisk
(mkMerge [ (vm.disk "river" "esp") { frontendOpts.bootindex = 0; } ])
];
hostDevices = {
et100g0vf1 = {
index = 0;
hostBDF = "44:00.2";
};
};
};
sfh = {
uuid = "82ec149d-577c-421a-93e2-a9307c756cd8";
cpu = "host,topoext";
smp = {
cpus = 8;
threads = 2;
};
memory = 32768;
cleanShutdown.timeout = 120;
networks.netboot = {
bridge = "lan-lo";
waitOnline = "carrier";
mac = "52:54:00:a5:7e:93";
extraOptions.bootindex = 1;
};
hostDevices = {
et100g0vf2 = {
index = 0;
hostBDF = "44:00.3";
};
};
};
};
};
};
};
}

View File

@@ -0,0 +1,142 @@
{
imports = [ (import ../../routing-common 0) ];
config.nixos.systems.river = {
system = "x86_64-linux";
nixpkgs = "mine";
home-manager = "mine";
configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib.my) networkdAssignment mkVLAN;
inherit (lib.my.c) networkd;
inherit (lib.my.c.home) vlans;
lanLink = {
matchConfig = {
Driver = "mlx5_core";
PermanentMACAddress = "52:54:00:8a:8a:f2";
};
linkConfig = {
Name = "lan";
MTUBytes = toString lib.my.c.home.hiMTU;
};
};
in
{
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
];
config = {
boot = {
kernelModules = [ "kvm-intel" ];
kernelParams = [ "console=ttyS0,115200n8" ];
initrd = {
availableKernelModules = [
"virtio_pci" "ahci" "sr_mod" "virtio_blk"
"ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" "8021q"
"rdma_cm" "iw_cm" "ib_cm" "nvme_core" "nvme_rdma"
];
kernelModules = [ "dm-snapshot" "nvme-fabrics" ];
systemd = {
extraBin = with pkgs; {
dmesg = "${util-linux}/bin/dmesg";
ip = "${iproute2}/bin/ip";
};
extraConfig = ''
DefaultTimeoutStartSec=50
DefaultDeviceTimeoutSec=50
'';
network = {
enable = true;
wait-online.enable = true;
links."10-lan" = lanLink;
netdevs = mkVLAN "lan-hi" vlans.hi;
networks = {
"20-lan" = {
matchConfig.Name = "lan";
vlan = [ "lan-hi" ];
linkConfig.RequiredForOnline = "no";
networkConfig = networkd.noL3;
};
"30-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
};
};
};
};
};
hardware = {
enableRedistributableFirmware = true;
cpu = {
intel.updateMicrocode = true;
};
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-partuuid/3ec6c49e-b485-40cb-8eff-315581ac6fe9";
fsType = "vfat";
};
"/nix" = {
device = "/dev/main/nix";
fsType = "ext4";
};
"/persist" = {
device = "/dev/main/persist";
fsType = "ext4";
neededForBoot = true;
};
};
services = {
lvm = {
boot.thin.enable = true;
dmeventd.enable = true;
};
};
systemd.network = {
links = {
"10-wan" = {
matchConfig = {
# Matching against MAC address seems to break VLAN interfaces
# (since they share the same MAC address)
Driver = "virtio_net";
PermanentMACAddress = "e0:d5:5e:68:0c:6e";
};
linkConfig = {
Name = "wan";
RxBufferSize = 4096;
TxBufferSize = 4096;
};
};
"10-lan" = lanLink;
};
# So we don't drop the IP we use to connect to NVMe-oF!
networks."60-lan-hi".networkConfig.KeepConfiguration = "static";
};
my = {
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP9uFa4z9WPuXRFVA+PClQSitQCSPckhKTxo1Hq585Oa";
};
server.enable = true;
nvme = {
uuid = "12b52d80-ccb6-418d-9b2e-2be34bff3cd9";
boot = {
nqn = "nqn.2016-06.io.spdk:river";
address = "192.168.68.80";
};
};
deploy.node.hostname = "192.168.68.1";
};
};
};
};
}

View File

@@ -0,0 +1,384 @@
index: { lib, allAssignments, ... }:
let
inherit (builtins) elemAt;
inherit (lib.my) net mkVLAN;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) domain vlans prefixes vips routers routersPubV4;
name = elemAt routers index;
otherIndex = 1 - index;
in
{
nixos.systems."${name}" = {
assignments = {
modem = {
ipv4 = {
address = net.cidr.host (254 - index) prefixes.modem.v4;
gateway = null;
};
};
core = {
name = "${name}-core";
inherit domain;
mtu = 1500;
ipv4 = {
address = net.cidr.host (index + 1) prefixes.core.v4;
gateway = null;
};
};
hi = {
name = "${name}-hi";
inherit domain;
mtu = 9000;
ipv4 = {
address = net.cidr.host (index + 1) prefixes.hi.v4;
mask = 22;
gateway = null;
};
ipv6.address = net.cidr.host (index + 1) prefixes.hi.v6;
};
lo = {
name = "${name}-lo";
inherit domain;
mtu = 1500;
ipv4 = {
address = net.cidr.host (index + 1) prefixes.lo.v4;
mask = 21;
gateway = null;
};
ipv6.address = net.cidr.host (index + 1) prefixes.lo.v6;
};
untrusted = {
name = "${name}-ut";
inherit domain;
mtu = 1500;
ipv4 = {
address = net.cidr.host (index + 1) prefixes.untrusted.v4;
mask = 24;
gateway = null;
};
ipv6.address = net.cidr.host (index + 1) prefixes.untrusted.v6;
};
as211024 = {
ipv4 = {
address = net.cidr.host (index + 2) prefixes.as211024.v4;
gateway = null;
};
ipv6 = {
address = net.cidr.host ((1*65536*65536*65536) + index + 1) prefixes.as211024.v6;
gateway = net.cidr.host ((2*65536*65536*65536) + 1) prefixes.as211024.v6;
};
};
};
extraAssignments = {
router-hi.hi = {
name = "router-hi";
inherit domain;
ipv4 = {
address = vips.hi.v4;
mask = 22;
};
ipv6.address = vips.hi.v6;
};
router-lo.lo = {
name = "router-lo";
inherit domain;
ipv4 = {
address = vips.lo.v4;
mask = 21;
};
ipv6.address = vips.lo.v6;
};
router-ut.untrusted = {
name = "router-ut";
inherit domain;
ipv4.address = vips.untrusted.v4;
ipv6.address = vips.untrusted.v6;
};
};
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib) mkIf mkMerge mkForce;
inherit (lib.my) networkdAssignment;
inherit (lib.my.c) networkd;
in
{
imports = map (m: import m index) [
./keepalived.nix
./dns.nix
./radvd.nix
./kea.nix
];
config = {
environment = {
systemPackages = with pkgs; [
ethtool
conntrack-tools
];
};
services = {
resolved = {
llmnr = "false";
extraConfig = ''
MulticastDNS=false
'';
};
iperf3 = {
enable = true;
openFirewall = true;
};
networkd-dispatcher = {
enable = true;
rules = {
# tc filter hasn't been networkd-ified yet
setup-wan-mirror = {
onState = [ "configured" ];
script = ''
#!${pkgs.runtimeShell}
if [ $IFACE = "wan-ifb" ]; then
${pkgs.iproute2}/bin/tc filter add dev wan parent ffff: matchall action mirred egress redirect dev $IFACE
fi
'';
};
};
};
};
networking.domain = "h.${pubDomain}";
systemd.services = {
ipsec =
let
waitOnline = "systemd-networkd-wait-online@wan.service";
in
{
after = [ waitOnline ];
requires = [ waitOnline ];
};
};
systemd.network = {
wait-online.enable = false;
config = {
networkConfig = {
ManageForeignRoutes = false;
};
};
netdevs = mkMerge [
{
"25-wan-ifb".netdevConfig = {
Name = "wan-ifb";
Kind = "ifb";
};
"30-lan-core".netdevConfig = {
Name = "lan-core";
Kind = "macvlan";
MTUBytes = "1500";
};
}
(mkVLAN "lan-hi" vlans.hi)
(mkVLAN "lan-lo" vlans.lo)
(mkVLAN "lan-untrusted" vlans.untrusted)
];
networks =
let
mkVLANConfig = name:
let
iface = "lan-${name}";
in
{
"60-${iface}" = mkMerge [
(networkdAssignment iface assignments."${name}")
{
dns = [ "127.0.0.1" "::1" ];
domains = [ config.networking.domain ];
networkConfig.IPv6AcceptRA = mkForce false;
}
];
};
in
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" = {
matchConfig.Name = "lan";
vlan = [ "lan-hi" "lan-lo" "lan-untrusted" "wan-tunnel" ];
macvlan = [ "lan-core" ];
networkConfig = networkd.noL3;
};
"60-lan-core" = mkMerge [
(networkdAssignment "lan-core" assignments.core)
{
matchConfig.Name = "lan-core";
networkConfig.IPv6AcceptRA = mkForce false;
}
];
"90-l2mesh-as211024" = mkMerge [
(networkdAssignment "as211024" assignments.as211024)
{
matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false;
routes = map (r: { routeConfig = r; }) [
{
Destination = lib.my.c.colony.prefixes.all.v4;
Gateway = allAssignments.estuary.as211024.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.britway.as211024.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.britway.as211024.ipv6.address;
}
];
}
];
}
(mkVLANConfig "hi")
(mkVLANConfig "lo")
(mkVLANConfig "untrusted")
{
"60-lan-hi" = {
routes = map (r: { routeConfig = r; }) [
{
Destination = elemAt routersPubV4 otherIndex;
Gateway = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
}
];
};
}
];
};
my = {
secrets = {
files = {
"l2mesh/as211024.key" = {};
};
};
vpns = {
l2.pskFiles = {
as211024 = config.age.secrets."l2mesh/as211024.key".path;
};
};
firewall = {
trustedInterfaces = [ "lan-hi" "lan-lo" ];
udp.allowed = [ 5353 ];
tcp.allowed = [ 5353 ];
nat = {
enable = true;
externalInterface = "wan";
};
extraRules =
let
aa = allAssignments;
in
''
table inet filter {
chain input {
${lib.my.c.as211024.nftTrust}
iifname base meta l4proto { udp, tcp } th dport domain accept
iifname lan-core meta l4proto vrrp accept
}
chain routing-tcp {
ip daddr {
${aa.castle.hi.ipv4.address},
${aa.cellar.hi.ipv4.address},
${aa.palace.hi.ipv4.address}
} tcp dport ssh accept
ip6 daddr {
${aa.castle.hi.ipv6.address},
${aa.cellar.hi.ipv6.address},
${aa.palace.hi.ipv6.address}
} tcp dport ssh accept
return
}
chain routing-udp {
return
}
chain filter-routing {
tcp flags & (fin|syn|rst|ack) == syn ct state new jump routing-tcp
meta l4proto udp ct state new jump routing-udp
return
}
chain filter-untrusted {
ip daddr ${prefixes.modem.v4} reject
oifname wan accept
return
}
chain forward {
${lib.my.c.as211024.nftTrust}
iifname lan-untrusted jump filter-untrusted
iifname { wan, as211024, lan-untrusted } oifname { lan-hi, lan-lo } jump filter-routing
oifname as211024 accept
}
chain output { }
}
table inet nat {
chain prerouting {
ip daddr ${elemAt routersPubV4 index} meta l4proto { udp, tcp } th dport domain redirect to :5353
ip6 daddr ${assignments.as211024.ipv6.address} meta l4proto { udp, tcp } th dport domain redirect to :5353
}
chain postrouting {
oifname wan masquerade
}
}
'';
};
};
};
};
};
}

View File

@@ -0,0 +1,246 @@
index: { lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (builtins) attrNames elemAt;
inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) prefixes vips routers;
name = elemAt routers index;
otherIndex = 1 - index;
otherName = elemAt routers otherIndex;
authZones = attrNames config.my.pdns.auth.bind.zones;
in
{
config = {
my = {
secrets.files = {
"home/pdns/auth.conf" = {
owner = "pdns";
group = "pdns";
};
"home/pdns/recursor.conf" = {
owner = "pdns-recursor";
group = "pdns-recursor";
};
"home/ddclient-cloudflare.key" = {};
};
pdns.recursor = {
enable = true;
extraSettingsFile = config.age.secrets."home/pdns/recursor.conf".path;
};
};
services = {
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 = {
query-local-address = [
"0.0.0.0"
"::"
];
forward-zones = map (z: "${z}=127.0.0.1:5353") authZones;
# DNS NOTIFY messages override TTL
allow-notify-for = authZones;
allow-notify-from = [ "127.0.0.0/8" "::1/128" ];
webserver = true;
webserver-address = "::";
webserver-allow-from = [ "127.0.0.1" "::1" ];
};
};
};
systemd = {
services = {
# Add AF_NETLINK to allow pulling IP from network interfaces
pdns.serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
ddns-update = {
description = "DNS update script";
after = [ "network.target" ];
path = [
(pkgs.python3.withPackages (ps: [ ps.cloudflare ]))
pkgs.ldns
];
serviceConfig = {
Type = "oneshot";
ExecStart =
''${./dns_update.py} -k ${config.age.secrets."home/ddclient-cloudflare.key".path} '' +
''${pubDomain} ns${toString (index + 1)}.${config.networking.domain}'';
};
wantedBy = [ "multi-user.target" ];
};
};
timers = {
ddns-update = {
description = "Periodically update DNS";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "5min";
OnUnitInactiveSec = "5min";
};
};
};
};
environment.systemPackages = with pkgs; [
# For rec_control
pdns-recursor
sqlite
];
my.pdns.auth = {
enable = true;
extraSettingsFile = config.age.secrets."home/pdns/auth.conf".path;
settings = {
primary = true;
resolver = "127.0.0.1";
expand-alias = true;
local-address = [
"0.0.0.0:5353" "[::]:5353"
];
also-notify = [ "127.0.0.1" ];
enable-lua-records = true;
# loglevel = 7;
# log-dns-queries = true;
# log-dns-details = true;
api = true;
webserver = true;
webserver-address = "::";
webserver-allow-from = [ "127.0.0.1" "::1" ];
dnsupdate = true;
launch = [ "gsqlite3" ];
gsqlite3-database = "/var/lib/pdns/dynamic.sqlite3";
};
bind.zones =
let
names = [ "core" "hi" "lo" "untrusted" ];
i = toString (index + 1);
in
{
"${config.networking.domain}" = {
type = "master";
text = ''
$TTL 60
@ IN SOA ns${i}.${config.networking.domain}. dev.nul.ie. (
@@SERIAL@@ ; serial
3h ; refresh
1h ; retry
1w ; expire
1h ; minimum
)
${name} IN LUA ${lib.my.dns.ifaceA {
inherit pkgs;
iface = "wan";
skipBroadcasts = [ (lib.my.netBroadcast prefixes.modem.v4) ];
}}
${otherName} IN LUA ${lib.my.dns.lookupIP {
inherit pkgs;
hostname = "${otherName}.${config.networking.domain}";
server = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
}}
${elemAt routers 0} IN AAAA ${net.cidr.host 1 prefixes.hi.v6}
${elemAt routers 1} IN AAAA ${net.cidr.host 2 prefixes.hi.v6}
@ IN NS ns1
@ IN NS ns2
ns1 IN ALIAS ${elemAt routers 0}.${config.networking.domain}.
ns2 IN ALIAS ${elemAt routers 1}.${config.networking.domain}.
dyn IN NS ns1.dyn.h.nul.ie.
dyn IN NS ns2.dyn.h.nul.ie.
ns1.dyn.h.nul.ie. IN ALIAS ${elemAt routers 0}.${config.networking.domain}.
ns2.dyn.h.nul.ie. IN ALIAS ${elemAt routers 1}.${config.networking.domain}.
jim-core IN A ${net.cidr.host 10 prefixes.core.v4}
jim IN A ${net.cidr.host 10 prefixes.hi.v4}
jim IN AAAA ${net.cidr.host (65536+1) prefixes.hi.v6}
jim-lo IN A ${net.cidr.host 10 prefixes.lo.v4}
jim-lo IN AAAA ${net.cidr.host (65536+1) prefixes.lo.v6}
dave-core IN A ${net.cidr.host 11 prefixes.core.v4}
dave IN A ${net.cidr.host 11 prefixes.hi.v4}
dave IN AAAA ${net.cidr.host (65536+2) prefixes.hi.v6}
dave-lo IN A ${net.cidr.host 11 prefixes.lo.v4}
dave-lo IN AAAA ${net.cidr.host (65536+2) prefixes.lo.v6}
;ap0 IN A ${net.cidr.host 12 prefixes.hi.v4}
;ap0 IN AAAA ${net.cidr.host (65536+3) prefixes.hi.v6}
vibe IN A ${net.cidr.host 13 prefixes.hi.v4}
vibe IN AAAA ${net.cidr.host (65536+4) prefixes.hi.v6}
ups IN A ${net.cidr.host 20 prefixes.lo.v4}
palace-kvm IN A ${net.cidr.host 21 prefixes.lo.v4}
${lib.my.dns.fwdRecords {
inherit allAssignments names;
domain = config.networking.domain;
}}
'';
};
"168.192.in-addr.arpa" = {
type = "master";
text = ''
$TTL 60
@ IN SOA ns${i}.${config.networking.domain}. dev.nul.ie. (
@@SERIAL@@ ; serial
3h ; refresh
1h ; retry
1w ; expire
1h ; minimum
)
@ IN NS ns1.${config.networking.domain}.
@ IN NS ns2.${config.networking.domain}.
${lib.my.dns.ptrRecords {
inherit allAssignments names;
domain = config.networking.domain;
ndots = 2;
}}
'';
};
"0.d.4.0.0.c.7.9.e.0.a.2.ip6.arpa" = {
type = "master";
text = ''
$TTL 60
@ IN SOA ns${i}.${config.networking.domain}. dev.nul.ie. (
@@SERIAL@@ ; serial
3h ; refresh
1h ; retry
1w ; expire
1h ; minimum
)
@ IN NS ns1.${config.networking.domain}.
@ IN NS ns2.${config.networking.domain}.
${lib.my.dns.ptr6Records {
inherit allAssignments names;
domain = config.networking.domain;
ndots = 20;
}}
'';
};
};
};
};
}

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import argparse
import subprocess
import CloudFlare
def main():
parser = argparse.ArgumentParser(description='Cloudflare DNS update script')
parser.add_argument('-k', '--api-token-file', help='Cloudflare API token file')
parser.add_argument('zone', help='Cloudflare Zone')
parser.add_argument('record', help='Cloudflare record name')
args = parser.parse_args()
address = subprocess.check_output(
['drill', '-Q', '-p5353', '@127.0.0.1', args.record, 'A'],
encoding='utf8').strip()
cf_token = None
if args.api_token_file:
with open(args.api_token_file) as f:
cf_token = f.readline().strip()
cf = CloudFlare.CloudFlare(token=cf_token)
zones = cf.zones.get(params={'name': args.zone})
assert zones, f'Zone {args.zone} not found'
records = cf.zones.dns_records.get(zones[0]['id'], params={'name': args.record})
assert records, f'Record {args.record} not found in zone {args.zone}'
print(f'Updating {args.record} -> {address}')
cf.zones.dns_records.patch(
zones[0]['id'], records[0]['id'],
data={'type': 'A', 'name': args.record, 'content': address})
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,149 @@
index: { lib, pkgs, assignments, ... }:
let
inherit (lib) mkForce;
inherit (lib.my) net;
inherit (lib.my.c.home) domain prefixes vips hiMTU;
dns-servers = [
{
ip-address = net.cidr.host 1 prefixes.core.v4;
port = 5353;
}
{
ip-address = net.cidr.host 2 prefixes.core.v4;
port = 5353;
}
];
in
{
users = with lib.my.c.ids; {
users.kea= {
isSystemUser = true;
uid = uids.kea;
group = "kea";
};
groups.kea.gid = gids.kea;
};
systemd.services = {
kea-dhcp4-server.serviceConfig.DynamicUser = mkForce false;
kea-dhcp-ddns-server.serviceConfig.DynamicUser = mkForce false;
};
services = {
kea = {
dhcp4 = {
enable = true;
settings = {
interfaces-config = {
interfaces = [
"lan-hi/${assignments.hi.ipv4.address}"
"lan-lo/${assignments.lo.ipv4.address}"
"lan-untrusted/${assignments.untrusted.ipv4.address}"
];
};
lease-database = {
type = "memfile";
persist = true;
name = "/var/lib/kea/dhcp.leases";
};
option-data = [
{
name = "domain-name";
data = domain;
}
{
name = "domain-search";
data = "${domain}, dyn.${domain}, ${lib.my.c.colony.domain}, ${lib.my.c.britway.domain}";
always-send = true;
}
];
subnet4 = [
{
id = 1;
subnet = prefixes.hi.v4;
interface = "lan-hi";
option-data = [
{
name = "routers";
data = vips.hi.v4;
}
{
name = "domain-name-servers";
data = "${net.cidr.host 1 prefixes.hi.v4}, ${net.cidr.host 2 prefixes.hi.v4}";
}
{
name = "interface-mtu";
data = toString hiMTU;
}
];
pools = [
{
pool = if index == 0
then "192.168.68.120 - 192.168.69.255"
else "192.168.70.0 - 192.168.71.240";
}
];
reservations = [
{
# castle
hw-address = "24:8a:07:a8:fe:3a";
ip-address = net.cidr.host 40 prefixes.hi.v4;
}
];
}
{
id = 2;
subnet = prefixes.lo.v4;
interface = "lan-lo";
option-data = [
{
name = "routers";
data = vips.lo.v4;
}
{
name = "domain-name-servers";
data = "${net.cidr.host 1 prefixes.lo.v4}, ${net.cidr.host 2 prefixes.lo.v4}";
}
];
pools = [
{
pool = if index == 0
then "192.168.72.120 - 192.168.75.255"
else "192.168.76.0 - 192.168.79.240";
}
];
reservations = [
{
# castle
hw-address = "24:8a:07:a8:fe:3a";
ip-address = net.cidr.host 40 prefixes.lo.v4;
}
];
}
];
ddns-send-updates = true;
ddns-replace-client-name = "when-not-present";
ddns-qualifying-suffix = "dyn.${domain}";
ddns-generated-prefix = "ip";
ddns-update-on-renew = true;
dhcp-ddns.enable-updates = true;
};
};
dhcp-ddns = {
enable = true;
settings = {
forward-ddns.ddns-domains = [
{
name = "dyn.${domain}.";
inherit dns-servers;
}
];
};
};
};
};
}

View File

@@ -0,0 +1,52 @@
index: { lib, pkgs, config, ... }:
let
inherit (builtins) attrNames concatMap;
inherit (lib) optional;
inherit (lib.my) net;
inherit (lib.my.c.home) prefixes vips;
vlanIface = vlan: if vlan == "as211024" then vlan else "lan-${vlan}";
vrrpIPs = family: concatMap (vlan: [
{
addr = "${vips.${vlan}.${family}}/${toString (net.cidr.length prefixes.${vlan}.${family})}";
dev = vlanIface vlan;
}
] ++ (optional (family == "v6") {
addr = "fe80::1/64";
dev = vlanIface vlan;
})) (attrNames vips);
mkVRRP = family: routerId: {
state = if index == 0 then "MASTER" else "BACKUP";
interface = "lan-core";
priority = 255 - index;
virtualRouterId = routerId;
virtualIps = vrrpIPs family;
extraConfig = ''
notify_master "${config.systemd.package}/bin/systemctl start radvd.service"
notify_backup "${config.systemd.package}/bin/systemctl stop radvd.service"
'';
};
in
{
services = {
keepalived = {
enable = true;
extraGlobalDefs = ''
vrrp_version 3
nftables keepalived
'';
vrrpInstances = {
v4 = mkVRRP "v4" 51;
v6 = mkVRRP "v6" 52;
};
extraConfig = ''
vrrp_sync_group main {
group {
v4
v6
}
}
'';
};
};
}

View File

@@ -0,0 +1,53 @@
{ lib, pkgs, ... }:
let
# TODO: Move into nixpkgs
mstpd = pkgs.mstpd.overrideAttrs {
patches = [ ./mstpd.patch ];
};
in
{
environment = {
systemPackages = [
mstpd
];
etc = {
"bridge-stp.conf".text = ''
MANAGE_MSTPD=n
MSTP_BRIDGES=lan
'';
};
};
services = {
networkd-dispatcher.rules = {
configure-mstpd = {
onState = [ "routable" ];
script = ''
#!${pkgs.runtimeShell}
if [ $IFACE = "lan" ]; then
${mstpd}/sbin/mstpctl setforcevers $IFACE rstp
fi
'';
};
};
};
systemd = {
services = {
mstpd = {
description = "MSTP daemon";
before = [ "network-pre.target" ];
serviceConfig = {
Type = "forking";
ExecStart = "${mstpd}/sbin/bridge-stp restart";
ExecReload = "${mstpd}/sbin/bridge-stp restart_config";
PIDFile = "/run/mstpd.pid";
Restart = "always";
PrivateTmp = true;
ProtectHome = true;
};
wantedBy = [ "multi-user.target" ];
};
};
};
}

View File

@@ -0,0 +1,26 @@
diff --git a/bridge-stp.in b/bridge-stp.in
index 3807873..9c73126 100755
--- a/bridge-stp.in
+++ b/bridge-stp.in
@@ -31,6 +31,10 @@
# bridge or any associated kernel network interfaces in any code paths that are
# used when this script is called by the kernel.
+# Ensure that we have a sane PATH.
+PATH='/run/current-system/sw/bin'
+export PATH
+
# Parse arguments.
CalledAs="$(basename "$0")"
if [ "$CalledAs" = 'mstpctl_restart_config' ]; then
@@ -62,10 +66,6 @@ fi
# Ensure that we have a sane umask.
umask 022
-# Ensure that we have a sane PATH.
-PATH='/sbin:/usr/sbin:/bin:/usr/bin'
-export PATH
-
# Define some relevant paths.
mstpctl='@mstpctlfile@'
mstpd='@mstpdfile@'

View File

@@ -0,0 +1,28 @@
index: { lib, pkgs, ... }:
let
inherit (lib) mkForce concatMapStringsSep;
inherit (lib.my) net;
inherit (lib.my.c.home) domain prefixes;
mkInterface = name: ''
interface lan-${name} {
AdvSendAdvert on;
AdvRASrcAddress { fe80::1; };
AdvLinkMTU ${toString prefixes."${name}".mtu};
prefix ${prefixes."${name}".v6} {};
RDNSS ${net.cidr.host 1 prefixes."${name}".v6} ${net.cidr.host 2 prefixes."${name}".v6} {};
DNSSL ${domain} dyn.${domain} ${lib.my.c.colony.domain} ${lib.my.c.britway.domain} {};
};
'';
in
{
# To be started by keepalived
systemd.services.radvd.wantedBy = mkForce [ ];
services = {
radvd = {
enable = true;
config = concatMapStringsSep "\n" mkInterface [ "hi" "lo" "untrusted" ];
};
};
}

131
nixos/boxes/home/stream.nix Normal file
View File

@@ -0,0 +1,131 @@
{
imports = [ (import ./routing-common 1) ];
config.nixos.systems.stream = {
system = "x86_64-linux";
nixpkgs = "mine";
home-manager = "mine";
configuration = { lib, pkgs, config, ... }:
let
inherit (lib);
in
{
imports = [ ./routing-common/mstpd.nix ];
config = {
boot = {
kernelModules = [ "kvm-intel" ];
kernelParams = [ "intel_iommu=on" ];
initrd.availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" "sd_mod" "sdhci_pci" ];
};
hardware = {
enableRedistributableFirmware = true;
cpu = {
intel.updateMicrocode = true;
};
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-partuuid/fe081885-9157-46b5-be70-46ac6fcb4069";
fsType = "vfat";
};
"/nix" = {
device = "/dev/disk/by-partuuid/a195e55e-397f-440d-a190-59ffa63cdb3f";
fsType = "ext4";
};
"/persist" = {
device = "/dev/disk/by-partuuid/ad71fafd-2d26-49c8-b0cb-794a28e0beb7";
fsType = "ext4";
neededForBoot = true;
};
};
systemd.network = {
netdevs = {
"25-lan" = {
netdevConfig = {
Name = "lan";
Kind = "bridge";
};
extraConfig = ''
[Bridge]
STP=true
'';
};
};
links = {
"10-wan" = {
matchConfig = {
# Matching against MAC address seems to break VLAN interfaces
# (since they share the same MAC address)
Driver = "igc";
PermanentMACAddress = "00:f0:cb:ee:ca:dd";
};
linkConfig = {
Name = "wan";
RxBufferSize = 4096;
TxBufferSize = 4096;
};
};
"10-lan-jim" = {
matchConfig = {
Driver = "igc";
PermanentMACAddress = "00:f0:cb:ee:ca:de";
};
linkConfig = {
Name = "lan-jim";
MTUBytes = toString lib.my.c.home.hiMTU;
};
};
"10-et2" = {
matchConfig = {
Driver = "igc";
PermanentMACAddress = "00:f0:cb:ee:ca:df";
};
linkConfig.Name = "et2";
};
"10-lan-dave" = {
matchConfig = {
Driver = "mlx4_en";
PermanentMACAddress = "00:02:c9:d5:b1:d6";
};
linkConfig = {
Name = "lan-dave";
MTUBytes = toString lib.my.c.home.hiMTU;
};
};
"10-et5" = {
matchConfig = {
Driver = "mlx4_en";
PermanentMACAddress = "00:02:c9:d5:b1:d7";
};
linkConfig.Name = "et5";
};
};
networks = {
"50-lan-jim" = {
matchConfig.Name = "lan-jim";
networkConfig.Bridge = "lan";
};
"50-lan-dave" = {
matchConfig.Name = "lan-dave";
networkConfig.Bridge = "lan";
};
};
};
my = {
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
};
server.enable = true;
deploy.node.hostname = "192.168.68.2";
};
};
};
};
}

View File

@@ -1,12 +1,13 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.kelder) domain prefixes;
inherit (lib.my.c.kelder) domain prefixes;
in
{
nixos.systems.kelder-acquisition = {
nixos.systems.kelder-acquisition = { config, ...}: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -44,7 +45,7 @@ in
};
users = {
groups.media.gid = lib.my.kelder.groups.media;
groups.media.gid = lib.my.c.kelder.groups.media;
users = {
"${config.my.user.config.name}".extraGroups = [ "media" ];

View File

@@ -104,12 +104,12 @@ in
}
{
From = lib.my.kelder.prefixes.all.v4;
From = lib.my.c.kelder.prefixes.all.v4;
Table = "main";
Priority = 100;
}
{
To = lib.my.kelder.prefixes.all.v4;
To = lib.my.c.kelder.prefixes.all.v4;
Table = "main";
Priority = 100;
}

View File

@@ -2,12 +2,13 @@
let
inherit (lib) mkForce mkMerge;
inherit (lib.my) net;
inherit (lib.my.kelder) domain prefixes;
inherit (lib.my.c.kelder) domain prefixes;
in
{
nixos.systems.kelder-spoder = {
nixos.systems.kelder-spoder = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
@@ -54,9 +55,9 @@ in
dnsResolver = "8.8.8.8";
};
certs = {
"${lib.my.kelder.domain}" = {
"${domain}" = {
extraDomainNames = [
"*.${lib.my.kelder.domain}"
"*.${domain}"
];
dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."kelder/cloudflare-credentials.conf".path;
@@ -65,7 +66,7 @@ in
};
users = {
groups.storage.gid = lib.my.kelder.groups.storage;
groups.storage.gid = lib.my.c.kelder.groups.storage;
users = {
nginx.extraGroups = [ "acme" ];
@@ -91,13 +92,12 @@ in
nextcloud = {
enable = true;
package = pkgs.nextcloud27;
package = pkgs.nextcloud28;
datadir = "/mnt/storage/nextcloud";
hostName = "cloud.${lib.my.kelder.domain}";
hostName = "cloud.${domain}";
https = true;
enableBrokenCiphersForSSE = false;
config = {
extraTrustedDomains = [ "cloud-local.${lib.my.kelder.domain}" ];
extraTrustedDomains = [ "cloud-local.${domain}" ];
adminpassFile = config.age.secrets."kelder/nextcloud-root.txt".path;
defaultPhoneRegion = "IE";
};

View File

@@ -2,6 +2,8 @@
let
inherit (builtins) mapAttrs;
inherit (lib) mkMerge mkIf mkDefault;
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
inherit (lib.my.c.kelder) domain;
in
{
config = {
@@ -37,43 +39,7 @@ in
# Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = ''
# NixOS provides a logrotate config that auto-compresses :)
log_format main
'$remote_addr - $remote_user [$time_local] $scheme "$host" "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
# optimisation
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# gzip
gzip on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types
application/atom+xml
application/javascript
application/json
application/xml
application/xml+rss
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
gzip_vary on;
# proxying
proxy_buffering off;
proxy_redirect off;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_http_version 1.1;
${lib.my.nginx.proxyHeaders}
${baseHttpConfig}
# caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=4g;
@@ -135,15 +101,15 @@ in
};
};
"monitor.${lib.my.kelder.domain}" = withAuth {
serverAliases = [ "monitor-local.${lib.my.kelder.domain}" ];
extraConfig = localRedirect "monitor-local.${lib.my.kelder.domain}";
"monitor.${domain}" = withAuth {
serverAliases = [ "monitor-local.${domain}" ];
extraConfig = localRedirect "monitor-local.${domain}";
locations = {
"/" = {
proxyPass = "http://${allAssignments.kelder.ctrs.ipv4.address}:19999";
extraConfig = ''
proxy_pass_request_headers on;
${lib.my.nginx.proxyHeaders}
${proxyHeaders}
proxy_set_header Connection "keep-alive";
proxy_store off;
@@ -155,8 +121,8 @@ in
};
};
"kontent.${lib.my.kelder.domain}" = {
serverAliases = [ "kontent-local.${lib.my.kelder.domain}" ];
"kontent.${domain}" = {
serverAliases = [ "kontent-local.${domain}" ];
locations = {
"/".proxyPass = "${acquisition}:8096";
"= /".return = "302 $scheme://$host/web/";
@@ -164,47 +130,47 @@ in
"/socket" = {
proxyPass = "${acquisition}:8096/socket";
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
};
};
};
"torrents.${lib.my.kelder.domain}" = withAuth {
serverAliases = [ "torrents-local.${lib.my.kelder.domain}" ];
extraConfig = localRedirect "torrents-local.${lib.my.kelder.domain}";
"torrents.${domain}" = withAuth {
serverAliases = [ "torrents-local.${domain}" ];
extraConfig = localRedirect "torrents-local.${domain}";
locations."/".proxyPass = "${acquisition}:9091";
};
"jackett.${lib.my.kelder.domain}" = withAuth {
serverAliases = [ "jackett-local.${lib.my.kelder.domain}" ];
extraConfig = localRedirect "jackett-local.${lib.my.kelder.domain}";
"jackett.${domain}" = withAuth {
serverAliases = [ "jackett-local.${domain}" ];
extraConfig = localRedirect "jackett-local.${domain}";
locations."/".proxyPass = "${acquisition}:9117";
};
"radarr.${lib.my.kelder.domain}" = withAuth {
serverAliases = [ "radarr-local.${lib.my.kelder.domain}" ];
extraConfig = localRedirect "radarr-local.${lib.my.kelder.domain}";
"radarr.${domain}" = withAuth {
serverAliases = [ "radarr-local.${domain}" ];
extraConfig = localRedirect "radarr-local.${domain}";
locations."/" = {
proxyPass = "${acquisition}:7878";
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
};
};
"sonarr.${lib.my.kelder.domain}" = withAuth {
serverAliases = [ "sonarr-local.${lib.my.kelder.domain}" ];
extraConfig = localRedirect "sonarr-local.${lib.my.kelder.domain}";
"sonarr.${domain}" = withAuth {
serverAliases = [ "sonarr-local.${domain}" ];
extraConfig = localRedirect "sonarr-local.${domain}";
locations."/" = {
proxyPass = "${acquisition}:8989";
proxyWebsockets = true;
extraConfig = lib.my.nginx.proxyHeaders;
extraConfig = proxyHeaders;
};
};
"cloud.${lib.my.kelder.domain}" = {
serverAliases = [ "cloud-local.${lib.my.kelder.domain}" ];
"cloud.${domain}" = {
serverAliases = [ "cloud-local.${domain}" ];
};
};
defaultsFor = mapAttrs (n: _: {
onlySSL = mkDefault true;
useACMEHost = mkDefault lib.my.kelder.domain;
useACMEHost = mkDefault domain;
kTLS = mkDefault true;
http2 = mkDefault true;
});

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.kelder) domain prefixes;
inherit (lib.my.c.kelder) domain prefixes;
in
{
imports = [ ./containers ];
@@ -14,7 +14,7 @@ in
assignments = {
estuary = {
ipv4 ={
address = net.cidr.host 0 lib.my.colony.prefixes.vip2;
address = net.cidr.host 0 lib.my.c.colony.prefixes.vip2;
mask = 32;
gateway = null;
};
@@ -54,7 +54,7 @@ in
efi.canTouchEfiVariables = true;
timeout = 5;
};
kernelPackages = pkgs.linuxKernel.packages.linux_6_1;
kernelPackages = lib.my.c.kernel.lts pkgs;
kernelModules = [ "kvm-intel" ];
kernelParams = [ "intel_iommu=on" ];
initrd = {
@@ -85,7 +85,7 @@ in
};
users = {
groups = with lib.my.kelder.groups; {
groups = with lib.my.c.kelder.groups; {
storage.gid = storage;
media.gid = media;
};
@@ -135,12 +135,14 @@ in
samba-wsdd.enable = true;
minecraft-server = {
enable = true;
package = pkgs.minecraftServers.vanilla-1-19;
enable = false;
package = pkgs.minecraftServers.vanilla-1-20;
declarative = true;
eula = true;
whitelist = {
devplayer0 = "6d7d971b-ce10-435b-85c5-c99c0d8d288c";
Elderlypug = "dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56";
shr3kas0ras = "1d366062-12c0-4e29-aba7-6ab5d8c6bb05";
};
serverProperties = {
motd = "Simpcraft";
@@ -150,7 +152,7 @@ in
};
networking = {
domain = lib.my.kelder.domain;
inherit domain;
};
system.nixos.distroName = "KelderOS";
@@ -180,7 +182,7 @@ in
{
wireguardPeerConfig = {
PublicKey = "bP1XUNxp9i8NLOXhgPaIaRzRwi5APbam44/xjvYcyjU=";
Endpoint = "estuary-vm.${lib.my.colony.domain}:${toString lib.my.kelder.vpn.port}";
Endpoint = "estuary-vm.${lib.my.c.colony.domain}:${toString lib.my.c.kelder.vpn.port}";
AllowedIPs = [ "0.0.0.0/0" ];
PersistentKeepalive = 25;
};
@@ -270,7 +272,6 @@ in
config.name = "kontent";
};
#deploy.generate.system.mode = "boot";
#deploy.node.hostname = "10.16.9.21";
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFvUdJshXkqmchEgkZDn5rgtZ1NO9vbd6Px+S6YioWi";
@@ -311,7 +312,7 @@ in
chain prerouting {
type filter hook prerouting priority mangle; policy accept;
ip daddr ${assignments.estuary.ipv4.address} ct state new ct mark set ${toString dnatMark}
ip saddr ${lib.my.kelder.prefixes.all.v4} ct mark != 0 meta mark set ct mark
ip saddr ${lib.my.c.kelder.prefixes.all.v4} ct mark != 0 meta mark set ct mark
}
chain output {
type filter hook output priority mangle; policy accept;
@@ -320,7 +321,7 @@ in
}
table inet nat {
chain postrouting {
ip saddr ${lib.my.kelder.prefixes.all.v4} oifname et1g0 masquerade
ip saddr ${lib.my.c.kelder.prefixes.all.v4} oifname et1g0 masquerade
}
}
'';

View File

@@ -25,23 +25,20 @@
efi.canTouchEfiVariables = true;
timeout = 10;
};
kernelPackages = pkgs.linuxKernel.packages.linux_6_5;
kernelPackages = lib.my.c.kernel.latest pkgs;
kernelModules = [ "kvm-intel" ];
kernelParams = [ "intel_iommu=on" ];
initrd = {
availableKernelModules = [ "nvme" "xhci_pci" "usb_storage" "usbhid" "thunderbolt" ];
luks = {
reusePassphrases = true;
devices = {
persist = {
device = "/dev/disk/by-uuid/27840c6f-445c-4b95-8c39-e69d07219f33";
allowDiscards = true;
preLVM = false;
};
home = {
device = "/dev/disk/by-uuid/c16c5038-7883-42c3-960a-a085a99364eb";
allowDiscards = true;
preLVM = false;
};
};
};
@@ -110,10 +107,19 @@
fprintd.enable = true;
blueman.enable = true;
tailscale = {
enable = true;
openFirewall = true;
};
};
programs = {
steam.enable = true;
wireshark = {
enable = true;
package = pkgs.wireshark-qt;
};
};
networking = {
@@ -169,6 +175,14 @@
packages = with pkgs; [ ];
};
programs = {
fish = {
shellAbbrs = {
tsup = "doas tailscale up --login-server=https://ts.nul.ie --accept-routes";
};
};
};
services = {
blueman-applet.enable = true;
};

View File

@@ -1,4 +1,4 @@
{ lib, pkgsFlakes, hmFlakes, inputs, pkgs', config, ... }:
{ self, lib, pkgsFlakes, hmFlakes, inputs, pkgs', config, ... }:
let
inherit (builtins) attrValues mapAttrs;
inherit (lib)
@@ -25,10 +25,14 @@ let
modules' = [ hmFlakes.${config'.home-manager}.nixosModule ] ++ (attrValues cfg.modules);
in
pkgsFlake.lib.nixosSystem {
# Import eval-config ourselves since the flake now force-sets lib
import "${pkgsFlake}/nixos/lib/eval-config.nix" {
# Gotta override lib here unforunately, eval-config.nix likes to import its own (unextended) lib. We explicitly
# don't pass pkgs so that it'll be imported with modularly applied config and overlays.
lib = pkgs.lib;
lib = pkgs.lib.extend (lib.my.versionOverlay { inherit self pkgsFlake; });
# Set to null since we pass modularly
system = null;
# Put the inputs in specialArgs to avoid infinite recursion when modules try to do imports
specialArgs = { inherit inputs pkgsFlakes pkgsFlake allAssignments; inherit (cfg) systems; };
@@ -51,7 +55,7 @@ let
pkgs' = allPkgs;
};
system.name = name;
system = { inherit name; };
networking = {
domain = let d = config'.assignments.internal.domain or null; in mkIf (d != null) (mkDefault' d);
hostName = mkDefault (config'.assignments.internal.name or name);
@@ -86,6 +90,8 @@ let
pkgsPath = toString pkgsFlakes.${config'.hmNixpkgs};
pkgs' = allPkgs;
};
home.enableNixpkgsReleaseCheck = false;
}
(homeStateVersion config'.home-manager)
];
@@ -100,6 +106,7 @@ let
altNames = mkOpt' (listOf str) [ ] "Extra names to assign.";
visible = mkBoolOpt' true "Whether or not this assignment should be visible.";
domain = mkOpt' (nullOr str) null "Domain for this assignment.";
mtu = mkOpt' (nullOr ints.unsigned) null "Interface MTU.";
ipv4 = {
address = mkOpt' net.types.ipv4 null "IPv4 address.";
mask = mkOpt' ints.u8 24 "Network mask.";
@@ -125,6 +132,9 @@ let
l2MeshOpts = with lib.types; { name, ... }: {
options = {
interface = mkOpt' str name "Name of VXLAN interface.";
ipv6 = mkBoolOpt' false "Whether this mesh's underlay operates over IPv6.";
baseMTU = mkOpt' ints.unsigned 1500 "Base MTU to calculate VXLAN MTU with.";
l3Overhead = mkOpt' ints.unsigned 40 "Overhead of L3 header (to calculate MTU).";
firewall = mkBoolOpt' true "Whether to generate firewall rules.";
vni = mkOpt' ints.unsigned 1 "VXLAN VNI.";
peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers.";
@@ -164,6 +174,7 @@ let
});
};
};
rendered = mkOpt' unspecified config'.configuration "Final NixOS modules system output.";
};
config = {
@@ -180,6 +191,11 @@ in
secretsPath = mkOpt' path null "Path to encrypted secret files.";
modules = mkOpt' (attrsOf commonOpts.moduleType) { } "NixOS modules to be exported by nixfiles.";
systems = mkOpt' (attrsOf (submodule systemOpts)) { } "NixOS systems to be exported by nixfiles.";
allAssignments = mkOption {
type = attrsOf (attrsOf (submodule assignmentOpts));
description = "All network assignments.";
readOnly = true;
};
vpns = {
l2 = mkOpt' (attrsOf (submodule l2MeshOpts)) { } "Layer 2 meshes.";
};
@@ -205,5 +221,9 @@ in
message = "Duplicate assignments: ${toString dupIPs}";
}
];
nixos = {
inherit allAssignments;
};
};
}

View File

@@ -1,8 +1,9 @@
{
nixos.systems.installer = {
nixos.systems.installer = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "unstable";
docCustom = false;
rendered = config.configuration.config.my.asISO;
configuration =
{ lib, pkgs, modulesPath, config, ... }:
@@ -31,7 +32,8 @@
};
isoImage = {
isoBaseName = "nixos-installer-devplayer0";
isoBaseName = "jackos-installer";
volumeID = "jackos-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}";
edition = "devplayer0";
appendToMenuLabel = " /dev/player0 Installer";
};
@@ -39,7 +41,7 @@
environment.sessionVariables = {
INSTALL_ROOT = installRoot;
};
users.users.root.openssh.authorizedKeys.keyFiles = [ lib.my.sshKeyFiles.deploy ];
users.users.root.openssh.authorizedKeys.keyFiles = [ lib.my.c.sshKeyFiles.deploy ];
home-manager.users.root = {
programs = {
starship.settings = {

View File

@@ -17,5 +17,8 @@
gui = ./gui.nix;
l2mesh = ./l2mesh.nix;
borgthin = ./borgthin.nix;
nvme = ./nvme;
spdk = ./spdk.nix;
librespeed = ./librespeed;
};
}

View File

@@ -1,4 +1,4 @@
{ lib, pkgs, extendModules, modulesPath, baseModules, options, config, ... }:
{ lib, pkgs, extendModules, modulesPath, options, config, ... }:
let
inherit (lib) recursiveUpdate mkOption mkDefault mkIf mkMerge flatten optional;
inherit (lib.my) mkBoolOpt' dummyOption;
@@ -18,6 +18,9 @@ let
"${modulesPath}/installer/cd-dvd/iso-image.nix"
allHardware
{
# Doesn't work right now... (missing /dev/root)
boot.initrd.systemd.enable = false;
isoImage = {
makeEfiBootable = true;
makeUsbBootable = true;
@@ -40,18 +43,28 @@ let
modules = flatten [
"${modulesPath}/installer/netboot/netboot.nix"
allHardware
({ pkgs, config, ... }: {
system.build.netbootArchive = pkgs.runCommand "netboot-${config.system.name}-archive.tar" { } ''
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.kernel}" \
-f "$out" "${config.system.boot.loader.kernelFile}"
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.netbootRamdisk}" \
-f "$out" initrd
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.netbootIpxeScript}" \
-f "$out" netboot.ipxe
'';
})
];
};
mkAsOpt = ext: desc: mkOption {
inherit (ext) type;
default = { };
mkAsOpt = ext: desc: with lib.types; mkOption {
type = unspecified;
default = ext;
visible = "shallow";
description = "Configuration as ${desc}.";
};
in
{
options = with lib.types; {
options = {
my = {
build = {
isDevVM = mkBoolOpt' false "Whether the system is a development VM.";
@@ -79,6 +92,7 @@ in
};
isoImage = {
isoBaseName = dummyOption;
volumeID = dummyOption;
edition = dummyOption;
appendToMenuLabel = dummyOption;
};
@@ -92,10 +106,11 @@ in
my = {
buildAs = {
# The meta.mainProgram should probably be set upstream but oh well...
devVM = recursiveUpdate config.my.asDevVM.system.build.vm { meta.mainProgram = "run-${config.system.name}-vm"; };
iso = config.my.asISO.system.build.isoImage;
container = config.my.asContainer.system.build.toplevel;
kexecTree = config.my.asKexecTree.system.build.kexecTree;
devVM = recursiveUpdate config.my.asDevVM.config.system.build.vm { meta.mainProgram = "run-${config.system.name}-vm"; };
iso = config.my.asISO.config.system.build.isoImage;
container = config.my.asContainer.config.system.build.toplevel;
kexecTree = config.my.asKexecTree.config.system.build.kexecTree;
netbootArchive = config.my.asKexecTree.config.system.build.netbootArchive;
};
};
};

View File

@@ -1,7 +1,7 @@
{ lib, pkgs, pkgs', inputs, config, ... }:
let
inherit (lib) mkIf mkDefault mkMerge;
inherit (lib.my) mkBoolOpt' dummyOption;
inherit (lib.my) mkDefault';
in
{
options = with lib.types; {
@@ -12,6 +12,7 @@ in
inputs.impermanence.nixosModule
inputs.ragenix.nixosModules.age
inputs.sharry.nixosModules.default
inputs.attic.nixosModules.atticd
];
config = mkMerge [
@@ -40,14 +41,13 @@ in
nix = {
package = pkgs'.mine.nix;
settings = {
settings = with lib.my.c.nix; {
trusted-users = [ "@wheel" ];
experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
substituters = [
#"https://nix-cache.nul.ie"
"https://cache.nixos.org"
];
trusted-public-keys = lib.my.nix.cacheKeys;
extra-substituters = cache.substituters;
extra-trusted-public-keys = cache.keys;
connect-timeout = 5;
fallback = true;
};
registry = {
pkgs = {
@@ -88,12 +88,13 @@ in
boot = {
# Use latest LTS release by default
kernelPackages = mkDefault pkgs.linuxKernel.packages.linux_6_1;
kernelPackages = mkDefault (lib.my.c.kernel.lts pkgs);
kernel = {
sysctl = {
"net.ipv6.route.max_size" = mkDefault 16384;
};
};
loader = {
efi = {
efiSysMountPoint = mkDefault "/boot";
@@ -111,12 +112,24 @@ in
memtest86.enable = mkDefault true;
};
};
initrd = {
systemd = {
enable = mkDefault true;
emergencyAccess = mkDefault true;
};
services.lvm.enable = mkDefault true;
};
};
system = {
nixos = {
distroName = mkDefault' "JackOS";
};
};
environment.systemPackages = with pkgs; mkMerge [
[
bash-completion
vim
git
unzip
]
@@ -129,6 +142,7 @@ in
fish.enable = mkDefault true;
# TODO: This is expecting to look up the channel for the database...
command-not-found.enable = mkDefault false;
vim.defaultEditor = true;
};
services = {
@@ -142,6 +156,7 @@ in
font-name=SauceCodePro Nerd Font Mono
'';
};
getty.greetingLine = mkDefault' ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
openssh = {
enable = mkDefault true;

View File

@@ -208,7 +208,17 @@ in
mkdir -p -m 0755 "$root"/sbin "$root"/etc
touch "$root"/etc/os-release
ln -sf "${containerSystem}"/init "$root"/sbin/init
${if system == sysProfile then ''
if [ -e "${sysProfile}"/prepare-root ]; then
initSource="${containerSystem}"/prepare-root
else
initSource="${containerSystem}"/init
fi
ln -sf "$initSource" "$root"/sbin/init
'' else ''
ln -sf "${containerSystem}/prepare-root" "$root"/sbin/init
''}
'';
postStop =
''

View File

@@ -15,16 +15,20 @@ let
# Based on https://github.com/serokell/deploy-rs/blob/master/flake.nix
nixosActivate = cfg': base: (pkgs.deploy-rs.lib.activate.custom // {
dryActivate = "$PROFILE/bin/switch-to-configuration dry-activate";
boot = "$PROFILE/bin/switch-to-configuration boot";
boot = ''
$PROFILE/bin/switch-to-configuration boot
${keepGensSnippet "$PROFILE" cfg'.keepGenerations}
'';
}) base.config.system.build.toplevel ''
# work around https://github.com/NixOS/nixpkgs/issues/73404
cd /tmp
"$PROFILE"/bin/switch-to-configuration ${cfg'.mode}
"$PROFILE"/bin/switch-to-configuration switch
# https://github.com/serokell/deploy-rs/issues/31
${with base.config.boot.loader;
optionalString ((cfg'.mode == "switch" || cfg'.mode == "boot") && systemd-boot.enable)
optionalString systemd-boot.enable
"sed -i '/^default /d' ${efi.efiSysMountPoint}/loader/loader.conf"}
${keepGensSnippet "$PROFILE" cfg'.keepGenerations}
@@ -59,7 +63,11 @@ let
{
name = "container-${n}";
value = {
path = pkgs.deploy-rs.lib.activate.custom ctrConfig.my.buildAs.container ''
path = (pkgs.deploy-rs.lib.activate.custom // {
boot = ''
echo "Next systemd-nspawn@${n}.service restart / reload will load config"
'';
}) ctrConfig.my.buildAs.container ''
source ${systemdUtil}/bin/systemd-util.sh
${if c.hotReload then ''
if (! systemctl show -p ActiveState systemd-nspawn@${n} | grep -q "ActiveState=active") || \
@@ -86,7 +94,7 @@ in
options.my.deploy = with lib.types; {
authorizedKeys = {
keys = mkOpt' (listOf singleLineStr) [ ] "SSH public keys to add to the default deployment user.";
keyFiles = mkOpt' (listOf path) [ lib.my.sshKeyFiles.deploy ] "SSH public key files to add to the default deployment user.";
keyFiles = mkOpt' (listOf path) [ lib.my.c.sshKeyFiles.deploy ] "SSH public key files to add to the default deployment user.";
};
enable = mkBoolOpt' true "Whether to expose deploy-rs configuration for this system.";

View File

@@ -1,6 +1,9 @@
{ lib, options, config, ... }:
let
inherit (lib) optionalString concatStringsSep concatMapStringsSep optionalAttrs mkIf mkDefault mkMerge mkOverride;
inherit (builtins) typeOf attrNames;
inherit (lib)
optionalString concatStringsSep concatMapStringsSep mapAttrsToList optionalAttrs mkIf
mkDefault mkMerge mkOverride;
inherit (lib.my) isIPv6 mkOpt' mkBoolOpt';
allowICMP = ''
@@ -63,8 +66,8 @@ in
nat = with options.networking.nat; {
enable = mkBoolOpt' true "Whether to enable IP forwarding and NAT.";
inherit externalInterface externalIP;
forwardPorts = mkOpt' (listOf (submodule forwardOpts)) [ ] "List of port forwards.";
inherit externalInterface;
forwardPorts = mkOpt' (either (listOf (submodule forwardOpts)) (attrsOf (listOf (submodule forwardOpts)))) [ ] "IPv4 port forwards";
};
};
@@ -131,9 +134,15 @@ in
chain prerouting {
type nat hook prerouting priority dstnat;
}
chain output {
type nat hook output priority dstnat;
}
chain postrouting {
type nat hook postrouting priority srcnat;
}
chain input {
type nat hook input priority srcnat;
}
}
${cfg.extraRules}
@@ -141,11 +150,16 @@ in
};
};
}
(mkIf cfg.nat.enable {
(mkIf cfg.nat.enable (
let
iifForward = typeOf cfg.nat.forwardPorts == "list" && cfg.nat.forwardPorts != [ ];
dipForward = typeOf cfg.nat.forwardPorts == "set" && cfg.nat.forwardPorts != { };
in
{
assertions = [
{
assertion = with cfg.nat; (forwardPorts != [ ]) -> (externalInterface != null);
message = "my.firewall.nat.forwardPorts requires my.firewall.nat.external{Interface,IP}";
assertion = with cfg.nat; iifForward -> (externalInterface != null);
message = "my.firewall.nat.forwardPorts as list requires my.firewall.nat.externalInterface";
}
];
@@ -168,43 +182,75 @@ in
my.firewall.extraRules =
let
inherit (lib.my.nft) natFilterChain dnatChain;
ipK = ip: "ip${optionalString (isIPv6 ip) "6"}";
makeFilter = f:
let
v6 = isIPv6 f.dst;
in
"ip${optionalString v6 "6"} daddr ${f.dst} ${f.proto} dport ${toString f.dstPort} accept";
"${ipK f.dst} daddr ${f.dst} ${f.proto} dport ${toString f.dstPort} accept";
makeForward = f:
let
v6 = isIPv6 f.dst;
in
"${f.proto} dport ${toString f.port} dnat ip${optionalString v6 "6"} to ${f.dst}:${toString f.dstPort}";
"${f.proto} dport ${toString f.port} dnat ${ipK f.dst} to ${f.dst}:${toString f.dstPort}";
dnatJumps = ''
${optionalString
iifForward
"iifname ${cfg.nat.externalInterface} jump iif-port-forward"}
${optionalString
dipForward
(concatMapStringsSep "\n " (ip: "${ipK ip} daddr ${ip} jump ${dnatChain ip}") (attrNames cfg.nat.forwardPorts))}
'';
in
''
table inet filter {
chain filter-port-forwards {
${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts}
return
}
${optionalString iifForward ''
chain filter-iif-port-forwards {
${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts}
return
}
''}
${optionalString
dipForward
(concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
chain ${natFilterChain ip} {
${concatMapStringsSep "\n " makeFilter fs}
return
}
'') cfg.nat.forwardPorts))}
chain forward {
${optionalString
(cfg.nat.externalInterface != null)
"iifname ${cfg.nat.externalInterface} jump filter-port-forwards"}
iifForward
"iifname ${cfg.nat.externalInterface} jump filter-iif-port-forwards"}
${optionalString
dipForward
(concatMapStringsSep "\n " (ip: "jump ${natFilterChain ip}") (attrNames cfg.nat.forwardPorts))}
}
}
table inet nat {
chain port-forward {
${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts}
return
}
${optionalString iifForward ''
chain iif-port-forward {
${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts}
return
}
''}
${optionalString
dipForward
(concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
chain ${dnatChain ip} {
${concatMapStringsSep "\n " makeForward fs}
return
}
'') cfg.nat.forwardPorts))}
chain prerouting {
${optionalString
(cfg.nat.externalInterface != null)
"${if (cfg.nat.externalIP != null) then "ip daddr ${cfg.nat.externalIP}" else "iifname ${cfg.nat.externalInterface}"} jump port-forward"}
${dnatJumps}
}
chain output {
${dnatJumps}
}
}
'';
})
}))
]);
meta.buildDocsInSandbox = false;

View File

@@ -57,7 +57,7 @@ in
programs.dconf.enable = true;
fonts.fonts = with pkgs; [
fonts.packages = with pkgs; [
dejavu_fonts
freefont_ttf
gyre-fonts # TrueType substitutes for standard PostScript fonts
@@ -69,8 +69,19 @@ in
xdg = {
portal = {
enable = true;
extraPortals = with pkgs; [
xdg-desktop-portal-gtk
];
# For sway
wlr.enable = true;
configPackages = [
(pkgs.writeTextDir "share/xdg-desktop-portal/sway-portals.conf" ''
[preferred]
default=gtk
org.freedesktop.impl.portal.Screenshot=wlr
org.freedesktop.impl.portal.ScreenCast=wlr
'')
];
};
};
};

View File

@@ -1,7 +1,8 @@
{ lib, pkgs, config, vpns, ... }:
{ lib, config, vpns, ... }:
let
inherit (lib) optionalString mapAttrsToList concatStringsSep filterAttrs mkIf mkMerge;
inherit (lib.my) isIPv6;
inherit (builtins) any attrValues;
inherit (lib) optionalString mapAttrsToList concatStringsSep concatMapStringsSep filterAttrs mkIf mkMerge;
inherit (lib.my) isIPv6 mkOpt';
vxlanPort = 4789;
@@ -24,38 +25,34 @@ let
Local = ownAddr;
MacLearning = true;
DestinationPort = vxlanPort;
PortRange = "${toString vxlanPort}-${toString (vxlanPort + 1)}";
Independent = true;
};
};
links."20-l2mesh-${name}" = {
matchConfig.Name = mesh.interface;
# TODO: ipv6? ipsec?
linkConfig.MTUBytes = "1450";
};
networks."90-l2mesh-${name}" = {
matchConfig.Name = mesh.interface;
extraConfig = concatStringsSep "\n" (mapAttrsToList (n: peer: ''
[BridgeFDB]
MACAddress=00:00:00:00:00:00
Destination=${peer.addr}
'') otherPeers);
linkConfig.MTUBytes =
let
espOverhead =
if (!mesh.security.enable) then 0
else
# SPI + seq + IV + pad / header + ICV
4 + 4 + (if mesh.security.encrypt then 8 else 0) + 2 + 16;
# UDP + VXLAN + Ethernet + L3 (IPv4/IPv6)
overhead = espOverhead + 8 + 8 + 14 + mesh.l3Overhead;
in
toString (mesh.baseMTU - overhead);
bridgeFDBs = mapAttrsToList (n: peer: {
bridgeFDBConfig = {
MACAddress = "00:00:00:00:00:00";
Destination = peer.addr;
};
}) otherPeers;
};
};
mkLibreswanConfig = name: mesh: with info mesh; {
enable = true;
# TODO: finish this...
connections."l2mesh-${name}" = ''
keyexchange=ike
type=transport
left=${ownAddr}
auto=start
phase2=esp
ikev2=yes
'';
};
vxlanAllow = vni: "udp dport ${toString vxlanPort} @th,96,24 ${toString vni} accept";
mkFirewallConfig = name: mesh: with info mesh;
let
netProto = if (isIPv6 ownAddr) then "ip6" else "ip";
@@ -63,8 +60,11 @@ let
''
table inet filter {
chain l2mesh-${name} {
${optionalString mesh.security.enable "meta l4proto esp accept"}
udp dport ${toString vxlanPort} @th,96,24 ${toString mesh.vni} accept
${optionalString mesh.security.enable ''
udp dport isakmp accept
meta l4proto esp accept
''}
${optionalString (!mesh.security.enable) (vxlanAllow mesh.vni)}
return
}
chain input {
@@ -72,12 +72,63 @@ let
}
}
'';
mkLibreswanConfig = name: mesh: with info mesh; {
enable = true;
connections = mkMerge (mapAttrsToList
(pName: peer: {
"l2mesh-${name}-${pName}" = ''
keyexchange=ike
hostaddrfamily=ipv${if mesh.ipv6 then "6" else "4"}
type=transport
left=${ownAddr}
leftprotoport=udp/${toString vxlanPort}
right=${peer.addr}
rightprotoport=udp/${toString vxlanPort}
rightupdown=
auto=start
authby=secret
phase2=esp
esp=${if mesh.security.encrypt then "aes_gcm256" else "null-sha256"}
ikev2=yes
modecfgpull=no
'';
})
otherPeers);
};
genSecrets = name: mesh: with info mesh; concatMapStringsSep "\n" (p: ''
echo "${ownAddr} ${p.addr} : PSK \"$(< "${config.my.vpns.l2.pskFiles.${name}}")\"" >> /run/l2mesh.secrets
'') (attrValues otherPeers);
anySecurity = any (c: c.security.enable) (attrValues memberMeshes);
in
{
options = {
my.vpns.l2 = with lib.types; {
pskFiles = mkOpt' (attrsOf str) { } "PSK files for secured meshes.";
};
};
config = {
systemd.network = mkMerge (mapAttrsToList mkNetConfig memberMeshes);
# TODO: finish this...
#services.libreswan = mkMerge (mapAttrsToList mkLibreswanConfig (filterAttrs (_: c: c.security.enable) memberMeshes));
environment.etc."ipsec.d/l2mesh.secrets" = mkIf anySecurity {
source = "/run/l2mesh.secrets";
};
systemd.services.ipsec = mkIf anySecurity {
preStart = ''
oldUmask="$(umask)"
umask 006
> /run/l2mesh.secrets
${concatStringsSep "\n" (mapAttrsToList genSecrets memberMeshes)}
umask "$oldUmask"
'';
};
services.libreswan = mkMerge (mapAttrsToList mkLibreswanConfig (filterAttrs (_: c: c.security.enable) memberMeshes));
my.firewall.extraRules = concatStringsSep "\n" (mapAttrsToList mkFirewallConfig (filterAttrs (_: c: c.firewall) memberMeshes));
};
}

View File

@@ -0,0 +1,76 @@
{ lib, pkgs, config, ... }:
let
inherit (builtins) toJSON;
inherit (lib) mkOption mkMerge mkIf mkDefault;
inherit (lib.my) mkOpt' mkBoolOpt';
cfg = config.my.librespeed;
serversConf = map (s: s // {
dlURL = "backend/garbage";
ulURL = "backend/empty";
pingURL = "backend/empty";
getIpURL = "backend/getIP";
}) cfg.frontend.servers;
frontendTree = pkgs.runCommand "librespeed-frontend" {
speedtestServers = toJSON serversConf;
} ''
mkdir "$out"
cp "${pkgs.librespeed-go}"/assets/* "$out"/
substitute ${./index.html} "$out"/index.html --subst-var speedtestServers
'';
backendConf = pkgs.writers.writeTOML "librespeed.toml" cfg.backend.settings;
generateBackendSettings = base: dst: if (cfg.backend.extraSettingsFile != null) then ''
oldUmask="$(umask)"
umask 006
cat "${base}" "${cfg.backend.extraSettingsFile}" > "${dst}"
umask "$oldUmask"
'' else ''
cp "${base}" "${dst}"
'';
in
{
options.my.librespeed = with lib.types; {
frontend = {
servers = mkOpt' (listOf (attrsOf unspecified)) { } "Server configs.";
webroot = mkOption {
description = "Frontend webroot.";
type = package;
readOnly = true;
};
};
backend = {
enable = mkBoolOpt' false "Whether to enable librespeed backend.";
settings = mkOpt' (attrsOf unspecified) { } "Backend settings.";
extraSettingsFile = mkOpt' (nullOr str) null "Extra settings file.";
};
};
config = mkMerge [
(mkIf (cfg.frontend.servers != { }) {
my.librespeed.frontend.webroot = frontendTree;
})
(mkIf cfg.backend.enable {
my.librespeed.backend.settings = {
assets_path = frontendTree;
database_type = mkDefault "bolt";
database_file = mkDefault "/var/lib/librespeed-go/speedtest.db";
};
systemd.services.librespeed = {
description = "LibreSpeed Go backend";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
preStart = generateBackendSettings backendConf "/run/librespeed-go/settings.toml";
serviceConfig = {
ExecStart = "${pkgs.librespeed-go}/bin/speedtest -c /run/librespeed-go/settings.toml";
RuntimeDirectory = "librespeed-go";
StateDirectory = "librespeed-go";
};
wantedBy = [ "multi-user.target" ];
};
})
];
}

View File

@@ -0,0 +1,491 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" href="favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
<meta charset="UTF-8" />
<script type="text/javascript" src="speedtest.js"></script>
<script type="text/javascript">
function I(i){return document.getElementById(i);}
//LIST OF TEST SERVERS. See documentation for details if needed
var SPEEDTEST_SERVERS=@speedtestServers@;
// var SPEEDTEST_SERVERS=[
// { //this server doesn't actually exist, remove it
// name:"Example Server 1", //user friendly name for the server
// server:"//test1.mydomain.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
// dlURL:"backend/garbage.php", //path to download test on this server (garbage.php or replacement)
// ulURL:"backend/empty.php", //path to upload test on this server (empty.php or replacement)
// pingURL:"backend/empty.php", //path to ping/jitter test on this server (empty.php or replacement)
// getIpURL:"backend/getIP.php" //path to getIP on this server (getIP.php or replacement)
// },
// { //this server doesn't actually exist, remove it
// name:"Example Server 2", //user friendly name for the server
// server:"//test2.example.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
// dlURL:"garbage.php", //path to download test on this server (garbage.php or replacement)
// ulURL:"empty.php", //path to upload test on this server (empty.php or replacement)
// pingURL:"empty.php", //path to ping/jitter test on this server (empty.php or replacement)
// getIpURL:"getIP.php" //path to getIP on this server (getIP.php or replacement)
// }
// //add other servers here, comma separated
// ];
//INITIALIZE SPEEDTEST
var s=new Speedtest(); //create speed test object
s.setParameter("telemetry_level","basic"); //enable telemetry
//SERVER AUTO SELECTION
function initServers(){
var noServersAvailable=function(){
I("message").innerHTML="No servers available";
}
var runServerSelect=function(){
s.selectServer(function(server){
if(server!=null){ //at least 1 server is available
I("loading").className="hidden"; //hide loading message
//populate server list for manual selection
for(var i=0;i<SPEEDTEST_SERVERS.length;i++){
if(SPEEDTEST_SERVERS[i].pingT==-1) continue;
var option=document.createElement("option");
option.value=i;
option.textContent=SPEEDTEST_SERVERS[i].name;
if(SPEEDTEST_SERVERS[i]===server) option.selected=true;
I("server").appendChild(option);
}
//show test UI
I("testWrapper").className="visible";
initUI();
}else{ //no servers are available, the test cannot proceed
noServersAvailable();
}
});
}
if(typeof SPEEDTEST_SERVERS === "string"){
//need to fetch list of servers from specified URL
s.loadServerList(SPEEDTEST_SERVERS,function(servers){
if(servers==null){ //failed to load server list
noServersAvailable();
}else{ //server list loaded
SPEEDTEST_SERVERS=servers;
runServerSelect();
}
});
}else{
//hardcoded server list
s.addTestPoints(SPEEDTEST_SERVERS);
runServerSelect();
}
}
var meterBk=/Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent)?"#EAEAEA":"#80808040";
var dlColor="#6060AA",
ulColor="#616161";
var progColor=meterBk;
//CODE FOR GAUGES
function drawMeter(c,amount,bk,fg,progress,prog){
var ctx=c.getContext("2d");
var dp=window.devicePixelRatio||1;
var cw=c.clientWidth*dp, ch=c.clientHeight*dp;
var sizScale=ch*0.0055;
if(c.width==cw&&c.height==ch){
ctx.clearRect(0,0,cw,ch);
}else{
c.width=cw;
c.height=ch;
}
ctx.beginPath();
ctx.strokeStyle=bk;
ctx.lineWidth=12*sizScale;
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,Math.PI*0.1);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle=fg;
ctx.lineWidth=12*sizScale;
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,amount*Math.PI*1.2-Math.PI*1.1);
ctx.stroke();
if(typeof progress !== "undefined"){
ctx.fillStyle=prog;
ctx.fillRect(c.width*0.3,c.height-16*sizScale,c.width*0.4*progress,4*sizScale);
}
}
function mbpsToAmount(s){
return 1-(1/(Math.pow(1.3,Math.sqrt(s))));
}
function format(d){
d=Number(d);
if(d<10) return d.toFixed(2);
if(d<100) return d.toFixed(1);
return d.toFixed(0);
}
//UI CODE
var uiData=null;
function startStop(){
if(s.getState()==3){
//speed test is running, abort
s.abort();
data=null;
I("startStopBtn").className="";
I("server").disabled=false;
initUI();
}else{
//test is not running, begin
I("startStopBtn").className="running";
I("shareArea").style.display="none";
I("server").disabled=true;
s.onupdate=function(data){
uiData=data;
};
s.onend=function(aborted){
I("startStopBtn").className="";
I("server").disabled=false;
updateUI(true);
if(!aborted){
//if testId is present, show sharing panel, otherwise do nothing
try{
var testId=uiData.testId;
if(testId!=null){
var shareURL=window.location.href.substring(0,window.location.href.lastIndexOf("/"))+"/results/?id="+testId;
I("resultsImg").src=shareURL;
I("resultsURL").value=shareURL;
I("testId").innerHTML=testId;
I("shareArea").style.display="";
}
}catch(e){}
}
};
s.start();
}
}
//this function reads the data sent back by the test and updates the UI
function updateUI(forced){
if(!forced&&s.getState()!=3) return;
if(uiData==null) return;
var status=uiData.testState;
I("ip").textContent=uiData.clientIp;
I("dlText").textContent=(status==1&&uiData.dlStatus==0)?"...":format(uiData.dlStatus);
drawMeter(I("dlMeter"),mbpsToAmount(Number(uiData.dlStatus*(status==1?oscillate():1))),meterBk,dlColor,Number(uiData.dlProgress),progColor);
I("ulText").textContent=(status==3&&uiData.ulStatus==0)?"...":format(uiData.ulStatus);
drawMeter(I("ulMeter"),mbpsToAmount(Number(uiData.ulStatus*(status==3?oscillate():1))),meterBk,ulColor,Number(uiData.ulProgress),progColor);
I("pingText").textContent=format(uiData.pingStatus);
I("jitText").textContent=format(uiData.jitterStatus);
}
function oscillate(){
return 1+0.02*Math.sin(Date.now()/100);
}
//update the UI every frame
window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||(function(callback,element){setTimeout(callback,1000/60);});
function frame(){
requestAnimationFrame(frame);
updateUI();
}
frame(); //start frame loop
//function to (re)initialize UI
function initUI(){
drawMeter(I("dlMeter"),0,meterBk,dlColor,0);
drawMeter(I("ulMeter"),0,meterBk,ulColor,0);
I("dlText").textContent="";
I("ulText").textContent="";
I("pingText").textContent="";
I("jitText").textContent="";
I("ip").textContent="";
}
</script>
<style type="text/css">
html,body{
border:none; padding:0; margin:0;
background:#FFFFFF;
color:#202020;
}
body{
text-align:center;
font-family:"Roboto",sans-serif;
}
h1{
color:#404040;
}
#loading{
background-color:#FFFFFF;
color:#404040;
text-align:center;
}
span.loadCircle{
display:inline-block;
width:2em;
height:2em;
vertical-align:middle;
background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAP1BMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZyFzwnAAAAFHRSTlMAEvRFvX406baecwbf0casimhSHyiwmqgAAADpSURBVHja7dbJbQMxAENRahnN5lkc//5rDRAkDeRgHszXgACJoKiIiIiIiIiIiIiIiIiIiIj4HHspsrpAVhdVVguzrA4OWc10WcEqpwKbnBo0OU1Q5NSpsoJFTgOecrrdEag85DRgktNqfoEdTjnd7hrEHMEJvmRUYJbTYk5Agy6nau6Abp5Cm7mDBtRdPi9gyKdU7w4p1fsLvyqs8hl4z9/w3n/Hmr9WoQ65lAU4d7lMYOz//QboRR5jBZibLMZdAR6O/Vfa1PlxNr3XdS3HzK/HVPRu/KnLs8iAOh993VpRRERERMT/fAN60wwWaVyWwAAAAABJRU5ErkJggg==');
background-size:2em 2em;
margin-right:0.5em;
animation: spin 0.6s linear infinite;
}
@keyframes spin{
0%{transform:rotate(0deg);}
100%{transform:rotate(359deg);}
}
#startStopBtn{
display:inline-block;
margin:0 auto;
color:#6060AA;
background-color:rgba(0,0,0,0);
border:0.15em solid #6060FF;
border-radius:0.3em;
transition:all 0.3s;
box-sizing:border-box;
width:8em; height:3em;
line-height:2.7em;
cursor:pointer;
box-shadow: 0 0 0 rgba(0,0,0,0.1), inset 0 0 0 rgba(0,0,0,0.1);
}
#startStopBtn:hover{
box-shadow: 0 0 2em rgba(0,0,0,0.1), inset 0 0 1em rgba(0,0,0,0.1);
}
#startStopBtn.running{
background-color:#FF3030;
border-color:#FF6060;
color:#FFFFFF;
}
#startStopBtn:before{
content:"Start";
}
#startStopBtn.running:before{
content:"Abort";
}
#serverArea{
margin-top:1em;
}
#server{
font-size:1em;
padding:0.2em;
}
#test{
margin-top:2em;
margin-bottom:12em;
}
div.testArea{
display:inline-block;
width:16em;
height:12.5em;
position:relative;
box-sizing:border-box;
}
div.testArea2{
display:inline-block;
width:14em;
height:7em;
position:relative;
box-sizing:border-box;
text-align:center;
}
div.testArea div.testName{
position:absolute;
top:0.1em; left:0;
width:100%;
font-size:1.4em;
z-index:9;
}
div.testArea2 div.testName{
display:block;
text-align:center;
font-size:1.4em;
}
div.testArea div.meterText{
position:absolute;
bottom:1.55em; left:0;
width:100%;
font-size:2.5em;
z-index:9;
}
div.testArea2 div.meterText{
display:inline-block;
font-size:2.5em;
}
div.meterText:empty:before{
content:"0.00";
}
div.testArea div.unit{
position:absolute;
bottom:2em; left:0;
width:100%;
z-index:9;
}
div.testArea2 div.unit{
display:inline-block;
}
div.testArea canvas{
position:absolute;
top:0; left:0; width:100%; height:100%;
z-index:1;
}
div.testGroup{
display:block;
margin: 0 auto;
}
#shareArea{
width:95%;
max-width:40em;
margin:0 auto;
margin-top:2em;
}
#shareArea > *{
display:block;
width:100%;
height:auto;
margin: 0.25em 0;
}
#privacyPolicy{
position:fixed;
top:2em;
bottom:2em;
left:2em;
right:2em;
overflow-y:auto;
width:auto;
height:auto;
box-shadow:0 0 3em 1em #000000;
z-index:999999;
text-align:left;
background-color:#FFFFFF;
padding:1em;
}
a.privacy{
text-align:center;
font-size:0.8em;
color:#808080;
padding: 0 3em;
}
div.closePrivacyPolicy {
width: 100%;
text-align: center;
}
div.closePrivacyPolicy a.privacy {
padding: 1em 3em;
}
@media all and (max-width:40em){
body{
font-size:0.8em;
}
}
div.visible{
animation: fadeIn 0.4s;
display:block;
}
div.hidden{
animation: fadeOut 0.4s;
display:none;
}
@keyframes fadeIn{
0%{
opacity:0;
}
100%{
opacity:1;
}
}
@keyframes fadeOut{
0%{
display:block;
opacity:1;
}
100%{
display:block;
opacity:0;
}
}
</style>
<title>/dev/player0's speedtest</title>
</head>
<body onload="initServers()">
<h1>/dev/player0's speedtest</h1>
<div id="loading" class="visible">
<p id="message"><span class="loadCircle"></span>Selecting a server...</p>
</div>
<div id="testWrapper" class="hidden">
<div id="startStopBtn" onclick="startStop()"></div><br/>
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
<div id="serverArea">
Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
</div>
<div id="test">
<div class="testGroup">
<div class="testArea2">
<div class="testName">Ping</div>
<div id="pingText" class="meterText" style="color:#AA6060"></div>
<div class="unit">ms</div>
</div>
<div class="testArea2">
<div class="testName">Jitter</div>
<div id="jitText" class="meterText" style="color:#AA6060"></div>
<div class="unit">ms</div>
</div>
</div>
<div class="testGroup">
<div class="testArea">
<div class="testName">Download</div>
<canvas id="dlMeter" class="meter"></canvas>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<canvas id="ulMeter" class="meter"></canvas>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div id="ipArea">
<span id="ip"></span>
</div>
<div id="shareArea" style="display:none">
<h3>Share results</h3>
<p>Test ID: <span id="testId"></span></p>
<input type="text" value="" id="resultsURL" readonly="readonly" onclick="this.select();this.focus();this.select();document.execCommand('copy');alert('Link copied')"/>
<img src="" id="resultsImg" />
</div>
</div>
<a href="https://github.com/librespeed/speedtest">Source code</a>
</div>
<div id="privacyPolicy" style="display:none">
<h2>Privacy Policy</h2>
<p>This HTML5 speed test server is configured with telemetry enabled.</p>
<h4>What data we collect</h4>
<p>
At the end of the test, the following data is collected and stored:
<ul>
<li>Test ID</li>
<li>Time of testing</li>
<li>Test results (download and upload speed, ping and jitter)</li>
<li>IP address</li>
<li>ISP information</li>
<li>Approximate location (inferred from IP address, not GPS)</li>
<li>User agent and browser locale</li>
<li>Test log (contains no personal information)</li>
</ul>
</p>
<h4>How we use the data</h4>
<p>
Data collected through this service is used to:
<ul>
<li>Allow sharing of test results (sharable image for forums, etc.)</li>
<li>To improve the service offered to you (for instance, to detect problems on our side)</li>
</ul>
No personal information is disclosed to third parties.
</p>
<h4>Your consent</h4>
<p>
By starting the test, you consent to the terms of this privacy policy.
</p>
<h4>Data removal</h4>
<p>
If you want to have your information deleted, you need to provide either the ID of the test or your IP address. This is the only way to identify your data, without this information we won't be able to comply with your request.<br/><br/>
Contact this email address for all deletion requests: <a href="mailto:dev@nul.ie">dev@nul.ie</a>.
</p>
<br/><br/>
<div class="closePrivacyPolicy">
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a>
</div>
<br/>
</div>
</body>
</html>

View File

@@ -6,7 +6,7 @@ in
config = mkMerge [
{
networking = {
domain = mkDefault "int.${lib.my.pubDomain}";
domain = mkDefault "int.${lib.my.c.pubDomain}";
useDHCP = false;
enableIPv6 = mkDefault true;
useNetworkd = mkDefault true;

View File

@@ -0,0 +1,58 @@
{ lib, pkgs, config, ... }:
let
inherit (lib) mkIf;
inherit (lib.my) mkOpt';
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}";
etc = prefix: {
"${prefix}nvme/hostnqn".text = hostNQN;
"${prefix}nvme/hostid".text = cfg.uuid;
};
in
{
options.my.nvme = with lib.types; {
uuid = mkOpt' (nullOr str) null "NVMe host ID";
boot = {
nqn = mkOpt' (nullOr str) null "NQN to connect to on boot";
address = mkOpt' str null "Address of NVMe-oF target.";
};
};
config = mkIf (cfg.uuid != null) {
environment = {
systemPackages = [
nvme-cli
];
etc = etc "";
};
boot.initrd.systemd = mkIf (cfg.boot.nqn != null) {
contents = etc "/etc/";
extraBin.nvme = "${nvme-cli}/bin/nvme";
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 = "${nvme-cli}/bin/nvme connect -t rdma -a ${cfg.boot.address} -n ${cfg.boot.nqn}";
Restart = "on-failure";
RestartSec = 10;
};
wantedBy = [ "initrd-root-device.target" ];
};
};
};
}

View File

@@ -0,0 +1,15 @@
diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c
index 21fb292..f9090d1 100644
--- a/src/nvme/fabrics.c
+++ b/src/nvme/fabrics.c
@@ -41,8 +41,8 @@
#define NVMF_HOSTID_SIZE 37
-#define NVMF_HOSTNQN_FILE SYSCONFDIR "/nvme/hostnqn"
-#define NVMF_HOSTID_FILE SYSCONFDIR "/nvme/hostid"
+#define NVMF_HOSTNQN_FILE "/etc/nvme/hostnqn"
+#define NVMF_HOSTID_FILE "/etc/nvme/hostid"
const char *nvmf_dev = "/dev/nvme-fabrics";

View File

@@ -268,6 +268,9 @@ in
# For pdns_control etc
systemPackages = with pkgs; [
pdns
(pkgs.writeShellScriptBin "pu" ''
${pdns}/bin/pdnsutil --config-dir /run/pdns "$@"
'')
pdns-file-record
];

94
nixos/modules/spdk.nix Normal file
View File

@@ -0,0 +1,94 @@
{ lib, pkgs, config, ... }:
let
inherit (builtins) toJSON;
inherit (lib) optional optionalAttrs mapAttrsToList mkIf withFeature;
inherit (lib.my) mkOpt' mkBoolOpt';
rpcOpts = with lib.types; {
options = {
method = mkOpt' str null "RPC method name.";
params = mkOpt' (attrsOf unspecified) { } "RPC params";
};
};
cfg = config.my.spdk;
config' = {
subsystems = mapAttrsToList (subsystem: c: {
inherit subsystem;
config = map (rpc: {
inherit (rpc) method;
} // (optionalAttrs (rpc.params != { }) { inherit (rpc) params; })) c;
}) cfg.config.subsystems;
};
configJSON = pkgs.writeText "spdk-config.json" (toJSON config');
spdk = pkgs.spdk.overrideAttrs (o: {
configureFlags = o.configureFlags ++ (map (withFeature true) [ "rdma" "ublk" ]);
buildInputs = o.buildInputs ++ (with pkgs; [ liburing ]);
});
spdk-rpc = (pkgs.writeShellScriptBin "spdk-rpc" ''
exec ${pkgs.python3}/bin/python3 ${spdk.src}/scripts/rpc.py "$@"
'');
spdk-setup = (pkgs.writeShellScriptBin "spdk-setup" ''
exec ${spdk.src}/scripts/setup.sh "$@"
'');
spdk-debug = pkgs.writeShellApplication {
name = "spdk-debug";
runtimeInputs = [ spdk ];
text = ''
set -m
if [ "$(id -u)" -ne 0 ]; then
echo "I need to be root!"
exit 1
fi
spdk_tgt ${cfg.extraArgs} --wait-for-rpc &
until spdk-rpc spdk_get_version > /dev/null; do
sleep 0.5
done
spdk-rpc bdev_set_options --disable-auto-examine
spdk-rpc framework_start_init
${cfg.debugCommands}
fg %1
'';
};
in
{
options.my.spdk = with lib.types; {
enable = mkBoolOpt' false "Whether to enable SPDK target.";
extraArgs = mkOpt' str "" "Extra arguments to pass to spdk_tgt.";
debugCommands = mkOpt' lines "" "Commands to run with the spdk-debug script.";
config.subsystems = mkOpt' (attrsOf (listOf (submodule rpcOpts))) { } "Subsystem config / RPCs.";
};
config = mkIf cfg.enable {
boot.kernelModules = [ "ublk_drv" ];
environment.systemPackages = [
spdk
spdk-setup
spdk-rpc
] ++ (optional (cfg.debugCommands != "") spdk-debug);
systemd.services = {
spdk-tgt = {
description = "SPDK target";
path = with pkgs; [
bash
python3
kmod
gawk
util-linux
];
serviceConfig = {
ExecStartPre = "${spdk.src}/scripts/setup.sh";
ExecStart = "${spdk}/bin/spdk_tgt ${cfg.extraArgs} -c ${configJSON}";
};
wantedBy = [ "multi-user.target" ];
};
};
};
}

View File

@@ -187,6 +187,9 @@ in
(mkIf config.networking.resolvconf.enable {
my.tmproot.unsaved.ignore = [ "/etc/resolv.conf" ];
})
(mkIf config.networking.nftables.enable {
my.tmproot.unsaved.ignore = [ "/var/lib/nftables/deletions.nft" ];
})
(mkIf config.security.doas.enable {
my.tmproot.unsaved.ignore = [ "/etc/doas.conf" ];
})
@@ -221,8 +224,9 @@ in
# Catch non-existent source directories that are needed for boot (see `pathsNeededForBoot` in
# nixos/lib/util.nix). We do this by monkey-patching the `waitDevice` function that would otherwise hang.
boot.initrd.postDeviceCommands =
''
# Seems like systemd initrd doesn't care because it uses the systemd.mount units
# ("If this mount is a bind mount and the specified path does not exist yet it is created as directory.")
boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) ''
ensurePersistSource() {
[ -e "/mnt-root$1" ] && return
echo "Persistent source directory $1 does not exist, creating..."
@@ -256,6 +260,18 @@ in
my.tmproot.persistence.config.files =
concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys;
})
(mkIf config.services.lvm.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/etc/lvm/archive";
mode = "0700";
}
{
directory = "/etc/lvm/backup";
mode = "0700";
}
];
})
(mkIf (config.security.acme.certs != { }) {
my.tmproot.persistence.config.directories = [
{
@@ -323,6 +339,16 @@ in
(persistSimpleSvc "jackett")
(persistSimpleSvc "radarr")
(persistSimpleSvc "sonarr")
(mkIf config.services.jellyseerr.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/jellyseerr";
mode = "0750";
user = "jellyseerr";
group = "jellyseerr";
}
];
})
(mkIf config.services.minio.enable {
my.tmproot.persistence.config.directories = [
{
@@ -424,6 +450,48 @@ in
"/var/lib/samba"
];
})
(mkIf config.hardware.rasdaemon.enable {
my.tmproot.persistence.config.directories = [ "/var/lib/rasdaemon" ];
})
(mkIf (config.services.gitea-actions-runner.instances != { }) {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/gitea-runner";
mode = "0750";
user = "gitea-runner";
group = "gitea-runner";
}
];
})
(mkIf config.virtualisation.libvirtd.enable {
my.tmproot.persistence.config.directories = [ "/var/lib/libvirt" ];
})
(mkIf (with config.services.kea; (dhcp4.enable || dhcp6.enable || dhcp-ddns.enable)) {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/kea";
mode = "0750";
user = "kea";
group = "kea";
}
];
})
(persistSimpleSvc "headscale")
(mkIf config.services.tailscale.enable {
my.tmproot.persistence.config.directories = [ "/var/lib/tailscale" ];
})
(mkIf config.my.librespeed.backend.enable {
my.tmproot.persistence.config.directories = [ "/var/lib/librespeed-go" ];
})
(mkIf config.services.hedgedoc.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/hedgedoc";
user = "hedgedoc";
group = "hedgedoc";
}
];
})
]))
]);

View File

@@ -35,13 +35,16 @@ in
isNormalUser = true;
uid = mkDefault 1000;
extraGroups =
[ "wheel" "kvm" ] ++
(optional config.networking.networkmanager.enable "networkmanager");
[ "wheel" "kvm" "dialout" ] ++
(optional config.networking.networkmanager.enable "networkmanager") ++
(optional config.virtualisation.libvirtd.enable "libvirtd") ++
(optional config.programs.wireshark.enable "wireshark") ++
(with config.services.headscale; (optional enable group));
password = mkIf (cfg.passwordSecret == null) (mkDefault "hunter2");
shell =
let shell = cfg.homeConfig.my.shell;
in mkIf (shell != null) (mkDefault' shell);
openssh.authorizedKeys.keyFiles = [ lib.my.sshKeyFiles.me ];
openssh.authorizedKeys.keyFiles = [ lib.my.c.sshKeyFiles.me ];
};
homeConfig = {
# In order for this option to evaluate on its own, home-manager expects the `name` (which is derived from the
@@ -57,33 +60,17 @@ in
"/home/${user'.name}/.config/fish/fish_variables"
];
persistence.config =
let
perms = {
mode = "0700";
user = user.name;
group = user.group;
};
in
{
files = (map (file: {
inherit file;
parentDirectory = perms;
}) [
"/home/${user'.name}/.bash_history"
"/home/${user'.name}/.lesshst"
]) ++ [
# Just to make sure we get correct default perms
"/home/.tmproot.dummy"
persistence.config.users."${user'.name}" = {
files = [
".bash_history"
".lesshst"
];
directories = map (directory: {
inherit directory;
} // perms) [
directories = [
# Persist all of fish; it's not easy to persist just the history fish won't let you move it to a different
# directory. Also it does some funny stuff and can't really be a symlink it seems.
"/home/${user'.name}/.local/share/fish"
".local/share/fish"
"/home/${user'.name}/.cache/nix"
".cache/nix"
];
};
};
@@ -99,7 +86,7 @@ in
(mkIf (cfg.passwordSecret != null) {
my = {
secrets.files."${cfg.passwordSecret}" = {};
user.config.passwordFile = config.age.secrets."${cfg.passwordSecret}".path;
user.config.hashedPasswordFile = config.age.secrets."${cfg.passwordSecret}".path;
};
})
]);

View File

@@ -48,29 +48,6 @@ let
asyncio.run(main())
'';
# TODO: Upstream or something...
vfio-pci-bind = pkgs.stdenv.mkDerivation rec {
pname = "vfio-pci-bind";
version = "b41e4545b21de434fc51a34a9bf1d72e3ac66cc8";
src = fetchGit {
url = "https://github.com/andre-richter/vfio-pci-bind";
rev = version;
};
prePatch = ''
substituteInPlace vfio-pci-bind.sh \
--replace modprobe ${pkgs.kmod}/bin/modprobe
substituteInPlace 25-vfio-pci-bind.rules \
--replace vfio-pci-bind.sh "$out"/bin/vfio-pci-bind.sh
'';
installPhase = ''
mkdir -p "$out"/bin/ "$out"/lib/udev/rules.d
cp vfio-pci-bind.sh "$out"/bin/
cp 25-vfio-pci-bind.rules "$out"/lib/udev/rules.d/
'';
};
cfg = config.my.vms;
netOpts = with lib.types; { name, iName, ... }: {
@@ -139,7 +116,7 @@ let
});
default = { };
};
drives = mkOpt' (listOf (submodule driveOpts)) { } "Drives to attach to VM.";
drives = mkOpt' (listOf (submodule driveOpts)) [ ] "Drives to attach to VM.";
hostDevices = mkOpt' (attrsOf (submodule hostDevOpts)) { } "Host PCI devices to pass to the VM.";
};
};
@@ -149,6 +126,8 @@ let
(map
(i: mapAttrsToList (name: c: c // { inherit name; }) i.hostDevices)
(attrValues cfg.instances));
anyVfioDevs = any (d: d.bindVFIO);
vfioHostDevs = filter (d: d.bindVFIO);
mkQemuScript = n: i:
let
@@ -184,6 +163,7 @@ let
else "ifname=${c.ifname},script=no,downscript=no"))
("device ${c.model},netdev=${nn},mac=${c.mac}" + (extraQEMUOpts c.extraOptions))
]) i.networks)) ++
(optional (i.networks == { }) "nic none") ++
(flatten (map (d: [
"blockdev node-name=${d.name}-backend,${d.backend}"
"blockdev node-name=${d.name}-format,${d.formatBackendProp}=${d.name}-backend,${d.format}"
@@ -224,15 +204,15 @@ in
services.udev = {
packages =
optionals
(any (d: d.bindVFIO) allHostDevs)
(anyVfioDevs allHostDevs)
[
vfio-pci-bind
pkgs.vfio-pci-bind
(pkgs.writeTextDir
"etc/udev/rules.d/20-vfio-tags.rules"
(concatMapStringsSep
"\n"
(d: ''ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:${d.hostBDF}", TAG="vfio-pci-bind"'')
(filter (d: d.bindVFIO) allHostDevs)))
(vfioHostDevs allHostDevs)))
];
};
@@ -281,10 +261,30 @@ in
};
preStart =
let
hostDevs = attrValues i.hostDevices;
in
''
if [ ! -e "$STATE_DIRECTORY"/ovmf_vars.bin ]; then
cp "${cfg.ovmfPackage.fd}"/FV/OVMF_VARS.fd "$STATE_DIRECTORY"/ovmf_vars.bin
fi
${optionalString (anyVfioDevs hostDevs) ''
iommu_group() {
g=/sys/bus/pci/devices/0000:$1/iommu_group
until [ -e $g ]; do
sleep 0.1
done
basename $(readlink $g)
}
wait_vfio() {
until [ -e /dev/vfio/$(iommu_group $1) ]; do
sleep 0.1
done
}
${concatMapStringsSep "\n" (d: "wait_vfio ${d.hostBDF}") (vfioHostDevs hostDevs) }
''}
'';
script = mkQemuScript n i;
postStart =

View File

@@ -5,4 +5,7 @@ in
{
# yeah turns out this is in nixpkgs now... we'll leave it as a sample i guess lol
monocraft' = callPackage ./monocraft.nix { };
}
vfio-pci-bind = callPackage ./vfio-pci-bind.nix { };
librespeed-go = callPackage ./librespeed-go.nix { };
modrinth-app = callPackage ./modrinth-app { };
}

26
pkgs/librespeed-go.nix Normal file
View File

@@ -0,0 +1,26 @@
{ lib, fetchFromGitHub, buildGoModule, ... }:
let
webSrc = fetchFromGitHub {
owner = "librespeed";
repo = "speedtest";
rev = "5.3.0";
hash = "sha256-OgKGLQcfWX/sBLzaHI6TcJHxX1Wol6K7obLf0+CHrC8=";
};
in
buildGoModule rec {
pname = "librespeed-go";
version = "1.1.5";
src = fetchFromGitHub {
owner = "librespeed";
repo = "speedtest-go";
rev = "v${version}";
hash = "sha256-ywGrodl/mj/WB25F0TKVvaV0PV4lgc+KEj0x/ix9HT8=";
};
vendorHash = "sha256-ev5TEv8u+tx7xIvNaK8b5iq2XXF6I37Fnrr8mb+N2WM=";
postInstall = ''
mkdir -p "$out"/assets
cp "${webSrc}"/{speedtest.js,speedtest_worker.js,favicon.ico} "$out"/assets/
'';
}

6239
pkgs/modrinth-app/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
{ lib
, fetchFromGitHub
, rustPlatform
, pkg-config
, openssl
, libsoup
, dbus
, glib
, glib-networking
, gtk3
, webkitgtk
, libayatana-appindicator
, librsvg
, wrapGAppsHook
, stdenvNoCC
, jq
, moreutils
, nodePackages
, cacert
}:
rustPlatform.buildRustPackage rec {
pname = "modrinth-app";
version = "0.6.3";
src = fetchFromGitHub {
owner = "modrinth";
repo = "theseus";
rev = "v${version}";
hash = "sha256-gFQXcTqHgSKfne6+v837ENXYYiEYu/Yks9TpnfBCPnA=";
};
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"tauri-plugin-single-instance-0.0.0" = "sha256-G4h2OXKPpZMmradutdUWxGG5axL9XMz2ACAe8AQ40eg=";
};
};
nativeBuildInputs = [
pkg-config
nodePackages.pnpm
wrapGAppsHook
];
buildInputs = [
openssl
libsoup
dbus
glib
glib-networking
gtk3
webkitgtk
libayatana-appindicator
librsvg
];
pnpm-deps = stdenvNoCC.mkDerivation {
pname = "${pname}-pnpm-deps";
inherit src version;
sourceRoot = "${src.name}/theseus_gui";
nativeBuildInputs = [
jq
moreutils
nodePackages.pnpm
cacert
];
installPhase = ''
export HOME=$(mktemp -d)
pnpm config set store-dir $out
pnpm install --ignore-scripts
# Remove timestamp and sort the json files
rm -rf $out/v3/tmp
for f in $(find $out -name "*.json"); do
sed -i -E -e 's/"checkedAt":[0-9]+,//g' $f
jq --sort-keys . $f | sponge $f
done
'';
dontFixup = true;
outputHashMode = "recursive";
outputHash = "sha256-9HtTdIotG3sNIlWhd76v7Ia6P69ufp/FFqZfINXSkVc=";
};
preBuild = ''
cd theseus_gui
export HOME=$(mktemp -d)
pnpm config set store-dir ${pnpm-deps}
pnpm install --ignore-scripts --offline
chmod -R +w node_modules
pnpm rebuild
pnpm build
cd ..
'';
}

22
pkgs/vfio-pci-bind.nix Normal file
View File

@@ -0,0 +1,22 @@
{ stdenv, kmod, ... }:
stdenv.mkDerivation rec {
pname = "vfio-pci-bind";
version = "b41e4545b21de434fc51a34a9bf1d72e3ac66cc8";
src = fetchGit {
url = "https://github.com/andre-richter/vfio-pci-bind";
rev = version;
};
prePatch = ''
substituteInPlace vfio-pci-bind.sh \
--replace modprobe ${kmod}/bin/modprobe
substituteInPlace 25-vfio-pci-bind.rules \
--replace vfio-pci-bind.sh "$out"/bin/vfio-pci-bind.sh
'';
installPhase = ''
mkdir -p "$out"/bin/ "$out"/lib/udev/rules.d
cp vfio-pci-bind.sh "$out"/bin/
cp 25-vfio-pci-bind.rules "$out"/lib/udev/rules.d/
'';
}

View File

@@ -0,0 +1,10 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IERMTWVGZyBJYzdr
b1d2aEtVd3lVb3NBamRwVFpTaTRjZFlCczk4MVIwQUoxWklwN0NrCmFFekpPYUlO
YlgvVlQ2WDR6amZDN0ZSY0Q3WWtTME5pUmhQeks1c3dGOVUKLT4gWDI1NTE5IHVK
bUZvUVAvL0NmQzFkY3BuYm1wMjcwbFpLRUNpZjJCaW15SEVDUDV0REkKRXFMQ2d1
NlFBejBvTlJrcUtCYWZxSDBkbmxIdExBNTU0aC8zRW5OVWllVQotPiBDLWdyZWFz
ZSBkeC4oTFUgO0FtJG9+RyB2IF1QXGhxVwpJS213MXBRMWY1cXlHZwotLS0gRUF2
ZkswNlhvcDhTc2tybCs1dldwa2l6SDZZVmNkNUNjNGlkV1ZVa09pOAp88dxHGxVU
vuFQO6JcroY9MF5Te/YV+wMc3hVxksibMnH1TWGh207prwcOWNOEz2iEkZY=
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IERMTWVGZyBLVlg2
ZUdtUlFUQXBLT3B5YmkycWJnMXJyKzltbyt6K2dJK1RBUm5ydWwwCmRUdjJFSEYz
Wnp4cGtzTXZwc0s0SUJQUjRlOUJlQUoyQ3BETXZmY0ppWG8KLT4gWDI1NTE5IGR5
QUJzeG1DQTRLUmRwNnNSRUJRSFFtUDl5VjB0clVLYUp3R1g4TEVKaTgKb09tVlhF
R0tmcmIxMHVHcG80RGxRTEhBcngvT3MrdnNPbmpCTlRyZW5xQQotPiA6RzEtZ3Jl
YXNlCldSUlN2ejB0MGpyUHYzNS9OQmJTK3MvcXRDakdBVlhuUEt6SFE4QmhnWVN0
V1JMOE1oaEp5QQotLS0gYW1OMTcxNWJrUndmR2h0ZmRDWk5CYnJjc29pSERUMnNk
d2VhTGdpOW4yUQrEeH2E9m0YycuW60QrdlRetO1kNU5FaKXRQt46iA9lACIWD4rC
Cx6WxhCBgz3hvm9x6iuYiiQxZzgJNK7qXcV2MBeQdivazeEbC7blAKVPhwD/dl+b
PzBDXRXG3c3jMjeZFT69fIBGJfZrrLGKpTALVE4dTVXmQmVVQDTp19IC+jUXHBti
Pj6Dpc7452s8zPkzZyRbasO9b4PYTwq8IHT6X7ITwbzZZm8gexDYe2SzRZ5VcPz3
El+yoULZ92WbXPSQIP/Slx4BEZjmsQS+sm0N8AnBRNZkWVbHPF9IZRg3VjDAn2i8
F3un5js=
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IERMTWVGZyAyMWox
cEFDWWUrVzJSM1o2azJqei9HV2xFY25oQXV5SzM3akxEaUJiN1J3CjkrQVhjWHY5
Uk01eGp5Q0RKNVREVXJVZytndTM0SXNZSGIvUVp1TnRiNXMKLT4gWDI1NTE5IFZn
WjAyR1RMajEyMnFSYnNGT2EyekROVWNoakFJQVZxaFc0YThOMHVyanMKWldrdlAv
cVg0OTFHL2loeGJ3RHA0MnlnckN3czF4RUgyc1NjYXVOSXF5cwotPiBvTFQtZ3Jl
YXNlIDRsSDp6eikgT1ojIGEzOFZMIFc3MUZGCkttZ3ZLNGxucnlJc01kRXFZTGpC
eCsvRzl5WnFUMUIyTWNVWGk4SjQyN3V0bXQ4VzFuM1RjSzVMcDJkaW1JYXMKQkE4
YWI1Mmg3MkpjdVpVYWJkczJQMnM1SVMvNkZhOXBDVEZPCi0tLSB3SVIyU3M5RHBE
VFRtMFU4OUFLbkNjbkJmVmhlc2VsYzdsd0pFaFkrUmkwCs+h2KlXoyZ8U++A9wmV
kh4N2YyI/a84yPGDNoFdSlXaU8jxUzolnadImXSXyB4CroqQLvGEbkZZlWqPLVfh
Iav7Ja8=
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USAwUDl0
WENqQ09VdkJSbDgzdjNFSDZhM0I1MlN4NGJ3MDBhUHVYUWhHZkJvCkRMaDBoNDN6
eGk0NFZpTjdXSm1DNTF6bzVOdTNTcTdJYUtRdVVzQWUvZGcKLT4gWDI1NTE5IDJF
dWkzMExMKy92ODAvZnYwTkRnYk92RjFObTZORVI0amtaZG5lZXRjRzAKSmQ1Zloz
ME5Ca1ZiNkxsU3BLYlNuU3RSaHBWWnZRRTBFc2V1K1hkQUZRSQotPiBWSldTemtM
Uy1ncmVhc2UKSEtXOGVCRC9EZGVTVjYzaDhQdVJTS0dHUHRiN1cybVlLK3JOaGMw
TUJyUE5CdnN1OFY3Nm9wbnRMeDV3TXQ5aApvRXdEOTRzT3NYbVRSVkpKT2lpbEFo
RGdXbjhvYnl3Ci0tLSBlSmliMk5TMWU5aWwwYkdwd2NDQnNWUXJJclFHKzB3M2Zs
YlAyWHREQitZCrcVpAY1+uNiY1rOKCLzWaFJLdKZ1/iwN+BhD7AoMo65Tb0g65SV
kfHePz8prE4OCGU57Mi3A/4Uwo4NCqwDGoqHDDgxOy1cA33Urp8Mlor8XFxI1/mt
hwzY8F4=
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USB2WFY2
MzFnVFYvb0wzc3grRDBrUi9teW1tNmx5dVBIRVc5OUdlcjdvTDJFCmpJL2tlYXND
bVJKbzd2S3JkSFVMdG40MS84V0lPTVJSNHVBQlNXSkN1eTAKLT4gWDI1NTE5IFBM
ZGo3L2VlNUZqOEE1UHBuMHdqc1pyYlQ3R29ucE9pajU0bHMzamlXRDAKcG1Qdmw2
cUlncDFWNXBOWnpIeDNZSFA3d1E1bjNaVVpKU3lMRjRaSHNtMAotPiB2XUh8eF4t
Z3JlYXNlICUrO0cxIH4gaTRoIF81SEpTN0Q5CnpWdEZpb1hZa2t5YkE5RnJFMHVZ
WkhkQ2o0eWtyOE9ueDJkeGd2aUhmLzRUUGs1aUc1NURIOTYxczZhOEVmT0EKd2xk
TXFHN051d25PQmtNUVZkVEFGUVliZjdmZDF3RWFkaEhNTzd3ZVd5N3dlNzQKLS0t
IGZDR1Mxd24zOW05bitzQnN5WWVOOGtCNEc5aXIraEF4eXFUQm5CZUdCV2MKd44C
/Trgg0OEZ89/jqbj56z/Hia1Ka3ZsEv6bXPI/kcRvFDBFTgtvG3KWCgMBtTUHXzY
TKBPoQqrUf7plH7a/mTx3KR+4Y+yF+1i86s7TzYjD8d1xfFH3BsVtg==
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,26 +1,30 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USByZ2Ji
NnR4c1JUTjBZaGpxTFAzU0lQMkRIbzBwZHgyL045blhDU3RFaUFzCmdwYUhqT0xw
dlpHNUJRVWNpbkdqaE04eS80M2ZDNU5QUWtqU1NqMDA5OWMKLT4gWDI1NTE5IG1I
Nm5wZUJaOFRaWk54THFybXZTOHA0bWQzYWdyQnR3bFBNTTE2c043RFUKS2pnN0h2
NkpMRWtoTm11bEp6bEhaNXBLOW1jWFh5ZGhkbWZVTkxqcFpJcwotPiA6K19JIzM9
Wy1ncmVhc2UgLSlMTktAaCB7ICQ5Ck1OY2NNUTI4RXkrVHRBMjZiNDhOVlRHYWs5
SnBVNmtDSUdmdnVST3dJMEl2Z3VmOVVkbHBjaWovVm50NUoxRzIKUGc0RThGdndw
dW9RRzNPQzRHMXo2UHBldnpPYkJYdFRQblhPU04yVTF5Tmc2TXRPdy82ZWRFU1p3
SzJPUGVVdAoKLS0tIHJwRkxvT0dYMnUvL0cyVVBHeE92NXFSSFJOMzhUU21MRS9S
MlZ2TWtZNWcKQ2j8mZSXH4kgasTeLdN4L6f8Bgwkg+X+mZydMl0i4RWnBj9xB/CI
FlRvIN1Z+PQ0dvxUjoUpe0JRRLCbFsYOWVnT9G6bwkHORMCzQg3Zqib4bHOanXPj
dNfTIdSxnmWR4DYZnYgNYx/gvP5XAyfeIbAgG3V6Ade8x6bBNd6yz9HPWNlq0XLx
TeJXQC29FswzfHYdoabfIOQb+UfxcccJpi86i4ZSbZWZjogVs39khJI/DCtKJNks
tdXVumTwaCJCEjMukerMOXl0MmL831di51ypPeTnFgEVdSny+tmjns9wPyWVhWVS
DsxkzQi+3Fa2dBt0RQBoec2QbXPoyF9Vs2+jp4zmR+I9wEIfrWJQdXYi1V15fkT2
HUAaxlKxFNQYpEtltIvMR+2T495LtSAlqtoU0vuTzBUC7V0XaAeBgOX2iFOE5buL
hA0G43Zw/uamR1ulAx+8VrJs2Cv9ARFI70/mpxhqaqQuI0svJ5AZ0bhkz/07NEzl
pgNIUsaUR2/KJCbb41YBaVlaAkFjgBrzJULM8uGkc/tD0aylJhEbBZx30js1w9VL
3nb6aGpAIFMvkObQPQAYgVYoCeIcsZ9oqv+CCLZ8bbDxAWEBJbTKzoOvLs4yt5mp
pIviM0ROtq+kLrTXCE90BZ8A28vf/IXIkCMq+XY6Xc8HrlfX/lmCwTPRPr62KeAj
ok5ibnWrp4UY2L+U+xE3SAob2Ag0/S6ckT/gRMCsJMAKfG1f4XyIoifEULUBsqSL
71beu74Yc/wucfrJ3w07lGHCYEawh6rDd6yGiYuFF0Tvcy60kaHCTMnmxCLzcvH7
UTSsAVv0dvxtIFo38Onfjtz5tMDmYAnFVUa9e6IYWqjjgdFAzGYam1GAnQh4Nq4f
HqT7oX04wQFm7Jge1LVFUsjTJO5M3GGNW1QZxc+LvB7OnrEd4NeYlnNY25eW
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBuQjBY
K1ZTUzR0SmFoYUZLdzUzNUt2M1B4RFdPZzFkcW8rMWJLUjVHOUN3Cjd3ZHdMSzdp
OWswditVWUhYNjZCdjV6M1d6U3pNak5hUlZjTHNqSkZPSk0KLT4gWDI1NTE5IGNG
ZytHazJQWTExMjNkZWV2aDUwcnZjUGcwcXRkZWl0M0F2R2JpUVlJekUKWTFlbkEz
YlN6dkgrMmFFcmlZMjQ0b2oySjlEMC85cUF5QlA4NGF3blBKRQotPiA8M1hbWS1n
cmVhc2UgOmlZJFYmJgo3UExzUHhSVi9lbVRjNW4xTnVaUTVGSGVDNzRUNlJ2TnVu
U0wrWU5kd3dyR2phcUNVUHJCYjdzeWhjNVFOV011CmZMOVN0WDk5eUg3ajhwbStQ
aHExT0EwblpreTBXVHh6Zk1HRkhzcmFQNnoyVUlaeStMUkgKLS0tIG1aY3Zadko3
V3pDMGswSDZCeit4SVhSRCsrZVRVZVkvQkNsQWN1MXFMUGcKcK74YULWqWw9VvkI
NH38VHfDPW5ueSiyHHKn8MI9YosPr/TmkwgKd/DOIMVB74ahPalo1QUeg+eaBVnq
Uncsbx5ecW3JswthkAhiktWHcdHmioGD6hCcgbWtfA3VvbAYc2gtAF2plfDVH6BD
c8veai3B15ZmDmq3b62sgVA9JUgbFaB1eRqcaPXmNgQJR4c8J+CZWXGQ/TKMqtAg
chCDuczjLX8IxhMPF1gdCggSt5GtMhY47fpZJRbBUw01l8koqjm63KvEeg2ejFGU
+tXonI4jm2JUxlDTqmNSrVpmAgtPoEPszHc+GsnyNjAz4IXMm5UrQAZyDPgxtwQW
ZAn0IdOB8rSL/AnRGUpPrecuauINQjONe8CapijjJPQK7g1AHscfxr+OJD1j2eV3
O6TNlz8vKW4t/V7CUf46ykxxExhA0mKJ53ksaXy2+HWoro2+c4nao/bEld5gt08U
uCpJtjPKB64X6vdzbY21/l87VRDyxbb3poENfrXlawqS0Z7i2gAbHN5EuiMv+35o
sBaR0BfcbTn6VagC3i8HEEOO964FrW65pkqmGJcJMgUdcZSl+Y7gHMjWyodqGPOY
evT6xj4iyeM49vXynV2vrJRu5rr02hOS/8rPphV9c9q9ju5n8xbhS6IiybAelPwt
sa+xM+w0n+fxVksiXeFj7qD1LDN4+kwqhYP9SlzbA4fT3AkYYrxthTww4IFAweyQ
IJkpVL0/IZEIEfRzpr0lWLprAaoyPc64pecl0z/gBJDgz1kSG6iEh6K4qx7ahGde
kBBpS2I2ZsdBkvmQxDSM7tYYBzPxcUNZlSFOIeUy3Xl+OGPlKgtMofbnjV8AFqUf
tOpgKpLUEkTgXbGUKBxHLj+8pbw7zOFp4sJ00i48ZzLP5D9jom+jS19wGshRc7cH
dK92CctvrfndQ85yO0vqlyOdMTqjh/z3P2KmhL5SW6P51q5mseTvj7FNM7331DTs
rq7XWaBLE61eWWQ/dZKnqh5YbkSlU1+08Rl6H/vCU5hTC8fht3KTQSWPofkrP+5f
We/Pe62LyhV/MbLRA0nmU7Sf4IAnpHfa4kLtlYeB3xiqKd0McM//qzjuk5NoIgE4
nL0T8YXdGt8K01w+nt+j5bo5gFoRz5+1/ZZ9BgN2DOo4SClYnJWQ/x80X061yJ33
0SGv4eAC3vFi6xE=
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USA3MEFh
YURySHY5RjVQa1c0QmMvTkZtZElNd1BIeDFpR1U1YXZ5dndnclUwClA2M0ZYMk90
RmlCZHVISkxZQTZXeEhZVUROSVRGRVg4TmZKZytSL05FcTgKLT4gWDI1NTE5IFY2
V2k3S0VQdGlQZ0hkU2ZoSWdOUG9rQ0p5Ukg3ZHU3MXVvNHlWYVN5VVUKcWtoWDJh
LzRwcktWUGQ1bnJvRzVBVlpHU3NhdzYwUDBPTmx3MFNrdWNQYwotPiBKQil6Km0t
Z3JlYXNlIFdlRTlUU2cgVlxLIzUgJlVyQiJ8Cng3NzF2b3d6MnlEdURQWHgrSFJv
Y1BMUkZyelJSdDJvUUUwY1k5R3ZBMnJaa1R4NWxlUll2azNseW5MQlpvVWUKYUdN
d1ZscXVxd1dxSGpzMjBYeWhhYyt5ZlNkSjhmV1ZTTDBTCi0tLSBBTG45VUJOSXZv
N3lYNVlyZldMVkpZdUMvS21NdSswVmU3VThZK2M2eis4CjwfWWJVR9Ty5dBXEr5s
U+QrifXvyMJJvqOKEfMYYomFLpt/VtbDAUwNlqLnFIk1VZ2xPUBisXPtReiCK3gq
P+i6bUONSmPYvmF82VgiuKDuz/kmWqVGg2iSBPIXAGLNIEojndQwmhnrsN9ex4Xy
lBHkvVsN8EamUJwZ+FEZe4+PjA5yEnANWpeTELOt2gA92/jwwnNIsr07eVTlkh8X
UmQ2owGwktXP7it6//NV3C7sAdazs2bX65WSnog2E8WPNAorKWI8RCYs33CshVFt
zzclZ4hRmlAmqMHYqFY=
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBmOURS
dzkyWWlrU0FoOHlmNzZzQUk0cWNueEJQY1c3aDdZMDltbXcvU21ZCkhrb2VkQUxK
U1hwdkcxSGhNUFNITmZRM0NsQ0hEVjJGRXcrOXpVMEp3ZTgKLT4gWDI1NTE5IFNK
Vzh3VnZsbGFJVjVZQTd0b1VLM3RpSUF2QlMzY3pLTExReXVDb3IySGcKbXdhNVcr
LzVPc1hmOUQ0eDdRVWJzTUFLZXZVT1NzRnNpS05GQ1hidnI3cwotPiBlO1BHIzst
Z3JlYXNlIGtoLjRXUTZGCllYeUs5QQotLS0gbTlsWXIycFFyWXNWQmpxejVYb0dr
SU1iTnl2dU01UXRJYXJFb2RCZW9sSQoyxOoyPMiWLvMelIOLcxb3I6g2kf1dp2X9
UeZBTWke9NkQgC3YscKbhpl4bq74SLzN1qThnyLoWN6yRipSu/ttO1i7VTMFTz0T
Ho8+JqExIMCtmGRzMXfEqMepY6WizsK+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBoYlRq
ckk2c21uMVZSR05SdlJpck5MT0FZeUpLTmpTRVN2WDkxWWxmVTBvCjlHZWtEQ2Jh
YUdybjJLWkNsZkxabTVIR0JnMno5YlMvVmhodDU1bktrSzgKLT4gWDI1NTE5IFZT
Um9lWXhqRFFLTWdNaTR3Sm1acmY4N0l6WE1FSW54ZEtQcEE5RHBRRHcKcE5KYmp1
WVdCVk0wMHZ2SCtUR0xKWU10N0xETXlXZmgxcmZZOFBXYWtBawotPiBlRm1gKGxU
LWdyZWFzZSAqXF8gOkU8Tm96IGA4IC1TCjY3NUgKLS0tIDhjaTNrbG1iQ05iTWRS
UzIvNzA5TmVGS2ozZmYrYjlBY3J4Z1RRUGRNeWcKUTIEhWqr0fOODu86MDll7k3U
ThgmS9nlcUY3fMgXzZLtpHIJ/4ZSI+miu8RmLMaeC61qv6xNThGdx+MvU4tMBWKA
Hv3XGi2MYL1jdHh2KYg5PgdqchYuHrFuBPS7c/tQow==
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,82 +1,82 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSB2em85
Qk1UN3VxWXJCV2xVRnpxSEhDTmttQXljMStlZlhmQ2t1Qi9VbEh3CmpXU05vNVVr
a1NxV28vYlRKczRSQVBSSmwyWFpLRW5xMndVTkF0UnprY2sKLT4gWDI1NTE5IDdK
V21PeXRQODE1UWYxYmI2VmN2aGlOWVMzSWY3aEtkdlNtSkphVEdBMWMKQ0V0N2Vn
WEU5cnRuNFRjNVYxYWNzTjlZUmx0ci9VdTBERHVRSlErTjY5dwotPiA+fGZXKnxC
LS1ncmVhc2UgOnZeIGRnSytTMn4yIDh9PGR+TlswCm9xODRucm1RYTlEZklkcEdO
RmR0L1BwTzdCSG9NR0sxcUV2Wm5uT3lia2FuTXA5M2VhQVZRaTF3NEZXckFiWTUK
dXlXNWtHWUY4THN0K3lRUmR4YkxwMHJhb1RZCi0tLSBBc1VRQWo2SjRFV3YvZ0NL
eUNEeUQ5YzNlWU1LMUlOeWh1UHJVS3JBMFdjCruYC0f782So3smThuTgVCo7eZ4h
LsHM6Ak+TWwLmyYiTvyGBopAYcQsoXogrV0lfHFDOJw4/PbsDzLsl5bUmmk5+YbT
dJoNBFoxVxrQJGmwxNm6d8Dsd6dWP+sq9ETWD3aEh+erxfZiFoYEL88NuIRWy61t
3VNyOsg2QduTpIsEtZnkrOMk3ZH5GF/EtTqyKaGlJCGBU9t1gsfrik4JReM01VqS
BSV0b8+abrSc2oQIIxXQaq5+UFDXo7Cu6+hI7nuqCuHW+ckPVivHi4fqZI+ZWu5I
HxQxUfH0B2tsllzdOkbw4TBv+uRFwS9HOiXpKx2dsITiSOtfD98ZFeb+lsnc0rpH
sErWwwR/GxrRtRZ2U4cRB9lKMkXrd/6n2o4UqfropxC29C2pdPiZElNT3e91+0Zl
MoPBF8eHBM0K1i7uhtZ2a3TJcqM7LOjWmeQDI11ZWcGPUqKyRDM5KjMX2UnCeqkb
cJ35gPpkLeamTGmFjROlnYyqvQtnFu6J2gq8rus4btxI0P9lZ+vRr1jCFm5ZAi7o
AFFj9wvg8oDxIPVKQ2HQRFH9BDYJ3pjQiXYf3TKDlaJD2BD6rrmGP+HzRPw1bgzu
JS17pMY9rgMMnEOm9FadnYOBE2fESUktnd5wD9qcF4Szv86sl03kkte3bypgy/RN
9X6+a/xEUAUzjlUZkZYIpGnO4tX+Q09xDhAU0POJUQ/bpI9nnEsdxGKxr+nA1r4C
eTeg4M2epxEGWcOmUAHku2rWg5ou3U2kPdiNfl8XkiCMX2dVCcMF+VPLsaBj4V1T
JEfXN9bACRhanm3q7xiWQiYtNrv8rCcJ3XZR+TZtTN0oIjdhpB8vCYc28f9upZ04
W3Ki7D5M/B00RTT1klpYCwp2d+TrpCURJtAWOgW2LSINE1PlcdhmTFbMkPNsKJR8
8NjYPGt6xkyKJUUaJ6/joQ3kXm5+eiKXY5ornpdgvk0Yjb8QVB5bAWPYHTjqMGRV
8wZqlmUUB8yVxUTWOTCB8ngT8ys/YXElcdGGnjdmZRjN2CvAoJWLbw48SZdr+nuh
f6CKctNPDM8Gfb4ULHApapd21L7wXqZC1yd0fsQ98jZdVUjNkrKVv1oVlAOjuMRr
MTgiRE26MDdXlKVseMxDv/wEocmRxgOz1tBrw6FHnFCrFuvIuJ2UKO/0guTbMD+z
iW+CeT81DlLik3NVgyfOz8j5AhEilxoRanF8Hacr+4RoLQCLZQYFGvzgtNIxMeRb
2FAwDBk7mVcumiEqYWXBFGM/FGqxNXuKLird7x/R9PbBptBhF50E89L1yyOb4klo
CIuEopydCtRD+WlmzxOYkCFph+oZpJmJb55Kq+i/Xmcjt7326055U5EhSo8wvT7b
iIWC+qzSHDR3PSipe5XaivfavGULiFY6yz1i72FIncCR2g/KmrQP/BIkI9Nldoqa
vsUGi+EhST28VG+HXa09O4R/JK/sgdQfolohG1XCRBIPn8GTJfPFDZ1CgK2RIRbl
n9Az1u5HPc2t+Pz+UsyBDVb2Vmlmvbs3lX4EtzdGWOmBPtD735rmLre9Fu7jn/A6
KwXyknlvV5OLh3nNTUUCHrGDREw51fhnOlD//YNY+copvfxtOXN8NvRabbD+d4hn
K9jlqbxHBTKW+SxxAI1l+z8Eujd/nB2ISmiM8Nf7/U2L5TavMLJ3BFGL7i8ktFFm
a9JNPjxWDtIJLK7TrjLpFM/Kw6jFdsP70oZ+elN892NPsCZF712D56KbvW4ga+Zy
uwxk/FoY7mOYZFMbNXuPg1gcVBxT/TklPrMK1Nd9qkuMIcg1rnmr/HsuoqaPfsxq
zG+86LDx1jwFNtllpUqf6Cyr6akxlvOofSi0HHut1qcsHWqBxzkC/TdO3rc2nt9T
hodHQrOg3XXuNaNtrmmAxMJmr0qbtftMxUxSjA406QfoOaTe41YYlRLWcrR4UM2S
APTQEBiqCzlItGbXVBjFfJMdMRue/yDXbz6ggCTRlOMjuGLP6+lLuois+nY/BSNi
zhTD6iEdCmLZ/AlQZBs2ZoLicFPsArqP7TXjKlY6GcHFyoWJGfdINlYniGgX4zk4
UO9pgNtkI1PIMRIDTIVMkFp0FAdbcnY6NMTyJBZR1fxS7WPiWcLEhd4u3XWlCt3c
i2G7D5hnVC4ZnJWj671dnQgKdWAS+Y1UcjrmhvfYzi1k/qrN0HICxk7Nde4QJxUZ
NgO6CATuSzUXa1kq1IK1S73FBWrxhbV540N76ws512fWHK2vISHGi1TK546FXtZD
lpkbjr564pjqL01Y8HwehW2Q8y80YQ8T64/VTwSzVi4dQKTI0ZnqPRHGH7wg3Shw
v1O6LOqO3r5XELziHxpFJSreEeEB5c4rqxPQjl1WeOYBoChZS3JHwEMeT2UAzXVv
L0Y+qoa0pB0lwDZm4LR2h4UWLWaJSEj2BqBihVBmGH0s/umou+LztWkVA+vYVyDF
rU9bIXpdYm1zTYuyB3DHJVXkDQJwsjfQhpe4Kcd79pdPH9pudnMQliVQMuzoD8R3
dAzP/SU/5i5t8aQPOkStzJXE0nZe1bHrY+/9Cmq//n7zIRall8GVMJ5LBwbg3qex
OHViAM/+VI6UOwB3FjGbCJTP3wX0y08bxM9klvVOi0gVQkEF058A6Ir18Tfo9Vho
fcjJEaYHacB+oVE7/Gwz35mTOvDzkTRlfHVjbcxzLQ0UJJzY7hZl0tXviK1cphiP
rSQqwaaxp0uZCrOiQkMrwS1X3RZ5MqIJ2B34y/rSnwU6kJyx5uD0rIslZgohmNx0
oOCDQgfh13O2SVMNYDMrpIeFQtksMN9TEmRu5bzZwo/lmf/9DjZmDe3aDqlf4wuI
PO7Qt2vmPHcgC7aQ5fqQYzFtYko86FrRLc/ih1Jto+tfnqt/mD1kCLi/w2dhqKu3
E1FWKmc1jzL6/K5sPxHHSaYjmJB4YqQ5VzmhV23uzyydarySDO+atM8UkaC/FIvX
uV/LrLBDxXAmuuV5493ga4Lo+EWraqeuCWcA+kofqrN7ChH4hQnGQF9IAwhqaOzQ
DSCrnL6Ame3W1nhJrQmzm7ACtowyFmTL6BVtnPNnHTEeirrKZzrtpEk9tm9GsMxT
DxmUqWk7A0s6nBiQ+Z9oCFyCAsLaXUPIIxfyhirp+S5yyhKrkgy2rBGeKIXi+jmp
WSARJVaFCqkaRYqJdnm4mB8haXSNyHLqFSYxwijr+1uAuD7GkXhbFztv6bvKC0yN
mbCTQJT+tn0ixOTUb6w+EYftekpg3sLuxrIlfBBUBBUde2ZqZtqZ8hCUBoYDgovk
Ka4eXTfFwxhy0efC3DAjXwlvCXjRH5dF5Opw5PdQjt7sPD1qHYb0g+YcspNNOOMm
cK0mBD2atI/TRA+JAHzTD7yL8t3XP6QA/ELvDpdtRkmVRhJk2vfDMF4NVwfjYWqc
RBBFozGx1yffVoRn4ZZfvdITelxotwidMBpZ2ene+Q9Qj47szfKm147EqFqZq5ZZ
5b7asWfCd3K9M4mYGy3lqtckbNEEFWRHXxiCObIRC3GRJ3rGWRWx5BRclGAeFHqd
eFANicRCHuTvgRI8JtGk8wH6VJC1oHAL0f2GnqNY0ve206+pKMuqcpxqP5VPI2EW
eIlqiGL3zDuJFsyjOEVpt8adEo0Ks2IV4Wc7zn3dtVMRn5VBuXlLF5/8/bYJZEOM
YgW6l2O5ABaY0Hrog0UqqdluvrPl7/qwmMFK8rHkv07n1N0EWRpBAdOXpgv28T2E
r/5CWA5PIoX86WIOrFAklEg0pfwc/nMl4G22z4hVKeRcrXcSTJ/hpicjajEVre36
VMYOyc+/4CXoJ9rL/kdSJ87zlOnejM+SBld1nTikRI3icG5ahX+WKOiEOEKkw0V0
PQX/9W5l3v3g8O+4fbrjIpsnHfdaQE+X1Rfju+EQ//IdWj3Uiaph+OXgCX6tr4r1
c5DhsAl4tRhzgHQHeHXPqb89B6mf5FuCDiKmUNYl92vxn8+AUHiCah/n3Jf97Ny7
2lliV8bKL3XoioKrC92+942TP25l0ufbbxekYfKnmXMmSf6aLx1vY4cj4OjkJU4T
UZVWnh4w7P5LzoEMZCoLHBzBIEa/puUaAL5vCPefaXekBKEiAnl+LQh6//kTAFRK
XKgoU3BAIy6D8exVdkniva6kVbQ+0mMROywSJ3P2QrCNr/Kid0js/46m5qUpgJKp
m+SnkdmALU6vIPEORnqSEa/Ku2s/kOdUwm9vJPdD21chLCXVwDPmyj1ZNmIiQB9Y
EULpw6z1Sg24awXuiTKkhIpJhm6CalOhTNkeLrU1W1Q3zaq1oVWkCLCsukhZk4xk
jugqrDYRPPv6qQj9dr6sF0SKe/2hfIXCve9yeHU6jhp8PaBNp73EErsjdkbt/FOm
fbr85mqB7ZvXf0My9joxZLKp7ajarwsFxSggthekLdYwlLm8gSGqYRk2AkvYa5E0
WGZsi+DM1FvsA+St3m3znaHyQKKRxMlcNwaMWP0TJCuWUj0DTIWHvjjLKr6dJshM
nrVMBlK1X7TO5QQh1+r10gcMy2o1
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBZVkFh
RDNNZCtySlNQTTZFbzJvZmF2TEZKVUM5VjFjQWNSeHlNc1JNZ2hNClVac2pEdWdw
OVpOTmhWWklVRDA3UzZWWk9QVG1ySUFmTzRSVlFReVdFVFEKLT4gWDI1NTE5IDVD
eWN2S29aWVNVTTdldXFxZG1vWUNYK2RrajI0OGNGaklzdFByTzlPUkUKTmsvdTE3
ZFBvalZsdENxR0VnV3FVSnFxTitBRlFWR1BzcjJWd1h0U3hpYwotPiAyOkUlJC1n
cmVhc2UgIWFOMiogVDc8IFZETk9BSDVTIHEKQ01rc1ZoNXQxUDdtWGRQUlRCV3k1
djNZeEtOaUl6Tkl5TDA1S2ZXWTFQZEFiV2pWMDJpZ2dkYXdBOGhSTVA2ego4UnBz
ZGhXcklMd0NVWlhsc0Voem1wUzMyREViUllCRAotLS0gMU5GQjdJaVdlUFJGMG82
YnZCUm05ak9qcmVyUVJSS0tHaWRRTld3ZkNPMArRgojrBQvlyjMhChn8jgUNDysj
9Nh3BVqf44fiUNMKARTrFTz3wVMlioo6MmKRzJ/yj0fWkZ2h80X68Nq3rQvy+0UK
4PNPRbwY5ib0nf7n3itFKjehrYr8HnOjreQps4it52bT5re2TWIC9ogZhNKBLRvB
qv14HjcF5bGnia8TgSU/8nUZJuIaLwHvS5sc9GOtmV4lDwiSTEJQEtGh716Jzuvs
XxQLPEVVufAjZUKLp+jWUhXGkvUEeY5bxE2aOLuB8kLfHPC/5p2rl/n2sW3oEDFn
Fw+XcNnCHWBzk3DlzKZFXwIaZL6Ocqrt+p8CBpWoNhbssuKyOtEbWQOmJxX3UPMt
sBFNdKSKFhoBQ4ukqkKDhgcm0i+8wG4eBbNIMIDwzPBxK0QiVriwlPQHl7RS2M/J
LgGeS7qrKC3SgmLu57CdUVUlnogJ4oOg7FPh6x+jQBIIv7gtYCzJb040TUZWZWvi
cfKWBBwLqLa+7Y6kEhcosLUgw2c9Et5P4rvNjU598q7qrZq7uK6Mr+PMwD3Pd66O
SQyT4nYhhXBnaIsLXZfima2nRzPocaBmfsuXQwlnLJC8JB6yzqEViK0xYKfl0fIi
jxuYBw/8Y+MWQhIkF1pP8z+Kw51QdK4CIV33mlLBJnGjlizewsjj04EIgpzQ0+3I
ZRyee/SzFmGBBk/pMGN0DizA8LS9yZ9DRGrc13rYVUTN6G5xT5AT1eEtFbQ6DH/n
q13Tur4QJ8AqJmwv1cnX+faH61dXahfbinveXWqB5S95/HE3OGnZfAiS5bs1jW4U
D3BLaBiFMYD+ccPedg5MLXRgEryVaOWlQ5wuXxqmaCG0KS6YLxl8o0bRR3lKXNZP
B7Ye0Px69aYtOs58dTOq0Peu039Fi5iNC0beocdKOjig4tZ6p41uz0jPGMUoDebZ
MYHax+a7Qpi5M6ZVGVpF1ieW2hqnm0lfb18cEYHuXjoI/YlgVhFPCGSzHJpuNiDU
qJSfOuD8HUB2iXFeEgAi+V3EKTsyjlS+R1zWxkr2awqAk7Uf3svLGWZNINHNoJRe
CPPANHzTSHlNPSelQ1CyOhl0W/eMCEJuJ9/6kZsmUC3zvzxEMGofG+Ub9RRBW/+/
TkrVvMrQv7/Va/4dxMPCqrdOQ+3/LkVzGX7qg6tQR76Su0q7aqh38Ki9qWY47bTm
lq5I2EcQApt/O1cGQJTFowdXVJXx/qQv436p/jL1mymQ8241iQ6XX3DUjbnqdI33
4XcPa/B7naRkgSG1Z55Vv+IIBOzcjzUMzeQVP/WM3SFRNYT79mJnV0SsKvUFtUOM
1OxMGEx+ZxfNIZqdp+qo0+uIU00ciokldh4URo9u8EIHjFVcV3Eyb7EbbCxzDiU9
iwoAAaJjwBlyaRQFWYsKhY2J3NEay142WAyAiHmnNBNhZM9TKkCpjton4ehxeRUC
hoXpzh4J2lMhdcmXXZ675C3MYiLDpajgLS1dwRp+mAUXwjvQG5rmJd7UWdgpbdpJ
K6wABNkl601XgKv3/nV9touE0KQjHg1fzo9vzmf8zVEViTgXxH3NECZHctObwgoo
SlbUOW8vihCi2HU9G3tqH7dwkUsgB2a22JFovgwwW8ePKJQpdvwAV/jyFMLL6jyj
6Tf0Gsc/Iut6Mh5TYiTWJdCzvEBjcybOVnppj2zR32/A/vAXUrJQaq0kwKkCwG+e
TL0hAQuA2Su27qTR9Xy2QKpQ5NppRH7V9YxCpjGfcLbcCh7XH7AEZaYfrrX8NoP5
zPalFA+7E1IuDRtT29wFNRVb3lAet9t5q0LUHf3x+Qk23dQ7Mdq2JIf9RIo5xZ6X
nsKa9eBQ0O5OAv1VGWVvbrVCMpmyiklYV2rcIT+dbrhrgiTu/BvODSvtmeANIkhC
uF112/+QW1UNPCENioYq9OWPcEwby+s9JkQf5nTub2o/0lTFKJxNgXa1QBcKKVyC
9aTCRhcxXsfuR7YzbCXRcK2hUW203iS8+UgAfZBVe5GhskOd0ZR0WSkse+jGzPqI
tnH5F0yQgLn9emebpL6dNFUVvssjpiMlvkijXFJv1tWSKLb3TxJd2BE/w5Zj7Haz
iv7Wz6o5+bY4v2YL2Ev7hGzfSaef+ip7/BYZ9TGuzSoZEHMYa7dNT8kRR18IhfXv
Y3wa2xlOhB+WE3AjtYy1oTi2c6Vmd9UKASZy/Eb++j1MSPtxZQLNaj+svCmqqfvO
/gElVfGlgkomYwc2EfJvR1lt9u9YGepxKrafPsgvjR6bYPwLF7eHKkKyEiqGCYnW
mcNJz/B0egxhFjoGd0U17tNuZcYRD/Tsj9ugGF+4/q+IaEV2YzZTdGzupI3lW17o
Q4H+EksxkWYDr0WRlYKn7VT1gTThuggEz77JskjNP4jK29EOIEO9IqGMh39tXsud
mhL2Z6XMX6sgxSjSkYxLpFnS1mRZ6uQnSptxHTfnG6jYhq//MTjGX7xmYBj5EoTC
duHuaCqjRXc3yHWoNm8jepkpbe1PPbwEbL6RDK16G3g0WVREjZopj+66C2xlY8Bq
ZCmHhud22QHs+5r1LLSIynsUlGIOvq30DZ0F2/f9Gm7uayIYbp8gA4z7M7RjmHoZ
+XHYSPc4kH4a3T9MucQQGzJMl1k/bifBAWLbu9uPcDUe7Cglfz0wHnPemOu8HZXY
6qS1n6PAHyyGlqX/pNWxR2vQrHJRksdTCvjl4g6256PhaMDop3QAHuQj9meYKIqP
MiZexiuOW3KjPJ30Gx1Q7PnUc+w3SjjsKaZEvRgeWKa37rvj1ICTnBQUPq2HV6y8
zaGnJcfd0ENAmFnRaCIbdU6TRonnIAAuSfE2gtLni/PJbmUpznuq82W//kxvH1tR
oPx6LuM2+hRncMoyXUTWSUVO1DAUPXgZA+j7fkkwmosi14d/5xe3wO4U3dGZ6wAY
D+zlzhq4d/4vIvLIL0NoBcp8yM+xWxPTtvj7HUJ7BOVV06ICnilUlv2wjR+dZLZK
DyjWhMoqk8r2TDZbAQr8MNX+sSlp1JxQEgBijpqBvRdF/ulqraF/GFDFRLcEi7D+
AkHPg4TrmaNB5ixsj72j0xruFJxgQJGj8fwQtemJGu8QcuJbCvJ74TI3vu0Pac/C
MRov1TAVJB7+iVvVNEgCMlzimJCSCUYh1Zgk5Ci2CDFMFRAEcGoHywIW6v5V8j5o
v30mJKZCVFc4Yibivjj1aGhQVO72vgiog4L++i7CeXHIu6Fe1Jmlve5iuxrQA2aM
9vjCbFJIi8k5vQTVcKPRcsto7/qbyxvWvahKBBbHl53XnSb0WtLUyTEaXJ+0l5gy
Epxfl6ZRNkt+YBYBqCZL9aK6TNpCR+zTC7OhrKcZntqFYCeRB25YEfc1z0lTH4SE
TPymGSMOeUX8YEIr+XNYG0H/I7ZtEdNGBEXaUrC4YUNrvojpq3PS9bhfLhAf5D11
wmI2tS9cqxAuS2zkaBr9Qo5xNEVsyGNFd1K0q0JJosiMnVZDzOA4wJTYhOw5gwcW
T1Kh/LDvK4RE0CV0jRqN81YO8V6Jq+/c2kgtwTLzrx1GCcdHRsY2kPhCEWOTLKb8
Jn5nyyvOOjMIkAEYYqoRRxl05UbNWNS9SuO2g9HF6JpNJmw0cmzT8erIIjVvaUs5
dd0Bd5EgkkLq3RMPNTiidQCyIy8QtoElmxYfM11mbf+JMgxsB3sDcRGN4lauUhho
JlayuFUfrQEwtHFwyULiJwUC55faVqOQzyvcQ8C9AJezDXS7//ku1kvEgbzK0nFm
3bCCiuog7pG9XlldSReYvlJtnqTmp6E5+m9YaP+yB2E+swKBORHkbCfBu1CuX6R+
aryBYIDeLaIiJs5GWJN//37TT0hVUCipY9QISfTiZ5tIy0aNKRu9syjWphcoTJuv
YBaRxKNdZs81ppFTa3sXB824+QXwzRJJPjBndXOtyX/a2U68NK3dqau/A5190VAn
bn7jnJ68rhcL9ewwydQRm72qwyxBbRHFXnEXpkJhzYRw6fosglqbOJMxFiy36HWD
RkVluvTInw8Em/DhhWgn+UrxLlQN6JnVdpkT9hwrlFWooiM56UKAPO2a1FM2XKXe
TqsolY07uWnA+ouiljqIpsaFlVDBcz/sGZWqy4aKKPBTcZlreW88PMsS0c9nixVi
nKnmI00HuzeL85zVHGo0IKYfktVVjuZNPzJ6+w18XpKIi8TxHpxELC9M2kVOCnuy
uwuntJlRty+GpXVvx2VLi1wBKyLpXUxhyg1tSi2sZyjIdVt4yEOKfajZYOp73JZx
IcInD7Cby9olkMsa25YhloljY8YELfW8EWutF13m8tsjGP9c6aOuMIXhrrpwLgYy
E27iFh9tHq0mjCMibwQnHld7Ccd4OqO4AKfPofHDxWoY9+ivArOBAXjsCxhxWWWp
aLqLJz+JH+idcsVDlw8jJzFW6pQFbM3VxXObvCg9ou5+P+Pc5XYyALJzIlmoOrN+
ns5Z+U/2XKGyySQASUyFXUNml6csSrTd+ejz1QvEX9POU1nLmvS1+aojgnptgdpn
sAtksQHMt1Njo1oRug3+/0iC6XWEig==
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,28 +1,35 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSBncWNO
M2Rzc0xVZ0hvQWxtZTJJd2dZWUZ5MkVUckZ5MU9lcFJlN0xQS3owCmdvZTBZNXIy
dTIxdytiVzJGeXFwTjZHY1hoWWlVVDhOZnRmRWJZcS9CY1UKLT4gc3NoLWVkMjU1
MTkgVkZjdzVnIHRLRisyQ0ExSkUzR2R6Z0xocmZ4SXV6YUlsMnZESm5oc0NOdEFK
WW9TUjgKclVCbmZLNjBNUjErcVZ4aklHTU1KRVd2SXpCQzlPcTBtUkgrT3QvY1pG
ZwotPiBYMjU1MTkgSUZwWE5pOGNMeHZOU1RhVlBIckZ1UmlZT3JXNUlTYXdmYUo0
UU5KQU9Edwp2STJ0VE9jZDJ4aWhaVFFuajRHR3E2UzVsZDNHbHVTNW9aVlB4UGRu
UU1VCi0+ICVBLi1ncmVhc2UgbGBiZ0MnelUgK0ogcyU4dTIgVGQKNkJ3MgotLS0g
Tlg5TmJpVkV2dkJUZDI1YjR1MitSTW05dHdpTFdGOFlQemwvQ2p6dVpCQQrcRVSM
EaN+F8T+cF6tMjJC3BOF1wYm+iJ1EVesnuFa85J4T50sRQgb+ePlJIwR87NYRrNn
9wpiDSgv+7dS33yfqVo8M6/Bvt/wanBK2uteEfoJyB0a6UZ4cbRqAr8msFCriyPY
GrGZpinA/h9jxr+GlQdS+WYce3xbzc4b1tKzHY3NiUY9aXWokhXjzjTzHrqefBLW
wa0EvYFUxuazoVX5mjhG+pET/WEYRPGZoIfLUTRvV1Xx7W+/7SIsqLYLKqqb+GLk
ec6P1104mIjhBY9TUbbeMUMTMBoskd8nr387QQAtU9c89FgvlZ4am3X0L7iUv9lP
hxTOgK86HwrGK/eKzO62gSjIfrRWBOMDWl2iCZ/4t6pCGnUxky6IA4iN/slnKBEg
Y+V/8YQ5iQN7F9UpRSVkaD6IGEaY8gu2A9FDfh+i/KkfgBsAtLGhf8XavKOLCjYR
1csUFlyJMn+Va0iPr8YLlLmxn+JU8usX/Fysun5qYHLoeTJAUWV0yIIrCgk0XvgT
P9Rj12ShAKeln+nNX8hTA9WgEoqsASxUvJyAP+m09fOczr2xLcROU6YZfXLxb03R
GCBJtPf5pYWRW+562rjg2gnnkvsi4VhpG6lnaPitZDXEE5VAoErFTEo2NEKfdaJv
SwTYt8cb8Y2GR4Zl5qJEZnlC9GbJo2Xy4fAEfCo0Ox9uuJrvchXIl/tIu5W2o5ie
i9FnaU0ZwGBWn8gayOPgos9nNsxqj5s5XL11IyWvcxapP1eomf6ud0nY0RiQNVmN
tj/oFRGFBUkva7amacWrYmUUD/JSc6s2aJSI3kuiuFkaBAXF3UPRLtg7Gw4B+OL3
VkJiBpNP4KJUvmMBAbHDoIIVwNW854BFSI6R4kLGC1FvsEbx+aPu6dPFNnzO9Yom
fWH0Q2FHBU7OLKJygm+X6jqQishe6mcaVQl5vaP7EHuylKwmA+dFCoHkTY4/gpT8
ikSOzoIguvIITXEJdRi8+K08QfJ1h6F4Hrzj1BOZj6aWTwHdrBcAFzRniRVtPoaN
Z5fwht5nryfb5wuGH8Smvycqx2EXgu0TURtmd3k=
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IERMTWVGZyB3QTU1
TEdvbFNaUmE4U1lCNzI1dlloVDBTNXdURjZ2akRMV0R6dm5ST0FNCk9YclRSakhT
NlV2UVJwak9Fa3dQNTNBK0xKWGlkNXdwZTBEMm4xZzc3azQKLT4gc3NoLWVkMjU1
MTkgakk4UkFnIGNBMHNrdG84cUQxd1lqQmJIczlScU1jNk52Y3BtR2tXeDNWdWRX
cEFCR0UKVU5vem5MZUFPbGhoQzJTYnF4OTd3OW9jYTRkazdocVJkZ0pRRGNLek93
cwotPiBzc2gtZWQyNTUxOSBnU3hQMFEgQ2RUaEUyV1ZGbGRtZnlIUEtTQXk5MUZF
djYxZ0hBUThlV2tXTHNvdXpVSQpMdiswMy9QNUtCb1hkbGRqR0Nia3FXTzE1ajZL
UXljSTZqM2YvbzVuWUFzCi0+IHNzaC1lZDI1NTE5IFZGY3c1ZyBYL1NrRjRaRnFn
RlNLdWJ3cThteDM3WmNaY210d3RmZzhCYmNpVXBwVGlVCjBkM0IzZC9zNjhmTTZV
R25ySkVoQWxQQ252WWFlWWZFMlc5dWtUN3VvVTgKLT4gWDI1NTE5IE8yUU1pWGFr
NzUvZVpwalB6aklkWmJGWUQzTnJiRjNzdzY2MUp6MkY5M1UKR1VIWDVyTFpxem5F
TmRNWE9zZStLanRTdlU5d0NOSEJKdWhDUjBCdy9vOAotPiBTJCd2NyFYey1ncmVh
c2UgfE4mYSBXfms0TC5FPiBiYmx8IGRFV35mciNoCjVZdHRiVUtMMWxEMlJ4b1F1
TGgxY01XTlZpTEtndkg0T0hRVjlqVUJGMUpSaE5tVlZWR3VGenpkUXY2eXJtbmkK
ZCtqRVN4dnZENXdoYkpjNHRWYXkvN2laY1p4YjV0WQotLS0gOHlEVTVOSmlNemNQ
TW5ISk1DeDlEM1RoQ2JtUFlyTXRKQjdnT0hiend2VQqrUFvr+76sKn0ldBmZMlEW
U2k85DLo2KU+/+GtbkZwVXxxIZHMLpoJgghHk9ptdalUgLGcl0X15x9jVaw8aeta
hbeOHotRHY7bC3z0S74riTk3xDMR1eT0QGhDMWHjfo8SkCftOYBlFfhTftevdep3
pKMZsuQMwH9JzxgUfcxIcWE975cZzrEJ85nfWMGvdSjcg51KNxP/UUPRxDlcbCEf
9XX5apSzNsTI3ibGD1n6Qwq8bdVYDMHmy5pAhw4l8L+SdoU1tGdw7JOA16sMCJbx
T4bV0ky/PGRonjJuCyDBj8oe9vMe1ZI1O/ITtktekS+wocxBs6QXlY7pIZMlGUn2
6m59ZEEaf7R4/MdnmBDNDkQuyXaKc7SaTc6h5sKWzXdYScGUKvgUQ7U/WJ2ItUTC
N/Xq07GkZZMt5MYBlyEr+/mKWlcy+ylJPGb7EswvQWaHoeM1QF0XLZ1v+W/Xsso0
seIoz+geSu9a02kwfsa8WvWXdIAT5X2pNGPClVNzjQ23pfQfQuW8ZQrGmIFR4g5A
58T1K+vGLdShqqVGyJFMVrSuOzqX5FVmZalu7/++1IQfiRGUlrHKoPlKWnCfFEOu
AYjaPeEFX2ByxcqfMK1YVPvUufdISUQeaQOO7mXGE3FqB0oUqmRIUiWZATwhq3Pw
p5QdcySTnmMpD/w05hvwski77kCdmYuHlMlLZez/kfhTnIGXris+Vwi/V19bsZ8G
zwaZ/Xr6WNC+df5JqSfTGREnXZPFRDkaTt3ri5/eEm6BqliuYjGbuiKsDECi4+JX
bHpH6LBBoKQ6ms7jCAn0Ls4cUKF37PcjGAOuWnzCSBU+REht1EDfHzx4C7hNiP8X
87NjEqJbwE9lORho0hQJRTn8uriQcidlVoB3se2SYKbMy8UA4NNnxN9PTj0TuQjL
OD3LtqHBElqNPbGNyyEAAJmMBmmkUvPPXlGQ0D99b1+jIdHzYSRtOLshBFykqWYQ
LJD61duhGqcQqcLx4+JdQ+oVcfAI2nG7YINnHB0OmS2DOZvvwqQ7ASScSujUWIjA
LNQxu3ruMz+bw/G0tYZBBiE=
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyAwekMw
WG9LeUliQWhKQkpPbFljb3JpaWpJajlEL1htK1hVbDFibHRTNXg4Cng1RG9SMFhX
RXZoWHp4MFNaQ2FFeUpmV2lFOW04bzNRRGNnYWZ0NUROemMKLT4gWDI1NTE5IEg5
UUlKdGNRam5IMUp5aVQvUTI4eE9kWXV5cXR0blZnYTVFTTVaRlVqZ0kKQXRMbG5T
ZXgvVGpybTBlbjh0RDRnVm5wcXQxZk0yY0FCbW9UR1dTZ0pKVQotPiAuYy1ncmVh
c2UKZjFEV1RVRE1TVVUyLzNCZHp4VzlHRmNKZCt1bDIyT2lnWDNuTVN6ZHNpSjJX
UTVuY0gvYlcrdm40ZUYxem55QgpXSFdFaG5hMUdIakRpaHIzQ280eEJUdkRkRTBm
czdxNnZxUlh1dwotLS0gclFJQTd2OXQxZUJIdFhJcXBtR2lJT1J6RVR5dnluV0hM
VUdqMjVqMnNQUQqejqBp4AOrEgmQHT6loz9TYgmUK6iohAAcxKPRWKpSHFSPb6jH
nJSMdNXr9jo5iwETMANiRTCVOLTkuO/RqD6XwNoD+0wFkI/BD3Itlw==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBEcDNm
ajc3a0gwYmVSdkowak1aakJ3VWZ1Rk50S1JIS0ExVktVSW9OVWljCjJqbUEySlNK
a0xyT3NPK1BBMjZzN0RrLzhwVitvS0pzQ25iSnJSc3FmM0EKLT4gWDI1NTE5IHk0
M2pkWHgyMHR1WHc5bE4xWTFDTndKdkhRdTg5bzZIWEU2cm02UDZBQjgKUUY3dS9p
U3h1L1ZvTkFPdURLZ2tSYXJXOGNZZ21KVGdIbXdhSUJrd2puYwotPiAoL0RtO0lH
OS1ncmVhc2UKVGthN0ZSU0ZFTTg5YW9UOXMwa3RnSzFlMjE2VTN3Ci0tLSBwMEMx
Q0tQckd3SzBwUUE4SndMV01kUjVrOTdDWmxlcWs2Zy9TZk5yMzhvCrBXyLBZGuSD
dJodNI13obTM3UvX6hSgQ8Su+J3fOKr5NibkhQ0Auvlr2tUXhhDm2WOUlHjqVTq+
jWLSyhioDlIEyBgk8Zrl7KGeDzBi
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBYc04x
SC9kR1Y3anNQUGZSclBSMVIrMDFqZDFlZVhNWWVSb0JZc1FqYjM0Cm5nUWJzWllO
NE16amkvWFZ6Wms3ZzdLTjdpRUZOdndyeXNqMWZOSlJtZkkKLT4gWDI1NTE5IFV0
bVN0U3kxZENOU1RuaHVseUI0cGhtRXQyWHgrM0ZYNmpvLytucUUva1EKNS9QbU1l
NFovRXFnTnF1RnRkK3V6KzhBeDZMQ3ZPdGpva29zM0ZzcXdVNAotPiBZXzBzSF8t
Z3JlYXNlIGRKKTpgWyZXCitVTUg1VnRKU0dhUmZBY0F4VnNqNlprRE1YNWRveWw0
NTRDZUNNbm9MdGhlclMwcTZaS1BOMDgzRlp1N1k4eFgKVkcwRAotLS0gMS9tcDJL
c0JKSjdIQ1Jpelk0U1lCOHhBTjg1bGxuc3lsV3FzRkl2NnRnWQqapBTPnK0EVrvY
ycL8K7wHa/Wx6icU46t/F9rlwAdSTc+hrptogpPbEb0HxC9jjVacJOiMQI8cW7Qd
NpBXk3wdNtroDHbl3ZD4APcXohOf6fKVnuA4l5C3o2q9tA1grmcQAMwu+GjX+QAN
S3jIH3AUjjzIQLccetH+SZDIy0p9F1m/5mxTF2hEUBBH1MxHWMZWJ9kg
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBzaEo5
RzNndHBLMUl3Z2txdVZoOVJBbWsrTVJwY01WeHQ0VTBaSm5VclY0CnJLYStYNEI5
VTN3dEQ0ZjBKaU1PZSt3d0lldkE5VC9wNHdWazNWV2F6WWMKLT4gWDI1NTE5IEJ5
amlzTDhCOFF0TllPd2RsME5Cb1lRQUZPTGNvOEZnL1J2Wlpnb29GblUKT2t1NFZy
blNZSHdxZjlHTlZmaTFYT0laQ1IwOCtDZ21vTjhxM0owdTAvawotPiBLYS1ncmVh
c2UgRX0nVGtpSCUgNVNDWCVwIDBuPjM3MwplK2cwc2htQ2RLcFhUZ0RSc21uQlFV
Y1QwSStOK3lKYmVVQ0J5RXA3Zytaa0JoMWtlWU9qMHBLNktFZWxtbFpDCnJyS0JJ
WEcwbndYbERreFhLZlRyQ3E0czB5NkREancxZk5BTGdWRkR6N2NrZmFrbnhSYlZZ
NElHNEEKLS0tIGVLdXAxU0ZaZVkxaHV4dEZNNUVuc2J4N3VRMzZzTktSMDY3bDVB
bWtmUVkKigIZ3J0s23vNzmbzJGjSMGBXK6o6xnsA9HXeQZ13VgKv5Qv+UHu+Z0g2
TeKdQSrHbDB3ydIxaiXsi2ivULdrIMCyd96rEJFxrkVuVqSZE0ehG5j+o8lKk4OU
PDe70slbZrVYDSu+OOUOCVRSopZreCURlYn2Pc4rjvrMAn5r1r+/AxjZMdkmmQZ0
l0wMBTus5zZzKg==
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyB4Q0Qw
UWNSRmJ0V3c3NlppdXpueUlFdUJSWmpmWlU2OWN5MjNYVFJpa0NzCmVHbnhxSS9t
bGZCSmpzMGtNalBKZGVhV0h0SWc3azV4bmU5U2UwaU5Jc28KLT4gWDI1NTE5ICtu
d29NM0JORm5iZGFzUm52ZkFIYkFwci85TWRqL092WXozdUM0aXNYVm8KMEhtUE1j
S29raExJM01tNENMcTk2UzFTaDZrRmdkUm02OTdiZ1k1Y0VjcwotPiB7aU42LWdy
ZWFzZSBzIEM/MCA3KiROIC1QSkwKSWQvSkhUUVd4TzEwbTI1VTViWWlqY2QzTFhj
TVYvcHYKLS0tIHk5YkZ2M3FPY0lJVldvVTJKd3NTSHhOeEpyOERVa0c5LzlBU2Nt
SHE1VTgKVVe4VNvZral5pLuXVu1padzWA2w+xWhQgTtMVRbbO3fBvzzHzhbbmojV
/RAKf8roFkc89xJ9W+QGvhuHqoCEam7RYqc+Zt5AKOYVVxqDCUEb7bEAAXU5yp98
t0kTX0AxriBfgrjPSMcnN6MsYs1VPn4YhDKdOy1Kg01S1x2UKd2b+JkBh4dJJQ80
PBHPrCFbbbldQg==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBoZUI4
bEdWSW1pcDdMYVRJLzdQOWhsdTlZMmlyZjRVeEFwU3Z3NzVFa3lBCkxRZ1FYeU9D
L2dramp4WFFlREZ3NTFnWUs3clVEcnBhLzExclVac1M2SG8KLT4gWDI1NTE5IHVZ
bk9QYy94Tk50c1dVanNvNzhpYWNqeFMzVVR0eTQ5OXozMmp2VFlad2cKaEdvUnR5
ZjhsQ1FRaittQXFkbnRZei85MmtWb3pXRVFsc0RzNzBPMXFMWQotPiAzSXJZb0RE
LWdyZWFzZSBudCBGIDklTWIjVnwgM3YvCjRVYUd2TForc0gycnRBR05MbU90QmNu
dXlmQVB2bnZyME5heXlyMUdGTmR6SVd5SHcrTThrZEJYRlpnUmdmWnkKU1lyNjQ3
T0J6b3NPQ1FYZnhxTjFrYmN1UUJzMjEwcDBwSTJkTnlmeHl3WmFFTXB4eUVMWm9i
NExUZzM2SHlLLwpiZjQKLS0tIHdqakpMSUNOdnlBcVA4RGJHR2d5QnJNT2dVclZN
djd4WXBLSUhORktzZ2sKzMjxqL3UPrtGmXDijdfu0AwxLJooK7ZKauYvXWSuZUrR
vl5i1QPMEEBYEl+NaZIFgxAFqWpAXHQ1VxSijMWxNjKUd1Chq0tbLrpbh9wXJflK
SW19Wyqc9eTb8BnFoOSYWpDvAs2Lbyr2kJkOj2TNrwMv8nDJdOB8XIPlV5drCj1q
Dx/1zWqm6NMYxsep6eZkv4AzxA==
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBnaWFz
ZVlRd3pPTVpCM3dkakJXbnJid3NWYlFRZmJFREpJb1hHMGJucFFVCk0rRHh1bFBG
UnYrdm9xUUFLNVpyd2xRTnRFQ01mUGpOb2lieUh5ZDJTMTgKLT4gWDI1NTE5IEky
R0kzYTZQZnh0TGFXSzhaUUp4enNlZTZXQTUrZEYrbWE3ZkRPcitkbGMKa1dVbUVj
WUZGMUZpUXVCOXBnMnlXRVgxVzUvZjlyNFdFMjlzVWIvS3FUMAotPiAxcFJRdC1n
cmVhc2UgWHZ2Yk97W1wgK0RPClJWSmczZy8vK25iYVVIWVdmSjZMZ09zb0s5SStz
OCtrcGxVbTh4ZFhNU0lya2VML3VOWVgxWDAKLS0tIGR5MjhyOU1naGJyenVBYWZ6
ZUxRNmgwMVVON2pMWlZybHN0dXI4SS9HMXcKZTI6JjBiTsBbdv8Mr8REAxlVIG0q
Cl4PrX/rgiOAJbtrUsvBuIABgeWxPordi1yMXL8XQDZDs++O40nXAtW95zjd7pAH
nwlFdNyq
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBrd0pM
TGw1anJNaGoyQ3lMUis1cHVtRWFPcFFNZk9Cdm9TMEh1QTdKc25ZCm9VNzVQcUxE
Njh2Wi80RWszSU5NczRNcHpVYXpRMzJNcDRsdGxyWVZsUDgKLT4gWDI1NTE5IFpT
MEJVUkcycVFJaVFOUURoL29VT3R0MVJCWW04NXVRVEpKY3hlY1lWRkEKcENya1Jx
VnBCMVBrZkc4VDkvMnZoTllCL09vQ0VOZytuMnRvYVQxL2FldwotPiAzLWdyZWFz
ZSBCNX0gIiB7InIsfCAkOT9uM2UKUWpSYTZ1dUo3SHNaUlFibTl0UDhTaFh5Mk1n
bFp2cjM5RGhqU0s0Qm50Zk1pUVZmT0R6ZjBoUG1EUGlKbHFzVwozQ2t1cWVDVVAv
Q21QTjF3NW5UdTBZRldnL0RmSlV3QndHcDB0d0lvZ1RHTTByS1ZwNkNVQnU3WmVn
ZwotLS0gb0Fxb2FsYzgwRjJwSUF3ei9hZVR2Vk1ORDlIMWoyZ2RTd09hUmtvMWpD
cwrEjdaYfoGZ9i/S97xL9QvA/yii+sJLeuUzzv7a3DE661eQ5ezurV8Qz1tIhxWG
RsOppaaj1podFx3U1x7QQbLO6zQbJA458RMjYgc=
-----END AGE ENCRYPTED FILE-----

Some files were not shown because too many files have changed in this diff Show More