Compare commits

...

70 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
125 changed files with 10586 additions and 1656 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

@@ -1,4 +1,4 @@
name: Installer ISO name: Installer
on: on:
push: push:
@@ -6,7 +6,7 @@ on:
jobs: jobs:
installer: installer:
name: Build installer ISO name: Build installer
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -24,20 +24,27 @@ jobs:
extra-substituters = https://nix-cache.nul.ie/main extra-substituters = https://nix-cache.nul.ie/main
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8= extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=
- name: Set up attic - name: Set up attic
id: setup
run: | run: |
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \ nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
login --set-default colony https://nix-cache.nul.ie "${{ secrets.NIX_CACHE_TOKEN }}" 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 - name: Build installer ISO
id: build
run: | run: |
nix build .#nixosConfigurations.installer.config.my.buildAs.iso nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.iso
short_rev="$(git rev-parse --short HEAD)" ln -s "$(readlink result)"/iso/jackos-installer.iso \
ln -s result/iso/nixos-installer-devplayer0.iso nixos-installer-devplayer0-$short_rev.iso jackos-installer-${{ steps.setup.outputs.short_rev }}.iso
echo "short_rev=$short_rev" >> "$GITHUB_OUTPUT" - name: Build installer netboot archive
- name: Create release for ISO 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 uses: https://gitea.com/actions/release-action@main
with: with:
title: Latest installer title: Latest installer
files: | files: |
nixos-installer-devplayer0-${{ steps.build.outputs.short_rev }}.iso 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";

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

154
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": 1698921442, "lastModified": 1703087360,
"narHash": "sha256-7KmvhQ7FuXlT/wG4zjTssap6maVqeAMBdtel+VjClSM=", "narHash": "sha256-0VUbWBW8VyiDRuimMuLsEO4elGuUw/nc2WDeuO1eN1M=",
"owner": "serokell", "owner": "serokell",
"repo": "deploy-rs", "repo": "deploy-rs",
"rev": "660180bbbeae7d60dad5a92b30858306945fd427", "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,11 +465,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1700814205, "lastModified": 1703367386,
"narHash": "sha256-lWqDPKHRbQfi+zNIivf031BUeyciVOtwCwTjyrhDB5g=", "narHash": "sha256-FMbm48UGrBfOWGt8+opuS+uLBLQlRfhiYXhHNcYMS5k=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "aeb2232d7a32530d3448318790534d196bf9427a", "rev": "d5824a76bc6bb93d1dce9ebbbcb09a9b6abcc224",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -494,11 +485,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1701433070, "lastModified": 1703754036,
"narHash": "sha256-Gf9JStfENaUQ7YWFz3V7x/srIwr4nlnVteqaAxtwpgM=", "narHash": "sha256-JpJdcj9Tg4lMuYikXDpajA8wOp+rHyn9RD2rKBEM4cQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "4a8545f5e737a6338814a4676dc8e18c7f43fc57", "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": 1701607327, "lastModified": 1703756459,
"narHash": "sha256-pHX6S1mrUSFVq6v0HiZuShfXLL01wiWvgivCabX2x+M=", "narHash": "sha256-ztEMyPQZh3Pb+LOoWl5lbIK2LenP59sOUBC86CDmLio=",
"owner": "devplayer0", "owner": "devplayer0",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c8af66cb9046a65cbab33563f804b7bad46173af", "rev": "e80160eb2ac3a7111d07cc43a15c16b9edca01ea",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -576,11 +567,11 @@
}, },
"nixpkgs-mine-stable": { "nixpkgs-mine-stable": {
"locked": { "locked": {
"lastModified": 1701607437, "lastModified": 1703756491,
"narHash": "sha256-ozMDOyJtxr/CznI6lrwtt9JkU32Y2cLr2B4vlW85Tfw=", "narHash": "sha256-9VL34e0gzomwqRnryRn23V2ImYcaZIQdp7CsWg5TmlE=",
"owner": "devplayer0", "owner": "devplayer0",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "67ef05e2dd98d1fd856028eba1bb4edb847f6c6e", "rev": "36611f5f7cfd401f51ad4ca76fd6ee85a714bb74",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -592,11 +583,11 @@
}, },
"nixpkgs-stable": { "nixpkgs-stable": {
"locked": { "locked": {
"lastModified": 1701389149, "lastModified": 1703467016,
"narHash": "sha256-rU1suTIEd5DGCaAXKW6yHoCfR1mnYjOXQFOaH7M23js=", "narHash": "sha256-/5A/dNPhbQx/Oa2d+Get174eNI3LERQ7u6WTWOlR1eQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5de0b32be6e85dc1a9404c75131316e4ffbc634c", "rev": "d02d818f22c777aa4e854efc3242ec451e5d462a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -607,11 +598,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1701253981, "lastModified": 1703438236,
"narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=", "narHash": "sha256-aqVBq1u09yFhL7bj1/xyUeJjzr92fXVvQSSEx6AdB1M=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58", "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

@@ -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; }) {
@@ -118,6 +118,7 @@
nixos/boxes/castle nixos/boxes/castle
nixos/boxes/home/stream.nix nixos/boxes/home/stream.nix
nixos/boxes/home/palace nixos/boxes/home/palace
nixos/boxes/britway
nixos/boxes/kelder nixos/boxes/kelder
# Homes # Homes
@@ -128,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

@@ -10,12 +10,14 @@ 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;
}; };
}; };
@@ -24,7 +26,7 @@ rec {
latest = pkgs: pkgs.linuxKernel.packages.linux_6_6; latest = pkgs: pkgs.linuxKernel.packages.linux_6_6;
}; };
nginx = { 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;
@@ -38,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 = {
@@ -104,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
@@ -125,8 +227,8 @@ rec {
"stream" "stream"
]; ];
routersPubV4 = [ routersPubV4 = [
"109.255.252.123" # placeholder "109.255.1.246"
"109.255.252.166" "109.255.252.63"
]; ];
prefixes = with lib.my.net.cidr; rec { prefixes = with lib.my.net.cidr; rec {
@@ -143,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;
}; };
@@ -174,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,12 @@ 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: { mkVLAN = name: vid: {
"25-${name}" = { "25-${name}" = {
netdevConfig = { netdevConfig = {
@@ -146,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;
@@ -157,7 +167,7 @@ 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
@@ -230,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

@@ -17,13 +17,9 @@ in
mask = 22; mask = 22;
gateway = null; gateway = null;
}; };
}; ipv6 = {
lo = { iid = "::3:1";
inherit domain; address = net.cidr.host (65536*3+1) prefixes.hi.v6;
ipv4 = {
address = net.cidr.host 40 prefixes.lo.v4;
mask = 21;
gateway = null;
}; };
}; };
}; };
@@ -108,7 +104,13 @@ in
blueman.enable = true; blueman.enable = true;
}; };
programs.virt-manager.enable = true; programs = {
virt-manager.enable = true;
wireshark = {
enable = true;
package = pkgs.wireshark-qt;
};
};
virtualisation.libvirtd.enable = true; virtualisation.libvirtd.enable = true;
networking = { networking = {
@@ -147,7 +149,6 @@ in
wait-online.enable = false; wait-online.enable = false;
netdevs = mkMerge [ netdevs = mkMerge [
(mkVLAN "lan-hi" vlans.hi) (mkVLAN "lan-hi" vlans.hi)
(mkVLAN "lan-lo" vlans.lo)
]; ];
links = { links = {
"10-et2.5g" = { "10-et2.5g" = {
@@ -169,28 +170,23 @@ in
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" = { "50-et100g" = {
matchConfig.Name = "et100g"; matchConfig.Name = "et100g";
vlan = [ "lan-hi" "lan-lo" ]; vlan = [ "lan-hi" ];
networkConfig.IPv6AcceptRA = false; networkConfig.IPv6AcceptRA = false;
}; };
"60-lan-hi" = mkMerge [ "60-lan-hi" = mkMerge [
(networkdAssignment "lan-hi" assignments.hi) (networkdAssignment "lan-hi" assignments.hi)
{ {
DHCP = "yes";
matchConfig.Name = "lan-hi"; matchConfig.Name = "lan-hi";
linkConfig.MTUBytes = "9000"; linkConfig.MTUBytes = "9000";
} }
]; ];
"60-lan-lo" = mkMerge [
(networkdAssignment "lan-lo" assignments.lo)
{
matchConfig.Name = "lan-lo";
linkConfig.MTUBytes = "1500";
}
];
}; };
}; };
}; };
@@ -251,6 +247,7 @@ in
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 ];
@@ -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;
@@ -351,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 {

View File

@@ -120,7 +120,7 @@
cpus = 12; cpus = 12;
threads = 2; threads = 2;
}; };
memory = 49152; 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) [
@@ -160,7 +160,7 @@
cpus = 12; cpus = 12;
threads = 2; threads = 2;
}; };
memory = 32768; memory = 40960;
networks.vms.mac = "52:54:00:75:78:a8"; networks.vms.mac = "52:54:00:75:78:a8";
cleanShutdown.timeout = 120; cleanShutdown.timeout = 120;
drives = [ drives = [
@@ -181,7 +181,7 @@
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";

View File

@@ -27,7 +27,9 @@ in
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};
@@ -42,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";
@@ -188,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 {
@@ -204,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

@@ -2,7 +2,7 @@
let let
inherit (builtins) elemAt; inherit (builtins) elemAt;
inherit (lib.my) net mkVLAN; 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 ];
@@ -246,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 +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: [
{ {
@@ -321,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" = {
@@ -355,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
@@ -408,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 {
@@ -430,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
@@ -443,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 ${aa.git.internal.ipv4.address} tcp dport { http, https } dnat to ${aa.middleman.internal.ipv4.address}
ip6 daddr ${aa.git.internal.ipv6.address} tcp dport { http, https } dnat to ${aa.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

@@ -1,8 +1,11 @@
{ lib, ... }: { lib, ... }:
let let
inherit (builtins) mapAttrs;
inherit (lib) mkMerge mkDefault;
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
in in
{ {
nixos.systems.git = { nixos.systems.git = {
@@ -72,9 +75,109 @@ in
}; };
}; };
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 = { services = {
fstrim = lib.my.c.colony.fstrimConfig; fstrim = lib.my.c.colony.fstrimConfig;
# Hacks for Jsch (Minecraft FastBack) to work
openssh = {
hostKeys = [
{
bits = 4096;
path = "/etc/ssh/ssh_host_rsa_key";
type = "rsa";
}
{
path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
{
type = "ecdsa-sha2-nistp256";
path = "/etc/ssh/ssh_host_ecdsa_key";
}
];
settings = {
Macs = [
"hmac-sha2-512-etm@openssh.com"
"hmac-sha2-256-etm@openssh.com"
"umac-128-etm@openssh.com"
"hmac-sha2-256"
];
};
};
netdata.enable = true; netdata.enable = true;
nginx = {
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 = { virtualisation = {
@@ -104,11 +207,24 @@ in
}; };
my = { my = {
secrets.key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP+KINpHLMduBuW96JzfSRDLUzkI+XaCBghu5/wHiW5R"; 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; server.enable = true;
firewall = { firewall = {
tcp.allowed = [ 19999 ]; tcp.allowed = [ 19999 "http" "https" ];
extraRules = '' extraRules = ''
table inet filter { table inet filter {
chain forward { chain forward {

View File

@@ -26,18 +26,6 @@ in
systemd = { systemd = {
services = { services = {
# TODO: Figure out a way to do this properly... redirecting localhost is awkward...
local-http-forward = {
description = "Forward local HTTP connections";
serviceConfig.ExecStart = "${pkgs.socat}/bin/socat tcp-listen:80,fork tcp:${allAssignments.middleman.internal.ipv4.address}:80";
wantedBy = [ "multi-user.target" ];
};
local-https-forward = {
description = "Forward local HTTPS connections";
serviceConfig.ExecStart = "${pkgs.socat}/bin/socat tcp-listen:443,fork tcp:${allAssignments.middleman.internal.ipv4.address}:443";
wantedBy = [ "multi-user.target" ];
};
gitea = mkMerge [ gitea = mkMerge [
(lib.my.systemdAwaitPostgres pkgs.postgresql "colony-psql") (lib.my.systemdAwaitPostgres pkgs.postgresql "colony-psql")
{ {
@@ -141,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

@@ -347,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://git-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;
}; };
}; };
@@ -387,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

@@ -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 ];
@@ -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,6 +65,7 @@ in
"${modulesPath}/profiles/qemu-guest.nix" "${modulesPath}/profiles/qemu-guest.nix"
./valheim.nix ./valheim.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

@@ -2,7 +2,7 @@
let let
inherit (lib.my) net mkVLAN; inherit (lib.my) net mkVLAN;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) domain vlans prefixes vips; inherit (lib.my.c.home) domain vlans prefixes vips hiMTU;
in in
{ {
imports = [ ./vms ]; imports = [ ./vms ];
@@ -15,15 +15,21 @@ in
assignments = { assignments = {
hi = { hi = {
inherit domain; inherit domain;
mtu = hiMTU;
ipv4 = { ipv4 = {
address = net.cidr.host 22 prefixes.hi.v4; address = net.cidr.host 22 prefixes.hi.v4;
mask = 22; mask = 22;
gateway = vips.hi.v4; gateway = vips.hi.v4;
}; };
ipv6 = {
iid = "::2:1";
address = net.cidr.host (65536*2+1) prefixes.hi.v6;
};
}; };
core = { core = {
inherit domain; inherit domain;
name = "palace-core"; name = "palace-core";
mtu = 1500;
ipv4 = { ipv4 = {
address = net.cidr.host 20 prefixes.core.v4; address = net.cidr.host 20 prefixes.core.v4;
gateway = null; gateway = null;
@@ -35,6 +41,7 @@ in
let let
inherit (lib) mkForce mkMerge; inherit (lib) mkForce mkMerge;
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
inherit (lib.my.c) networkd;
in in
{ {
boot = { boot = {
@@ -103,7 +110,7 @@ in
hwloc hwloc
]; ];
networking.domain = "h.${pubDomain}"; networking = { inherit domain; };
systemd = { systemd = {
tmpfiles.rules = [ tmpfiles.rules = [
@@ -113,11 +120,14 @@ in
network = { network = {
links = { links = {
"10-et1g0" = { "10-et1g0" = {
matchConfig.MACAddress = "e0:d5:5e:68:0c:6e"; matchConfig = {
PermanentMACAddress = "e0:d5:5e:68:0c:6e";
Driver = "igb";
};
linkConfig.Name = "et1g0"; linkConfig.Name = "et1g0";
}; };
"10-lan-core" = { "10-lan-core" = {
matchConfig.MACAddress = "e0:d5:5e:68:0c:70"; matchConfig.PermanentMACAddress = "e0:d5:5e:68:0c:70";
linkConfig.Name = "lan-core"; linkConfig.Name = "lan-core";
}; };
"10-et100g" = { "10-et100g" = {
@@ -127,13 +137,20 @@ in
}; };
linkConfig = { linkConfig = {
Name = "et100g"; Name = "et100g";
MTUBytes = "9000"; MTUBytes = toString hiMTU;
}; };
}; };
}; };
netdevs = mkMerge [ netdevs = mkMerge [
(mkVLAN "lan-hi" vlans.hi) (mkVLAN "lan-hi" vlans.hi)
(mkVLAN "lan-lo-phy" vlans.lo)
{
"25-lan-lo".netdevConfig = {
Name = "lan-lo";
Kind = "bridge";
};
}
]; ];
networks = { networks = {
@@ -141,6 +158,7 @@ in
(networkdAssignment "lan-core" assignments.core) (networkdAssignment "lan-core" assignments.core)
{ {
matchConfig.Name = "lan-core"; matchConfig.Name = "lan-core";
vlan = [ "lan-lo-phy" ];
networkConfig.IPv6AcceptRA = mkForce false; networkConfig.IPv6AcceptRA = mkForce false;
} }
]; ];
@@ -148,7 +166,8 @@ in
"50-et100g" = { "50-et100g" = {
matchConfig.Name = "et100g"; matchConfig.Name = "et100g";
vlan = [ "lan-hi" ]; vlan = [ "lan-hi" ];
networkConfig.IPv6AcceptRA = false; networkConfig = networkd.noL3;
linkConfig.RequiredForOnline = "no";
extraConfig = '' extraConfig = ''
# cellar # cellar
[SR-IOV] [SR-IOV]
@@ -156,19 +175,34 @@ in
VLANId=${toString vlans.hi} VLANId=${toString vlans.hi}
LinkState=yes LinkState=yes
MACAddress=52:54:00:cc:3e:70 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" = mkMerge [ "60-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
(networkdAssignment "lan-hi" assignments.hi)
{ "50-lan-lo-phy" = {
matchConfig.Name = "lan-hi"; matchConfig.Name = "lan-lo-phy";
linkConfig.MTUBytes = "9000"; networkConfig = {
networkConfig.DNS = [ Bridge = "lan-lo";
(allAssignments.stream.hi.ipv4.address) } // networkd.noL3;
# (allAssignments.river.hi.ipv4.address) };
]; "60-lan-lo" = {
} matchConfig.Name = "lan-lo";
]; linkConfig.RequiredForOnline = "no";
networkConfig = networkd.noL3;
};
}; };
}; };
}; };

View File

@@ -2,7 +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.home) domain prefixes vips; inherit (lib.my.c.home) domain prefixes vips hiMTU;
in in
{ {
nixos.systems.cellar = { nixos.systems.cellar = {
@@ -12,11 +12,16 @@ in
assignments = { assignments = {
hi = { hi = {
inherit domain; inherit domain;
mtu = hiMTU;
ipv4 = { ipv4 = {
address = net.cidr.host 80 prefixes.hi.v4; address = net.cidr.host 80 prefixes.hi.v4;
mask = 22; mask = 22;
gateway = vips.hi.v4; gateway = vips.hi.v4;
}; };
ipv6 = {
iid = "::4:1";
address = net.cidr.host (65536*4+1) prefixes.hi.v6;
};
}; };
}; };
@@ -24,21 +29,17 @@ in
let let
inherit (lib) mkMerge; inherit (lib) mkMerge;
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
spdk = pkgs.spdk.overrideAttrs (o: {
configureFlags = o.configureFlags ++ [ "--with-rdma" ];
});
in in
{ {
imports = [ imports = [
"${modulesPath}/profiles/qemu-guest.nix" "${modulesPath}/profiles/qemu-guest.nix"
./spdk.nix
]; ];
config = mkMerge [ config = mkMerge [
{ {
boot = { boot = {
kernelParams = [ "console=ttyS0,115200n8" ]; kernelParams = [ "console=ttyS0,115200n8" "intel_iommu=on" ];
blacklistedKernelModules = [ "nvme" ];
}; };
fileSystems = { fileSystems = {
@@ -57,58 +58,27 @@ in
}; };
}; };
networking = { inherit domain; };
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
pciutils pciutils
spdk partclone
(pkgs.writeShellScriptBin "spdk-rpc" ''
exec ${pkgs.python3}/bin/python3 ${spdk.src}/scripts/rpc.py "$@"
'')
]; ];
services = { services = {
netdata.enable = true; netdata.enable = true;
}; };
systemd.services = {
spdk-nvmf = {
description = "SPDK NVMe-oF target";
path = with pkgs; [
bash
python3
kmod
gawk
util-linux
];
after = [ "systemd-networkd-wait-online@lan-hi.service" ];
preStart = ''
${spdk.src}/scripts/setup.sh
'';
serviceConfig.ExecStart = "${spdk}/bin/spdk_tgt -c ${./spdk_nvmf.json}";
wantedBy = [ "multi-user.target" ];
};
};
systemd.network = { systemd.network = {
links = { links = {
"10-lan-hi" = { "10-lan-hi" = {
matchConfig.PermanentMACAddress = "52:54:00:cc:3e:70"; matchConfig.PermanentMACAddress = "52:54:00:cc:3e:70";
linkConfig = { linkConfig.Name = "lan-hi";
Name = "lan-hi";
MTUBytes = "9000";
};
}; };
}; };
networks = { networks = {
"80-vms" = mkMerge [ "80-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
(networkdAssignment "lan-hi" assignments.hi)
{
networkConfig.DNS = [
(allAssignments.stream.hi.ipv4.address)
# (allAssignments.river.hi.ipv4.address)
];
}
];
}; };
}; };

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

@@ -1,337 +0,0 @@
{
"subsystems": [
{
"subsystem": "scheduler",
"config": [
{
"method": "framework_set_scheduler",
"params": {
"name": "static"
}
}
]
},
{
"subsystem": "vmd",
"config": []
},
{
"subsystem": "sock",
"config": [
{
"method": "sock_impl_set_options",
"params": {
"impl_name": "posix",
"recv_buf_size": 2097152,
"send_buf_size": 2097152,
"enable_recv_pipe": true,
"enable_quickack": false,
"enable_placement_id": 0,
"enable_zerocopy_send_server": true,
"enable_zerocopy_send_client": false,
"zerocopy_threshold": 0,
"tls_version": 0,
"enable_ktls": false
}
},
{
"method": "sock_impl_set_options",
"params": {
"impl_name": "ssl",
"recv_buf_size": 4096,
"send_buf_size": 4096,
"enable_recv_pipe": true,
"enable_quickack": false,
"enable_placement_id": 0,
"enable_zerocopy_send_server": true,
"enable_zerocopy_send_client": false,
"zerocopy_threshold": 0,
"tls_version": 0,
"enable_ktls": false
}
}
]
},
{
"subsystem": "iobuf",
"config": [
{
"method": "iobuf_set_options",
"params": {
"small_pool_count": 8192,
"large_pool_count": 1024,
"small_bufsize": 8192,
"large_bufsize": 135168
}
}
]
},
{
"subsystem": "accel",
"config": [
{
"method": "accel_set_options",
"params": {
"small_cache_size": 128,
"large_cache_size": 16,
"task_count": 2048,
"sequence_count": 2048,
"buf_count": 2048
}
}
]
},
{
"subsystem": "bdev",
"config": [
{
"method": "bdev_set_options",
"params": {
"bdev_io_pool_size": 65535,
"bdev_io_cache_size": 256,
"bdev_auto_examine": true
}
},
{
"method": "bdev_nvme_set_options",
"params": {
"action_on_timeout": "none",
"timeout_us": 0,
"timeout_admin_us": 0,
"keep_alive_timeout_ms": 10000,
"transport_retry_count": 4,
"arbitration_burst": 0,
"low_priority_weight": 0,
"medium_priority_weight": 0,
"high_priority_weight": 0,
"nvme_adminq_poll_period_us": 10000,
"nvme_ioq_poll_period_us": 0,
"io_queue_requests": 512,
"delay_cmd_submit": true,
"bdev_retry_count": 3,
"transport_ack_timeout": 0,
"ctrlr_loss_timeout_sec": 0,
"reconnect_delay_sec": 0,
"fast_io_fail_timeout_sec": 0,
"generate_uuids": false,
"transport_tos": 0,
"io_path_stat": false,
"allow_accel_sequence": false
}
},
{
"method": "bdev_nvme_attach_controller",
"params": {
"name": "NVMe0",
"trtype": "PCIe",
"traddr": "02:00.0",
"prchk_reftag": false,
"prchk_guard": false,
"ctrlr_loss_timeout_sec": 0,
"reconnect_delay_sec": 0,
"fast_io_fail_timeout_sec": 0,
"hostnqn": "nqn.2014-08.org.nvmexpress:uuid:2b16606f-b82c-49f8-9b20-a589dac8b775",
"hdgst": false,
"ddgst": false
}
},
{
"method": "bdev_nvme_attach_controller",
"params": {
"name": "NVMe1",
"trtype": "PCIe",
"traddr": "03.00.0",
"prchk_reftag": false,
"prchk_guard": false,
"ctrlr_loss_timeout_sec": 0,
"reconnect_delay_sec": 0,
"fast_io_fail_timeout_sec": 0,
"hostnqn": "nqn.2014-08.org.nvmexpress:uuid:2b16606f-b82c-49f8-9b20-a589dac8b775",
"hdgst": false,
"ddgst": false
}
},
{
"method": "bdev_nvme_attach_controller",
"params": {
"name": "NVMe2",
"trtype": "PCIe",
"traddr": "04.00.0",
"prchk_reftag": false,
"prchk_guard": false,
"ctrlr_loss_timeout_sec": 0,
"reconnect_delay_sec": 0,
"fast_io_fail_timeout_sec": 0,
"hostnqn": "nqn.2014-08.org.nvmexpress:uuid:2b16606f-b82c-49f8-9b20-a589dac8b775",
"hdgst": false,
"ddgst": false
}
},
{
"method": "bdev_nvme_set_hotplug",
"params": {
"period_us": 100000,
"enable": false
}
},
{
"method": "bdev_raid_create",
"params": {
"name": "Raid0",
"strip_size_kb": 64,
"raid_level": "raid0",
"base_bdevs": [
"NVMe0n1",
"NVMe1n1",
"NVMe2n1"
]
}
},
{
"method": "bdev_wait_for_examine"
}
]
},
{
"subsystem": "nvmf",
"config": [
{
"method": "nvmf_set_config",
"params": {
"discovery_filter": "match_any",
"admin_cmd_passthru": {
"identify_ctrlr": false
}
}
},
{
"method": "nvmf_set_max_subsystems",
"params": {
"max_subsystems": 1024
}
},
{
"method": "nvmf_set_crdt",
"params": {
"crdt1": 0,
"crdt2": 0,
"crdt3": 0
}
},
{
"method": "nvmf_create_transport",
"params": {
"trtype": "RDMA",
"max_queue_depth": 128,
"max_io_qpairs_per_ctrlr": 127,
"in_capsule_data_size": 4096,
"max_io_size": 131072,
"io_unit_size": 8192,
"max_aq_depth": 128,
"num_shared_buffers": 4095,
"buf_cache_size": 4294967295,
"dif_insert_or_strip": false,
"zcopy": false,
"max_srq_depth": 4096,
"no_srq": false,
"acceptor_backlog": 100,
"no_wr_batching": false,
"abort_timeout_sec": 1
}
},
{
"method": "nvmf_create_subsystem",
"params": {
"nqn": "nqn.2016-06.io.spdk:cellar",
"allow_any_host": false,
"serial_number": "SPDK00000000000001",
"model_number": "SPDK bdev Controller",
"max_namespaces": 32,
"min_cntlid": 1,
"max_cntlid": 65519,
"ana_reporting": false
}
},
{
"method": "nvmf_subsystem_add_host",
"params": {
"nqn": "nqn.2016-06.io.spdk:cellar",
"host": "nqn.2014-08.org.nvmexpress:uuid:de712327-7fc8-6e54-15af-c87f546e170f"
}
},
{
"method": "nvmf_subsystem_add_ns",
"params": {
"nqn": "nqn.2016-06.io.spdk:cellar",
"namespace": {
"nsid": 1,
"bdev_name": "Raid0",
"nguid": "FA9D43CFD8D54885BB73F038BDC31B25",
"uuid": "fa9d43cf-d8d5-4885-bb73-f038bdc31b25"
}
}
},
{
"method": "nvmf_subsystem_add_listener",
"params": {
"nqn": "nqn.2016-06.io.spdk:cellar",
"listen_address": {
"trtype": "RDMA",
"adrfam": "IPv4",
"traddr": "192.168.68.80",
"trsvcid": "4420"
},
"secure_channel": false
}
}
]
},
{
"subsystem": "nbd",
"config": []
},
{
"subsystem": "vhost_blk",
"config": []
},
{
"subsystem": "scsi",
"config": null
},
{
"subsystem": "iscsi",
"config": [
{
"method": "iscsi_set_options",
"params": {
"node_base": "iqn.2016-06.io.spdk",
"max_sessions": 128,
"max_connections_per_session": 2,
"max_queue_depth": 64,
"default_time2wait": 2,
"default_time2retain": 20,
"first_burst_length": 8192,
"immediate_data": true,
"allow_duplicated_isid": false,
"error_recovery_level": 0,
"nop_timeout": 60,
"nop_in_interval": 30,
"disable_chap": false,
"require_chap": false,
"mutual_chap": false,
"chap_group": 0,
"max_large_datain_per_connection": 64,
"max_r2t_per_connection": 4,
"pdu_pool_size": 36864,
"immediate_data_pool_size": 16384,
"data_out_pool_size": 2048
}
}
]
},
{
"subsystem": "vhost_scsi",
"config": []
}
]
}

View File

@@ -1,12 +1,14 @@
{ {
imports = [ imports = [
./cellar ./cellar
./river.nix
]; ];
nixos.systems.palace.configuration = { lib, pkgs, config, systems, ... }: nixos.systems.palace.configuration = { lib, pkgs, config, systems, allAssignments, ... }:
let let
inherit (lib) mkMerge; inherit (lib) mkMerge;
inherit (lib.my) vm; inherit (lib.my) vm;
inherit (lib.my.c) networkd;
installerDisk = { installerDisk = {
name = "installer"; name = "installer";
@@ -23,7 +25,49 @@
}; };
in in
{ {
systemd.services = { 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" = { "vm@cellar" = {
serviceConfig = { serviceConfig = {
CPUAffinity = "numa"; CPUAffinity = "numa";
@@ -31,6 +75,19 @@
NUMAMask = "1"; NUMAMask = "1";
}; };
}; };
"vm@river" =
let
vtapUnit = "sys-subsystem-net-devices-vm\\x2det1g0.device";
in
mkMerge [
awaitCellar
{
requires = [ vtapUnit ];
after = [ vtapUnit ];
}
];
"vm@sfh" = awaitCellar;
}; };
my = { my = {
@@ -43,7 +100,8 @@
cpus = 8; cpus = 8;
threads = 2; threads = 2;
}; };
memory = 32768; memory = 16384;
cleanShutdown.timeout = 120;
drives = [ drives = [
(mkMerge [ (vm.disk "cellar" "esp") { frontendOpts.bootindex = 0; } ]) (mkMerge [ (vm.disk "cellar" "esp") { frontendOpts.bootindex = 0; } ])
(vm.disk "cellar" "nix") (vm.disk "cellar" "nix")
@@ -67,6 +125,64 @@
hostBDF = "43:00.0"; 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

@@ -3,9 +3,10 @@ let
inherit (builtins) elemAt; inherit (builtins) elemAt;
inherit (lib.my) net mkVLAN; inherit (lib.my) net mkVLAN;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) domain vlans prefixes routers routersPubV4; 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;
@@ -61,20 +66,50 @@ in
}; };
ipv6 = { ipv6 = {
address = net.cidr.host ((1*65536*65536*65536) + index + 1) prefixes.as211024.v6; address = net.cidr.host ((1*65536*65536*65536) + index + 1) prefixes.as211024.v6;
gateway = net.cidr.host 1 prefixes.as211024.v6; gateway = net.cidr.host ((2*65536*65536*65536) + 1) prefixes.as211024.v6;
}; };
}; };
}; };
extraAssignments = {
router-hi.hi = {
name = "router-hi";
inherit domain;
ipv4 = {
address = vips.hi.v4;
mask = 22;
};
ipv6.address = vips.hi.v6;
};
router-lo.lo = {
name = "router-lo";
inherit domain;
ipv4 = {
address = vips.lo.v4;
mask = 21;
};
ipv6.address = vips.lo.v6;
};
router-ut.untrusted = {
name = "router-ut";
inherit domain;
ipv4.address = vips.untrusted.v4;
ipv6.address = vips.untrusted.v6;
};
};
configuration = { lib, pkgs, config, assignments, allAssignments, ... }: configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
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) [
./keepalived.nix ./keepalived.nix
./dns.nix ./dns.nix
./radvd.nix
./kea.nix
]; ];
config = { config = {
@@ -106,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
''; '';
}; };
@@ -138,14 +173,10 @@ in
netdevs = mkMerge [ netdevs = 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";
};
"30-lan-core".netdevConfig = { "30-lan-core".netdevConfig = {
Name = "lan-core"; Name = "lan-core";
Kind = "macvlan"; Kind = "macvlan";
@@ -156,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
@@ -169,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
@@ -228,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)
{ {
@@ -241,12 +229,17 @@ 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
'';
} }
]; ];
@@ -254,12 +247,7 @@ in
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)
@@ -274,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;
}
];
};
}
]; ];
}; };
@@ -303,23 +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
aa = allAssignments;
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
} }
@@ -338,8 +359,10 @@ 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 { }
} }

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,13 +165,27 @@ 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} palace-kvm IN A ${net.cidr.host 21 prefixes.lo.v4}

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

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

@@ -57,7 +57,7 @@
}; };
}; };
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)
@@ -65,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;
}; };

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

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

@@ -107,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 = {
@@ -166,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.";

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

@@ -43,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
'';
})
]; ];
}; };
@@ -82,6 +92,7 @@ in
}; };
isoImage = { isoImage = {
isoBaseName = dummyOption; isoBaseName = dummyOption;
volumeID = dummyOption;
edition = dummyOption; edition = dummyOption;
appendToMenuLabel = dummyOption; appendToMenuLabel = dummyOption;
}; };
@@ -99,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; {
@@ -121,11 +121,15 @@ in
services.lvm.enable = 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
] ]
@@ -138,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 = {
@@ -151,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

@@ -209,12 +209,16 @@ 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
if [ -e "${containerSystem}"/prepare-root ]; then ${if system == sysProfile then ''
if [ -e "${sysProfile}"/prepare-root ]; then
initSource="${containerSystem}"/prepare-root initSource="${containerSystem}"/prepare-root
else else
initSource="${containerSystem}"/init initSource="${containerSystem}"/init
fi fi
ln -sf "$initSource" "$root"/sbin/init 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";
}; };
}; };
@@ -137,6 +140,9 @@ in
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}
@@ -144,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";
} }
]; ];
@@ -171,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

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

@@ -466,6 +466,32 @@ in
(mkIf config.virtualisation.libvirtd.enable { (mkIf config.virtualisation.libvirtd.enable {
my.tmproot.persistence.config.directories = [ "/var/lib/libvirt" ]; 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

@@ -37,7 +37,9 @@ in
extraGroups = extraGroups =
[ "wheel" "kvm" "dialout" ] ++ [ "wheel" "kvm" "dialout" ] ++
(optional config.networking.networkmanager.enable "networkmanager") ++ (optional config.networking.networkmanager.enable "networkmanager") ++
(optional config.virtualisation.libvirtd.enable "libvirtd"); (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;

View File

@@ -116,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.";
}; };
}; };
@@ -126,8 +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) allHostDevs; anyVfioDevs = any (d: d.bindVFIO);
vfioHostDevs = filter (d: d.bindVFIO) allHostDevs; vfioHostDevs = filter (d: d.bindVFIO);
mkQemuScript = n: i: mkQemuScript = n: i:
let let
@@ -204,7 +204,7 @@ in
services.udev = { services.udev = {
packages = packages =
optionals optionals
anyVfioDevs (anyVfioDevs allHostDevs)
[ [
pkgs.vfio-pci-bind pkgs.vfio-pci-bind
(pkgs.writeTextDir (pkgs.writeTextDir
@@ -212,7 +212,7 @@ in
(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"'')
vfioHostDevs)) (vfioHostDevs allHostDevs)))
]; ];
}; };
@@ -261,12 +261,15 @@ 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 '' ${optionalString (anyVfioDevs hostDevs) ''
iommu_group() { iommu_group() {
g=/sys/bus/pci/devices/0000:$1/iommu_group g=/sys/bus/pci/devices/0000:$1/iommu_group
until [ -e $g ]; do until [ -e $g ]; do
@@ -280,7 +283,7 @@ in
done done
} }
${concatMapStringsSep "\n" (d: "wait_vfio ${d.hostBDF}") vfioHostDevs} ${concatMapStringsSep "\n" (d: "wait_vfio ${d.hostBDF}") (vfioHostDevs hostDevs) }
''} ''}
''; '';
script = mkQemuScript n i; script = mkQemuScript n i;

View File

@@ -6,4 +6,6 @@ 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 { }; 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 ..
'';
}

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,12 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBlL0tm YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USB2WFY2
THFkTDVWLzFjMWJFdFZOdWt5UjFIMWtVYjI4SStCTmIxbGZjL21FClRZT3NtMU1D MzFnVFYvb0wzc3grRDBrUi9teW1tNmx5dVBIRVc5OUdlcjdvTDJFCmpJL2tlYXND
OEthcm8rUUs5VERVWllxZGhDd0NxM3NGbXNHRHBFbGs3ancKLT4gWDI1NTE5IE82 bVJKbzd2S3JkSFVMdG40MS84V0lPTVJSNHVBQlNXSkN1eTAKLT4gWDI1NTE5IFBM
VzJ0MkRnZUNSZm42Q2VqQjBBWU1JTWRkOVdtTXpwcStsWkpsaGRxQkEKanNBZGUz ZGo3L2VlNUZqOEE1UHBuMHdqc1pyYlQ3R29ucE9pajU0bHMzamlXRDAKcG1Qdmw2
dUM4UFRhRXFBVDYzU09Zc3A5ck5reEJid1BHTWNrcjFYUFNEMAotPiAqWk4tZ3Jl cUlncDFWNXBOWnpIeDNZSFA3d1E1bjNaVVpKU3lMRjRaSHNtMAotPiB2XUh8eF4t
YXNlIDdiIGdjOko5PnU1IDY7CmZaNXN1VnZOY2VVemJhRnFyQ1ZvVVJ1WGFEOGtY Z3JlYXNlICUrO0cxIH4gaTRoIF81SEpTN0Q5CnpWdEZpb1hZa2t5YkE5RnJFMHVZ
K3VZc3crZXQzdExBMU0wZWhFMzFwUGF0eS9PWW16N1VIYVgKL1MwQVQveGw0S3Z4 WkhkQ2o0eWtyOE9ueDJkeGd2aUhmLzRUUGs1aUc1NURIOTYxczZhOEVmT0EKd2xk
Ci0tLSBveS9pSFEzWFY4d3RpWnU5NXhkWGE4bEtKTjREVGk0SElndXlHWVZzejVF TXFHN051d25PQmtNUVZkVEFGUVliZjdmZDF3RWFkaEhNTzd3ZVd5N3dlNzQKLS0t
Cp2+3mlaFEETvf/S1EsHszEfvvneDkHpdtyv4V6slRsAI6ieuVWXSo615ge5F/Cm IGZDR1Mxd24zOW05bitzQnN5WWVOOGtCNEc5aXIraEF4eXFUQm5CZUdCV2MKd44C
wzERcvjx4fMTSkgxmpv68o1HxiZmiCitF8Uq7qzVe+MII0keSiK1aDSnteE= /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+IHNzaC1lZDI1NTE5IFpCM2U2USBmNVFZ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBuQjBY
ZTlIM1JjbE04d2VSMm5jTGlRSStHU2JMeVRFeXhwVGtBby9NK3hzCk9ZRUh3Tk1L K1ZTUzR0SmFoYUZLdzUzNUt2M1B4RFdPZzFkcW8rMWJLUjVHOUN3Cjd3ZHdMSzdp
R2FhSVdtbzIzZDMreENJa0ExSk9LVDIxTDY1dnZEUGF0NWcKLT4gWDI1NTE5IHBC OWswditVWUhYNjZCdjV6M1d6U3pNak5hUlZjTHNqSkZPSk0KLT4gWDI1NTE5IGNG
R2tVU29BY1ZYLzgyVnpVSC9UR1NXWnFGd0N1RXJwcDdpUmFVKzdmbFEKbmVOT1My ZytHazJQWTExMjNkZWV2aDUwcnZjUGcwcXRkZWl0M0F2R2JpUVlJekUKWTFlbkEz
dlBJK1pSQmtvd1BMcXZoOHY2RGhteHgrTjVFVjNaRmU3eHB3RQotPiBLJEBWLWdy YlN6dkgrMmFFcmlZMjQ0b2oySjlEMC85cUF5QlA4NGF3blBKRQotPiA8M1hbWS1n
ZWFzZSBHVEEKNGpuTlp0V3lFakZaMVNzckNzd0hNZDdtNWE0R0V3Ci0tLSB0VEwv cmVhc2UgOmlZJFYmJgo3UExzUHhSVi9lbVRjNW4xTnVaUTVGSGVDNzRUNlJ2TnVu
TmYzUm5ZRmJ2eGdrM1RhWkJIWXJkMjRsNWxhRCtNUzMvbE92N3NJCmFkBVJWwyL9 U0wrWU5kd3dyR2phcUNVUHJCYjdzeWhjNVFOV011CmZMOVN0WDk5eUg3ajhwbStQ
zbTtBadwL+AaGqlbL/n6s9XTlRsyjaYyJNrMeRkUGBVjXUtKJY58Z7MNy2p9QM+E aHExT0EwblpreTBXVHh6Zk1HRkhzcmFQNnoyVUlaeStMUkgKLS0tIG1aY3Zadko3
EXoCtmp7htBmLaHB9vsRyno5IQ1cnOTBMzeOlFdP3IFtPCX0W6vZmbcjwqOAj26v V3pDMGswSDZCeit4SVhSRCsrZVRVZVkvQkNsQWN1MXFMUGcKcK74YULWqWw9VvkI
x4Nil/AEHPz6X5ud0+iJClZWWjVeIzQrl/cIYGi65LNNl1aeW/rHj+SGHV/7iQ9j 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
HSZ7wUTthaodVC+q9iAX We/Pe62LyhV/MbLRA0nmU7Sf4IAnpHfa4kLtlYeB3xiqKd0McM//qzjuk5NoIgE4
nL0T8YXdGt8K01w+nt+j5bo5gFoRz5+1/ZZ9BgN2DOo4SClYnJWQ/x80X061yJ33
0SGv4eAC3vFi6xE=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,15 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USBqeVVR YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpCM2U2USA3MEFh
bjlGYjl5SjVKYUhzNy8xZUZKcjJyYjlXSHl6TnppUGwvSk9POENvCitzc3g1Wmt0 YURySHY5RjVQa1c0QmMvTkZtZElNd1BIeDFpR1U1YXZ5dndnclUwClA2M0ZYMk90
SGdPakcxd09NbExMTXVjRW5YMnd6V3hUaHBXdHR2SmxiSFUKLT4gWDI1NTE5IDdI RmlCZHVISkxZQTZXeEhZVUROSVRGRVg4TmZKZytSL05FcTgKLT4gWDI1NTE5IFY2
dDduZUVtK00zQzg1cmRxMWgwcmNiQVdqN2NUeUJMMGpFa1I2MnY2MkUKWmY1V3Aw V2k3S0VQdGlQZ0hkU2ZoSWdOUG9rQ0p5Ukg3ZHU3MXVvNHlWYVN5VVUKcWtoWDJh
cTlRVkMzNU1hYkRaL1A0aE5qV0lJa2lPQ0o0TGtuaUJRMUF5QQotPiBCcy1ncmVh LzRwcktWUGQ1bnJvRzVBVlpHU3NhdzYwUDBPTmx3MFNrdWNQYwotPiBKQil6Km0t
c2UgMUBLWXBjeyBaNidteyBpKDFEd3BzIDI/dQp6allkWkExUlVicmE5akN3UmtF Z3JlYXNlIFdlRTlUU2cgVlxLIzUgJlVyQiJ8Cng3NzF2b3d6MnlEdURQWHgrSFJv
WXFRVmVXaXcydkl0d2pzdDViUEQramQ2V2srZG8zNWdJZmZVN1hTMXdMcUQrCmpK Y1BMUkZyelJSdDJvUUUwY1k5R3ZBMnJaa1R4NWxlUll2azNseW5MQlpvVWUKYUdN
WitqQQotLS0gMzFrZ1ZKLzhiSHV5UFhVV2xwaUMweER2MjFiZFB3THBoSmdRVmlo d1ZscXVxd1dxSGpzMjBYeWhhYyt5ZlNkSjhmV1ZTTDBTCi0tLSBBTG45VUJOSXZv
TGVCYwqoZ8aMTHStyXBPSprQC1f4QkIbxYM6ThJb0xjIrPxcVFUZT6xJTfAgCNbr N3lYNVlyZldMVkpZdUMvS21NdSswVmU3VThZK2M2eis4CjwfWWJVR9Ty5dBXEr5s
TpsnIYf07r0w5HL2fYe8FpPnw9qTtf5AyvVcy6ktJ9WawFf9BmJilkMkJ7Rv7YZD U+QrifXvyMJJvqOKEfMYYomFLpt/VtbDAUwNlqLnFIk1VZ2xPUBisXPtReiCK3gq
UUGWtKgXnDZg3BEcmxL1sFJVD/WtHNhXIynBKTA3hqYyIkO8ugVCNbEQN871Yuu3 P+i6bUONSmPYvmF82VgiuKDuz/kmWqVGg2iSBPIXAGLNIEojndQwmhnrsN9ex4Xy
xiwGEEUHrjKEFZjO2S3HyXNEoV/NiaDAE8sbI3R+ju8ssV39XdNx0gDwlPzfgmSU lBHkvVsN8EamUJwZ+FEZe4+PjA5yEnANWpeTELOt2gA92/jwwnNIsr07eVTlkh8X
4UFReW1Zv23HlYzB4C/E4YDDwZviF/oSNwemlIXuj4VH UmQ2owGwktXP7it6//NV3C7sAdazs2bX65WSnog2E8WPNAorKWI8RCYs33CshVFt
zzclZ4hRmlAmqMHYqFY=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBDd3Ix YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBoYlRq
REFzVUVsSS9VT0diTEw5c2VCYnY3VEFvZ1o5MGh0MWdydUVDbGdjCnZPUTVCZ09n ckk2c21uMVZSR05SdlJpck5MT0FZeUpLTmpTRVN2WDkxWWxmVTBvCjlHZWtEQ2Jh
eFd2a2h4M1BOdythTlk5VzQ4V0RsL0tCNzJlcHF5ajBleDgKLT4gWDI1NTE5IGtj YUdybjJLWkNsZkxabTVIR0JnMno5YlMvVmhodDU1bktrSzgKLT4gWDI1NTE5IFZT
VWwrTHpRRUVpbmdxM2xEVm5qdkZCcHVCRnJZb3Q5bDJvNGRMMisyUVUKMWV2emVM Um9lWXhqRFFLTWdNaTR3Sm1acmY4N0l6WE1FSW54ZEtQcEE5RHBRRHcKcE5KYmp1
a21QYlRMdHNkMjFuVDhXdmpTajE1dllubUpra0F5eTcwNXZtVQotPiAhdFNSJW9a WVdCVk0wMHZ2SCtUR0xKWU10N0xETXlXZmgxcmZZOFBXYWtBawotPiBlRm1gKGxU
LWdyZWFzZQpQR3dzbjZqZ2hIRUlOT0hTcW5VQXJDZ0s0S2tONEVHRGJwU1QveFE4 LWdyZWFzZSAqXF8gOkU8Tm96IGA4IC1TCjY3NUgKLS0tIDhjaTNrbG1iQ05iTWRS
eVJFd3pVamVPUWRGVi90Y0F2T2dZZlBIClFYV2ZxNlBMbGdyT3NNckZpTW9vb01V UzIvNzA5TmVGS2ozZmYrYjlBY3J4Z1RRUGRNeWcKUTIEhWqr0fOODu86MDll7k3U
dmkxVUZiRnRLZlV3Ci0tLSBzdzVtakx3QWtiK2FkVnlqNkJPYmpCMGFZUHNkWndW ThgmS9nlcUY3fMgXzZLtpHIJ/4ZSI+miu8RmLMaeC61qv6xNThGdx+MvU4tMBWKA
WlorOXQxcVhWUDg4CvqG8Zngak01fTo/JU7Vh9ViXfzC0PDr3jn/j1XIXmYOAqxi Hv3XGi2MYL1jdHh2KYg5PgdqchYuHrFuBPS7c/tQow==
g3/rsRxhPcTWDAEu+bFd2XvkM8SZEEsLdsdPzP0VQD+UfIR5pujKmoLqEpX0oxNC
4SQusSIRxFc4uaSR6Wg=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,82 +1,82 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSA2UUhN YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGo2N0ZYUSBZVkFh
QTF2TUF2UXVkT096L3NqWndMbXRDd3hYWEdVaWlHMG9KemRkUnpFCk84SHc0YUNZ RDNNZCtySlNQTTZFbzJvZmF2TEZKVUM5VjFjQWNSeHlNc1JNZ2hNClVac2pEdWdw
QXJTN2VxL0ZsT053V1UxOVdQV2orb2c5UmF4NS8yTzBHN00KLT4gWDI1NTE5IDVC OVpOTmhWWklVRDA3UzZWWk9QVG1ySUFmTzRSVlFReVdFVFEKLT4gWDI1NTE5IDVD
RnVRbDVYU2lyRHJGemVvSjBTRitFUmJUQ2F5RDB5RDlCR3JOMGhzeDAKYjB2ZEg4 eWN2S29aWVNVTTdldXFxZG1vWUNYK2RrajI0OGNGaklzdFByTzlPUkUKTmsvdTE3
K1dmWmhFUUhZMXN3ZldDMnNWdnpGYktGWVNGSmhGY29nWVNqawotPiBUPjtPUyhZ ZFBvalZsdENxR0VnV3FVSnFxTitBRlFWR1BzcjJWd1h0U3hpYwotPiAyOkUlJC1n
Ji1ncmVhc2UKK1pZNmRGMnlsK042dmlodUNURndSNW5hR3ZiZHpBaFhwL3VsSDdN cmVhc2UgIWFOMiogVDc8IFZETk9BSDVTIHEKQ01rc1ZoNXQxUDdtWGRQUlRCV3k1
RlpMK3g2NjByZ25ENE5wYllEOG4wMzlsZgpxZzBkZVBKNSt2QjdxY0w4aGY0N0xp djNZeEtOaUl6Tkl5TDA1S2ZXWTFQZEFiV2pWMDJpZ2dkYXdBOGhSTVA2ego4UnBz
VURYV2JPTDhDRlVPMjIKLS0tIDBGa0ppTkFtaDJkNWxhWEFLcm5sR1RjWFBLMVgr ZGhXcklMd0NVWlhsc0Voem1wUzMyREViUllCRAotLS0gMU5GQjdJaVdlUFJGMG82
aWdxeEpsQW5xem5ubDQK+840lJ58bsN7IWJ+CiK3dEI6DxN0VPSufstDTgvpBoT0 YnZCUm05ak9qcmVyUVJSS0tHaWRRTld3ZkNPMArRgojrBQvlyjMhChn8jgUNDysj
Xl6CHPtEKITEpOCEFtqkElx7d42PGMvhkaOyo4zs2T8m/gSJxOojG4heAkXU78gq 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
xG01FgOaJ4w= sAtksQHMt1Njo1oRug3+/0iC6XWEig==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,30 +1,35 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSAyUHB0 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IERMTWVGZyB3QTU1
bEI1UXExVDB4dklPNDdFeUJmR3ZZWEo3ektMcHAzNHArQWtqK0FBCjd5Vkw3V2Iw TEdvbFNaUmE4U1lCNzI1dlloVDBTNXdURjZ2akRMV0R6dm5ST0FNCk9YclRSakhT
S3dhOEtKSmpzQi9VVWpJZHcxWHBoMlV3bUZZN25VUGZONVEKLT4gc3NoLWVkMjU1 NlV2UVJwak9Fa3dQNTNBK0xKWGlkNXdwZTBEMm4xZzc3azQKLT4gc3NoLWVkMjU1
MTkgVkZjdzVnIDc0N01uYmVZbGYxdlM2TEdaMEdlODZGUDlLVUFMUzBMd21WTWFK MTkgakk4UkFnIGNBMHNrdG84cUQxd1lqQmJIczlScU1jNk52Y3BtR2tXeDNWdWRX
SEQ0MGsKcXpyMzhPTEEwWVorZEZGc0ZpVTlxL2dod0dzd1ZsV2toZXdXSWxGd0sy cEFCR0UKVU5vem5MZUFPbGhoQzJTYnF4OTd3OW9jYTRkazdocVJkZ0pRRGNLek93
WQotPiBYMjU1MTkgbWpjSE5pd1ZIdjY0eVNFRFlIUkJVWTNYS3J6RlB6bGFLb2lr cwotPiBzc2gtZWQyNTUxOSBnU3hQMFEgQ2RUaEUyV1ZGbGRtZnlIUEtTQXk5MUZF
OVJsZHczYwpZOGxFTTZmTGFSZCtLWS9YWVJBRXVmZGN6QzlDb0ZmQUVMK1ZoMlQw djYxZ0hBUThlV2tXTHNvdXpVSQpMdiswMy9QNUtCb1hkbGRqR0Nia3FXTzE1ajZL
WEU4Ci0+IDQxOX1OLWdyZWFzZSBZZUBOOmcgZHx+biA6ZiA1VwpVb3NabW9OcGlm UXljSTZqM2YvbzVuWUFzCi0+IHNzaC1lZDI1NTE5IFZGY3c1ZyBYL1NrRjRaRnFn
dVpiaU81R0dDSGN5cnBNbnhzb1o5TGdDb3VRRVZFVWRJRmkrNkg0Rmp0U0g0MmNl RlNLdWJ3cThteDM3WmNaY210d3RmZzhCYmNpVXBwVGlVCjBkM0IzZC9zNjhmTTZV
Q1ZqZy9DClRHR3E3bzRCTHdDSTBOYjdWcGwvQU1aY3pLZXJkdSttSTFNCi0tLSBM R25ySkVoQWxQQ252WWFlWWZFMlc5dWtUN3VvVTgKLT4gWDI1NTE5IE8yUU1pWGFr
dnhlWjEvb0JkOS9DNks0clRESGxGeDB4MU1ET0IrSzQ5Qm9RMC91WERRCnjG+n2N NzUvZVpwalB6aklkWmJGWUQzTnJiRjNzdzY2MUp6MkY5M1UKR1VIWDVyTFpxem5F
bOhx+OuMHdbNoLdj9zvnNkRkx2D1imvab1/F6jZnVD4qgDzQ6/pzlkCQ98Co6/Gn TmRNWE9zZStLanRTdlU5d0NOSEJKdWhDUjBCdy9vOAotPiBTJCd2NyFYey1ncmVh
LnLps1aJuNV/eSkZv2AifXDNyIfFRza4MHcSGXnE6IdpioIFQu+/QIoOSw2MLE/p c2UgfE4mYSBXfms0TC5FPiBiYmx8IGRFV35mciNoCjVZdHRiVUtMMWxEMlJ4b1F1
qka6v8cOATV7Bw2s5EFYw+Vp8pl7ZJCYbWEBYGW9M9OoH9EdsA30Uz3LQO/0d2m0 TGgxY01XTlZpTEtndkg0T0hRVjlqVUJGMUpSaE5tVlZWR3VGenpkUXY2eXJtbmkK
8qfYJvCoTVcLooRP4fH00Hu44urMyxft5Xret+BvE8uy3+Zr/lGseh8csjQlSO2z ZCtqRVN4dnZENXdoYkpjNHRWYXkvN2laY1p4YjV0WQotLS0gOHlEVTVOSmlNemNQ
kVmfjYemVy1MKQhJ49ztQ11rPI+DDgQiBp1LeGX1ioNRdhSr5aut+pj3gA8tm3F+ TW5ISk1DeDlEM1RoQ2JtUFlyTXRKQjdnT0hiend2VQqrUFvr+76sKn0ldBmZMlEW
TK2Mqs2Zg6SfVR+UGeBxmj4LlysvX9GfMbWP3B35+eVcg9oD5iqltAEGczVD6H1C U2k85DLo2KU+/+GtbkZwVXxxIZHMLpoJgghHk9ptdalUgLGcl0X15x9jVaw8aeta
H3tijabyFniWyriN8ObcYr2XladDBMedX6r8yWXBxjk9Jm99bvyW07V5ZbiZNQVw hbeOHotRHY7bC3z0S74riTk3xDMR1eT0QGhDMWHjfo8SkCftOYBlFfhTftevdep3
R/C1ZBbwbWA6nxdMHftzXC8OBpaKY0ReQ3YEZTBVHC9F9X3W+SmC4xwLNsFdWMdL pKMZsuQMwH9JzxgUfcxIcWE975cZzrEJ85nfWMGvdSjcg51KNxP/UUPRxDlcbCEf
zL2cPBs1GXX7ePaEJdylw4jEePRk2uCaPuApqwqNP4i/FYbOsVAaJBQsj5FW5e42 9XX5apSzNsTI3ibGD1n6Qwq8bdVYDMHmy5pAhw4l8L+SdoU1tGdw7JOA16sMCJbx
Qdea4iZ3fSPEyGCkq9fxiuUr01rWDqg0XOwe+xkNlc37RtWxSfjzThQdp94kkSO+ T4bV0ky/PGRonjJuCyDBj8oe9vMe1ZI1O/ITtktekS+wocxBs6QXlY7pIZMlGUn2
PX+I5chLgQ+XXeZOWy1aUDQ+tiefvnYKd3cHYq5xyILwVZ+qimq3dUXcNuXMOJQR 6m59ZEEaf7R4/MdnmBDNDkQuyXaKc7SaTc6h5sKWzXdYScGUKvgUQ7U/WJ2ItUTC
oMXHD26j8vjF+F6tp0/bclP6fqj70ODnJYGfpSeqKHT7MbLQHSL9F4OMZuiJca+H N/Xq07GkZZMt5MYBlyEr+/mKWlcy+ylJPGb7EswvQWaHoeM1QF0XLZ1v+W/Xsso0
1t4NdV7dVKm2sckjNS8UbQH5GER0U46scL/7oaqxV6R5FW/GZGrXYy6w+venHFSz seIoz+geSu9a02kwfsa8WvWXdIAT5X2pNGPClVNzjQ23pfQfQuW8ZQrGmIFR4g5A
FhJPvBJVyoHV5Qa8dyw/3/sqG08oaXMXoRk4doEsaC/PnoSEv4KDfU9jf4uxrFl8 58T1K+vGLdShqqVGyJFMVrSuOzqX5FVmZalu7/++1IQfiRGUlrHKoPlKWnCfFEOu
6E1M7HIx3riMcs3RJDc9O9fqaQOyz8bMdQl6tC6B6JNf7/+mgIvp/dZBmGVeaGnh AYjaPeEFX2ByxcqfMK1YVPvUufdISUQeaQOO7mXGE3FqB0oUqmRIUiWZATwhq3Pw
w920E918ad6niZs3yf3Zl3rXRj/0CEO16B6SwgP6Uovr3uqbMkVmV3vQIyuf00QM p5QdcySTnmMpD/w05hvwski77kCdmYuHlMlLZez/kfhTnIGXris+Vwi/V19bsZ8G
4kr+X4tHP9pZXYMpdqwAw72JkflD6hAvDdLWDw== 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+IHNzaC1lZDI1NTE5IG44Q3BVdyBqVWU0 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBEcDNm
dkhad0tteUU2emZ2alJXTE52TW01dVlIZVJXdmZmc0J6RzVqYkdnCkdFTWh2Q0xh ajc3a0gwYmVSdkowak1aakJ3VWZ1Rk50S1JIS0ExVktVSW9OVWljCjJqbUEySlNK
VU5QYjlYNnY3NXBDcitBNWtXSEZwRHZQVzRvRU9vSXpoNGcKLT4gWDI1NTE5IEdy a0xyT3NPK1BBMjZzN0RrLzhwVitvS0pzQ25iSnJSc3FmM0EKLT4gWDI1NTE5IHk0
MGdiZ3dsMnFYVms4WUxab24rSFBPdHg3MUNSczh1WkpJMWFDWDJKMHMKOUhTR3lE M2pkWHgyMHR1WHc5bE4xWTFDTndKdkhRdTg5bzZIWEU2cm02UDZBQjgKUUY3dS9p
OCtYYy9MRDJlTXhWcjF5eU9nUnNzYnlyUE8xWmFRWjZpYWxzWQotPiBKPDpeQS1n U3h1L1ZvTkFPdURLZ2tSYXJXOGNZZ21KVGdIbXdhSUJrd2puYwotPiAoL0RtO0lH
cmVhc2UgL0JIJ19KCjJSNFVhYlNTQlhGZG1DZC9DUXd5ME5idC9uU0hzdHpncllm OS1ncmVhc2UKVGthN0ZSU0ZFTTg5YW9UOXMwa3RnSzFlMjE2VTN3Ci0tLSBwMEMx
MTBHSmQ3NVJtV0JLZjNYVXFhYjhTQ1UvSWRDdTUKR3cKLS0tIDRDeHRMK0hLbS80 Q0tQckd3SzBwUUE4SndMV01kUjVrOTdDWmxlcWs2Zy9TZk5yMzhvCrBXyLBZGuSD
NmU1R1l0bVlLeW5SWE0rTzQxdnhSblRuL0hsL3dMTjQKKbq4CiNLIiQ5hnVzqRcl dJodNI13obTM3UvX6hSgQ8Su+J3fOKr5NibkhQ0Auvlr2tUXhhDm2WOUlHjqVTq+
NQkwDqWV5EZgAcnOKeSwyAQUhAo/H8lW08D3CLrZWknODWuKO4oPo0aDHam12QBL jWLSyhioDlIEyBgk8Zrl7KGeDzBi
a6Q/qn0OyiDDcGVHi8I=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,14 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBXdU0z YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBzaEo5
YnpQb3EvcDZCeHp5amlNVURIOW5YaTNIbHBsdERGdVlNMVhkb25ZClljM0JCYUlp RzNndHBLMUl3Z2txdVZoOVJBbWsrTVJwY01WeHQ0VTBaSm5VclY0CnJLYStYNEI5
cHM5SlAvOHNBRFRrTW1DNU51ZUtsVU1xbUZSbkNmQ3lKNFUKLT4gWDI1NTE5IEhk VTN3dEQ0ZjBKaU1PZSt3d0lldkE5VC9wNHdWazNWV2F6WWMKLT4gWDI1NTE5IEJ5
NUlaUWJvb2djK2x6MGloSWdXaCtOeW9UYlUwWXllVGJtbVRuazhIa0kKRVpCNXV5 amlzTDhCOFF0TllPd2RsME5Cb1lRQUZPTGNvOEZnL1J2Wlpnb29GblUKT2t1NFZy
WmtYWklBcnVNYnViajBQWWd3RnFSU1E2d0paR3R5TkdMWVdGSQotPiA9Qjd9LWdy blNZSHdxZjlHTlZmaTFYT0laQ1IwOCtDZ21vTjhxM0owdTAvawotPiBLYS1ncmVh
ZWFzZSB1JVREIUwyYCBjIVozRjU6ICg4KQpvcXkyRTFTRmVqaHB2WHJuUW1Wb2I3 c2UgRX0nVGtpSCUgNVNDWCVwIDBuPjM3MwplK2cwc2htQ2RLcFhUZ0RSc21uQlFV
RlU3c0d1QXVsZHVpaTBzSVVWcHhYZ1JRKzU0bzNnQnJQZkdoOTQ0RHBJCi9aelFG Y1QwSStOK3lKYmVVQ0J5RXA3Zytaa0JoMWtlWU9qMHBLNktFZWxtbFpDCnJyS0JJ
N1dqVDZIQW1YNzJLMnYrQjZZajhoN290b1FqQllqRwotLS0gTjlJMUtyS3JhWkVZ WEcwbndYbERreFhLZlRyQ3E0czB5NkREancxZk5BTGdWRkR6N2NrZmFrbnhSYlZZ
VWRWTnlPSVBIMmFEZ0U4YkpCYUpBT3hmYnZUNng5VQqSdcZWFUKsMsL/zJ2pd/09 NElHNEEKLS0tIGVLdXAxU0ZaZVkxaHV4dEZNNUVuc2J4N3VRMzZzTktSMDY3bDVB
drHoNrGw9V/MZe5uacNXcNxZCkBdtndYuhsnkiPPM7nTY6umbB+oGKfceiVvuDjH bWtmUVkKigIZ3J0s23vNzmbzJGjSMGBXK6o6xnsA9HXeQZ13VgKv5Qv+UHu+Z0g2
4i2um6GLWLPDDkH8JuYbSlB72j5vkFLJf8N5ZwquzOg8VnXjrpnBFyhod1b1KL9I TeKdQSrHbDB3ydIxaiXsi2ivULdrIMCyd96rEJFxrkVuVqSZE0ehG5j+o8lKk4OU
18f6M6LsNNgOHukkT8P/6e9ziRqQrvuE/3Ave4lSdI6OcDm5 PDe70slbZrVYDSu+OOUOCVRSopZreCURlYn2Pc4rjvrMAn5r1r+/AxjZMdkmmQZ0
l0wMBTus5zZzKg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,14 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBDc1cz YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBoZUI4
Q1oxbGdjRkl4S21jVWYvMVlEeUpBM1E1UUYrajAwaURVTkkwRkVVCm1lM1JueUNi bEdWSW1pcDdMYVRJLzdQOWhsdTlZMmlyZjRVeEFwU3Z3NzVFa3lBCkxRZ1FYeU9D
Zk5iNjR5dnNtMmRLMFI5dGNzc1crZ1ZIZHd1RTNwbTg0UVkKLT4gWDI1NTE5IHI0 L2dramp4WFFlREZ3NTFnWUs3clVEcnBhLzExclVac1M2SG8KLT4gWDI1NTE5IHVZ
STNjSDYwTlBZc0VFN2hCWlRCOWF5K0JENXVDQVJDNmVPeExBck1pQ1kKM0VEMGhG bk9QYy94Tk50c1dVanNvNzhpYWNqeFMzVVR0eTQ5OXozMmp2VFlad2cKaEdvUnR5
cVRsNy9zMHpVODJrNy9Id2RuK1NRWE5KbjlaWkc0Z0RxSGs2dwotPiBuQzR7LWdy ZjhsQ1FRaittQXFkbnRZei85MmtWb3pXRVFsc0RzNzBPMXFMWQotPiAzSXJZb0RE
ZWFzZSBiPSkrPXd7cApmdExmdm45eVNaWHpGeklWUWU3elZ0ZzRTN2JrZ2xDejZM LWdyZWFzZSBudCBGIDklTWIjVnwgM3YvCjRVYUd2TForc0gycnRBR05MbU90QmNu
ckdOWmpSTDNRZ2JPaDAyTGliTS9mOGtPTkppdVc4CjhLRms3eVNVc1lXSkpoenNq dXlmQVB2bnZyME5heXlyMUdGTmR6SVd5SHcrTThrZEJYRlpnUmdmWnkKU1lyNjQ3
ZwotLS0gUFFwVlUvZ3J3bEJvZEZMMWc5V29nYkR1eW00ckJtcjNLRG1BQktaSTIv T0J6b3NPQ1FYZnhxTjFrYmN1UUJzMjEwcDBwSTJkTnlmeHl3WmFFTXB4eUVMWm9i
dwqes+5a5N4cGdVozlP3LV8UgbKh72dE4rK35Els82L6669nuBOvR8irauexjjQN NExUZzM2SHlLLwpiZjQKLS0tIHdqakpMSUNOdnlBcVA4RGJHR2d5QnJNT2dVclZN
Tu687x1fKdDJaxTd52+hEqhcHXPLJ80ZtTmZxRyzaZs4sc3jcCBW1O6OEm+c8JK5 djd4WXBLSUhORktzZ2sKzMjxqL3UPrtGmXDijdfu0AwxLJooK7ZKauYvXWSuZUrR
CstmZW/ZiFAgtNb7TcrUsfOznRrr7GbFOcQQ0ZXP6pjP3mtyNGWzZ+jmvIJ8hQ66 vl5i1QPMEEBYEl+NaZIFgxAFqWpAXHQ1VxSijMWxNjKUd1Chq0tbLrpbh9wXJflK
qfvaDzFM 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+IHNzaC1lZDI1NTE5IG44Q3BVdyBHMm9U YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBrd0pM
RjN1NFhXekFFLzRKMHhKQlEycWhTQ2VYSFdvVU54alpXL3doTFdJCkpvQWUvMkVD TGw1anJNaGoyQ3lMUis1cHVtRWFPcFFNZk9Cdm9TMEh1QTdKc25ZCm9VNzVQcUxE
bjduamVxYjluUVpmZW12eUordEhJL24vK1cyajFoaFV5QlEKLT4gWDI1NTE5IHVH Njh2Wi80RWszSU5NczRNcHpVYXpRMzJNcDRsdGxyWVZsUDgKLT4gWDI1NTE5IFpT
SEtKdXFoRGF6b0ZRdDM4b0ovOGlUVTlIa3RidnU1TFliVE9XUnZrR28KU2NydUVm MEJVUkcycVFJaVFOUURoL29VT3R0MVJCWW04NXVRVEpKY3hlY1lWRkEKcENya1Jx
Z21oVWhiY0RONUJvLzRaMjUvZ2plYkowMzBmTy9EQzVBN3JROAotPiAiIiM3T0xj VnBCMVBrZkc4VDkvMnZoTllCL09vQ0VOZytuMnRvYVQxL2FldwotPiAzLWdyZWFz
LWdyZWFzZSBra3FnOyh+CjhOdVc4S2dDYjVlT3FDV2ZFUFpMTGJUZy9YQ0lwVFhD ZSBCNX0gIiB7InIsfCAkOT9uM2UKUWpSYTZ1dUo3SHNaUlFibTl0UDhTaFh5Mk1n
MWJ4RlFOSE0zb0VBdWw0aXBpdkhEbXRxalpHQ1FicUkKek9SdDRCOUY0M0JvYVEK bFp2cjM5RGhqU0s0Qm50Zk1pUVZmT0R6ZjBoUG1EUGlKbHFzVwozQ2t1cWVDVVAv
LS0tIGJvc252UTJFcU91NmFkTVpFQ0owTHljZEp6VFRITm13QTR5bFlIY2Jub00K Q21QTjF3NW5UdTBZRldnL0RmSlV3QndHcDB0d0lvZ1RHTTByS1ZwNkNVQnU3WmVn
AfBoa9IqRVJqI/MGSJOhraKctjDtAXQFqPtqOZtMjYS0JBwJL0Mbne8uqDBb5dTt ZwotLS0gb0Fxb2FsYzgwRjJwSUF3ei9hZVR2Vk1ORDlIMWoyZ2RTd09hUmtvMWpD
OjiPT3jxovReurqn9TC7fDxEfpravsCD2cPQ 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+IHNzaC1lZDI1NTE5IG44Q3BVdyBiTUJJ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyBHNWg0
R1J2c2JuN0RKR2NxbkpFanIraU9TbjBiNGZGaVlvdmlidlNRZlNnClFESmN6MW03 WGRGZnlTdEhsRm96T0ZMOWx4UUxkTitJa3hMWDE1dVkreEtEa0RVClpvR2I5NGpz
c1NyUCtITUdrVmJneXhxVHdMeE4vTkJWQys4cmZMT3htSzgKLT4gWDI1NTE5IHJH aUF0N1VZU05oWXUzTEI1TVBLYlVVang3Zzgwbi9teDVlQlEKLT4gWDI1NTE5IG4v
VVAwSVFmYnZsSW1aUC9GMUxMMHM2Zk9BUHFWWE4yY1FIWENRbnZFbHcKTWc3eUpD OXRIbzhaZEdXc1pjTnRRWHVxc2ZTcmozSlJkY3lyOGtvd1BVeGprVUkKdUNqSW94
U0psQVRCaU8xaE1xNW5tNnRaeWdRT3VXL3d1bDArZ0xPTFFDUQotPiBAXUkpLWdy SUIyOWtsNU83cnVOTUxNR1BvWHJPczdzUWc4aGp1MjlpZm5ESQotPiA2KSV2PVx5
ZWFzZSByMGtJIy9LIDFeQEggOV8wXiBrKzhrZz9PCkcvNkV1UldNMDVCL3VhNmUy LWdyZWFzZSBqezVxOHc9biA+aD4tP20mXwpwRCtMUDhmcVhGNXpCZlFmSllpdDVp
QmptQnFvCi0tLSB1dWZFQXVvV2NQdVB5c0toWHppcDJtSDJJMWlsalNUTStzcjJs SmFZelNhZnJlR25DS2l2MlQ3ZGFtdGxkZEdEWVNrRlk1VEZBRm9GMHBFCkVlQ1hp
ajZEZ3I4Cvuto58XMLkBmj5UQ1RRvnQGmpqtyyWgSOR1EC6BxpCUJ7UFuaJt8g5W WnhOTGl2R2s5RDRKN0p5TmF6Y0cyN3ZlR2pDZlhMVjQ1c0FJN3hCbFEKLS0tIEFT
/ySWGj3/EJOM10hy1X2ORlkmiqyWks32vdVZ/DY0At1X0w== bjFiRStXMmJueHdsRm1nU084dDRpS0tBT21ENzZFclJXbE11NWJETm8KyMHU+tZY
QELtZCbXKWnP8QC6V84JIFAxoRslACwsIJZpogcZO/IFIV2RGunGjCJk6QBmhOPV
kJXRcGO/ndYjWfuU0U3+9HtPocnO
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyB6cVhZ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBOZ1RV
bC9XWlI0ZlR2UXBZV2FTSTZYQmJ1WFk0RGlNb2NFVGVlTU9pVENzClhWOVQvU3FS VVpOZ0Jhd2x4YklMOHdkNkd0MEM4ekZiYXFKMjRIdXlZVDFkOTFZCkRreDZlK3Bo
TjIzWlZGcTlCU3ZnMEFMR0liMmlSUFc5TlVYdmNjT29uQkEKLT4gWDI1NTE5IHk3 WmJ1MDBxSU5UYzRuUDMvdnpVc0prNFF4MjM0M1FaZmg4V1EKLT4gWDI1NTE5IEkz
aWJQK21VSjVkQ3VKVkZ2V29EbW8vMUk4RUFVMGlNR3VEUzR5L0xNVGMKRjRMZ20z THNsVDVCbU9KYWpXL010OVRmNjBVVmVyVzhRZW5McVFJcnA3ZWJvZzQKOE1lQnVX
dCsrN3BnV1dBSTNxMkU5QXVLbGo1TGhmVWk4WURJZUFMa1hydwotPiBQIT84UU8l Nk1rWVAvS1lpbDEzb2ViUUE4RlB5NnJOZmNoUGdUMGNuMk1iMAotPiBuYVwtZ3Jl
LWdyZWFzZSAzc2BWViByajU8IG9PUENud0FVCk5SQkxTM1AveHk0MlRlQS9XSmZo YXNlIHdYXzh3MSB0TyREIGpdTjheeQpJZ2pYUyt6UC9vVmRNZU1uaDdvR2ZUNG0K
RVUvR2Z1Tm9jNUE2N25DK1pZU1NVWXZNRkthcXR4MU1tdUx1czZqdENTaDUKWXZC LS0tIHNCRno2V21tZU9XV1UwR2IvZDdkWEMzZDI4V25Yb0lvdmJadXNEZFV6TEEK
OEhQWHdYTktqeUJtd3ByOGdvU1FUYXdtK3c4ZGcxVGRDCi0tLSBEajZDNlVBSjJa 81uT8S3QJNe+mVadi/VpXSPEP0Ygzm0/+1pB5qqYlSQEWTHqS55gCyFCwu+sjaDq
b3k3MmVhUEp5Z3NaeWZXdnZ1UEpLUlFzNndzNGtTOHE4CuSUBEd67OnVhX47nGhB DpYTSm1JAk5ql9NRj4fJvCS53lJZ4zo+5c0iJKmuRg==
tRVCS+h88eK5HJD5rzh8WOW81E9Jx54QdnNUAgRhiVTo4MUT1EknoUIHXJqFpQjh
hvpf+nVCCD5AKTpjufqcATo=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBIWXYz YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBBOVJi
OVRObmozM1dydmxGR1VvaGpLYjFEemFGU0luMVMrc0lCYmx3K0FvCkVQYlY1RDFo SXdGVDlXWmVGZUM0OTJPUWFYem5qZzAwZWxhNzhpT2t1VjdZU0ZrCjYxWUE2VW5o
K2cwVHVGem84VlFsZzZCOGl5WjRWQzRIOGd0SFFLczM2U2cKLT4gWDI1NTE5IHdh cGFLZ3g3Y3BpUWlWOUtUYTg4MGZXVHZvdVV3eU9iZW0ybE0KLT4gWDI1NTE5IDIw
eHVBcGc2czZ5WFR6cVd6ZnBCVnVvYUlabVRxYkYwalpCblhyM2E1RUUKcnkveEtp QmxaWTY0WjJFZUd6TUxqaHhRelpRQ1hGZVBEcm43d3JYUVhpTWp4aGcKVzhJdFFu
Vkk5REZiMFRRYkpTd0VIYlBnK0cwRDEwSTdyMjJWSVo2WUc2awotPiAsVVp7Jl56 SEhUUUZVNVk1N2tzekpzUS9RazRCcFBhb2xxbkhRNEwzVys5cwotPiBkeE19PHEm
LWdyZWFzZSBNcTpCMDE9OiAsbDVtZWEkIEJXPTM/PUkKTVZmeE80V0k0RVdBMDJE LWdyZWFzZQpwR0xsb05JVlFXQTZZQkJSWHY0akNRdjh1eXFnbmFDWUlCM0xLWXBi
Z1ViMkN0eEZDOUlHZDZUWWFmdU0xY2U5T2pnb2NTdzExQzJkd0dhR25BOG8vVUdN QkVuanloRQotLS0gZTkrT212MXdsZy9Kb1AwMkFHU3VsTElweGNlYkZ2UWVXRzkr
awovK2xzK0EKLS0tIGhTckhmN3ZOQSs1K0NyblBGV0paWldqOEFsMlJTWHZuSDll dnB0SHRnYwrveLSY6SdUDO+QH7WGniLIOPcECTQ7CiTj9lwD5Hm0rYLdvizolb33
VjdmcnRSZVUKqZIgJl3Kef2Vl4hTraEND7TuwI8pl+NZFec3PUY7mVxXpQxfkNtg CsGX/kSEI2bD
FZeF3b0VRvVBnA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,10 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyAvZUJz YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBrTW1E
RXNGUVgvUGF2ZTV6cUhzT21zcjVlUFVESkhVTEZyQ2Y4aU1NUUFJClowczVvUFJu ZDFSRXIwbXZXUlNJbHZCVjFzMzRxTmdtbDQ5eWVOWWp3SEdSUERrClVMeFRLOGUx
SmFzc3NHaFFrSm5EYmRiSkc1MVljYk9yK3pKNjFRWFRBS1kKLT4gWDI1NTE5IG1O U0dmT2ZUUXlCSGhJdE5ibkZLMmZ2VmZFWU9PQXpGZFRPQkkKLT4gWDI1NTE5IDIw
Um1Yd2ZxY2VGN0tHK0J6UW0xUlBVc1Joa29LOGpsNlA2aWlhL1lHaU0KZTErSW5Q VXd5N2EwTVdhdkRYeVF3c201SnV3TmxnUGpOZWY5bFJaQWRWMWxQVmMKR2RNc2pE
TmdpUW5tWGxwMFNZOGsvUWRQT2JUTzFBdXZhbUdRaXd5YkI3YwotPiBaLWdyZWFz aUs4ZW1XSGU1RzM4bHZYQks2cHZWUkhZN1hob2d1QnM3cCtlMAotPiB0XENxMSct
ZQoyaXdEbVljRlQ4UDdCc0NICi0tLSBqVGpDTXV3dS9YcHp5cnpZTnJYUGUvT0s3 Z3JlYXNlIC5LIEIoJ1w6Ci93eE5lMUxDQjE3dWI4a05wUE5sV2dWN21ZN2lFRThv
dWhGcVpKL0wwS2VyVlFlWkxnCriPULjxyfA41HDGsmqGUQvZMmQmWZE5aFoS5p45 YVJGNXNWaEtmbjVMaXhnemVuTXhOMXFtNkQvbGNuS3oKalZ6bEtGdHcxYnZNb0Zy
7I2BuDgNW9xD5gcMyADCeSDYLFQ= YnlGTDdoVVdyeTJpYjJjZkc3alUyV2ZGa0taeEFIYTdiRnZmYmRjR3BzNUp2Ci0t
LSBROCtSMzFNR3hTK1p5NVhGcFpqM0U0L0MwbWpzNUNTUWUyNEVXbzFFMGpjCnot
A6mYbp+jhpoyjZidXQfzLVcu6y34WqAfJZsfT6l5SJONVfSvSw+iP7XXW2T5OnE=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBVMTZr YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyA3dDEx
OGZjMlJOZVN4WUpia2VWK2lCaDN4aEdRWUFrWEFaVWdDMkRuYmcwCjQrMHVzaEc5 SUw4QVh5T3QySHFFemo2N1JWTjNCSGRiS1lmRTkxVEQ3S1NKR2dNClZNM1Vwc3dF
a2xaQWhQRXhJSGpKQjR4NHpweldvYnV5a3F5ZXhPbDdjcjgKLT4gWDI1NTE5IFZo elB0Unk1OWpUb1RQMVpSTUpJUDNrZzFMWkhKK0l1dXVBYlkKLT4gWDI1NTE5IGRF
bDBKSnVFT3R6VllnVHA2OG5OTDZvakVXdmpMWHY3Tk5EbkVybnlRUnMKWnZsY25u TTZlMjdaamlwTUJzYlVkcVIxbmtybkFwakVtK2craXVBNVZraGZ1VlEKN2VvaU5r
MjJ4cXJmSVdLVmx4V1lJZ0wxeWFGSk1mYkh5QnIvaGFpSzZnVQotPiBaIUtVSGhT VjNRS2I5WE9kVDhES2dWTUV2cEVJaXZxNzRUcFo5blhGTDBaawotPiAxXCJQemxa
ay1ncmVhc2UgISYrWiFBIFxEdz9ZLGVWIHYsNGpMIDk9dk5JClBqSzNDdkhrQ0V2 dS1ncmVhc2UgcDphKHFVTGUgIUonYydBPgowQnc2Wk5yS0E1bVBCWmpOc25MWWs2
MW1JMUNTT3N1VHJRUjZHWnJpQ05VcVpLOGNxM2dJdkd4QmF2dVc5TnFkalZzOUVU UTR1dVlJbnNXYUVJSkx5bWFIcmtpT1lPSVE3b2Zpd0JaTGZWcEtjelFDCitQcy83
OHM3dWwKNVpscS9Pb3NicksrZnFQbVNSd0lndjZTYzJLZEMrZTFDcFBVd21qMVRE bEVvM3FNTEhyWVVFS21tS0VQWG5OOHFza28KLS0tIEZZeUZwVGxUdEdtQTZuYk5K
SXlLTVdoSnRpKzhBCi0tLSA1cXM4YzJtRUJPYkV5UnVkSUw1QW1TZ2lWS0R4bjhM SHJaUDhHZ0JqbGNFSjJCaVlQTVo0OStkMWcKz/w0SnoHxnw71gr5DbXgMl59Kgjy
cXdrV1FyWE9JaU5BCozLT73zDlLthpJyWZtONXnfgnNT7ukuG7mF0d3eaevD6RYW SW4tzNGeRcX2j4YdRjr77TP5UAzpQE30tEcrtw==
O8vs4vcc842JpxvSpcI=
-----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,11 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHMrcVJmZyBwb1Ra YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpOcUlvZyBFb2hL
NTAzWDhCYkUwamhoZ1d1eFFhSC9RdSswZDZUZTZuR2RLK1BUZkZjCmlXcUYxNVZ5 MCt6c0I4Nm5SMFhEaWNZRTRtNkpYeVQvVGFVZzVqd0E3L2dZdzFBCiszUXZSVENo
bnZHcEs2R3NLN09Wd0RhSGVlQmhjcWRxSE5ia3hUajI4N3MKLT4gWDI1NTE5IEhv cmxqdVFwMFA0elpFOGZoM3B6SW80MEhCNlJoRXV0WXpUeG8KLT4gc3NoLWVkMjU1
WU5Pc0FycUdWL3pqeG5tN0lGbXhVcnZ3YzBWYVRMQjl5SXQrd2tJbXMKZkxiWHl2 MTkgcytxUmZnIHQxR2MrakZmRFJMcjhabGFON2xQU2RBSDNvRCtuV3NwNGJ2bjZF
ZFJIc1dmN3VIZjFQY2o3WmpWRitFTjFFcmZ1WExGZDlEaTM0ZwotPiAzcGFZUi1n b2lCQVUKcFNzM3paS1ZnWk8xY1VzVmdTWndMK0JCTU14bUJvM3E2bjR2TmlTY2tF
cmVhc2UgKDAgaCAnNiYlM3UgYzFTXDEKMkFKRWRtUQotLS0gWEUvc2drOG1vNnNI NAotPiBYMjU1MTkgY1MrT3ZkN1pUd0JVb0JWSDByNUNRd1NUd2ZiNVJrc0JCb0J5
N1BWS1NXQXg0NVRMUFc0WWgraVhnbkpuMUNPOEFOdwqYr2C5El+K2gTiD9Ssnmkl NENrU0MySQp0bFpwRXRZcHRVdnN3eitkNHlWc0c0a0NmUjVYSVFXSVNFVHI0b0ZB
Dh/whzpy0DAl7Kk0A5FA2LOfkxB/bgm0FGHwu1pxpvZWvxTynyX7Zikhd9T9PxUs U3kwCi0+IHQmKm9ERFdfLWdyZWFzZSBJIFkvRG5JIC9ZI002bmkgNX1hQHcKc2l4
SYQgsngQTC6Iw6s= S0N4YzgvK2xqZm1YVkl4ZlF1REVOTGRWOVZRQm80R2NnczlsdFlhTEd1RmVoNjZa
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+IHNzaC1lZDI1NTE5IHMrcVJmZyBvLzM0 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpOcUlvZyBjbUF2
VlpuL3I4OUYyQnhXK2pGTCtUdnVFWUw1KzVMdDJtZ3NXb3dCVXlJCjhIYzJKYXlh VXYyVkRud3JFU0tDQThjQlVMTTFaaW5uTkt3K2dmd1pleTFOQm5jCkNUeEVqSXVX
blZwemVTcnFTbHY1QkViUVFZVVZ2TzVWMW1XV1FiT2Fkc2cKLT4gWDI1NTE5IHdo RGUxcU14Z04zMlRubVIxV0VwazhlZlFtTCtXT2dVS3l4b2MKLT4gc3NoLWVkMjU1
NEViNE5TQ0NzUEdtc3h5YzVvdy9GN2N0Um1Ebm9iQ1BFQzFlRGVIVVkKRkd2YlNi MTkgcytxUmZnIG5SRTFOWWVIMU5yK3VTdkVtc0VYNnFkRXFrdEpiNTJIRHBvVnky
QkpJOEgrNjd1ZEpHd2FNSlFuZUpMS2czV2xVTDFmQjkyRFVDNAotPiAie3RkP0ot OHRyWEUKVXVvZlBBR0dsUXFMa2dSZ1FWVWpvZGlwcHJ3YXRscnBoaU1FWlVFYTdt
Z3JlYXNlIHJZK3RgezogSU5iZzJECkd3akE1bHVLOFdSanEvdDRsb1pUWXRLczdj dwotPiBYMjU1MTkgMWlBWCtSU0I4Q1I0T0JrZ1ErYzlHRTZsUHhZQkpYQVJGNnFy
NENMYW1MWm85VVkrbjVZVHgyTURCOFZnbmVkQTZKa1lOc1pyOW8KCi0tLSBrQzdP U3ZPeU9RTQoxaGZDYnRaNjBRRGQzMGNsTmZwcjhBRXdLeEFqVWxoT3hNNHk5cU9C
WngydFlQTEZpRHJ6VEZMMDhQMVFsUTN0NmN0QU45N0FUQ1M2cnJJCjpYZlk83/mf Qk00Ci0+ICF8Q0EuPSIhLWdyZWFzZSBKcCp6MgpaTXQwdEllbmhRVDhOQTdpb0RU
ZEzH+9fcVGlBOneXSlIDb8haRDO/8uqcIfaUGjIaGbWb+8XsVJB208/+wY6Eii37 T1VGZmdZK1VEMWdPUXduYWQ2YWx5aDFTQ3ZzRnRWbFRGN0lWUU5iQWdPakpZCnl6
Y3AMdP68LuPxNcuyc+bXSjUV5A== MnI0SE5sS0x2MUZibW96SllDQVVOK2grRldPOWo0VSs3SkFUN1dqS3RqTTdPZG1M
eHI0T1BHK1F1cWlINAotLS0gckFnWkoydklhWHZhZHBkSkN5ZmdadVdiMU1QOUZW
VkJENWlHVWNXcEVsWQqYscIBmSi//ev3IN2ax0Ei7p8Atu4nYQui7yoY/1fiyGQL
DB5+R9Dm4YUNHt3bjrBYclLohDGdLUnOB00BXUqNmlLm4psL4Ey5Go8=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyBQdC9R YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyB1N0V0
enJPZ0RLeWtrZ21XZGlTWnRMdGdDKzN5c1VteFhOV1cvQmRXVWc4Cno1M2dOVXlh UW5ZKzQrSlFUMnR5QUE5N2Mzd0FvK3JCeFM2RVRtUklVQkNkU0dNCnJuUUxjQjcw
anpMTmdsK3g0eG1pcE5ISDJIb3NVOVZML0puSTlEaWtMSGsKLT4gWDI1NTE5IGxU NURFOFRWRG5WSnBPT1dOVjFNQWZVUjJUSU9WVTExeS9XZkEKLT4gWDI1NTE5IHdq
aWlhODY4aTVrWG9Cd1NPYzFORHFwTUlkSmVBNm00U3BtRFVBQjN3RHcKWm1PQkda eDhJSTZKN1lQRXNaZzNIWEx4NTZINzZpdFRPZWJGcys5SUV6bmc0Q1EKeGlqTjF6
aFY2UDhyUy9SSCtkNXpxQzVrNWJUdzI0YTNrY0Y2YzB2QlltcwotPiBsLWdyZWFz b3Y3ZWNhcjF4WlpOcXh1OHBucmNCZmp6VTI1MXRjN1FlNlA3VQotPiBtQ01Dey8t
ZSA2YyBNNksxVTwgfkRvQkkxbSAsd0ZdU1BhCk1VazRBVlAwNysremkrbHoxZwot Z3JlYXNlCnFKKys4Y0I3RkE3bzl2NDJ2aDNMeTRTUUU4dFFwelV3SjVReExnCi0t
LS0gSTd1UWoxeTJsMHZsWVBHcklOejFZdnlYZU9ndmhlWDVtSDYzdEx4VDZnOArm LSA1QSthdHR0cVE4bXhiczZqUTNJdzkzdnZ2TEpESWwwQ3BWNnoyNmp4ZXdrCl1g
nUspT8mk2F5sszqhdjGtQ+sY6fNzzeYGDiq7dDzvFIMxqpCivwF/QtBr7KYovoeH oNwCvlTrPx5b5l0OUymxMo1HMCV9fsQ9zyaoJoPFN1hv7l4hjjX5oVoj/IdTJbbe
V3Gv3CToGo3ZBO6FLxXjQ63QQdMBbhIQYZeJcQ== ZMs1yqa7YbR4+HG9GG8nPhU36hchSN2whoz8
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyBuZWR6 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGhNYTRudyB6dGZN
N3VBU1d1WmJ2UjFNaUgxL3dJMU1hZVlGUW54NnYyM3BsU1pOUXhFCmZpYTFTT2s2 VDF3ME9IWWJMQUVrbmlKeXhZbzE1THUwVWhBc2ZBQXdUUkpod0FzCkVncGlyTXVv
UXVFQ3ZSVUN5dktEMXJFWk9pYWU5NEk3RGlCRkRXdHhvUkEKLT4gWDI1NTE5IC9F bmxPN0F3ODdvUmw3Sit1cERNTXN6YWZlU2IvYUhoUThER3cKLT4gWDI1NTE5IGQ2
WEh3MzRoSnowTTFaNW9ITkt2L3BqZ1N5dzhybytSUERnSExHNXVlbk0KSDZGd3hT RzdCWUw2N1BHWVhiOGhvNEdzQVlUanR3emdDV3FUTFRXUDB3RDdrMmsKZDkvWWgv
NEpSUWZwNG13OGJ5TXd5N1IzSHZta3JHd0hKVUlrZWo4ZW41OAotPiBtTl1MJC1n MFZhbHZtVDVQSWVEQ2NxQUZrOXFMVmYxbEU0STFJTVBzZm16ZwotPiB+Ty1ncmVh
cmVhc2UgPjZ+CnJXTWVXM3dNWGRXajhTOTVWK1dGRGdwOGtubG1nQmtnbU1VMlFt c2UgMnpCPlV9ClY2TzJGcnBrbUJTU3lyNVlrNDdwYTd3Ci0tLSBHaU9ZVnZoUkx5
dTlRUHN4dE5JREpVeTVhWDVGb3lnbEtZT1EKTkxwS0ZSeGxNeElyOEZhUmtacEhF QnE4UXhMdEg1elE1dDRoQWx4bDhBMHNwS1BlQkRaUm93CspT9YnuzfpKxC9y6SWJ
VVUyRmcKLS0tIFdpMmRNcWlhcDVEWWJTUExUQnJxY1BZMS9SRXNMWU9mSmVnZEhO JRyT8aFEJTjoDEqN2I/DBwRikSxKyspHi7grCwFaoofylqJzsP/In7Xlf91xbMXz
QUpYdlkKrNMg+0131P7eLgWVrsBL7uCblbgPq8/JrMnabnvPsevbqcqZ/sf0XHft njjXbBQQP9PG3Z2c0OHk
eP4jbM8PGKVIAyycjTR05B/Ocvjas9cE/5PpcpaqT2MVOmM=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSBtL2Rk YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSBaUm1C
NlMzRmhHZk81b2ZvSnVQcVlVUmFOUU5MT1gyQnlzcHFZd1dmWUNrCnBRYkRZRnpE WGJDSUNtTUYxbHVnWU44VkRnY3F6cTR0Tm1EUEdDYmltWHBrL1dvCnMzSEFqZTdS
RjJ2UXpLZ0FXZXlvbGhzaXh5MGlzeTVmM1k3MWc0M1JqVW8KLT4gWDI1NTE5IFhG K25tMkdkNEZ1dGhBMVhSUkR2M21EY0NtbnJjb0R5RVoyTDAKLT4gWDI1NTE5IHhv
bDdrNDlWN1JyK1VWQzI4Y2RHY01qSUs0WjJiM1FsZThqTUVvRUlTbkkKV3hVcVdm RlJOd3dCa3ZaeE5JS0ovV3ZiY0N5QThNSi9LaXQ5d1JIa1UyeVFJekUKd216RGw2
Rktmc1ZDNCtHY2JIZWJOVXQreTZmMi9tOUhXZ3NyVW1LQjArNAotPiAhSmZYZ0k0 Szg3VG1PWUVweE9udFpta0gxdWx1NEU5ZmNwK3hMdHdZNlB6SQotPiB3dlxKIXxG
LWdyZWFzZQowSHlzTFoxeWUwdHlnVXcrUXN2eHRzZ29rVEI3VG4wNjVaUHV4S25W LWdyZWFzZSBCLGsgWCU+UWwKWXVYQTBqVkRMKzNsU3JuWmlQdVpzdWJWcVdwVUM5
Z2VhMU56NAotLS0gczZ0NUx6eEFnSUhxTVQ5VHdUNUxXM0J0d1FuczNUN3J4RzVr aHpmZFVPbWlidHJEYlN2M3NvUWNqVgotLS0gZHhwUlE3YXJSdHFkdFpkTnh0bC9a
N3JFSVMrWQpOmxXeyAL3Vzx2ZQ3nTyg2frLthjvgRHqDUjD1nObOjIuGq7uaGZiP TytZcmtxd0pldUg2YUEzMFJ4QjR5dwqaUnjT3oaUunudOqNfh9twKyaRttf4sk9G
ejuPVRJD6JIeK/uTvPlQc/D7JZocNQ4x5kcUD+S7VYXjnyilnw== uiiKoEa314HbI1vgS4iCNX4vG+468SECiF9llZL9U1w+1MSF1y1BKy7XrDCsp7Xs
HiA2aA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSA4K1RL YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDdXUTlQQSBIdGI1
c1JEOFVGTGlhdDFJVURuU0l6NmtzNUQ1YWRkemdidnVMZmt0NkJJCmNsd1NKeEtl UVl5bVN5TDUvMTRuVFdpdG9lSkRjL2hOTHo3OVFxQ25IMDRlRGdJCm1oOEM5Rzlv
OTAxMWMyUFZzV1BSdUdZcjI2Rzc2OVE0bFVSNTZyZnptbmMKLT4gWDI1NTE5IFA0 cUljejB0aUNCS0x5Qjh1Y1JWQTNmWE9kcjF1ZktJVUxLSUEKLT4gWDI1NTE5IHBN
ZU5qSW5FWmFWaEttUmIzWFpBU2JlcSt3WkQxOEo3SitrZzVKZ2RSbWMKT2hITkFm Vm4vbC9GcUdaYjNlWEg0UUVTVVVHWG5VTnVPRFBkUXk1dm8wWlF1QU0KZEU3dXFR
VThFZVIrdlB3aGRUR0VrajF6RGI4L2xBMTRZRjMwb1Y0MW5scwotPiBVdCFqLWdy YU5GalhSakF3OEduSlhuYTN3SVdFU0NrWmJBZThvN1BGU21TUQotPiBdLVQtZ3Jl
ZWFzZSAsCkF5OVFVTFgrN0QveWEyNkkrbXhabC9ZMjZWUXd4OEpGdXhDNCtqbFVT YXNlIElWPzRFcicgYVoldUFzClA3M2JUNFU4T2xSRmpyeStMQjZNa1I0bmxLSUdj
dFlHQStNaTM5YmxUMGZtanNKS3YwblUKcnREQVFzcEtHYm4xcFBsTG9WYlRGeUFZ NGN6NkFvbmJEVWJXSEhCMjhkWjhyZkZPRyt5N2diZDI5SGwKczFsaEZXbDljVGQz
TjRDSFdsQmZlVTZGRE1OeWRVQitYbFdsSUEKLS0tIGZTbGxid1RTTVRKeFl1V1px cnpMcHpPdTBPL0hwb0t3dE1XNURWckVEOFZmQwotLS0gbmQrNm9QQkpxTnYzTTFt
RG9EdlNTc0N5OEUrMTJSbG9mZHRkT1BWczQKvp957P+EdrI0r4XdrK7+ImumCcE+ bHU3WWVDai9BY3VSaTB3NDN5bk1IdUNiMWk4NAqyrvPkq+FP+fSs9mZMgxLTJICD
wiFpG64xBcr6h2ICQOu2EXP/qSdFO28AuxAOZ9W+8pTlGHQMgZ0ydoSmRKEZ+j56 5l8Ii5h9fl2APzedygOLFGQLq0qW7pjBygfmQWgG4gqsO4iKIwEjUiv30/QCUhKL
4JOev8wbvnI= CGZJpN8AfZAcow==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,16 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSAwa2t2 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSBSdmly
dVBqUzZOZmtRUk5Dcm9zL0cxRTZvcU5nb0xFNi9zQ3ZOeENGbldRCm5US1NoYWJX aEtUTHh3Uy95ZGhEcno5VktOcmlWYzVqUTE1MUs4NDg4WnZBT0Y4CjY2QndadEF2
Z2lubncvTXVjTFk3Z0tBWWZnQkxQNGV5MWs0Y0g4MGRkNUEKLT4gWDI1NTE5IElZ TzF6TlJxall2cW4zeGltQVNabUJkcWNCUDRwZXpjYXJubVUKLT4gWDI1NTE5IGVL
VUJWdmFpOXpwampNcDVTQUllZjVBMm5VZzNOQXU0Q3k2YmJpbzJtQTQKaU9YUG81 N3dkVDFuSXdXZTFCVStzSCtCRUJGWUd0bnNGdnBhUlIzMExSNjBqWEEKOUdvZTlK
RWZMaXpucUY3am1lbXVVVzNtbFVES3lITTZmTDQwOHp5dVdYRQotPiBbeXMyVG1Y dzhyYWUzTmFzbnVxRTZRNHZ1NGFXSkJ3ejM0S0JCTGNpcHVzcwotPiB1dS1ncmVh
Yi1ncmVhc2UgIkQjIyBpClhyU3VOS3FCVG9sdFlrb0xpbVJDR1FTKzkzVkRJWUtq c2UgY0NxK2VxdyBdRE8vQSV1cSBcIFw/TG8jQwplZk1ZeTAzUnZJUm1qQ0VxUGpU
aDdvTVFTVndFelphWGt6eHRDc1JlZnZNSHQ3Y1NpbEUKQnROSzFlYTYybWFpSGdh NU1waVZ6OXF3NXQvS08vblYvRDl6TzF4RUhvdW16anZWcXdORTVnCi0tLSB6ayth
ZGFrbEdCelEvNUdDUjFuMmFsZlBqOTFFajFkSitJcW9iUXp2dGVxT2tzTmVSSExR aWRVUGgwazBMSUk0MEdETkhnbURkbEZXUGNCNnN4OUdPMXpkelcwCoil0uw2yDNc
Ci0tLSBhT0ZldFZ1MkJhcXdqRDIwajNZQ042K1JMS3d0Z0RjZ0F6VDNJMFZJbVJJ mOUMhVzALll4DgCX/MskwCdYohSBnMpSzqtltAhkW1aqdbuU3PC9LHQElpuHqpl9
Co/d2lNPILYqmod7NlFmKuTlh3GUs8pnT0HBJaWf4KHdujvFzWltAyUGJUk8Eye9 leNTu6n5nU5BHQ5qWShedL1G11SRzcblImwUcyg10d6Z0nUoCUjmw3BnGQ2wwvhC
Z4OpYgkXBBzHqio5hFuvVj+EPGrA0at1ef9XrTNZIV2XTcRHbTitbAjqQX2nY/C5 M+GGeb9X1ORRciqprtTq1WXWGG3tYgL9SjTAEtyMhAZ3CFjh3p4BdC9pVhCAJ9TA
u5Tt5L68xai2MQt5zEZZmyDaO3Blz2gCHNG2Nb0DZruT0cm3XIeWwPt/3tAWSDyk tbRAtqIz8VTzG86G4zPJYzBwtAse8tJDEO40GUqcOsmXhmPPpd7je1k381+GVrMj
FthdxxgpYYPnqN4ellzKfOG2qeyVtQahadBK+jmOxJWVOv2btcuL46nad9/WPXWG q2g2gTCUOrMryVRX/7+H2iRxtIxmhqESXIWgY5SIUv9uRA==
A7d8pddiLEcKT4HNFVGhHpHztYLv/JdHIy8RjMkBpgvsjdMnIif4gPPNxQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGV5cTNkZyAraUFM YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGV5cTNkZyB0blBa
MUc1c1k1V0RRTnJ0eGxLMXA0Rk5EWWozRWc1N0N4MVJ0UVFKOTF3CktndHNXRFFm Nlo2VGUzZzdTNW9heXhuUTJWYi9lWkl5WEp2VXcycExWd3FsLzNzCnpoZVVHaUxK
RWpFL2ZKdVcrQlR1ZTBDelhMSCt1dEhSR2Vwa0dzRDFlNmsKLT4gWDI1NTE5IFZG aHAxVmxQSkJ6a0JnSFpkdmRzLzl3ZktwYy9yckFSNGxzRWsKLT4gWDI1NTE5IFhl
WnBSN1Ara1daRkpDUWxTbkpCL0pNa1FOQTZZZWIvbkVHSmRQMk10MzQKSFlmeXR3 RlR5TjJoUVdScjRKN3hDQmg4aDJpSllnd25WNmlVVTU4SzZRZEdnQ28KVGsyVHN6
SDdhelM4azBMSmxLWE9TQjYvWG1BN3YvLy9lTisvT242NTc5bwotPiBNN2tfREIt TmZyV1hSbWZqWUtYNFdEQ0FLMjczRW5HSWVEaHJidHBjZzBZTQotPiA/by1ncmVh
Z3JlYXNlIG4jYnR5Wz1pIFd0QSAvbEolCktnCi0tLSBFbkQ4b1BNV1NVTENpeVhz c2UKUlJ1V2Y1eW1aNG1CTkx5VjZlREt5VkJjOVZoUno1T3p4WldSUTFpNDBXb21m
dDFBbDlZemtQd3BVcGpTNW44Ri9sRWIweHhRCiyITgLrK+irMnL52bIHaK22Kf7O Z0l6eGZKaTBqbjRiTTRybnEyagpxQlRIZldEVGdNblRrZkMycFljdkwxSjl4VHcK
lrKaePo6d6SkplhadDJ80s1wHtqCg1HoQG4k/uuffooEeAVQiWGlBbgyCVaTALtE LS0tIDV2Q0NHeGRUblhCc3JUenl2bGhueER0UnFkcUZNWXR1c09QWDI0R2FkdXcK
oUUz9wQ= 8DfpILM67mlC23bKjt2hWfpI51JvTa2YBEvHwHDuaaZBw8FIU6E8s77iyjTysmsw
vmQwTrdoUPhfLHXspjHH1GGObwOxvdw/Mg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,12 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGV5cTNkZyBNR1kw YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGV5cTNkZyAxUzdG
NUtYSWxBRzRYWmdmcVU4SEQxYUx4dllOalUwS3hBb2x3b29KREdJCnBCdk5QSjhW dG9ZNkpPNGlreWxRMFlPQUdFTk9FZ0kvVjNhbUNvMWU4a2krcmcwCnl1Vk1PUS8r
bU9ZRlRpSlVHWmtMTmc3ZDdreW02SitXTjM5UVVuT3hGM3cKLT4gWDI1NTE5IEp1 S0lveDV3OVphc3d6UDdLKzR4ZjB3RUloRzE2Zis3bjhsL00KLT4gWDI1NTE5IGVx
MnhBTVUzcjVvNkJOaTlWQmZhUzZZSzNnOUllaHlZQk5TVktQUlBmVmcKQXdPdlhQ TEMzdDRDbFBuT0c0V1Y1d1diSUVYMEw0dmJwcG9WaWQwcUdMMEVtZ3cKZ0x0OExq
TTY4OGE2dTFKdnV0UDNISjBadDdxODF6SmtwSUs1S09KTXNMVQotPiBTYyJUKy1n REFEdlJPU3o2WTlUQWU3b1NxcnJ1ajRRNkhxR1lxUmNpM1BLZwotPiBrMTREQ0lb
cmVhc2UgYWt+Z1h7TiBIIyZQeCBCCk13cU9BRU5sZGYvcVRoUW9RNDE3Vng0bGRQ LWdyZWFzZSB6TSp5Y0knClQ3V1VFY0dzSy9iNVdkRVZYU09yWjhONXkzck9VZVpC
UjdueCtFVTMrTGVES2VIYnlWazJ3WTZxRUxJejhsK3NRSFZKV1kKNkd0bVEvMFFq THh5amMvTlNLRDN2YUh1Mk1ncFcvZTZJZTRIRUlON1QKUzVDTDJJVk8KLS0tIENE
T2FQOUEKLS0tIG0rMEZFc2FickM0SVZBMndNUVQrS0VNYzBYSGRuejZ5dklOZHhP N0ZzdjNnTER6ejRqZ0Y2WTd6dUJOaHJqTk1HT2U2TlkyUnBPTFc5WmsKYcIKEURp
MzRrMUkKa233E5gkQ4ohFfzanVMnm3zk6le0omKF05tTnESTDTeduEMcOou6SmcU YcoFwU+gxq7BQlXo5LJDIcB73HPeerPnKYpfdyXFlsdm8mv3IXPEabSPwSNHXGIx
I5qzBKJdNoRBrOL2UTk6Nj9ymOoYl4lCBKYQc7ZRrYqau1g= BdinoqfHgAjMwptksMA3iIOWqp1cF6E=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,13 +1,12 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSBBSUky YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSBvRWE1
OENIdUpNVnNiZWhaVDVVMFFYVTh0bGgycjlUSWJqM3ZxSkJ6T1VFCklQNS9TOTlR Z1ZpNTFjTnluOUlPMldCQ3BzQzdVaEw4dlZjc2xuMkhGWlNDU1ZZCjVGNk9PNjNS
NThncEs4dldsV3h6NnhsbHlRcHc5R3kybExjbEZkclcyR3cKLT4gWDI1NTE5IGwx elNlSzMzYUNOQ0dzYkNFUDlxNlg4S3BGbXNldGN5eWYwZTgKLT4gWDI1NTE5IHp2
bFQ1UEp4REJZbkZ2VHhraldZeXJwVGtrcXJVME5Vd2xYVUpxWm42U0UKS2wrTGY5 eDFPZ0VadjFVZFdVMXlYd3ZFRXIyeDRQU1dMV3ZEWHJEaGxOSzgxRE0KVVpvVnFQ
THVxRXJHalF4SzY3eWRZMXJqSXV6S3hxcVlmaWN6Z1d0Z1c1YwotPiBdXlktZ3Jl WHhpWjN2Mm9TV0EwajlyRTg4TVIvbm0zZGFBVkJqbkJTZEh5RQotPiB6QS1ncmVh
YXNlIEUKeDBnd3RYejFkeUVER1IvczBoQ0g5TG9tOE11UlRweWpsRnBUZ2NId2Fw c2UgdycgQmJMCnVhd01JSEpUQ1U1Q3o2Wnh5UQotLS0gSkZwd0tNTGs0NkFiY250
S0gvSUZsT2ladWl5UkR1Z2pWS3NrWAoxMGk1L204WlNTY3RtVk1LWGRBRDNncHlH eGR1WXRLTWhzWlZOaHlGaHQvNUh2MjNDUm8yWQpH1cWbszmSTjpqz8Wyrt6g2TNP
V3d5ejB0NXdnCi0tLSBTMWMrQzlqeEI0dEtKeFJDU0hGVGxIWFdIL0pleitHVGx5 rtCRSnfw7UcoMh2oW3kyYcQrwf/sAFAHLNMh8oOWoxrKG1vtPxpOz251hlnee8JV
bGxkSWpwa3FjCnWQfvArLtwaAjQNtJ0bgS0Bi+bzL4xRQ9fmOsXegiKg+DW85ofI dIZ/2Gj/lPXDFTkhmX0TfABAe4wPJlM2wu9pj70UvGnI1osR6avrpYr9mMau3Ypm
641YVPvRkXqDI/+6PCCRxNk+KfUJeg0SvkyBjZwk3bWWMmEkN1SKMszINuQaI0Tl Ucix6cE=
a5lAKtUOr0Hy2fGF2ypFnTTjVy/Ax8FjHaJv81SqbVpkWw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,11 +1,10 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSA4Qmgv YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGdTeFAwUSArMzRH
YmRKT3ZZR0FtRmlUQmxERnNiOGphaTBXM3B0a2NnbXVSMTE2UEhFCnVUTTB6RmVu YnRFQUNJd0dRZDBEMGJZeitaS3Zaa214c2FnakxMYVVxOW1xMkh3ClpSeEprOTBW
akxtRHR5TmtPVGlOR1NuODFlb2lTcEtnTWphbHhwT0lHMFUKLT4gWDI1NTE5IGdZ dk1tRWhmUXRzeWpLUm13Z05pTnhsV0hCRk1YUjJQMTdDQ28KLT4gWDI1NTE5IGQ4
ZE9SdU11akt2SEpvZE9XMjRnUHJzVnBCSXlocWUvQUNxekszWE5uZ2cKOS9vZFZN OXBuVkRiS0lRQitweU1TRURzK1VhY0lnWFpZRGQ0aG9yOGhsa1JnUlEKUkU2VUhr
Y2ErVlQwVkJQR3ljQ3JoRDVuQXd0V2hGRjIrbVhDV2htb254UQotPiBoOiZRLWdy MTc5N2tkdFYxd1RUemk3MFEvb3d4eHFaVU9ycEhBWHk3QVRwZwotPiBbXGVcI3J6
ZWFzZSBMVnpRUzgiIDl+OFExIDFAL342UQpycExFU1BiV09UT0NzS1N1OWJOWjdh LWdyZWFzZQptTGx5SmVNM0FPMHdGY0NQK3AwCi0tLSBwbUxtKzMxYTNpdUlPc3BZ
WkkydmwweGRrbTI5ZmRTVEtGdlBOM2tjK0lrK0xrCi0tLSBpNjYzNWUzYkxnamQ2 V1N2cFlyTkZvYThES2wrZTdHTy82cXVoeStJCk0ed1c9gQUw93efGmqJDQ608cwj
ejlGQzR6dlExWE1kbHhJQUM5UG5PSzhxS01mN0VvChgTMuB7wDbjXfDikKEKbd5k PDVTyJ7erVlArctJkTKOx57QMqWzMSEGh7O196SceZEv
gp8QdI55ueCnEl5UfFwurHn9Cd3I6EPiSN1J3rRFMTiuO5Ke
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

View File

@@ -1,15 +1,18 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG44Q3BVdyA2VDkr YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IERMTWVGZyBUUmlu
NFl3MGV4YW44TWhWVklaTW1pazVkU1ViZkNUdWdGaXBiVXJVZFdjClMyWHc3dk9W VkJMYVltVTZyUnh0aXdZZzduSEl0ZFAxb2lIb2F2ck4zdlBjdFZNCm51RnV0M0pi
ZFVzTnh4MnE2Q1h1MGhEVkk4c2JMVmlVZzdJeHlWaUR4OFkKLT4gc3NoLWVkMjU1 VkVaZVdOMWpKOW43ZjhFODgzZTJ2c1F1eXc5WnJvMHVZUGMKLT4gc3NoLWVkMjU1
MTkgcytxUmZnIEZERng2OGhhL1dTeTFYM2d5Y3ljWk1EV1dyb2s2RHJaZ2pJN3pR MTkgbjhDcFV3IDdhNjB5U1h3ellMRDRRQWVkQ3hNcVQxUnQrbkQ0V2doR3pFWFRI
cUlPajgKWkdrVTdYRUl3N1FUVEU4elA3dU80TENVZXduRDVHeEpUVENvcW1VZ2Vs RE9xMDQKTUh2bXV0eTdvRmhmTVVaRllRcEpiRStnSnpMeGFpeGt0eEhvQlZETXhi
VQotPiBYMjU1MTkgR1VEZFVHeWdxakV5NXYrYXlJaHZ0V3dLbXNIUmd6S0U2MFJP awotPiBzc2gtZWQyNTUxOSBaTnFJb2cgUW03aXgrM3FWbURXR3o0ZkZsUE83YVk1
SnhQSVh4cwp6ZFhJRXkvaUFsb0p5N1JpcHNnc2UwbmVpdVovQXdLcGNLam5WNUs2 ZzkwZm1JdC81aHI4YVJOdWQzTQpzS1dHRFZYeHV2djVFN1Q3MHdhWVl5WVc5NExy
a0NzCi0+IEleLWdyZWFzZSA7Iy9ueTUKQjlHZStJTElISmpaNERmdkxZcTNWTnlU M2xEckFMTjFvTWI1Y3BrCi0+IHNzaC1lZDI1NTE5IHMrcVJmZyAwQ0Z0a1FHU0lV
N2pZOGFDaGtudlUxRmhSOVYrWjd6M1lyazFlQyt5YkMvUk15eVNsaApSRlF6ZGh0 VEFmMFpsNXF3Z0l3eTkrdzRZQ3lSQUg4MFhPaUNEbGdVCnFuVEhXWDdndnhxdUtV
QnA2YllsK2lESC92SjltZlV6UFZzRXp3WEg0SQotLS0gY3lXNFUxQlUxQXQxdjJv TkdRZnpFTGpaZDNJalI3b1NHUld2NEV0TlphK2MKLT4gWDI1NTE5IExObHUwOEFT
aUJWaEtrRUpNdlZqWlFwMW5MQXcvQlkzYmN6cwoFYEIL6t0rQBxOjMnSD+ySJAwI bGl4S2F0YVdHaGFnRjdIQ3VDeVFDbjh5SFpkczN3d0ZuQUUKSEFvdTIwSUhvd0dz
CHu+SNUkK6gyAR+vqv5+cuaouCHT0ljrL+7e/wuBg2X+mbNi2Sa9GkfT7+U6GtuX dDdBSUxDOHZaNkNLL0x0aDRCS1ZORUsrdWpJS3EwVQotPiApcDQxLWdyZWFzZSA5
beimorHFaSU4KA== ZSA9YD4gbWMgRH18eXd0YnkKbmcKLS0tIElQa3pEcVhtN0N1bW4rSTZ5VnB3N256
bnM0OXpSc1BuNmJwZnBremRIZnMKhN29J3s+Cif3jvx63Xay77CdC6uVhjsojdbF
zhWad9vPolrrbEiNkhcdrutyTRniFBUEwxQVGmAxcEySNTr3lnWnWQFagphu3F27
zLs=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----

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