Compare commits

..

85 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
131 changed files with 11405 additions and 1423 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

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";
@@ -91,6 +97,18 @@ in
help = "Build NixOS configuration into an ISO"; help = "Build NixOS configuration into an ISO";
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.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";
category = "tasks"; category = "tasks";
@@ -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

@@ -24,7 +24,9 @@ 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 attic-client
]; ];

158
flake.lock generated
View File

@@ -35,11 +35,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1698258239, "lastModified": 1702969472,
"narHash": "sha256-qnhoYYIJ0L/P7H/f56lQUEvpzNlXh4sxuHpRERV+B44=", "narHash": "sha256-IJP9sC+/gLUdWhm6TsnWpw6A1zQWUfn53ym63KeLXvU=",
"owner": "zhaofengli", "owner": "zhaofengli",
"repo": "attic", "repo": "attic",
"rev": "e9918bc6be268da6fa97af6ced15193d8a0421c0", "rev": "bdafd64910bb2b861cf90fa15f1fc93318b6fbf6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -95,26 +95,17 @@
}, },
"crane": { "crane": {
"inputs": { "inputs": {
"flake-compat": [
"attic",
"flake-compat"
],
"flake-utils": [
"attic",
"flake-utils"
],
"nixpkgs": [ "nixpkgs": [
"attic", "attic",
"nixpkgs" "nixpkgs"
], ]
"rust-overlay": "rust-overlay"
}, },
"locked": { "locked": {
"lastModified": 1677892403, "lastModified": 1702918879,
"narHash": "sha256-/Wi0L1spSWLFj+UQxN3j0mPYMoc7ZoAujpUF/juFVII=", "narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "105e27adb70a9890986b6d543a67761cbc1964a2", "rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -185,11 +176,11 @@
"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": {
@@ -241,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": {
@@ -276,11 +267,11 @@
"flake-compat_2": { "flake-compat_2": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1668681692, "lastModified": 1696426674,
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=", "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "009399224d5e398d03b22badca40a37ac85412a1", "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -418,14 +409,14 @@
}, },
"flake-utils_7": { "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": {
@@ -451,7 +442,7 @@
}, },
"flake-utils_9": { "flake-utils_9": {
"inputs": { "inputs": {
"systems": "systems_5" "systems": "systems_6"
}, },
"locked": { "locked": {
"lastModified": 1681202837, "lastModified": 1681202837,
@@ -474,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"
} }
}, },
@@ -494,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": {
@@ -508,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": {
@@ -531,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"
} }
@@ -560,11 +551,11 @@
}, },
"nixpkgs-mine": { "nixpkgs-mine": {
"locked": { "locked": {
"lastModified": 1700347575, "lastModified": 1703756459,
"narHash": "sha256-wHdY7YFRepLNtPRh7gBP8EDJRbqC/hwYWupxTof7PQ8=", "narHash": "sha256-ztEMyPQZh3Pb+LOoWl5lbIK2LenP59sOUBC86CDmLio=",
"owner": "devplayer0", "owner": "devplayer0",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "72cc1ce8a7e476a724de861bbd066a1cb700e39b", "rev": "e80160eb2ac3a7111d07cc43a15c16b9edca01ea",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -576,11 +567,11 @@
}, },
"nixpkgs-mine-stable": { "nixpkgs-mine-stable": {
"locked": { "locked": {
"lastModified": 1700347610, "lastModified": 1703756491,
"narHash": "sha256-NLRu2yPRc6BRIIcI0KG9csLGiAhmZG2JXLrJI+gLJQk=", "narHash": "sha256-9VL34e0gzomwqRnryRn23V2ImYcaZIQdp7CsWg5TmlE=",
"owner": "devplayer0", "owner": "devplayer0",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8b2769b59113858ecf4cf24ddae9ab1b8dd7920d", "rev": "36611f5f7cfd401f51ad4ca76fd6ee85a714bb74",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -592,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": {
@@ -678,7 +669,7 @@
"nixpkgs": [ "nixpkgs": [
"nixpkgs-unstable" "nixpkgs-unstable"
], ],
"rust-overlay": "rust-overlay_2" "rust-overlay": "rust-overlay"
}, },
"locked": { "locked": {
"lastModified": 1682237245, "lastModified": 1682237245,
@@ -715,33 +706,6 @@
} }
}, },
"rust-overlay": { "rust-overlay": {
"inputs": {
"flake-utils": [
"attic",
"crane",
"flake-utils"
],
"nixpkgs": [
"attic",
"crane",
"nixpkgs"
]
},
"locked": {
"lastModified": 1675391458,
"narHash": "sha256-ukDKZw922BnK5ohL9LhwtaDAdCsJL7L6ScNEyF1lO9w=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "383a4acfd11d778d5c2efcf28376cbd845eeaedf",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"rust-overlay_2": {
"inputs": { "inputs": {
"flake-utils": [ "flake-utils": [
"ragenix", "ragenix",
@@ -862,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
@@ -63,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; }) {
@@ -117,6 +117,8 @@
nixos/boxes/tower nixos/boxes/tower
nixos/boxes/castle nixos/boxes/castle
nixos/boxes/home/stream.nix nixos/boxes/home/stream.nix
nixos/boxes/home/palace
nixos/boxes/britway
nixos/boxes/kelder nixos/boxes/kelder
# Homes # Homes
@@ -127,7 +129,7 @@
modules = [ modules = [
{ {
_module.args = { _module.args = {
inherit lib pkgsFlakes hmFlakes inputs; inherit lib pkgsFlakes hmFlakes self inputs;
pkgs' = configPkgs'; pkgs' = configPkgs';
}; };

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,12 +241,6 @@ in
exact = true; exact = true;
}; };
}; };
settings = with lib.my.c.nix; {
extra-substituters = cache.substituters;
extra-trusted-public-keys = cache.keys;
connect-timeout = 5;
fallback = true;
};
}; };
}) })
(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";
}; };
}; };

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 = ''
if ${pkgs.systemd}/bin/systemctl --user is-active --quiet swaync; then
${cfg.package}/bin/swaync-client --reload-config ${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 = ''
if ${pkgs.systemd}/bin/systemctl --user is-active --quiet swaync; then
${cfg.package}/bin/swaync-client --reload-css ${cfg.package}/bin/swaync-client --reload-css
fi
''; '';
}; };
}; };

View File

@@ -10,16 +10,23 @@ rec {
gitea-runner = 401; gitea-runner = 401;
jellyseerr = 402; jellyseerr = 402;
atticd = 403; atticd = 403;
kea = 404;
}; };
gids = { gids = {
matrix-syncv3 = 400; matrix-syncv3 = 400;
gitea-runner = 401; gitea-runner = 401;
jellyseerr = 402; jellyseerr = 402;
atticd = 403; 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;
@@ -33,6 +40,55 @@ rec {
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 = {
@@ -91,6 +147,7 @@ rec {
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 = { as211024 = {
v4 = subnet 8 50 all.v4; v4 = subnet 8 50 all.v4;
@@ -98,6 +155,57 @@ rec {
}; };
home.v6 = "2a0e:97c0:4d0::/48"; 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
@@ -119,8 +227,8 @@ rec {
"stream" "stream"
]; ];
routersPubV4 = [ routersPubV4 = [
"109.255.252.123" # placeholder "109.255.1.246"
"109.255.252.104" "109.255.252.63"
]; ];
prefixes = with lib.my.net.cidr; rec { prefixes = with lib.my.net.cidr; rec {
@@ -137,14 +245,17 @@ rec {
hi = { hi = {
v4 = subnet 4 1 all.v4; v4 = subnet 4 1 all.v4;
v6 = subnet 4 1 all.v6; v6 = subnet 4 1 all.v6;
mtu = hiMTU;
}; };
lo = { lo = {
v4 = subnet 3 1 all.v4; v4 = subnet 3 1 all.v4;
v6 = subnet 4 2 all.v6; v6 = subnet 4 2 all.v6;
mtu = 1500;
}; };
untrusted = { untrusted = {
v4 = subnet 6 16 all.v4; v4 = subnet 6 16 all.v4;
v6 = subnet 4 3 all.v6; v6 = subnet 4 3 all.v6;
mtu = 1500;
}; };
inherit (colony.prefixes) as211024; inherit (colony.prefixes) as211024;
}; };
@@ -168,6 +279,47 @@ rec {
}; };
}; };
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,10 +1,11 @@
{ 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 {
@@ -123,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 =
@@ -137,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;
@@ -148,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
@@ -202,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}";
};
};
} }

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,7 @@ in
"vm-estuary-persist" "vm-estuary-persist"
"vm-whale2-persist" "vm-whale2-persist"
"vm-mail-data" "vm-mail-data"
"vm-git-persist"
"git" "git"
]; ];
compression = "zstd,5"; compression = "zstd,5";

View File

@@ -3,37 +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 (lib) mkIf mkMerge optionals; inherit (lib) mkIf mkMerge optionals;
inherit (lib.my) vm;
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;
vmLVM = vm: lv: lvmDisk' lv "vm-${vm}-${lv}";
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";
@@ -116,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 = {
@@ -135,18 +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")
(lvmDisk "media") (vm.lvmDisk "media")
(lvmDisk "minio") (vm.lvmDisk "minio")
(lvmDisk "nix-atticd") (vm.lvmDisk "nix-atticd")
(lvmDisk "git")
]); ]);
}; };
@@ -157,19 +141,39 @@
cpus = 8; cpus = 8;
threads = 2; threads = 2;
}; };
memory = 32768; memory = 16384;
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")
(lvmDisk "oci") (vm.lvmDisk "oci")
(lvmDisk "gitea-actions-cache")
]); ]);
}; };
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";
@@ -177,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")
]; ];
}; };
@@ -203,8 +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; } ])
(lvmDisk' "media" "darts-media") (vm.lvmDisk' "media" "darts-media")
]; ];
}; };
}; };

View File

@@ -16,17 +16,20 @@ 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 = ${lib.my.c.home.vips.as211024.v6}; define DUB1IP6 = ${lib.my.c.home.vips.as211024.v6};
@@ -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,8 +1,8 @@
{ lib, ... }: { lib, ... }:
let let
inherit (builtins) elemAt; inherit (builtins) elemAt;
inherit (lib.my) net; inherit (lib.my) net mkVLAN;
inherit (lib.my.c.colony) pubV4 domain prefixes; inherit (lib.my.c.colony) pubV4 domain prefixes firewallForwards;
in in
{ {
nixos = { nixos = {
@@ -13,8 +13,9 @@ in
security.enable = true; security.enable = true;
peers = { peers = {
estuary.addr = pubV4; estuary.addr = pubV4;
# river.addr = elemAt lib.my.c.home.routersPubV4 0; river.addr = elemAt lib.my.c.home.routersPubV4 0;
stream.addr = elemAt lib.my.c.home.routersPubV4 1; stream.addr = elemAt lib.my.c.home.routersPubV4 1;
britway.addr = lib.my.c.britway.pubV4;
}; };
}; };
}; };
@@ -65,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 ];
@@ -141,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)
@@ -258,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";
@@ -302,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;
@@ -310,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: [
{ {
@@ -329,6 +326,12 @@ in
{ {
matchConfig.Name = "as211024"; matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false; networkConfig.IPv6AcceptRA = mkForce false;
routes = map (r: { routeConfig = r; }) [
{
Destination = lib.my.c.home.prefixes.all.v4;
Gateway = lib.my.c.home.vips.as211024.v4;
}
];
} }
]; ];
"95-kelder" = { "95-kelder" = {
@@ -363,38 +366,12 @@ in
}; };
}; };
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
@@ -416,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 {
@@ -438,7 +419,8 @@ 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 { base, wan, $ixps } accept iifname base oifname { base, wan, $ixps } accept
oifname { as211024, kelder } accept oifname { as211024, kelder } accept
@@ -451,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,7 +6,7 @@ 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; privileged = true;
}; };
cache = { cache = {

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
@@ -25,7 +26,10 @@ in
systemd = { systemd = {
services = { services = {
gitea.preStart = gitea = mkMerge [
(lib.my.systemdAwaitPostgres pkgs.postgresql "colony-psql")
{
preStart =
let let
repSec = "${pkgs.replace-secret}/bin/replace-secret"; repSec = "${pkgs.replace-secret}/bin/replace-secret";
confPath = "${config.services.gitea.customDir}/conf/app.ini"; confPath = "${config.services.gitea.customDir}/conf/app.ini";
@@ -39,6 +43,8 @@ in
(umask 027; gitea_extra_setup) (umask 027; gitea_extra_setup)
''; '';
}
];
}; };
}; };
@@ -123,21 +129,6 @@ in
"gitea/minio.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

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

View File

@@ -2,6 +2,7 @@
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
{ {
@@ -65,6 +66,7 @@ in
owner = "nginx"; owner = "nginx";
group = "nginx"; group = "nginx";
}; };
"librespeed.toml" = { };
}; };
}; };
@@ -121,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 = {
@@ -131,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 = {
@@ -231,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

@@ -318,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";
extraConfig = ''
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; proxyWebsockets = true;
extraConfig = '' extraConfig = proxyHeaders;
${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;
}; };
@@ -394,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;
}; };
}; };
@@ -434,7 +419,22 @@ 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;
}; };

View File

@@ -48,11 +48,17 @@ in
group = config.my.user.config.group; group = config.my.user.config.group;
}; };
"object/atticd.env" = {}; "object/atticd.env" = {};
"object/hedgedoc.env" = {};
}; };
}; };
firewall = { firewall = {
tcp.allowed = [ 9000 9001 config.services.sharry.config.bind.port 8069 ]; tcp.allowed = [
9000 9001
config.services.sharry.config.bind.port
8069
config.services.hedgedoc.settings.port
];
}; };
user.homeConfig = { user.homeConfig = {
@@ -194,6 +200,26 @@ in
}; };
}; };
}; };
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,5 +1,6 @@
{ 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
@@ -54,8 +55,7 @@ in
tcp.allowed = [ tcp.allowed = [
19999 19999
config.services.mastodon.webPort "http"
config.services.mastodon.streamingPort
]; ];
}; };
}; };
@@ -78,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;
@@ -90,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;
@@ -134,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

@@ -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" ./gitea.nix ]; imports = [ "${modulesPath}/profiles/qemu-guest.nix" ];
config = mkMerge [ config = mkMerge [
{ {
@@ -139,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;
}
];
} }
]; ];
}; };
@@ -151,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 {
@@ -158,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}
}
}
''; '';
}; };
@@ -184,6 +206,7 @@ in
}; };
}; };
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

@@ -1,11 +1,12 @@
index: { lib, allAssignments, ... }: index: { lib, allAssignments, ... }:
let let
inherit (builtins) elemAt; inherit (builtins) elemAt;
inherit (lib.my) net; inherit (lib.my) net mkVLAN;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) domain vlans prefixes routers; inherit (lib.my.c.home) domain vlans prefixes vips routers routersPubV4;
name = elemAt routers index; name = elemAt routers index;
otherIndex = 1 - index;
in in
{ {
nixos.systems."${name}" = { nixos.systems."${name}" = {
@@ -19,14 +20,16 @@ in
core = { core = {
name = "${name}-core"; name = "${name}-core";
inherit domain; inherit domain;
mtu = 1500;
ipv4 = { ipv4 = {
address = net.cidr.host (index + 1) prefixes.core.v4; address = net.cidr.host (index + 1) prefixes.core.v4;
gateway = null; gateway = null;
}; };
}; };
hi = { hi = {
inherit domain;
name = "${name}-hi"; name = "${name}-hi";
inherit domain;
mtu = 9000;
ipv4 = { ipv4 = {
address = net.cidr.host (index + 1) prefixes.hi.v4; address = net.cidr.host (index + 1) prefixes.hi.v4;
mask = 22; mask = 22;
@@ -37,6 +40,7 @@ in
lo = { lo = {
name = "${name}-lo"; name = "${name}-lo";
inherit domain; inherit domain;
mtu = 1500;
ipv4 = { ipv4 = {
address = net.cidr.host (index + 1) prefixes.lo.v4; address = net.cidr.host (index + 1) prefixes.lo.v4;
mask = 21; mask = 21;
@@ -47,6 +51,7 @@ in
untrusted = { untrusted = {
name = "${name}-ut"; name = "${name}-ut";
inherit domain; inherit domain;
mtu = 1500;
ipv4 = { ipv4 = {
address = net.cidr.host (index + 1) prefixes.untrusted.v4; address = net.cidr.host (index + 1) prefixes.untrusted.v4;
mask = 24; mask = 24;
@@ -59,7 +64,37 @@ in
address = net.cidr.host (index + 2) prefixes.as211024.v4; address = net.cidr.host (index + 2) prefixes.as211024.v4;
gateway = null; gateway = null;
}; };
ipv6.address = net.cidr.host ((1*65536*65536*65536) + index + 1) prefixes.as211024.v6; 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;
}; };
}; };
@@ -67,12 +102,14 @@ in
let let
inherit (lib) mkIf mkMerge mkForce; inherit (lib) mkIf mkMerge mkForce;
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
inherit (lib.my.c) networkd;
in in
{ {
imports = map (m: import m index) [ imports = map (m: import m index) [
./mstpd.nix
./keepalived.nix ./keepalived.nix
./dns.nix ./dns.nix
./radvd.nix
./kea.nix
]; ];
config = { config = {
@@ -104,8 +141,8 @@ in
onState = [ "configured" ]; onState = [ "configured" ];
script = '' script = ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}
if [ $IFACE = "wan-phy-ifb" ]; then if [ $IFACE = "wan-ifb" ]; then
${pkgs.iproute2}/bin/tc filter add dev wan-phy parent ffff: matchall action mirred egress redirect dev $IFACE ${pkgs.iproute2}/bin/tc filter add dev wan parent ffff: matchall action mirred egress redirect dev $IFACE
fi fi
''; '';
}; };
@@ -134,38 +171,12 @@ in
}; };
}; };
netdevs = netdevs = mkMerge [
let
mkVLAN = name: vid: {
"25-${name}" = {
netdevConfig = {
Name = name;
Kind = "vlan";
};
vlanConfig.Id = vid;
};
};
in
mkMerge [
{ {
"25-wan-phy-ifb".netdevConfig = { "25-wan-ifb".netdevConfig = {
Name = "wan-phy-ifb"; Name = "wan-ifb";
Kind = "ifb"; Kind = "ifb";
}; };
"25-wan".netdevConfig = {
Name = "wan";
Kind = "bridge";
};
"25-lan" = {
netdevConfig = {
Name = "lan";
Kind = "bridge";
};
extraConfig = ''
[Bridge]
STP=true
'';
};
"30-lan-core".netdevConfig = { "30-lan-core".netdevConfig = {
Name = "lan-core"; Name = "lan-core";
Kind = "macvlan"; Kind = "macvlan";
@@ -176,12 +187,11 @@ in
(mkVLAN "lan-hi" vlans.hi) (mkVLAN "lan-hi" vlans.hi)
(mkVLAN "lan-lo" vlans.lo) (mkVLAN "lan-lo" vlans.lo)
(mkVLAN "lan-untrusted" vlans.untrusted) (mkVLAN "lan-untrusted" vlans.untrusted)
(mkVLAN "wan-tunnel" vlans.wan)
]; ];
networks = networks =
let let
mkVLANConfig = name: mtu: mkVLANConfig = name:
let let
iface = "lan-${name}"; iface = "lan-${name}";
in in
@@ -189,54 +199,18 @@ in
"60-${iface}" = mkMerge [ "60-${iface}" = mkMerge [
(networkdAssignment iface assignments."${name}") (networkdAssignment iface assignments."${name}")
{ {
linkConfig.MTUBytes = toString mtu; dns = [ "127.0.0.1" "::1" ];
domains = [ config.networking.domain ]; domains = [ config.networking.domain ];
networkConfig = { networkConfig.IPv6AcceptRA = mkForce false;
IPv6AcceptRA = mkForce false;
# IPv6SendRA = true;
};
ipv6SendRAConfig = {
DNS = [
(net.cidr.host 1 prefixes."${name}".v4)
(net.cidr.host 2 prefixes."${name}".v4)
(net.cidr.host 1 prefixes."${name}".v6)
(net.cidr.host 2 prefixes."${name}".v6)
];
Domains = [ config.networking.domain ];
};
ipv6Prefixes = [
{
ipv6PrefixConfig.Prefix = prefixes."${name}".v6;
}
];
} }
]; ];
}; };
in in
mkMerge [ mkMerge [
{ {
"50-wan-phy" = { "50-wan-ifb" = {
matchConfig.Name = "wan-phy"; matchConfig.Name = "wan-ifb";
networkConfig.Bridge = "wan"; networkConfig = networkd.noL3;
qdiscConfig = {
Parent = "ingress";
Handle = "0xffff";
};
extraConfig = ''
[CAKE]
Parent=root
Bandwidth=24M
RTTSec=1ms
'';
};
"50-wan-phy-ifb" = {
matchConfig.Name = "wan-phy-ifb";
networkConfig = {
LinkLocalAddressing = "no";
IPv6AcceptRA = false;
LLDP = false;
EmitLLDP = false;
};
extraConfig = '' extraConfig = ''
[CAKE] [CAKE]
Bandwidth=235M Bandwidth=235M
@@ -248,12 +222,6 @@ in
CompensationMode=none CompensationMode=none
''; '';
}; };
"50-wan-tunnel" = {
matchConfig.Name = "wan-tunnel";
networkConfig.Bridge = "wan";
linkConfig.MTUBytes = "1500";
};
"50-wan" = mkMerge [ "50-wan" = mkMerge [
(networkdAssignment "wan" assignments.modem) (networkdAssignment "wan" assignments.modem)
{ {
@@ -261,33 +229,25 @@ in
DHCP = "ipv4"; DHCP = "ipv4";
dns = [ "127.0.0.1" "::1" ]; dns = [ "127.0.0.1" "::1" ];
dhcpV4Config.UseDNS = false; dhcpV4Config.UseDNS = false;
routes = map (r: { routeConfig = r; }) [
# { qdiscConfig = {
# Destination = prefixes.ctrs.v4; Parent = "ingress";
# Gateway = allAssignments.shill.routing.ipv4.address; Handle = "0xffff";
# } };
]; extraConfig = ''
[CAKE]
Parent=root
Bandwidth=24M
RTTSec=1ms
'';
} }
]; ];
"50-lan-jim" = {
matchConfig.Name = "lan-jim";
networkConfig.Bridge = "lan";
};
"50-lan-dave" = {
matchConfig.Name = "lan-dave";
networkConfig.Bridge = "lan";
};
"55-lan" = { "55-lan" = {
matchConfig.Name = "lan"; matchConfig.Name = "lan";
vlan = [ "lan-hi" "lan-lo" "lan-untrusted" "wan-tunnel" ]; vlan = [ "lan-hi" "lan-lo" "lan-untrusted" "wan-tunnel" ];
macvlan = [ "lan-core" ]; macvlan = [ "lan-core" ];
networkConfig = { networkConfig = networkd.noL3;
LinkLocalAddressing = "no";
IPv6AcceptRA = false;
LLDP = false;
EmitLLDP = false;
};
}; };
"60-lan-core" = mkMerge [ "60-lan-core" = mkMerge [
(networkdAssignment "lan-core" assignments.core) (networkdAssignment "lan-core" assignments.core)
@@ -302,13 +262,39 @@ in
{ {
matchConfig.Name = "as211024"; matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false; 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" 9000) (mkVLANConfig "hi")
(mkVLANConfig "lo" 1500) (mkVLANConfig "lo")
(mkVLANConfig "untrusted" 1500) (mkVLANConfig "untrusted")
{
"60-lan-hi" = {
routes = map (r: { routeConfig = r; }) [
{
Destination = elemAt routersPubV4 otherIndex;
Gateway = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
}
];
};
}
]; ];
}; };
@@ -331,31 +317,30 @@ in
nat = { nat = {
enable = true; enable = true;
externalInterface = "wan"; externalInterface = "wan";
# externalIP = assignments.internal.ipv4.address;
forwardPorts = [
# {
# port = "http";
# dst = allAssignments.middleman.internal.ipv4.address;
# }
];
}; };
extraRules = extraRules =
let let
aa = allAssignments; aa = allAssignments;
matchInet = rule: sys: ''
ip daddr ${aa."${sys}".hi.ipv4.address} ${rule}
ip6 daddr ${aa."${sys}".hi.ipv6.address} ${rule}
'';
in in
'' ''
table inet filter { table inet filter {
chain input { chain input {
${lib.my.c.as211024.nftTrust}
iifname base meta l4proto { udp, tcp } th dport domain accept iifname base meta l4proto { udp, tcp } th dport domain accept
iifname lan-core meta l4proto vrrp accept
} }
chain routing-tcp { chain routing-tcp {
# Safe enough to allow all SSH ip daddr {
tcp dport ssh accept ${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 return
} }
@@ -374,14 +359,17 @@ in
} }
chain forward { chain forward {
${lib.my.c.as211024.nftTrust}
iifname lan-untrusted jump filter-untrusted iifname lan-untrusted jump filter-untrusted
iifname { wan, lan-untrusted } oifname { lan-hi, lan-lo } jump filter-routing iifname { wan, as211024, lan-untrusted } oifname { lan-hi, lan-lo } jump filter-routing
oifname as211024 accept
} }
chain output { } chain output { }
} }
table inet nat { table inet nat {
chain prerouting { chain prerouting {
${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" name} 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 { chain postrouting {
oifname wan masquerade oifname wan masquerade

View File

@@ -2,6 +2,7 @@ index: { lib, pkgs, config, assignments, allAssignments, ... }:
let let
inherit (builtins) attrNames elemAt; inherit (builtins) attrNames elemAt;
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) prefixes vips routers; inherit (lib.my.c.home) prefixes vips routers;
name = elemAt routers index; name = elemAt routers index;
@@ -22,6 +23,7 @@ in
owner = "pdns-recursor"; owner = "pdns-recursor";
group = "pdns-recursor"; group = "pdns-recursor";
}; };
"home/ddclient-cloudflare.key" = {};
}; };
pdns.recursor = { pdns.recursor = {
@@ -42,18 +44,13 @@ in
"127.0.0.0/8" "::1/128" "127.0.0.0/8" "::1/128"
prefixes.hi.v4 prefixes.hi.v6 prefixes.hi.v4 prefixes.hi.v6
prefixes.lo.v4 prefixes.lo.v6 prefixes.lo.v4 prefixes.lo.v6
]; ] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
}; };
settings = { settings = {
query-local-address = [ query-local-address = [
# TODO: IPv6
"0.0.0.0" "0.0.0.0"
"::" "::"
# TODO: Dynamic IPv4 WAN address?
# assignments.internal.ipv4.address
# assignments.internal.ipv6.address
# assignments.hi.ipv6.address
]; ];
forward-zones = map (z: "${z}=127.0.0.1:5353") authZones; forward-zones = map (z: "${z}=127.0.0.1:5353") authZones;
@@ -68,14 +65,42 @@ in
}; };
}; };
systemd.services = { systemd = {
services = {
# Add AF_NETLINK to allow pulling IP from network interfaces # Add AF_NETLINK to allow pulling IP from network interfaces
pdns.serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK"; 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";
};
};
};
}; };
# For rec_control
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
# For rec_control
pdns-recursor pdns-recursor
sqlite
]; ];
my.pdns.auth = { my.pdns.auth = {
@@ -98,11 +123,15 @@ in
webserver = true; webserver = true;
webserver-address = "::"; webserver-address = "::";
webserver-allow-from = [ "127.0.0.1" "::1" ]; webserver-allow-from = [ "127.0.0.1" "::1" ];
dnsupdate = true;
launch = [ "gsqlite3" ];
gsqlite3-database = "/var/lib/pdns/dynamic.sqlite3";
}; };
bind.zones = bind.zones =
let let
names = [ "core" "hi" "lo" ]; names = [ "core" "hi" "lo" "untrusted" ];
i = toString (index + 1); i = toString (index + 1);
in in
{ {
@@ -136,15 +165,30 @@ in
ns1 IN ALIAS ${elemAt routers 0}.${config.networking.domain}. ns1 IN ALIAS ${elemAt routers 0}.${config.networking.domain}.
ns2 IN ALIAS ${elemAt routers 1}.${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-core IN A ${net.cidr.host 10 prefixes.core.v4}
jim IN A ${net.cidr.host 10 prefixes.hi.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 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-core IN A ${net.cidr.host 11 prefixes.core.v4}
dave IN A ${net.cidr.host 11 prefixes.hi.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 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} 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 { ${lib.my.dns.fwdRecords {
inherit allAssignments names; inherit allAssignments names;

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

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

View File

@@ -1,4 +1,4 @@
index: { lib, pkgs, ... }: { lib, pkgs, ... }:
let let
# TODO: Move into nixpkgs # TODO: Move into nixpkgs
mstpd = pkgs.mstpd.overrideAttrs { mstpd = pkgs.mstpd.overrideAttrs {

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" ];
};
};
}

View File

@@ -11,6 +11,8 @@
inherit (lib); inherit (lib);
in in
{ {
imports = [ ./routing-common/mstpd.nix ];
config = { config = {
boot = { boot = {
kernelModules = [ "kvm-intel" ]; kernelModules = [ "kvm-intel" ];
@@ -42,8 +44,20 @@
}; };
systemd.network = { systemd.network = {
netdevs = {
"25-lan" = {
netdevConfig = {
Name = "lan";
Kind = "bridge";
};
extraConfig = ''
[Bridge]
STP=true
'';
};
};
links = { links = {
"10-wan-phy" = { "10-wan" = {
matchConfig = { matchConfig = {
# Matching against MAC address seems to break VLAN interfaces # Matching against MAC address seems to break VLAN interfaces
# (since they share the same MAC address) # (since they share the same MAC address)
@@ -51,7 +65,7 @@
PermanentMACAddress = "00:f0:cb:ee:ca:dd"; PermanentMACAddress = "00:f0:cb:ee:ca:dd";
}; };
linkConfig = { linkConfig = {
Name = "wan-phy"; Name = "wan";
RxBufferSize = 4096; RxBufferSize = 4096;
TxBufferSize = 4096; TxBufferSize = 4096;
}; };
@@ -92,6 +106,16 @@
linkConfig.Name = "et5"; 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 = { my = {
@@ -99,7 +123,7 @@
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
}; };
server.enable = true; server.enable = true;
deploy.node.hostname = "192.168.72.2"; deploy.node.hostname = "192.168.68.2";
}; };
}; };
}; };

View File

@@ -92,7 +92,7 @@ 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;

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.";
@@ -184,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.";
}; };
@@ -209,5 +221,9 @@ in
message = "Duplicate assignments: ${toString dupIPs}"; message = "Duplicate assignments: ${toString dupIPs}";
} }
]; ];
nixos = {
inherit allAssignments;
};
}; };
} }

View File

@@ -32,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

@@ -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,6 +43,16 @@ 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
'';
})
]; ];
}; };
@@ -79,6 +92,7 @@ in
}; };
isoImage = { isoImage = {
isoBaseName = dummyOption; isoBaseName = dummyOption;
volumeID = dummyOption;
edition = dummyOption; edition = dummyOption;
appendToMenuLabel = dummyOption; appendToMenuLabel = dummyOption;
}; };
@@ -96,6 +110,7 @@ in
iso = config.my.asISO.config.system.build.isoImage; iso = config.my.asISO.config.system.build.isoImage;
container = config.my.asContainer.config.system.build.toplevel; container = config.my.asContainer.config.system.build.toplevel;
kexecTree = config.my.asKexecTree.config.system.build.kexecTree; kexecTree = config.my.asKexecTree.config.system.build.kexecTree;
netbootArchive = config.my.asKexecTree.config.system.build.netbootArchive;
}; };
}; };
}; };

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; {
@@ -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 ''
chain filter-iif-port-forwards {
${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts} ${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts}
return 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 ''
chain iif-port-forward {
${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts} ${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts}
return return
} }
chain prerouting { ''}
${optionalString ${optionalString
(cfg.nat.externalInterface != null) dipForward
"${if (cfg.nat.externalIP != null) then "ip daddr ${cfg.nat.externalIP}" else "iifname ${cfg.nat.externalInterface}"} jump port-forward"} (concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
chain ${dnatChain ip} {
${concatMapStringsSep "\n " makeForward fs}
return
}
'') cfg.nat.forwardPorts))}
chain prerouting {
${dnatJumps}
}
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

@@ -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..."
@@ -459,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,11 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBvaUQ4 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USB2WFY2
L2IybE1EZEtXSmdXTnM5V0Mxd2tQUWtmZ2wweERwcGU0bU4xU25JCmY3ZGtNY3pj MzFnVFYvb0wzc3grRDBrUi9teW1tNmx5dVBIRVc5OUdlcjdvTDJFCmpJL2tlYXND
NHlDUVJGYzM1MitmMGlZamdqaVNQRlJ3SWFQSVdUK0NZN0EKLT4gWDI1NTE5IGxi bVJKbzd2S3JkSFVMdG40MS84V0lPTVJSNHVBQlNXSkN1eTAKLT4gWDI1NTE5IFBM
cVRCMDJaeTB2a3U1dS9RMHVwczZTbmE2VlZzYTd4RVB1L0s3dGJ1bmcKK21jajBV ZGo3L2VlNUZqOEE1UHBuMHdqc1pyYlQ3R29ucE9pajU0bHMzamlXRDAKcG1Qdmw2
RytOSzZiZklKTkZ6RGFTUzlYYkZkWlhtQXA0QkxhNW11ZlF0WQotPiB2eTJhVihr cUlncDFWNXBOWnpIeDNZSFA3d1E1bjNaVVpKU3lMRjRaSHNtMAotPiB2XUh8eF4t
ay1ncmVhc2UgNUNiYCBHLApQSUZIbVB1VC9EME9XdUh3YUVua21DTVJLVXZYCi0t Z3JlYXNlICUrO0cxIH4gaTRoIF81SEpTN0Q5CnpWdEZpb1hZa2t5YkE5RnJFMHVZ
LSA2SnJjeUY2MXBla1crZGwwRysrZVBUMWg1ZW5QaUpsbHhLbTY1TityUnQ4ChJH WkhkQ2o0eWtyOE9ueDJkeGd2aUhmLzRUUGs1aUc1NURIOTYxczZhOEVmT0EKd2xk
wXMLGclaIT1UAe4u/G4+UUS+zOGdLcvIMygEPNugccysWXBNW2uLjAIksWGXrF7A TXFHN051d25PQmtNUVZkVEFGUVliZjdmZDF3RWFkaEhNTzd3ZVd5N3dlNzQKLS0t
R3EdsIYyYM8Pq1baHbfUH6BJN4SGXzBY/nQ8jAkhBLcmnVy9/L+B3kU= IGZDR1Mxd24zOW05bitzQnN5WWVOOGtCNEc5aXIraEF4eXFUQm5CZUdCV2MKd44C
/Trgg0OEZ89/jqbj56z/Hia1Ka3ZsEv6bXPI/kcRvFDBFTgtvG3KWCgMBtTUHXzY
TKBPoQqrUf7plH7a/mTx3KR+4Y+yF+1i86s7TzYjD8d1xfFH3BsVtg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,28 +1,30 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBmR2JR YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBuQjBY
ZTJCZ0IyeDBNYTFJcjN6cWVEcWlvU05MTjVacVF3VDJ5M0M3QUcwCmZyZjR3bW5X K1ZTUzR0SmFoYUZLdzUzNUt2M1B4RFdPZzFkcW8rMWJLUjVHOUN3Cjd3ZHdMSzdp
NXRQTXkraEJkUXNKbmR0ejV3bnNFTnlqS0R1SlBrc3NSVUUKLT4gWDI1NTE5IEYy OWswditVWUhYNjZCdjV6M1d6U3pNak5hUlZjTHNqSkZPSk0KLT4gWDI1NTE5IGNG
bmFzZmd5S0RiZzZEMTNWd3liMkV1S0MrcTNCRkRpRnJSbUd2LzM1elUKZ0d1SCth ZytHazJQWTExMjNkZWV2aDUwcnZjUGcwcXRkZWl0M0F2R2JpUVlJekUKWTFlbkEz
UnhMcnIxMWVTUjEzaFJWTmc3ZE04SitNbmUxNUNaRTUreE5XRQotPiAxR01EUlBq YlN6dkgrMmFFcmlZMjQ0b2oySjlEMC85cUF5QlA4NGF3blBKRQotPiA8M1hbWS1n
LWdyZWFzZQp6bklwRjI5Ry94a3dDcnVRV0U0eTZVdS9wVlBjL2Q5czdBCi0tLSBY cmVhc2UgOmlZJFYmJgo3UExzUHhSVi9lbVRjNW4xTnVaUTVGSGVDNzRUNlJ2TnVu
dGJZTkcrb0sxODFseVI0UkVIUGZ5bXlUYlphNDdDUjhlSVh2MC9obEdvCjcWripY U0wrWU5kd3dyR2phcUNVUHJCYjdzeWhjNVFOV011CmZMOVN0WDk5eUg3ajhwbStQ
nhjDDf/v0jPRA9frtP73z5WPOFgeq6EqOFIZgJarvjdSh1bSBz2LvuAdK1b8uf4M aHExT0EwblpreTBXVHh6Zk1HRkhzcmFQNnoyVUlaeStMUkgKLS0tIG1aY3Zadko3
b/ehJ54u1sG65H32MWbDwoXUIoVzUvNg37Vze5VGBtIq7+2k3Stl++lkXze2hTPe V3pDMGswSDZCeit4SVhSRCsrZVRVZVkvQkNsQWN1MXFMUGcKcK74YULWqWw9VvkI
ztpM72FOL6iz6NA5K5eVq5wsWOIVHhVOsW9BKc9xjJfweZDDZ3fGgKn+VYLi1q6X NH38VHfDPW5ueSiyHHKn8MI9YosPr/TmkwgKd/DOIMVB74ahPalo1QUeg+eaBVnq
SsSXqaa0dBgd/wo1v+8YCUTMvcHdmMxJhC7RpyNwRZQbd1ju3e7G02D7B2uLhKoD Uncsbx5ecW3JswthkAhiktWHcdHmioGD6hCcgbWtfA3VvbAYc2gtAF2plfDVH6BD
g5kPPEJ6mf9QHpTv1TedyylPjgt3T3vjBIXnkutBIIgwGpW7sfs/gK8yYVqbw5JU c8veai3B15ZmDmq3b62sgVA9JUgbFaB1eRqcaPXmNgQJR4c8J+CZWXGQ/TKMqtAg
j6ro71pPW+SxPBabeF9jfo8E+GatCr7WA3xzOa7+rx5ZYUIuWcsvOhcr1u8L3Xqq chCDuczjLX8IxhMPF1gdCggSt5GtMhY47fpZJRbBUw01l8koqjm63KvEeg2ejFGU
IpIqKYqqZbGda7mvZxEFsTC2gLhqBk0Ndhv+KSRMUJYHlBi3VXldS7yLqlV8W5Tr +tXonI4jm2JUxlDTqmNSrVpmAgtPoEPszHc+GsnyNjAz4IXMm5UrQAZyDPgxtwQW
eppSsXw3dfQcKnDxRBihPPBIYpHDyJxiXN5oslcFO+0ITwmsjBEZK/CcqOUkVVJe ZAn0IdOB8rSL/AnRGUpPrecuauINQjONe8CapijjJPQK7g1AHscfxr+OJD1j2eV3
s/BmseckbfQVaIrAmBUjUBZx0w/g1dgkfb1YcNkotWfFKxN1RWnH0YgIS1l+NTG1 O6TNlz8vKW4t/V7CUf46ykxxExhA0mKJ53ksaXy2+HWoro2+c4nao/bEld5gt08U
7KeiSyw4m7Q7b7lyZvK1I3kholQ0wfNb2jXyBqvP12wqCMUE0LCaf3M8S+gq2Los uCpJtjPKB64X6vdzbY21/l87VRDyxbb3poENfrXlawqS0Z7i2gAbHN5EuiMv+35o
mUwHZfo8E9mUzo3PFerAIHd7fKKkqymgbzpXMSmO8VKXOTVuo+5wQ8iNhU0jTCzL sBaR0BfcbTn6VagC3i8HEEOO964FrW65pkqmGJcJMgUdcZSl+Y7gHMjWyodqGPOY
W+B4IMKK0QuRiMxdO/UW5VBwDT+5j9moZze332z9zgoQsrxBYdg8HIVb/kzrnmiB evT6xj4iyeM49vXynV2vrJRu5rr02hOS/8rPphV9c9q9ju5n8xbhS6IiybAelPwt
Z38uqgzRMPBxh43yd2vmS1cSdty4z8eDOj0SvIIdVNQNSIYEvMMR51WdyoIbqPw3 sa+xM+w0n+fxVksiXeFj7qD1LDN4+kwqhYP9SlzbA4fT3AkYYrxthTww4IFAweyQ
/8cwG4bKYJ/VAlk2ksdIZWzKabdqQ8fcgh4Esti57Dt3ghljDOyHqZH0eojb0k8s IJkpVL0/IZEIEfRzpr0lWLprAaoyPc64pecl0z/gBJDgz1kSG6iEh6K4qx7ahGde
1yz3kUKloq0rB8E3Zz7JiFjXLHv68bgYtX8bmBCS0YnUZQJ539xCbIeVb/W4hntf kBBpS2I2ZsdBkvmQxDSM7tYYBzPxcUNZlSFOIeUy3Xl+OGPlKgtMofbnjV8AFqUf
6VDlFfs9HNMtvV2ryq4CDOhSTvqixrXqO6YRtyH/JpU3IOm090e+IkUsPZ2u5nLB tOpgKpLUEkTgXbGUKBxHLj+8pbw7zOFp4sJ00i48ZzLP5D9jom+jS19wGshRc7cH
azRuhWq3zzJFfL97dHmkCO+Xy5whMwbQABCmWT+bVDFhJCHNIZjwJoLga3u48GDk dK92CctvrfndQ85yO0vqlyOdMTqjh/z3P2KmhL5SW6P51q5mseTvj7FNM7331DTs
mTxu0+XbWugXekSvWnSB2tcTo5YRPUxFn6uCbyvI0vNZmaFQZXHRqNlE4GzWEfkv rq7XWaBLE61eWWQ/dZKnqh5YbkSlU1+08Rl6H/vCU5hTC8fht3KTQSWPofkrP+5f
+jFJjr3OZFSm8G7j8CPftuW1 We/Pe62LyhV/MbLRA0nmU7Sf4IAnpHfa4kLtlYeB3xiqKd0McM//qzjuk5NoIgE4
nL0T8YXdGt8K01w+nt+j5bo5gFoRz5+1/ZZ9BgN2DOo4SClYnJWQ/x80X061yJ33
0SGv4eAC3vFi6xE=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,14 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBYdEYx YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USA3MEFh
QlZJaldncVZVQWF5S3dIR3ZpbXBZdUZoNnpjakgvbUs2TGlDeTBJCkRNSks4a2M4 YURySHY5RjVQa1c0QmMvTkZtZElNd1BIeDFpR1U1YXZ5dndnclUwClA2M0ZYMk90
MHVrNkNyVisweVdxMDA1NGxBVlMzdFl5U0pyQXMzWVFsYkEKLT4gWDI1NTE5IHJ2 RmlCZHVISkxZQTZXeEhZVUROSVRGRVg4TmZKZytSL05FcTgKLT4gWDI1NTE5IFY2
TmZKdXpKOE03MFRJT3B4NlB3dktoV3JpUUloREhNMXFrVkR1MkM2RmsKOVQ2dGNB V2k3S0VQdGlQZ0hkU2ZoSWdOUG9rQ0p5Ukg3ZHU3MXVvNHlWYVN5VVUKcWtoWDJh
TlM3aE9hN1g4UkpyM3JFYUdjbWZTOUt5QUxFdEM1ck1vWnowUQotPiAkRExcQUtF LzRwcktWUGQ1bnJvRzVBVlpHU3NhdzYwUDBPTmx3MFNrdWNQYwotPiBKQil6Km0t
OS1ncmVhc2UgVCBIe24rVz98bApsejlsdW15Lzk0dVBYTHp0b1dISmtpc3duWCsw Z3JlYXNlIFdlRTlUU2cgVlxLIzUgJlVyQiJ8Cng3NzF2b3d6MnlEdURQWHgrSFJv
aXVwN2RpcjhHT1hEdk41emR3Ci0tLSBBakFqdnlYMWUrNFc0R2MydjR2czc0eVNq Y1BMUkZyelJSdDJvUUUwY1k5R3ZBMnJaa1R4NWxlUll2azNseW5MQlpvVWUKYUdN
dUdkWUR5dWFpQ3dXWlNTclNBCkqHl+vYAsHNKPq/180XcAZ2ERmKSRWGVdlGr8VH d1ZscXVxd1dxSGpzMjBYeWhhYyt5ZlNkSjhmV1ZTTDBTCi0tLSBBTG45VUJOSXZv
T6Gf8PHML8SbS+5kSdoPHOfz7/dlJ82KEdmihO1cWaCAVJTxYbOZR1lShNipMk8V N3lYNVlyZldMVkpZdUMvS21NdSswVmU3VThZK2M2eis4CjwfWWJVR9Ty5dBXEr5s
qAO+r+7Lm8L68ig0LC3y4qRngYif3tCbFA3J7Ukw3CgAVynxLh293yM2CXRPoX8O U+QrifXvyMJJvqOKEfMYYomFLpt/VtbDAUwNlqLnFIk1VZ2xPUBisXPtReiCK3gq
2i6gJ28OlDpvfHXhR5sTQS2yAHYCYevGoAduEqxVRBt7mWJvno0K0i2MTug3pt3u P+i6bUONSmPYvmF82VgiuKDuz/kmWqVGg2iSBPIXAGLNIEojndQwmhnrsN9ex4Xy
3l2cR8jUv2qFLroZP4XKLjxaMFXDPkzcTwH9aah+jPkNgAwir4Iw2g9Xnd2w7C4= lBHkvVsN8EamUJwZ+FEZe4+PjA5yEnANWpeTELOt2gA92/jwwnNIsr07eVTlkh8X
UmQ2owGwktXP7it6//NV3C7sAdazs2bX65WSnog2E8WPNAorKWI8RCYs33CshVFt
zzclZ4hRmlAmqMHYqFY=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBqbzA5 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBoYlRq
UG9WdzRKMFRCd1BPQ2FvdmhXeW43eDcxQ1NSeis3QzFmUVRFVWo4CjNmY2k2bGow ckk2c21uMVZSR05SdlJpck5MT0FZeUpLTmpTRVN2WDkxWWxmVTBvCjlHZWtEQ2Jh
TFVtR1VTanpMdEt5RmwrYmxzbjJKYWFERDViYk8rc3JRc3MKLT4gWDI1NTE5IE5Y YUdybjJLWkNsZkxabTVIR0JnMno5YlMvVmhodDU1bktrSzgKLT4gWDI1NTE5IFZT
dUhBaTZWdE92QmdIcGczZ205VDR5TEFCM2dZVlRLUDViSTRYNlBYVXMKTlF6Rnps Um9lWXhqRFFLTWdNaTR3Sm1acmY4N0l6WE1FSW54ZEtQcEE5RHBRRHcKcE5KYmp1
V1ljT0ZDTi9HVXA1akZTeFdKZVpEUTI0cVMrb3Q2MzUzNUlNMAotPiBpRnNkeGUt WVdCVk0wMHZ2SCtUR0xKWU10N0xETXlXZmgxcmZZOFBXYWtBawotPiBlRm1gKGxU
Z3JlYXNlIHg/SyxwND5UIGtmc0NfID9YJz1WCkFrd241UDJ4TGRaMERWR3hjaytQ LWdyZWFzZSAqXF8gOkU8Tm96IGA4IC1TCjY3NUgKLS0tIDhjaTNrbG1iQ05iTWRS
U3JtZGZsY1ZxWGxiNDkrRjFyYm55em9wbVBMcUVWNjlFR0EKLS0tIGZzeE50cGlF UzIvNzA5TmVGS2ozZmYrYjlBY3J4Z1RRUGRNeWcKUTIEhWqr0fOODu86MDll7k3U
UzZ2ZStzUG1sVE5ieEtFMjE0SkZIRWtQK0Q2U2MzWHJITVkKsokrFMnFLDaR3HSe ThgmS9nlcUY3fMgXzZLtpHIJ/4ZSI+miu8RmLMaeC61qv6xNThGdx+MvU4tMBWKA
fabmNuirYnT5MAkkYCYgCBR9PQmQ5CIThDqjEh0u6KK6TSnqWluMV1pG7O3OxS9g Hv3XGi2MYL1jdHh2KYg5PgdqchYuHrFuBPS7c/tQow==
LlOrVb0vJhewY/MSZARfrU7Hq6ii7Tln0NLOHoNy5w1TfqYwJg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,80 +1,82 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBWYi84 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBZVkFh
UU4zeC9KQ1NQeFJHR21CU09kdVNmMVo4MXBqdlFTb0FaVnNSbW1VCkxxaUU1RHJz RDNNZCtySlNQTTZFbzJvZmF2TEZKVUM5VjFjQWNSeHlNc1JNZ2hNClVac2pEdWdw
S3pDS3NwQzFHTVJxbDhjbEtHak1yNlBOZnVIaGhpdmpZRXMKLT4gWDI1NTE5IGJW OVpOTmhWWklVRDA3UzZWWk9QVG1ySUFmTzRSVlFReVdFVFEKLT4gWDI1NTE5IDVD
aVFmb3hVWU1hOXludUdId3J6VExXamNHOWhQMTR4bDZwM2JremNpVW8Ka0dkWTZY eWN2S29aWVNVTTdldXFxZG1vWUNYK2RrajI0OGNGaklzdFByTzlPUkUKTmsvdTE3
UFdFWDIveFFjZFAraTRQQm9xYXhpWVpySzFtS3N1ell1d1d6ZwotPiBRfU99e3Uu ZFBvalZsdENxR0VnV3FVSnFxTitBRlFWR1BzcjJWd1h0U3hpYwotPiAyOkUlJC1n
MC1ncmVhc2UKU0t3MVJYQ1VWbjBNcVBRZ2czNEszOU5ld3VWcWYrVzhEUEFQUG4z cmVhc2UgIWFOMiogVDc8IFZETk9BSDVTIHEKQ01rc1ZoNXQxUDdtWGRQUlRCV3k1
YU9nCi0tLSBPMnV0ZmhBQnl6N3Zxd08rM0ZYQnorQTJnYUpNMGtJVEVSTXBvRUtv djNZeEtOaUl6Tkl5TDA1S2ZXWTFQZEFiV2pWMDJpZ2dkYXdBOGhSTVA2ego4UnBz
VjBzCjpxu1swTIZcnullNvM2TOzCJ8DGF7lyNLGtPKZ0T7ZD5/9M4r1Gbey02Mm5 ZGhXcklMd0NVWlhsc0Voem1wUzMyREViUllCRAotLS0gMU5GQjdJaVdlUFJGMG82
uU+WBJXEa2vNzMU0SihCryCcQsvKZENO1RNX0+Up/YWuxl0fMVYYYlwblEDtZT0k YnZCUm05ak9qcmVyUVJSS0tHaWRRTld3ZkNPMArRgojrBQvlyjMhChn8jgUNDysj
hLnXtrxKEfCaoAkZVwYLAuExm6WzzqzJIqPUaqhgt2tYaGLt9wxxGHPQfDC7S5+U 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
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+IHNzaC1lZDI1NTE5IGdTeFAwUSBwVysw YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IERMTWVGZyB3QTU1
SjB2L21leVY2blhaQkx2a3RQNVRuTlFtTzJWdVpkaTNOMDRZMm4wCmlWVUVNK1VM TEdvbFNaUmE4U1lCNzI1dlloVDBTNXdURjZ2akRMV0R6dm5ST0FNCk9YclRSakhT
UFFmaXhDcllxaUo1eElpcE5DMTcwVXp0VEVjZkVOakNYVzQKLT4gc3NoLWVkMjU1 NlV2UVJwak9Fa3dQNTNBK0xKWGlkNXdwZTBEMm4xZzc3azQKLT4gc3NoLWVkMjU1
MTkgVkZjdzVnIDRsVUNrTW81UUR5b2lNQURVTUEvTmUvRnpGV2dON2oyamVxSFk2 MTkgakk4UkFnIGNBMHNrdG84cUQxd1lqQmJIczlScU1jNk52Y3BtR2tXeDNWdWRX
QkRWa2cKMmc3a20xNFF5UHdldkRwWXJ3UjIvY3FnZVFUSitaTTlHMjYwRlZCWndB cEFCR0UKVU5vem5MZUFPbGhoQzJTYnF4OTd3OW9jYTRkazdocVJkZ0pRRGNLek93
NAotPiBYMjU1MTkgNXYwU0tpUTN6eEJKVjZ6em5mU0huaEtFMEFuNDY2UkNiOVNy cwotPiBzc2gtZWQyNTUxOSBnU3hQMFEgQ2RUaEUyV1ZGbGRtZnlIUEtTQXk5MUZF
UUJYQyt5YworeFZXV2V2TlJXYUhEWTg0ZEFUOUFGeEV4b3hDRVc4QVhJWEZnYWdr djYxZ0hBUThlV2tXTHNvdXpVSQpMdiswMy9QNUtCb1hkbGRqR0Nia3FXTzE1ajZL
YlJFCi0+IHtlI3NQLWdyZWFzZSBLYCBdPCBOSApVMjJweHBFTkNZQzJvQQotLS0g UXljSTZqM2YvbzVuWUFzCi0+IHNzaC1lZDI1NTE5IFZGY3c1ZyBYL1NrRjRaRnFn
Q1NFbDVIUEJNWURPUjlycVlxS3VzNUhmdVNGQncvNitWMG9FeVc3M1lXawrC8dSz RlNLdWJ3cThteDM3WmNaY210d3RmZzhCYmNpVXBwVGlVCjBkM0IzZC9zNjhmTTZV
xEFTDaITaS0DaYMrqlX45Hrzjw0sPV/lLNvBWHxX57dWwGpCo7lc7rmfcGSPOfsW R25ySkVoQWxQQ252WWFlWWZFMlc5dWtUN3VvVTgKLT4gWDI1NTE5IE8yUU1pWGFr
gi0Sql5avPRrarXhGxGaruEbQf76qvZoBh9I2j8zWDZ818Nri1FqhUfTLcmZtb7T NzUvZVpwalB6aklkWmJGWUQzTnJiRjNzdzY2MUp6MkY5M1UKR1VIWDVyTFpxem5F
Jm0miOLcTye8RD08LCO/tSCdxucbQ98Y6mjZH20LRv7JCEzCuUowcNZi+bU+z/ZS TmRNWE9zZStLanRTdlU5d0NOSEJKdWhDUjBCdy9vOAotPiBTJCd2NyFYey1ncmVh
xzfaT9HXeEA//9blWm64R5NRLwNrkzZxyIxZOz5i+Q2O395OIVmh6WmylGxKQ6GP c2UgfE4mYSBXfms0TC5FPiBiYmx8IGRFV35mciNoCjVZdHRiVUtMMWxEMlJ4b1F1
WweodKuqUt5eGVrrMPraaPXMhSA7R2tm3PSB5uDMIQbO5fJS6iJqC1e+2v30GRga TGgxY01XTlZpTEtndkg0T0hRVjlqVUJGMUpSaE5tVlZWR3VGenpkUXY2eXJtbmkK
TwWgsVmAgdfcVO7q0bb5nsbcIOoNWBtXbR+a9rkhFiEZ9w60DQjntC4LYEB/eSIm ZCtqRVN4dnZENXdoYkpjNHRWYXkvN2laY1p4YjV0WQotLS0gOHlEVTVOSmlNemNQ
1tDCBsC64YZxhh/8oZ6hNpd/2N6ptQCV0lty/R9EM14HN2eoRSNYT93/OclNKkHU TW5ISk1DeDlEM1RoQ2JtUFlyTXRKQjdnT0hiend2VQqrUFvr+76sKn0ldBmZMlEW
ErxoaQgOC/v+RjKQhQj0Pt7+DqUsZt+elCxIUBZ4+P8imMf4eF21ERo8ExZ5MW5D U2k85DLo2KU+/+GtbkZwVXxxIZHMLpoJgghHk9ptdalUgLGcl0X15x9jVaw8aeta
e/6RTG0sNlufypGXZ4SVi1aENO1dh/njOb51kuFaXqu7earX+0Fyl+nZdlCxfaWH hbeOHotRHY7bC3z0S74riTk3xDMR1eT0QGhDMWHjfo8SkCftOYBlFfhTftevdep3
wb1Gkw3JdNebeL5z7Nse1gmEUDNcPdJX/IoQFrzpNdojLogampeyGX6u1u7+pzU9 pKMZsuQMwH9JzxgUfcxIcWE975cZzrEJ85nfWMGvdSjcg51KNxP/UUPRxDlcbCEf
Tb5FqhndWFy0q0i+yQdJNKRmSOXZopbGKAGWDF7dyvbom88LgEWeb3q8N9pTiVHX 9XX5apSzNsTI3ibGD1n6Qwq8bdVYDMHmy5pAhw4l8L+SdoU1tGdw7JOA16sMCJbx
/SOW6yf0mmZZcFUXehYjrYyQOFi368pIYMO9s5iVZz8AUkQFN+Hu9OrBtNyoEkoY T4bV0ky/PGRonjJuCyDBj8oe9vMe1ZI1O/ITtktekS+wocxBs6QXlY7pIZMlGUn2
cR9CjN5UfVc4dXYOLAigdCjB0GnKDwHV7LfKPpIkTN4uYmL/92HdzqBDpUeK5KfD 6m59ZEEaf7R4/MdnmBDNDkQuyXaKc7SaTc6h5sKWzXdYScGUKvgUQ7U/WJ2ItUTC
nNOMRRGkX98cYH7PqB9HOXZBW/Td/uxWEd5FaOxnVN4f2B+PnT1avoKxn7x+eWYJ N/Xq07GkZZMt5MYBlyEr+/mKWlcy+ylJPGb7EswvQWaHoeM1QF0XLZ1v+W/Xsso0
IXHU3i8dEkBRBfXDQ+amC0n8y9sheyra15Ob1Ujwud0Oq1F7Okec3AifbZAAn8M9 seIoz+geSu9a02kwfsa8WvWXdIAT5X2pNGPClVNzjQ23pfQfQuW8ZQrGmIFR4g5A
hTspHjnl3vacuE21dzE1368ZVPvIPOuGM88MQGADjP5CBRlolpU5hnBKN6zQOjuh 58T1K+vGLdShqqVGyJFMVrSuOzqX5FVmZalu7/++1IQfiRGUlrHKoPlKWnCfFEOu
Gpam+y/kbcopy5vIdnFGPH0vMAEnmEF6ER4OA/0= 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+IHNzaC1lZDI1NTE5IG44Q3BVdyBpeWMr YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBEcDNm
Q2U1RUFYMmY1cW13bzZUNHBZQ1VEQzRFcHpIMnpWWjNhcHhQdmhJCk0vT211aFo2 ajc3a0gwYmVSdkowak1aakJ3VWZ1Rk50S1JIS0ExVktVSW9OVWljCjJqbUEySlNK
RTFvY2JiblBUTkFWelVDQzdQZXhxUkxoQkFIVWQ0eTFNTVUKLT4gWDI1NTE5IFV6 a0xyT3NPK1BBMjZzN0RrLzhwVitvS0pzQ25iSnJSc3FmM0EKLT4gWDI1NTE5IHk0
UndBc3pwVUlqUHVPSE1rRTN2Y0FBT0duYUxNY3ZOTGtnWTRxTDZ0UUkKeTVBUE5O M2pkWHgyMHR1WHc5bE4xWTFDTndKdkhRdTg5bzZIWEU2cm02UDZBQjgKUUY3dS9p
dS9RN1l0Mjh6cU5GVXA5UFNCODVCL0hESVg4bzY5WlVuTFhXWQotPiB0LlVsXy1n U3h1L1ZvTkFPdURLZ2tSYXJXOGNZZ21KVGdIbXdhSUJrd2puYwotPiAoL0RtO0lH
cmVhc2UgNWpFcSA9SGtwbnxDbSBBK00gMl4qJ2wKaGxPUmFQZkliRWI4SEV0YVl2 OS1ncmVhc2UKVGthN0ZSU0ZFTTg5YW9UOXMwa3RnSzFlMjE2VTN3Ci0tLSBwMEMx
Sm1DNGZFN1pjZjNnbDFHeTFOVHI4cUJNL3BVUVNiS3RjVXJvSjF3bUVyUFEKLS0t Q0tQckd3SzBwUUE4SndMV01kUjVrOTdDWmxlcWs2Zy9TZk5yMzhvCrBXyLBZGuSD
IFFVbHNUSjEvelA5WTBwQWo4SnlvTHgyc29RcytBbzI2SWhmYXU0WVMvdkEKmIk5 dJodNI13obTM3UvX6hSgQ8Su+J3fOKr5NibkhQ0Auvlr2tUXhhDm2WOUlHjqVTq+
Orx8hL+TESnZ0RXl6T/UfQKJQfuNJB+1XFwo/OHp2FX4INjoxSGEq/wmR17+OxIk jWLSyhioDlIEyBgk8Zrl7KGeDzBi
yXNZJTxXmbjDm+Pf5S2NSselO9yh228EGNg=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,14 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBrVVBF YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBzaEo5
elo4ZmFnZVRkYzhIWTJ4c3g4bEhmUDNXejh1REJnd3dzT2QvL0hVCkExM0xEVWY0 RzNndHBLMUl3Z2txdVZoOVJBbWsrTVJwY01WeHQ0VTBaSm5VclY0CnJLYStYNEI5
a1FVTGt3aXEydi84NkxFcE5RbnFQRkVuYU5lYkkrMHhFc1kKLT4gWDI1NTE5IFp4 VTN3dEQ0ZjBKaU1PZSt3d0lldkE5VC9wNHdWazNWV2F6WWMKLT4gWDI1NTE5IEJ5
Ty8vdVV5T3RpVURYV0xXNFhQcXpzc2ZBZ2kwQ2JoY0gzNWNLZmNzQ0UKdStpd3JB amlzTDhCOFF0TllPd2RsME5Cb1lRQUZPTGNvOEZnL1J2Wlpnb29GblUKT2t1NFZy
TTYvMVQra09Ma25xM0ZJODdoY1Q3VkdVVlF3dXc0V1gzN0x2RQotPiAxJGFfezso blNZSHdxZjlHTlZmaTFYT0laQ1IwOCtDZ21vTjhxM0owdTAvawotPiBLYS1ncmVh
LWdyZWFzZSByeCh8K0cKck5FTE85MW5mNzg0ckVOM3lNUWhIeVhOYjRaYmdjWXVE c2UgRX0nVGtpSCUgNVNDWCVwIDBuPjM3MwplK2cwc2htQ2RLcFhUZ0RSc21uQlFV
aWVCK3M3cnJ6eFhPVU9GbGZtc1pWV0crdHVzakdWUApJU21CZWY4QUxpTmgzZWVu Y1QwSStOK3lKYmVVQ0J5RXA3Zytaa0JoMWtlWU9qMHBLNktFZWxtbFpDCnJyS0JJ
bUVKWE50REJTKzhNMXgzbE5pSzZQQQotLS0gVGFEUGJEaE5MSzgyQ1RRSTRWVmJs WEcwbndYbERreFhLZlRyQ3E0czB5NkREancxZk5BTGdWRkR6N2NrZmFrbnhSYlZZ
Y0xXYy9WWmVKT1p2ZWR3ZVpKMitUdwoj0dhAT0GqfI3GQtsiApILEWh59elSbB77 NElHNEEKLS0tIGVLdXAxU0ZaZVkxaHV4dEZNNUVuc2J4N3VRMzZzTktSMDY3bDVB
dq1FWJowbJeNRcXwRI+54Umt1awJ1ilafkTpKnqSof4GBTbRrj7qHAOU6GPvBz4v bWtmUVkKigIZ3J0s23vNzmbzJGjSMGBXK6o6xnsA9HXeQZ13VgKv5Qv+UHu+Z0g2
jj8U3xOClJ8pGvN3HTVACYyHouOJa2IznFsV8DUc1J2Eq8TY6AZ2IwsVXrakvZET TeKdQSrHbDB3ydIxaiXsi2ivULdrIMCyd96rEJFxrkVuVqSZE0ehG5j+o8lKk4OU
nwzpAUL6xDVaNRDEvFxr7qLWMVheif3TOucY PDe70slbZrVYDSu+OOUOCVRSopZreCURlYn2Pc4rjvrMAn5r1r+/AxjZMdkmmQZ0
l0wMBTus5zZzKg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyAraW1l YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBoZUI4
RHIwOVJscnB6dm56VTVqRkhGQ1FGbFNQRENqUk9DTWFHR2xwc1dNCjRHbjZhanRU bEdWSW1pcDdMYVRJLzdQOWhsdTlZMmlyZjRVeEFwU3Z3NzVFa3lBCkxRZ1FYeU9D
Z3ppblJ4L09kVVQrMEtHbkFTL0REY21pL0Rkd3dIdFdHVjAKLT4gWDI1NTE5IHUv L2dramp4WFFlREZ3NTFnWUs3clVEcnBhLzExclVac1M2SG8KLT4gWDI1NTE5IHVZ
QnV1NzN1K25Sb2R6Y09wNnZpMTVld29OV0pJNTRrcEt2cDNmaThHSDQKRjNmN2tG bk9QYy94Tk50c1dVanNvNzhpYWNqeFMzVVR0eTQ5OXozMmp2VFlad2cKaEdvUnR5
by9JMXhhYTFRZDVDOUJSTUIzVHpFTlQwRXVQc0c1QldBblhDYwotPiA3PHotZ3Jl ZjhsQ1FRaittQXFkbnRZei85MmtWb3pXRVFsc0RzNzBPMXFMWQotPiAzSXJZb0RE
YXNlIGFhe1M1Ti8gNUEpQWMgTl0KWWtuTTVKOAotLS0gZk9BMHJFT0YwOXBPR2Za LWdyZWFzZSBudCBGIDklTWIjVnwgM3YvCjRVYUd2TForc0gycnRBR05MbU90QmNu
a2ZDMXJJbzVETEN6NmtZaXU2blhwMG0vbW1GOApWIqThRiZNsfg1onVuyNpfxwPl dXlmQVB2bnZyME5heXlyMUdGTmR6SVd5SHcrTThrZEJYRlpnUmdmWnkKU1lyNjQ3
w2dxnKKGn57z0hPzoi+yDmxZhHWEO+rYS593Jo/RDRBBFTxJslKvPPyIPrR3rslr T0J6b3NPQ1FYZnhxTjFrYmN1UUJzMjEwcDBwSTJkTnlmeHl3WmFFTXB4eUVMWm9i
Y6/VyfmdxDkLBnjwlLkREJQy4buK0jDYv6Un/yR3psSp2ufcsW+2k57fD1uv0kpc NExUZzM2SHlLLwpiZjQKLS0tIHdqakpMSUNOdnlBcVA4RGJHR2d5QnJNT2dVclZN
HB1eGAwRXE3cfAEABTeXSIsX7gCGPWjRPswtgUx2wPXx djd4WXBLSUhORktzZ2sKzMjxqL3UPrtGmXDijdfu0AwxLJooK7ZKauYvXWSuZUrR
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+IHNzaC1lZDI1NTE5IG44Q3BVdyBlNU1a YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBrd0pM
dFNpdWEyeFpXbFNQWHN2YjJlWjdDNmhvaG5WRzh1b3M1VXVkVG1JClVXOHZ4ZUpL TGw1anJNaGoyQ3lMUis1cHVtRWFPcFFNZk9Cdm9TMEh1QTdKc25ZCm9VNzVQcUxE
UGpDY1E1VUg4Zk96SVhFTm1iNllBYnNUbWphYWNyYXFISVUKLT4gWDI1NTE5IEFV Njh2Wi80RWszSU5NczRNcHpVYXpRMzJNcDRsdGxyWVZsUDgKLT4gWDI1NTE5IFpT
UDhuNkJ6anRrM1RDS09RZk5HYVdYMTRhdUhFTHpua2F1WEVNL1lIQlkKVEZOaHNM MEJVUkcycVFJaVFOUURoL29VT3R0MVJCWW04NXVRVEpKY3hlY1lWRkEKcENya1Jx
UDBER3RmZXh2TEo4bm1NekZwQXdpd28yR1lYL3JadjczcFowdwotPiBNNW5QJlBH VnBCMVBrZkc4VDkvMnZoTllCL09vQ0VOZytuMnRvYVQxL2FldwotPiAzLWdyZWFz
Uy1ncmVhc2UKWXVNYkV3OGtkYkRucGFtZW5NcFF3aDVGdER4ZkRnd1VxdzFONldo ZSBCNX0gIiB7InIsfCAkOT9uM2UKUWpSYTZ1dUo3SHNaUlFibTl0UDhTaFh5Mk1n
NW0wV2owVG8yck5XUWEvaUxkMHRWTU9DegpXSWJkNmJaaElhdWhsR0pmeTB5ZUlw bFp2cjM5RGhqU0s0Qm50Zk1pUVZmT0R6ZjBoUG1EUGlKbHFzVwozQ2t1cWVDVVAv
TlROVVNXCi0tLSBJeDJqcFJWS05LSVJXVDI4a2Q5Wi9YZi9qL3Fyc3NwL3h5L1Vp Q21QTjF3NW5UdTBZRldnL0RmSlV3QndHcDB0d0lvZ1RHTTByS1ZwNkNVQnU3WmVn
ZHRReXBJCnhKAU+NGUjF/Kzo60E+TfxVyQv1I8JCOTBcTBr/VyC0YaB/8jJGY1C1 ZwotLS0gb0Fxb2FsYzgwRjJwSUF3ei9hZVR2Vk1ORDlIMWoyZ2RTd09hUmtvMWpD
ynYfUAT/MLEeuscQC4lSz6zCncKL6xWSwCzGSEmycPR+wg== 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+IHNzaC1lZDI1NTE5IG44Q3BVdyB6cExv YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBHNWg0
amxYQkFFT0tuZ0d6TDM3Z0pqR2VJRW1zU3kxeU5KdVdrSEJFQVNZCnpLeFZMVG5j WGRGZnlTdEhsRm96T0ZMOWx4UUxkTitJa3hMWDE1dVkreEtEa0RVClpvR2I5NGpz
ZC9UVm9tVG5pYW1rNndia1l5Ni9vVUVHVDJrTTMzWm1mNzQKLT4gWDI1NTE5IDJE aUF0N1VZU05oWXUzTEI1TVBLYlVVang3Zzgwbi9teDVlQlEKLT4gWDI1NTE5IG4v
Y2V6dStNQW5NeTBWdlB6MnpmclZXS1AwL1VBeGpzK2owbG0yNGluMFkKbE52cm14 OXRIbzhaZEdXc1pjTnRRWHVxc2ZTcmozSlJkY3lyOGtvd1BVeGprVUkKdUNqSW94
R29YNkZXa0JmWldOeGNqYnM0Y1RxbTRHK1N6U0VFVXhzOTJOdwotPiA7LWdyZWFz SUIyOWtsNU83cnVOTUxNR1BvWHJPczdzUWc4aGp1MjlpZm5ESQotPiA2KSV2PVx5
ZQp0UWpNSEwvNUxOeWZGRzRjM3ducTRSUmlmQXB4M0QycXdScVJRNGRUS1c0MENn LWdyZWFzZSBqezVxOHc9biA+aD4tP20mXwpwRCtMUDhmcVhGNXpCZlFmSllpdDVp
Ci0tLSB0bTE1ek1uYzhxMFFBS1R5M2Q2dE5Cd1VFaDFycDVnQVQvbG1tK1RGa1lJ SmFZelNhZnJlR25DS2l2MlQ3ZGFtdGxkZEdEWVNrRlk1VEZBRm9GMHBFCkVlQ1hp
Cjydk7j73bl0Alw3tnls5qh3LIMgXabTio9Nk+qVGm2HYaxgza9bsDqDZ8cI0ttD WnhOTGl2R2s5RDRKN0p5TmF6Y0cyN3ZlR2pDZlhMVjQ1c0FJN3hCbFEKLS0tIEFT
+lXtlYMEl+/ZiHEleXlnY/Af3sUv5b6rAdwWzg== bjFiRStXMmJueHdsRm1nU084dDRpS0tBT21ENzZFclJXbE11NWJETm8KyMHU+tZY
QELtZCbXKWnP8QC6V84JIFAxoRslACwsIJZpogcZO/IFIV2RGunGjCJk6QBmhOPV
kJXRcGO/ndYjWfuU0U3+9HtPocnO
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IC9FSlh2ZyBHZFA4 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBOZ1RV
c1BSN2hlZXF3R2JHYU1PVnVyMncwVEt5aHJMNjYwSWxzbTZBc1UwClZZNWpMMGZS VVpOZ0Jhd2x4YklMOHdkNkd0MEM4ekZiYXFKMjRIdXlZVDFkOTFZCkRreDZlK3Bo
cVM1NlpSc0hxLzcrV3REdkdiSjIwUFlsVVFtdE1HUVlaVDAKLT4gWDI1NTE5IEVW WmJ1MDBxSU5UYzRuUDMvdnpVc0prNFF4MjM0M1FaZmg4V1EKLT4gWDI1NTE5IEkz
cEdSeEx1bVhJb1ZUdngxRk9OTmZ2ZXp4UWtXbEFPR1RxdDUrT0tVRFkKNkZ4UGlS THNsVDVCbU9KYWpXL010OVRmNjBVVmVyVzhRZW5McVFJcnA3ZWJvZzQKOE1lQnVX
Sng5cFN2WmQzbDkrclZKTXN1OUFaQUVPSnNqRzBvTnppUURrMAotPiBnZy1ncmVh Nk1rWVAvS1lpbDEzb2ViUUE4RlB5NnJOZmNoUGdUMGNuMk1iMAotPiBuYVwtZ3Jl
c2UgU1lXNHcgZWMuPCBycnJVQSBtClp6MXU1QnF4OEN3WktBVENneERnc2FLaU9l YXNlIHdYXzh3MSB0TyREIGpdTjheeQpJZ2pYUyt6UC9vVmRNZU1uaDdvR2ZUNG0K
YTFNbkMwUnVWNXdCUWhETWZOa29Nc0xseVB4UXo4MlFFbmJ4enQKWFgrSjNwcjJh LS0tIHNCRno2V21tZU9XV1UwR2IvZDdkWEMzZDI4V25Yb0lvdmJadXNEZFV6TEEK
WklWTUVpdwotLS0gdFRhQkNBaFhDbG5DUC9QcE5xKy9yVFdYQklRMWtWRXlVTkNF 81uT8S3QJNe+mVadi/VpXSPEP0Ygzm0/+1pB5qqYlSQEWTHqS55gCyFCwu+sjaDq
M0I1bE51YwqBt/qRzgVI1cCuPn7IODeAF/MLbSzAUCGeqqM15vY+P+oBuPHptNG2 DpYTSm1JAk5ql9NRj4fJvCS53lJZ4zo+5c0iJKmuRg==
/uPhBBQ5ymuPR9akl0NrLDgsfLoY7JmyNdKXFsGkyGRlpgZpAXGc
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEJhUWxSZyBoT0s5 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBBOVJi
eVFJQWpjZFNoS0hTL0pMeEMvb2JsTEFmQW1WeDZqOW5PUTVDZURZCmlXd2tqM2Zw SXdGVDlXWmVGZUM0OTJPUWFYem5qZzAwZWxhNzhpT2t1VjdZU0ZrCjYxWUE2VW5o
QlhJcmRHajlsTk8rNEgyWEtGbENBaGhwUElBK1VWRHJoOHMKLT4gWDI1NTE5IE41 cGFLZ3g3Y3BpUWlWOUtUYTg4MGZXVHZvdVV3eU9iZW0ybE0KLT4gWDI1NTE5IDIw
V1poMTlPYkt2d2JFU0hnZHZBVXZJTC91d2QxOGMwSXRXb3pPb2h3R1kKbVFpZFNN QmxaWTY0WjJFZUd6TUxqaHhRelpRQ1hGZVBEcm43d3JYUVhpTWp4aGcKVzhJdFFu
NjA2b1R4YkRtZjlER0VUMThDU3lDbTNvYTBzcnFqVmt3YjliRQotPiBnWTc+QG8t SEhUUUZVNVk1N2tzekpzUS9RazRCcFBhb2xxbkhRNEwzVys5cwotPiBkeE19PHEm
Z3JlYXNlIFtqYSBYKXBxW3BgQiBrIHhXWU8KMjdUUHN4VFQ3K2dEQmVYTWV3VG80 LWdyZWFzZQpwR0xsb05JVlFXQTZZQkJSWHY0akNRdjh1eXFnbmFDWUlCM0xLWXBi
N1FicFZTeW50cnJkZUMrMHhlN096Um5PUQotLS0gM1pCZDEyOGdiakRJTlRWZzMr QkVuanloRQotLS0gZTkrT212MXdsZy9Kb1AwMkFHU3VsTElweGNlYkZ2UWVXRzkr
Y2ZtcXRaK3RCcG9OV2hrOG43UkJiRlJLdwoDxkSGj2lFD+ewF0tFCx/oCPanyZ34 dnB0SHRnYwrveLSY6SdUDO+QH7WGniLIOPcECTQ7CiTj9lwD5Hm0rYLdvizolb33
pkIDm9ptOluOUTxlmz9gYDZcMv57wfQ+zhli CsGX/kSEI2bD
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEJhUWxSZyBjeXUw YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBrTW1E
cHJLL20rYW9PUVBUMjk1RWtoS2FJMG5PMUJ4VkZ1b0ErSE1kR1h3CnB5VXZMbklz ZDFSRXIwbXZXUlNJbHZCVjFzMzRxTmdtbDQ5eWVOWWp3SEdSUERrClVMeFRLOGUx
RU9Bam5kaFRFZ25xTUpzVFRaU0ZqM2pCQnppWUpmb3JWVlUKLT4gWDI1NTE5IFRp U0dmT2ZUUXlCSGhJdE5ibkZLMmZ2VmZFWU9PQXpGZFRPQkkKLT4gWDI1NTE5IDIw
Tk9lSVU5eVpQd0w1T01xTkdNZnlhLzJjVVMxWTZuSTJ3c25Xd29WaVEKUENlR1FQ VXd5N2EwTVdhdkRYeVF3c201SnV3TmxnUGpOZWY5bFJaQWRWMWxQVmMKR2RNc2pE
eHQ3em9URDRqWWNaSjE2WWtGM2g1Q0grK0lGMDlkVnhTc3hYMAotPiBKXi1ncmVh aUs4ZW1XSGU1RzM4bHZYQks2cHZWUkhZN1hob2d1QnM3cCtlMAotPiB0XENxMSct
c2UgZHlfRWgKUXpTOTVRcDBGOUN4MjY2MktheEFVaWg4SWVRUEVNYkNWMkd4VGkz Z3JlYXNlIC5LIEIoJ1w6Ci93eE5lMUxDQjE3dWI4a05wUE5sV2dWN21ZN2lFRThv
M0F5SEF2WmhnTEltK3VPMnVKdwotLS0gak5vb0labGZxaEJJeExMZG9DMTBLcFhu YVJGNXNWaEtmbjVMaXhnemVuTXhOMXFtNkQvbGNuS3oKalZ6bEtGdHcxYnZNb0Zy
b1NUVGFPVWVDMlZkcGZqQmVIYwrDGRcof+Y7YXHDVIAehITHSDlmRAh+/EcyZqF3 YnlGTDdoVVdyeTJpYjJjZkc3alUyV2ZGa0taeEFIYTdiRnZmYmRjR3BzNUp2Ci0t
8JPmV2/tb1xsGuY7ykyElnbzSi3m LSBROCtSMzFNR3hTK1p5NVhGcFpqM0U0L0MwbWpzNUNTUWUyNEVXbzFFMGpjCnot
A6mYbp+jhpoyjZidXQfzLVcu6y34WqAfJZsfT6l5SJONVfSvSw+iP7XXW2T5OnE=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEJhUWxSZyBEbWZv YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyA3dDEx
R01SaC9JdWcvSXdJS0F3T3pRVkxnZHBJd2lLeUN5N2U2Ui9OZUh3CnI4cXpMeEp1 SUw4QVh5T3QySHFFemo2N1JWTjNCSGRiS1lmRTkxVEQ3S1NKR2dNClZNM1Vwc3dF
dlZQNVJCNkM3c1RxZ2ZYczBmaWtkVzZ3RUk3TDZ5RFcrREUKLT4gWDI1NTE5IHZU elB0Unk1OWpUb1RQMVpSTUpJUDNrZzFMWkhKK0l1dXVBYlkKLT4gWDI1NTE5IGRF
dVJzMUdDdXltREh2VjdlT3F5TGpoei82SWZRd0hHcGxCa1FSK1RtaEkKb1ZKSmwy TTZlMjdaamlwTUJzYlVkcVIxbmtybkFwakVtK2craXVBNVZraGZ1VlEKN2VvaU5r
ZDlBblpCejRRVGhadVdjMWRmSXd4THFHMkNpMTBRV0E2U1gzOAotPiBsIW44eC1n VjNRS2I5WE9kVDhES2dWTUV2cEVJaXZxNzRUcFo5blhGTDBaawotPiAxXCJQemxa
cmVhc2UgNmB1byB7VG00NywKSGdHR29PejdDNE1senk3L3Z5SEdYN3RVUmdCdC85 dS1ncmVhc2UgcDphKHFVTGUgIUonYydBPgowQnc2Wk5yS0E1bVBCWmpOc25MWWs2
NjluQkEKLS0tIENzajVxNFZjM29kQU1ON1J4WStRZmJBLzViQTBwM1BTT29XU3NG UTR1dVlJbnNXYUVJSkx5bWFIcmtpT1lPSVE3b2Zpd0JaTGZWcEtjelFDCitQcy83
V3YzbWcKDH97AxK1rbC1vyV4CmnsicPyk2xRNL/lrtt9Z0Syuu9VXJzL5gdW2PuS bEVvM3FNTEhyWVVFS21tS0VQWG5OOHFza28KLS0tIEZZeUZwVGxUdEdtQTZuYk5K
ayp71Qho1w== SHJaUDhHZ0JqbGNFSjJCaVlQTVo0OStkMWcKz/w0SnoHxnw71gr5DbXgMl59Kgjy
SW4tzNGeRcX2j4YdRjr77TP5UAzpQE30tEcrtw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpOcUlvZyBNalB5
RUZRNE1CTUJsbW1kSkxBSWVIcG1RUnBKd1gvcnRQVkZCUXFOQmhvClJUN2ltbnNk
T1grdVJSTzIyNTBTTGVEckVGQXdYNHdwOU5NbW1md3lGM0kKLT4gc3NoLWVkMjU1
MTkgcytxUmZnIHZ4bFZSS0huWFBDbUhNcTd2MFhvV0lOY1l3d3ZXNU4vT3dwMmlI
emhoV0kKcDF4M0FPK0JpclI5Q3Q5WGxpZWVYbHVWbkNWdTArclZsN09XK3VJSXc1
awotPiBYMjU1MTkgRjRCNVZmcXVnQnJ4KzZoM1ZkdWxYUkJTM1JuK3ZlRWJYdkFR
WXpFSmR4NApTbU5qR3ZuN0ZmbzIvMTFsMkdNSGJXSVlrVmZPdnZvcHFiZW45SW9I
endJCi0+IDEoIjlcJi1ncmVhc2UgJUE4IWl5ODkgfGVdLihEfT4gWCAreSduPS4K
bkI2Wm9LRGJXdW11aDl2VgotLS0gTENqYjZEUUZaWVZEcWQvWW5yTzJEdHRLeDJm
QUl5aytXdDE5QVMwVHZVSQo+aDbaGNOrz+hTSUQ4IAjDC9EfNwrlXDZtBqw8HkRv
1/Rr737scjrM7Bgt9zuKn6CB0zdeHTW5u685V2hCW/3aTy1eppWMWj3r
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHMrcVJmZyBpaDdW YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpOcUlvZyBFb2hL
UnBhRU5GdjRJYUJUSWZ5SER2clJkcUNuT2lBNHcrQ1RFOGdYRDF3CitVeUVWdmdz MCt6c0I4Nm5SMFhEaWNZRTRtNkpYeVQvVGFVZzVqd0E3L2dZdzFBCiszUXZSVENo
Z2tiK3ozL0MyYWNIYUxXQ3dKOTA2dVJ4ZXdnVS8rUmV4S0UKLT4gWDI1NTE5IGF3 cmxqdVFwMFA0elpFOGZoM3B6SW80MEhCNlJoRXV0WXpUeG8KLT4gc3NoLWVkMjU1
VTBrUVQzL2NtbDl0S0hmb1VMbGtQMXBsdXEyTDNmOXVOQkcwZlZrVEUKOXROY2Mx MTkgcytxUmZnIHQxR2MrakZmRFJMcjhabGFON2xQU2RBSDNvRCtuV3NwNGJ2bjZF
dzZhMzdpR0pIZERmQmJwZ3BhemU0amhxQXBaR2ZGS0dVQlQwdwotPiAuLWdyZWFz b2lCQVUKcFNzM3paS1ZnWk8xY1VzVmdTWndMK0JCTU14bUJvM3E2bjR2TmlTY2tF
ZSBuOVBkIGBXKjJKcDcgT0w2ZCBNCnBEY2VPYUN0Q2Rvd2tSeWc1aGhtbUxMa1RJ NAotPiBYMjU1MTkgY1MrT3ZkN1pUd0JVb0JWSDByNUNRd1NUd2ZiNVJrc0JCb0J5
cE1LQkZPSEwwZHM3NEdXbTRVWXBKM05YbwotLS0gNVlxRElEN0owQzNFUEhEOWRE NENrU0MySQp0bFpwRXRZcHRVdnN3eitkNHlWc0c0a0NmUjVYSVFXSVNFVHI0b0ZB
TDhOeWE4aTF5R3ViTk9rSGpmaEJsa1k0NAoAFgMMjCVFT2ZAXX+aIQdGNZ7Rpf6j U3kwCi0+IHQmKm9ERFdfLWdyZWFzZSBJIFkvRG5JIC9ZI002bmkgNX1hQHcKc2l4
A2KeGkhJ58vlLBM3wIKsyAXsQ4MuAb87JHhwWRGkqN1z6qzkGw+C+2qARyqXm8+7 S0N4YzgvK2xqZm1YVkl4ZlF1REVOTGRWOVZRQm80R2NnczlsdFlhTEd1RmVoNjZa
soYKs7I= KzlkVXNpbGZsNTRybAp4RDlIWmRqR0t3VjF6WVlSeTJ4aGZBd1dDNEpMTUhZenZS
WjZCK0FZcXJORkJwc1piS1FvSlkzc3R5T2s3Vk0KLS0tIDFPODlkZ1BSWEhHUHJK
QjdZOE1KcFNvcUYxYlFkL1FLNVJETTkySVNYRTgKOipmWGTV9SvGE4KVqgQqGw4e
CLP4PYlgdSmOATTIg32G/GVTM8NlvaII3q2GNS0Enx7Y8YwnwS2dGkYKVN3Da3b2
WJwMiBZRu/PC
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHMrcVJmZyB2Nlll YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpOcUlvZyBjbUF2
Y2FvWHB5ck55dWVaR2lEQkpvNHdyQWxqRFJlM0xFVktDTjJDSENBCjJ6WlAweTc1 VXYyVkRud3JFU0tDQThjQlVMTTFaaW5uTkt3K2dmd1pleTFOQm5jCkNUeEVqSXVX
bHVmZVNDWDRsbE1BbkJibTV3ZkdPK3h6ZUE2NDdLZ3ErL2cKLT4gWDI1NTE5IFE3 RGUxcU14Z04zMlRubVIxV0VwazhlZlFtTCtXT2dVS3l4b2MKLT4gc3NoLWVkMjU1
SXBWSWpteWpaREtCdG5EQnRtUFY5Uy8xUHo2R0RsdlJJdnVUV2k5WFUKZTRidWk5 MTkgcytxUmZnIG5SRTFOWWVIMU5yK3VTdkVtc0VYNnFkRXFrdEpiNTJIRHBvVnky
RDE2bmxaV3M5cXlQeWRXUXROMlNtSXIvTC9lWVRDOU5QWVJTawotPiBAYDNcNy1n OHRyWEUKVXVvZlBBR0dsUXFMa2dSZ1FWVWpvZGlwcHJ3YXRscnBoaU1FWlVFYTdt
cmVhc2UgbSA0Zy9yIyA2ZjJ4blAgSi1gXFZXRHEKZElIWXpHWUhacUVMMEUzazZS dwotPiBYMjU1MTkgMWlBWCtSU0I4Q1I0T0JrZ1ErYzlHRTZsUHhZQkpYQVJGNnFy
UDIrUWlIdzFQdjRIZjZ1SHR0U3JIUXRsMHhLNXpvRzUxVS8vdnhCdwotLS0gZElq U3ZPeU9RTQoxaGZDYnRaNjBRRGQzMGNsTmZwcjhBRXdLeEFqVWxoT3hNNHk5cU9C
VVhnV1ZPQnN3UVJTdGNJR0hFaTZ6aFBKVmR4eWxqdHpkTnYzTlRvYwrUjxxTMSxc Qk00Ci0+ICF8Q0EuPSIhLWdyZWFzZSBKcCp6MgpaTXQwdEllbmhRVDhOQTdpb0RU
07SzREkDIC7QgfCf1eFURkihesdoII10t6tyythWPGPZy99tMTFnSwN7p0aWkO6q T1VGZmdZK1VEMWdPUXduYWQ2YWx5aDFTQ3ZzRnRWbFRGN0lWUU5iQWdPakpZCnl6
qJgsbysI2/FQSZx7YUdnLWn9Gvg= MnI0SE5sS0x2MUZibW96SllDQVVOK2grRldPOWo0VSs3SkFUN1dqS3RqTTdPZG1M
eHI0T1BHK1F1cWlINAotLS0gckFnWkoydklhWHZhZHBkSkN5ZmdadVdiMU1QOUZW
VkJENWlHVWNXcEVsWQqYscIBmSi//ev3IN2ax0Ei7p8Atu4nYQui7yoY/1fiyGQL
DB5+R9Dm4YUNHt3bjrBYclLohDGdLUnOB00BXUqNmlLm4psL4Ey5Go8=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyBmR1BL YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyB1N0V0
a3ExRUZvS25raU92YkRWY3hQWWJoYnB2Ry9Da1ZwWHE1Y0tQdjBZCldDZkVwYTZl UW5ZKzQrSlFUMnR5QUE5N2Mzd0FvK3JCeFM2RVRtUklVQkNkU0dNCnJuUUxjQjcw
WkRGMkprekQ4SlNoTEpWUkxSbVBPRDFCZnYycnpacjB1OVUKLT4gWDI1NTE5IDVM NURFOFRWRG5WSnBPT1dOVjFNQWZVUjJUSU9WVTExeS9XZkEKLT4gWDI1NTE5IHdq
SDNqczJkSXczZXYwdkpiWm93Q1BqaTZRcTA2N0RGMSsxS2dJNm5paFkKMk0rUDhn eDhJSTZKN1lQRXNaZzNIWEx4NTZINzZpdFRPZWJGcys5SUV6bmc0Q1EKeGlqTjF6
b2x3bFJsMmJ5SE8xUXJvQ281VmZ5NVhnbk9lL09qb25wMzQ3VQotPiAhfGNKdidv b3Y3ZWNhcjF4WlpOcXh1OHBucmNCZmp6VTI1MXRjN1FlNlA3VQotPiBtQ01Dey8t
LWdyZWFzZSAvZiA2OHIgSDsgKy0yXAo0czFhQS9wV0lUN3JBUVo3RjlySGxBSHZZ Z3JlYXNlCnFKKys4Y0I3RkE3bzl2NDJ2aDNMeTRTUUU4dFFwelV3SjVReExnCi0t
U3NIUjREZmNHRFBSakpIRmR3cHprUXh6ditzWDROT1dqaHJQTUZICjhFR1dsVVVR LSA1QSthdHR0cVE4bXhiczZqUTNJdzkzdnZ2TEpESWwwQ3BWNnoyNmp4ZXdrCl1g
c1cyZG1UZGt3T1NuK2RnWktjbmcKLS0tIHRrZHFuUkk0R0lIaitYS1NPVWxBcjIw oNwCvlTrPx5b5l0OUymxMo1HMCV9fsQ9zyaoJoPFN1hv7l4hjjX5oVoj/IdTJbbe
aVR3d3ZkSU5kMGNGazQ3MksyMWMKiSNhBy5dvcGolFSkFHcWwzqnratvRNsbKaYp ZMs1yqa7YbR4+HG9GG8nPhU36hchSN2whoz8
Nt2DziTf6EyitXsktaBNi+0+SwtCQaRb9FMT3ff6zVTP9qweYAg2uqxLT3q1FoNg
Qa8=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyA1dUxB YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyB6dGZN
Q0VjQkdvamFldms1RXlXU21KdDJVVG9lcGVLMTFuWEpBWGFFSWpJCmVBRFF4a3pk VDF3ME9IWWJMQUVrbmlKeXhZbzE1THUwVWhBc2ZBQXdUUkpod0FzCkVncGlyTXVv
NDBBdGJkZGJNVTErWG9zYis3SnVDbW9PejZuMG12WURCMlEKLT4gWDI1NTE5IDJl bmxPN0F3ODdvUmw3Sit1cERNTXN6YWZlU2IvYUhoUThER3cKLT4gWDI1NTE5IGQ2
SEpoQ3pZRkpNVGVoSWp1ODVoZXpCUHZMOU5RMVo0V3hYOFduWUVoUlEKRG1zblgz RzdCWUw2N1BHWVhiOGhvNEdzQVlUanR3emdDV3FUTFRXUDB3RDdrMmsKZDkvWWgv
SStaUlVYbnQ3ZUx0RzlXMm1nSGRWUDRVVHlOYUF6UUJ5ZURaTQotPiBNJS1ncmVh MFZhbHZtVDVQSWVEQ2NxQUZrOXFMVmYxbEU0STFJTVBzZm16ZwotPiB+Ty1ncmVh
c2UgbylbCmx2RHpZRHg5dGlVUnZpT0taR3p1bXd4NTlZclh3M0hRL083akJFMlM0 c2UgMnpCPlV9ClY2TzJGcnBrbUJTU3lyNVlrNDdwYTd3Ci0tLSBHaU9ZVnZoUkx5
d0gzZVVBCi0tLSBDc3ZDcitIS0s3NVR6YmQyb0Q2bHoxNUpwU0R6TlBlVjhpdVAx QnE4UXhMdEg1elE1dDRoQWx4bDhBMHNwS1BlQkRaUm93CspT9YnuzfpKxC9y6SWJ
ZnoxMzVjCjB+mmwhKRmYzzjnyLDhQad/GGijeEN9L3zcpcdQtd9CdRyVrEH9qOpt JRyT8aFEJTjoDEqN2I/DBwRikSxKyspHi7grCwFaoofylqJzsP/In7Xlf91xbMXz
Nck07iGllfjDmELLIWr6obuol9/GplFiR8eSHteiOheFIYsk njjXbBQQP9PG3Z2c0OHk
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSBDT2Q5 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSBaUm1C
d1FJVnEyK04zNFBqc1FhRDhxOUFwRUdNcVdJRnI3MDdTeTA3cDJBCjQzUFQvbEFn WGJDSUNtTUYxbHVnWU44VkRnY3F6cTR0Tm1EUEdDYmltWHBrL1dvCnMzSEFqZTdS
YmJ2eHRkTjFVKzFpM3hNSit4Z0hzVXVJZzFnSEM1Z0dNRVkKLT4gWDI1NTE5IGxC K25tMkdkNEZ1dGhBMVhSUkR2M21EY0NtbnJjb0R5RVoyTDAKLT4gWDI1NTE5IHhv
clhCMFlVRnR2cCtOcUpBSVFvYUozcTc3dElOaXRJQ1ZMZUtNa0FWSGMKN2QrdEhq RlJOd3dCa3ZaeE5JS0ovV3ZiY0N5QThNSi9LaXQ5d1JIa1UyeVFJekUKd216RGw2
SWllUFdRdGY3QTdTdGhKaUFrR1ZXQjNyVkFJWlFlV2hUZkFyTQotPiAjMyItZ3Jl Szg3VG1PWUVweE9udFpta0gxdWx1NEU5ZmNwK3hMdHdZNlB6SQotPiB3dlxKIXxG
YXNlCnIrZnQwMk03b01NV0JyRUdhOEk0ODd3UG5kaFo1ZExDa2dqTUdjMEkzalJu LWdyZWFzZSBCLGsgWCU+UWwKWXVYQTBqVkRMKzNsU3JuWmlQdVpzdWJWcVdwVUM5
U2lQRlJaVFR1MjgrUi9YTkFIYWwKcWNZCi0tLSBnNThPdkJqSDc3MjBQQnI0azlX aHpmZFVPbWlidHJEYlN2M3NvUWNqVgotLS0gZHhwUlE3YXJSdHFkdFpkTnh0bC9a
NWRjazI2a1AxdnZscUpZOTAyM1liY1c4Csp02Ywz7dQmII/sZoDj1Q/XjcXuQYbc TytZcmtxd0pldUg2YUEzMFJ4QjR5dwqaUnjT3oaUunudOqNfh9twKyaRttf4sk9G
E2YMSbw/CPXUNoX8vKbL4Rsz1rErPQuBu8/r4Bg7o+Xo4Q5dvHOk+dBofz/hwyZY uiiKoEa314HbI1vgS4iCNX4vG+468SECiF9llZL9U1w+1MSF1y1BKy7XrDCsp7Xs
UdxWbRjh HiA2aA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSB0SHRv YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSBIdGI1
Rmwzc01TUitCZ3N2TVhvNDRTZStIUmFWdkpDS3kvR1ZnUCtWQW5FCk82cmsyb01k UVl5bVN5TDUvMTRuVFdpdG9lSkRjL2hOTHo3OVFxQ25IMDRlRGdJCm1oOEM5Rzlv
UzIwZWo3TENmUnVJMzllWHhaVkR5UFQwOTJneThrYVdORDQKLT4gWDI1NTE5IFFv cUljejB0aUNCS0x5Qjh1Y1JWQTNmWE9kcjF1ZktJVUxLSUEKLT4gWDI1NTE5IHBN
ZUE4OVF1ZHhyK0lubzc0ZHlWeG9aVmpjVmpEc2owZ3BXSmNpemVNU28KOHlrTi9T Vm4vbC9GcUdaYjNlWEg0UUVTVVVHWG5VTnVPRFBkUXk1dm8wWlF1QU0KZEU3dXFR
aXY1cUVEaElhWFIrbzBxNVZ0cUdRZk5haXo2RDlERTdBRktrQQotPiAhWiVKdjkt YU5GalhSakF3OEduSlhuYTN3SVdFU0NrWmJBZThvN1BGU21TUQotPiBdLVQtZ3Jl
Z3JlYXNlIFMwOSBHamBpfUkKZ0k5c213ZmI3aUpyY2d5WCttd0c4eVFMY3pHNGNV YXNlIElWPzRFcicgYVoldUFzClA3M2JUNFU4T2xSRmpyeStMQjZNa1I0bmxLSUdj
UFRvYUpMclVnNHpRCi0tLSByVVZMZ0tSanpqVExuVm9XTHV1dDg2MXdyQW84cGEv NGN6NkFvbmJEVWJXSEhCMjhkWjhyZkZPRyt5N2diZDI5SGwKczFsaEZXbDljVGQz
MURkOTBDVEFzMXNBCnI0ptvub3BR5DkNh9It+5zlpfVXUB8iV1eMprs/qSi/RNiV cnpMcHpPdTBPL0hwb0t3dE1XNURWckVEOFZmQwotLS0gbmQrNm9QQkpxTnYzTTFt
Pb4eW+ZwZbgHCESi1b6giSHnm5jvL6H27ODVhuG8I0WhTvpyuhL/zyzM bHU3WWVDai9BY3VSaTB3NDN5bk1IdUNiMWk4NAqyrvPkq+FP+fSs9mZMgxLTJICD
5l8Ii5h9fl2APzedygOLFGQLq0qW7pjBygfmQWgG4gqsO4iKIwEjUiv30/QCUhKL
CGZJpN8AfZAcow==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

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