Compare commits

..

117 Commits

Author SHA1 Message Date
9ace8f4d19 backups: exclude cache directory
All checks were successful
flake / flake (push) Successful in 1m40s
2024-11-13 23:02:33 +00:00
9430e16ff0 resilio: split backups between repos
All checks were successful
flake / flake (push) Successful in 1m41s
2024-11-13 18:31:22 +00:00
95669cc2de frigate: initial setup
All checks were successful
flake / flake (push) Successful in 1m40s
2024-11-10 12:23:35 +00:00
390bdaaf51 resilio: update to unstable module
All checks were successful
flake / flake (push) Successful in 2m19s
Currently this pins `rslsync`'s group ID using https://github.com/NixOS/nixpkgs/pull/350055
2024-11-09 21:03:56 +00:00
ba9d54ddab chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 2m7s
2024-11-09 15:20:26 +00:00
843802bcb7 backups: include more git repos
All checks were successful
flake / flake (push) Successful in 1m45s
2024-11-08 12:23:54 +00:00
a07c493802 stinger: update firewall for homeassistant
All checks were successful
flake / flake (push) Successful in 1m47s
2024-11-06 20:12:59 +00:00
3a2d6f4e2e stinger: enable bluetooth
All checks were successful
flake / flake (push) Successful in 1m35s
2024-11-06 10:34:33 +00:00
a383e013c6 homeassistant: microserver.home -> stinger.pop
All checks were successful
flake / flake (push) Successful in 2m0s
2024-11-06 01:36:14 +00:00
ed3b9019f2 homeassistant: backup database
All checks were successful
flake / flake (push) Successful in 1m36s
2024-11-06 01:05:52 +00:00
a3fd10be31 stinger: init host
All checks were successful
flake / flake (push) Successful in 1m36s
2024-11-05 22:10:12 +00:00
79a3c62924 defaults: enable all firmware
All checks were successful
flake / flake (push) Successful in 1m37s
2024-11-05 22:10:01 +00:00
0761162e34 chore(deps): update determinatesystems/nix-installer-action action to v15
All checks were successful
flake / flake (push) Successful in 1m31s
2024-11-04 23:01:03 +00:00
2999a5f744 merlin: init host
All checks were successful
flake / flake (push) Successful in 1m29s
2024-11-04 22:35:55 +00:00
5146d6cd6f lavd: finish cleanup
All checks were successful
flake / flake (push) Successful in 1m27s
2024-11-03 18:36:01 +00:00
3ebba9d7a5 home: enable zoxide
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-31 23:00:45 +00:00
1b5f342aab home-manager: enable ssh-agent
All checks were successful
flake / flake (push) Successful in 1m28s
2024-10-31 22:32:57 +00:00
87d311dabe sched_ext: switch to unstable for packages
All checks were successful
flake / flake (push) Successful in 2m3s
2024-10-31 21:59:09 +00:00
0cf7aa1760 tang: remove tywin ip
All checks were successful
flake / flake (push) Successful in 1m24s
Missed this when cleaning up. We should probably get these static IPs from
authoritative DNS like Tailscale IPs, then they wouldn't have been missed. We
can then construct the static IP mappings from this, moving some stuff out of
router/default.nix.
2024-10-29 23:35:20 +00:00
363b8fe3c0 tywin.storage: delete
All checks were successful
flake / flake (push) Successful in 1m26s
2024-10-29 23:24:19 +00:00
ca57201ad5 tmux: increase history-limit
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-29 22:54:30 +00:00
32de6b05be tmux: add kernel rev and extended hostname to status-right
All checks were successful
flake / flake (push) Successful in 1m31s
2024-10-29 22:48:12 +00:00
0149d53da2 restic: backup to backblaze
All checks were successful
flake / flake (push) Successful in 1m33s
2024-10-27 21:24:20 +00:00
c7efa1fad4 restic: backup to wasabi
Some checks failed
flake / flake (push) Has been cancelled
2024-10-27 20:09:45 +00:00
dbc2931052 restic: split out common behaviour
All checks were successful
flake / flake (push) Successful in 1m28s
2024-10-27 15:57:07 +00:00
817cc3f356 phoenix: temporarily add a password to debug boot issues
All checks were successful
flake / flake (push) Successful in 1m28s
2024-10-27 15:37:52 +00:00
c33d5c2edd secrets: re-encrypt with boron user key
All checks were successful
flake / flake (push) Successful in 1m29s
2024-10-27 00:43:47 +01:00
187c15b5ab backups: update scripts for new host/path
All checks were successful
flake / flake (push) Successful in 1m28s
2024-10-26 23:47:34 +01:00
fc1fb7b528 sapling: set default merge style to :merge3
All checks were successful
flake / flake (push) Successful in 1m31s
2024-10-26 19:26:48 +01:00
caa3128310 home-manager: pass through nixos stateVersion if >24.05
All checks were successful
flake / flake (push) Successful in 1m27s
home-manager currently has a pinned stateVersion on all hosts, even though many
of the hosts were initialised after that point. Create a condition such that
any hosts initialised after 24.05 (the latest currently host) will use that
version in home-manager instead of pinning to 22.11.

Any future users can pass the stateVersion through without the `if`.

Test plan:
```
# system.stateVersion = "23.11";
$ nix eval '.#nixosConfigurations."boron.cx.ts.hillion.co.uk".config.home-manager.users.root.home.stateVersion'
"22.11"
```
```
# system.stateVersion = "24.05";
$ nix eval '.#nixosConfigurations."phoenix.st.ts.hillion.co.uk".config.home-manager.users.root.home.stateVersion'
"22.11"
```
```
# system.stateVersion = "24.11"; // no-commit change
$ nix eval '.#nixosConfigurations."phoenix.st.ts.hillion.co.uk".config.home-manager.users.root.home.stateVersion'
nix eval '.#nixosConfigurations."phoenix.st.ts.hillion.co.uk".config.home-manager.users.root.home.stateVersion'
error:
       ...
       (stack trace truncated; use '--show-trace' to show the full trace)

       error: A definition for option `home-manager.users.root.home.stateVersion' is not of type `one of "18.09", "19.03", "19.09", "20.03", "20.09", "21.03", "21.05", "21.11", "22.05", "22.11", "23.05", "23.11", "24.05"'. Definition values:
       - In `/nix/store/8dhsknmlnv571bg100j9v9yqq1nnh346-source/modules/home/default.nix': "24.11"
```
2024-10-26 19:13:08 +01:00
72e7aead94 sapling: set ui.username
All checks were successful
flake / flake (push) Successful in 1m29s
2024-10-26 18:49:14 +01:00
b5489abf98 ssh: allow on all ports for sodium/phoenix
All checks were successful
flake / flake (push) Successful in 1m28s
2024-10-26 18:15:31 +01:00
9970dc413d boron: persist ssh key
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-26 15:04:14 +01:00
b3fb80811c chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-26 14:00:16 +00:00
4c7a99bfb7 home-manager: enable neovim
All checks were successful
flake / flake (push) Successful in 1m29s
2024-10-26 00:39:35 +01:00
edad7248a5 chore(deps): update actions/checkout action to v4.2.2
All checks were successful
flake / flake (push) Successful in 1m25s
2024-10-24 23:00:54 +00:00
ce6c9a25c5 flake: fix unstable path pointing at github instead of gitea
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-24 09:39:20 +01:00
3a0c8effbb flake: use generic branch from forked nixpkgs
All checks were successful
flake / flake (push) Successful in 1m29s
Rather than pulling a specific feature branch into `nixpkgs-unstable`, pull a branch for `nixos-unstable`. Then we can cherry pick and rebase multiple features in the `nixpkgs` fork and keep up more easily with upstream.

The preferred method here would be a list of PRs or URLs to patch into the flake input, but that doesn't seem to be an option.

This change adds in the resilio gid PR, but is otherwise unchanged.
2024-10-23 23:27:53 +01:00
172e6c7415 router: enable ssh on eth0 and add work mbp key
All checks were successful
flake / flake (push) Successful in 1m28s
2024-10-23 21:06:24 +01:00
9a18124847 phoenix: enable zswap
All checks were successful
flake / flake (push) Successful in 1m25s
2024-10-21 23:00:04 +01:00
efbf9575f2 phoenix: enable plex
All checks were successful
flake / flake (push) Successful in 1m25s
2024-10-21 22:27:12 +01:00
e03ce4e26c phoenix: enable resilio sync and backups
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-21 20:49:13 +01:00
b18ae44ccb resilio: place storagePath in directoryPath by default
All checks were successful
flake / flake (push) Successful in 1m25s
2024-10-21 08:54:20 +01:00
e80ef10eb7 resilio: calculate default deviceName automatically
Some checks failed
flake / flake (push) Has been cancelled
2024-10-21 08:54:20 +01:00
26beb4116a phoenix: serve restic
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-21 00:39:36 +01:00
1822d07cfe phoenix: enable downloads
All checks were successful
flake / flake (push) Successful in 1m26s
2024-10-21 00:20:42 +01:00
a6efbb1b68 phoenix: import practical-defiant-coffee zpool
All checks were successful
flake / flake (push) Successful in 1m24s
2024-10-20 20:07:59 +01:00
6fe4ca5b61 phoenix: mount disk btrfs partitions and add chia
All checks were successful
flake / flake (push) Successful in 1m23s
2024-10-20 20:07:59 +01:00
3e8dcd359e secrets: clean up tywin secrets
All checks were successful
flake / flake (push) Successful in 1m26s
2024-10-20 20:07:16 +01:00
86bca8ce1c tywin: prepare for zpool export
All checks were successful
flake / flake (push) Successful in 1m23s
2024-10-20 19:37:26 +01:00
ee3b420220 backups/git: move tywin->phoenix
All checks were successful
flake / flake (push) Successful in 1m24s
2024-10-20 17:40:20 +01:00
58ce44df6b phoenix: add chia
All checks were successful
flake / flake (push) Successful in 1m24s
2024-10-20 16:29:55 +01:00
f34592926e phoenix: init host
All checks were successful
flake / flake (push) Successful in 1m24s
2024-10-20 16:07:21 +01:00
7dd820685f backup-git: fix systemd timer
All checks were successful
flake / flake (push) Successful in 1m28s
2024-10-19 18:30:57 +01:00
4047b0d8b2 router: reserve ips for nanokvms
All checks were successful
flake / flake (push) Successful in 1m27s
2024-10-19 16:53:35 +01:00
d7a8562c7d restic: modularise server component
All checks were successful
flake / flake (push) Successful in 1m25s
2024-10-19 15:24:32 +01:00
ea163448df homeassistant: enable waze
All checks were successful
flake / flake (push) Successful in 1m23s
2024-10-19 00:39:33 +01:00
a8288ec678 scx_layered: get from forked nixpkgs
All checks were successful
flake / flake (push) Successful in 1m24s
2024-10-18 13:56:40 +01:00
50a8411ac8 nixos: add nixpkgs-unstable to flake registry
All checks were successful
flake / flake (push) Successful in 1m15s
2024-10-13 00:33:57 +01:00
6f5b9430c9 prometheus: add alert for resilio sync going down
All checks were successful
flake / flake (push) Successful in 1m17s
2024-10-12 21:39:00 +01:00
33cdcdca0a prometheus: enable systemd collector
All checks were successful
flake / flake (push) Successful in 1m15s
2024-10-12 15:27:13 +01:00
c42a4e5297 chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m43s
2024-10-12 13:37:53 +00:00
2656c0dba9 scx_lavd: package and ship
All checks were successful
flake / flake (push) Successful in 1m18s
2024-10-12 00:54:02 +01:00
961acd80d7 scx_layered: package and ship
All checks were successful
flake / flake (push) Successful in 1m14s
2024-10-11 20:15:55 +01:00
eb07e4c4fd chore(deps): update actions/checkout action to v4.2.1
All checks were successful
flake / flake (push) Successful in 1m15s
2024-10-07 23:00:25 +00:00
4eaae0fa75 isponsorblocktv: deploy docker container
All checks were successful
flake / flake (push) Successful in 1m18s
2024-10-06 21:38:06 +01:00
72955e2377 homeassistant: announce locally and deploy to hallway tablet
All checks were successful
flake / flake (push) Successful in 1m17s
2024-10-06 20:43:48 +01:00
0a2330cb90 www: fix cloning script
All checks were successful
flake / flake (push) Successful in 1m15s
2024-10-06 16:35:59 +01:00
3d8a60da5b sched_ext: bump kernel to 6.12-rc1
All checks were successful
flake / flake (push) Successful in 1m13s
Removes the custom kernel features and requires any host running
sched_ext to pull a kernel at least 6.12. Looks at
pkgs.unstable.linuxPackages first, if that's too old it falls back to
pkgs.linuxPackages_latest, and if that's too old it goes for
pkgs.unstable.linuxPackages_testing.

The plan is to leave `boot.kernelPackages` alone if new enough, but
we'll keep the assertion. Some schedulers might require more specific
kernel constraints in the future.
2024-10-03 00:17:59 +01:00
c0e331bf80 boron: enable resilio sync
All checks were successful
flake / flake (push) Successful in 1m16s
2024-09-28 15:01:30 +01:00
9c419376c5 chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m19s
2024-09-28 12:31:15 +00:00
4332fee3ce chore(deps): update actions/checkout action to v4.2.0
All checks were successful
flake / flake (push) Successful in 1m15s
2024-09-26 23:00:18 +00:00
ceb8591705 step-ca: pin uid and gid
All checks were successful
flake / flake (push) Successful in 1m14s
2024-09-23 20:30:35 +01:00
415a061842 prometheus: move id pinning to correct module
All checks were successful
flake / flake (push) Successful in 1m15s
2024-09-23 20:26:34 +01:00
31a9828430 prometheus: add service and enable reporting globally (#330)
All checks were successful
flake / flake (push) Successful in 1m15s
## Test plan:

- https://prometheus.ts.hillion.co.uk/graph?g0.expr=1%20-%20(node_filesystem_avail_bytes%7Bmountpoint%20%3D%20%22%2F%22%2C%20device%3D%22tmpfs%22%7D%20%2F%20node_filesystem_size_bytes%7Bmountpoint%20%3D%20%22%2F%22%2C%20device%3D%22tmpfs%22%7D)&g0.tab=0&g0.display_mode=lines&g0.show_exemplars=0&g0.range_input=1h - reports percentage used on all tmpfs roots. This is exactly what I wanted, in the future I might add alerts for it as high tmpfs usage is a sign of something being wrong and is likely to lead to OOMing.

Aside: NixOS is awesome. I just deployed full monitoring to every host I have and all future hosts in minutes.
Reviewed-on: #330
Co-authored-by: Jake Hillion <jake@hillion.co.uk>
Co-committed-by: Jake Hillion <jake@hillion.co.uk>
2024-09-23 20:24:31 +01:00
7afa21e537 chia: update to 2.4.3
All checks were successful
flake / flake (push) Successful in 1m15s
2024-09-22 21:09:31 +01:00
739e1f6ab3 home: move tailscale exit node from microserver to router (#328)
All checks were successful
flake / flake (push) Successful in 1m15s
## Test plan:

- Connected MacBook to iPhone hotspot (off network).
- With Tailscale connected can ping/ssh to microserver.home on both LANs (main and IoT).
- With exit node enabled traceroute shows router's tailscale IP as a hop.
- With exit node enabled ipinfo.io shows my home IP.
- With exit node disabled ipinfo.io shows an EE IP.

iPhone exit node is still playing up, it shows no Internet connection. This behaviour was identical with the Pi setup that this replaces, maybe an iOS 18 bug for Tailscale? Treating this as not a regression.
Co-authored-by: Jake Hillion <jake@hillion.co.uk>
Co-committed-by: Jake Hillion <jake@hillion.co.uk>
2024-09-22 21:04:53 +01:00
8933d38d36 sched_ext: ship pre-release 6.12 kernel
All checks were successful
flake / flake (push) Successful in 1m14s
2024-09-22 16:18:04 +01:00
0ad31dddae gendry: decrypt encrypted disk with clevis/tang
All checks were successful
flake / flake (push) Successful in 1m15s
2024-09-22 11:06:03 +01:00
d5c2f8d543 router: setup cameras vlan
All checks were successful
flake / flake (push) Successful in 1m15s
2024-09-17 09:20:27 +01:00
1189a41df9 chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m43s
2024-09-15 16:01:08 +00:00
39730d2ec3 macbook: add shell utilities
All checks were successful
flake / flake (push) Successful in 1m16s
2024-09-14 02:39:26 +01:00
ac6f285400 resilio: require mounts be available
All checks were successful
flake / flake (push) Successful in 1m15s
Without this resilio fails on boot on tywin.storage where the paths are
on a ZFS array which gets mounted reliably later than the resilio
service attempts to start.
2024-09-14 02:30:20 +01:00
e4b8fd7438 chore(deps): update determinatesystems/nix-installer-action action to v14
All checks were successful
flake / flake (push) Successful in 1m27s
2024-09-10 00:00:51 +00:00
24be3394bc chore(deps): update determinatesystems/magic-nix-cache-action action to v8
All checks were successful
flake / flake (push) Successful in 1m13s
2024-09-09 23:00:50 +00:00
ba053c539c boron: enable podman
All checks were successful
flake / flake (push) Successful in 1m13s
2024-09-06 19:04:25 +01:00
3aeeb69c2b nix-darwin: add macbook
All checks were successful
flake / flake (push) Successful in 1m13s
2024-09-05 00:50:02 +01:00
85246af424 caddy: update to unstable
All checks were successful
flake / flake (push) Successful in 1m13s
The default config for automatic ACME no longer works in Caddy <2.8.0.
This is due to changes with ZeroSSL's auth. Update to unstable Caddy
which is new enough to renew certs again.

Context: https://github.com/caddyserver/caddy/releases/tag/v2.8.0

Add `pkgs.unstable` as an overlay as recommended on the NixOS wiki. This
is needed here as Caddy must be runnable on all architectures.
2024-09-05 00:04:08 +01:00
ba7a39b66e chore(deps): pin dependencies
All checks were successful
flake / flake (push) Successful in 1m16s
2024-09-02 23:00:12 +00:00
df31ebebf8 boron: bump tmpfs to 100% of RAM
All checks were successful
flake / flake (push) Successful in 1m18s
2024-08-31 22:04:38 +01:00
2f3a33ad8e chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m15s
2024-08-30 19:01:46 +00:00
343b34b4dc boron: support sched_ext in kernel
All checks were successful
flake / flake (push) Successful in 1m45s
2024-08-30 18:52:31 +01:00
264799952e bathroom_light: trust switchbot if more recently updated
All checks were successful
flake / flake (push) Successful in 1m13s
2024-08-30 18:46:38 +01:00
5cef32cf1e gitea actions: use cache for nix
All checks were successful
flake / flake (push) Successful in 1m15s
2024-08-30 18:39:02 +01:00
6cc70e117d tywin: mount d7
All checks were successful
flake / flake (push) Successful in 1m14s
2024-08-22 15:17:11 +01:00
a52aed5778 gendry: use zram swap
All checks were successful
flake / flake (push) Successful in 1m14s
2024-08-18 13:51:28 +01:00
70b53b5c01 chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m15s
2024-08-18 10:35:15 +00:00
3d642e2320 boron: move postgresqlBackup to disk to reduce ram pressure
All checks were successful
flake / flake (push) Successful in 1m14s
2024-08-09 23:37:16 +01:00
41d5f0cc53 homeassistant: add sonos
All checks were successful
flake / flake (push) Successful in 1m17s
2024-08-08 18:31:10 +01:00
974c947130 homeassistant: add smartthings
All checks were successful
flake / flake (push) Successful in 1m15s
2024-08-04 18:15:34 +01:00
8a9498f8d7 homeassistant: expose sleep_mode to google
All checks were successful
flake / flake (push) Successful in 1m15s
2024-08-04 17:56:32 +01:00
2ecdafe1cf chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m32s
2024-08-03 12:15:23 +01:00
db5dc5aee6 step-ca: enable server on sodium and load root certs
All checks were successful
flake / flake (push) Successful in 1m14s
2024-08-01 23:28:22 +01:00
f96f03ba0c boron: update to Linux 6.10
All checks were successful
flake / flake (push) Successful in 1m13s
2024-07-27 15:16:59 +01:00
e81cad1670 chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m25s
2024-07-26 13:40:49 +00:00
67c8e3dcaf homeassistant: migrate to basnijholt/adaptive-lighting
All checks were successful
flake / flake (push) Successful in 1m14s
2024-07-22 11:16:34 +01:00
1052379119 unifi: switch to nixos module
All checks were successful
flake / flake (push) Successful in 1m24s
2024-07-19 16:43:53 +01:00
0edb8394c8 tywin: mount d6
All checks were successful
flake / flake (push) Successful in 1m14s
2024-07-17 22:19:41 +01:00
bbab551b0f be.lt: connect to Hillion WPA3 Network
All checks were successful
flake / flake (push) Successful in 1m14s
2024-07-17 17:10:08 +01:00
13c937b196 chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m15s
2024-07-17 14:14:27 +00:00
6bdaca40e0 tmux: index from 0 and always allow attach
All checks were successful
flake / flake (push) Successful in 1m14s
2024-07-17 15:02:19 +01:00
462f0eecf4 gendry: allow luks discards
All checks were successful
flake / flake (push) Successful in 1m15s
2024-07-17 09:33:33 +01:00
5dcf3b8e3f chia: update to 2.4.1
All checks were successful
flake / flake (push) Successful in 1m13s
2024-07-10 10:01:16 +01:00
b0618cd3dc chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m29s
2024-07-06 22:31:08 +00:00
a9829eea9e chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m30s
2024-06-23 10:18:56 +00:00
cfd64e9a73 chore(deps): lock file maintenance
All checks were successful
flake / flake (push) Successful in 1m29s
2024-06-16 12:13:03 +00:00
b3af1739a8 chore(deps): update actions/checkout action to v4.1.7
All checks were successful
flake / flake (push) Successful in 1m14s
2024-06-13 23:01:06 +00:00
136 changed files with 3225 additions and 1023 deletions

View File

@ -11,12 +11,9 @@ jobs:
flake:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Prepare for Nix installation
run: |
apt-get update
apt-get install -y sudo
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: DeterminateSystems/nix-installer-action@b92f66560d6f97d6576405a7bae901ab57e72b6a # v15
- uses: DeterminateSystems/magic-nix-cache-action@87b14cf437d03d37989d87f0fa5ce4f5dc1a330b # v8
- name: lint
run: |
nix fmt

View File

@ -0,0 +1,27 @@
{ config, pkgs, ... }:
{
config = {
system.stateVersion = 4;
networking.hostName = "jakehillion-mba-m2-15";
nix = {
useDaemon = true;
};
programs.zsh.enable = true;
security.pam.enableSudoTouchIdAuth = true;
environment.systemPackages = with pkgs; [
fd
htop
mosh
neovim
nix
ripgrep
sapling
];
};
}

View File

@ -2,7 +2,9 @@
"nodes": {
"agenix": {
"inputs": {
"darwin": "darwin",
"darwin": [
"darwin"
],
"home-manager": [
"home-manager"
],
@ -12,11 +14,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1716561646,
"narHash": "sha256-UIGtLO89RxKt7RF2iEgPikSdU53r6v/6WYB0RW3k89I=",
"lastModified": 1723293904,
"narHash": "sha256-b+uqzj+Wa6xgMS9aNbX4I+sXeb5biPDi39VgvSFqFvU=",
"owner": "ryantm",
"repo": "agenix",
"rev": "c2fc0762bbe8feb06a2e59a364fa81b3a57671c9",
"rev": "f6291c5935fdc4e0bef208cfc0dcab7e3f7a1c41",
"type": "github"
},
"original": {
@ -28,35 +30,53 @@
"darwin": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1700795494,
"narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
"lastModified": 1731153869,
"narHash": "sha256-3Ftf9oqOypcEyyrWJ0baVkRpvQqroK/SVBFLvU3nPuc=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
"rev": "5c74ab862c8070cbf6400128a1b56abb213656da",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1731060864,
"narHash": "sha256-aYE7oAYZ+gPU1mPNhM0JwLAQNgjf0/JK1BF1ln2KBgk=",
"owner": "nix-community",
"repo": "disko",
"rev": "5e40e02978e3bd63c2a6a9fa6fa8ba0e310e747f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
@ -72,11 +92,11 @@
]
},
"locked": {
"lastModified": 1716736833,
"narHash": "sha256-rNObca6dm7Qs524O4st8VJH6pZ/Xe1gxl+Rx6mcWYo0=",
"lastModified": 1726989464,
"narHash": "sha256-Vl+WVTJwutXkimwGprnEtXc/s/s8sMuXzqXaspIGlwM=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "a631666f5ec18271e86a5cde998cba68c33d9ac6",
"rev": "2f23fa308a7c067e52dfcc30a0758f47043ec176",
"type": "github"
},
"original": {
@ -93,11 +113,11 @@
]
},
"locked": {
"lastModified": 1717316182,
"narHash": "sha256-Xi0EpZcu39N0eW7apLjFfUOR9y80toyjYizez7J1wMI=",
"lastModified": 1730837930,
"narHash": "sha256-0kZL4m+bKBJUBQse0HanewWO0g8hDdCvBhudzxgehqc=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "9b53a10f4c91892f5af87cf55d08fba59ca086af",
"rev": "2f607e07f3ac7e53541120536708e824acccfaa8",
"type": "github"
},
"original": {
@ -108,11 +128,11 @@
},
"impermanence": {
"locked": {
"lastModified": 1708968331,
"narHash": "sha256-VUXLaPusCBvwM3zhGbRIJVeYluh2uWuqtj4WirQ1L9Y=",
"lastModified": 1730403150,
"narHash": "sha256-W1FH5aJ/GpRCOA7DXT/sJHFpa5r8sq2qAUncWwRZ3Gg=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "a33ef102a02ce77d3e39c25197664b7a636f9c30",
"rev": "0d09341beeaa2367bac5d718df1404bf2ce45e6f",
"type": "github"
},
"original": {
@ -124,11 +144,11 @@
},
"nixos-hardware": {
"locked": {
"lastModified": 1717248095,
"narHash": "sha256-e8X2eWjAHJQT82AAN+mCI0B68cIDBJpqJ156+VRrFO0=",
"lastModified": 1730919458,
"narHash": "sha256-yMO0T0QJlmT/x4HEyvrCyigGrdYfIXX3e5gWqB64wLg=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "7b49d3967613d9aacac5b340ef158d493906ba79",
"rev": "e1cc1f6483393634aee94514186d21a4871e78d7",
"type": "github"
},
"original": {
@ -139,11 +159,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1717144377,
"narHash": "sha256-F/TKWETwB5RaR8owkPPi+SPJh83AQsm6KrQAlJ8v/uA=",
"lastModified": 1730963269,
"narHash": "sha256-rz30HrFYCHiWEBCKHMffHbMdWJ35hEkcRVU0h7ms3x0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "805a384895c696f802a9bf5bf4720f37385df547",
"rev": "83fb6c028368e465cd19bb127b86f971a5e41ebc",
"type": "github"
},
"original": {
@ -155,23 +175,22 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1716948383,
"narHash": "sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ad57eef4ef0659193044870c731987a6df5cf56b",
"type": "github"
"lastModified": 1730867498,
"narHash": "sha256-Ce3a1w7Qf+UEPjVJcXxeSiWyPMngqf1M2EIsmqiluQw=",
"rev": "9240e11a83307a6e8cf2254340782cba4aa782fd",
"type": "tarball",
"url": "https://gitea.hillion.co.uk/api/v1/repos/JakeHillion/nixpkgs/archive/9240e11a83307a6e8cf2254340782cba4aa782fd.tar.gz"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
"type": "tarball",
"url": "https://gitea.hillion.co.uk/JakeHillion/nixpkgs/archive/nixos-unstable.tar.gz"
}
},
"root": {
"inputs": {
"agenix": "agenix",
"darwin": "darwin",
"disko": "disko",
"flake-utils": "flake-utils",
"home-manager": "home-manager",
"home-manager-unstable": "home-manager-unstable",

124
flake.nix
View File

@ -1,14 +1,18 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs-unstable.url = "https://gitea.hillion.co.uk/JakeHillion/nixpkgs/archive/nixos-unstable.tar.gz";
nixos-hardware.url = "github:nixos/nixos-hardware";
flake-utils.url = "github:numtide/flake-utils";
darwin.url = "github:lnl7/nix-darwin";
darwin.inputs.nixpkgs.follows = "nixpkgs";
agenix.url = "github:ryantm/agenix";
agenix.inputs.nixpkgs.follows = "nixpkgs";
agenix.inputs.darwin.follows = "darwin";
agenix.inputs.home-manager.follows = "home-manager";
home-manager.url = "github:nix-community/home-manager/release-24.05";
@ -17,51 +21,89 @@
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
impermanence.url = "github:nix-community/impermanence/master";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
};
description = "Hillion Nix flake";
outputs = { self, nixpkgs, nixpkgs-unstable, nixos-hardware, flake-utils, agenix, home-manager, home-manager-unstable, impermanence, ... }@inputs: {
nixosConfigurations =
let
fqdns = builtins.attrNames (builtins.readDir ./hosts);
getSystemOverlays = system: nixpkgsConfig: [
(final: prev: {
"storj" = final.callPackage ./pkgs/storj.nix { };
})
];
mkHost = fqdn:
let
system = builtins.readFile ./hosts/${fqdn}/system;
func = if builtins.pathExists ./hosts/${fqdn}/unstable then nixpkgs-unstable.lib.nixosSystem else nixpkgs.lib.nixosSystem;
home-manager-pick = if builtins.pathExists ./hosts/${fqdn}/unstable then home-manager-unstable else home-manager;
in
func {
inherit system;
specialArgs = inputs;
modules = [
./hosts/${fqdn}/default.nix
./modules/default.nix
outputs =
{ self
, agenix
, darwin
, disko
, flake-utils
, home-manager
, home-manager-unstable
, impermanence
, nixos-hardware
, nixpkgs
, nixpkgs-unstable
, ...
}@inputs:
let
getSystemOverlays = system: nixpkgsConfig: [
(final: prev: {
unstable = nixpkgs-unstable.legacyPackages.${prev.system};
agenix.nixosModules.default
impermanence.nixosModules.impermanence
"storj" = final.callPackage ./pkgs/storj.nix { };
})
];
in
{
nixosConfigurations =
let
fqdns = builtins.attrNames (builtins.readDir ./hosts);
mkHost = fqdn:
let
system = builtins.readFile ./hosts/${fqdn}/system;
func = if builtins.pathExists ./hosts/${fqdn}/unstable then nixpkgs-unstable.lib.nixosSystem else nixpkgs.lib.nixosSystem;
home-manager-pick = if builtins.pathExists ./hosts/${fqdn}/unstable then home-manager-unstable else home-manager;
in
func {
inherit system;
specialArgs = inputs;
modules = [
./hosts/${fqdn}/default.nix
./modules/default.nix
home-manager-pick.nixosModules.default
{
home-manager.sharedModules = [
impermanence.nixosModules.home-manager.impermanence
];
}
agenix.nixosModules.default
impermanence.nixosModules.impermanence
disko.nixosModules.disko
({ config, ... }: {
system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;
nixpkgs.overlays = getSystemOverlays config.nixpkgs.hostPlatform.system config.nixpkgs.config;
})
];
};
in
nixpkgs.lib.genAttrs fqdns mkHost;
} // flake-utils.lib.eachDefaultSystem (system: {
formatter = nixpkgs.legacyPackages.${system}.nixpkgs-fmt;
});
home-manager-pick.nixosModules.default
{
home-manager.sharedModules = [
impermanence.nixosModules.home-manager.impermanence
];
}
({ config, ... }: {
system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;
nixpkgs.overlays = getSystemOverlays config.nixpkgs.hostPlatform.system config.nixpkgs.config;
})
];
};
in
nixpkgs.lib.genAttrs fqdns mkHost;
darwinConfigurations = {
jakehillion-mba-m2-15 = darwin.lib.darwinSystem {
system = "aarch64-darwin";
specialArgs = inputs;
modules = [
./darwin/jakehillion-mba-m2-15/configuration.nix
({ config, ... }: {
nixpkgs.overlays = getSystemOverlays "aarch64-darwin" config.nixpkgs.config;
})
];
};
};
} // flake-utils.lib.eachDefaultSystem (system: {
formatter = nixpkgs.legacyPackages.${system}.nixpkgs-fmt;
});
}

View File

@ -24,6 +24,17 @@
];
};
## WiFi
age.secrets."wifi/be.lt.ts.hillion.co.uk".file = ../../secrets/wifi/be.lt.ts.hillion.co.uk.age;
networking.wireless = {
enable = true;
environmentFile = config.age.secrets."wifi/be.lt.ts.hillion.co.uk".path;
networks = {
"Hillion WPA3 Network".psk = "@HILLION_WPA3_NETWORK_PSK@";
};
};
## Desktop
custom.users.jake.password = true;
custom.desktop.awesome.enable = true;

View File

@ -34,14 +34,27 @@
### Explicitly use the latest kernel at time of writing because the LTS
### kernels available in NixOS do not seem to support this server's very
### modern hardware.
boot.kernelPackages = pkgs.linuxPackages_6_9;
### custom.sched_ext.enable implies >=6.12, if this is removed the kernel may need to be pinned again. >=6.10 seems good.
custom.sched_ext.enable = true;
## Enable btrfs compression
fileSystems."/data".options = [ "compress=zstd" ];
fileSystems."/nix".options = [ "compress=zstd" ];
## Impermanence
custom.impermanence.enable = true;
custom.impermanence = {
enable = true;
cache.enable = true;
userExtraFiles.jake = [
".ssh/id_ecdsa"
".ssh/id_rsa"
];
};
boot.initrd.postDeviceCommands = lib.mkAfter ''
btrfs subvolume delete /cache/system
btrfs subvolume snapshot /cache/empty_snapshot /cache/system
'';
## Custom Services
custom = {
@ -75,6 +88,42 @@
fileSystems = [ "/data" ];
};
## Resilio
custom.resilio = {
enable = true;
folders =
let
folderNames = [
"dad"
"joseph"
"projects"
"resources"
"sync"
];
mkFolder = name: {
name = name;
secret = {
name = "resilio/plain/${name}";
file = ../../secrets/resilio/plain/${name}.age;
};
};
in
builtins.map (mkFolder) folderNames;
};
services.resilio.directoryRoot = "/data/sync";
## General usability
### Make podman available for dev tools such as act
virtualisation = {
containers.enable = true;
podman = {
enable = true;
dockerCompat = true;
dockerSocket.enable = true;
};
};
users.users.jake.extraGroups = [ "podman" ];
## Networking
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = true;

View File

@ -18,7 +18,7 @@
{
device = "tmpfs";
fsType = "tmpfs";
options = [ "mode=0755" ];
options = [ "mode=0755" "size=100%" ];
};
fileSystems."/boot" =
@ -35,6 +35,13 @@
options = [ "subvol=data" ];
};
fileSystems."/cache" =
{
device = "/dev/disk/by-uuid/9aebe351-156a-4aa0-9a97-f09b01ac23ad";
fsType = "btrfs";
options = [ "subvol=cache" ];
};
fileSystems."/nix" =
{
device = "/dev/disk/by-uuid/9aebe351-156a-4aa0-9a97-f09b01ac23ad";

View File

@ -0,0 +1,7 @@
# gendry.jakehillion-terminals.ts.hillion.co.uk
Additional installation step for Clevis/Tang:
$ echo -n $DISK_ENCRYPTION_PASSWORD | clevis encrypt sss "$(cat /etc/nixos/hosts/gendry.jakehillion-terminals.ts.hillion.co.uk/clevis_config.json)" >/mnt/data/disk_encryption.jwe
$ sudo chown root:root /mnt/data/disk_encryption.jwe
$ sudo chmod 0400 /mnt/data/disk_encryption.jwe

View File

@ -15,8 +15,24 @@
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
];
boot.initrd = {
availableKernelModules = [ "r8169" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices."root".secretFile = "/data/disk_encryption.jwe";
};
};
custom.defaults = true;
## Custom scheduler
custom.sched_ext.enable = true;
## Impermanence
custom.impermanence = {
enable = true;
@ -29,6 +45,13 @@
];
};
## Enable ZRAM swap to help with root on tmpfs
zramSwap = {
enable = true;
memoryPercent = 200;
algorithm = "zstd";
};
## Desktop
custom.users.jake.password = true;
custom.desktop.awesome.enable = true;
@ -36,9 +59,7 @@
## Resilio
custom.resilio.enable = true;
services.resilio.deviceName = "gendry.jakehillion-terminals";
services.resilio.directoryRoot = "/data/sync";
services.resilio.storagePath = "/data/sync/.sync";
custom.resilio.folders =
let

View File

@ -28,7 +28,10 @@
options = [ "subvol=nix" ];
};
boot.initrd.luks.devices."root".device = "/dev/disk/by-uuid/af328e8d-d929-43f1-8d04-1c96b5147e5e";
boot.initrd.luks.devices."root" = {
device = "/dev/disk/by-uuid/af328e8d-d929-43f1-8d04-1c96b5147e5e";
allowDiscards = true;
};
fileSystems."/data" =
{

View File

@ -0,0 +1,75 @@
{ config, pkgs, lib, ... }:
{
imports = [
./disko.nix
./hardware-configuration.nix
];
config = {
system.stateVersion = "24.05";
networking.hostName = "merlin";
networking.domain = "rig.ts.hillion.co.uk";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
# zswap
"zswap.enabled=1"
"zswap.compressor=zstd"
"zswap.max_pool_percent=20"
];
boot.initrd = {
availableKernelModules = [ "igc" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices = {
"disk0-crypt".secretFile = "/data/disk_encryption.jwe";
};
};
};
boot.kernelPackages = pkgs.linuxPackages_latest;
custom.defaults = true;
custom.locations.autoServe = true;
custom.impermanence.enable = true;
custom.users.jake.password = true;
security.sudo.wheelNeedsPassword = lib.mkForce true;
# Networking
networking = {
interfaces.enp171s0.name = "eth0";
interfaces.enp172s0.name = "eth1";
};
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [ ];
allowedUDPPorts = lib.mkForce [ ];
};
};
};
## Tailscale
age.secrets."tailscale/merlin.rig.ts.hillion.co.uk".file = ../../secrets/tailscale/merlin.rig.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/merlin.rig.ts.hillion.co.uk".path;
};
};
}

View File

@ -0,0 +1,70 @@
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
disk0-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk0-crypt";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
subvolumes = {
"/data" = {
mountpoint = "/data";
mountOptions = [ "compress=zstd" "ssd" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "ssd" ];
};
};
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"size=100%"
];
};
};
};
}

View File

@ -0,0 +1,28 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usbhid" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp171s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp172s0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp173s0f0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@ -23,12 +23,6 @@
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/microserver.home.ts.hillion.co.uk".path;
useRoutingFeatures = "server";
extraUpFlags = [
"--advertise-routes"
"10.64.50.0/24,10.239.19.0/24"
"--advertise-exit-node"
];
};
## Enable IoT VLAN
@ -43,11 +37,6 @@
bluetooth.enable = true;
};
## Enable IP forwarding for Tailscale
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = true;
};
## Run a persistent iperf3 server
services.iperf3.enable = true;
services.iperf3.openFirewall = true;
@ -56,11 +45,9 @@
networking.firewall.interfaces = {
"eth0" = {
allowedUDPPorts = [
5353 # HomeKit
];
allowedTCPPorts = [
7654 # Tang
21063 # HomeKit
];
};
};

View File

@ -0,0 +1,7 @@
# phoenix.st.ts.hillion.co.uk
Additional installation step for Clevis/Tang:
$ echo -n $DISK_ENCRYPTION_PASSWORD | clevis encrypt sss "$(cat /etc/nixos/hosts/phoenix.st.ts.hillion.co.uk/clevis_config.json)" >/mnt/data/disk_encryption.jwe
$ sudo chown root:root /mnt/data/disk_encryption.jwe
$ sudo chmod 0400 /mnt/data/disk_encryption.jwe

View File

@ -0,0 +1,14 @@
{
"t": 1,
"pins": {
"tang": [
{
"url": "http://10.64.50.21:7654"
},
{
"url": "http://10.64.50.25:7654"
}
]
}
}

View File

@ -0,0 +1,168 @@
{ config, pkgs, lib, ... }:
let
zpool_name = "practical-defiant-coffee";
in
{
imports = [
./disko.nix
./hardware-configuration.nix
];
config = {
system.stateVersion = "24.05";
networking.hostName = "phoenix";
networking.domain = "st.ts.hillion.co.uk";
networking.hostId = "4d7241e9";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
"zfs.zfs_arc_max=34359738368"
# zswap
"zswap.enabled=1"
"zswap.compressor=zstd"
"zswap.max_pool_percent=20"
];
boot.initrd = {
availableKernelModules = [ "igc" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices = {
"disk0-crypt".secretFile = "/data/disk_encryption.jwe";
"disk1-crypt".secretFile = "/data/disk_encryption.jwe";
};
};
};
custom.defaults = true;
custom.locations.autoServe = true;
custom.impermanence.enable = true;
custom.users.jake.password = true; # TODO: remove me once booting has stabilised
## Filesystems
boot.supportedFilesystems = [ "zfs" ];
boot.zfs = {
forceImportRoot = false;
extraPools = [ zpool_name ];
};
services.btrfs.autoScrub = {
enable = true;
interval = "Tue, 02:00";
# All filesystems includes the BTRFS parts of all the hard drives. This
# would take forever and is redundant as they get fully read regularly.
fileSystems = [ "/data" ];
};
services.zfs.autoScrub = {
enable = true;
interval = "Wed, 02:00";
};
## Resilio
custom.resilio = {
enable = true;
backups.enable = true;
folders =
let
folderNames = [
"dad"
"joseph"
"projects"
"resources"
"sync"
];
mkFolder = name: {
name = name;
secret = {
name = "resilio/plain/${name}";
file = ../../secrets/resilio/plain/${name}.age;
};
};
in
builtins.map (mkFolder) folderNames;
};
services.resilio.directoryRoot = "/${zpool_name}/sync";
## Chia
age.secrets."chia/farmer.key" = {
file = ../../secrets/chia/farmer.key.age;
owner = "chia";
group = "chia";
};
custom.chia = {
enable = true;
keyFile = config.age.secrets."chia/farmer.key".path;
plotDirectories = builtins.genList (i: "/mnt/d${toString i}/plots/contract-k32") 8;
};
## Restic
custom.services.restic.path = "/${zpool_name}/backups/restic";
## Backups
### Git
custom.backups.git = {
enable = true;
extraRepos = [ "https://gitea.hillion.co.uk/JakeHillion/nixos.git" ];
};
## Downloads
custom.services.downloads = {
metadataPath = "/${zpool_name}/downloads/metadata";
downloadCachePath = "/${zpool_name}/downloads/torrents";
filmsPath = "/${zpool_name}/media/films";
tvPath = "/${zpool_name}/media/tv";
};
## Plex
users.users.plex.extraGroups = [ "mediaaccess" ];
services.plex.enable = true;
## Networking
networking = {
interfaces.enp4s0.name = "eth0";
interfaces.enp5s0.name = "eth1";
interfaces.enp6s0.name = "eth2";
interfaces.enp8s0.name = "eth3";
vlans = {
cameras = {
id = 3;
interface = "eth0";
};
};
};
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [
32400 # Plex
];
allowedUDPPorts = lib.mkForce [ ];
};
};
};
## Tailscale
age.secrets."tailscale/phoenix.st.ts.hillion.co.uk".file = ../../secrets/tailscale/phoenix.st.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/phoenix.st.ts.hillion.co.uk".path;
};
};
}

View File

@ -0,0 +1,103 @@
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
disk0-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk0-crypt";
settings = {
allowDiscards = true;
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
disk1 = {
type = "disk";
device = "/dev/nvme1n1";
content = {
type = "gpt";
partitions = {
disk1-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk1-crypt";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
extraArgs = [
"-d raid1"
"/dev/mapper/disk0-crypt"
];
subvolumes = {
"/data" = {
mountpoint = "/data";
mountOptions = [ "compress=zstd" "ssd" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "ssd" ];
};
};
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"size=100%"
];
};
};
};
}

View File

@ -9,28 +9,11 @@
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.availableKernelModules = [ "nvme" "ahci" "xhci_pci" "thunderbolt" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{
device = "/dev/disk/by-uuid/cb48d4ed-d268-490c-9977-2b5d31ce2c1b";
fsType = "btrfs";
};
boot.initrd.luks.devices."root" = {
device = "/dev/disk/by-uuid/32837730-5e15-4917-9939-cbb58bb0aabf";
allowDiscards = true;
};
fileSystems."/boot" =
{
device = "/dev/disk/by-uuid/BC57-0AF6";
fsType = "vfat";
};
fileSystems."/mnt/d0" =
{
device = "/dev/disk/by-uuid/9136434d-d883-4118-bd01-903f720e5ce1";
@ -67,19 +50,28 @@
fsType = "btrfs";
};
fileSystems."/export/ccache" = {
device = "/data/ccache";
options = [ "bind" ];
};
fileSystems."/mnt/d6" =
{
device = "/dev/disk/by-uuid/b461e07d-39ab-46b4-b1d1-14c2e0791915";
fsType = "btrfs";
};
swapDevices = [ ];
fileSystems."/mnt/d7" =
{
device = "/dev/disk/by-uuid/eb8d32d0-e506-449b-8dbc-585ba05c4252";
fsType = "btrfs";
};
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp7s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp4s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp5s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp6s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp8s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@ -0,0 +1 @@
x86_64-linux

View File

@ -32,6 +32,14 @@
nat.enable = lib.mkForce false;
useDHCP = false;
vlans = {
cameras = {
id = 3;
interface = "eth2";
};
};
interfaces = {
enp1s0 = {
name = "eth0";
@ -56,6 +64,14 @@
}
];
};
cameras /* cameras@eth2 */ = {
ipv4.addresses = [
{
address = "10.133.145.1";
prefixLength = 24;
}
];
};
enp4s0 = { name = "eth3"; };
enp5s0 = { name = "eth4"; };
enp6s0 = { name = "eth5"; };
@ -82,8 +98,10 @@
ip protocol icmp counter accept comment "accept all ICMP types"
iifname "eth0" ct state { established, related } counter accept
iifname "eth0" drop
iifname "eth0" tcp dport 22 counter accept comment "SSH"
iifname { "eth0", "cameras" } ct state { established, related } counter accept
iifname { "eth0", "cameras" } drop
}
chain forward {
@ -92,6 +110,7 @@
iifname {
"eth1",
"eth2",
"tailscale0",
} oifname {
"eth0",
} counter accept comment "Allow trusted LAN to WAN"
@ -101,12 +120,13 @@
} oifname {
"eth1",
"eth2",
} ct state established,related counter accept comment "Allow established back to LANs"
"tailscale0",
} ct state { established,related } counter accept comment "Allow established back to LANs"
ip daddr 10.64.50.20 tcp dport 32400 counter accept comment "Plex"
ip daddr 10.64.50.20 tcp dport 8444 counter accept comment "Chia"
iifname "tailscale0" oifname { "eth1", "eth2" } counter accept comment "Allow LAN access from Tailscale"
iifname { "eth1", "eth2" } oifname "tailscale0" ct state { established,related } counter accept comment "Allow established back to Tailscale"
ip daddr 10.64.50.27 tcp dport 32400 counter accept comment "Plex"
ip daddr 10.64.50.21 tcp dport 7654 counter accept comment "Tang"
}
}
@ -115,16 +135,17 @@
chain prerouting {
type nat hook prerouting priority filter; policy accept;
iifname eth0 tcp dport 32400 counter dnat to 10.64.50.20
iifname eth0 tcp dport 8444 counter dnat to 10.64.50.20
iifname eth0 tcp dport 32400 counter dnat to 10.64.50.27
iifname eth0 tcp dport 7654 counter dnat to 10.64.50.21
}
chain postrouting {
type nat hook postrouting priority filter; policy accept;
oifname "eth0" masquerade
iifname tailscale0 oifname eth1 snat to 10.64.50.1
iifname tailscale0 oifname eth2 snat to 10.239.19.1
}
}
'';
@ -138,7 +159,7 @@
settings = {
interfaces-config = {
interfaces = [ "eth1" "eth2" ];
interfaces = [ "eth1" "eth2" "cameras" ];
};
lease-database = {
type = "memfile";
@ -195,18 +216,22 @@
data = "10.64.50.1, 1.1.1.1, 8.8.8.8";
}
];
reservations = lib.lists.imap0
(i: el: {
reservations = lib.lists.remove null (lib.lists.imap0
(i: el: if el == null then null else {
ip-address = "10.64.50.${toString (20 + i)}";
inherit (el) hw-address hostname;
}) [
{ hostname = "tywin"; hw-address = "c8:7f:54:6d:e1:03"; }
null
{ hostname = "microserver"; hw-address = "e4:5f:01:b4:58:95"; }
{ hostname = "theon"; hw-address = "00:1e:06:49:06:1e"; }
{ hostname = "server-switch"; hw-address = "84:d8:1b:9d:0d:85"; }
{ hostname = "apc-ap7921"; hw-address = "00:c0:b7:6b:f4:34"; }
{ hostname = "sodium"; hw-address = "d8:3a:dd:c3:d6:2b"; }
];
{ hostname = "gendry"; hw-address = "18:c0:4d:35:60:1e"; }
{ hostname = "phoenix"; hw-address = "a8:b8:e0:04:17:a5"; }
{ hostname = "merlin"; hw-address = "b0:41:6f:13:20:14"; }
{ hostname = "stinger"; hw-address = "7c:83:34:be:30:dd"; }
]);
}
{
subnet = "10.239.19.0/24";
@ -230,17 +255,58 @@
];
reservations = [
{
# bedroom-everything-presence-one
hw-address = "40:22:d8:e0:1d:50";
ip-address = "10.239.19.2";
hostname = "bedroom-everything-presence-one";
}
{
# living-room-everything-presence-one
hw-address = "40:22:d8:e0:0f:78";
ip-address = "10.239.19.3";
hostname = "living-room-everything-presence-one";
}
{
hw-address = "a0:7d:9c:b0:f0:14";
ip-address = "10.239.19.4";
hostname = "hallway-wall-tablet";
}
{
hw-address = "d8:3a:dd:c3:d6:2b";
ip-address = "10.239.19.5";
hostname = "sodium";
}
{
hw-address = "48:da:35:6f:f2:4b";
ip-address = "10.239.19.6";
hostname = "hammer";
}
{
hw-address = "48:da:35:6f:83:b8";
ip-address = "10.239.19.7";
hostname = "charlie";
}
];
}
{
subnet = "10.133.145.0/24";
interface = "cameras";
pools = [{
pool = "10.133.145.64 - 10.133.145.254";
}];
option-data = [
{
name = "routers";
data = "10.133.145.1";
}
{
name = "broadcast-address";
data = "10.133.145.255";
}
{
name = "domain-name-servers";
data = "1.1.1.1, 8.8.8.8";
}
];
reservations = [
];
}
];
@ -284,6 +350,13 @@
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/router.home.ts.hillion.co.uk".path;
useRoutingFeatures = "server";
extraSetFlags = [
"--advertise-routes"
"10.64.50.0/24,10.239.19.0/24,10.133.145.0/24"
"--advertise-exit-node"
"--netfilter-mode=off"
];
};
## Enable btrfs compression
@ -307,9 +380,34 @@
};
services.caddy = {
enable = true;
virtualHosts."http://graphs.router.home.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = "reverse_proxy unix///run/netdata/netdata.sock";
virtualHosts = {
"graphs.router.home.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy unix///run/netdata/netdata.sock
'';
};
"hammer.kvm.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://10.239.19.6
'';
};
"charlie.kvm.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://10.239.19.7
'';
};
};
};
users.users.caddy.extraGroups = [ "netdata" ];

View File

@ -1,4 +1,4 @@
{ config, pkgs, nixpkgs-unstable, lib, nixos-hardware, ... }:
{ config, pkgs, lib, nixos-hardware, ... }:
{
imports = [
@ -22,14 +22,19 @@
fileSystems."/nix".options = [ "compress=zstd" ];
## Impermanence
custom.impermanence.enable = true;
custom.impermanence = {
enable = true;
cache.enable = true;
};
boot.initrd.postDeviceCommands = lib.mkAfter ''
btrfs subvolume delete /cache/tmp
btrfs subvolume snapshot /cache/empty_snapshot /cache/tmp
chmod 0777 /cache/tmp
chmod +t /cache/tmp
chmod 1777 /cache/tmp
'';
## CA server
custom.ca.service.enable = true;
### nix only supports build-dir from 2.22. bind mount /tmp to something persistent instead.
fileSystems."/tmp" = {
device = "/cache/tmp";
@ -41,29 +46,19 @@
# };
# };
## Use ccache for building the Linux kernel
programs.ccache.enable = true;
nix.settings.extra-sandbox-paths = [ config.programs.ccache.cacheDir ];
fileSystems."${config.programs.ccache.cacheDir}" = {
device = "${config.custom.dns.authoritative.ipv4.uk.co.hillion.ts.storage.tywin}:/ccache";
fsType = "nfs";
options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ];
};
programs.ccache.packageNames = [
# linux_rpi5 overrides from linux_rpi4
"linux_rpi4"
];
## Custom Services
custom.locations.autoServe = true;
custom.www.home.enable = true;
custom.www.iot.enable = true;
custom.services.isponsorblocktv.enable = true;
# Networking
networking = {
useDHCP = false;
interfaces = {
end0 = {
name = "eth0";
useDHCP = true;
interfaces.end0.name = "eth0";
vlans = {
iot = {
id = 2;
interface = "eth0";
};
};
};
@ -72,14 +67,27 @@
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [
80 # HTTP 1-2
443 # HTTPS 1-2
7654 # Tang
];
allowedUDPPorts = lib.mkForce [
443 # HTTP 3
];
};
iot = {
allowedTCPPorts = lib.mkForce [
80 # HTTP 1-2
443 # HTTPS 1-2
];
allowedUDPPorts = lib.mkForce [
443 # HTTP 3
];
};
};

View File

@ -0,0 +1,84 @@
{ config, pkgs, lib, ... }:
{
imports = [
./disko.nix
./hardware-configuration.nix
];
config = {
system.stateVersion = "24.05";
networking.hostName = "stinger";
networking.domain = "pop.ts.hillion.co.uk";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
# zswap
"zswap.enabled=1"
"zswap.compressor=zstd"
"zswap.max_pool_percent=20"
];
boot.initrd = {
availableKernelModules = [ "r8169" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices = {
"disk0-crypt".secretFile = "/data/disk_encryption.jwe";
};
};
};
custom.defaults = true;
custom.locations.autoServe = true;
custom.impermanence.enable = true;
hardware = {
bluetooth.enable = true;
};
# Networking
networking = {
interfaces.enp1s0.name = "eth0";
vlans = {
iot = {
id = 2;
interface = "eth0";
};
};
};
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [
1400 # HA Sonos
21063 # HomeKit
];
allowedUDPPorts = lib.mkForce [
5353 # HomeKit
];
};
};
};
## Tailscale
age.secrets."tailscale/stinger.pop.ts.hillion.co.uk".file = ../../secrets/tailscale/stinger.pop.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/stinger.pop.ts.hillion.co.uk".path;
};
};
}

View File

@ -0,0 +1,70 @@
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
disk0-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk0-crypt";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
subvolumes = {
"/data" = {
mountpoint = "/data";
mountOptions = [ "compress=zstd" "ssd" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "ssd" ];
};
};
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"size=100%"
];
};
};
};
}

View File

@ -0,0 +1,28 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp0s20f0u2.useDHCP = lib.mkDefault true;
# networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@ -0,0 +1 @@
x86_64-linux

View File

@ -1,7 +0,0 @@
# tywin.storage.ts.hillion.co.uk
Additional installation step for Clevis/Tang:
$ echo -n $DISK_ENCRYPTION_PASSWORD | clevis encrypt sss "$(cat /etc/nixos/hosts/tywin.storage.ts.hillion.co.uk/clevis_config.json)" >/mnt/disk_encryption.jwe
$ sudo chown root:root /mnt/disk_encryption.jwe
$ sudo chmod 0400 /mnt/disk_encryption.jwe

View File

@ -1,265 +0,0 @@
{ config, pkgs, lib, ... }:
{
imports = [
./hardware-configuration.nix
];
config = {
system.stateVersion = "22.11";
networking.hostName = "tywin";
networking.domain = "storage.ts.hillion.co.uk";
networking.hostId = "2a9b6df5";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
"zfs.zfs_arc_max=25769803776"
];
boot.initrd = {
availableKernelModules = [ "r8169" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices."root".secretFile = "/disk_encryption.jwe";
};
};
custom.locations.autoServe = true;
custom.defaults = true;
# zram swap: used in the hope it will give the ZFS ARC more room to back off
zramSwap = {
enable = true;
memoryPercent = 200;
algorithm = "zstd";
};
## Tailscale
age.secrets."tailscale/tywin.storage.ts.hillion.co.uk".file = ../../secrets/tailscale/tywin.storage.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/tywin.storage.ts.hillion.co.uk".path;
};
## Filesystems
fileSystems."/".options = [ "compress=zstd" ];
boot.supportedFilesystems = [ "zfs" ];
boot.zfs = {
forceImportRoot = false;
extraPools = [ "data" ];
};
services.btrfs.autoScrub = {
enable = true;
interval = "Tue, 02:00";
# All filesystems includes the BTRFS parts of all the hard drives. This
# would take forever and is redundant as they get fully read regularly.
fileSystems = [ "/" ];
};
services.zfs.autoScrub = {
enable = true;
interval = "Wed, 02:00";
};
## NFS
services.nfs.server = {
enable = true;
exports = ''
/export ${config.custom.dns.authoritative.ipv4.uk.co.hillion.ts.pop.sodium}(ro,fsid=0,no_subtree_check)
/export/ccache ${config.custom.dns.authoritative.ipv4.uk.co.hillion.ts.pop.sodium}(rw,no_subtree_check)
'';
};
## Backups
### Git
age.secrets."git/git_backups_ecdsa".file = ../../secrets/git/git_backups_ecdsa.age;
age.secrets."git/git_backups_remotes".file = ../../secrets/git/git_backups_remotes.age;
custom.backups.git = {
enable = true;
sshKey = config.age.secrets."git/git_backups_ecdsa".path;
reposFile = config.age.secrets."git/git_backups_remotes".path;
repos = [ "https://gitea.hillion.co.uk/JakeHillion/nixos.git" ];
};
## Resilio
custom.resilio.enable = true;
services.resilio.deviceName = "tywin.storage";
services.resilio.directoryRoot = "/data/users/jake/sync";
services.resilio.storagePath = "/data/users/jake/sync/.sync";
custom.resilio.folders =
let
folderNames = [
"dad"
"joseph"
"projects"
"resources"
"sync"
];
mkFolder = name: {
name = name;
secret = {
name = "resilio/plain/${name}";
file = ../../secrets/resilio/plain/${name}.age;
};
};
in
builtins.map (mkFolder) folderNames;
age.secrets."resilio/restic/128G.key" = {
file = ../../secrets/restic/128G.age;
owner = "rslsync";
group = "rslsync";
};
services.restic.backups."sync" = {
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/128G";
user = "rslsync";
passwordFile = config.age.secrets."resilio/restic/128G.key".path;
timerConfig = {
Persistent = true;
OnUnitInactiveSec = "15m";
RandomizedDelaySec = "5m";
};
paths = [ "/data/users/jake/sync" ];
exclude = [
"/data/users/jake/sync/.sync"
"/data/users/jake/sync/*/.sync"
"/data/users/jake/sync/resources/media/films"
"/data/users/jake/sync/resources/media/iso"
"/data/users/jake/sync/resources/media/tv"
"/data/users/jake/sync/dad/media"
];
};
## Restic
age.secrets."restic/128G.key" = {
file = ../../secrets/restic/128G.age;
owner = "restic";
group = "restic";
};
age.secrets."restic/1.6T.key" = {
file = ../../secrets/restic/1.6T.age;
owner = "restic";
group = "restic";
};
services.restic.server = {
enable = true;
appendOnly = true;
extraFlags = [ "--no-auth" ];
dataDir = "/data/backups/restic";
listenAddress = "127.0.0.1:8000"; # TODO: can this be a Unix socket?
};
services.caddy = {
enable = true;
virtualHosts."http://restic.tywin.storage.ts.hillion.co.uk".extraConfig = ''
bind ${config.custom.dns.tailscale.ipv4} ${config.custom.dns.tailscale.ipv6}
reverse_proxy http://localhost:8000
'';
};
### HACK: Allow Caddy to restart if it fails. This happens because Tailscale
### is too late at starting. Upstream nixos caddy does restart on failure
### but it's prevented on exit code 1. Set the exit code to 0 (non-failure)
### to override this.
systemd.services.caddy = {
requires = [ "tailscaled.service" ];
after = [ "tailscaled.service" ];
serviceConfig = {
RestartPreventExitStatus = lib.mkForce 0;
};
};
services.restic.backups."prune-128G" = {
repository = "/data/backups/restic/128G";
user = "restic";
passwordFile = config.age.secrets."restic/128G.key".path;
timerConfig = {
Persistent = true;
OnCalendar = "02:30";
RandomizedDelaySec = "1h";
};
pruneOpts = [
"--keep-last 48"
"--keep-within-hourly 7d"
"--keep-within-daily 1m"
"--keep-within-weekly 6m"
"--keep-within-monthly 24m"
];
};
services.restic.backups."prune-1.6T" = {
repository = "/data/backups/restic/1.6T";
user = "restic";
passwordFile = config.age.secrets."restic/1.6T.key".path;
timerConfig = {
Persistent = true;
OnCalendar = "Wed, 02:30";
RandomizedDelaySec = "4h";
};
pruneOpts = [
"--keep-within-daily 14d"
"--keep-within-weekly 2m"
"--keep-within-monthly 18m"
];
};
## Chia
age.secrets."chia/farmer.key" = {
file = ../../secrets/chia/farmer.key.age;
owner = "chia";
group = "chia";
};
custom.chia = {
enable = true;
openFirewall = true;
keyFile = config.age.secrets."chia/farmer.key".path;
plotDirectories = builtins.genList (i: "/mnt/d${toString i}/plots/contract-k32") 7;
};
## Downloads
custom.services.downloads = {
metadataPath = "/data/downloads/metadata";
downloadCachePath = "/data/downloads/torrents";
filmsPath = "/data/media/films";
tvPath = "/data/media/tv";
};
## Plex
users.users.plex.extraGroups = [ "mediaaccess" ];
services.plex = {
enable = true;
openFirewall = true;
};
## Networking
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [
];
allowedUDPPorts = lib.mkForce [
];
};
};
};
};
}

View File

@ -2,7 +2,7 @@
{
imports = [
./git.nix
./git/default.nix
./homeassistant.nix
./matrix.nix
];

View File

@ -7,25 +7,17 @@ in
options.custom.backups.git = {
enable = lib.mkEnableOption "git";
repos = lib.mkOption {
extraRepos = lib.mkOption {
description = "A list of remotes to clone.";
type = with lib.types; listOf str;
default = [ ];
};
reposFile = lib.mkOption {
description = "A file containing the remotes to clone, one per line.";
type = with lib.types; nullOr str;
default = null;
};
sshKey = lib.mkOption {
description = "SSH private key to use when cloning repositories over SSH.";
type = with lib.types; nullOr str;
default = null;
};
};
config = lib.mkIf cfg.enable {
age.secrets."git-backups/restic/128G".file = ../../secrets/restic/128G.age;
age.secrets."git/git_backups_ecdsa".file = ../../../secrets/git/git_backups_ecdsa.age;
age.secrets."git/git_backups_remotes".file = ../../../secrets/git/git_backups_remotes.age;
age.secrets."git-backups/restic/128G".file = ../../../secrets/restic/128G.age;
systemd.services.backup-git = {
description = "Git repo backup service.";
@ -37,9 +29,10 @@ in
WorkingDirectory = "%C/backup-git";
LoadCredential = [
"id_ecdsa:${config.age.secrets."git/git_backups_ecdsa".path}"
"repos_file:${config.age.secrets."git/git_backups_remotes".path}"
"restic_password:${config.age.secrets."git-backups/restic/128G".path}"
] ++ (if cfg.sshKey == null then [ ] else [ "id_ecdsa:${cfg.sshKey}" ])
++ (if cfg.reposFile == null then [ ] else [ "repos_file:${cfg.reposFile}" ]);
];
};
environment = {
@ -48,11 +41,12 @@ in
};
script = ''
set -x
shopt -s nullglob
# Read and deduplicate repos
${if cfg.reposFile == null then "" else "readarray -t raw_repos < $CREDENTIALS_DIRECTORY/repos_file"}
declare -A repos=(${builtins.concatStringsSep " " (builtins.map (x : "[${x}]=1") cfg.repos)})
readarray -t raw_repos < $CREDENTIALS_DIRECTORY/repos_file
declare -A repos=(${builtins.concatStringsSep " " (builtins.map (x : "[${x}]=1") cfg.extraRepos)})
for repo in ''${raw_repos[@]}; do repos[$repo]=1; done
# Clean up existing repos
@ -79,7 +73,7 @@ in
# Backup to Restic
${pkgs.restic}/bin/restic \
-r rest:http://restic.tywin.storage.ts.hillion.co.uk/128G \
-r rest:https://restic.ts.hillion.co.uk/128G \
--cache-dir .restic --exclude .restic \
backup .
@ -93,9 +87,9 @@ in
wantedBy = [ "timers.target" ];
timerConfig = {
Persistent = true;
OnBootSec = "10m";
OnUnitInactiveSec = "15m";
RandomizedDelaySec = "5m";
Unit = "backup-git.service";
};
};
};

View File

@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIc3WVROMCifYtqHRWf5gZAOQFdpbcSYOC0JckKzUVM5sGdXtw3VXNiVqY3npdMizS4e1V8Hh77UecD3q9CLkMA= backups-git@nixos

View File

@ -14,19 +14,44 @@ in
owner = "hass";
group = "hass";
};
age.secrets."backups/homeassistant/restic/1.6T" = {
file = ../../secrets/restic/1.6T.age;
owner = "postgres";
group = "postgres";
};
services = {
restic.backups."homeassistant" = {
user = "hass";
timerConfig = {
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
postgresqlBackup = {
enable = true;
compression = "none"; # for better diffing
databases = [ "homeassistant" ];
};
restic.backups = {
"homeassistant-config" = {
user = "hass";
timerConfig = {
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
};
repository = "rest:https://restic.ts.hillion.co.uk/128G";
passwordFile = config.age.secrets."backups/homeassistant/restic/128G".path;
paths = [
config.services.home-assistant.configDir
];
};
"homeassistant-database" = {
user = "postgres";
timerConfig = {
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
};
repository = "rest:https://restic.ts.hillion.co.uk/1.6T";
passwordFile = config.age.secrets."backups/homeassistant/restic/1.6T".path;
paths = [
"${config.services.postgresqlBackup.location}/homeassistant.sql"
];
};
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/128G";
passwordFile = config.age.secrets."backups/homeassistant/restic/128G".path;
paths = [
config.services.home-assistant.configDir
];
};
};
};

View File

@ -24,7 +24,7 @@ in
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
};
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/128G";
repository = "rest:https://restic.ts.hillion.co.uk/128G";
passwordFile = config.age.secrets."backups/matrix/restic/128G".path;
paths = [
"${config.services.postgresqlBackup.location}/matrix-synapse.sql"

11
modules/ca/README.md Normal file
View File

@ -0,0 +1,11 @@
# ca
Getting the certificates in the right place is a manual process (for now, at least). This is to keep the most control over the root certificate's key and allow manual cycling. The manual commands should be run on a trusted machine.
Creating a 10 year root certificate:
nix run nixpkgs#step-cli -- certificate create 'Hillion ACME' cert.pem key.pem --kty=EC --curve=P-521 --profile=root-ca --not-after=87600h
Creating the intermediate key:
nix run nixpkgs#step-cli -- certificate create 'Hillion ACME (sodium.pop.ts.hillion.co.uk)' intermediate_cert.pem intermediate_key.pem --kty=EC --curve=P-521 --profile=intermediate-ca --not-after=8760h --ca=$NIXOS_ROOT/modules/ca/cert.pem --ca-key=DOWNLOADED_KEY.pem

13
modules/ca/cert.pem Normal file
View File

@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIB+TCCAVqgAwIBAgIQIZdaIUsuJdjnu7DQP1N8oTAKBggqhkjOPQQDBDAXMRUw
EwYDVQQDEwxIaWxsaW9uIEFDTUUwHhcNMjQwODAxMjIyMjEwWhcNMzQwNzMwMjIy
MjEwWjAXMRUwEwYDVQQDEwxIaWxsaW9uIEFDTUUwgZswEAYHKoZIzj0CAQYFK4EE
ACMDgYYABAAJI3z1PrV97EFc1xaENcr6ML1z6xdXTy+ReHtf42nWsw+c3WDKzJ45
+xHJ/p2BTOR5+NQ7RGQQ68zmFJnEYTYDogAw6U9YzxxDGlG1HlgnZ9PPmXoF+PFl
Zy2WZCiDPx5KDJcjTPzLV3ITt4fl3PMA12BREVeonvrvRLcpVrMfS2b7wKNFMEMw
DgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFFBT
fMT0uUbS+lVUbGKK8/SZHPISMAoGCCqGSM49BAMEA4GMADCBiAJCAPNIwrQztPrN
MaHB3J0lNVODIGwQWblt99vnjqIWOKJhgckBxaElyInsyt8dlnmTCpOCJdY4BA+K
Nr87AfwIWdAaAkIBV5i4zXPXVKblGKnmM0FomFSbq2cYE3pmi5BO1StakH1kEHlf
vbkdwFgkw2MlARp0Ka3zbWivBG9zjPoZtsL/8tk=
-----END CERTIFICATE-----

14
modules/ca/consumer.nix Normal file
View File

@ -0,0 +1,14 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.ca.consumer;
in
{
options.custom.ca.consumer = {
enable = lib.mkEnableOption "ca.service";
};
config = lib.mkIf cfg.enable {
security.pki.certificates = [ (builtins.readFile ./cert.pem) ];
};
}

8
modules/ca/default.nix Normal file
View File

@ -0,0 +1,8 @@
{ ... }:
{
imports = [
./consumer.nix
./service.nix
];
}

48
modules/ca/service.nix Normal file
View File

@ -0,0 +1,48 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.ca.service;
in
{
options.custom.ca.service = {
enable = lib.mkEnableOption "ca.service";
};
config = lib.mkIf cfg.enable {
users.users.step-ca.uid = config.ids.uids.step-ca;
users.groups.step-ca.gid = config.ids.gids.step-ca;
services.step-ca = {
enable = true;
address = config.custom.dns.tailscale.ipv4;
port = 8443;
intermediatePasswordFile = "/data/system/ca/intermediate.psk";
settings = {
root = ./cert.pem;
crt = "/data/system/ca/intermediate.crt";
key = "/data/system/ca/intermediate.pem";
dnsNames = [ "ca.ts.hillion.co.uk" ];
logger = { format = "text"; };
db = {
type = "badgerv2";
dataSource = "/var/lib/step-ca/db";
};
authority = {
provisioners = [
{
type = "ACME";
name = "acme";
}
];
};
};
};
};
}

View File

@ -22,8 +22,8 @@ in
default = null;
};
plotDirectories = lib.mkOption {
type = with lib.types; nullOr (listOf str);
default = null;
type = with lib.types; listOf str;
default = [ ];
};
openFirewall = lib.mkOption {
type = lib.types.bool;
@ -46,7 +46,7 @@ in
};
virtualisation.oci-containers.containers.chia = {
image = "ghcr.io/chia-network/chia:2.2.1";
image = "ghcr.io/chia-network/chia:2.4.3";
ports = [ "8444" ];
extraOptions = [
"--uidmap=0:${toString config.users.users.chia.uid}:1"
@ -62,6 +62,11 @@ in
};
};
systemd.tmpfiles.rules = [
"d ${cfg.path} 0700 chia chia - -"
"d ${cfg.path}/.chia 0700 chia chia - -"
];
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ 8444 ];
};

View File

@ -3,6 +3,7 @@
{
imports = [
./backups/default.nix
./ca/default.nix
./chia.nix
./defaults.nix
./desktop/awesome/default.nix
@ -12,14 +13,15 @@
./ids.nix
./impermanence.nix
./locations.nix
./prometheus/default.nix
./resilio.nix
./sched_ext.nix
./services/default.nix
./shell/default.nix
./ssh/default.nix
./storj.nix
./users.nix
./www/global.nix
./www/www-repo.nix
./www/default.nix
];
options.custom = {

View File

@ -1,9 +1,10 @@
{ pkgs, lib, config, agenix, ... }:
{ pkgs, nixpkgs-unstable, lib, config, agenix, ... }:
{
options.custom.defaults = lib.mkEnableOption "defaults";
config = lib.mkIf config.custom.defaults {
hardware.enableAllFirmware = true;
nix = {
settings.experimental-features = [ "nix-command" "flakes" ];
settings = {
@ -38,7 +39,6 @@
git
htop
nix
sapling
vim
];
variables.EDITOR = "vim";
@ -53,10 +53,17 @@
};
networking.firewall.enable = true;
nix.registry.nixpkgs-unstable.to = {
type = "path";
path = nixpkgs-unstable;
};
# Delegation
custom.ca.consumer.enable = true;
custom.dns.enable = true;
custom.home.defaults = true;
custom.hostinfo.enable = true;
custom.prometheus.client.enable = true;
custom.shell.enable = true;
custom.ssh.enable = true;
};

View File

@ -50,10 +50,16 @@ in
pop = {
li = "100.106.87.35";
sodium = "100.87.188.4";
stinger = "100.117.89.126";
};
rig = {
merlin = "100.69.181.56";
};
st = {
phoenix = "100.92.37.106";
};
storage = {
theon = "100.104.142.22";
tywin = "100.115.31.91";
};
};
};
@ -77,10 +83,16 @@ in
pop = {
li = "fd7a:115c:a1e0::e701:5723";
sodium = "fd7a:115c:a1e0::3701:bc04";
stinger = "fd7a:115c:a1e0::8401:597e";
};
rig = {
merlin = "fd7a:115c:a1e0::8d01:b538";
};
st = {
phoenix = "fd7a:115c:a1e0::6901:256a";
};
storage = {
theon = "fd7a:115c:a1e0::4aa8:8e16";
tywin = "fd7a:115c:a1e0:ab12:4843:cd96:6273:1f5b";
};
};
};

View File

@ -3,30 +3,47 @@
{
imports = [
./git.nix
./neovim.nix
./tmux/default.nix
];
options.custom.home.defaults = lib.mkEnableOption "home";
config = lib.mkIf config.custom.home.defaults {
home-manager = {
users.root.home = {
stateVersion = "22.11";
home-manager =
let
stateVersion = if (builtins.compareVersions config.system.stateVersion "24.05") > 0 then config.system.stateVersion else "22.11";
in
{
users.root.home = {
inherit stateVersion;
## Set an empty ZSH config and defer to the global one
file.".zshrc".text = "";
## Set an empty ZSH config and defer to the global one
file.".zshrc".text = "";
};
users."${config.custom.user}" = {
home = {
inherit stateVersion;
};
services = {
ssh-agent.enable = true;
};
programs = {
zoxide = {
enable = true;
options = [ "--cmd cd" ];
};
zsh.enable = true;
};
};
};
users."${config.custom.user}".home = {
stateVersion = "22.11";
## Set an empty ZSH config and defer to the global one
file.".zshrc".text = "";
};
};
# Delegation
custom.home.git.enable = true;
custom.home.neovim.enable = true;
custom.home.tmux.enable = true;
};
}

View File

@ -9,21 +9,34 @@ in
};
config = lib.mkIf cfg.enable {
home-manager.users.jake.programs.git = lib.mkIf (config.custom.user == "jake") {
enable = true;
extraConfig = {
user = {
email = "jake@hillion.co.uk";
name = "Jake Hillion";
home-manager.users.jake.programs = {
sapling = lib.mkIf (config.custom.user == "jake") {
enable = true;
userName = "Jake Hillion";
userEmail = "jake@hillion.co.uk";
extraConfig = {
ui = {
"merge:interactive" = ":merge3";
};
};
pull = {
rebase = true;
};
merge = {
conflictstyle = "diff3";
};
init = {
defaultBranch = "main";
};
git = lib.mkIf (config.custom.user == "jake") {
enable = true;
userName = "Jake Hillion";
userEmail = "jake@hillion.co.uk";
extraConfig = {
pull = {
rebase = true;
};
merge = {
conflictstyle = "diff3";
};
init = {
defaultBranch = "main";
};
};
};
};

82
modules/home/neovim.nix Normal file
View File

@ -0,0 +1,82 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.home.neovim;
in
{
options.custom.home.neovim = {
enable = lib.mkEnableOption "neovim";
};
config = lib.mkIf config.custom.home.neovim.enable {
home-manager.users."${config.custom.user}".programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
plugins = with pkgs.vimPlugins; [
a-vim
dracula-nvim
telescope-nvim
];
extraLuaConfig = ''
-- Logical options
vim.opt.splitright = true
vim.opt.splitbelow = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.expandtab = true
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
-- Appearance
vim.cmd[[colorscheme dracula-soft]]
vim.opt.number = true
vim.opt.relativenumber = true
-- Telescope
require('telescope').setup({
pickers = {
find_files = {
find_command = {
"${pkgs.fd}/bin/fd",
"--type=f",
"--strip-cwd-prefix",
"--no-require-git",
"--hidden",
"--exclude=.sl",
},
},
},
defaults = {
vimgrep_arguments = {
"${pkgs.ripgrep}/bin/rg",
"--color=never",
"--no-heading",
"--with-filename",
"--line-number",
"--column",
"--smart-case",
"--no-require-git",
"--hidden",
"--glob=!.sl",
},
},
})
-- Key bindings
vim.g.mapleader = ","
--- Key bindings: Telescope
local telescope_builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', telescope_builtin.find_files, {})
vim.keymap.set('n', '<leader>fg', telescope_builtin.live_grep, {})
vim.keymap.set('n', '<leader>fb', telescope_builtin.buffers, {})
vim.keymap.set('n', '<leader>fh', telescope_builtin.help_tags, {})
'';
};
};
}

View File

@ -1,10 +1,25 @@
setw -g mouse on
# Large history
set -g history-limit 500000
# Bindings
bind C-Y set-window-option synchronize-panes
bind -n C-k clear-history
# Status pane
set -g status-right-length 100
set -g status-right "#(uname -r) • #(hostname -f | sed 's/\.ts\.hillion\.co\.uk//g') • %d-%b-%y %H:%M"
# New panes in the same directory
bind '"' split-window -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"
# Start indices at 1 to match keyboard
set -g base-index 1
setw -g pane-base-index 1
# Open a new session when attached to and one isn't open
# Must come after base-index settings
new-session

View File

@ -7,6 +7,10 @@
unifi = 183;
chia = 185;
gitea = 186;
node-exporter = 188;
step-ca = 198;
isponsorblocktv = 199;
frigate = 200;
## Consistent People
jake = 1000;
@ -17,6 +21,10 @@
unifi = 183;
chia = 185;
gitea = 186;
node-exporter = 188;
step-ca = 198;
isponsorblocktv = 199;
frigate = 200;
## Consistent Groups
mediaaccess = 1200;

View File

@ -2,7 +2,6 @@
let
cfg = config.custom.impermanence;
listIf = (enable: x: if enable then x else [ ]);
in
{
options.custom.impermanence = {
@ -12,6 +11,13 @@ in
type = lib.types.str;
default = "/data";
};
cache = {
enable = lib.mkEnableOption "impermanence.cache";
path = lib.mkOption {
type = lib.types.str;
default = "/cache";
};
};
users = lib.mkOption {
type = with lib.types; listOf str;
@ -33,53 +39,94 @@ in
services = {
openssh.hostKeys = [
{ path = "/data/system/etc/ssh/ssh_host_ed25519_key"; type = "ed25519"; }
{ path = "/data/system/etc/ssh/ssh_host_rsa_key"; type = "rsa"; bits = 4096; }
{ path = "${cfg.base}/system/etc/ssh/ssh_host_ed25519_key"; type = "ed25519"; }
{ path = "${cfg.base}/system/etc/ssh/ssh_host_rsa_key"; type = "rsa"; bits = 4096; }
];
matrix-synapse.dataDir = "${cfg.base}/system/var/lib/matrix-synapse";
gitea.stateDir = "${cfg.base}/system/var/lib/gitea";
};
environment.persistence."${cfg.base}/system" = {
hideMounts = true;
directories = [
"/etc/nixos"
] ++ (listIf config.services.tailscale.enable [ "/var/lib/tailscale" ]) ++
(listIf config.services.zigbee2mqtt.enable [ config.services.zigbee2mqtt.dataDir ]) ++
(listIf config.services.postgresql.enable [ config.services.postgresql.dataDir ]) ++
(listIf config.hardware.bluetooth.enable [ "/var/lib/bluetooth" ]) ++
(listIf config.custom.services.unifi.enable [ "/var/lib/unifi" ]) ++
(listIf (config.virtualisation.oci-containers.containers != { }) [ "/var/lib/containers" ]) ++
(listIf config.services.tang.enable [ "/var/lib/private/tang" ]);
custom.chia = lib.mkIf config.custom.chia.enable {
path = lib.mkOverride 999 "${cfg.base}/chia";
};
custom.services.frigate = lib.mkIf config.custom.services.frigate.enable {
dataPath = lib.mkOverride 999 "${cfg.base}/frigate";
};
services.resilio = lib.mkIf config.services.resilio.enable {
directoryRoot = lib.mkOverride 999 "${cfg.base}/sync";
};
services.plex = lib.mkIf config.services.plex.enable {
dataDir = lib.mkOverride 999 "${cfg.base}/plex";
};
services.home-assistant = lib.mkIf config.services.home-assistant.enable {
configDir = lib.mkOverride 999 "/data/home-assistant";
};
environment.persistence = lib.mkMerge [
{
"${cfg.base}/system" = {
hideMounts = true;
directories = [
"/etc/nixos"
] ++ (lib.lists.optional config.services.tailscale.enable "/var/lib/tailscale") ++
(lib.lists.optional config.services.zigbee2mqtt.enable config.services.zigbee2mqtt.dataDir) ++
(lib.lists.optional config.services.postgresql.enable config.services.postgresql.dataDir) ++
(lib.lists.optional config.hardware.bluetooth.enable "/var/lib/bluetooth") ++
(lib.lists.optional config.custom.services.unifi.enable "/var/lib/unifi") ++
(lib.lists.optional (config.virtualisation.oci-containers.containers != { }) "/var/lib/containers") ++
(lib.lists.optional config.services.tang.enable "/var/lib/private/tang") ++
(lib.lists.optional config.services.caddy.enable "/var/lib/caddy") ++
(lib.lists.optional config.services.prometheus.enable "/var/lib/${config.services.prometheus.stateDir}") ++
(lib.lists.optional config.custom.services.isponsorblocktv.enable "${config.custom.services.isponsorblocktv.dataDir}") ++
(lib.lists.optional config.services.step-ca.enable "/var/lib/step-ca/db");
};
}
(lib.mkIf cfg.cache.enable {
"${cfg.cache.path}/system" = {
hideMounts = true;
directories = (lib.lists.optional config.services.postgresqlBackup.enable config.services.postgresqlBackup.location);
};
})
];
home-manager.users =
let
mkUser = (x: {
name = x;
value = {
home = {
persistence."/data/users/${x}" = {
allowOther = false;
mkUser = (x:
let
homeCfg = config.home-manager.users."${x}";
in
{
name = x;
value = {
home = {
persistence."${cfg.base}/users/${x}" = {
allowOther = false;
files = cfg.userExtraFiles.${x} or [ ];
directories = cfg.userExtraDirs.${x} or [ ];
files = cfg.userExtraFiles.${x} or [ ];
directories = cfg.userExtraDirs.${x} or [ ];
};
sessionVariables = lib.attrsets.optionalAttrs homeCfg.programs.zoxide.enable { _ZO_DATA_DIR = "${cfg.base}/users/${x}/.local/share/zoxide"; };
};
programs = {
zsh.history.path = lib.mkOverride 999 "${cfg.base}/users/${x}/.zsh_history";
};
file.".zshrc".text = lib.mkForce ''
HISTFILE=/data/users/${x}/.zsh_history
'';
};
};
});
});
in
builtins.listToAttrs (builtins.map mkUser cfg.users);
systemd.tmpfiles.rules = lib.lists.flatten (builtins.map
(user:
let details = config.users.users.${user}; in [
"d /data/users/${user} 0700 ${user} ${details.group} - -"
"L ${details.home}/local - ${user} ${details.group} - /data/users/${user}"
"d ${cfg.base}/users/${user} 0700 ${user} ${details.group} - -"
"L ${details.home}/local - ${user} ${details.group} - ${cfg.base}/users/${user}"
])
cfg.users);
};

View File

@ -20,11 +20,14 @@ in
custom.locations.locations = {
services = {
authoritative_dns = [ "boron.cx.ts.hillion.co.uk" ];
downloads = "tywin.storage.ts.hillion.co.uk";
downloads = "phoenix.st.ts.hillion.co.uk";
frigate = "phoenix.st.ts.hillion.co.uk";
gitea = "boron.cx.ts.hillion.co.uk";
homeassistant = "microserver.home.ts.hillion.co.uk";
homeassistant = "stinger.pop.ts.hillion.co.uk";
mastodon = "";
matrix = "boron.cx.ts.hillion.co.uk";
prometheus = "boron.cx.ts.hillion.co.uk";
restic = "phoenix.st.ts.hillion.co.uk";
tang = [
"li.pop.ts.hillion.co.uk"
"microserver.home.ts.hillion.co.uk"

View File

@ -0,0 +1,24 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.prometheus.client;
in
{
options.custom.prometheus.client = {
enable = lib.mkEnableOption "prometheus-client";
};
config = lib.mkIf cfg.enable {
users.users.node-exporter.uid = config.ids.uids.node-exporter;
users.groups.node-exporter.gid = config.ids.gids.node-exporter;
services.prometheus.exporters.node = {
enable = true;
port = 9000;
enabledCollectors = [
"systemd"
];
};
};
}

View File

@ -0,0 +1,8 @@
{ ... }:
{
imports = [
./client.nix
./service.nix
];
}

View File

@ -0,0 +1,67 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.services.prometheus;
in
{
options.custom.services.prometheus = {
enable = lib.mkEnableOption "prometheus-client";
};
config = lib.mkIf cfg.enable {
services.prometheus = {
enable = true;
globalConfig = {
scrape_interval = "15s";
};
retentionTime = "1y";
scrapeConfigs = [{
job_name = "node";
static_configs = [{
targets = builtins.map (x: "${x}:9000") (builtins.attrNames (builtins.readDir ../../hosts));
}];
}];
rules = [
''
groups:
- name: service alerting
rules:
- alert: ResilioSyncDown
expr: node_systemd_unit_state{ name = 'resilio.service', state != 'active' } > 0
for: 10m
annotations:
summary: "Resilio Sync systemd service is down"
description: "The Resilio Sync systemd service is not active on instance {{ $labels.instance }}."
''
];
};
services.caddy = {
enable = true;
virtualHosts."prometheus.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
reverse_proxy http://localhost:9090
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
'';
};
};
### HACK: Allow Caddy to restart if it fails. This happens because Tailscale
### is too late at starting. Upstream nixos caddy does restart on failure
### but it's prevented on exit code 1. Set the exit code to 0 (non-failure)
### to override this.
systemd.services.caddy = {
requires = [ "tailscaled.service" ];
after = [ "tailscaled.service" ];
serviceConfig = {
RestartPreventExitStatus = lib.mkForce 0;
};
};
};
}

View File

@ -4,6 +4,9 @@ let
cfg = config.custom.resilio;
in
{
imports = [ "${nixpkgs-unstable}/nixos/modules/services/networking/resilio.nix" ];
disabledModules = [ "services/networking/resilio.nix" ];
options.custom.resilio = {
enable = lib.mkEnableOption "resilio";
@ -16,50 +19,113 @@ in
type = with lib.types; uniq (listOf attrs);
default = [ ];
};
};
config = lib.mkIf cfg.enable {
users.users =
let
mkUser =
(user: {
name = user;
value = {
extraGroups = [ "rslsync" ];
};
});
in
builtins.listToAttrs (builtins.map mkUser cfg.extraUsers);
age.secrets =
let
mkSecret = (secret: {
name = secret.name;
value = {
file = secret.file;
owner = "rslsync";
group = "rslsync";
};
});
in
builtins.listToAttrs (builtins.map (folder: mkSecret folder.secret) cfg.folders);
services.resilio = {
enable = true;
sharedFolders =
let
mkFolder = name: secret: {
directory = "${config.services.resilio.directoryRoot}/${name}";
secretFile = "${config.age.secrets."${secret.name}".path}";
knownHosts = [ ];
searchLAN = true;
useDHT = true;
useRelayServer = true;
useSyncTrash = false;
useTracker = true;
};
in
builtins.map (folder: mkFolder folder.name folder.secret) cfg.folders;
backups = {
enable = lib.mkEnableOption "resilio.backups";
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
users.users =
let
mkUser =
(user: {
name = user;
value = {
extraGroups = [ "rslsync" ];
};
});
in
builtins.listToAttrs (builtins.map mkUser cfg.extraUsers);
age.secrets =
let
mkSecret = (secret: {
name = secret.name;
value = {
file = secret.file;
owner = "rslsync";
group = "rslsync";
};
});
in
builtins.listToAttrs (builtins.map (folder: mkSecret folder.secret) cfg.folders);
services.resilio = {
enable = true;
deviceName = lib.mkOverride 999 (lib.strings.concatStringsSep "." (lib.lists.take 2 (lib.strings.splitString "." config.networking.fqdnOrHostName)));
storagePath = lib.mkOverride 999 "${config.services.resilio.directoryRoot}/.sync";
sharedFolders =
let
mkFolder = name: secret: {
directory = "${config.services.resilio.directoryRoot}/${name}";
secretFile = "${config.age.secrets."${secret.name}".path}";
knownHosts = [ ];
searchLAN = true;
useDHT = true;
useRelayServer = true;
useSyncTrash = false;
useTracker = true;
};
in
builtins.map (folder: mkFolder folder.name folder.secret) cfg.folders;
};
systemd.services.resilio.unitConfig.RequiresMountsFor = builtins.map (folder: "${config.services.resilio.directoryRoot}/${folder.name}") cfg.folders;
}
(lib.mkIf cfg.backups.enable {
age.secrets."resilio/restic/128G.key" = {
file = ../secrets/restic/128G.age;
owner = "rslsync";
group = "rslsync";
};
age.secrets."resilio/restic/1.6T.key" = {
file = ../secrets/restic/1.6T.age;
owner = "rslsync";
group = "rslsync";
};
services.restic.backups."resilio-128G" = {
repository = "rest:https://restic.ts.hillion.co.uk/128G";
user = "rslsync";
passwordFile = config.age.secrets."resilio/restic/128G.key".path;
timerConfig = {
OnBootSec = "10m";
OnUnitInactiveSec = "15m";
RandomizedDelaySec = "5m";
};
paths = [ config.services.resilio.directoryRoot ];
exclude = [
"${config.services.resilio.directoryRoot}/.sync"
"${config.services.resilio.directoryRoot}/*/.sync"
"${config.services.resilio.directoryRoot}/dad/media"
"${config.services.resilio.directoryRoot}/resources/media"
];
};
services.restic.backups."resilio-1.6T" = {
repository = "rest:https://restic.ts.hillion.co.uk/1.6T";
user = "rslsync";
passwordFile = config.age.secrets."resilio/restic/1.6T.key".path;
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "24h";
RandomizedDelaySec = "1h";
};
paths = [
"${config.services.resilio.directoryRoot}/resources/media/audiobooks"
"${config.services.resilio.directoryRoot}/resources/media/home"
"${config.services.resilio.directoryRoot}/resources/media/iso"
];
};
})
]);
}

22
modules/sched_ext.nix Normal file
View File

@ -0,0 +1,22 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.sched_ext;
in
{
options.custom.sched_ext = {
enable = lib.mkEnableOption "sched_ext";
};
config = lib.mkIf cfg.enable {
assertions = [{
assertion = config.boot.kernelPackages.kernelAtLeast "6.12";
message = "sched_ext requires a kernel >=6.12";
}];
boot.kernelPackages = if pkgs.linuxPackages.kernelAtLeast "6.12" then pkgs.linuxPackages else (if pkgs.linuxPackages_latest.kernelAtLeast "6.12" then pkgs.linuxPackages_latest else pkgs.unstable.linuxPackages_testing);
environment.systemPackages = with pkgs; [ unstable.scx.layered unstable.scx.lavd ];
};
}

View File

@ -32,14 +32,22 @@ in
86400 NS ns1.hillion.co.uk.
deluge.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
ca 21600 CNAME sodium.pop.ts.hillion.co.uk.
frigate 21600 CNAME ${config.custom.locations.locations.services.frigate}.
prometheus 21600 CNAME ${config.custom.locations.locations.services.prometheus}.
restic 21600 CNAME ${config.custom.locations.locations.services.restic}.
deluge.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
prowlarr.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
radarr.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
sonarr.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
graphs.router.home 21600 CNAME router.home.ts.hillion.co.uk.
prowlarr.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
radarr.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
restic.tywin.storage 21600 CNAME tywin.storage.ts.hillion.co.uk.
sonarr.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
zigbee2mqtt.home 21600 CNAME router.home.ts.hillion.co.uk.
charlie.kvm 21600 CNAME router.home.ts.hillion.co.uk.
hammer.kvm 21600 CNAME router.home.ts.hillion.co.uk.
'' + (makeRecords "A" config.custom.dns.authoritative.ipv4.uk.co.hillion.ts) + "\n\n" + (makeRecords "AAAA" config.custom.dns.authoritative.ipv6.uk.co.hillion.ts);
};
};

View File

@ -4,10 +4,13 @@
imports = [
./authoritative_dns.nix
./downloads.nix
./frigate.nix
./gitea/default.nix
./homeassistant.nix
./isponsorblocktv.nix
./mastodon/default.nix
./matrix.nix
./restic.nix
./tang.nix
./unifi.nix
./version_tracker.nix

View File

@ -24,27 +24,33 @@ in
};
config = lib.mkIf cfg.enable {
services.caddy = {
enable = true;
virtualHosts = builtins.listToAttrs (builtins.map
(x: {
name = "http://${x}.downloads.ts.hillion.co.uk";
value = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = "reverse_proxy unix//${cfg.metadataPath}/caddy/caddy.sock";
};
}) [ "prowlarr" "sonarr" "radarr" "deluge" ]);
};
## Wireguard
age.secrets."wireguard/downloads".file = ../../secrets/wireguard/downloads.age;
age.secrets."deluge/auth" = {
file = ../../secrets/deluge/auth.age;
owner = "deluge";
};
services.caddy = {
enable = true;
virtualHosts = builtins.listToAttrs (builtins.map
(x: {
name = "${x}.downloads.ts.hillion.co.uk";
value = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
reverse_proxy unix//${cfg.metadataPath}/caddy/caddy.sock
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
'';
};
}) [ "prowlarr" "sonarr" "radarr" "deluge" ]);
};
## Wireguard
networking.wireguard.interfaces."downloads" = {
privateKeyFile = config.age.secrets."wireguard/downloads".path;
ips = [ "10.2.0.2/32" ];

View File

@ -0,0 +1,126 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.services.frigate;
in
{
options.custom.services.frigate = {
enable = lib.mkEnableOption "frigate";
dataPath = lib.mkOption {
type = lib.types.str;
default = "/var/lib/frigate";
};
recordingsPath = lib.mkOption {
type = lib.types.str;
default = "/practical-defiant-coffee/cctv";
};
};
config = lib.mkIf cfg.enable {
age.secrets."frigate/secrets.env".file = ../../secrets/frigate/secrets.env.age;
services.caddy = {
enable = true;
virtualHosts."frigate.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
reverse_proxy unix///run/nginx-frigate/nginx.sock
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
'';
};
};
users.users.frigate = {
group = "frigate";
home = cfg.dataPath;
createHome = true;
uid = config.ids.uids.frigate;
};
users.groups.frigate.gid = config.ids.gids.frigate;
users.users.nginx = {
group = "nginx";
uid = config.ids.uids.nginx;
};
users.groups.nginx.gid = config.ids.gids.nginx;
systemd.tmpfiles.rules = [
"d /run/nginx-frigate 0750 nginx caddy - -"
];
containers."frigate" = {
autoStart = true;
ephemeral = true;
additionalCapabilities = [ "CAP_NET_ADMIN" ];
macvlans = [ "cameras" ];
bindMounts = {
"/run/agenix/frigate/secrets.env".hostPath = config.age.secrets."frigate/secrets.env".path;
"/run/nginx-frigate" = { hostPath = "/run/nginx-frigate"; isReadOnly = false; };
"/var/lib/frigate" = { hostPath = cfg.dataPath; isReadOnly = false; };
"/media/frigate/recordings" = { hostPath = cfg.recordingsPath; isReadOnly = false; };
};
config = (hostConfig: { config, pkgs, ... }: {
config = {
system.stateVersion = "24.05";
systemd.network = {
enable = true;
networks."10-cameras" = {
matchConfig.Name = "mv-cameras";
networkConfig.DHCP = "ipv4";
dhcpV4Config.ClientIdentifier = "mac";
linkConfig.MACAddress = "00:b7:43:f3:81:a0";
};
};
services.resolved.enable = false;
users.users.frigate.uid = hostConfig.ids.uids.frigate;
users.groups.frigate.gid = hostConfig.ids.gids.frigate;
services.nginx.virtualHosts."frigate.ts.hillion.co.uk".listen = lib.mkForce [
{ addr = "unix:/run/nginx-frigate/nginx.sock"; }
];
services.frigate = {
enable = true;
package = pkgs.frigate;
hostname = "frigate.ts.hillion.co.uk";
settings = {
record = {
enabled = true;
retain.mode = "motion";
};
cameras = {
living_room = {
enabled = true;
ffmpeg.inputs = [
{
path = "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@10.133.145.2:554/h264Preview_01_sub";
roles = [ "detect" ];
}
{
path = "rtsp://admin:{FRIGATE_RTSP_PASSWORD}@10.133.145.2:554/h264Preview_01_main";
roles = [ "record" ];
}
];
};
};
};
};
systemd.services.frigate.serviceConfig.EnvironmentFile = "/run/agenix/frigate/secrets.env";
};
}) config;
};
};
}

View File

@ -63,6 +63,11 @@ in
runner = {
capacity = 3;
};
cache = {
enabled = true;
host = "10.108.27.2";
port = 41919;
};
};
};
@ -76,6 +81,8 @@ in
chain output {
type filter hook output priority 100; policy accept;
ct state { established, related } counter accept
ip daddr 10.0.0.0/8 drop
ip daddr 100.64.0.0/10 drop
ip daddr 172.16.0.0/12 drop

View File

@ -1,4 +1,4 @@
{ config, pkgs, lib, nixpkgs-unstable, ... }:
{ config, pkgs, lib, ... }:
let
cfg = config.custom.services.gitea;
@ -55,7 +55,7 @@ in
services.gitea = {
enable = true;
package = nixpkgs-unstable.legacyPackages.x86_64-linux.gitea;
package = pkgs.unstable.gitea;
mailerPasswordFile = config.age.secrets."gitea/mailer_password".path;
appName = "Hillion Gitea";

View File

@ -44,20 +44,40 @@ in
"bluetooth"
"default_config"
"esphome"
"flux"
"fully_kiosk"
"google_assistant"
"homekit"
"met"
"mobile_app"
"mqtt"
"otp"
"smartthings"
"sonos"
"sun"
"switchbot"
"waze_travel_time"
];
customComponents = with pkgs.home-assistant-custom-components; [
adaptive_lighting
];
config = {
default_config = { };
homeassistant = {
auth_providers = [
{ type = "homeassistant"; }
{
type = "trusted_networks";
trusted_networks = [ "10.239.19.4/32" ];
trusted_users = {
"10.239.19.4" = "fb4979873ecb480d9e3bb336250fa344";
};
allow_bypass_login = true;
}
];
};
recorder = {
db_url = "postgresql://@/homeassistant";
};
@ -67,6 +87,8 @@ in
trusted_proxies = with config.custom.dns.authoritative; [
ipv4.uk.co.hillion.ts.cx.boron
ipv6.uk.co.hillion.ts.cx.boron
ipv4.uk.co.hillion.ts.pop.sodium
ipv6.uk.co.hillion.ts.pop.sodium
];
};
@ -79,6 +101,9 @@ in
report_state = true;
expose_by_default = true;
exposed_domains = [ "light" ];
entity_config = {
"input_boolean.sleep_mode" = { };
};
};
homekit = [{
filter = {
@ -88,25 +113,19 @@ in
bluetooth = { };
switch = [
{
platform = "flux";
start_time = "07:00";
stop_time = "23:59";
mode = "mired";
disable_brightness_adjust = true;
lights = [
"light.bedroom_lamp"
"light.bedroom_light"
"light.cubby_light"
"light.desk_lamp"
"light.hallway_light"
"light.living_room_lamp"
"light.living_room_light"
"light.wardrobe_light"
];
}
];
adaptive_lighting = {
lights = [
"light.bedroom_lamp"
"light.bedroom_light"
"light.cubby_light"
"light.desk_lamp"
"light.hallway_light"
"light.living_room_lamp"
"light.living_room_light"
"light.wardrobe_light"
];
min_sunset_time = "21:00";
};
light = [
{
@ -114,7 +133,7 @@ in
lights = {
bathroom_light = {
unique_id = "87a4cbb5-e5a7-44fd-9f28-fec2d6a62538";
value_template = "{{ states('sensor.bathroom_motion_sensor_illuminance_lux') | int > 500 }}";
value_template = "{{ false if state_attr('script.bathroom_light_switch_if_on', 'last_triggered') > states.sensor.bathroom_motion_sensor_illuminance_lux.last_reported else states('sensor.bathroom_motion_sensor_illuminance_lux') | int > 500 }}";
turn_on = { service = "script.noop"; };
turn_off = { service = "script.bathroom_light_switch_if_on"; };
};
@ -145,6 +164,13 @@ in
}
];
input_boolean = {
sleep_mode = {
name = "Set house to sleep mode";
icon = "mdi:sleep";
};
};
# UI managed expansions
automation = "!include automations.yaml";
script = "!include scripts.yaml";

View File

@ -0,0 +1,62 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.services.isponsorblocktv;
ver = "v2.2.1";
ctl = pkgs.writeScriptBin "isponsorblocktv-config" ''
#! ${pkgs.runtimeShell}
set -e
sudo systemctl stop podman-isponsorblocktv
sudo ${pkgs.podman}/bin/podman run \
--rm -it \
--uidmap=0:${toString config.users.users.isponsorblocktv.uid}:1 \
--gidmap=0:${toString config.users.groups.isponsorblocktv.gid}:1 \
-v ${cfg.dataDir}:/app/data \
ghcr.io/dmunozv04/isponsorblocktv:${ver} \
--setup-cli
sudo systemctl start podman-isponsorblocktv
'';
in
{
options.custom.services.isponsorblocktv = {
enable = lib.mkEnableOption "isponsorblocktv";
dataDir = lib.mkOption {
type = lib.types.str;
default = "/var/lib/isponsorblocktv";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ ctl ];
users.groups.isponsorblocktv = {
gid = config.ids.gids.isponsorblocktv;
};
users.users.isponsorblocktv = {
home = cfg.dataDir;
createHome = true;
isSystemUser = true;
group = "isponsorblocktv";
uid = config.ids.uids.isponsorblocktv;
};
virtualisation.oci-containers.containers.isponsorblocktv = {
image = "ghcr.io/dmunozv04/isponsorblocktv:${ver}";
extraOptions = [
"--uidmap=0:${toString config.users.users.isponsorblocktv.uid}:1"
"--gidmap=0:${toString config.users.groups.isponsorblocktv.gid}:1"
];
volumes = [ "${cfg.dataDir}:/app/data" ];
};
systemd.tmpfiles.rules = [
"d ${cfg.dataDir} 0700 isponsorblocktv isponsorblocktv - -"
];
};
}

306
modules/services/restic.nix Normal file
View File

@ -0,0 +1,306 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.services.restic;
in
{
options.custom.services.restic = {
enable = lib.mkEnableOption "restic http server";
path = lib.mkOption {
type = lib.types.path;
default = "/var/lib/restic";
};
repos = lib.mkOption {
readOnly = true;
type = with lib.types; attrsOf (submodule {
options = {
path = lib.mkOption {
default = null;
type = nullOr str;
};
passwordFile = lib.mkOption {
default = null;
type = nullOr str;
};
environmentFile = lib.mkOption {
default = null;
type = nullOr str;
};
forgetConfig = lib.mkOption {
default = null;
type = nullOr (submodule {
options = {
timerConfig = lib.mkOption {
type = attrs;
};
opts = lib.mkOption {
type = listOf str;
};
};
});
};
clones = lib.mkOption {
default = [ ];
type = listOf (submodule {
options = {
timerConfig = lib.mkOption {
type = attrs;
};
repo = lib.mkOption {
type = str;
};
};
});
};
};
});
default = {
"128G" = {
path = "${cfg.path}/128G";
passwordFile = config.age.secrets."restic/128G.key".path;
forgetConfig = {
timerConfig = {
OnCalendar = "02:30";
RandomizedDelaySec = "1h";
};
opts = [
"--keep-last 48"
"--keep-within-hourly 7d"
"--keep-within-daily 1m"
"--keep-within-weekly 6m"
"--keep-within-monthly 24m"
];
};
clones = [
{
repo = "128G-wasabi";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
{
repo = "128G-backblaze";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
];
};
"1.6T" = {
path = "${cfg.path}/1.6T";
passwordFile = config.age.secrets."restic/1.6T.key".path;
forgetConfig = {
timerConfig = {
OnCalendar = "Wed, 02:30";
RandomizedDelaySec = "4h";
};
opts = [
"--keep-within-daily 14d"
"--keep-within-weekly 2m"
"--keep-within-monthly 18m"
];
};
clones = [
{
repo = "1.6T-wasabi";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
{
repo = "1.6T-backblaze";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
];
};
"128G-wasabi" = {
environmentFile = config.age.secrets."restic/128G-wasabi.env".path;
};
"1.6T-wasabi" = {
environmentFile = config.age.secrets."restic/1.6T-wasabi.env".path;
};
"128G-backblaze" = {
environmentFile = config.age.secrets."restic/128G-backblaze.env".path;
};
"1.6T-backblaze" = {
environmentFile = config.age.secrets."restic/1.6T-backblaze.env".path;
};
};
};
};
config = lib.mkIf cfg.enable {
age.secrets = {
"restic/128G.key" = {
file = ../../secrets/restic/128G.age;
owner = "restic";
group = "restic";
};
"restic/128G-wasabi.env".file = ../../secrets/restic/128G-wasabi.env.age;
"restic/128G-backblaze.env".file = ../../secrets/restic/128G-backblaze.env.age;
"restic/1.6T.key" = {
file = ../../secrets/restic/1.6T.age;
owner = "restic";
group = "restic";
};
"restic/1.6T-wasabi.env".file = ../../secrets/restic/1.6T-wasabi.env.age;
"restic/1.6T-backblaze.env".file = ../../secrets/restic/1.6T-backblaze.env.age;
};
services.restic.server = {
enable = true;
appendOnly = true;
extraFlags = [ "--no-auth" ];
dataDir = cfg.path;
listenAddress = "127.0.0.1:8000"; # TODO: can this be a Unix socket?
};
services.caddy = {
enable = true;
virtualHosts."restic.ts.hillion.co.uk".extraConfig = ''
bind ${config.custom.dns.tailscale.ipv4} ${config.custom.dns.tailscale.ipv6}
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://localhost:8000
'';
};
systemd =
let
mkRepoInfo = repo_cfg: (if (repo_cfg.passwordFile != null) then {
serviceConfig.LoadCredential = [
"password_file:${repo_cfg.passwordFile}"
];
environment = {
RESTIC_REPOSITORY = repo_cfg.path;
RESTIC_PASSWORD_FILE = "%d/password_file";
};
} else {
serviceConfig.EnvironmentFile = repo_cfg.environmentFile;
});
mkForgetService = name: repo_cfg:
if (repo_cfg.forgetConfig != null) then
({
description = "Restic forget service for ${name}";
serviceConfig = {
User = "restic";
Group = "restic";
};
script = ''
set -xe
${pkgs.restic}/bin/restic forget ${lib.strings.concatStringsSep " " repo_cfg.forgetConfig.opts} \
--prune \
--retry-lock 30m
'';
} // (mkRepoInfo repo_cfg)) else { };
mkForgetTimer = repo_cfg:
if (repo_cfg.forgetConfig != null) then {
wantedBy = [ "timers.target" ];
timerConfig = repo_cfg.forgetConfig.timerConfig;
} else { };
mkCloneService = from_repo: clone_cfg: to_repo: {
name = "restic-clone-${from_repo.name}-${to_repo.name}";
value = lib.mkMerge [
{
description = "Restic copy from ${from_repo.name} to ${to_repo.name}";
serviceConfig = {
User = "restic";
Group = "restic";
LoadCredential = [
"from_password_file:${from_repo.cfg.passwordFile}"
];
};
environment = {
RESTIC_FROM_PASSWORD_FILE = "%d/from_password_file";
};
script = ''
set -xe
${pkgs.restic}/bin/restic copy \
--from-repo ${from_repo.cfg.path} \
--retry-lock 30m
'';
}
(mkRepoInfo to_repo.cfg)
];
};
mkCloneTimer = from_repo: clone_cfg: to_repo: {
name = "restic-clone-${from_repo.name}-${to_repo.name}";
value = {
wantedBy = [ "timers.target" ];
timerConfig = clone_cfg.timerConfig;
};
};
mapClones = fn: builtins.listToAttrs (lib.lists.flatten (lib.mapAttrsToList
(
from_repo_name: from_repo_cfg: (builtins.map
(
clone_cfg: (fn
{ name = from_repo_name; cfg = from_repo_cfg; }
clone_cfg
{ name = clone_cfg.repo; cfg = cfg.repos."${clone_cfg.repo}"; }
)
)
from_repo_cfg.clones)
)
cfg.repos));
in
{
services = {
caddy = {
### HACK: Allow Caddy to restart if it fails. This happens because Tailscale
### is too late at starting. Upstream nixos caddy does restart on failure
### but it's prevented on exit code 1. Set the exit code to 0 (non-failure)
### to override this.
requires = [ "tailscaled.service" ];
after = [ "tailscaled.service" ];
serviceConfig = {
RestartPreventExitStatus = lib.mkForce 0;
};
};
}
// lib.mapAttrs' (name: value: lib.attrsets.nameValuePair ("restic-forget-" + name) (mkForgetService name value)) cfg.repos
// mapClones mkCloneService;
timers = lib.mapAttrs' (name: value: lib.attrsets.nameValuePair ("restic-forget-" + name) (mkForgetTimer value)) cfg.repos
// mapClones mkCloneTimer;
};
};
}

View File

@ -13,7 +13,10 @@ in
enable = true;
ipAddressAllow = [
"138.201.252.214/32"
"10.64.50.20/32"
"10.64.50.26/32"
"10.64.50.27/32"
"10.64.50.28/32"
"10.64.50.29/32"
];
};
};

View File

@ -10,20 +10,14 @@ in
dataDir = lib.mkOption {
type = lib.types.str;
default = "/var/lib/unifi";
readOnly = true; # NixOS module only supports this directory
};
};
config = lib.mkIf cfg.enable {
users.users.unifi = {
uid = config.ids.uids.unifi;
isSystemUser = true;
group = "unifi";
description = "UniFi controller daemon user";
home = "${cfg.dataDir}";
};
users.groups.unifi = {
gid = config.ids.gids.unifi;
};
# Fix dynamically allocated user and group ids
users.users.unifi.uid = config.ids.uids.unifi;
users.groups.unifi.gid = config.ids.gids.unifi;
services.caddy = {
enable = true;
@ -38,21 +32,9 @@ in
};
};
virtualisation.oci-containers.containers = {
"unifi" = {
image = "lscr.io/linuxserver/unifi-controller:8.0.24-ls221";
environment = {
PUID = toString config.ids.uids.unifi;
PGID = toString config.ids.gids.unifi;
TZ = "Etc/UTC";
};
volumes = [ "${cfg.dataDir}:/config" ];
ports = [
"8080:8080"
"8443:8443"
"3478:3478/udp"
];
};
services.unifi = {
enable = true;
unifiPackage = pkgs.unifi8;
};
};
}

View File

@ -75,7 +75,7 @@ in
};
services.restic.backups."zigbee2mqtt" = lib.mkIf cfg.backup {
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/1.6T";
repository = "rest:https://restic.ts.hillion.co.uk/1.6T";
user = "zigbee2mqtt";
passwordFile = config.age.secrets."resilio/zigbee2mqtt/1.6T.key".path;

View File

@ -14,6 +14,8 @@ in
"jake".openssh.authorizedKeys.keys = [
"sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBBwJH4udKNvi9TjOBgkxpBBy7hzWqmP0lT5zE9neusCpQLIiDhr6KXYMPXWXdZDc18wH1OLi2+639dXOvp8V/wgAAAAEc3NoOg== jake@beryllium-keys"
"sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBPPJtW19jOaUsjmxc0+QibaLJ3J3yxPXSXZXwKT0Ean6VeaH5G8zG+zjt1Y6sg2d52lHgrRfeVl1xrG/UGX8qWoAAAAEc3NoOg== jakehillion@jakehillion-mbp"
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOt74U+rL+BMtAEjfu/Optg1D7Ly7U+TupRxd5u9kfN7oJnW4dJA25WRSr4dgQNq7MiMveoduBY/ky2s0c9gvIA= jake@jake-gentoo"
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC0uKIvvvkzrOcS7AcamsQRFId+bqPwUC9IiUIsiH5oWX1ReiITOuEo+TL9YMII5RyyfJFeu2ZP9moNuZYlE7Bs= jake@jake-mbp"
@ -48,7 +50,6 @@ in
"router.home.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAlCj/i2xprN6h0Ik2tthOJQy6Qwq3Ony73+yfbHYTFu";
"sodium.pop.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQmG7v/XrinPmkTU2eIoISuU3+hoV4h60Bmbwd+xDjr";
"theon.storage.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN59psLVu3/sQORA4x3p8H3ei8MCQlcwX5T+k3kBeBMf";
"tywin.storage.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATsjWO0qZNFp2BhfgDuWi+e/ScMkFxp79N2OZoed1k";
};
programs.ssh.knownHostsFiles = [ ./github_known_hosts ];
};

10
modules/www/default.nix Normal file
View File

@ -0,0 +1,10 @@
{ config, lib, ... }:
{
imports = [
./global.nix
./home.nix
./iot.nix
./www-repo.nix
];
}

View File

@ -33,6 +33,11 @@ in
services.caddy = {
enable = true;
package = pkgs.unstable.caddy;
globalConfig = ''
email acme@hillion.co.uk
'';
virtualHosts = {
"hillion.co.uk".extraConfig = ''

27
modules/www/home.nix Normal file
View File

@ -0,0 +1,27 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.www.home;
locations = config.custom.locations.locations;
in
{
options.custom.www.home = {
enable = lib.mkEnableOption "home";
};
config = lib.mkIf cfg.enable {
services.caddy = {
enable = true;
virtualHosts = {
"homeassistant.home.hillion.co.uk".extraConfig = ''
bind 10.64.50.25
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://${locations.services.homeassistant}:8123
'';
};
};
};
}

32
modules/www/iot.nix Normal file
View File

@ -0,0 +1,32 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.www.iot;
locations = config.custom.locations.locations;
in
{
options.custom.www.iot = {
enable = lib.mkEnableOption "iot";
};
config = lib.mkIf cfg.enable {
services.caddy = {
enable = true;
package = pkgs.unstable.caddy;
virtualHosts = {
"homeassistant.iot.hillion.co.uk".extraConfig = ''
bind 10.239.19.5
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
@blocked not remote_ip 10.239.19.4
respond @blocked "<h1>Access Denied</h1>" 403
reverse_proxy http://${locations.services.homeassistant}:8123
'';
};
};
};
}

View File

@ -57,7 +57,7 @@ in
${pkgs.git}/bin/git clone ${cfg.remote} ${cfg.location}
else
cd ${cfg.location}
${pkgs.git} remote set-url origin ${cfg.remote}
${pkgs.git}/bin/git remote set-url origin ${cfg.remote}
${pkgs.git}/bin/git fetch
${pkgs.git}/bin/git reset --hard origin/${cfg.branch}
fi

View File

@ -0,0 +1,15 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p restic rsync
set -e
HOST="restic.ts.hillion.co.uk"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd "$DIR"
rsync -ar --no-perms --delete-after --rsync-path='sudo -u restic rsync' --progress $HOST:/practical-defiant-coffee/backups/restic/128G/ restic/128G
echo 'checking 128G'
restic -r restic/128G check --read-data-subset=25%
touch last_synced

View File

@ -2,12 +2,12 @@
#!nix-shell -i bash -p restic rsync
set -e
HOST="tywin.storage.ts.hillion.co.uk"
HOST="restic.ts.hillion.co.uk"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd "$DIR"
rsync -ar --no-perms --delete-after --rsync-path='sudo -u restic rsync' --progress $HOST:/data/backups/restic/ restic
rsync -ar --no-perms --delete-after --rsync-path='sudo -u restic rsync' --progress --exclude .cache $HOST:/practical-defiant-coffee/backups/restic/ restic
echo 'checking 128G'
restic -r restic/128G check --read-data
@ -15,4 +15,3 @@ echo 'checking 1.6T'
restic -r restic/1.6T check --read-data
touch last_synced

Binary file not shown.

Binary file not shown.

View File

@ -1,23 +1,31 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
AiswwF+XOeVnSZ7QbCIG+4D+o5JxVYlw6d4R1GDj3orsEGPy8a83QtjiUiNDUwFO
/3GD3I6Hv5/H16s5kLVjxachAKKequNMtZ3R9y8pJaCa3i2Ypf0itlryoPT5MywA
vlgLzPHmc/eYdyOGWJ+Lfe5ZYq8cGSyWn7E2g5KzWxj3+Pn4onNwTCQBClPtebrw
E3mvTywpIvWQ5QyrJHIkFQERoKO1kprL9G5ikvgvGqC87SLPsOhvCX/5a83znKPo
8HMEUDIUbcyjaTusdXpXdRtFEtjDoXR2lDsbJB73LG7u9mBFwJNDPeI5ulWL/7kl
whb9xs87Z6JaEJFZOB1jwfsIUGQKVSoHAWNWfM6WT9tNkYfh+mulQOuE99rEwGVn
+/Mo5nYMfmb0hUeQEEVAdfko4w4NvWvUIq0keZ4M9IjfggOdDPM7wG41xKcqXJkz
d92e4Qj7fl8ldMcha7EJkXKTuP48zBeEmnWwMRY0UAbnHsIb0ko03Wi5fO8tKiHj
sl/asjk074jextEkhlzGQrDtJm2rK9GXuZVvtuIy+wKRWNs0sS7NqArlZj5jlUbB
sPYf5JeWBC3pEcdN73T+O4CIssOT5k7D7k8P2h8zw5Se2iMAjB50sFnCOe6Iiyfq
RHL/686aDDCJVqQ2tk0N25oT4h1Xk7dJZSNFaFh47zs
-> ssh-rsa GxPFJQ
F1+/Im3cdSzHDBn2W5LAV+ezO23nrjzaeVLQ0pFNYMt6v8XTA/WHHTPsImg/Nebi
noLNcxgjPu3fSnubXJuhsL5kQ6qHWvjQlQ0cRVSnMvV4UvrrPyp0066q1iMBL3PA
rh4hQ7sF4sHz3pm9jtxfdnWamGxqsowE793AjtNEvSU/i9/ZkFrKRj0LLAhHeIHJ
dWdYpnlZIrRjXFo6y+jdYpK0gyGQopy4SwoRwvDmQ/5paBRhpiXYUPtwRckiz/KE
iHA0WJhDO9a5OHLN9WPnKVRIutH/YHviy4twyPDjhHCDeJBQE02Lykd6wvJg9cWT
BkCeYhVdGp7VpWZEbLCtnw
in/cSQ8GJaS5GpEdJFXBC8no9M3BRcB+F98R1Xm+MSlyS153MVuNcGUAfEQ9RbOd
8nKFnJ0ZjvgWnE342cJPnOZJ9yrMi7pcRy8RXslMIIBXLnt5Tv32kJJSdWYfOyHc
aT3ZYQN3j+rqokLb2Axe95pWCXKj/wUyoBCYgC5evb8/ek6XRVA647bnET5DsD0J
HCTXanw3Nm6l6Uy/AJ87FM3tBaIzA/EIktZiS1ujY7yKOU5R0VPvmwZEFC8Y4dSE
k3FbG6aUcz9JP+BSA2bO3p3z89fyun7MFgPZt0xW7EVN+YQaamqdQOFtXZZAdDzq
jDVgoCbFm/g8bJzvFAFq+w
-> ssh-rsa K9mW1w
Pm7esUbyDOoLdzxxrRkOXYxodhfKdK+X41nAT2AOR0bBcNHqGt8BSvL+HOK1vbOW
AquzA7+S91aNF7noV3JPIDrWZxDquqBp4z89YYyWNjpH9EIOYa2Sbp5xNFdgyAJw
C0uS3B/XKJcQi2np39szNzuR2h7uTKkKW5wMan+YUK1ODxmTJVibo0c0JUybMjXA
VS31BrkDb0LKRgvp/kNfn9aEaI7d78oca+6X0BDHk2zYRPwL1Q/MGw4tkx3pqI+x
i6frCSHlo715tHaYvxRQyimWthmUft/1etMasFINhLidx4ufdawLR6sbR09sCrMj
5NMeB1wc1XrGxwMumNDWyg
-> ssh-ed25519 nWv9MA CxCjLfbIfEn1voAr3jX+dRGnHmdAyylNuPn/2QfuphM
PS2cpu13uyPlo9PJIT+dybq4P2juNpiZY61sJq7DepQ
-> )ei-grease ,jk Bw
IucfaHwBWhzdMcTDXxOUyTy1kCUnSlsfAwVw8YKimsxHyvJS+wkyPXUzepHMZYId
SvebFA
--- q53LwYLjQ6p0yHStpDma4DYMpfFTpv7+MAzaHjM7Kaw
¹§—?0šîÕº9S>íƒÊìhàÛ(Æ Qå>8;þÓz°Øø¿L‰úPYhJÞsÛ@¥dÕÒœC׌<C397>9Ú¿{r|­±t¶¡aNv3'o•
ôÖ±¹d= í¦àZ<C3A0>`T62(€÷$ÓH°ŸZ:œQhµú<C2B5>
sSMw9YdkrWxDctoj4jEEd7d3Pzaar13X/AlYmaUjl4JFG5en372xl1TUvqX8GN0k
ZwtLyUdSZCubpu+2Hd17hMK06KYz0jyV8qobsepgifgEvsFCeGq356pd6wDWaoog
hARFKOgHgHV6ElHlwkeWjIeFkz5BunTUn3NTZGlbDFnhKnEw1dexy98ZRmrZ7vlO
6+55q81hL1eRsBuenq8/hF+5zsnJK6pNnfL34NlPU8uQ5EmHZoDxpsk0AhJAOijK
Jh18WlnsHdLganJpa7vx8fn/rPUJq+M5ieNCu+BFvta08UA7vmlW/OYkaFIn4weS
OfzizypwKK4Y1HV+PMdtKg
-> ssh-ed25519 RR/L5A iVk2PxSSxsqJ6TKq2kXOATgdBf3WXVikjuXTonkXnwQ
cbeSnGk8CAwz20mJUa2tkaJMNCbcU2tvzH+aRdJ1aPE
--- CaEXrA9MPkvFWguILIVe2FhmFbgeHMNTHXgvDBf3Hbk
Baüm'm™£ÏAOðOU!ýzÊ(ÿü#Ô°¾ÿ_?!g÷q" ù§žŽZƒ<5A>è|æ·šî÷aOó|HªüÐûSq&ÆÆn+w<>$áZ^ù Gg|à(HR,<06>àm1VÁÈÄû<C384>3]ÁE~püÃ/¥e÷²

View File

@ -0,0 +1,31 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
UxtA9OeecK4nVLsDq1BVy1NnWzf4ITfGqQdd31u1iTDhKhPmg8bvu4TuiVn+hD7/
B2Ar0GOHXH/JB882Q8o2D2DlkVYfwkGmtSxnw/p/um05+l59JGi6zAkw3uSAGrMF
rdte3EdlRrxCImxJVp6AvXEH3kXjT1BtDVBwARpLBGAONc++rwDIFeP56ras4hIa
H2JReFBethNMMZCOa0jH1nUDoRVohdt2wD2KO4LuOxTK0qkRxky4Pwt/+MP26X4j
Uo2FRLrgBb4DlqeNz9EQEnJeRS1Pz6GbzfqZi7f1LUoa5ifKuBobPe2sDBDvE0RR
jFAHZp1Nir/FXEkfjPw2uOfdCUZRLQ2OQ5JLlb0BKo5hV202eGCTY3Fhf34JYkE2
n++1I5oX2dX1qY9KDpj+LMASKWHtC7ayei9Mcr4Ee7m/dGq9y7Ipjw5aliFJd74n
23idt0MgZbM6GPcqig/Mbz1EQTGwu99HN5wAp8jxusjAkMfmmqosLtkUii4hvKCa
7YnbOJT/6AofZucuylfrJpD3uzkEyaKVlNeZUU82ROSKZK6VdILLB8WfWAMPAsH9
bLXHtcUo3nF/eQ4P8DZhJM/8tUdg0at4LW4Rb/wQX72xMB2l0F6sqS741f5jjqN3
gL0tSpYQXtB3ni1mBbj2jKq8+UUP6NpDwOU5p5pfK+I
-> ssh-rsa GxPFJQ
cNBRc51b5Pw2KQq+EHQ4tCGSQQu+JMZHpJjoSAPx5sLd1DgGhE1x1F9h9CuSRFZh
J6fh5xtr9l0rzae/IgSQgfaaq4KVgIC/TIiyLX1VN1MSgbrisMAFA1fu0N/mTJGJ
XEiZI3RebiBxnfS3yJpBAdsFvZDw6o0xD2d1rSzN2dKFKNr9XGPNX1wUqERnWVQF
B5fRpNiWygGPdBaXYDc5OC49vlCAkldaU2EvA1wEuesdTDDn9nisqAlgBBYXEDWq
EBGgiITNyz+wI7ncSVW6JWr5TFfDNEtqGo1JS8nYnpVNHTgU4PbTpqq5fvlAy+hI
gyIR51YZaJZNQhrQ/N4KYw
-> ssh-rsa K9mW1w
c9vvvWH+MrbfNifQYOsfhiw1Ie6npjBVCMrx8YZJwtVmRy3RYjXYn6zcFH+xzSGr
tC0WPwyQ1dqgUNTUdvxaGrSayo5WF2CNosjc66eMrqyG72pcpJwAqlNbWbDNHbm9
nE3c6XBv0WnEq7G+nkRx0luD96twkor4LVzDa37MUtljNqJo1Puv2AcEylEqXCUX
bKN78TlUOhCpec93ZIxQoE71+26qqpGNnwyTQkHII/RzMKZGQjtmdRtQiUPzVmxT
/3WjnQm4vbeKjOeBjjIdV1Wc7dykBRE7rq/Oe8AGl/7FAnbe6c2Bav50OOL5/Tbo
Fe5TZbOwFDc1ob3IezRI5Q
-> ssh-ed25519 RR/L5A Mvv5Y3bd2IqR1cffLQbJ3WuJCoGMpJaqf/TvV0kcYQs
jRBAHCUuW6hBsuv0VjR+uSKmmqK5rsU4vUIuNHQyAiE
--- /Xx07JneqPgZUC0LrtSMMHAt9eSoB0KUHxQ+j8mkr6c
).<2E><><EFBFBD>*<2A><><EFBFBD>?$M<15><>e1<65><31>"v84<38><34><EFBFBD><EFBFBD><EFBFBD> <<3C>ގJ+<2B><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ۃ<EFBFBD>Q<EFBFBD>"ӵz<D3B5>"<22><>ǠB,<2C><><EFBFBD><EFBFBD><01><>^k<><6B>I<EFBFBD>,<2C>B

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,19 +1,31 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
uM89I2iF5jhpx3ndhtroIDxXWMSZew8mSLLBiSisYM+CfjtGKOVCDvmOFNSgPz1g
96D93T9esUo+KSwWyhhEqUwEHyrdnHVPkTAQ73ie/ZoSvdCPmdSA0RgKbyYHSTp1
Z1hS/SN0xLSshz8aur/dd6FC01Ju0dqjDgCMN6edNhJfMhZXc2xidcj7Ir2T77B9
kFnLqAADHL8yyHfQzU7EIzWp857mcn4sbtPV5CuMfSvOzAdQWMxtFQndJUTnokTz
e3Gg57gkNOa50jYbr0fxN7fUBWu7J/MyOErwbRq5TzF/ZfbHkrH5iXvMBaNjSQy1
RVQozDXJ+j5oc14TQDbi59kOi4ZJmXJTk+eCFMTalYrHqdtTqV9c7KsOdiceeajp
wec3Iowgben4uqEgBluJtn5lRoDSdmKWbYSYxL3E/wX+0df8P9aZpTTEGGfhH+RY
yxhalaRYmLHD8kJ8yBlPdcKx9y6hkkE5Me+IB9Soh4pqtTbk3k/7pHLRYD2sq1T2
gxipaSumoj5trfPnp75qOTubnJ+a3l5FPiv3JOe0nOxY8zFuv7IcOsvcVipo1z+X
miuOVgbZeZrKb7//zVb0Dn1IJSZITY0SsWbNh8MVB7N5xN9iGWAmE9adrnbCKFIZ
PWteIw7/tlZHdUEr0QJwim1Br639Ah41mMspltsKA2c
-> ssh-rsa GxPFJQ
bIDYJMyhT4Tt0oTNWGwahKnw7ewWky+O8J7ExiDqIRNskA99SJ45u4n03mc6uQl0
56gTQ53HEQIH7u4DfITmKTf5iSn6O7QDW0c15dJ7P1hz3JaPu4p/Lyuzy9B1RcUY
XjNhmRyIAJdUWla/+k+o/dLRp46CtK3nIoxLd8all7vPys8i2gBQIr4HmmkZ4Nd1
CFQCXbb58asmAcYta1nTTY5yvFq6IHDbWPZDGOYCWceFoPCjlkvVH4VJjTQ7uyLX
bopz3gKxUogk0RTuF0o8tfqeZzJhcXOB0tvMfpPmMnQh08u8t4j8kVlnv+Ru5ZsE
H95rbwVArprojzFBy1fcHQ
sGxzilQ4r6X/D1MXKuXVI4p8v6KGnqN+4WUqda8OknlK79PTQY58fL/Mtmep+syZ
D11IuciWOSbaH21ZH3fuUNuNz0CRq+JWgkLnI0HwcnXH/Ni2MvXTiX9ihHzLawDE
ApkC+UbpUcZ5qsOQp2Kh0hbtLj+vegsQeIiMej4g13Z0QS6P7ElX5lYkdv/TBjXS
b1AwwmSuhl/IPzfnvMqnwpa/QeyweC7aSPuuy4WQQwjJa7pAPvuyZl7Bi2KfSa/c
p3SJsGNMJxeNUrD0FC/k/Jn0qm1B0Hf7qHi1/0ax58j1uNQCfqv6/YG0uC+SGHjY
LcFKAvBPn3HvH672Ed6ytQ
-> ssh-rsa K9mW1w
s/y+2ROtxC5AY/1KxqAYuFUJgV4QtrnhOwi+fF+61TKet/8ni1oEjvAiUwg40b9h
eNPGr/ohywXgAj77j8y/J3uWA6uXt1zhpoadZFw63Gdy236AAK6mqU+pf6tEFGYf
ZiD25Z+rhorfYsMS6CkWEVIrM8dgMAGri1pdhXTuoC7LYn9f7GRmcyujbwRUOT9Q
DXmdb6V1j5Q5I6f6TYljGdhp6I8KrAnHMBEcZBECfgTnHE3Hot51p/aoS1KeNcsb
Y9gjpQgNIcccx3CANNJ8y8iZpPzd6Qp5eFk0/IrqqMlGfYrVJ6hX37mV1pqPp4Z+
KTuVZ47PIByw6yabdJdmsg
-> ssh-ed25519 iWiFbA SMZ9gRd/HVCXD7q7W+D5ooyS+k3Nr/hoU2bbwmv/3mI
AZEH5d3rS9B27j52SYNt7wy2IanpmrYJor5OjZAzaVI
--- xWHau8xcErvYJZNB/HkjjfzWWvLrOqdI4ccGSmg/xjo
Ý׍vÄď˝J/XçM<C3A7>Mkě@Î9ämÎ"vĹ «äĆŻî—ÉŤç¨ H÷ń-×<>cŽuĹ…€Z†3nv\°ĘMĽ5đXşQ6
FmNlXwGIN5E/7btwU2TBzyEJq99RNWLE8+P1VSqXI3ZRvbq4sGMW8wpEYSlkYrLk
A9wsYA3x0U7oEt6pnJzuZkyVrc08k/7Jr8Oboscti7wPjbNyMCsPNHJU0CvpuVfh
lRVyy8Ir793jNkqvtWpJS203YyPyV9WwYzH8Q6QlKVs8VLi8KLv4truVbM4MfBQd
BiGYakuK+ckkoS3GBaSm8NNDV1+Ocf3M9RFJnqke7dQIS5tRyY2HCYzqZXbzDtSl
9CRaHnu/H46O3R9Fqq3314Uj94HoUe0SuayXBD7WCzkn9ZevsHZPhk9BHulHaRBT
ExOUcA6hCIDopz+xqErcRQ
-> ssh-ed25519 iWiFbA lsC4GR0vzRjpq20voTzHrzRuoU/aamdM3sIIHYpUQWc
KA5kjHA1lxdKM6Y2yK0rvtlUaB8EI8q5jMZPs/VywCg
--- q/m5+KxRTtf5vgXnAXd8HLGCCE9BfsDs/uJuGSGbw/Y
€@âºv<Y&4•[ýæð![U•<55>oÂ8³Œ`.Mt¤ÝSg¹.S5Tñg²˜»¼cp:g$¥f{eânñAdt¦Þò‰R@lŒ?

View File

@ -1,19 +1,31 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
HWrOsyGtCBR++xduIQUiaVMo8UplDwkbb6WnO6zOLdDD+ZRjje0X+jKbvpSD/k8m
d9p/0AnneewAPmP3xToEk9hRjhGwUcrYURH3ed7/1a+ef7wIGfeNfJfAe5muAUWE
OLNxu9s6OIRUK5f6EtHKto5IEhr/GoGWCCCxDJheXQ5e4DLzhrH3FxfWDXz7PqiH
trXWMploZArJ2f1RGr0wOJIhtHLcv5PxXpHz4ASx1m6pL+q3OdmIV7J+JQaDKVWE
LDa4tQnwcvFodB+g9U8a/KetsCyCdlQnpDqjVAYelMDz+IaMqz8htO/nTL8uS9bg
2AHdB7c1NiPzbI1M4G563jQ7q7n5eQB+nFSTiyXYfvz3tz47GlDvO3DiVtoRnfiY
Yp+K02/emsa6wcBVEPcLlWqACp3+DwL48BVll5XfPadXeR2jsv4JFuDfBSzhr7P8
Xb+PfXbcK3fbXykMrVlfuMfNgnnyFoyTSXqVByolU9lu+FHewgVkyFOhpOse/5eo
azm2Bg7DICsXiSXhlJj1iQmacjxdav771qqs71W68Vu3BgImKrRdgKJ1/DY/yZs+
UhyGN316EcMen+DhV1ZSD7FfO6A/HFB/GwoOgth+HAFCmL8XrDwWeSYHIqHmK9LI
z8rfksUGQPV1p1SL3UGBZZYO86ufXY9h0vdrKTUxGh0
-> ssh-rsa GxPFJQ
EpPN7UHHRuytMr2vXy9CHzjVkH1iCt9LiTLhFsqXbL03Rk+X82q233Lm12f0sQvz
Hqjukkh9bU90TLKcEOFpKrU5FQwKUjzEy85A+4UoovWdJ8VwACOzoJf29Ys1bX3i
Xp4gUT7ne5+4afNwKXVFDS1YCPjIoQPu2cGw6iTNIYwVY5fNxz5y5ZHLkI9lOqJD
mT7jCgLLdkK8vJiDcu4Ofr21GaziQh93YXK69i3gyAt6pqSRyQdAfhMGkykOWdrO
FXMtpzT82UCvfbFbbRCRuSFga0uq5zx2cwvBD4Xagw8Dfg9RQO0rX9NAbbgcoyfG
qnk+0bYAk3pWfdXW9Z/psQ
AaJUbzqcQ1JuhUECy6HAlijuUFCldS7YuLYnm6iv31IFK3W5X4k38CO5lHQGnzvy
GJKEhqETzgNk05MKu5kVWqPC/DwwO99JCmy6v3G88cTi1ekAox7OIwmeqnyT0NQX
Un8eiBECIT3WXfHUZ263n0b96s8YW/MDVlBi+EKjhj701qsjpl2b1aFiPUfETGZd
eyE0pi68mI3Mnp/WX6hR9DODAysJE0nubU/d50A+HtOSCNrrjguDuoEMhNgAPEjP
UzTMQ/+lrKJfIYRf+zvXnFbeUvQZu4Aswo+ghgwnNRYoYiEvbQyK+dhqQunHO6tI
e+A5GMBycd4IRz0AvCeEaA
-> ssh-rsa K9mW1w
NaR245c+88dGflT9cG73bQOBxQsVi5x8JkMTrqjabzwzHpRiBUdtP+Ou1w+klOI4
cv1RLngEZH9jsSiEdvpvRkzE2ILOR/abgABXZi/4vl7iXiC8T23QSOPXnMxrAgpH
RV9B3GcSClb70+Lf3pJtPBVHVENhFVFvj5JgxQ2Zi6eMpcMuL18r/Szn4erk8zXQ
330oEau80X6WoPtRaSqSxVRrMGecGHdIE9chLosCf1x8CgIcYBTtviky+fDQMkKZ
iwueW1luuBj1AuP33jUqjeyyMaJ6SqSmaxGqGHGXA/ayxF8HnHU9AJlhPH+tEEbs
84Xu2vwg9ikUz7B1tTBYeA
-> ssh-ed25519 iWiFbA CuUeGNUBc5K+AkXBRvp7SUTJNoMDW0bWRnYs3ZhFSGM
UwwyxNA2L9q6yYK+BqYcqOq6F5CF+iCUpuceWsEj7ck
--- 3XKIweSg0UFqbadbOP0APwaLyquaEdoanlvndvxcQkk
ßüu _śnTx#H-7<>‰yó—Îç<>¨—B„ô»uµŐÉîj<C3AE>ZHĐ'&÷_đś#ľĽ3ŕHÍ˝0f  šBęą×Ö™ýčÂLĽîz
e/qVub+2c0GRMuVkaGwyQ1I7GodKg0njuLCLhgWcGfqvov2aqbBy5QuTEIwPm/CM
Wp+LcPU+m0wyw/oAtiANeb1TNXRwX/k2oSbbbhN6912Sf/7QRJukgSWaT/l/TDYz
H9RW4EJ3ir2aGS+/rZRchhrbTgMdluH132Pkg9hy0Wybj+PnqUFf2RXk3bC6CfMB
uM1S6LdFPE09ublSk80U6M8Ihu8j2MSIQ+LSfHJJY3aoRZSr1DWz991+0WyC9fOQ
U885VoYh8tmw7rs0aw8gHLZ23Rx/Kf8S/ZQIM9ZVQnQrCEsGCmPXOZLbPM0hawXh
gQUICXoa4AXslSdBn+FG4g
-> ssh-ed25519 iWiFbA 0xrDzh2/OoYkwZck8r6VYNBsITWDuDBe+8AYYxvCiTM
jSlH57h8pxoeyTbUQQUoTTK0KwTvvg2LUggAJ8xUL9s
--- QGRYDjRpaQ369Yej6tuziZ/TJOjjDc7FGX9LRNuHYLU
~”Ubp§ÜƒÖ³Š"0·2çw´“KdŠ†K.÷Ïmк Þ¾4š¥NbÊôºÅŒGßçnb+AD-cDÙùÛ¡Ž?8fbãö“GÚ© <0C> ß‰<C39F>&

Binary file not shown.

View File

@ -1,18 +1,29 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
BZKLDxLAZZ/1TfFlNANKnS0NEzsrS45xVuppc21R8wx9OwzTGZIPJcEigYjbWCE1
XCvkvpw6oTk0RNXx6VdAdZ0dfvfOcU3PfR30/IeiEZyKioyO3gSPp1L2z4Qacz88
MZDsdFMKhivexSRcNl89Xa1LEDf30XYaLKMxhcgZ1b5Z8TEG5trIs2NKek0eHY5k
jBgfvAoKqILVeot8cRANoIhC7aVI6lvVxNVhKzshv/ZAADUN7AZgYyvD09i2qFRX
AP1Y8ld5Csi9pp1KQHn1qzu2ofGjkERUHDipfV7oL3C9s1/sJP2u1oBthqYhPi2R
YUv7k9c8jILWzYw0ryO5bvQJkUWW+SS4WuQAZA127+aq4XECAUiNS4R/aNKwzDK9
8N1k5vkzJLXdcmP8jL3D+03O7igV//CbxzbyLzJXP2dAQI85VW4oxsFiPBHLYqoo
Nq79K6iZll7FUvyPxiFEzll7N6wzM2nPIrDSJeeDxbfZiZzAW0FN7bWl4qS/olp6
sA62SyRPtLd1WugaULIeCa1hopJGUJM4kQpnXPu8uUIOpnHUbc+iGXMWqxTqmyZD
bH7oNIAtTQKXzZiO7M30rt85x1eiADZqWirH26yYufxg6KndUKe+SAsBZUAkoziO
0Lotv232mcwbyWrc3/+sk0rtEP8GYRlCFhP72EQW9dA
-> ssh-rsa GxPFJQ
kYf/CWnC4WoFnELMnW4b3k1df6r1sZn7JeLPqIvQ7haKwA0SUrbMGiyWui9ewn6s
VSZb7T7zCPFAgs546wiq9O3JzeQPtWdTFCA5F/U48ftbZsAL1YjINqm3ySUbg0O0
eh8wZnO4ludFG0bi0/7B6eDmDpfqn/TKLjDJkWiintYg1PMLMR0iDaF8D5oy0zCi
Yiz+sAWyYBVvUSD9PEdCPFu/q8ySj9upBxaObf/Z5pDC94I2Ex7NZm8lGoXyMmnk
vrhVuHc8eRcqqN+ID48JakdPopA/X7IvFIIJVh40yw4Qldzr7H+b7M98Wg0/UEiC
mZldTPzpKznZntXXx6O89Q
EBc6e5bYK8E+adyWM+l4nKIHbun1S2nTh8sLJiZYd9EYdKG/2S/+Pe7vREB5I3Ab
5femntN0JeCTwzjjlbBZKyULAN85sSm0pGibT/RUneOBzmENp1hQSbvGTNK0a/l1
ELRxmlDvziU5DO0WK574lXdTdXTkk8SGCpvNBtwnTov3TqlcO36BoCE5Apw62b1g
7l5/P9hMnIQovAHorh96kqj/unSCTc4Y0ZsNX9IqUqWLLxYNJovUEORFUnPTSvWh
Lbxp7wVXRxccY2bnr2BzmCObzujH7o4K1x/7zxVjxXc4TqGsPT/pzemZ6vzY0LPA
FCCV6Mk41j0O6ez0OyerHw
-> ssh-rsa K9mW1w
M89fPs6WcToz9Jr2YXrRGM26lHU/8Xk72OExtArEsqKpHcRw27EeOQpcXkQ1baro
0MFQTKRsMc2PV0S85BPlUIBrvIiTTHNr/eusKTpAlDwnwllGk26xdaqPO3SYXzMX
/zo65i6837vQZr1aRPg1LFzRMWfrj7tLGQPuuE9F68Q/BYaCBbEo8t1vVVVNibjt
1rGuaMnjJd7scHkJ+wEJURQQuwrIY4oH8obmrzjQrFbBiWPH/lIUH1OUv5SDbYul
/Oq0QEHws/o68EQxOKWrPWRu9P1A3xYu3I19YYZgrKr1G9c0+ZEtZsvfeqCE9w79
OOz3GtRRHRCLBDDzv8nfrA
--- e0EGzCgM5FbTE91OLwUaICK+yruDi8EAIBMGxFJXWLE
ªœ£®AòÂÙî{3·…óÚûí€ñ=Õ89ð9<C3B0>es<65>Gïj
!”Áü;ætr0¼î&Øgd;ü‚¢&Ã¥Í1£^§ 'ø6û<36>Wh^qœ
FHEQuY/JPZLSrSuHj8HTc+jNrxidPpM6qKAkZTl/0vYgWExdJtGi/IukNibjF47q
176i56gjkJZPts8FRgBFXUimBGUBiMCvEOV/Xc/ZK/Foz7JQuTpiNmxhPyVSrGhW
GgyXqLycs2KxWbIz7fPLghGXXEeq86z0yTKiQOx5IkkwZOdvUDmzpHrqnCNi4uG5
LfgzbAcZxhZEKEfOsqq46Gzww96uLmcLypnppUFfelioceMcMnzyXbwXYMDODoL4
c7rXqWqHA9Bu7P8uiPFeiGXuISQGsRUJVbK2GsT7lr6+WfSdTxJcOG/63nKWStbg
VkHYEs6vACX4gHb9BHvpLA
--- nPAuzxbz5u9828pB+pGjc8p63zEYMSyHjHdZZepEs1g
¢(dsÜç´WæâÆó3þo&<26>$÷îùIuÜßBF `PìíØHº¬}d4(H<>@&`kô«'öh«Õzqªâ2Š´[?0Ìt„<74>¾æU½ÊÈ

View File

@ -1,19 +1,31 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
YmBjkjwn3FjgeFaxQ9G5h3ex2ZGe1TStge4BZsHOPizJnaU0aGmN/e2hjgRRWBgc
Js+SrukIFZjrQ/z9qgEQhhktBkpn/y34a9jSeqYA7IUjBEb3puWFqREbkEtC91Wa
DGlJFnGQiFCZ5Z6cIj+CkylQme2UeFD8Mbzrnhslh3w3Y3M6oA0zbBmwsJZ3WtQv
1QUjkPoVEd7t5RDcvm7au+ezUFUm06TsVz77vYkm9P82/fq+u684kwsPTJY0AL6x
RXtAjN8y324nrwij/x1EHl1J9h4hYL8QT8S/9hnVQ5YuANAO3W39wRPedOyESIXt
MXssIp7mMdDzaWBBKsfGkV0SinN9LQ1m9lTEFthmhX76lrLUvlVCKIHMfZWUWf3i
fXf3AREcLCVcEqeWJw4Js8Phet7MsnWReDV/jrTFSISP7BJj5oSLtuLCRwa/imGk
rcZaCG14oo4Tnf4VJDV5UdjJQMo67/rROezv6wDKUW/NkNOKtN+fcErCLjiu2tGV
M9oe/G4sE6gCthy00BpeAJTiK8XDObnGAokBddS/SbhHyh3kZ6Zu49HWMBVW5dOV
RWmvKy++8nDft6oRVITOdxHoyurRUqSzN0p9K7HS6GDP78ThlDT9nigR1HAB1BMD
Ouh1N8tXdP4Ap5osPaOzcN5UWkdnFOPJ8+Fma8gQLZ4
-> ssh-rsa GxPFJQ
oP+PAtaEFPKwY/p94ofdBlLtCJDzaOkE7jblE4COKCp6NcfjUot7a7G2rSzG3Z9A
cF3HcuKsMrG2Tth4ElBB0nwfaXUJZOBO3nrZaU0RGBBkxqooDVV2LzA+NG0HpIZ6
8Rd/Ch621gaBDYbSNFKLx5pAotqsARt13BMVY9nuifNGmWAamZ5UsJwZ/OhxKC5i
bkFZGeHZm4tilpsBEnh99PxofQmFy52AQhpx2UZETaD7yXvyEQjN9yBVKGhwA8Xs
xIRZLVgCbMv5lroYChpj/SiuoNhuaLo+05+3r5JnL/ODEl2dYZHkr2fAo31QUPnr
kmir2Nwoq4MlNELjQvSfdQ
E9g838j5piMvfHhBSdMC5wDSkncDCP1GP2a5hVEL34Krn4+mWE9yRoYGTdcz5AK/
heVfg8h+NteAUYLcBw8B3TSxKKt1BBiQj3gGwX/mrMmOmOZD508JEQAVb6Bmvi5u
3DgxN9gci7iUF6BpaHcHi3U4Sl6bJK2oH6QmRFd/oM3c0f+0vDsTM1f0Yyt+Gab4
/DiGdSmvvTO9tq4sEd5Y1Y96HSBmRoCQU9VWv48/yx2HCiDioZH7vdgOdOkBWDX9
Ba1Qa0pev2PJFiCujCLLvb+6YWzlBlUKc6Awv7tEicExFUXbfiKVGxZmI50xY0Wl
KXRzoyUmAodsdp/HkwaoEA
-> ssh-rsa K9mW1w
GVNgJY5cse7GnU0UpgfBT2a8Ev0KeFC+Tfvj8Jd7Wgu7pYv/DlwIumJN2NcmU76S
zW1Z6we+Fs4DO83v+4Bug4d+m7oUbKxbUfgIDE4MDEkc4B/XUKv0Ex1VaO4lGh6h
0lRF4PO22OjyO4TT4tkLZgTAStq/vS20GhluEdVPp2ovSsn7KYLwmx81iBnfNDbm
0uEKAE2dj01BHSiRZ1rVj9OnTacrRpzp6mbVJxZqkUMJ+A/tMp1B7eTjWiGhUUGs
+de52Ba/ww1jM0BmbdemWS5SA1Kch1ttgnSFKIh24tYyRxX4AVsuYqPkIuH8+W4O
Jh0387AJaR+3+Tvweo88Tw
-> ssh-ed25519 iWiFbA xx3oYrX6/Z1srbxmAztZV5AJgYZn20UMvRnn4qrSoAU
98oe26dqhSE3eET7hwdV/jJTVu2ldBiZ6ysabwK97Co
--- qrw4HhyaHrpDmws4EoABLFF3HU30AaZFCt4qHKo6gUM
OnÄCfIÚìQÿlµç@‡?:5¸<09>Ö m*63ÿgtjÁÃïÖA8 ^;TÓx%;O»·»×ß׶YG¢€,mïÞÜ â¥ ®Éîs8Pƒ òR«\nòXA÷·w;—ÚXRMÛù2´òFÏ
GLZFcv1WXomvnn05f2db9MIAmDQXbD1dCpcNJNFK3Ax/efvgZxHNrppHZXDYwWn3
EkNDZVd9iMb+YZ8Zqa58TtTOhiNH8yTsZeq6jF3bM8Lc2ZoS+v/DqOA/pNALcS3b
LlIkIH4cZ/4cvrVc32+uRFhByLR5vgKuf+F/p41zzFUeA50rCFUR9/gzCByrxSRT
zW6z+PbgNARqbburCp8Jh7yJo74Phki8pizgDrfvWH7hoSYxtf/hbo2DobRntsnn
irlmXjXrXy3P41VoE+4PjbJpDBygAYy6uy0MwkSVXMc+y6i+txeMOw74amvOrF+f
gCaexeMDPmyGm3N2j7qDFg
-> ssh-ed25519 iWiFbA +U3hUeNDk/1M0AGubIlLJeJRiVG6yrLFeSxi5Q7rPBk
BVVyNpQ+BJOhl3/6gy96NXGGGA4nNHoUePDkIVj0xGU
--- 5udXYFRKZut6o3QTDjezohRg3uwaLSBo0IxC88yercs
`"íq·(9~úš‹:€S"ò±ê´1%ÉæNüÅ@â‹=†p„j?|Í_ñùeÈÊâdP)8`ÕEÀã¨FµAàæhÚÚJ<$ðò¢ÃtÌgüáP/z¶mÿHàu7Æja³|Ôsn÷Ð c ªz<àå5·¼>

View File

@ -1,20 +1,32 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
D5yNwwuWnJ0EUY0GvE7/ZMVDOGJaqv212LZhLLoUEd50l6fgXhk9bhycbLCJcchN
Sw9qr+uryVvuWjemKH5YTSx9iTpFegXcB4ekCGopUV5UwMbXFh0mdSHu8ZWm8dXi
7FldNgEe57JTink8macAON+jcZya3sP5QDa3/MITLv0FZHvkUTCDGIZY9u0utkc1
TZlOivvY7vx2CP2y+2Jh3NTOqbncVzDpOnhE1l0Wc+Lcwz8yWWatigwcC2O53lk4
kDdBNGOPppx8AjHbDxPe22hN7XW4axCAgek8IobTfo9ROto+Mwb5DZXM2afTQGjj
rX49hTjksoi0vXXssX/fhp8PPHnHLU8MwURaqJKBwGBFjiIms67rPKc99hFHsJP9
BLF/ajs9yOZDDl1zwspP0koIBYTtrhxXF8mJ3azDFvVcFWBJcNdWd4PldW/71yEE
iA5Q1qDqhixRLkTcihYikekNZ410hmigPnotkxsRdHxKRjnNi1t7kLrWmA21VyCy
93FasIUZOCKpm7KjzJkFhfijTJCxaBu6e1qBfCJdCC0xUT+eTNWSOpF69ymOZ8n3
uDFqEUi2U0nw7BbVMjv4ny8Qi3dMNKASw3PcuHLxxeuHgqxrqkGcvpJZffCEefZt
C9bicJ7OcESTDyFp+t4Nu/+PJyTiOClVCDMi1IXBTy0
-> ssh-rsa GxPFJQ
XtQerUqo59DoCzMSSbYWK1fZR5uPzA20NJcpZ8YOCD2cPJvGiSN4ePnPLSSnyUAV
ei45c6sdRRRZEKcq/uabTWPqk1xymKv4CrLn4FjS9VviKNYW0p+oo0J9ummwE6sa
/REKRIQRrsP1HwSO0gaq6CQEyGr9NouybuDDS5AIGpvaRV9+F1Htc/Um5JmsGaeC
aUBSJdZGnUajg/NN+QBlhhndT1PevFNKaviOJB4ghZUv8V89KZUhZMLuWQHVSr6D
fPQLXCI7mDjv82rydG5NWzE3OiMycLlt3AXWE97G4dhrtQGMPb3KDeGaY2mXBQ5G
Qq+myMZJqx9LbQWPyBgLgQ
JoNUBVV9WIA0Zx/+EjA47T5uRLEC9/wHsRR6aN51Ldd0Q5BzcM+bGCKM3yVLiUlC
ATiMcs3Z1PnpD5pgWlAbWRlvFO/HEPxofhXO6KK61KvhyIfwVfRxDf46WUERL9XM
28+YziZtobuhyYzf4knQumjkp3K7qOC64BqU2rXSwwHVqI/JdgMCf1H5w0bJi0rH
62RGy+D9vEnqwNPvnvwTMkOCvmFVcqCCJdpbOjZ9i3imsREO81aGdGz0daTm9FDU
iWrdpRcAnLFbxOkGn6tb5daTKut6hzpiV55yzN4LK8eo+gbVLUsrmCe8oz8EnSni
Cw0pDV2Y1YuNrbg0xWPiJw
-> ssh-rsa K9mW1w
QjWGmW/CSl1+xidbzQ7rBjPI9gphRAqC9j4dTsQmK48WlnlG9fAX1w7s8zJI287c
VwtOndfP/yYe8zj14IB0bn/efJ4s9DXS1toSVHhHMXGRYNx4LZJtGrHPEe7bmBDR
g3m6i3dFB48o4niZwLFmsTWD8KpYFPRmGnD9AX7cXtA1KzjD61ebb7dsjQ0J7/04
QLUmPUJ4BMgvMhv5zHfjq/LjasrOVY5QkKi95xYVREacy9hp/5COs9d0d40+qLFh
QJ2CuiOba5+aYnVWtbq63F9YJsAwknPOI2nV64l1BflQGJjec+wS4LPi4+l6pT+0
wPStv+iVu5vYdVNuq7KtnQ
-> ssh-ed25519 iWiFbA Qrs30RtQXXEglsSKQbmeLhZvQB9yR0EbKeba8IV1/wo
o89IKeFH91zdqfRJrIge0Sod6k+66BbN3DcUfnqSgqg
--- zhvOvBCbfuhWDejcJcXxtdypACwgPb1KrqpsNYWiKNc
å<}š‡: ¸Ã2bzªüÙÄq¬SYîÌõr掄Ý}¯â<C2AF> u—»ÌòbŸ\;
VuOJ56O13sQx72WUoSSPcjiFx5soxvzhr7RsmLibpoPKq859nXPObhsSqLm89zrp
1kI0AHcOKzuOV8DPI0e67b5NSaORUQMLCcbWhCI5Bgs486BEcwMMtPWDDq5X6z3/
V8mgHoh8iOIH65vxsU7TC9SG7CzYEQWOkuayrfYBi1eoiC8Zo7eiWoyskFa2MjLx
/QM0j4f6iLGIGi60dOgbaseZKLgKX+jYKWQCB8poRyLpCyWrf1o/ldBrJvDpT3s6
J+8tg/y4wJnZ8Pvdnmbeq+L6oz4+w7mC1phsvzQQg6sfSL1ZS80NKexgLnqR07Bq
O2lOHX6pciENCRDVY7YKPw
-> ssh-ed25519 iWiFbA rsOEDax/kHkNdNdNe+dMwJUKoWGcEchbqRTBz4OlUlY
McVrDhF2MuCtbBd7ibOWmJmXRzwDLIthhEv0FLlFi0Y
--- kCiH49m3rceEryHxukITIRQ4cmCUC0kHya+GYoBAeuk
’¡¦™÷>Á¼€Pßb?bñÊ,_
°!1#±Ü=ð¼â䈔® R׳ ]AcHíÛdTú8Ϥ‘

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