Compare commits

...

122 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
140 changed files with 12699 additions and 1412 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 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) (nodesFor homes)
); );
autoRollback = true; autoRollback = false;
magicRollback = true; magicRollback = false;
}; };
# Filter out null values so deploy merges overriding options correctly # 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"; help = "Print the ed25519 pubkey for a host";
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'"; command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
} }
{
name = "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"; name = "fmt";
@@ -89,7 +95,19 @@ in
name = "build-iso"; name = "build-iso";
category = "tasks"; category = "tasks";
help = "Build NixOS configuration into an ISO"; 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"; name = "build-home";
@@ -121,5 +139,11 @@ in
help = "Update home-manager flake inputs"; help = "Update home-manager flake inputs";
command = ''update-inputs home-manager-{unstable,stable}''; 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, ... }: { lib, pkgs, ... }:
let let
inherit (lib) concatStringsSep;
inherit (lib.my) attrsToNVList; inherit (lib.my) attrsToNVList;
in in
{ {
@@ -13,9 +12,9 @@ in
NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf" NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf"
'' ''
experimental-features = nix-command flakes ca-derivations repl-flake experimental-features = nix-command flakes ca-derivations repl-flake
#substituters = https://nix-cache.nul.ie https://cache.nixos.org connect-timeout = 5
substituters = https://cache.nixos.org fallback = true
trusted-public-keys = ${concatStringsSep " " lib.my.c.nix.cacheKeys} ${lib.my.c.nix.cache.conf}
''); '');
INSTALLER_SSH_OPTS = "-i .keys/deploy.key"; INSTALLER_SSH_OPTS = "-i .keys/deploy.key";
@@ -25,7 +24,10 @@ in
coreutils coreutils
nixVersions.stable nixVersions.stable
rage rage
deploy-rs.deploy-rs (pkgs.writeShellScriptBin "deploy" ''
exec ${deploy-rs.deploy-rs}/bin/deploy --skip-checks "$@"
'')
home-manager home-manager
attic-client
]; ];
} }

253
flake.lock generated
View File

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

View File

@@ -7,13 +7,13 @@
devshell.inputs.nixpkgs.follows = "nixpkgs-unstable"; devshell.inputs.nixpkgs.follows = "nixpkgs-unstable";
nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
nixpkgs-stable.url = "nixpkgs/nixos-23.05"; nixpkgs-stable.url = "nixpkgs/nixos-23.11";
nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0"; nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0";
nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable"; nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable";
home-manager-unstable.url = "home-manager"; home-manager-unstable.url = "home-manager";
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable"; home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
home-manager-stable.url = "home-manager/release-23.05"; home-manager-stable.url = "home-manager/release-23.11";
home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable"; home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
# Stuff used by the flake for build / deployment # Stuff used by the flake for build / deployment
@@ -26,7 +26,7 @@
impermanence.url = "github:nix-community/impermanence"; impermanence.url = "github:nix-community/impermanence";
boardie.url = "github:devplayer0/boardie"; boardie.url = "github:devplayer0/boardie";
boardie.inputs.nixpkgs.follows = "nixpkgs-unstable"; boardie.inputs.nixpkgs.follows = "nixpkgs-unstable";
nixGL.url = "github:guibou/nixGL"; nixGL.url = "github:nix-community/nixGL";
nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable"; nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable";
# Packages not in nixpkgs # Packages not in nixpkgs
@@ -34,6 +34,9 @@
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable"; sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
borgthin.url = "github:devplayer0/borg"; borgthin.url = "github:devplayer0/borg";
borgthin.inputs.nixpkgs.follows = "nixpkgs-mine"; 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 = outputs =
@@ -48,8 +51,8 @@
... ...
}: }:
let let
inherit (builtins) mapAttrs; inherit (builtins) mapAttrs replaceStrings;
inherit (lib) genAttrs recurseIntoAttrs evalModules; inherit (lib) mapAttrs' filterAttrs nameValuePair recurseIntoAttrs evalModules;
inherit (lib.flake) flattenTree eachDefaultSystem; inherit (lib.flake) flattenTree eachDefaultSystem;
inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay; inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay;
@@ -60,7 +63,7 @@
flake = flake-utils.lib; flake = flake-utils.lib;
}; };
pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; }; 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 # 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; }) { pkgsFlakes = mapAttrs (_: pkgsFlake: pkgsFlake // { lib = pkgsFlake.lib.extend libOverlay; }) {
@@ -92,6 +95,7 @@
inputs.ragenix.overlays.default inputs.ragenix.overlays.default
inputs.deploy-rs.overlay inputs.deploy-rs.overlay
(flakePackageOverlay inputs.home-manager-unstable system) (flakePackageOverlay inputs.home-manager-unstable system)
inputs.attic.overlays.default
]; ];
})) }))
pkgsFlakes; pkgsFlakes;
@@ -112,18 +116,20 @@
nixos/boxes/colony nixos/boxes/colony
nixos/boxes/tower nixos/boxes/tower
nixos/boxes/castle nixos/boxes/castle
nixos/boxes/home/stream.nix
nixos/boxes/home/palace
nixos/boxes/britway
nixos/boxes/kelder nixos/boxes/kelder
# Homes # Homes
# home-manager/configs/castle.nix # home-manager/configs/macsimum.nix
home-manager/configs/macsimum.nix
]; ];
nixfiles = evalModules { nixfiles = evalModules {
modules = [ modules = [
{ {
_module.args = { _module.args = {
inherit lib pkgsFlakes hmFlakes inputs; inherit lib pkgsFlakes hmFlakes self inputs;
pkgs' = configPkgs'; pkgs' = configPkgs';
}; };
@@ -150,52 +156,49 @@
nixosModules = nixfiles.config.nixos.modules; nixosModules = nixfiles.config.nixos.modules;
homeModules = nixfiles.config.home-manager.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; homeConfigurations = mapAttrs (_: s: s.configuration) nixfiles.config.home-manager.homes;
deploy = nixfiles.config.deploy-rs.rendered; 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: (eachDefaultSystem (system:
let let
pkgs = pkgs'.mine.${system}; pkgs = pkgs'.mine.${system};
lib = pkgs.lib; 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; shell = pkgs.devshell.mkShell ./devshell;
in in
# Stuff for each platform # Stuff for each platform
{ rec {
checks = flattenTree { checks = flattenTree {
homeConfigurations = recurseIntoAttrs (mapAttrs (_: h: h.activationPackage) homeConfigurations = recurseIntoAttrs homes;
(lib.filterAttrs (_: h: h.config.nixpkgs.system == system) self.homeConfigurations));
deploy = recurseIntoAttrs (pkgs.deploy-rs.lib.deployChecks self.deploy); deploy = recurseIntoAttrs (pkgs.deploy-rs.lib.deployChecks self.deploy);
}; };
packages = flattenTree (import ./pkgs { inherit lib pkgs; }); packages = flattenTree (import ./pkgs { inherit lib pkgs; });
devShells.default = shell; 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 = { nix = {
package = mkIf (!(versionAtLeast config.home.stateVersion "22.11")) pkgs.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" ]; experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
max-jobs = mkDefault "auto"; 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 = { tmux = {
enable = true; enable = true;
keyMode = "vi";
}; };
bash = { bash = {
@@ -235,13 +241,6 @@ in
exact = true; exact = true;
}; };
}; };
settings = {
substituters = [
#"https://nix-cache.nul.ie"
"https://cache.nixos.org"
];
trusted-public-keys = lib.my.c.nix.cacheKeys;
};
}; };
}) })
(mkIf config.my.isStandalone { (mkIf config.my.isStandalone {

View File

@@ -61,6 +61,7 @@ in
settings = { settings = {
background_opacity = "0.8"; background_opacity = "0.8";
tab_bar_edge = "top"; tab_bar_edge = "top";
shell_integration = "no-sudo";
}; };
}; };
@@ -166,7 +167,7 @@ in
"${mod}+Shift+x" = "exec rofi -show drun"; "${mod}+Shift+x" = "exec rofi -show drun";
"${mod}+q" = "kill"; "${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+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+s" = ''exec grim -g "$(slurp)" - | swappy -f -'';
"${mod}+Shift+e" = "exec rofi -show emoji"; "${mod}+Shift+e" = "exec rofi -show emoji";
# Config for this doesn't seem to work :/ # 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; playerctld.enable = true;
spotifyd = { spotifyd = {
enable = false; enable = false;

View File

@@ -70,13 +70,17 @@ in
"swaync/config.json" = mkIf (cfg.settings != { }) { "swaync/config.json" = mkIf (cfg.settings != { }) {
source = configSource; source = configSource;
onChange = '' 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) { "swaync/style.css" = mkIf (cfg.style != null) {
source = styleSource; source = styleSource;
onChange = '' 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
''; '';
}; };
}; };

View File

@@ -1,17 +1,32 @@
{ lib }: rec { { lib }:
let
inherit (lib) concatStringsSep;
in
rec {
# See https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/misc/ids.nix # See https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/misc/ids.nix
ids = { ids = {
uids = { uids = {
matrix-syncv3 = 400; matrix-syncv3 = 400;
gitea-runner = 401; gitea-runner = 401;
jellyseerr = 402;
atticd = 403;
kea = 404;
}; };
gids = { gids = {
matrix-syncv3 = 400; matrix-syncv3 = 400;
gitea-runner = 401; gitea-runner = 401;
jellyseerr = 402;
atticd = 403;
kea = 404;
}; };
}; };
nginx = { kernel = {
lts = pkgs: pkgs.linuxKernel.packages.linux_6_1;
latest = pkgs: pkgs.linuxKernel.packages.linux_6_6;
};
nginx = rec {
proxyHeaders = '' proxyHeaders = ''
# Setting any proxy_header in a child (e.g. location) will nuke the parents... # 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 X-Origin-URI $request_uri;
@@ -25,18 +40,76 @@
proxy_set_header X-Forwarded-Protocol $scheme; proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Scheme $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 = { nix = {
cacheKeys = [ cache = rec {
"nix-cache.nul.ie-1:XofkqdHQSGFoPjB6aRohQbCU2ILKFqhNjWfoOdQgF5Y=" substituters = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" "https://nix-cache.${pubDomain}/main"
]; ];
keys = [
"main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8="
];
conf = ''
extra-substituters = ${concatStringsSep " " substituters}
extra-trusted-public-keys = ${concatStringsSep " " keys}
'';
};
}; };
pubDomain = "nul.ie"; pubDomain = "nul.ie";
colony = { colony = {
domain = "ams1.int.${pubDomain}"; domain = "ams1.int.${pubDomain}";
pubV4 = "94.142.240.44";
prefixes = with lib.my.net.cidr; rec { prefixes = with lib.my.net.cidr; rec {
all = { all = {
v4 = "10.100.0.0/16"; v4 = "10.100.0.0/16";
@@ -74,13 +147,179 @@
vip1 = "94.142.241.224/30"; vip1 = "94.142.241.224/30";
vip2 = "94.142.242.254/31"; 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 = { fstrimConfig = {
enable = true; enable = true;
# backup happens at 05:00 # backup happens at 05:00
interval = "04:45"; 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 = { kelder = {
groups = { groups = {
storage = 2000; storage = 2000;

View File

@@ -1,13 +1,26 @@
{ lib }: { lib }:
let let
inherit (builtins) length match elemAt filter; inherit (builtins) length match elemAt filter replaceStrings substring;
inherit (lib) inherit (lib)
genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types
mkOption mkOverride mkForce mkIf mergeEqualOption optional mkOption mkOverride mkForce mkIf mergeEqualOption optional
showWarnings concatStringsSep flatten unique; showWarnings concatStringsSep flatten unique optionalAttrs
mkBefore;
inherit (lib.flake) defaultSystems; inherit (lib.flake) defaultSystems;
in in
rec { 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; attrsToNVList = mapAttrsToList nameValuePair;
inherit (import ./net.nix { inherit lib; }) net; inherit (import ./net.nix { inherit lib; }) net;
@@ -29,6 +42,8 @@ rec {
ports = checked (elemAt m 1); 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; })); mkDefaultSystemsPkgs = path: args': genAttrs defaultSystems (system: import path ((args' system) // { inherit system; }));
mkApp = program: { type = "app"; inherit program; }; mkApp = program: { type = "app"; inherit program; };
mkShellApp = pkgs: name: text: mkApp (pkgs.writeShellScript name text).outPath; mkShellApp = pkgs: name: text: mkApp (pkgs.writeShellScript name text).outPath;
@@ -109,6 +124,21 @@ rec {
home-manager = mkOpt' (enum [ "unstable" "stable" "mine" "mine-stable" ]) "unstable" "Branch of home-manager to use."; 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: { networkdAssignment = iface: a: {
matchConfig.Name = iface; matchConfig.Name = iface;
address = address =
@@ -123,6 +153,9 @@ rec {
LLDP = true; LLDP = true;
EmitLLDP = "customer-bridge"; EmitLLDP = "customer-bridge";
}; };
linkConfig = optionalAttrs (a.mtu != null) {
MTUBytes = toString a.mtu;
};
ipv6AcceptRAConfig = { ipv6AcceptRAConfig = {
Token = mkIf (a.ipv6.iid != null) "static:${a.ipv6.iid}"; Token = mkIf (a.ipv6.iid != null) "static:${a.ipv6.iid}";
UseDNS = true; UseDNS = true;
@@ -134,13 +167,32 @@ rec {
systemdAwaitPostgres = pkg: host: { systemdAwaitPostgres = pkg: host: {
after = [ "systemd-networkd-wait-online.service" ]; after = [ "systemd-networkd-wait-online.service" ];
preStart = '' preStart = mkBefore ''
until ${pkg}/bin/pg_isready -h ${host}; do until ${pkg}/bin/pg_isready -h ${host}; do
sleep 0.5 sleep 0.5
done 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 = deploy-rs =
with types; with types;
let let
@@ -188,4 +240,18 @@ rec {
filterOpts = filterAttrsRecursive (_: v: v != null); filterOpts = filterAttrsRecursive (_: v: v != null);
}; };
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}";
};
};
} }

View File

@@ -67,4 +67,35 @@ rec {
(a.ipv6.address != null && a.ipv6.genPTR) (a.ipv6.address != null && a.ipv6.genPTR)
''@@PTR:${a.ipv6.address}:${toString ndots}@@ IN PTR ${a.name}.${domain}.''; ''@@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 = { nixos.systems.castle = {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
home-manager = "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, ... }: configuration = { lib, pkgs, modulesPath, config, systems, assignments, allAssignments, ... }:
let let
inherit (lib) mkIf mkMerge mkForce; inherit (lib) mkIf mkMerge mkForce;
inherit (lib.my) mkVLAN networkdAssignment;
in in
{ {
hardware = { hardware = {
@@ -25,7 +46,7 @@
efi.canTouchEfiVariables = false; efi.canTouchEfiVariables = false;
timeout = 10; timeout = 10;
}; };
kernelPackages = pkgs.linuxKernel.packages.linux_6_5; kernelPackages = lib.my.c.kernel.latest pkgs;
kernelModules = [ "kvm-amd" ]; kernelModules = [ "kvm-amd" ];
kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ]; kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ];
kernelPatches = [ kernelPatches = [
@@ -83,6 +104,15 @@
blueman.enable = true; blueman.enable = true;
}; };
programs = {
virt-manager.enable = true;
wireshark = {
enable = true;
package = pkgs.wireshark-qt;
};
};
virtualisation.libvirtd.enable = true;
networking = { networking = {
domain = "h.${lib.my.c.pubDomain}"; domain = "h.${lib.my.c.pubDomain}";
firewall.enable = false; firewall.enable = false;
@@ -117,6 +147,9 @@
systemd = { systemd = {
network = { network = {
wait-online.enable = false; wait-online.enable = false;
netdevs = mkMerge [
(mkVLAN "lan-hi" vlans.hi)
];
links = { links = {
"10-et2.5g" = { "10-et2.5g" = {
matchConfig.MACAddress = "c8:7f:54:6e:17:0f"; matchConfig.MACAddress = "c8:7f:54:6e:17:0f";
@@ -127,15 +160,33 @@
linkConfig.Name = "et10g"; linkConfig.Name = "et10g";
}; };
"12-et100g" = { "12-et100g" = {
matchConfig.MACAddress = "24:8a:07:a8:fe:3a"; matchConfig.PermanentMACAddress = "24:8a:07:a8:fe:3a";
linkConfig.Name = "et100g"; linkConfig = {
Name = "et100g";
MTUBytes = "9000";
};
}; };
}; };
networks = { networks = {
"50-lan" = { "50-lan" = {
matchConfig.Name = "et2.5g"; 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 = { secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr";
}; };
nvme.uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b";
firewall = { firewall = {
enable = false; enable = false;

View File

@@ -1,7 +1,7 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes firewallForwards;
in in
{ {
imports = [ ./vms ]; imports = [ ./vms ];
@@ -57,7 +57,7 @@ in
}; };
boot = { 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 { kernel = super.kernel.override {
structuredExtraConfig = with lib.kernel; { structuredExtraConfig = with lib.kernel; {
#SOME_OPT = yes; #SOME_OPT = yes;
@@ -66,10 +66,21 @@ in
}; };
}); });
kernelModules = [ "kvm-amd" ]; 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 = { initrd = {
kernelModules = [ "dm-raid" ]; kernelModules = [ "dm-raid" ];
availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "sr_mod" ]; 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" ]
'';
};
}; };
}; };
@@ -137,6 +148,15 @@ in
services = { services = {
"serial-getty@ttyS0".enable = true; "serial-getty@ttyS0".enable = true;
"serial-getty@ttyS1".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 = { rsync-lvm-meta = {
description = "rsync lvm metadata backups / archives to rsync.net"; description = "rsync lvm metadata backups / archives to rsync.net";
@@ -248,6 +268,14 @@ in
Destination = allAssignments.shill.internal.ipv4.address; Destination = allAssignments.shill.internal.ipv4.address;
Gateway = allAssignments.shill.routing.ipv4.address; Gateway = allAssignments.shill.routing.ipv4.address;
} }
{
Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.shill.routing.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.shill.internal.ipv6.address;
}
{ {
Destination = prefixes.oci.v4; Destination = prefixes.oci.v4;
@@ -261,6 +289,11 @@ in
Destination = allAssignments.whale2.internal.ipv4.address; Destination = allAssignments.whale2.internal.ipv4.address;
Gateway = allAssignments.whale2.routing.ipv4.address; Gateway = allAssignments.whale2.routing.ipv4.address;
} }
{
Destination = allAssignments.git.internal.ipv4.address;
Gateway = allAssignments.git.routing.ipv4.address;
}
]; ];
} }
]; ];
@@ -346,6 +379,7 @@ in
firewall = { firewall = {
trustedInterfaces = [ "vms" ]; trustedInterfaces = [ "vms" ];
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
extraRules = '' extraRules = ''
define cust = { vm-mail, vm-darts } define cust = { vm-mail, vm-darts }
table inet filter { table inet filter {
@@ -372,6 +406,8 @@ in
"vm-estuary-persist" "vm-estuary-persist"
"vm-whale2-persist" "vm-whale2-persist"
"vm-mail-data" "vm-mail-data"
"vm-git-persist"
"git"
]; ];
compression = "zstd,5"; compression = "zstd,5";
extraCreateArgs = [ "--stats" ]; extraCreateArgs = [ "--stats" ];

View File

@@ -3,36 +3,22 @@
./estuary ./estuary
./shill ./shill
./whale2 ./whale2
./git
]; ];
nixos.systems.colony.configuration = { lib, pkgs, config, systems, ... }: nixos.systems.colony.configuration = { lib, pkgs, config, systems, ... }:
let let
inherit (builtins) listToAttrs;
inherit (lib) mkIf mkMerge optionals; inherit (lib) mkIf mkMerge optionals;
inherit (lib.my) vm;
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";
};
installerDisk = { installerDisk = {
name = "installer"; name = "installer";
backend = { backend = {
driver = "file"; driver = "file";
#filename = "${systems.installer.configuration.config.my.buildAs.iso}/iso/nixos-installer-devplayer0.iso"; #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/nixos-installer-devplayer0-b4d0d9a.iso";
#filename = "/persist/home/dev/debian-12.1.0-amd64-netinst.iso"; #filename = "/persist/home/dev/debian-12.1.0-amd64-netinst.iso";
filename = "/persist/home/dev/ubuntu-22.04.3-live-server-amd64.iso"; # filename = "/persist/home/dev/ubuntu-22.04.3-live-server-amd64.iso";
read-only = "on"; read-only = "on";
}; };
format.driver = "raw"; format.driver = "raw";
@@ -115,9 +101,9 @@
}; };
}; };
drives = [ ] ++ (optionals (!config.my.build.isDevVM) [ drives = [ ] ++ (optionals (!config.my.build.isDevVM) [
(mkMerge [ (vmLVM "estuary" "esp") { frontendOpts.bootindex = 0; } ]) (mkMerge [ (vm.disk "estuary" "esp") { frontendOpts.bootindex = 0; } ])
(vmLVM "estuary" "nix") (vm.disk "estuary" "nix")
(vmLVM "estuary" "persist") (vm.disk "estuary" "persist")
]); ]);
hostDevices = { hostDevices = {
net-wan0 = { net-wan0 = {
@@ -134,52 +120,17 @@
cpus = 12; cpus = 12;
threads = 2; threads = 2;
}; };
memory = 65536; memory = 40960;
networks.vms.mac = "52:54:00:27:3d:5c"; networks.vms.mac = "52:54:00:27:3d:5c";
cleanShutdown.timeout = 120; cleanShutdown.timeout = 120;
drives = [ ] ++ (optionals (!config.my.build.isDevVM) [ drives = [ ] ++ (optionals (!config.my.build.isDevVM) [
(mkMerge [ (vmLVM "shill" "esp") { frontendOpts.bootindex = 0; } ]) (mkMerge [ (vm.disk "shill" "esp") { frontendOpts.bootindex = 0; } ])
(vmLVM "shill" "nix") (vm.disk "shill" "nix")
(vmLVM "shill" "persist") (vm.disk "shill" "persist")
{
name = "media"; (vm.lvmDisk "media")
backend = { (vm.lvmDisk "minio")
driver = "host_device"; (vm.lvmDisk "nix-atticd")
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";
}
{
name = "git";
backend = {
driver = "host_device";
filename = "/dev/main/git";
discard = "unmap";
};
format = {
driver = "raw";
discard = "unmap";
};
frontend = "virtio-blk";
}
]); ]);
}; };
@@ -194,25 +145,35 @@
networks.vms.mac = "52:54:00:d5:d9:c6"; networks.vms.mac = "52:54:00:d5:d9:c6";
cleanShutdown.timeout = 120; cleanShutdown.timeout = 120;
drives = [ ] ++ (optionals (!config.my.build.isDevVM) [ drives = [ ] ++ (optionals (!config.my.build.isDevVM) [
(mkMerge [ (vmLVM "whale2" "esp") { frontendOpts.bootindex = 0; } ]) (mkMerge [ (vm.disk "whale2" "esp") { frontendOpts.bootindex = 0; } ])
(vmLVM "whale2" "nix") (vm.disk "whale2" "nix")
(vmLVM "whale2" "persist") (vm.disk "whale2" "persist")
{
name = "oci"; (vm.lvmDisk "oci")
backend = {
driver = "host_device";
filename = "/dev/main/oci";
discard = "unmap";
};
format = {
driver = "raw";
discard = "unmap";
};
frontend = "virtio-blk";
}
]); ]);
}; };
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 = { mail = {
uuid = "fd95fe0f-c204-4dd5-b16f-2b808e14a43a"; uuid = "fd95fe0f-c204-4dd5-b16f-2b808e14a43a";
cpu = "host,topoext"; cpu = "host,topoext";
@@ -220,15 +181,15 @@
cpus = 3; cpus = 3;
threads = 2; threads = 2;
}; };
memory = 8192; memory = 6144;
networks.public = { networks.public = {
bridge = null; bridge = null;
mac = "52:54:00:a8:d1:03"; mac = "52:54:00:a8:d1:03";
}; };
cleanShutdown.timeout = 120; cleanShutdown.timeout = 120;
drives = [ drives = [
(mkMerge [ (vmLVM "mail" "root") { frontendOpts.bootindex = 0; } ]) (mkMerge [ (vm.disk "mail" "root") { frontendOpts.bootindex = 0; } ])
(vmLVM "mail" "data") (vm.disk "mail" "data")
]; ];
}; };
@@ -246,7 +207,8 @@
}; };
cleanShutdown.timeout = 120; cleanShutdown.timeout = 120;
drives = [ drives = [
(mkMerge [ (vmLVM "darts" "root") { frontendOpts.bootindex = 0; } ]) (mkMerge [ (vm.disk "darts" "root") { frontendOpts.bootindex = 0; } ])
(vm.lvmDisk' "media" "darts-media")
]; ];
}; };
}; };

View File

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

View File

@@ -1,9 +1,8 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (builtins) elemAt;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my) net mkVLAN;
inherit (lib.my.c.colony) pubV4 domain prefixes firewallForwards;
pubV4 = "94.142.240.44";
in in
{ {
nixos = { nixos = {
@@ -11,9 +10,12 @@ in
l2 = { l2 = {
as211024 = { as211024 = {
vni = 211024; vni = 211024;
security.enable = true;
peers = { peers = {
estuary.addr = pubV4; 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 = { as211024 = {
ipv4 = { ipv4 = {
address = "10.255.3.1"; address = net.cidr.host 1 prefixes.as211024.v4;
gateway = null; 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 let
inherit (lib) flatten mkIf mkMerge mkForce; inherit (lib) flatten mkIf mkMerge mkForce;
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
inherit (lib.my.c) networkd;
in in
{ {
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ./dns.nix ./bgp.nix ]; imports = [ "${modulesPath}/profiles/qemu-guest.nix" ./dns.nix ./bgp.nix ];
@@ -90,6 +93,7 @@ in
environment = { environment = {
systemPackages = with pkgs; [ systemPackages = with pkgs; [
ethtool ethtool
conntrack-tools
wireguard-tools wireguard-tools
]; ];
}; };
@@ -114,34 +118,19 @@ in
}; };
systemd = { systemd = {
services = { services =
# Use this as a way to make sure the router always knows we're here (NDP seems kindy funky) let
ipv6-neigh-keepalive = waitOnline = "systemd-networkd-wait-online@wan.service";
let in
waitOnline = "systemd-networkd-wait-online@wan.service"; {
in bird2 = {
{
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
{
after = [ waitOnline ]; after = [ waitOnline ];
# requires = [ waitOnline ]; # requires = [ waitOnline ];
}; };
ipsec = {
after = [ waitOnline ];
requires = [ waitOnline ];
};
}; };
}; };
@@ -154,19 +143,7 @@ in
}; };
}; };
netdevs = netdevs = mkMerge [
let
mkVLAN = name: vid: {
"25-${name}" = {
netdevConfig = {
Name = name;
Kind = "vlan";
};
vlanConfig.Id = vid;
};
};
in
mkMerge [
(mkVLAN "ifog" 409) (mkVLAN "ifog" 409)
(mkVLAN "frys-ix" 701) (mkVLAN "frys-ix" 701)
@@ -271,13 +248,7 @@ in
Kind = "vlan"; Kind = "vlan";
}; };
vlan = [ "frys-ix" "nl-ix" "fogixp" "ifog-transit" ]; vlan = [ "frys-ix" "nl-ix" "fogixp" "ifog-transit" ];
networkConfig = { networkConfig = networkd.noL3;
LinkLocalAddressing = "no";
DHCP = "no";
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
};
}; };
"85-ifog-transit" = { "85-ifog-transit" = {
matchConfig.Name = "ifog-transit"; matchConfig.Name = "ifog-transit";
@@ -315,6 +286,10 @@ in
Destination = prefixes.vip1; Destination = prefixes.vip1;
Gateway = allAssignments.colony.routing.ipv4.address; Gateway = allAssignments.colony.routing.ipv4.address;
} }
{
Destination = prefixes.vip3;
Gateway = allAssignments.colony.routing.ipv4.address;
}
{ {
Destination = prefixes.darts.v4; Destination = prefixes.darts.v4;
Gateway = allAssignments.colony.routing.ipv4.address; Gateway = allAssignments.colony.routing.ipv4.address;
@@ -323,6 +298,15 @@ in
Destination = prefixes.cust.v6; Destination = prefixes.cust.v6;
Gateway = allAssignments.colony.internal.ipv6.address; Gateway = allAssignments.colony.internal.ipv6.address;
} }
{
Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.colony.routing.ipv4.address;
}
{
Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.colony.internal.ipv6.address;
}
] ++ ] ++
(map (pName: [ (map (pName: [
{ {
@@ -337,14 +321,19 @@ in
} }
]; ];
"90-l2mesh-as211024" = { "90-l2mesh-as211024" = mkMerge [
matchConfig.Name = "as211024"; (networkdAssignment "as211024" assignments.as211024)
address = with assignments.as211024; [ {
(with ipv4; "${address}/${toString mask}") matchConfig.Name = "as211024";
(with ipv6; "${address}/${toString mask}") networkConfig.IPv6AcceptRA = mkForce false;
]; routes = map (r: { routeConfig = r; }) [
networkConfig.IPv6AcceptRA = false; {
}; Destination = lib.my.c.home.prefixes.all.v4;
Gateway = lib.my.c.home.vips.as211024.v4;
}
];
}
];
"95-kelder" = { "95-kelder" = {
matchConfig.Name = "kelder"; matchConfig.Name = "kelder";
routes = [ routes = [
@@ -366,43 +355,23 @@ in
"estuary/kelder-wg.key" = { "estuary/kelder-wg.key" = {
owner = "systemd-network"; owner = "systemd-network";
}; };
"l2mesh/as211024.key" = {};
}; };
}; };
server.enable = true; server.enable = true;
vpns = {
l2.pskFiles = {
as211024 = config.age.secrets."l2mesh/as211024.key".path;
};
};
firewall = { firewall = {
trustedInterfaces = [ "as211024" ];
udp.allowed = [ 5353 lib.my.c.kelder.vpn.port ]; udp.allowed = [ 5353 lib.my.c.kelder.vpn.port ];
tcp.allowed = [ 5353 "bgp" ]; tcp.allowed = [ 5353 "bgp" ];
nat = { nat = {
enable = true; enable = true;
externalInterface = "wan"; externalInterface = "wan";
externalIP = assignments.internal.ipv4.address; forwardPorts."${assignments.internal.ipv4.address}" = firewallForwards allAssignments;
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";
}
];
}; };
extraRules = extraRules =
let let
@@ -424,12 +393,16 @@ in
# Safe enough to allow all SSH # Safe enough to allow all SSH
tcp dport ssh accept tcp dport ssh accept
${matchInet "tcp dport { http, https, 8448 } accept" "middleman"} ip6 daddr ${aa.middleman.internal.ipv6.address} tcp dport { http, https, 8448 } accept
${matchInet "udp dport { 2456-2457 } accept" "valheim-oci"} ${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 return
} }
chain routing-udp { 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 return
} }
chain filter-routing { chain filter-routing {
@@ -446,9 +419,10 @@ in
} }
chain forward { 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 oifname $ixps jump ixp
iifname base oifname { wan, $ixps } accept iifname base oifname { base, wan, $ixps } accept
oifname { as211024, kelder } accept oifname { as211024, kelder } accept
} }
chain output { chain output {
@@ -459,11 +433,9 @@ in
table inet nat { table inet nat {
chain prerouting { chain prerouting {
${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"} ${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"}
ip daddr ${allAssignments.shill.internal.ipv4.address} tcp dport { http, https } dnat to ${allAssignments.middleman.internal.ipv4.address}
ip6 daddr ${allAssignments.shill.internal.ipv6.address} tcp dport { http, https } dnat to ${allAssignments.middleman.internal.ipv6.address}
} }
chain postrouting { chain postrouting {
ip saddr ${prefixes.all.v4} snat to ${assignments.internal.ipv4.address} ip saddr ${prefixes.all.v4} oifname != as211024 snat to ${assignments.internal.ipv4.address}
} }
} }
''; '';

View File

@@ -52,7 +52,7 @@ in
allowFrom = [ allowFrom = [
"127.0.0.0/8" "::1/128" "127.0.0.0/8" "::1/128"
prefixes.all.v4 prefixes.all.v6 prefixes.all.v4 prefixes.all.v6
]; ] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
}; };
settings = { settings = {
@@ -145,9 +145,14 @@ in
http IN A ${assignments.internal.ipv4.address} http IN A ${assignments.internal.ipv4.address}
http IN AAAA ${allAssignments.middleman.internal.ipv6.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 A ${assignments.internal.ipv4.address}
valheim IN AAAA ${allAssignments.valheim-oci.internal.ipv6.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 ${net.cidr.host 0 prefixes.mail.v4} mail-vm IN A ${net.cidr.host 0 prefixes.mail.v4}
mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6} mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6}

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

@@ -6,18 +6,33 @@ let
cfgFile = pkgs.writeText "gitea-actions-runner.yaml" (toJSON { cfgFile = pkgs.writeText "gitea-actions-runner.yaml" (toJSON {
container = { container = {
network = "colony"; network = "podman";
privileged = true;
};
cache = {
enabled = true;
dir = "/var/cache/gitea-runner";
}; };
}); });
in in
{ {
config = { config = {
fileSystems = {
"/var/cache/gitea-runner" = {
device = "/dev/disk/by-label/actions-cache";
fsType = "ext4";
};
};
services = { services = {
gitea-actions-runner.instances = { gitea-actions-runner.instances = {
main = { main = {
enable = true; enable = true;
name = "main-docker"; name = "main-docker";
labels = [ ]; labels = [
"debian-node-bullseye:docker://node:18-bullseye"
"ubuntu-22.04:docker://git.nul.ie/dev/actions-ubuntu:22.04"
];
url = "https://git.${pubDomain}"; url = "https://git.${pubDomain}";
tokenFile = config.age.secrets."gitea/actions-runner.env".path; tokenFile = config.age.secrets."gitea/actions-runner.env".path;
}; };
@@ -42,6 +57,7 @@ in
services = { services = {
gitea-runner-main.serviceConfig = { gitea-runner-main.serviceConfig = {
# Needs to be able to read its secrets # Needs to be able to read its secrets
CacheDirectory = "gitea-runner";
DynamicUser = mkForce false; DynamicUser = mkForce false;
User = "gitea-runner"; User = "gitea-runner";
Group = "gitea-runner"; Group = "gitea-runner";

View File

@@ -1,5 +1,6 @@
{ lib, pkgs, config, assignments, allAssignments, ... }: { lib, pkgs, config, assignments, allAssignments, ... }:
let let
inherit (lib) mkMerge;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) prefixes; inherit (lib.my.c.colony) prefixes;
in in
@@ -23,6 +24,30 @@ in
groups.git = {}; 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 = { services = {
gitea = { gitea = {
enable = true; enable = true;
@@ -72,6 +97,16 @@ in
PASSWORD = "#mailerpass#"; PASSWORD = "#mailerpass#";
REPLY_TO_ADDRESS = "git+%{token}@nul.ie"; 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 = { actions = {
ENABLED = true; ENABLED = true;
}; };
@@ -91,23 +126,9 @@ in
{ {
"gitea/db.txt" = ownedByGit; "gitea/db.txt" = ownedByGit;
"gitea/mail.txt" = ownedByGit; "gitea/mail.txt" = ownedByGit;
"gitea/minio.txt" = ownedByGit;
}; };
}; };
firewall.extraRules = ''
table inet filter {
chain input {
ip saddr ${prefixes.all.v4} tcp dport 3000 accept
ip6 saddr ${prefixes.all.v6} tcp dport 3000 accept
}
}
table inet nat {
chain prerouting {
ip daddr ${assignments.internal.ipv4.address} tcp dport { http, https } dnat to ${allAssignments.middleman.internal.ipv4.address}
ip6 daddr ${assignments.internal.ipv6.address} tcp dport { http, https } dnat to ${allAssignments.middleman.internal.ipv6.address}
}
}
'';
}; };
}; };
} }

View File

@@ -1,12 +1,14 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
nixos.systems.chatterbox = { nixos.systems.chatterbox = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {
@@ -93,6 +95,7 @@ in
settings = { settings = {
server_name = "nul.ie"; server_name = "nul.ie";
public_baseurl = "https://matrix.nul.ie"; public_baseurl = "https://matrix.nul.ie";
web_client_location = "https://element.${pubDomain}";
admin_contact = "dev@nul.ie"; admin_contact = "dev@nul.ie";
prescence.enabled = true; prescence.enabled = true;

View File

@@ -4,9 +4,10 @@ let
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
nixos.systems.colony-psql = { nixos.systems.colony-psql = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {

View File

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

View File

@@ -4,9 +4,10 @@ let
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
nixos.systems.jackflix = { nixos.systems.jackflix = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {
@@ -22,7 +23,7 @@ in
configuration = { lib, pkgs, config, ... }: configuration = { lib, pkgs, config, ... }:
let let
inherit (lib); inherit (lib) mkForce;
in in
{ {
imports = [ ./networking.nix ]; imports = [ ./networking.nix ];
@@ -37,14 +38,22 @@ in
}; };
}; };
users = { users = with lib.my.c.ids; {
groups.media.gid = 2000;
users = { users = {
"${config.my.user.config.name}".extraGroups = [ "media" ]; "${config.my.user.config.name}".extraGroups = [ "media" ];
transmission.extraGroups = [ "media" ]; transmission.extraGroups = [ "media" ];
radarr.extraGroups = [ "media" ]; radarr.extraGroups = [ "media" ];
sonarr.extraGroups = [ "media" ]; sonarr.extraGroups = [ "media" ];
jellyseerr = {
isSystemUser = true;
uid = uids.jellyseerr;
group = "jellyseerr";
};
};
groups = {
media.gid = 2000;
jellyseerr.gid = gids.jellyseerr;
}; };
}; };
@@ -55,6 +64,12 @@ in
radarr.serviceConfig.UMask = "0002"; radarr.serviceConfig.UMask = "0002";
sonarr.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 # https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
transmission.serviceConfig = { transmission.serviceConfig = {
@@ -96,6 +111,10 @@ in
jackett.enable = true; jackett.enable = true;
radarr.enable = true; radarr.enable = true;
sonarr.enable = true; sonarr.enable = true;
jellyseerr = {
enable = true;
openFirewall = true;
};
jellyfin.enable = true; jellyfin.enable = true;
}; };

View File

@@ -2,12 +2,14 @@
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.nginx) baseHttpConfig;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
nixos.systems.middleman = { nixos.systems.middleman = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {
@@ -64,6 +66,7 @@ in
owner = "nginx"; owner = "nginx";
group = "nginx"; group = "nginx";
}; };
"librespeed.toml" = { };
}; };
}; };
@@ -120,6 +123,19 @@ in
baseURL = "https://sso.${pubDomain}"; baseURL = "https://sso.${pubDomain}";
}; };
}; };
librespeed = {
frontend.servers = [
{
name = "Amsterdam, Netherlands";
server = "//librespeed.${domain}";
}
];
backend = {
enable = true;
extraSettingsFile = config.age.secrets."librespeed.toml".path;
};
};
}; };
users = { users = {
@@ -130,6 +146,10 @@ in
systemd = { systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal; 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 = { security = {
@@ -230,43 +250,9 @@ in
# Based on recommended*Settings, but probably better to be explicit about these # Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = '' appendHttpConfig = ''
# NixOS provides a logrotate config that auto-compresses :) ${baseHttpConfig}
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 resolver_timeout 5s;
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.c.nginx.proxyHeaders}
# caching # caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=4g; proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=4g;

View File

@@ -288,6 +288,10 @@ in
} }
(ssoServer "generic") (ssoServer "generic")
]; ];
"gib.${pubDomain}" = {
locations."/".proxyPass = "http://jackflix-ctr.${domain}:5055";
useACMEHost = pubDomain;
};
"jackflix.${pubDomain}" = "jackflix.${pubDomain}" =
let let
@@ -314,59 +318,12 @@ in
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
"toot.nul.ie" = "toot.nul.ie" = {
let locations."/" = {
mkAssetLoc = name: { proxyPass = "http://toot-ctr.${domain}:80";
tryFiles = "$uri =404"; proxyWebsockets = true;
extraConfig = '' extraConfig = proxyHeaders;
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubpubDomains";
'';
}; };
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.${domain}:55000";
proxyWebsockets = true;
extraConfig = ''
${proxyHeaders}
proxy_set_header Proxy "";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
'';
};
"@proxy" = {
proxyPass = "http://toot-ctr.${domain}:55001";
proxyWebsockets = true;
extraConfig = ''
${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 = pubDomain; useACMEHost = pubDomain;
}; };
@@ -390,9 +347,41 @@ in
}; };
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
"public.${pubDomain}" = {
serverAliases = [ "p.${pubDomain}" ];
locations."/" = {
root = "/mnt/media/public";
extraConfig = ''
fancyindex on;
fancyindex_show_dotfiles on;
'';
};
useACMEHost = pubDomain;
};
"git.${pubDomain}" = { "mc-map.${pubDomain}" = {
locations."/".proxyPass = "http://shill-vm.${domain}:3000"; 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; useACMEHost = pubDomain;
}; };
}; };
@@ -408,7 +397,6 @@ in
nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar\.\S+)$''; nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar\.\S+)$'';
nixCacheHeaders = '' nixCacheHeaders = ''
proxy_hide_header "X-Amz-Request-Id";
add_header Cache-Control $nix_cache_control; add_header Cache-Control $nix_cache_control;
add_header Expires $nix_expires; add_header Expires $nix_expires;
''; '';
@@ -431,24 +419,34 @@ in
"s3.${pubDomain}" = { "s3.${pubDomain}" = {
serverAliases = [ "*.s3.${pubDomain}" ]; serverAliases = [ "*.s3.${pubDomain}" ];
inherit extraConfig; inherit extraConfig;
locations."/".proxyPass = s3Upstream; locations = {
"/".proxyPass = s3Upstream;
"/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; useACMEHost = pubDomain;
}; };
"nix-cache.${pubDomain}" = { "nix-cache.${pubDomain}" = {
extraConfig = ''
${extraConfig}
proxy_set_header Host "nix-cache.s3.nul.ie";
'';
locations = { locations = {
"/".proxyPass = s3Upstream; "/".proxyPass = "http://${host}:8069";
"~ ${nixCacheableRegex}" = { "~ ${nixCacheableRegex}" = {
proxyPass = s3Upstream; proxyPass = "http://${host}:8069";
extraConfig = nixCacheHeaders; extraConfig = nixCacheHeaders;
}; };
}; };
useACMEHost = pubDomain; useACMEHost = pubDomain;
onlySSL = false;
}; };
}; };

View File

@@ -1,12 +1,14 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
nixos.systems.object = { nixos.systems.object = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {
@@ -22,7 +24,7 @@ in
configuration = { lib, pkgs, config, assignments, ... }: configuration = { lib, pkgs, config, assignments, ... }:
let let
inherit (lib) mkMerge mkIf; inherit (lib) mkMerge mkIf mkForce;
inherit (config.my.user.homeConfig.lib.file) mkOutOfStoreSymlink; inherit (config.my.user.homeConfig.lib.file) mkOutOfStoreSymlink;
inherit (lib.my) networkdAssignment systemdAwaitPostgres; inherit (lib.my) networkdAssignment systemdAwaitPostgres;
in in
@@ -45,11 +47,18 @@ in
owner = config.my.user.config.name; owner = config.my.user.config.name;
group = config.my.user.config.group; group = config.my.user.config.group;
}; };
"object/atticd.env" = {};
"object/hedgedoc.env" = {};
}; };
}; };
firewall = { 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 = { 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 = { systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal; network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
services = {
services =
let
awaitPostgres = systemdAwaitPostgres pkgs.postgresql "colony-psql";
in
{
minio = { minio = {
environment = { environment = {
MINIO_ROOT_USER = "minioadmin"; MINIO_ROOT_USER = "minioadmin";
@@ -68,7 +91,17 @@ in
MINIO_BROWSER_REDIRECT_URL = "https://minio.nul.ie"; 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" ];
};
}
];
}; };
}; };
@@ -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 { (mkIf config.my.build.isDevVM {

View File

@@ -1,12 +1,14 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib) mkForce;
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
nixos.systems.toot = { nixos.systems.toot = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {
@@ -53,8 +55,7 @@ in
tcp.allowed = [ tcp.allowed = [
19999 19999
config.services.mastodon.webPort "http"
config.services.mastodon.streamingPort
]; ];
}; };
}; };
@@ -77,10 +78,13 @@ in
services = { services = {
netdata.enable = true; netdata.enable = true;
mastodon = mkMerge [ mastodon = mkMerge [
{ rec {
enable = true; enable = true;
localDomain = "nul.ie"; localDomain = extraConfig.WEB_DOMAIN; # for nginx config
extraConfig.WEB_DOMAIN = "toot.nul.ie"; extraConfig = {
LOCAL_DOMAIN = "nul.ie";
WEB_DOMAIN = "toot.nul.ie";
};
secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path; secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path;
otpSecretFile = config.age.secrets."toot/otp-secret.txt".path; otpSecretFile = config.age.secrets."toot/otp-secret.txt".path;
@@ -89,9 +93,8 @@ in
"vapid-pubkey.txt" "vapid-pubkey.txt"
"BAyRyD2pnLQtMHr3J5AzjNMll_HDC6ra1ilOLAUmKyhkEdbm7_OwKZUgw1UefY4CHEcv4OOX9TnnN2DOYYuPZu8="); "BAyRyD2pnLQtMHr3J5AzjNMll_HDC6ra1ilOLAUmKyhkEdbm7_OwKZUgw1UefY4CHEcv4OOX9TnnN2DOYYuPZu8=");
enableUnixSocket = false; streamingProcesses = 4;
configureNginx = false; configureNginx = true;
trustedProxy = allAssignments.middleman.internal.ipv6.address;
database = { database = {
createLocally = false; 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 { (mkIf config.my.build.isDevVM {
virtualisation = { virtualisation = {
forwardPorts = with config.services.mastodon; [ forwardPorts = with config.services.mastodon; [
{ from = "host"; guest.port = webPort; } { from = "host"; guest.port = webPort; }
{ from = "host"; guest.port = streamingPort; }
]; ];
}; };
}) })

View File

@@ -4,9 +4,10 @@ let
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
nixos.systems.vaultwarden = { nixos.systems.vaultwarden = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {

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, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net nft;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes firewallForwards;
in in
{ {
imports = [ ./containers ]; imports = [ ./containers ];
@@ -49,7 +49,7 @@ in
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
in in
{ {
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ./hercules.nix ./gitea.nix ]; imports = [ "${modulesPath}/profiles/qemu-guest.nix" ];
config = mkMerge [ config = mkMerge [
{ {
@@ -81,6 +81,7 @@ in
fsType = "ext4"; fsType = "ext4";
neededForBoot = true; neededForBoot = true;
}; };
"/mnt/media" = { "/mnt/media" = {
device = "/dev/disk/by-label/media"; device = "/dev/disk/by-label/media";
fsType = "ext4"; fsType = "ext4";
@@ -89,6 +90,10 @@ in
device = "/dev/disk/by-label/minio"; device = "/dev/disk/by-label/minio";
fsType = "xfs"; fsType = "xfs";
}; };
"/mnt/atticd" = {
device = "/dev/disk/by-label/atticd";
fsType = "ext4";
};
}; };
nix.settings = { nix.settings = {
@@ -134,6 +139,16 @@ in
ipv6PrefixConfig.Prefix = 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 = { firewall = {
tcp.allowed = [ 19999 ]; tcp.allowed = [ 19999 ];
trustedInterfaces = [ "ctrs" ]; trustedInterfaces = [ "ctrs" ];
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
extraRules = '' extraRules = ''
table inet filter { table inet filter {
chain forward { chain forward {
@@ -153,6 +169,17 @@ in
iifname vms oifname ctrs accept 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 = { object = {
bindMounts = { bindMounts = {
"/mnt/minio".readOnly = false; "/mnt/minio".readOnly = false;
"/mnt/atticd".readOnly = false;
}; };
}; };
toot = {}; toot = {};
waffletail = {};
}; };
in in
mkMerge [ mkMerge [

View File

@@ -50,6 +50,8 @@ in
}; };
}) { }) {
valheim-oci = 2; valheim-oci = 2;
simpcraft-oci = 3;
simpcraft-staging-oci = 4;
}; };
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }: configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
@@ -63,7 +65,7 @@ in
"${modulesPath}/profiles/qemu-guest.nix" "${modulesPath}/profiles/qemu-guest.nix"
./valheim.nix ./valheim.nix
./gitea-actions.nix ./minecraft
]; ];
config = mkMerge [ config = mkMerge [

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

@@ -4,9 +4,10 @@ let
inherit (lib.my.c.kelder) domain prefixes; inherit (lib.my.c.kelder) domain prefixes;
in in
{ {
nixos.systems.kelder-acquisition = { nixos.systems.kelder-acquisition = { config, ...}: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {

View File

@@ -5,9 +5,10 @@ let
inherit (lib.my.c.kelder) domain prefixes; inherit (lib.my.c.kelder) domain prefixes;
in in
{ {
nixos.systems.kelder-spoder = { nixos.systems.kelder-spoder = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "mine"; nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = { assignments = {
internal = { internal = {
@@ -91,11 +92,10 @@ in
nextcloud = { nextcloud = {
enable = true; enable = true;
package = pkgs.nextcloud27; package = pkgs.nextcloud28;
datadir = "/mnt/storage/nextcloud"; datadir = "/mnt/storage/nextcloud";
hostName = "cloud.${domain}"; hostName = "cloud.${domain}";
https = true; https = true;
enableBrokenCiphersForSSE = false;
config = { config = {
extraTrustedDomains = [ "cloud-local.${domain}" ]; extraTrustedDomains = [ "cloud-local.${domain}" ];
adminpassFile = config.age.secrets."kelder/nextcloud-root.txt".path; adminpassFile = config.age.secrets."kelder/nextcloud-root.txt".path;

View File

@@ -2,7 +2,7 @@
let let
inherit (builtins) mapAttrs; inherit (builtins) mapAttrs;
inherit (lib) mkMerge mkIf mkDefault; inherit (lib) mkMerge mkIf mkDefault;
inherit (lib.my.c.nginx) proxyHeaders; inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
inherit (lib.my.c.kelder) domain; inherit (lib.my.c.kelder) domain;
in in
{ {
@@ -39,43 +39,7 @@ in
# Based on recommended*Settings, but probably better to be explicit about these # Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = '' appendHttpConfig = ''
# NixOS provides a logrotate config that auto-compresses :) ${baseHttpConfig}
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}
# caching # caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=4g; proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=4g;

View File

@@ -54,7 +54,7 @@ in
efi.canTouchEfiVariables = true; efi.canTouchEfiVariables = true;
timeout = 5; timeout = 5;
}; };
kernelPackages = pkgs.linuxKernel.packages.linux_6_1; kernelPackages = lib.my.c.kernel.lts pkgs;
kernelModules = [ "kvm-intel" ]; kernelModules = [ "kvm-intel" ];
kernelParams = [ "intel_iommu=on" ]; kernelParams = [ "intel_iommu=on" ];
initrd = { initrd = {
@@ -135,12 +135,14 @@ in
samba-wsdd.enable = true; samba-wsdd.enable = true;
minecraft-server = { minecraft-server = {
enable = true; enable = false;
package = pkgs.minecraftServers.vanilla-1-19; package = pkgs.minecraftServers.vanilla-1-20;
declarative = true; declarative = true;
eula = true; eula = true;
whitelist = { whitelist = {
devplayer0 = "6d7d971b-ce10-435b-85c5-c99c0d8d288c"; devplayer0 = "6d7d971b-ce10-435b-85c5-c99c0d8d288c";
Elderlypug = "dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56";
shr3kas0ras = "1d366062-12c0-4e29-aba7-6ab5d8c6bb05";
}; };
serverProperties = { serverProperties = {
motd = "Simpcraft"; motd = "Simpcraft";

View File

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

View File

@@ -1,4 +1,4 @@
{ lib, pkgsFlakes, hmFlakes, inputs, pkgs', config, ... }: { self, lib, pkgsFlakes, hmFlakes, inputs, pkgs', config, ... }:
let let
inherit (builtins) attrValues mapAttrs; inherit (builtins) attrValues mapAttrs;
inherit (lib) inherit (lib)
@@ -25,10 +25,14 @@ let
modules' = [ hmFlakes.${config'.home-manager}.nixosModule ] ++ (attrValues cfg.modules); modules' = [ hmFlakes.${config'.home-manager}.nixosModule ] ++ (attrValues cfg.modules);
in 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 # 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. # 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 # Put the inputs in specialArgs to avoid infinite recursion when modules try to do imports
specialArgs = { inherit inputs pkgsFlakes pkgsFlake allAssignments; inherit (cfg) systems; }; specialArgs = { inherit inputs pkgsFlakes pkgsFlake allAssignments; inherit (cfg) systems; };
@@ -51,7 +55,7 @@ let
pkgs' = allPkgs; pkgs' = allPkgs;
}; };
system.name = name; system = { inherit name; };
networking = { networking = {
domain = let d = config'.assignments.internal.domain or null; in mkIf (d != null) (mkDefault' d); domain = let d = config'.assignments.internal.domain or null; in mkIf (d != null) (mkDefault' d);
hostName = mkDefault (config'.assignments.internal.name or name); hostName = mkDefault (config'.assignments.internal.name or name);
@@ -86,6 +90,8 @@ let
pkgsPath = toString pkgsFlakes.${config'.hmNixpkgs}; pkgsPath = toString pkgsFlakes.${config'.hmNixpkgs};
pkgs' = allPkgs; pkgs' = allPkgs;
}; };
home.enableNixpkgsReleaseCheck = false;
} }
(homeStateVersion config'.home-manager) (homeStateVersion config'.home-manager)
]; ];
@@ -100,6 +106,7 @@ let
altNames = mkOpt' (listOf str) [ ] "Extra names to assign."; altNames = mkOpt' (listOf str) [ ] "Extra names to assign.";
visible = mkBoolOpt' true "Whether or not this assignment should be visible."; visible = mkBoolOpt' true "Whether or not this assignment should be visible.";
domain = mkOpt' (nullOr str) null "Domain for this assignment."; domain = mkOpt' (nullOr str) null "Domain for this assignment.";
mtu = mkOpt' (nullOr ints.unsigned) null "Interface MTU.";
ipv4 = { ipv4 = {
address = mkOpt' net.types.ipv4 null "IPv4 address."; address = mkOpt' net.types.ipv4 null "IPv4 address.";
mask = mkOpt' ints.u8 24 "Network mask."; mask = mkOpt' ints.u8 24 "Network mask.";
@@ -125,6 +132,9 @@ let
l2MeshOpts = with lib.types; { name, ... }: { l2MeshOpts = with lib.types; { name, ... }: {
options = { options = {
interface = mkOpt' str name "Name of VXLAN interface."; 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."; firewall = mkBoolOpt' true "Whether to generate firewall rules.";
vni = mkOpt' ints.unsigned 1 "VXLAN VNI."; vni = mkOpt' ints.unsigned 1 "VXLAN VNI.";
peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers."; peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers.";
@@ -164,6 +174,7 @@ let
}); });
}; };
}; };
rendered = mkOpt' unspecified config'.configuration "Final NixOS modules system output.";
}; };
config = { config = {
@@ -180,6 +191,11 @@ in
secretsPath = mkOpt' path null "Path to encrypted secret files."; secretsPath = mkOpt' path null "Path to encrypted secret files.";
modules = mkOpt' (attrsOf commonOpts.moduleType) { } "NixOS modules to be exported by nixfiles."; modules = mkOpt' (attrsOf commonOpts.moduleType) { } "NixOS modules to be exported by nixfiles.";
systems = mkOpt' (attrsOf (submodule systemOpts)) { } "NixOS systems 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 = { vpns = {
l2 = mkOpt' (attrsOf (submodule l2MeshOpts)) { } "Layer 2 meshes."; l2 = mkOpt' (attrsOf (submodule l2MeshOpts)) { } "Layer 2 meshes.";
}; };
@@ -205,5 +221,9 @@ in
message = "Duplicate assignments: ${toString dupIPs}"; 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"; system = "x86_64-linux";
nixpkgs = "unstable"; nixpkgs = "unstable";
docCustom = false; docCustom = false;
rendered = config.configuration.config.my.asISO;
configuration = configuration =
{ lib, pkgs, modulesPath, config, ... }: { lib, pkgs, modulesPath, config, ... }:
@@ -31,7 +32,8 @@
}; };
isoImage = { isoImage = {
isoBaseName = "nixos-installer-devplayer0"; isoBaseName = "jackos-installer";
volumeID = "jackos-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}";
edition = "devplayer0"; edition = "devplayer0";
appendToMenuLabel = " /dev/player0 Installer"; appendToMenuLabel = " /dev/player0 Installer";
}; };

View File

@@ -17,5 +17,8 @@
gui = ./gui.nix; gui = ./gui.nix;
l2mesh = ./l2mesh.nix; l2mesh = ./l2mesh.nix;
borgthin = ./borgthin.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 let
inherit (lib) recursiveUpdate mkOption mkDefault mkIf mkMerge flatten optional; inherit (lib) recursiveUpdate mkOption mkDefault mkIf mkMerge flatten optional;
inherit (lib.my) mkBoolOpt' dummyOption; inherit (lib.my) mkBoolOpt' dummyOption;
@@ -18,6 +18,9 @@ let
"${modulesPath}/installer/cd-dvd/iso-image.nix" "${modulesPath}/installer/cd-dvd/iso-image.nix"
allHardware allHardware
{ {
# Doesn't work right now... (missing /dev/root)
boot.initrd.systemd.enable = false;
isoImage = { isoImage = {
makeEfiBootable = true; makeEfiBootable = true;
makeUsbBootable = true; makeUsbBootable = true;
@@ -40,18 +43,28 @@ let
modules = flatten [ modules = flatten [
"${modulesPath}/installer/netboot/netboot.nix" "${modulesPath}/installer/netboot/netboot.nix"
allHardware 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 { mkAsOpt = ext: desc: with lib.types; mkOption {
inherit (ext) type; type = unspecified;
default = { }; default = ext;
visible = "shallow"; visible = "shallow";
description = "Configuration as ${desc}."; description = "Configuration as ${desc}.";
}; };
in in
{ {
options = with lib.types; { options = {
my = { my = {
build = { build = {
isDevVM = mkBoolOpt' false "Whether the system is a development VM."; isDevVM = mkBoolOpt' false "Whether the system is a development VM.";
@@ -79,6 +92,7 @@ in
}; };
isoImage = { isoImage = {
isoBaseName = dummyOption; isoBaseName = dummyOption;
volumeID = dummyOption;
edition = dummyOption; edition = dummyOption;
appendToMenuLabel = dummyOption; appendToMenuLabel = dummyOption;
}; };
@@ -92,10 +106,11 @@ in
my = { my = {
buildAs = { buildAs = {
# The meta.mainProgram should probably be set upstream but oh well... # 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"; }; devVM = recursiveUpdate config.my.asDevVM.config.system.build.vm { meta.mainProgram = "run-${config.system.name}-vm"; };
iso = config.my.asISO.system.build.isoImage; iso = config.my.asISO.config.system.build.isoImage;
container = config.my.asContainer.system.build.toplevel; container = config.my.asContainer.config.system.build.toplevel;
kexecTree = config.my.asKexecTree.system.build.kexecTree; 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, ... }: { lib, pkgs, pkgs', inputs, config, ... }:
let let
inherit (lib) mkIf mkDefault mkMerge; inherit (lib) mkIf mkDefault mkMerge;
inherit (lib.my) mkBoolOpt' dummyOption; inherit (lib.my) mkDefault';
in in
{ {
options = with lib.types; { options = with lib.types; {
@@ -12,6 +12,7 @@ in
inputs.impermanence.nixosModule inputs.impermanence.nixosModule
inputs.ragenix.nixosModules.age inputs.ragenix.nixosModules.age
inputs.sharry.nixosModules.default inputs.sharry.nixosModules.default
inputs.attic.nixosModules.atticd
]; ];
config = mkMerge [ config = mkMerge [
@@ -40,14 +41,13 @@ in
nix = { nix = {
package = pkgs'.mine.nix; package = pkgs'.mine.nix;
settings = { settings = with lib.my.c.nix; {
trusted-users = [ "@wheel" ]; trusted-users = [ "@wheel" ];
experimental-features = [ "nix-command" "flakes" "ca-derivations" ]; experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
substituters = [ extra-substituters = cache.substituters;
#"https://nix-cache.nul.ie" extra-trusted-public-keys = cache.keys;
"https://cache.nixos.org" connect-timeout = 5;
]; fallback = true;
trusted-public-keys = lib.my.c.nix.cacheKeys;
}; };
registry = { registry = {
pkgs = { pkgs = {
@@ -88,12 +88,13 @@ in
boot = { boot = {
# Use latest LTS release by default # Use latest LTS release by default
kernelPackages = mkDefault pkgs.linuxKernel.packages.linux_6_1; kernelPackages = mkDefault (lib.my.c.kernel.lts pkgs);
kernel = { kernel = {
sysctl = { sysctl = {
"net.ipv6.route.max_size" = mkDefault 16384; "net.ipv6.route.max_size" = mkDefault 16384;
}; };
}; };
loader = { loader = {
efi = { efi = {
efiSysMountPoint = mkDefault "/boot"; efiSysMountPoint = mkDefault "/boot";
@@ -111,12 +112,24 @@ in
memtest86.enable = mkDefault true; 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 [ environment.systemPackages = with pkgs; mkMerge [
[ [
bash-completion bash-completion
vim
git git
unzip unzip
] ]
@@ -129,6 +142,7 @@ in
fish.enable = mkDefault true; fish.enable = mkDefault true;
# TODO: This is expecting to look up the channel for the database... # TODO: This is expecting to look up the channel for the database...
command-not-found.enable = mkDefault false; command-not-found.enable = mkDefault false;
vim.defaultEditor = true;
}; };
services = { services = {
@@ -142,6 +156,7 @@ in
font-name=SauceCodePro Nerd Font Mono font-name=SauceCodePro Nerd Font Mono
''; '';
}; };
getty.greetingLine = mkDefault' ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
openssh = { openssh = {
enable = mkDefault true; enable = mkDefault true;

View File

@@ -208,7 +208,17 @@ in
mkdir -p -m 0755 "$root"/sbin "$root"/etc mkdir -p -m 0755 "$root"/sbin "$root"/etc
touch "$root"/etc/os-release 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 = postStop =
'' ''

View File

@@ -15,16 +15,20 @@ let
# Based on https://github.com/serokell/deploy-rs/blob/master/flake.nix # Based on https://github.com/serokell/deploy-rs/blob/master/flake.nix
nixosActivate = cfg': base: (pkgs.deploy-rs.lib.activate.custom // { nixosActivate = cfg': base: (pkgs.deploy-rs.lib.activate.custom // {
dryActivate = "$PROFILE/bin/switch-to-configuration dry-activate"; 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 '' }) base.config.system.build.toplevel ''
# work around https://github.com/NixOS/nixpkgs/issues/73404 # work around https://github.com/NixOS/nixpkgs/issues/73404
cd /tmp cd /tmp
"$PROFILE"/bin/switch-to-configuration ${cfg'.mode} "$PROFILE"/bin/switch-to-configuration switch
# https://github.com/serokell/deploy-rs/issues/31 # https://github.com/serokell/deploy-rs/issues/31
${with base.config.boot.loader; ${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"} "sed -i '/^default /d' ${efi.efiSysMountPoint}/loader/loader.conf"}
${keepGensSnippet "$PROFILE" cfg'.keepGenerations} ${keepGensSnippet "$PROFILE" cfg'.keepGenerations}
@@ -59,7 +63,11 @@ let
{ {
name = "container-${n}"; name = "container-${n}";
value = { 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 source ${systemdUtil}/bin/systemd-util.sh
${if c.hotReload then '' ${if c.hotReload then ''
if (! systemctl show -p ActiveState systemd-nspawn@${n} | grep -q "ActiveState=active") || \ if (! systemctl show -p ActiveState systemd-nspawn@${n} | grep -q "ActiveState=active") || \

View File

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

View File

@@ -57,7 +57,7 @@ in
programs.dconf.enable = true; programs.dconf.enable = true;
fonts.fonts = with pkgs; [ fonts.packages = with pkgs; [
dejavu_fonts dejavu_fonts
freefont_ttf freefont_ttf
gyre-fonts # TrueType substitutes for standard PostScript fonts gyre-fonts # TrueType substitutes for standard PostScript fonts
@@ -69,8 +69,19 @@ in
xdg = { xdg = {
portal = { portal = {
enable = true; enable = true;
extraPortals = with pkgs; [
xdg-desktop-portal-gtk
];
# For sway # For sway
wlr.enable = true; 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 let
inherit (lib) optionalString mapAttrsToList concatStringsSep filterAttrs mkIf mkMerge; inherit (builtins) any attrValues;
inherit (lib.my) isIPv6; inherit (lib) optionalString mapAttrsToList concatStringsSep concatMapStringsSep filterAttrs mkIf mkMerge;
inherit (lib.my) isIPv6 mkOpt';
vxlanPort = 4789; vxlanPort = 4789;
@@ -24,38 +25,34 @@ let
Local = ownAddr; Local = ownAddr;
MacLearning = true; MacLearning = true;
DestinationPort = vxlanPort; DestinationPort = vxlanPort;
PortRange = "${toString vxlanPort}-${toString (vxlanPort + 1)}";
Independent = true; Independent = true;
}; };
}; };
links."20-l2mesh-${name}" = {
matchConfig.Name = mesh.interface;
# TODO: ipv6? ipsec?
linkConfig.MTUBytes = "1450";
};
networks."90-l2mesh-${name}" = { networks."90-l2mesh-${name}" = {
matchConfig.Name = mesh.interface; matchConfig.Name = mesh.interface;
extraConfig = concatStringsSep "\n" (mapAttrsToList (n: peer: '' linkConfig.MTUBytes =
[BridgeFDB] let
MACAddress=00:00:00:00:00:00 espOverhead =
Destination=${peer.addr} if (!mesh.security.enable) then 0
'') otherPeers); 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; { vxlanAllow = vni: "udp dport ${toString vxlanPort} @th,96,24 ${toString vni} accept";
enable = true;
# TODO: finish this...
connections."l2mesh-${name}" = ''
keyexchange=ike
type=transport
left=${ownAddr}
auto=start
phase2=esp
ikev2=yes
'';
};
mkFirewallConfig = name: mesh: with info mesh; mkFirewallConfig = name: mesh: with info mesh;
let let
netProto = if (isIPv6 ownAddr) then "ip6" else "ip"; netProto = if (isIPv6 ownAddr) then "ip6" else "ip";
@@ -63,8 +60,11 @@ let
'' ''
table inet filter { table inet filter {
chain l2mesh-${name} { chain l2mesh-${name} {
${optionalString mesh.security.enable "meta l4proto esp accept"} ${optionalString mesh.security.enable ''
udp dport ${toString vxlanPort} @th,96,24 ${toString mesh.vni} accept udp dport isakmp accept
meta l4proto esp accept
''}
${optionalString (!mesh.security.enable) (vxlanAllow mesh.vni)}
return return
} }
chain input { 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 in
{ {
options = {
my.vpns.l2 = with lib.types; {
pskFiles = mkOpt' (attrsOf str) { } "PSK files for secured meshes.";
};
};
config = { config = {
systemd.network = mkMerge (mapAttrsToList mkNetConfig memberMeshes); 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)); 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

@@ -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 # For pdns_control etc
systemPackages = with pkgs; [ systemPackages = with pkgs; [
pdns pdns
(pkgs.writeShellScriptBin "pu" ''
${pdns}/bin/pdnsutil --config-dir /run/pdns "$@"
'')
pdns-file-record 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 { (mkIf config.networking.resolvconf.enable {
my.tmproot.unsaved.ignore = [ "/etc/resolv.conf" ]; 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 { (mkIf config.security.doas.enable {
my.tmproot.unsaved.ignore = [ "/etc/doas.conf" ]; 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 # 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. # 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() { ensurePersistSource() {
[ -e "/mnt-root$1" ] && return [ -e "/mnt-root$1" ] && return
echo "Persistent source directory $1 does not exist, creating..." echo "Persistent source directory $1 does not exist, creating..."
@@ -335,6 +339,16 @@ in
(persistSimpleSvc "jackett") (persistSimpleSvc "jackett")
(persistSimpleSvc "radarr") (persistSimpleSvc "radarr")
(persistSimpleSvc "sonarr") (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 { (mkIf config.services.minio.enable {
my.tmproot.persistence.config.directories = [ my.tmproot.persistence.config.directories = [
{ {
@@ -449,6 +463,35 @@ in
} }
]; ];
}) })
(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,8 +35,11 @@ in
isNormalUser = true; isNormalUser = true;
uid = mkDefault 1000; uid = mkDefault 1000;
extraGroups = extraGroups =
[ "wheel" "kvm" ] ++ [ "wheel" "kvm" "dialout" ] ++
(optional config.networking.networkmanager.enable "networkmanager"); (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"); password = mkIf (cfg.passwordSecret == null) (mkDefault "hunter2");
shell = shell =
let shell = cfg.homeConfig.my.shell; let shell = cfg.homeConfig.my.shell;
@@ -57,33 +60,17 @@ in
"/home/${user'.name}/.config/fish/fish_variables" "/home/${user'.name}/.config/fish/fish_variables"
]; ];
persistence.config = persistence.config.users."${user'.name}" = {
let files = [
perms = { ".bash_history"
mode = "0700"; ".lesshst"
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"
]; ];
directories = map (directory: { directories = [
inherit directory;
} // perms) [
# Persist all of fish; it's not easy to persist just the history fish won't let you move it to a different # 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. # 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) { (mkIf (cfg.passwordSecret != null) {
my = { my = {
secrets.files."${cfg.passwordSecret}" = {}; 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()) 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; cfg = config.my.vms;
netOpts = with lib.types; { name, iName, ... }: { netOpts = with lib.types; { name, iName, ... }: {
@@ -139,7 +116,7 @@ let
}); });
default = { }; 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."; hostDevices = mkOpt' (attrsOf (submodule hostDevOpts)) { } "Host PCI devices to pass to the VM.";
}; };
}; };
@@ -149,6 +126,8 @@ let
(map (map
(i: mapAttrsToList (name: c: c // { inherit name; }) i.hostDevices) (i: mapAttrsToList (name: c: c // { inherit name; }) i.hostDevices)
(attrValues cfg.instances)); (attrValues cfg.instances));
anyVfioDevs = any (d: d.bindVFIO);
vfioHostDevs = filter (d: d.bindVFIO);
mkQemuScript = n: i: mkQemuScript = n: i:
let let
@@ -184,6 +163,7 @@ let
else "ifname=${c.ifname},script=no,downscript=no")) else "ifname=${c.ifname},script=no,downscript=no"))
("device ${c.model},netdev=${nn},mac=${c.mac}" + (extraQEMUOpts c.extraOptions)) ("device ${c.model},netdev=${nn},mac=${c.mac}" + (extraQEMUOpts c.extraOptions))
]) i.networks)) ++ ]) i.networks)) ++
(optional (i.networks == { }) "nic none") ++
(flatten (map (d: [ (flatten (map (d: [
"blockdev node-name=${d.name}-backend,${d.backend}" "blockdev node-name=${d.name}-backend,${d.backend}"
"blockdev node-name=${d.name}-format,${d.formatBackendProp}=${d.name}-backend,${d.format}" "blockdev node-name=${d.name}-format,${d.formatBackendProp}=${d.name}-backend,${d.format}"
@@ -224,15 +204,15 @@ in
services.udev = { services.udev = {
packages = packages =
optionals optionals
(any (d: d.bindVFIO) allHostDevs) (anyVfioDevs allHostDevs)
[ [
vfio-pci-bind pkgs.vfio-pci-bind
(pkgs.writeTextDir (pkgs.writeTextDir
"etc/udev/rules.d/20-vfio-tags.rules" "etc/udev/rules.d/20-vfio-tags.rules"
(concatMapStringsSep (concatMapStringsSep
"\n" "\n"
(d: ''ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:${d.hostBDF}", TAG="vfio-pci-bind"'') (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 = preStart =
let
hostDevs = attrValues i.hostDevices;
in
'' ''
if [ ! -e "$STATE_DIRECTORY"/ovmf_vars.bin ]; then if [ ! -e "$STATE_DIRECTORY"/ovmf_vars.bin ]; then
cp "${cfg.ovmfPackage.fd}"/FV/OVMF_VARS.fd "$STATE_DIRECTORY"/ovmf_vars.bin cp "${cfg.ovmfPackage.fd}"/FV/OVMF_VARS.fd "$STATE_DIRECTORY"/ovmf_vars.bin
fi 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; script = mkQemuScript n i;
postStart = 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 # yeah turns out this is in nixpkgs now... we'll leave it as a sample i guess lol
monocraft' = callPackage ./monocraft.nix { }; 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----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USAwUDl0 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USB2WFY2
WENqQ09VdkJSbDgzdjNFSDZhM0I1MlN4NGJ3MDBhUHVYUWhHZkJvCkRMaDBoNDN6 MzFnVFYvb0wzc3grRDBrUi9teW1tNmx5dVBIRVc5OUdlcjdvTDJFCmpJL2tlYXND
eGk0NFZpTjdXSm1DNTF6bzVOdTNTcTdJYUtRdVVzQWUvZGcKLT4gWDI1NTE5IDJF bVJKbzd2S3JkSFVMdG40MS84V0lPTVJSNHVBQlNXSkN1eTAKLT4gWDI1NTE5IFBM
dWkzMExMKy92ODAvZnYwTkRnYk92RjFObTZORVI0amtaZG5lZXRjRzAKSmQ1Zloz ZGo3L2VlNUZqOEE1UHBuMHdqc1pyYlQ3R29ucE9pajU0bHMzamlXRDAKcG1Qdmw2
ME5Ca1ZiNkxsU3BLYlNuU3RSaHBWWnZRRTBFc2V1K1hkQUZRSQotPiBWSldTemtM cUlncDFWNXBOWnpIeDNZSFA3d1E1bjNaVVpKU3lMRjRaSHNtMAotPiB2XUh8eF4t
Uy1ncmVhc2UKSEtXOGVCRC9EZGVTVjYzaDhQdVJTS0dHUHRiN1cybVlLK3JOaGMw Z3JlYXNlICUrO0cxIH4gaTRoIF81SEpTN0Q5CnpWdEZpb1hZa2t5YkE5RnJFMHVZ
TUJyUE5CdnN1OFY3Nm9wbnRMeDV3TXQ5aApvRXdEOTRzT3NYbVRSVkpKT2lpbEFo WkhkQ2o0eWtyOE9ueDJkeGd2aUhmLzRUUGs1aUc1NURIOTYxczZhOEVmT0EKd2xk
RGdXbjhvYnl3Ci0tLSBlSmliMk5TMWU5aWwwYkdwd2NDQnNWUXJJclFHKzB3M2Zs TXFHN051d25PQmtNUVZkVEFGUVliZjdmZDF3RWFkaEhNTzd3ZVd5N3dlNzQKLS0t
YlAyWHREQitZCrcVpAY1+uNiY1rOKCLzWaFJLdKZ1/iwN+BhD7AoMo65Tb0g65SV IGZDR1Mxd24zOW05bitzQnN5WWVOOGtCNEc5aXIraEF4eXFUQm5CZUdCV2MKd44C
kfHePz8prE4OCGU57Mi3A/4Uwo4NCqwDGoqHDDgxOy1cA33Urp8Mlor8XFxI1/mt /Trgg0OEZ89/jqbj56z/Hia1Ka3ZsEv6bXPI/kcRvFDBFTgtvG3KWCgMBtTUHXzY
hwzY8F4= TKBPoQqrUf7plH7a/mTx3KR+4Y+yF+1i86s7TzYjD8d1xfFH3BsVtg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,26 +1,30 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USByZ2Ji YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBuQjBY
NnR4c1JUTjBZaGpxTFAzU0lQMkRIbzBwZHgyL045blhDU3RFaUFzCmdwYUhqT0xw K1ZTUzR0SmFoYUZLdzUzNUt2M1B4RFdPZzFkcW8rMWJLUjVHOUN3Cjd3ZHdMSzdp
dlpHNUJRVWNpbkdqaE04eS80M2ZDNU5QUWtqU1NqMDA5OWMKLT4gWDI1NTE5IG1I OWswditVWUhYNjZCdjV6M1d6U3pNak5hUlZjTHNqSkZPSk0KLT4gWDI1NTE5IGNG
Nm5wZUJaOFRaWk54THFybXZTOHA0bWQzYWdyQnR3bFBNTTE2c043RFUKS2pnN0h2 ZytHazJQWTExMjNkZWV2aDUwcnZjUGcwcXRkZWl0M0F2R2JpUVlJekUKWTFlbkEz
NkpMRWtoTm11bEp6bEhaNXBLOW1jWFh5ZGhkbWZVTkxqcFpJcwotPiA6K19JIzM9 YlN6dkgrMmFFcmlZMjQ0b2oySjlEMC85cUF5QlA4NGF3blBKRQotPiA8M1hbWS1n
Wy1ncmVhc2UgLSlMTktAaCB7ICQ5Ck1OY2NNUTI4RXkrVHRBMjZiNDhOVlRHYWs5 cmVhc2UgOmlZJFYmJgo3UExzUHhSVi9lbVRjNW4xTnVaUTVGSGVDNzRUNlJ2TnVu
SnBVNmtDSUdmdnVST3dJMEl2Z3VmOVVkbHBjaWovVm50NUoxRzIKUGc0RThGdndw U0wrWU5kd3dyR2phcUNVUHJCYjdzeWhjNVFOV011CmZMOVN0WDk5eUg3ajhwbStQ
dW9RRzNPQzRHMXo2UHBldnpPYkJYdFRQblhPU04yVTF5Tmc2TXRPdy82ZWRFU1p3 aHExT0EwblpreTBXVHh6Zk1HRkhzcmFQNnoyVUlaeStMUkgKLS0tIG1aY3Zadko3
SzJPUGVVdAoKLS0tIHJwRkxvT0dYMnUvL0cyVVBHeE92NXFSSFJOMzhUU21MRS9S V3pDMGswSDZCeit4SVhSRCsrZVRVZVkvQkNsQWN1MXFMUGcKcK74YULWqWw9VvkI
MlZ2TWtZNWcKQ2j8mZSXH4kgasTeLdN4L6f8Bgwkg+X+mZydMl0i4RWnBj9xB/CI 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
0SGv4eAC3vFi6xE=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,16 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBYUTlr YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USA3MEFh
Mm1ubFFPTHhhb3NERHJPaXoxamt5bUtqQmRkUTFGZCsvaUtyZGdrCmVNUEtrSTVQ YURySHY5RjVQa1c0QmMvTkZtZElNd1BIeDFpR1U1YXZ5dndnclUwClA2M0ZYMk90
MkdHWnd1OTNSN3lOaFdrdWsxUHM2S0VZVlNRWGt4R0FyZ0UKLT4gWDI1NTE5IFNZ RmlCZHVISkxZQTZXeEhZVUROSVRGRVg4TmZKZytSL05FcTgKLT4gWDI1NTE5IFY2
QUhpVU04WWpEZThEaE82WDJvT3NVVWxNRVFEZjluVW8rbGhOQ0Rua0kKOWlZYlFh V2k3S0VQdGlQZ0hkU2ZoSWdOUG9rQ0p5Ukg3ZHU3MXVvNHlWYVN5VVUKcWtoWDJh
RjduNDAwS0NJa0lPNmx6dmIvdmFtcXJyaG5FQ2hVU01qby9sTQotPiBVJjEhLWdy LzRwcktWUGQ1bnJvRzVBVlpHU3NhdzYwUDBPTmx3MFNrdWNQYwotPiBKQil6Km0t
ZWFzZSBOYyllZE4KV0lYMmErd3RvUWp1UVRvdlpKTUZWWlJHK1laN2FoZGh2aVlM Z3JlYXNlIFdlRTlUU2cgVlxLIzUgJlVyQiJ8Cng3NzF2b3d6MnlEdURQWHgrSFJv
dTdLbWZsVkozVTBwb2JyeGpFMy9FcjR0eDhxQQpUL05uT2ZSUytNV01wMjFYRWZL Y1BMUkZyelJSdDJvUUUwY1k5R3ZBMnJaa1R4NWxlUll2azNseW5MQlpvVWUKYUdN
NTRkSmlkcUl4LzJYTDQwMm43cVJVeTdkY0dxdkI1ZjA3Z2NoU3NBCi0tLSB4Y3lV d1ZscXVxd1dxSGpzMjBYeWhhYyt5ZlNkSjhmV1ZTTDBTCi0tLSBBTG45VUJOSXZv
UVphTEtIWFhVQ1h4MUZzNDYzSEhDUFpMcE1ZdFNtd1A2Rk5lMUhRCp/F11nGICQA N3lYNVlyZldMVkpZdUMvS21NdSswVmU3VThZK2M2eis4CjwfWWJVR9Ty5dBXEr5s
L5uInKKk8jVGzpG93U4Kz3zut6jGF/is4cVuvkRHZLKiUVC4kILirdinnbxlJY2Y U+QrifXvyMJJvqOKEfMYYomFLpt/VtbDAUwNlqLnFIk1VZ2xPUBisXPtReiCK3gq
Gkpef0zthmNhfFhsgjj5Tmxyiu1L+6GkY0z2GlKR8uRUG4bXsDQzDkZQm8Wcmrrt P+i6bUONSmPYvmF82VgiuKDuz/kmWqVGg2iSBPIXAGLNIEojndQwmhnrsN9ex4Xy
9lG04SI0Dj7Gc2HJEwjl7ZPBi9qnFU49on+/Y5DIpzTllgE7luduFLjLM2Q+kNGK lBHkvVsN8EamUJwZ+FEZe4+PjA5yEnANWpeTELOt2gA92/jwwnNIsr07eVTlkh8X
cU8cF+gvrpyAFytiUZ3jhATtXsqC0dzsugjj8pmT1WQzZd6iqVJ84G2pMg+boVyn UmQ2owGwktXP7it6//NV3C7sAdazs2bX65WSnog2E8WPNAorKWI8RCYs33CshVFt
aVLGrcn3qOnIIJLKrYEjyxSYMGs= zzclZ4hRmlAmqMHYqFY=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

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

View File

@@ -1,82 +1,82 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSB2em85 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBZVkFh
Qk1UN3VxWXJCV2xVRnpxSEhDTmttQXljMStlZlhmQ2t1Qi9VbEh3CmpXU05vNVVr RDNNZCtySlNQTTZFbzJvZmF2TEZKVUM5VjFjQWNSeHlNc1JNZ2hNClVac2pEdWdw
a1NxV28vYlRKczRSQVBSSmwyWFpLRW5xMndVTkF0UnprY2sKLT4gWDI1NTE5IDdK OVpOTmhWWklVRDA3UzZWWk9QVG1ySUFmTzRSVlFReVdFVFEKLT4gWDI1NTE5IDVD
V21PeXRQODE1UWYxYmI2VmN2aGlOWVMzSWY3aEtkdlNtSkphVEdBMWMKQ0V0N2Vn eWN2S29aWVNVTTdldXFxZG1vWUNYK2RrajI0OGNGaklzdFByTzlPUkUKTmsvdTE3
WEU5cnRuNFRjNVYxYWNzTjlZUmx0ci9VdTBERHVRSlErTjY5dwotPiA+fGZXKnxC ZFBvalZsdENxR0VnV3FVSnFxTitBRlFWR1BzcjJWd1h0U3hpYwotPiAyOkUlJC1n
LS1ncmVhc2UgOnZeIGRnSytTMn4yIDh9PGR+TlswCm9xODRucm1RYTlEZklkcEdO cmVhc2UgIWFOMiogVDc8IFZETk9BSDVTIHEKQ01rc1ZoNXQxUDdtWGRQUlRCV3k1
RmR0L1BwTzdCSG9NR0sxcUV2Wm5uT3lia2FuTXA5M2VhQVZRaTF3NEZXckFiWTUK djNZeEtOaUl6Tkl5TDA1S2ZXWTFQZEFiV2pWMDJpZ2dkYXdBOGhSTVA2ego4UnBz
dXlXNWtHWUY4THN0K3lRUmR4YkxwMHJhb1RZCi0tLSBBc1VRQWo2SjRFV3YvZ0NL ZGhXcklMd0NVWlhsc0Voem1wUzMyREViUllCRAotLS0gMU5GQjdJaVdlUFJGMG82
eUNEeUQ5YzNlWU1LMUlOeWh1UHJVS3JBMFdjCruYC0f782So3smThuTgVCo7eZ4h YnZCUm05ak9qcmVyUVJSS0tHaWRRTld3ZkNPMArRgojrBQvlyjMhChn8jgUNDysj
LsHM6Ak+TWwLmyYiTvyGBopAYcQsoXogrV0lfHFDOJw4/PbsDzLsl5bUmmk5+YbT 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
nrVMBlK1X7TO5QQh1+r10gcMy2o1 sAtksQHMt1Njo1oRug3+/0iC6XWEig==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBpTXRa YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBHNWg0
MFNiNmVGT1FXTTlrcW5iTVhIOVpaZ2xSUHJLUm5kOXd4ZUlPbEdJClQ1RjBvWFN4 WGRGZnlTdEhsRm96T0ZMOWx4UUxkTitJa3hMWDE1dVkreEtEa0RVClpvR2I5NGpz
aWk4VWNYM3k1QmxqMndBNzI4ZEYzd01KUlloTzExNnMvWXcKLT4gWDI1NTE5IGZ0 aUF0N1VZU05oWXUzTEI1TVBLYlVVang3Zzgwbi9teDVlQlEKLT4gWDI1NTE5IG4v
elpiN0F5T0twV215U2N4NVlTTVlGWnNaYnYveWJNTjJBc3hhNWJuWHcKeFF6UnlE OXRIbzhaZEdXc1pjTnRRWHVxc2ZTcmozSlJkY3lyOGtvd1BVeGprVUkKdUNqSW94
TFdrSldQUmRNZlZkdHJub3NFc3IzVTVQZXFMeGVjUVgwYTZpRQotPiBGLWdyZWFz SUIyOWtsNU83cnVOTUxNR1BvWHJPczdzUWc4aGp1MjlpZm5ESQotPiA2KSV2PVx5
ZQo4VFRXbVF6S0g1T1dyT280N3JKck4za3lQMk9IdVN4a3dWRmMKLS0tIHcyNU8y LWdyZWFzZSBqezVxOHc9biA+aD4tP20mXwpwRCtMUDhmcVhGNXpCZlFmSllpdDVp
S1ExZ2VBNHJRcGk0NTNTUVJWYjJGdlpkbjlITExUM2ZMNHFFUzAKpcjYGp5hpP48 SmFZelNhZnJlR25DS2l2MlQ3ZGFtdGxkZEdEWVNrRlk1VEZBRm9GMHBFCkVlQ1hp
d9o+H2zQplmAmMau78pnvPqsEoYSxvaQ/6oO5+vm81VqFkYEqUBMqVNZ9viJGRsq WnhOTGl2R2s5RDRKN0p5TmF6Y0cyN3ZlR2pDZlhMVjQ1c0FJN3hCbFEKLS0tIEFT
UA5l70tl+qgDc3EKxecQ0wAH bjFiRStXMmJueHdsRm1nU084dDRpS0tBT21ENzZFclJXbE11NWJETm8KyMHU+tZY
QELtZCbXKWnP8QC6V84JIFAxoRslACwsIJZpogcZO/IFIV2RGunGjCJk6QBmhOPV
kJXRcGO/ndYjWfuU0U3+9HtPocnO
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IC9FSlh2ZyA0Ynpx YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBOZ1RV
ZU84aTNsaCsrd0ZXRHQxZmt6dnpGNUViQlhJSEs2d0lVQWhWN3gwCi9KQmJPN092 VVpOZ0Jhd2x4YklMOHdkNkd0MEM4ekZiYXFKMjRIdXlZVDFkOTFZCkRreDZlK3Bo
Tjl5UjFNeWtsdXhtNjhFdlNrS0Ezb1lqT1NjNDY2ZXd1cFUKLT4gWDI1NTE5IFRT WmJ1MDBxSU5UYzRuUDMvdnpVc0prNFF4MjM0M1FaZmg4V1EKLT4gWDI1NTE5IEkz
Rld4NUNOaHB0b0l2eTQ5STB5V0dnZS9YUjF1RTZ5VndDaW5SYzg3aU0KcU9jbDlx THNsVDVCbU9KYWpXL010OVRmNjBVVmVyVzhRZW5McVFJcnA3ZWJvZzQKOE1lQnVX
czZkVm5pUVlmdEw1MEVheU9QN3hpRzlUbFR1WTdNOEpKU3lNTQotPiBkXDItZ3Jl Nk1rWVAvS1lpbDEzb2ViUUE4RlB5NnJOZmNoUGdUMGNuMk1iMAotPiBuYVwtZ3Jl
YXNlCnQrY3FaendQNVF6RXNUWGZ4TE81QXJycC9kVTZjMDY4dnVLVkhIVWlMSlRS YXNlIHdYXzh3MSB0TyREIGpdTjheeQpJZ2pYUyt6UC9vVmRNZU1uaDdvR2ZUNG0K
NjRsL0o0YVlUUQotLS0gY0tlekNPbzdaa1EySlA2bkdqbmRMbUhUVm01NjJ1MEgv LS0tIHNCRno2V21tZU9XV1UwR2IvZDdkWEMzZDI4V25Yb0lvdmJadXNEZFV6TEEK
K2dqcDhQL3BCdwpUfh9ODJOtZ0jkeOSdCj17XbuALje7tBa41o2nl6at+WIgiodJ 81uT8S3QJNe+mVadi/VpXSPEP0Ygzm0/+1pB5qqYlSQEWTHqS55gCyFCwu+sjaDq
xZ+COxUZ4W1ehK2Nrjqe0zGL08JwHB3vt3Y5oitdlrjZcQRsBmM/GRKu DpYTSm1JAk5ql9NRj4fJvCS53lJZ4zo+5c0iJKmuRg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,10 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEJhUWxSZyBRK3Zp YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBBOVJi
OTVnQ2lZRzloWE1hYmxMYWZERDdXL0pTdFVGYUN3Vk0wbkhBWTFBCkg1YkVidmwy SXdGVDlXWmVGZUM0OTJPUWFYem5qZzAwZWxhNzhpT2t1VjdZU0ZrCjYxWUE2VW5o
ZVhDSWJOTXB6Qmw5OXNVU0RVUlFyQkE4MVE2eUp6ZXZBOHMKLT4gWDI1NTE5IG9u cGFLZ3g3Y3BpUWlWOUtUYTg4MGZXVHZvdVV3eU9iZW0ybE0KLT4gWDI1NTE5IDIw
YUMxZkhHc0RVZkQ3UEEvazArRnl5NGpvTkJPRWdFbm9qYzdjZjNZZ28KRU1FdmMz QmxaWTY0WjJFZUd6TUxqaHhRelpRQ1hGZVBEcm43d3JYUVhpTWp4aGcKVzhJdFFu
cVlzbHRFWUZqbkw2Ry9QVXppTFdNRTIwWnJBYzc0NUxieUMvRQotPiAxbn4nYSxC SEhUUUZVNVk1N2tzekpzUS9RazRCcFBhb2xxbkhRNEwzVys5cwotPiBkeE19PHEm
LWdyZWFzZSBNRyAxO0EgVDoKQklPUS93Ci0tLSBoTXhqZ2VjNTlOVzdBN25CeUdD LWdyZWFzZQpwR0xsb05JVlFXQTZZQkJSWHY0akNRdjh1eXFnbmFDWUlCM0xLWXBi
VFJtT2pDWi9taXh1SHpNTG9oeHJsbE9jCgGD+69tbzN5f1FlBBSMb/2GgJW2cmXI QkVuanloRQotLS0gZTkrT212MXdsZy9Kb1AwMkFHU3VsTElweGNlYkZ2UWVXRzkr
97MXqA888ugf0vppdqy5yu+D4GdjoIvkKv0= dnB0SHRnYwrveLSY6SdUDO+QH7WGniLIOPcECTQ7CiTj9lwD5Hm0rYLdvizolb33
CsGX/kSEI2bD
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

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