Compare commits
6
Commits
installer
...
d24d71113f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d24d71113f | ||
|
|
87cdfdbd97 | ||
|
|
7bebac194c | ||
|
|
cdc5d9c1db | ||
|
|
e7122b8862 | ||
|
|
0aade09d7e |
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
name: flash-openwrt
|
||||||
|
description: >-
|
||||||
|
Flash a flake-built OpenWrt image onto one of the OpenWrt boxes (currently fergal): build the
|
||||||
|
image, pre-flight the box, back up its config, validate and stage the image, run sysupgrade, and
|
||||||
|
verify what came back. Use when the user wants to flash, reflash, upgrade or sysupgrade an OpenWrt
|
||||||
|
box, or after changing its baked-in package list.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Flash an OpenWrt box
|
||||||
|
|
||||||
|
The canonical, agent-agnostic procedure lives in the repo at
|
||||||
|
[`docs/openwrt-flash.md`](../../../docs/openwrt-flash.md). Read it and follow the phases in order.
|
||||||
|
|
||||||
|
Key reminders (see the doc for the full steps):
|
||||||
|
|
||||||
|
- **Stop at the ⏸ before Phase 5.** Flashing reboots the box and cannot be interrupted partway.
|
||||||
|
Confirm with the user, and confirm a serial console is reachable, *before* writing anything.
|
||||||
|
- **Packages are baked into the image**, so a package change means a reflash. Edit the box's list in
|
||||||
|
[`openwrt/default.nix`](../../../openwrt/default.nix), rebuild, and check the built `.manifest` —
|
||||||
|
a package name that doesn't exist is not a build error, it just isn't in the image.
|
||||||
|
- **`scp` does not work** on these boxes (no `sftp-server`). Move files with
|
||||||
|
`ssh <box> 'cat > /dev/…' < file` and `ssh <box> 'cat …' > file`.
|
||||||
|
- **Detach the upgrade with `setsid`**, not `nohup` (absent on busybox). `sysupgrade` kills the SSH
|
||||||
|
session mid-run, and an attached run dies with it — possibly after the firmware is erased.
|
||||||
|
- **Never reach for `sysupgrade -c`.** It needs `/overlay/upper/etc` and aborts *after* erasing the
|
||||||
|
firmware when that is missing, which is exactly the initramfs case. Plain `sysupgrade` already
|
||||||
|
keeps everything in `/lib/upgrade/keep.d/`.
|
||||||
|
- **Poll SSH to detect the reboot, never ping.** Successful pings return in milliseconds, so a
|
||||||
|
"wait for down" loop completes instantly and reports nonsense. Sleep between probes; expect about
|
||||||
|
three minutes.
|
||||||
|
- **Verify after**, don't assume: revision, management address, package count against the manifest,
|
||||||
|
and that the new packages are present and running.
|
||||||
|
|
||||||
|
The images are declared in [`openwrt/default.nix`](../../../openwrt/default.nix); background on the
|
||||||
|
outputs and the pinned package feeds is in
|
||||||
|
[`docs/deployment.md`](../../../docs/deployment.md#openwrt-images).
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: install-box
|
||||||
|
description: >-
|
||||||
|
Install a new NixOS box into this flake, from bare hardware booted into the custom installer
|
||||||
|
through to a deployable system: probe the hardware, partition and format the disks, write the box
|
||||||
|
config and flake entry, run do-install, and document the box. Use when the user wants to install,
|
||||||
|
bootstrap, provision or add a new box/host/machine.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Install a box
|
||||||
|
|
||||||
|
The canonical, agent-agnostic procedure lives in the repo at
|
||||||
|
[`docs/install-box.md`](../../../docs/install-box.md). Read it and follow the phases in order.
|
||||||
|
|
||||||
|
Key reminders (see the doc for the full steps):
|
||||||
|
|
||||||
|
- It is **guided, not automated** — stop at the ⏸ points: settling what the box actually is
|
||||||
|
(Phase 1), wiping and partitioning disks (Phase 3), and running `do-install` (Phase 6). The user
|
||||||
|
often wants to do the install step by hand.
|
||||||
|
- **Phase 1 is not derivable from the hardware.** Name, site, role, channel and whether the box gets
|
||||||
|
assignments now all have to come from the user. Ask before writing files.
|
||||||
|
- **`show-hw-config` is a shell alias**, so it needs `installer-shell bash -lic show-hw-config`.
|
||||||
|
Run it twice: once early for the kernel-module lists, once after mounting for the filesystems.
|
||||||
|
- **`git add` the new box directory before evaluating** — the flake reads through git, and an
|
||||||
|
untracked path fails as "Path … is not tracked by Git" rather than as a Nix error.
|
||||||
|
- **Validate with `check-system <host>`**, not `build-system` — evaluation catches module and option
|
||||||
|
errors cheaply.
|
||||||
|
- **Seed the SSH host key from the installer** (Phase 3) by copying `/etc/ssh/ssh_host_*` onto the
|
||||||
|
persist volume. The installer regenerates them each boot, so they are safe to adopt, and it means
|
||||||
|
`my.secrets.key` can be set and secrets encrypted before the install rather than after first boot.
|
||||||
|
- **Every box declares a secret even when its own config declares none** — `my.user` pulls in
|
||||||
|
`user-passwd.txt` by default — so setting `my.secrets.key` always requires
|
||||||
|
`ragenix --rekey-one secrets/user-passwd.txt.age`. Check with
|
||||||
|
`nix eval .#nixosConfigurations.<host>.config.age.secrets --apply builtins.attrNames` rather than
|
||||||
|
assuming there is nothing to do. Re-encrypt selectively; `ragenix --rekey` rewrites every secret
|
||||||
|
in `secrets/` and drowns the real change in churn.
|
||||||
|
- Take **everything** useful out of `show-hw-config`, not just the modules and filesystems — drop an
|
||||||
|
option only when a nixfiles module already sets it.
|
||||||
|
- Finish with Phase 8: box page, site index row, `networking.md` prose. Don't hand-edit anything
|
||||||
|
between `<!-- ... -->` markers.
|
||||||
@@ -61,6 +61,9 @@ Common ones:
|
|||||||
`SSH_AUTH_SOCK= ssh-machine …` (or add `-o IdentityAgent=none` to a raw `ssh`).
|
`SSH_AUTH_SOCK= ssh-machine …` (or add `-o IdentityAgent=none` to a raw `ssh`).
|
||||||
- `ragenix` — edit age secrets using `.keys/dev.key` as identity (see Secrets).
|
- `ragenix` — edit age secrets using `.keys/dev.key` as identity (see Secrets).
|
||||||
- `repl` — `nix repl .#`.
|
- `repl` — `nix repl .#`.
|
||||||
|
- `installer-shell` / `do-install <system>` — drive an install against a booted installer at
|
||||||
|
`$INSTALLER`. For bringing up a new box end to end follow the guided procedure in
|
||||||
|
[`docs/install-box.md`](docs/install-box.md).
|
||||||
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs. For the full periodic upgrade
|
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs. For the full periodic upgrade
|
||||||
(rebasing the `devplayer0` nixpkgs fork, stable-release bumps, version-gate sweep, input review)
|
(rebasing the `devplayer0` nixpkgs fork, stable-release bumps, version-gate sweep, input review)
|
||||||
follow the guided procedure in [`docs/nixpkgs-upgrade.md`](docs/nixpkgs-upgrade.md).
|
follow the guided procedure in [`docs/nixpkgs-upgrade.md`](docs/nixpkgs-upgrade.md).
|
||||||
@@ -177,6 +180,10 @@ recipient key list (always including `.keys/dev.pub`). Edit secrets with the `ra
|
|||||||
command, which supplies `.keys/dev.key` as the identity. The `.keys/` directory (dev + deploy
|
command, which supplies `.keys/dev.key` as the identity. The `.keys/` directory (dev + deploy
|
||||||
private keys) is required for editing secrets, deploying, and running dev VMs.
|
private keys) is required for editing secrets, deploying, and running dev VMs.
|
||||||
|
|
||||||
|
When a recipient list changes, re-encrypt selectively with `ragenix --rekey-one <file>` for each
|
||||||
|
affected secret. `ragenix --rekey` rewrites **every** secret in `secrets/`, burying the real change
|
||||||
|
in churn.
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
- Format with `nixpkgs-fmt` (`fmt`). 2-space indent, `inherit (...)` blocks at the top of `let` —
|
- Format with `nixpkgs-fmt` (`fmt`). 2-space indent, `inherit (...)` blocks at the top of `let` —
|
||||||
@@ -205,7 +212,14 @@ private keys) is required for editing secrets, deploying, and running dev VMs.
|
|||||||
- Commit subjects follow `area/scope: Capitalized summary` (e.g. `nixos/home: ...`); keep logically
|
- Commit subjects follow `area/scope: Capitalized summary` (e.g. `nixos/home: ...`); keep logically
|
||||||
distinct changes in separate commits. Aim for 50-character subjects and do not exceed 72
|
distinct changes in separate commits. Aim for 50-character subjects and do not exceed 72
|
||||||
characters. Wrap commit bodies at 72 columns. A concise body describing the change and its
|
characters. Wrap commit bodies at 72 columns. A concise body describing the change and its
|
||||||
rationale is welcome when the subject alone does not provide enough context.
|
rationale is welcome when the subject alone does not provide enough context — keep it to the
|
||||||
|
essentials rather than restating the diff. `Co-Authored-By` is the only trailer used here; do
|
||||||
|
**not** add a `Claude-Session` link (or any other session/tooling trailer).
|
||||||
|
- **"Logically distinct" means unrelated** — two different applications, two boxes that have nothing
|
||||||
|
to do with each other, a drive-by fix that happens to sit in a file you were editing anyway. One
|
||||||
|
piece of work stays in one commit even when it touches a config, several docs and a switch: if the
|
||||||
|
parts only make sense together, splitting them just makes each half unreviewable. Err towards one
|
||||||
|
commit and split when a reader would ask why two things arrived together.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ Not every box fits this pattern, but **colony** and **home** are organised this
|
|||||||
- [`deployment.md`](deployment.md) — deploy-rs, devshell commands, secrets workflow, CI.
|
- [`deployment.md`](deployment.md) — deploy-rs, devshell commands, secrets workflow, CI.
|
||||||
- [`nixpkgs-upgrade.md`](nixpkgs-upgrade.md) — guided procedure for the periodic upgrade of the four
|
- [`nixpkgs-upgrade.md`](nixpkgs-upgrade.md) — guided procedure for the periodic upgrade of the four
|
||||||
nixpkgs channels and home-manager (fork rebase, stable bumps, input review).
|
nixpkgs channels and home-manager (fork rebase, stable bumps, input review).
|
||||||
|
- [`install-box.md`](install-box.md) — guided procedure for installing a new box, from the booted
|
||||||
|
installer through partitioning, the box config, `do-install` and documentation.
|
||||||
|
- [`openwrt-flash.md`](openwrt-flash.md) — guided procedure for flashing a flake-built image onto an
|
||||||
|
OpenWrt box, from the build through pre-flight, `sysupgrade` and verification.
|
||||||
- [`reference/dns.md`](reference/dns.md) — generated forward and reverse DNS record reference.
|
- [`reference/dns.md`](reference/dns.md) — generated forward and reverse DNS record reference.
|
||||||
- [`reference/nixos-options.md`](reference/nixos-options.md) — generated per-option reference for
|
- [`reference/nixos-options.md`](reference/nixos-options.md) — generated per-option reference for
|
||||||
the custom `my.*` NixOS modules.
|
the custom `my.*` NixOS modules.
|
||||||
@@ -53,6 +57,9 @@ colony (physical VM host, ams1)
|
|||||||
├── git ────── Gitea + Gitea Actions runner
|
├── git ────── Gitea + Gitea Actions runner
|
||||||
├── mail ───── Debian VM running mailcow (not NixOS)
|
├── mail ───── Debian VM running mailcow (not NixOS)
|
||||||
└── darts ──── third-party/customer VM (opaque, not NixOS)
|
└── darts ──── third-party/customer VM (opaque, not NixOS)
|
||||||
|
|
||||||
|
portcullis (bare-metal edge box for Nikhef — staged, not yet in service)
|
||||||
|
└── fergal OpenWrt SFP+ switch, staged and moving with it
|
||||||
```
|
```
|
||||||
|
|
||||||
## Site: home
|
## Site: home
|
||||||
|
|||||||
@@ -171,6 +171,43 @@ default `/tmp/xchg/dev.key`), so dev VMs can decrypt the boxes' secrets without
|
|||||||
keys. Dev VMs also get DHCP on `eth0`, an SSH port forward (host 2222 → guest 22), and are
|
keys. Dev VMs also get DHCP on `eth0`, an SSH port forward (host 2222 → guest 22), and are
|
||||||
automatically excluded from deploy targets.
|
automatically excluded from deploy targets.
|
||||||
|
|
||||||
|
## OpenWrt images
|
||||||
|
|
||||||
|
The OpenWrt boxes are not NixOS and are not deployed by this flake, but their firmware is built
|
||||||
|
here. [`openwrt/default.nix`](../openwrt/default.nix) declares one image per box and packages it
|
||||||
|
through [`astro/nix-openwrt-imagebuilder`](https://github.com/astro/nix-openwrt-imagebuilder),
|
||||||
|
which drives OpenWrt's official ImageBuilder — prebuilt target packages assembled into a sysupgrade
|
||||||
|
image, with no cross-toolchain involved.
|
||||||
|
|
||||||
|
| Output | Box | Release |
|
||||||
|
|---|---|---|
|
||||||
|
| `openwrt-fergal` | [fergal](sites/colony/fergal.md) | `snapshot` |
|
||||||
|
| `openwrt-fergal-release` | The same, on the release branch | pinned in `openwrt/default.nix` |
|
||||||
|
|
||||||
|
Both are in `ci`, so images are built and pushed to the Harmonia cache like everything else. Build
|
||||||
|
one with `nix build .#openwrt-fergal`; the result holds the `-squashfs-sysupgrade.bin` to flash,
|
||||||
|
plus a package manifest and an SBOM. Getting it onto the box is a guided procedure of its own —
|
||||||
|
see [`openwrt-flash.md`](openwrt-flash.md).
|
||||||
|
|
||||||
|
Packages are baked into the image rather than installed on the box. OpenWrt's package server keeps
|
||||||
|
only the current build of each feed, so a box that installs packages at runtime stops being able to
|
||||||
|
do so as soon as the feed moves on from the firmware it is running. Adding a package means editing
|
||||||
|
the image's `packages` list and reflashing.
|
||||||
|
|
||||||
|
### The feed pin
|
||||||
|
|
||||||
|
OpenWrt's download server is never at rest: snapshot is rebuilt daily, and
|
||||||
|
`releases/<version>/packages/` is a symlink to the rolling `packages-<major>` feed shared by every
|
||||||
|
point release. Building straight against it fails on hash mismatches and, worse, resolves the
|
||||||
|
package list by import-from-derivation — which would drag *evaluation* of this flake onto the
|
||||||
|
network and let an OpenWrt feed rebuild break `check-system` for unrelated boxes.
|
||||||
|
|
||||||
|
The `openwrt-feeds` input exists to stop that. It holds expanded per-package hashes, so every `.apk`
|
||||||
|
is a plain pinned `fetchurl` and no import-from-derivation is involved. Its generated files run to
|
||||||
|
hundreds of thousands of lines and are rewritten wholesale on each refresh, which is why they live
|
||||||
|
in their own repository rather than here. Refresh the pin with `nix flake update openwrt-feeds`;
|
||||||
|
adding a release or target means adding it to that repo's `pins` and regenerating there first.
|
||||||
|
|
||||||
## CI
|
## CI
|
||||||
|
|
||||||
GitHub/Gitea Actions workflows live in [`.gitea/workflows/`](../.gitea/workflows).
|
GitHub/Gitea Actions workflows live in [`.gitea/workflows/`](../.gitea/workflows).
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
# Installing a box
|
||||||
|
|
||||||
|
Procedure for bringing a new NixOS box into this flake, from bare hardware booted into the custom
|
||||||
|
installer through to a deployable system. Written to be followed by a person or any coding agent; a
|
||||||
|
Claude Code entry point exists at `.claude/skills/install-box/` but the steps below are the
|
||||||
|
canonical source.
|
||||||
|
|
||||||
|
The install is **guided, not automated**: the mechanical steps (probing hardware, partitioning,
|
||||||
|
writing the config, evaluating it) can be done straight through, but stop at the judgment points
|
||||||
|
(marked ⏸) — what the box actually is, wiping disks, and running `do-install` itself. Keep a running
|
||||||
|
summary and present it before any destructive step.
|
||||||
|
|
||||||
|
For the installer image itself — what it contains, how it is built and released — see
|
||||||
|
[`misc/installer.md`](misc/installer.md).
|
||||||
|
|
||||||
|
## Setup facts
|
||||||
|
|
||||||
|
- **Installer access:** the box boots the custom installer (ISO, kexec or netboot) and is reached
|
||||||
|
over SSH as `root` with `.keys/deploy.key`. The devshell sets
|
||||||
|
`INSTALLER_SSH_OPTS = "-i .keys/deploy.key"`; set `INSTALLER` to the address, and
|
||||||
|
`INSTALLER_SSH_PORT` if it is not 22.
|
||||||
|
- **Devshell commands** (from [`devshell/install.nix`](../devshell/install.nix)):
|
||||||
|
`installer-shell [cmd]` and `do-install [--no-bootloader] [--no-substitute] <system>`.
|
||||||
|
- **`INSTALL_ROOT`** is `/mnt` in the installer's environment — everything is mounted under it and
|
||||||
|
`do-install` reads it from the installer rather than assuming.
|
||||||
|
- **`show-hw-config`** is a shell *alias* in the installer (wrapping
|
||||||
|
`nixos-generate-config --show-hardware-config --root $INSTALL_ROOT`), so it needs an interactive
|
||||||
|
shell: `installer-shell bash -lic show-hw-config`. A plain `installer-shell show-hw-config` will
|
||||||
|
not find it.
|
||||||
|
- **Validation:** `check-system <host>` evaluates a system without building it — use it while
|
||||||
|
iterating. Only `build-system` when you need the artifact.
|
||||||
|
- Nix reads the flake through git, so **`git add` new files before evaluating** — an untracked
|
||||||
|
box directory fails with "Path … is not tracked by Git", not a Nix error.
|
||||||
|
|
||||||
|
## Phase 1 — Establish what the box is
|
||||||
|
|
||||||
|
⏸ Settle these before writing anything; they decide where every file goes and they are not
|
||||||
|
recoverable from the hardware:
|
||||||
|
|
||||||
|
1. **Name and site** — the box name doubles as the `nixos.systems.<name>` attribute, the deploy node
|
||||||
|
name and the docs page name. The site decides the directory (`nixos/boxes/<site>/`), the
|
||||||
|
constants block in [`lib/constants.nix`](../lib/constants.nix) it draws prefixes from, and the
|
||||||
|
docs directory (`docs/sites/<site>/`, `docs/remote/`, `docs/mobile/`).
|
||||||
|
2. **Role** — what it does, which decides its modules, networking and firewall config.
|
||||||
|
3. **nixpkgs channel** — `unstable` / `stable` / `mine` / `mine-stable`; match the site's other
|
||||||
|
boxes unless there is a reason not to.
|
||||||
|
4. **Networking** — whether it gets `assignments` now, or bootstraps on DHCP because it is being
|
||||||
|
staged somewhere other than its final home. A box with no assignment still needs a reachable
|
||||||
|
`my.deploy.node.hostname`, since the default (`config.networking.fqdn`) will not resolve.
|
||||||
|
|
||||||
|
## Phase 2 — Reach the installer and inventory the hardware
|
||||||
|
|
||||||
|
With the box booted into the installer and `INSTALLER` set:
|
||||||
|
|
||||||
|
1. Confirm you are talking to the right thing — `installer-shell hostname` reports `installer`, and
|
||||||
|
`/etc/os-release` carries `VARIANT_ID=installer`.
|
||||||
|
2. Collect the inventory you will need for both the config and the docs page: `lscpu`, `free -h`,
|
||||||
|
`lsblk -o NAME,SIZE,TYPE,FSTYPE,MODEL,SERIAL`, `ip -br link`, `ip -br addr`,
|
||||||
|
`lspci -nn | grep -Ei 'ethernet|network|nvme|sata|raid'`, and whether `/sys/firmware/efi` exists.
|
||||||
|
3. Record every NIC's **permanent MAC** against its PCI address — interface naming in Phase 5 pins
|
||||||
|
names to MACs, and the PCI order tells you which physical port is which.
|
||||||
|
4. Run `installer-shell bash -lic show-hw-config` now for the kernel-module lists. Filesystems are
|
||||||
|
not mounted yet, so run it again in Phase 4 for those.
|
||||||
|
|
||||||
|
## Phase 3 — Partition, format and mount
|
||||||
|
|
||||||
|
⏸ Destructive. Check the target disks are the ones you think they are and that nothing on them is
|
||||||
|
wanted, then show the exact command sequence and get confirmation before running it.
|
||||||
|
|
||||||
|
The house layout is a tmpfs root (`my.tmproot`) with three mounts: an ESP at `/boot`, `/nix`, and
|
||||||
|
`/persist` (`neededForBoot = true`). Use **`sgdisk`** for partitioning and put `/nix` and `/persist`
|
||||||
|
on **LVM** so they can be resized later:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sgdisk -Z /dev/<disk>
|
||||||
|
sgdisk \
|
||||||
|
-n 1:0:+2G -t 1:ef00 -c 1:esp \
|
||||||
|
-n 2:0:0 -t 2:8e00 -c 2:lvm \
|
||||||
|
/dev/<disk>
|
||||||
|
partprobe /dev/<disk>
|
||||||
|
|
||||||
|
pvcreate /dev/<disk>p2
|
||||||
|
vgcreate main /dev/<disk>p2
|
||||||
|
lvcreate -L 48G -n <host>-nix main
|
||||||
|
lvcreate -l 100%FREE -n <host>-persist main
|
||||||
|
|
||||||
|
mkfs.vfat -n ESP /dev/<disk>p1
|
||||||
|
mkfs.ext4 -L nix /dev/main/<host>-nix
|
||||||
|
mkfs.ext4 -L persist /dev/main/<host>-persist
|
||||||
|
```
|
||||||
|
|
||||||
|
Conventions worth keeping: volume group `main`, logical volumes `<host>-nix` / `<host>-persist`,
|
||||||
|
and ext4 filesystem labels `nix` and `persist`. Size the ESP and `/nix` to the box — 2 GiB and
|
||||||
|
48 GiB suit a small single-disk box.
|
||||||
|
|
||||||
|
Then mount everything under `$INSTALL_ROOT`, with a tmpfs standing in for the eventual tmpfs root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mount -t tmpfs -o size=2G tmpfs "$INSTALL_ROOT"
|
||||||
|
mkdir -p "$INSTALL_ROOT"/{nix,persist,boot}
|
||||||
|
mount /dev/main/<host>-nix "$INSTALL_ROOT/nix"
|
||||||
|
mount /dev/main/<host>-persist "$INSTALL_ROOT/persist"
|
||||||
|
mount /dev/<disk>p1 "$INSTALL_ROOT/boot"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seed the SSH host key
|
||||||
|
|
||||||
|
The installer generates fresh host keys on every boot, so adopt them as the box's own rather than
|
||||||
|
letting it generate another set on first boot. Copy them onto the persist volume now:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
install -d -m 0755 "$INSTALL_ROOT/persist/etc/ssh"
|
||||||
|
for t in ed25519 rsa; do
|
||||||
|
install -m 0600 "/etc/ssh/ssh_host_${t}_key" "$INSTALL_ROOT/persist/etc/ssh/ssh_host_${t}_key"
|
||||||
|
install -m 0644 "/etc/ssh/ssh_host_${t}_key.pub" "$INSTALL_ROOT/persist/etc/ssh/ssh_host_${t}_key.pub"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
`my.tmproot` persists `services.openssh.hostKeys` at exactly those paths, so the installed system
|
||||||
|
picks them up. This means the box's key is known **before** it first boots, so `my.secrets.key` can
|
||||||
|
be set and its secrets encrypted as part of the same pass — no install, boot, re-encrypt, re-deploy
|
||||||
|
round trip. (For a box already up, the `ssh-get-ed25519 <host>` devshell command prints the same
|
||||||
|
value in the form `my.secrets.key` wants.)
|
||||||
|
|
||||||
|
## Phase 4 — Capture the hardware config
|
||||||
|
|
||||||
|
Re-run `installer-shell bash -lic show-hw-config` with the filesystems mounted.
|
||||||
|
|
||||||
|
Read the whole generated file and carry over **anything** in it that the flake does not already
|
||||||
|
provide — it reflects what was actually detected on this hardware, and the list below is just what
|
||||||
|
usually shows up, not a limit:
|
||||||
|
|
||||||
|
- `boot.initrd.availableKernelModules` and `boot.initrd.kernelModules` (LVM adds `dm-snapshot`)
|
||||||
|
- `boot.kernelModules` (`kvm-intel` / `kvm-amd`) and the microcode attribute
|
||||||
|
- the ESP's `by-uuid` device, and the device paths for `/nix` and `/persist`
|
||||||
|
- anything else it emits — `boot.extraModulePackages`, `hardware.*` attributes, `swapDevices`,
|
||||||
|
additional detected filesystems, `imports` such as `not-detected.nix`
|
||||||
|
|
||||||
|
The test is conflict, not familiarity: drop an option only when a nixfiles module already sets it,
|
||||||
|
and keep it otherwise. The flake's own modules cover the bootloader, `initrd.systemd`,
|
||||||
|
`initrd.services.lvm`, the kernel package and `nixpkgs.hostPlatform` (see
|
||||||
|
[`nixos/modules/common.nix`](../nixos/modules/common.nix) and
|
||||||
|
[`nixos/default.nix`](../nixos/default.nix)), so those are the ones to leave out. Don't paste the
|
||||||
|
file in wholesale either — translate it into the box's own style, and reference LVM volumes as
|
||||||
|
`/dev/main/<host>-nix` rather than the generated `/dev/mapper/main-<host>--nix`.
|
||||||
|
|
||||||
|
## Phase 5 — Write the box config
|
||||||
|
|
||||||
|
Create `nixos/boxes/<site>/<host>/default.nix` (a directory, so per-topic files can be added
|
||||||
|
alongside it later) declaring `nixos.systems.<host>`, and add its path to the `configs` list in
|
||||||
|
[`flake.nix`](../flake.nix). Then `git add` it.
|
||||||
|
|
||||||
|
The minimum is `system`, `nixpkgs`, `home-manager` and a `configuration` with the hardware from
|
||||||
|
Phase 4, the three filesystems, and networking. Beyond that:
|
||||||
|
|
||||||
|
- **Interface naming:** pin names to hardware with `.link` files matching `PermanentMACAddress`,
|
||||||
|
named for speed and index — `et1g0`, `et2g5-0`, `et10g-1`. Never rely on predictable-interface
|
||||||
|
names in the `.network` files.
|
||||||
|
- **Servers** set `my.server.enable = true`.
|
||||||
|
- **Secrets:** set `my.secrets.key` to the ed25519 public key seeded in Phase 3 (the key only, no
|
||||||
|
`root@installer` comment). Note that **every box declares at least one secret** even if its own
|
||||||
|
config declares none: [`nixos/modules/user.nix`](../nixos/modules/user.nix) adds
|
||||||
|
`user-passwd.txt` whenever `my.user.enable` is on, which is the default. So setting
|
||||||
|
`my.secrets.key` always adds the box to that file's recipients, and
|
||||||
|
`ragenix --rekey-one secrets/user-passwd.txt.age` is required — skip it and the box cannot
|
||||||
|
decrypt its user password on first boot. Confirm what the box actually declares with
|
||||||
|
`nix eval .#nixosConfigurations.<host>.config.age.secrets --apply builtins.attrNames`, and
|
||||||
|
re-encrypt each of those files the same way. Create any new secrets with `ragenix -e <path>`.
|
||||||
|
Never use `--rekey`, which rewrites every secret in `secrets/`.
|
||||||
|
- **A box staged away from its final home** gets a bootstrap `.network` taking DHCP, plus
|
||||||
|
`systemd.network.wait-online.anyInterface = true` so boot does not block on unpatched ports, and
|
||||||
|
an explicit `my.deploy.node.hostname`. Comment it as temporary and say what replaces it.
|
||||||
|
|
||||||
|
Validate with `check-system <host>` and fix eval errors before going near the target.
|
||||||
|
|
||||||
|
## Phase 6 — Install
|
||||||
|
|
||||||
|
⏸ The maintainer may want to run this step themselves; ask rather than assume.
|
||||||
|
|
||||||
|
`do-install <host>` builds the system's `toplevel`, `nix copy`s the closure into the installer's
|
||||||
|
`$INSTALL_ROOT` store, points `/nix/var/nix/profiles/system` at it, touches `/etc/NIXOS`, and runs
|
||||||
|
`switch-to-configuration boot` with `NIXOS_INSTALL_BOOTLOADER=1`. It prompts for confirmation and
|
||||||
|
prints the target it resolved.
|
||||||
|
|
||||||
|
- `--no-bootloader` skips the bootloader install (for a box that boots by other means).
|
||||||
|
- `--no-substitute` copies everything from the local store instead of letting the target substitute.
|
||||||
|
|
||||||
|
## Phase 7 — First boot and post-install
|
||||||
|
|
||||||
|
1. Reboot the box off the installer and confirm it comes up: it should get its address, and
|
||||||
|
`hostname` should be the system name. Its SSH host key is the one seeded in Phase 3, so it
|
||||||
|
presents the same fingerprint the installer did.
|
||||||
|
2. **Secrets.** If Phase 5 set `my.secrets.key`, they already decrypt. [`secrets.nix`](../secrets.nix)
|
||||||
|
computes the ragenix recipient list from that key at evaluation time, so nothing needs
|
||||||
|
regenerating — but any secret added to the box later must be re-encrypted for the new recipient
|
||||||
|
list with `ragenix --rekey-one <path>`, one file at a time. Never reach for `ragenix --rekey`:
|
||||||
|
it rewrites every secret in `secrets/` and buries the actual change in churn.
|
||||||
|
3. **Deploy.** `deploy .#<host>` should now work over the `deploy` user. If the box is staged
|
||||||
|
somewhere without its final DNS name, `deploy --hostname <address> .#<host>` overrides the node
|
||||||
|
hostname for one run.
|
||||||
|
|
||||||
|
## Phase 8 — Document it
|
||||||
|
|
||||||
|
Per [`AGENTS.md`](../AGENTS.md), a new box means:
|
||||||
|
|
||||||
|
- a box page under the right docs directory, following the standard layout (H1 + one-line intro;
|
||||||
|
`Source` / `Host` / `nixpkgs` bullets; hardware inventory; `## Role`; `## Network assignments`
|
||||||
|
linking to [`networking.md#box-assignments`](networking.md#box-assignments), or a short
|
||||||
|
explanation if it has none yet; one `##` per topic; `## Notable config files` last);
|
||||||
|
- a row in the site index `README.md` boxes table;
|
||||||
|
- affected prose in [`networking.md`](networking.md) — the assignment tables themselves are
|
||||||
|
CI-generated, so write the prose and leave the tables alone;
|
||||||
|
- the site diagram in [`README.md`](README.md) if the box changes its layout.
|
||||||
@@ -34,8 +34,10 @@ The custom NixOS installer image used to bootstrap new boxes.
|
|||||||
|
|
||||||
## Installing a box
|
## Installing a box
|
||||||
|
|
||||||
The devshell's installer commands ([`devshell/install.nix`](../../devshell/install.nix)) drive
|
The end-to-end procedure — hardware inventory, partitioning, writing the box config, installing and
|
||||||
an install over SSH against a booted installer reachable at `$INSTALLER`:
|
documenting it — is in [`install-box.md`](../install-box.md). The devshell's installer commands
|
||||||
|
([`devshell/install.nix`](../../devshell/install.nix)) drive an install over SSH against a booted
|
||||||
|
installer reachable at `$INSTALLER`:
|
||||||
|
|
||||||
- `installer-shell` — get a shell on the installer.
|
- `installer-shell` — get a shell on the installer.
|
||||||
- `do-install <system>` — builds the system's toplevel, `nix copy`s the closure to the
|
- `do-install <system>` — builds the system's toplevel, `nix copy`s the closure to the
|
||||||
|
|||||||
@@ -266,6 +266,11 @@ On top of that: `p2pTunnels` (`10.100.5.0/24`) holds point-to-point tunnel /30s
|
|||||||
public blocks and the per-customer `mail` / `darts` / `jam` prefixes carry customer-facing
|
public blocks and the per-customer `mail` / `darts` / `jam` prefixes carry customer-facing
|
||||||
services with their own public addresses (announced by BGP, routed via the host).
|
services with their own public addresses (announced by BGP, routed via the host).
|
||||||
|
|
||||||
|
This layout is expected to change: [`portcullis`](sites/colony/portcullis.md) is bare-metal edge
|
||||||
|
hardware headed for Nikhef that will take over most of `estuary`'s routing. It has no colony
|
||||||
|
assignments yet (only a home `hi` one, from being staged at home) and the replacement topology is
|
||||||
|
still being designed.
|
||||||
|
|
||||||
## home
|
## home
|
||||||
|
|
||||||
The home site prefixes (`lib.my.c.home.prefixes`) come from `192.168.64.0/18` and
|
The home site prefixes (`lib.my.c.home.prefixes`) come from `192.168.64.0/18` and
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# Flashing an OpenWrt box
|
||||||
|
|
||||||
|
Guided procedure for putting a flake-built OpenWrt image onto a box. The images themselves are
|
||||||
|
declared in [`openwrt/default.nix`](../openwrt/default.nix) and described in
|
||||||
|
[`deployment.md`](deployment.md#openwrt-images); the boxes are listed on their site pages (today
|
||||||
|
that is [fergal](sites/colony/fergal.md)).
|
||||||
|
|
||||||
|
Packages are baked into the image, so this runs whenever the package list changes — not only for
|
||||||
|
version upgrades. Work through the phases in order; ⏸ marks the point to stop and confirm.
|
||||||
|
|
||||||
|
## Phase 1 — Build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix build .#openwrt-<box>
|
||||||
|
```
|
||||||
|
|
||||||
|
The result holds the `-squashfs-sysupgrade.bin` to flash, plus a `.manifest` listing every package
|
||||||
|
in the image and an SBOM. Check the manifest for the packages the change was meant to add — an
|
||||||
|
unknown package name is not an error at build time, it just silently isn't there.
|
||||||
|
|
||||||
|
## Phase 2 — Pre-flight
|
||||||
|
|
||||||
|
Confirm on the box:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
grep -E 'RELEASE|REVISION' /etc/openwrt_release # what is running now
|
||||||
|
mount | grep -E ' / | /overlay | /rom ' # flash or RAM? (see below)
|
||||||
|
uci get network.lan.ipaddr # will it come back reachable?
|
||||||
|
cat /lib/upgrade/keep.d/* # what survives the flash
|
||||||
|
df -h /tmp # room for the image
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flash or RAM matters.** A box booted normally shows a squashfs `/rom` plus a jffs2 `/overlay`;
|
||||||
|
one booted from an initramfs has `/` on tmpfs. The initramfs case has its own hazards — see
|
||||||
|
[Flashing from an initramfs](sites/colony/fergal.md#flashing-notes).
|
||||||
|
|
||||||
|
**Check the address is in UCI**, not just present on the interface. An address added by hand with
|
||||||
|
`ip` disappears on reboot and the box comes back unreachable.
|
||||||
|
|
||||||
|
`keep.d` normally lists `/etc/config/`, `/etc/dropbear/authorized_keys` and the dropbear host keys,
|
||||||
|
so an ordinary flash preserves both access and identity. Verify rather than assume — losing
|
||||||
|
`authorized_keys` on a box reachable only over SSH means a serial console recovery.
|
||||||
|
|
||||||
|
## Phase 3 — Back up
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sysupgrade -b /tmp/<box>-config-backup.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
Fetch it with `ssh <box> 'cat /tmp/…' > local.tar.gz`. **`scp` does not work** — these boxes have no
|
||||||
|
`/usr/libexec/sftp-server`, so it fails with `Connection closed`. (`scp -O` forces the legacy
|
||||||
|
protocol if you prefer it.)
|
||||||
|
|
||||||
|
For a box being flashed off its **vendor** firmware for the first time, back up the whole flash
|
||||||
|
first — the vendor partitions hold per-unit MAC addresses and licence data that cannot be
|
||||||
|
regenerated. See [fergal's flash layout](sites/colony/fergal.md#flash-layout).
|
||||||
|
|
||||||
|
## Phase 4 — Stage and validate
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh <box> 'cat > /tmp/sysupgrade.bin' < <image>.bin
|
||||||
|
ssh <box> 'sha256sum /tmp/sysupgrade.bin; sysupgrade -T /tmp/sysupgrade.bin'
|
||||||
|
```
|
||||||
|
|
||||||
|
Compare the sha256 against the local file, and require `sysupgrade -T` to exit 0. `-T` validates the
|
||||||
|
image and its device-compatibility metadata without writing anything, which is the last cheap chance
|
||||||
|
to catch a wrong-profile image.
|
||||||
|
|
||||||
|
## Phase 5 — Flash ⏸
|
||||||
|
|
||||||
|
Confirm before this point. It reboots the box and is not interruptible.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh <box> 'setsid sh -c "sleep 2; sysupgrade -v /tmp/sysupgrade.bin" \
|
||||||
|
</dev/null >/tmp/upgrade.log 2>&1 & echo detached'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Detaching matters.** `sysupgrade` kills the SSH session partway through; without `setsid` the
|
||||||
|
upgrade dies with it, potentially after the flash has been erased. `nohup` is not available on these
|
||||||
|
boxes' busybox — use `setsid`.
|
||||||
|
|
||||||
|
Plain `sysupgrade` keeps the config in `keep.d`. Do not reach for `-c` out of caution: it needs
|
||||||
|
`/overlay/upper/etc` and aborts *after* erasing the firmware if that is missing.
|
||||||
|
|
||||||
|
## Phase 6 — Wait and verify
|
||||||
|
|
||||||
|
Poll SSH, not ping. A successful ping returns in milliseconds, so a naive "wait for it to go down"
|
||||||
|
loop finishes before the box has even started rebooting. Sleep between probes and wait on something
|
||||||
|
that only succeeds once userspace is up:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for i in $(seq 1 40); do
|
||||||
|
sleep 15
|
||||||
|
ssh -o ConnectTimeout=5 -o BatchMode=yes <box> 'grep REVISION /etc/openwrt_release' && break
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Expect roughly three minutes. Then confirm the revision changed, the management address returned,
|
||||||
|
the package count matches the manifest, and the new packages are actually present and running.
|
||||||
|
Connecting without host-key overrides also confirms the host keys survived.
|
||||||
|
|
||||||
|
If the box does not return, it needs the serial console — have that confirmed as reachable *before*
|
||||||
|
Phase 5, not after.
|
||||||
@@ -24,9 +24,16 @@ prefixes and routing overview are in the [`colony` section of networking.md](../
|
|||||||
| [`git`](git.md) | Gitea + Gitea Actions runner |
|
| [`git`](git.md) | Gitea + Gitea Actions runner |
|
||||||
| [`mail`](mail.md) | Debian VM running mailcow (not NixOS) |
|
| [`mail`](mail.md) | Debian VM running mailcow (not NixOS) |
|
||||||
| [`darts`](darts.md) | Third-party/customer VM (not NixOS) |
|
| [`darts`](darts.md) | Third-party/customer VM (not NixOS) |
|
||||||
|
| [`portcullis`](portcullis.md) | Bare-metal edge box for Nikhef; being staged, not yet in service |
|
||||||
|
|
||||||
The applications running on `shill` are listed on its own page — see
|
The applications running on `shill` are listed on its own page — see
|
||||||
[shill/README.md](shill/README.md#containers).
|
[shill/README.md](shill/README.md#containers).
|
||||||
|
|
||||||
`mail` and `darts` are host-defined VMs whose guest operating systems are managed out of band; their
|
`mail` and `darts` are host-defined VMs whose guest operating systems are managed out of band; their
|
||||||
pages document only what this repository controls.
|
pages document only what this repository controls.
|
||||||
|
|
||||||
|
`portcullis` is new hardware headed for Nikhef that will take over most of `estuary`'s edge routing.
|
||||||
|
It is not deployed yet and the resulting topology is still being worked out. It travels with
|
||||||
|
[`fergal`](fergal.md), an OpenWrt SFP+ switch whose firmware this flake builds; both are staged at
|
||||||
|
home for now, borrowing the home fabric through
|
||||||
|
[jim](../home/switches.md#fergal-portculliss-switch).
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# fergal
|
||||||
|
|
||||||
|
An 8-port SFP+ switch running OpenWrt, bought to sit in front of
|
||||||
|
[`portcullis`](portcullis.md) at Nikhef. It is physically at home for now, on the bench alongside
|
||||||
|
`portcullis` while that box is staged.
|
||||||
|
|
||||||
|
- **Source:** firmware built by this flake — [`openwrt/default.nix`](../../../openwrt/default.nix)
|
||||||
|
- **Host:** bare metal
|
||||||
|
- **OS:** OpenWrt (snapshot), configured through UCI rather than RouterOS or a UniFi controller
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
| Component | Inventory |
|
||||||
|
|---|---|
|
||||||
|
| Platform | XikeStor SKS8300-8X; the board itself is branded ONTi ONT-S508CL-8S |
|
||||||
|
| SoC | Realtek RTL9303 (MIPS 34Kc) |
|
||||||
|
| Memory | 512 MB |
|
||||||
|
| Storage | 32 MiB SPI NOR (`spi0.0`) |
|
||||||
|
| Network | 8×SFP+ (`lan1`…`lan8`) |
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
`portcullis`'s 10G switch. Nothing else depends on it, and it is not part of the home fabric — it
|
||||||
|
is expected to travel to Nikhef with `portcullis` rather than stay behind.
|
||||||
|
|
||||||
|
While staged at home it hangs off jim's spare SFP+ port, so `portcullis` can reach the home `hi`
|
||||||
|
VLAN over 10G: `lan1` uplinks to jim's `sfp-spare`, `lan2` goes to `portcullis`, and the other six
|
||||||
|
cages are empty. See [the home switches](../home/switches.md) for the fabric it borrows.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
fergal has no assignments — it is not managed by the flake. Its management address is
|
||||||
|
`192.168.64.30` on the home `core` VLAN, set in UCI as `network.lan`, with no DNS record; reach it
|
||||||
|
as `ssh root@192.168.64.30`.
|
||||||
|
|
||||||
|
## VLAN configuration
|
||||||
|
|
||||||
|
One bridge (`switch`), with VLAN 1 as the untagged PVID on every port — that's the native VLAN on
|
||||||
|
jim's `sfp-spare`, and `switch.1` is where fergal's own management address lives. `hi` (100) and
|
||||||
|
`lo` (110) are **tagged** members of every port, so a box on any cage can pick them up:
|
||||||
|
|
||||||
|
```
|
||||||
|
uci show network | grep bridge-vlan
|
||||||
|
```
|
||||||
|
|
||||||
|
Tagging all eight rather than just `lan1`/`lan2` keeps a spare cage usable without a reconfigure;
|
||||||
|
there is nothing sensitive behind it while fergal is on the bench.
|
||||||
|
|
||||||
|
**Jumbo frames pass, despite what `ip link` says.** Every DSA port and the `switch` bridge read
|
||||||
|
`mtu 1500`, but the RTL9303 forwards between ports in hardware and isn't bound by those — a
|
||||||
|
`ping -M do -s 8972` from `portcullis` to the `hi` VIP crosses fergal intact, which is what makes
|
||||||
|
the 9000-MTU `hi` VLAN usable over this path. The 1500 does apply to traffic punted to the CPU,
|
||||||
|
i.e. fergal's own management on `switch.1`.
|
||||||
|
|
||||||
|
## Firmware
|
||||||
|
|
||||||
|
The image is built by this flake — see [OpenWrt images](../../deployment.md#openwrt-images) for the
|
||||||
|
outputs and the feed pin. Packages are baked into the image, so adding tooling means editing
|
||||||
|
[`openwrt/default.nix`](../../../openwrt/default.nix) and reflashing rather than installing on the
|
||||||
|
box.
|
||||||
|
|
||||||
|
### Flash layout
|
||||||
|
|
||||||
|
A single 32 MiB SPI NOR chip (`spi0.0`, 64 KiB erase blocks). `kernel` and `rootfs` are
|
||||||
|
sub-partitions of `firmware`, and OpenWrt adds `rootfs_data` as the JFFS2 overlay after a real
|
||||||
|
flash.
|
||||||
|
|
||||||
|
| Partition | Device | Offset | Size |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `u-boot` | `mtd0` | `0x000000` | 1 MiB |
|
||||||
|
| `board-info` | `mtd1` | `0x100000` | 192 KiB |
|
||||||
|
| `syslog` | `mtd2` | `0x130000` | 832 KiB |
|
||||||
|
| `firmware` | `mtd3` | `0x200000` | 30 MiB |
|
||||||
|
|
||||||
|
**`board-info` is irreplaceable.** It holds the unit's MAC addresses (`[vlanmac]` / `[cpumac]`), its
|
||||||
|
`[license]` hash, the stock boot pointers and an SSH host key — only about 1.3 KiB of it is
|
||||||
|
non-blank, and none of it can be regenerated. A full dump of all four partitions, taken before
|
||||||
|
OpenWrt was flashed, is kept outside this repo — 33 MB of images, with per-partition checksums and
|
||||||
|
restore notes. Never write `u-boot` or `board-info` without a confirmed serial/TFTP recovery path.
|
||||||
|
|
||||||
|
### Flashing notes
|
||||||
|
|
||||||
|
The procedure itself is in [`openwrt-flash.md`](../../openwrt-flash.md); what follows is specific to
|
||||||
|
this board.
|
||||||
|
|
||||||
|
Stock u-boot boots `flash:/nos.img` from a JFFS2 filesystem, so OpenWrt's sysupgrade image is
|
||||||
|
itself a JFFS2 image containing `nos.img` rather than a raw kernel + squashfs. Two things bite when
|
||||||
|
flashing from an initramfs, as during the initial install:
|
||||||
|
|
||||||
|
- **`sysupgrade -c` does not work.** It needs `/overlay/upper/etc`, which doesn't exist when running
|
||||||
|
from RAM, and it aborts *after* `mtd erase firmware` has already run — leaving the box with no
|
||||||
|
bootable firmware until the job is finished. Pass the config as an explicit tarball instead
|
||||||
|
(`tar czf`, then `sysupgrade -f <tarball> …`).
|
||||||
|
- **The working management address may not be in UCI.** If it was set by hand with `ip` while UCI
|
||||||
|
still held the stock address, the box comes back unreachable. Write it into `network.lan` and
|
||||||
|
commit before flashing.
|
||||||
|
|
||||||
|
Neither applies to an ordinary flash-to-flash upgrade, where `sysupgrade` keeps `/etc/config` and
|
||||||
|
the files listed in `/lib/upgrade/keep.d/` by default. Dropbear host keys are regenerated by a flash
|
||||||
|
that doesn't preserve them, so clear the old `known_hosts` entry afterwards.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`openwrt/default.nix`](../../../openwrt/default.nix) — image definition and baked-in package list.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# portcullis
|
||||||
|
|
||||||
|
A bare-metal box destined for Nikhef, intended to take over most of the colony edge
|
||||||
|
routing currently done by the [`estuary`](estuary.md) VM.
|
||||||
|
|
||||||
|
- **Source:** [`nixos/boxes/colony/portcullis/`](../../../nixos/boxes/colony/portcullis)
|
||||||
|
- **Host:** bare metal
|
||||||
|
- **nixpkgs:** `mine-stable`
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
| Component | Inventory |
|
||||||
|
|---|---|
|
||||||
|
| Platform | Mini PC (no vendor DMI strings) |
|
||||||
|
| CPU | Intel N150 (4 cores / 4 threads) |
|
||||||
|
| Memory | 8 GiB |
|
||||||
|
| Storage | One 128 GB NVMe SSD (`nvme0n1`), partitioned as a 2 GiB ESP plus an LVM PV holding the `nix` and `persist` volumes |
|
||||||
|
| Network | Four Intel I226-V 2.5 GbE ports (`et2g5-0`…`et2g5-3`) and one dual-port Intel 82599ES 10 GbE SFP+ card (`et10g-0`, `et10g-1`) |
|
||||||
|
| Management | JetKVM (HDMI/USB KVM with virtual media) |
|
||||||
|
|
||||||
|
## Role
|
||||||
|
|
||||||
|
Not yet in service. The eventual job is to be the physical edge for the colony site at Nikhef,
|
||||||
|
taking over most of what `estuary` does today — WAN termination, firewalling and NAT, BGP for
|
||||||
|
AS211024 and DNS. Some of that functionality stays on `estuary`, and the surrounding network
|
||||||
|
topology will change with the move, so the split is not settled yet. Until it is, the config in
|
||||||
|
this repository covers only what is needed to boot and reach the box.
|
||||||
|
|
||||||
|
## Network assignments
|
||||||
|
|
||||||
|
`portcullis` has no colony assignments yet — those land alongside the routing config once the
|
||||||
|
topology is decided. While it is staged at home it holds a single home `hi` assignment, listed in
|
||||||
|
[`networking.md#box-assignments`](../../networking.md#box-assignments).
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
- The four I226-V ports are named `et2g5-0`…`et2g5-3` and the 82599ES SFP+ ports `et10g-0` /
|
||||||
|
`et10g-1`, pinned by permanent MAC address in `.link` files.
|
||||||
|
- Bootstrap: a single `.network` matches every `et2g5-*` port and takes DHCP on the home `lo` VLAN,
|
||||||
|
so whichever port happens to be patched in brings the box up. `wait-online.anyInterface` keeps
|
||||||
|
boot from blocking on the unpatched ports.
|
||||||
|
- kea registers the DHCP hostname, so while staged the box also answers to `portcullis.dyn.h.nul.ie`.
|
||||||
|
- `my.deploy.node.hostname` is the `hi` address, taken from the assignment rather than written out,
|
||||||
|
since there is no colony FQDN for the box yet.
|
||||||
|
|
||||||
|
### 10G to the home `hi` VLAN
|
||||||
|
|
||||||
|
`et10g-0` runs over fibre to [`fergal`](fergal.md), which uplinks to jim's `sfp-spare` port. That
|
||||||
|
uplink is untagged VLAN 1, so `hi` is carried tagged on a `lan-hi` VLAN interface rather than on the
|
||||||
|
port itself; the physical link takes the `hi` jumbo MTU so the whole path is consistent with the
|
||||||
|
rest of the VLAN. `lan-hi` carries the static assignment, resolves through the router VIPs like
|
||||||
|
every other `hi` client, and its gateway route outranks the DHCP default, so the 10G path is
|
||||||
|
preferred while the 2.5G one stays as a fallback.
|
||||||
|
|
||||||
|
Both jim and `fergal` tag `hi` and `lo` along that path. It exists only while the box is staged at
|
||||||
|
home — `fergal` goes to Nikhef with it.
|
||||||
|
|
||||||
|
The other SFP+ port, `et10g-1`, is unused.
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
A single NVMe SSD, following the usual tmpfs-root layout: a 2 GiB ESP at `/boot`, then one LVM PV
|
||||||
|
in volume group `main` carrying `portcullis-nix` (48 GiB, `/nix`) and `portcullis-persist` (the
|
||||||
|
remainder, `/persist`).
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
`my.secrets.key` is the SSH host key adopted from the installer session at install time (seeded onto
|
||||||
|
the persist volume before first boot), so secrets could be encrypted for the box without waiting for
|
||||||
|
it to come up. The box declares nothing of its own yet — only the default `user-passwd.txt` that
|
||||||
|
`my.user` brings in.
|
||||||
|
|
||||||
|
## Notable config files
|
||||||
|
|
||||||
|
- [`nixos/boxes/colony/portcullis/default.nix`](../../../nixos/boxes/colony/portcullis/default.nix) — hardware, filesystems and bootstrap networking.
|
||||||
@@ -11,7 +11,8 @@ carried untranslated because a single ONT makes it unique on the fabric — see
|
|||||||
[the WAN path](#the-digiweb-wan-path-trunked-vlan-10--pvid-140) and
|
[the WAN path](#the-digiweb-wan-path-trunked-vlan-10--pvid-140) and
|
||||||
[why not translation](#why-not-translation-for-one-ont). The router side lives in
|
[why not translation](#why-not-translation-for-one-ont). The router side lives in
|
||||||
[river.md](river.md); the logical network map in [networking.md](../../networking.md). The Wi-Fi
|
[river.md](river.md); the logical network map in [networking.md](../../networking.md). The Wi-Fi
|
||||||
APs that hang off these switches are in [aps.md](aps.md).
|
APs that hang off these switches are in [aps.md](aps.md). A fourth switch, **fergal**, hangs off jim
|
||||||
|
but belongs to the colony site — see [fergal](#fergal-portculliss-switch).
|
||||||
|
|
||||||
## The switches
|
## The switches
|
||||||
|
|
||||||
@@ -34,13 +35,14 @@ chips); brian cannot rewrite tags, only trunk/PVID them.
|
|||||||
The two WAN sources enter at the top: the Virgin Media modem lands on **jim** (VLAN 130), and the
|
The two WAN sources enter at the top: the Virgin Media modem lands on **jim** (VLAN 130), and the
|
||||||
Digiweb **ONT** lands on **brian**. Both `jim` and `brian` are edge switches that uplink down into
|
Digiweb **ONT** lands on **brian**. Both `jim` and `brian` are edge switches that uplink down into
|
||||||
the **dave** core; the home boxes hang off dave's 100G ports, with backup links up to jim. jim's
|
the **dave** core; the home boxes hang off dave's 100G ports, with backup links up to jim. jim's
|
||||||
`wan-pon-in` (`sfp-sfpplus2`) is a spare SFP+ port, unused today.
|
second SFP+ port (`sfp-spare`, `sfp-sfpplus2`) feeds [fergal](#fergal-portculliss-switch), which
|
||||||
|
[`portcullis`](../colony/portcullis.md) hangs off while it is staged at home.
|
||||||
|
|
||||||
```
|
```
|
||||||
Virgin Media cable modem Digiweb ONT
|
Virgin Media cable modem Digiweb ONT
|
||||||
stream WAN, VLAN 130 river WAN, management + VLAN 10
|
stream WAN, VLAN 130 river WAN, management + VLAN 10
|
||||||
| |
|
| |
|
||||||
jim brian
|
jim ---- 10G ---- fergal ---- portcullis brian
|
||||||
| 10G trunk 802.3ad LAG |
|
| 10G trunk 802.3ad LAG |
|
||||||
+--------------------+ +---------------+
|
+--------------------+ +---------------+
|
||||||
| |
|
| |
|
||||||
@@ -146,8 +148,13 @@ VLAN 140 also spans `brian-downlink,palace` (it carries a few other members too)
|
|||||||
this is plain tagged bridging.
|
this is plain tagged bridging.
|
||||||
|
|
||||||
**jim (RouterOS)** — carries **none** of the Digiweb WAN path: no translation rules, and no VLAN
|
**jim (RouterOS)** — carries **none** of the Digiweb WAN path: no translation rules, and no VLAN
|
||||||
10/140/141 rows. `wan-pon-in` (`sfp-sfpplus2`) sits at `pvid=1` as a spare port. jim only handles
|
10/140/141 rows. jim only handles stream's VLAN-130 WAN and the LAN VLANs. `sfp-spare`
|
||||||
stream's VLAN-130 WAN and the LAN VLANs.
|
(`sfp-sfpplus2`) stays at `pvid=1` — the switch feeding `portcullis` is reached over VLAN 1
|
||||||
|
untagged — and is a **tagged** member of `hi` (100) and `lo` (110) so those reach `portcullis`:
|
||||||
|
```
|
||||||
|
/interface bridge vlan set [find bridge=main vlan-ids=100] tagged=...,sfp-spare
|
||||||
|
/interface bridge vlan set [find bridge=main vlan-ids=110] tagged=...,sfp-spare
|
||||||
|
```
|
||||||
|
|
||||||
## Switches must not route
|
## Switches must not route
|
||||||
|
|
||||||
@@ -200,11 +207,22 @@ Each ONT port must also be a tagged member of bridge VLAN 10 for correct egress
|
|||||||
piece that otherwise shows up as pppd "Timeout waiting for PADO"). The pins bypass the FDB, so the
|
piece that otherwise shows up as pppd "Timeout waiting for PADO"). The pins bypass the FDB, so the
|
||||||
two ISP sessions never mix.
|
two ISP sessions never mix.
|
||||||
|
|
||||||
**Why a new switch:** jim (the only box with spare SFP+ *and* the translation feature) has just
|
**Why a new switch:** jim (the only box with spare SFP+ *and* the translation feature) had just
|
||||||
**one** free SFP+ port, so it can't host two ONTs. The plan is a dedicated
|
**one** free SFP+ port — now taken by fergal — so it can't host two ONTs. The plan is a dedicated
|
||||||
**CRS305-1G-4S+** (4×SFP+, same Marvell rule support) to land multiple ONTs and do the per-port
|
**CRS305-1G-4S+** (4×SFP+, same Marvell rule support) to land multiple ONTs and do the per-port
|
||||||
translation there, feeding distinct fabric VLANs up to dave.
|
translation there, feeding distinct fabric VLANs up to dave.
|
||||||
|
|
||||||
|
## fergal (portcullis's switch)
|
||||||
|
|
||||||
|
**fergal** is an 8-port SFP+ switch running OpenWrt, hanging off jim's `sfp-spare` port. It belongs
|
||||||
|
to [`portcullis`](../colony/portcullis.md) rather than to the home fabric — it is here only while
|
||||||
|
that box is staged at home, and goes to Nikhef with it. Nothing in the home fabric depends on it.
|
||||||
|
|
||||||
|
What it borrows from home is VLAN 1 untagged on the jim uplink (fergal's own management sits on it,
|
||||||
|
at `192.168.64.30` on core) plus tagged `hi` (100) and `lo` (110), so `portcullis` can reach those
|
||||||
|
over 10G. The switch itself — VLAN layout, flash layout, firmware and flashing notes — is
|
||||||
|
documented in [sites/colony/fergal.md](../colony/fergal.md).
|
||||||
|
|
||||||
## Accessing the switches
|
## Accessing the switches
|
||||||
|
|
||||||
The switches resolve by **short hostname** on the home network — the home routers serve their
|
The switches resolve by **short hostname** on the home network — the home routers serve their
|
||||||
|
|||||||
Generated
+92
-9
@@ -8,7 +8,7 @@
|
|||||||
"ragenix",
|
"ragenix",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"systems": "systems_7"
|
"systems": "systems_8"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761656077,
|
"lastModified": 1761656077,
|
||||||
@@ -315,6 +315,27 @@
|
|||||||
"url": "https://flakehub.com/f/hercules-ci/flake-parts/0.1"
|
"url": "https://flakehub.com/f/hercules-ci/flake-parts/0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-parts_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"openwrt-imagebuilder",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772408722,
|
||||||
|
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
@@ -335,7 +356,7 @@
|
|||||||
},
|
},
|
||||||
"flake-utils_10": {
|
"flake-utils_10": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_9"
|
"systems": "systems_10"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1709126324,
|
"lastModified": 1709126324,
|
||||||
@@ -353,7 +374,7 @@
|
|||||||
},
|
},
|
||||||
"flake-utils_11": {
|
"flake-utils_11": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_10"
|
"systems": "systems_11"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705309234,
|
"lastModified": 1705309234,
|
||||||
@@ -503,7 +524,7 @@
|
|||||||
},
|
},
|
||||||
"flake-utils_9": {
|
"flake-utils_9": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_8"
|
"systems": "systems_9"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731533236,
|
"lastModified": 1731533236,
|
||||||
@@ -910,6 +931,51 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"openwrt-feeds": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-unstable"
|
||||||
|
],
|
||||||
|
"openwrt-imagebuilder": [
|
||||||
|
"openwrt-imagebuilder"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1787353688,
|
||||||
|
"narHash": "sha256-YDEm+ev3BpDS9Sq1ByVv0i5QQCE1yezpjWVhcNBBjCE=",
|
||||||
|
"owner": "devplayer0",
|
||||||
|
"repo": "openwrt-feeds",
|
||||||
|
"rev": "a30b2b5f83c7d1fffca2146453e8d5866b882da4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "devplayer0",
|
||||||
|
"repo": "openwrt-feeds",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"openwrt-imagebuilder": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts_2",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-unstable"
|
||||||
|
],
|
||||||
|
"systems": "systems_7"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1787302424,
|
||||||
|
"narHash": "sha256-fg9pKzO6OeQhe/bY2CpHb6QnHq57P/icwQz35GF/R/8=",
|
||||||
|
"owner": "astro",
|
||||||
|
"repo": "nix-openwrt-imagebuilder",
|
||||||
|
"rev": "276c1dd6346f50231392e97b3a9987c9dd57da28",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "astro",
|
||||||
|
"repo": "nix-openwrt-imagebuilder",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pyproject-nix": {
|
"pyproject-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -976,6 +1042,8 @@
|
|||||||
"nixpkgs-mine-stable": "nixpkgs-mine-stable",
|
"nixpkgs-mine-stable": "nixpkgs-mine-stable",
|
||||||
"nixpkgs-stable": "nixpkgs-stable",
|
"nixpkgs-stable": "nixpkgs-stable",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
|
"openwrt-feeds": "openwrt-feeds",
|
||||||
|
"openwrt-imagebuilder": "openwrt-imagebuilder",
|
||||||
"ragenix": "ragenix",
|
"ragenix": "ragenix",
|
||||||
"sharry": "sharry"
|
"sharry": "sharry"
|
||||||
}
|
}
|
||||||
@@ -1073,6 +1141,21 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"systems_11": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"systems_2": {
|
"systems_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
@@ -1150,16 +1233,16 @@
|
|||||||
},
|
},
|
||||||
"systems_7": {
|
"systems_7": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1680978846,
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
"narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=",
|
||||||
"owner": "nix-systems",
|
"owner": "nix-systems",
|
||||||
"repo": "default",
|
"repo": "x86_64-linux",
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
"rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-systems",
|
"owner": "nix-systems",
|
||||||
"repo": "default",
|
"repo": "x86_64-linux",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,6 +58,15 @@
|
|||||||
# harmonia.url = "github:devplayer0/harmonia/cache-config-daemon-store";
|
# harmonia.url = "github:devplayer0/harmonia/cache-config-daemon-store";
|
||||||
harmonia.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
harmonia.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
|
||||||
|
# Firmware building for the OpenWrt boxes, which aren't managed by this flake otherwise.
|
||||||
|
# `openwrt-feeds` pins the package feeds; without it, evaluation would reach the OpenWrt
|
||||||
|
# download server over import-from-derivation and break on hashes that upstream rotates daily.
|
||||||
|
openwrt-imagebuilder.url = "github:astro/nix-openwrt-imagebuilder";
|
||||||
|
openwrt-imagebuilder.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
openwrt-feeds.url = "github:devplayer0/openwrt-feeds";
|
||||||
|
openwrt-feeds.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
openwrt-feeds.inputs.openwrt-imagebuilder.follows = "openwrt-imagebuilder";
|
||||||
|
|
||||||
# Packages not in nixpkgs
|
# Packages not in nixpkgs
|
||||||
sharry.url = "github:eikek/sharry";
|
sharry.url = "github:eikek/sharry";
|
||||||
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
@@ -181,6 +190,7 @@
|
|||||||
# Systems
|
# Systems
|
||||||
nixos/installer.nix
|
nixos/installer.nix
|
||||||
nixos/boxes/colony
|
nixos/boxes/colony
|
||||||
|
nixos/boxes/colony/portcullis
|
||||||
nixos/boxes/tower
|
nixos/boxes/tower
|
||||||
nixos/boxes/home/stream.nix
|
nixos/boxes/home/stream.nix
|
||||||
nixos/boxes/home/palace
|
nixos/boxes/home/palace
|
||||||
@@ -258,7 +268,9 @@
|
|||||||
deploy = recurseIntoAttrs (pkgs.deploy-rs.lib.deployChecks self.deploy);
|
deploy = recurseIntoAttrs (pkgs.deploy-rs.lib.deployChecks self.deploy);
|
||||||
};
|
};
|
||||||
|
|
||||||
packages = flattenTree (import ./pkgs { inherit lib pkgs; });
|
packages = flattenTree (
|
||||||
|
(import ./pkgs { inherit lib pkgs; }) //
|
||||||
|
(import ./openwrt { inherit pkgs inputs; }));
|
||||||
|
|
||||||
devShells.default = shell;
|
devShells.default = shell;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib.my) net;
|
||||||
|
inherit (lib.my.c.colony) domain;
|
||||||
|
home = lib.my.c.home;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixos.systems.portcullis = {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
nixpkgs = "mine-stable";
|
||||||
|
home-manager = "mine-stable";
|
||||||
|
|
||||||
|
assignments = {
|
||||||
|
# Staging-only: the 10G link lands on the home hi VLAN until portcullis is racked.
|
||||||
|
hi = {
|
||||||
|
domain = home.domain;
|
||||||
|
mtu = home.hiMTU;
|
||||||
|
ipv4 = {
|
||||||
|
address = net.cidr.host 41 home.prefixes.hi.v4;
|
||||||
|
mask = 22;
|
||||||
|
gateway = home.vips.hi.v4;
|
||||||
|
};
|
||||||
|
ipv6 = {
|
||||||
|
iid = "::6:1";
|
||||||
|
address = net.cidr.host (65536*6+1) home.prefixes.hi.v6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = { lib, pkgs, config, assignments, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkMerge;
|
||||||
|
inherit (lib.my) mkVLAN networkdAssignment;
|
||||||
|
inherit (lib.my.c) networkd;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
hardware = {
|
||||||
|
enableRedistributableFirmware = true;
|
||||||
|
cpu = {
|
||||||
|
intel.updateMicrocode = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
boot = {
|
||||||
|
kernelModules = [ "kvm-intel" ];
|
||||||
|
kernelParams = [ "intel_iommu=on" ];
|
||||||
|
initrd = {
|
||||||
|
availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
|
||||||
|
kernelModules = [ "dm-snapshot" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems = {
|
||||||
|
"/boot" = {
|
||||||
|
device = "/dev/disk/by-uuid/1A70-EBCB";
|
||||||
|
fsType = "vfat";
|
||||||
|
options = [ "fmask=0022" "dmask=0022" ];
|
||||||
|
};
|
||||||
|
"/nix" = {
|
||||||
|
device = "/dev/main/portcullis-nix";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
"/persist" = {
|
||||||
|
device = "/dev/main/portcullis-persist";
|
||||||
|
fsType = "ext4";
|
||||||
|
neededForBoot = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking = { inherit domain; };
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
pciutils
|
||||||
|
usbutils
|
||||||
|
ethtool
|
||||||
|
lm_sensors
|
||||||
|
smartmontools
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.network = {
|
||||||
|
# Only some ports are patched in while the box is being staged, so don't block
|
||||||
|
# boot on the others coming up.
|
||||||
|
wait-online.anyInterface = true;
|
||||||
|
|
||||||
|
netdevs = mkVLAN "lan-hi" home.vlans.hi;
|
||||||
|
|
||||||
|
links = {
|
||||||
|
"10-et2g5-0" = {
|
||||||
|
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:48";
|
||||||
|
linkConfig.Name = "et2g5-0";
|
||||||
|
};
|
||||||
|
"10-et2g5-1" = {
|
||||||
|
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:49";
|
||||||
|
linkConfig.Name = "et2g5-1";
|
||||||
|
};
|
||||||
|
"10-et2g5-2" = {
|
||||||
|
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:4a";
|
||||||
|
linkConfig.Name = "et2g5-2";
|
||||||
|
};
|
||||||
|
"10-et2g5-3" = {
|
||||||
|
matchConfig.PermanentMACAddress = "00:d0:b4:05:ed:4b";
|
||||||
|
linkConfig.Name = "et2g5-3";
|
||||||
|
};
|
||||||
|
|
||||||
|
"11-et10g-0" = {
|
||||||
|
matchConfig.PermanentMACAddress = "60:be:b4:2e:9b:a2";
|
||||||
|
linkConfig.Name = "et10g-0";
|
||||||
|
};
|
||||||
|
"11-et10g-1" = {
|
||||||
|
matchConfig.PermanentMACAddress = "60:be:b4:2e:9b:a3";
|
||||||
|
linkConfig.Name = "et10g-1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networks = {
|
||||||
|
# TODO: replace with the colony assignments and routing config once portcullis is
|
||||||
|
# racked at Nikhef. Until then it is staged at home, so every 2.5G port takes DHCP on
|
||||||
|
# the lo VLAN and whichever one is patched in provides connectivity. kea registers
|
||||||
|
# the DHCP hostname, making the box reachable as `portcullis.dyn.h.nul.ie`.
|
||||||
|
"80-bootstrap" = {
|
||||||
|
matchConfig.Name = "et2g5-*";
|
||||||
|
DHCP = "yes";
|
||||||
|
networkConfig.IPv6PrivacyExtensions = "no";
|
||||||
|
linkConfig.RequiredForOnline = "routable";
|
||||||
|
};
|
||||||
|
|
||||||
|
# 10G up to jim's spare SFP+ port via an intermediary switch. That uplink is
|
||||||
|
# untagged VLAN 1, so hi has to be tagged on its own interface.
|
||||||
|
"81-et10g-0" = {
|
||||||
|
matchConfig.Name = "et10g-0";
|
||||||
|
vlan = [ "lan-hi" ];
|
||||||
|
networkConfig = networkd.noL3;
|
||||||
|
linkConfig = {
|
||||||
|
# The carrier has to allow hi's jumbo frames before lan-hi can take that MTU
|
||||||
|
MTUBytes = toString home.hiMTU;
|
||||||
|
RequiredForOnline = "no";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"82-lan-hi" = mkMerge [
|
||||||
|
(networkdAssignment "lan-hi" assignments.hi)
|
||||||
|
{ networkConfig = home.vlanDns "hi"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
my = {
|
||||||
|
# As above: no colony assignment yet, so deploy over the staging hi address.
|
||||||
|
deploy.node.hostname = assignments.hi.ipv4.address;
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAUolR93Byg+Daw8pUYHVpQ34ioxSc2C8vzj9F4KbqMs";
|
||||||
|
};
|
||||||
|
|
||||||
|
server.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{ pkgs, inputs }:
|
||||||
|
# Firmware for the OpenWrt boxes. They are not NixOS and are not deployed by this flake - these
|
||||||
|
# outputs only build a sysupgrade image, which is then flashed by hand (see the box's docs page).
|
||||||
|
#
|
||||||
|
# Baking packages into the image is the only reliable way to have them: OpenWrt's package server
|
||||||
|
# keeps just the current build of each feed, so a box installing packages at runtime is broken as
|
||||||
|
# soon as the feed moves on from the firmware it is running.
|
||||||
|
let
|
||||||
|
inherit (inputs) openwrt-imagebuilder openwrt-feeds;
|
||||||
|
|
||||||
|
# Fallback to the release branch. Snapshot tracks OpenWrt main, which is where the rtl930x target
|
||||||
|
# is actually being developed; the release runs a much older kernel. Both are pinned by
|
||||||
|
# `openwrt-feeds`, so neither moves until that input is updated.
|
||||||
|
release = "25.12.5";
|
||||||
|
|
||||||
|
mkImage = args: openwrt-imagebuilder.lib.build (args // {
|
||||||
|
inherit pkgs;
|
||||||
|
cachePath = openwrt-feeds.cachePaths.${args.release};
|
||||||
|
});
|
||||||
|
|
||||||
|
# fergal, the 8-port SFP+ switch (XikeStor SKS8300-8X, board-branded ONTi ONT-S508CL-8S)
|
||||||
|
fergal = {
|
||||||
|
target = "realtek";
|
||||||
|
variant = "rtl930x";
|
||||||
|
profile = "xikestor_sks8300-8x";
|
||||||
|
packages = [ "luci" "ip-full" "ip-bridge" "ethtool-full" "luci-app-sfp-info" ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
openwrt-fergal = mkImage (fergal // { release = "snapshot"; });
|
||||||
|
openwrt-fergal-release = mkImage (fergal // { inherit release; });
|
||||||
|
}
|
||||||
+78
-74
@@ -1,76 +1,80 @@
|
|||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHNqUFR5ZyBkWHB2
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHNqUFR5ZyArcmVy
|
||||||
MHNUSnRIQkc4OENhN2dVdVlFZFRlWW5oVW9WbENaTGcwK2ZnQVFRCkQyYjdNaEtK
|
aWRwblUvTDlpdzVjTy9GdmsxUSswVlBoa01WU0J0WjVPNTlaazJZCnpjYW14dkU5
|
||||||
SXE5RExTMm9EZC9GSEJGcWNabUgyOERBZmFmV0VqRFVXWTgKLT4gc3NoLWVkMjU1
|
RFBZS1B2V0J3U2ZkUzJCZlN3WUZ0QzUvRGc3QkUyL3VXRWsKLT4gc3NoLWVkMjU1
|
||||||
MTkgRExNZUZnIEhJNHN3c0lGL1lrMTA0ZHV2bHdPZ0dveDNBNVlzazEwbU9NcVZl
|
MTkgRExNZUZnIGFFanNQbkFRQzJxcU5heE8xRlM2clZTaldSR3M2SzVyMHJDakFt
|
||||||
Z3RKelUKaWEwTTZvSTA0T2pKSGVoc3gwL3VPWjNPOFk3dTNjYVo5S2tPWUNtV3Fw
|
eWNHU1EKcWFka3hQajV5d1NiaENUNFhOUlpoSFlJUm8xSWNXVE5HaGM3blp0OVAy
|
||||||
dwotPiBzc2gtZWQyNTUxOSAzYkIzWmcgMEN2dDJiRHYrQzRIb1JJVXp1V0ZyMkdu
|
TQotPiBzc2gtZWQyNTUxOSAzYkIzWmcgdFNqcnJoWjh1SDN5Vml5UC8rZ1NXSi82
|
||||||
RWNtKy93QVR4SVJkK1VXTlUyQQpNdUU1c1NOdi9ZbmFTNHJBWmNTZFp2NDZORWp1
|
Sm11QXJKUXI5Ulc5Mi9rL3UxawpzSFVXQzBQbUZFM3l2aHlIU1dzREMvRXdrMWFL
|
||||||
ZzZzMzU3ZWFVYU1Lc1k0Ci0+IHNzaC1lZDI1NTE5IHErMFhjdyBiVS9ZeVE5Vytp
|
cEptWW4yVWF6aTJTVUdBCi0+IHNzaC1lZDI1NTE5IHErMFhjdyBCTnhBMG5RcTdt
|
||||||
a3p2c2xYM28rM0Q1UWMyNkQxYWRScStGRGVhTTYvQWc4ClNvSksydmhid20yTzNC
|
MHg5aVN2ZmZQR3VTcFo1K3lBMTh3c2dBRjlzRWdjM2pvCkpwZTFZVExJc25aLzFy
|
||||||
ZHF2b252MWwzRG9Zdi9uRVpqL1BacGRaemo5OEkKLT4gc3NoLWVkMjU1MTkgWkIz
|
OWZsNjVzMmNRREJ4ME56TVRneEZIdUdqODVZZnMKLT4gc3NoLWVkMjU1MTkgWkIz
|
||||||
ZTZRIFVZUTVjNS9pak9JSnF4N2JOMEZINmtKTzU5OUxHbHRKeng2cldvK2NXU2sK
|
ZTZRIDlvYURqSFRVNUdEM1lIV3oxQ00zMG5CNXIyNnhrVXpFd3VhS0IwTDhqRlkK
|
||||||
T0M3QTZJU2lqMk9nRGl2c3QwNTlWOEwyYVJUK3pleEtMZ0lYbm9TSmVUZwotPiBz
|
UzhsN3hLSUpQZ2lrNHNVd0s1aXBIY3ZaVm0rZjlXU0RSbU1Bb2h2NlBNawotPiBz
|
||||||
c2gtZWQyNTUxOSBqNjdGWFEgRzRXYTBxQWduN2QvZk84NXVWVHlQN2RiS0pkYXM2
|
c2gtZWQyNTUxOSBqNjdGWFEgaUsxSzNGS09nMGo0eXlsUVdDL2R5UjVXYm9PZ3R1
|
||||||
U2cvM0JKRXZvTjAxdwpCbWdMZVpMaXBBNHRRZjN4aU5lNmhRNmMzSnBIWUNtbW1w
|
R0cra1JWbldnREJ3UQozeVYveGNUTExDUEJjT254NGlzTGxLSkl2akpqRnVmWU0y
|
||||||
ZStLaXlUL09ZCi0+IHNzaC1lZDI1NTE5IGMwVE5hUSBxK09aSTRaUzZ4VnArMlF4
|
Z09oZFQ0YnFzCi0+IHNzaC1lZDI1NTE5IGMwVE5hUSBIQlZSU3VOYythUHo2NERV
|
||||||
ZFZvNmUrR1hPKzB1SUdRS2Z2dmgyVlROOUVZCllUcDNCSEpVUmVUN2RSYjZ5aDdp
|
cE1rbDNlazMzZk1CMlJaM295K09POUZUc1dJCkZ1Ui9DZ2JsUm9FWithQmMwcHpm
|
||||||
SWhCVFlwcWxJMkxodEwrQXNDOTh0TlUKLT4gc3NoLWVkMjU1MTkgbjhDcFV3IHJ4
|
SkNTcEtpSWVJdjJoZG1KY29hSEIvdDQKLT4gc3NoLWVkMjU1MTkgbjhDcFV3ICtP
|
||||||
Y1krN2hHTjhOakZSZ3VNNEZnYVVLb1BTZ01iT2dyRjdUdkRodit0QUkKRitpVFJB
|
dUUwMGx4WEtkQ21ySEFMREVXMHJaSitPalkxdXpPZTJJczZsUEI1MWMKVzRaOE10
|
||||||
WDU2eGw2aUhQZFcrMTB6MU5VTURPNE5HbUFYendOMnNDRXRQcwotPiBzc2gtZWQy
|
MEhjSHFVZW1RKzVDNnBYV2EyQXFTc2ozcVZlb0g4Z20zV3hROAotPiBzc2gtZWQy
|
||||||
NTUxOSBWN2xnR3cgcUNRZkp6R0llR0o0RmhvanBoamdoNVBKRWl0Sm1sQVhBRGJw
|
NTUxOSBWN2xnR3cgSmY1VEE1TjVROTU2U3lvMFNuaVhKNXlrNm1GV3NPSGtIZzhJ
|
||||||
MFFDcmdUbwpENlEvaTh4OUhVQ0VTeXBGMTBoajd1eE42YzYvc0ZvcWlVYU1HWnkr
|
YkVNV1hXSQpZVzhlMnd4OTViN1ZmZlI2TkRMZ29sSU8rY0ZxcUhxY1U3UlM5MHRi
|
||||||
R0FnCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyBFdEpIT3F1MGVtZ0ppN05hVDBLRjZz
|
N0VBCi0+IHNzaC1lZDI1NTE5IGpJOFJBZyAybmhuVGIzd1V3cCtoS2UxdVJ5S1p6
|
||||||
eTRWdjZkMGM0SWpqeVJpRXRGc2pRClVpTlB6Q3lCZ3FXd1g1eXU3Y3grRVBJRjBC
|
UmowTEpvSDA2Mjl3Q0dBeENhUXlJCmFCWkZ5VVBKUnRtTGlvcUtMblJWNlVPTHRa
|
||||||
aERTanp4dDhGRFdteThNNTgKLT4gc3NoLWVkMjU1MTkgVCtzYkdBIEpselBValht
|
RWdJY3kyL2dyUTV4Y09CWTgKLT4gc3NoLWVkMjU1MTkgVCtzYkdBIFE0R1hTaVAr
|
||||||
TTNtY2lTYWg3VHBUTWNIemdiQXpHd01jMS9BeERMclQ0RGcKYU85dnN3ZEJyQmZZ
|
aVBObnNVdkx2YVJ4TTJKYk9QUVhEbXlFQW1ESXVmWTBSbDAKVkJwRlZNVlBwNVJx
|
||||||
ZE9ZYXl4Ym1BZlBkcm9jMXBhZ1J3aUlxR1dPNm96dwotPiBzc2gtZWQyNTUxOSBo
|
WE9vVjl2OXFQcnZUTCtSS0NVQ1dFUmJXaDlhcVYwSQotPiBzc2gtZWQyNTUxOSBo
|
||||||
TWE0bncgcXdIK21EVDVMZGhMMEltQ3RNbkx5NDhGVWRVdU4wZlhUNDY0bEZTcEZG
|
TWE0bncgc0o4b2VsUDZsRDFlaFlJWEpkNU5jRERnWGJ0blJMbXhkR3RWQ1gxNkpB
|
||||||
QQpUSGZOb0FoSTgxckp0R3dxVjVPZkQ0b3Z5WXpjU1Q1Qy8zaGNVNzh4ZGJvCi0+
|
dwp4YU1WdGpJNUtQc09lRDdFVXNZdThWUEVnZDFvdVhUaW1JZUdQaGlId0NRCi0+
|
||||||
IHNzaC1lZDI1NTE5IGV5cTNkZyBwaGF3NXVNWUVQUUpuUm9pVHVRK1NoV1FmVFMz
|
IHNzaC1lZDI1NTE5IGV5cTNkZyBJTzdrSFdVTERDVk5hNHZsL1N6U010Y1ltZkpY
|
||||||
dys1bE9WaDdNV0RXZFRVCmVJSUx0VkQvbDRjOWdvNlhCNm9RSnhnUHFpYnp0ZjVM
|
VS9vUXl6N1FDV3pvWUhNCm1UUHZNTUlrMjdybkVEaWV1NmVlY2hFeklJZndFQUpP
|
||||||
UG82UElhM3R6MFkKLT4gc3NoLWVkMjU1MTkgN1dROVBBIERKYnFGQm9pVlIzYWxu
|
VGRNbzZ2SXVPVncKLT4gc3NoLWVkMjU1MTkgN1dROVBBIDdzKzBGUGNXZTYramx2
|
||||||
M3Fza0RucE9SczQzRk5ialU2R215L1NwcVU2bW8Ka2hCL2JSU1c1bEhFS0t3VnEy
|
VnAzYlcvOTZ6MTRtSGZTRFY0SFluQWMwTVdsMncKTG83dWdTbldMbnRBMVkwSnBu
|
||||||
YWtaMG5mZHBxYjNkK0JQUjlmVHJYSmNUMAotPiBzc2gtZWQyNTUxOSBnU3hQMFEg
|
QkxDTUhhUERXeUl2UXVEaldvRjZtQXZsbwotPiBzc2gtZWQyNTUxOSBnU3hQMFEg
|
||||||
VHhRN0VEV0xGL0hJUHd1V0drWVJzaWtucXJ2c2xMUVpYMFc2TklLUGdYVQp0VWZj
|
SnBrUFhPa3dmMC84V1E0VVlyUC9VME1HWThPaDNxKzZLZC9Ybnd5aHdsYwpxQk1m
|
||||||
UEZGeGhaRDh4SmFsek93ejBUM1A3OVorTWFmcWt0QkVCZmV0QU5ZCi0+IHNzaC1l
|
SW9TY2NOSExZeVJGY3NUbVcvMm5OUytjUnhJR1ZFV1NZUDRqdnRrCi0+IHNzaC1l
|
||||||
ZDI1NTE5IFZGY3c1ZyBmOHgraG50b2NiREZIcjRacTUwZ1Z3R2h0QXlHMTl6SFNJ
|
ZDI1NTE5IFZGY3c1ZyBac29qNmN5aGhpNDVVRHNsc1NIYzViZUdZK2tnSzFTc3pr
|
||||||
Qlc4MTFtT2k4CkVSdzY3cEVpR3J3L0szMXBVdmd2OUFsWGJwanRtSGl3QmRyREhJ
|
SWxFcXFONzNrCmxRK1haWkJ6aXU3amNPZ2hlMnovUDllTGMvSGZiTnNJWjhZN2k3
|
||||||
WjRwS2sKLT4gc3NoLWVkMjU1MTkgaGtidHZnIGVVNDZzenBDRlRmeW4ybUJqamRD
|
VnFmMzgKLT4gc3NoLWVkMjU1MTkgaGtidHZnIG41VkQyQW9rcmY2N2E2Qmc2bUds
|
||||||
UFFpWDY1ZjJuK1VQNWJJSGIyaFJnbmcKcmg3b284YmZQUWt0clBjVDZVZk5CUUlo
|
bWpUSWsxVHMvS2ZGUXhvSjNnanA5d0EKRE1IQ3UzNWE4VFdaQXZGakNscEE3RkQ3
|
||||||
aFRWWUwzVmVPcFBDaW1xKzRqSQotPiBzc2gtZWQyNTUxOSBldDJ6cFEgWEZpOXdx
|
V2FESTdIWGRFOWV2QmpuaWZOYwotPiBzc2gtZWQyNTUxOSBldDJ6cFEgM1NwL3Jt
|
||||||
bWo3NnZYSjFTdldoSDBBMVVobHRXYWJjZEd2RVBIanRrQUZROApkZG82RUZHSkRH
|
WDBHQmErN2JzNUVTelVqb1dRMzlha3NYVEVvRGRrMW9jN0ZoWQpkZ3N1TUExRVhz
|
||||||
TGkyTG1tRUlRMEg5MVRwRHNjTE9UL1BMRUpDdXVLZ2tVCi0+IHNzaC1lZDI1NTE5
|
aEE1QXFEbytySkdlcmpyV0JvRzFpRUZJc0VlenhBNDg0Ci0+IHNzaC1lZDI1NTE5
|
||||||
IFpiTEpXQSB0ZWtnRFVWKzRkSU45enFadkx6N3FpRmE1MnByZEljd3FyWGFyd2Ja
|
IENrT1RXUSBCSU8xbTBNaXRqK0kzZVZOUGo3ZmZYd09sMTd6UHJvU2t5SUJBams1
|
||||||
QVNvClpiZkxsQ04zR2pzOXBtODdnbjdSaHBtSVFVT3V2aFdyZ2FoaytISmNzR0UK
|
R0FjCjEzSEN0ZXd2NmI1M2xvWG1CRTNtcUZZZHhjOVpqemNXS3ByQWtmSy9MdmcK
|
||||||
LT4gc3NoLWVkMjU1MTkgWk5xSW9nIHMvWElOMTgvR0ZveHZLSlVqNW9PSmNvdm92
|
LT4gc3NoLWVkMjU1MTkgWmJMSldBIEx6TUdIT1I4VEIzbXFBdWh0eFZKSnN5R2pZ
|
||||||
ZW92dHhvbG8vRzl2NFdqMFEKUktLWEJuQ25LM1J6OXBvRlRlMEFEeXlhV3M1Yk85
|
VjhYejVtczZSTko5Ty93M2cKNUlBUHFKd0JwNFFHYk9pcnpTcVYvWmZrL3BIWDUx
|
||||||
Wk0rZWJMQ3U2SVYrcwotPiBzc2gtZWQyNTUxOSBxTGpxeVEgODlpYkhNQmFkNjlp
|
cW9vOEpkM1J1emorSQotPiBzc2gtZWQyNTUxOSBaTnFJb2cgY1VUU3BJeFBRYkN4
|
||||||
ZUYyZ0VDdzBsTmk5TGVPU0VTRnZlSUdMazN3cXB4awpGbEhWMVR0N0NzTGljeEpw
|
aWFsWEVWL2NHenYxODcvcnJHZXhRbERFU3YzQlZ4UQo4Z000SGJia01MdHpQU1kw
|
||||||
dWtrTXR0QW5CVHE5enA2dXZZNm55ajVSa3NVCi0+IHNzaC1lZDI1NTE5IEJhUWxS
|
VjRLWXhGczJNWWNMdVhWV09TUXB2UE1PejZZCi0+IHNzaC1lZDI1NTE5IHFManF5
|
||||||
ZyBJc2xSZUJkaEd5U1EyV0J6T2pUU1ZWMnRPSzYwNFRERndsdThYeFFQSUVJCmlT
|
USAzOWRKUTZDVTVpVlFuWGgrdlNYN2RBQXArbFNoN1k4OWxQR0VIZHRUWXlvCi9Y
|
||||||
OWh1dnpNRDU5WlNSaW5FTUZ5QVVHSmw0NUpQMklOb3JYU3FSM3ZTWEUKLT4gc3No
|
blkrellUVVROYUMyWHdBK1NHdjQ5YWs2blpvbS9JZVkybUVPbFRxb2sKLT4gc3No
|
||||||
LWVkMjU1MTkgcytxUmZnIERvYU1PNXJkWEs0VkI5YWNuY3ZGK2xpVkpxN01zbHA2
|
LWVkMjU1MTkgQmFRbFJnIDhoR0lXTlNLQ3Z2WTZXQUFlQ25odmJPeFI1TWIyWUlv
|
||||||
cm9DUzk1WHIrRWsKZVRGWUwrdmVnOTh4clFHdm1yL0JuSVRpVldjWkMwUkdFTk5J
|
SG02SjFYR29HQ1EKKzlqaTdMWmpwVCtQWDFDZlk3aXQ0L2t3YkZudHAzSC9WK2Vr
|
||||||
LzNlT0dZUQotPiBzc2gtZWQyNTUxOSA2MkpjY0EgTXI5QVZySXpsQlVoTE51c29j
|
L3RXbW44SQotPiBzc2gtZWQyNTUxOSBzK3FSZmcgYzFia3NxQWJvOFFqa3g2ZkFL
|
||||||
MWltaUZHZUlPUEo1WXZNdUUzSWdWRU94MApMVUYzYlhNZDc5RTRzc0NxNW5PblU0
|
YVlXOTRzdTFUZWIvV1piM2RDSDh0Z21nUQptQ1dSSC83Wnd4cnJQcDNPRlhtV24v
|
||||||
WndIV1FZTm51ZlhQdWFQa2NNS25FCi0+IHNzaC1lZDI1NTE5IC9oeC9kQSBWUkJS
|
THZ6bUd6Q25SU0Q4b3R6Y0d3dkQ0Ci0+IHNzaC1lZDI1NTE5IDYySmNjQSBCcFhE
|
||||||
ZmR2a3BDdHRrVUhqejFjZFI4cWw1MEkrRWUwdHZHZEloemtUT2pBCmFYU21lRllo
|
SUVnRzZCQitpNjd1S0h4VmxWSzBDMlkrbURLVlZ5ZzIzWE1TUlZrClF0bVh3UU5k
|
||||||
MTlic204cU41Vi95dFBMSSs2eWtVSzJndG1keWdVeUgxNFEKLT4gc3NoLWVkMjU1
|
TFRvWlc2Z2pXMzhiY3ZyN2g3akhQa25zY3NhSEhWV05DUFEKLT4gc3NoLWVkMjU1
|
||||||
MTkgSEovSjdBIGhhck53d1ZzYVY5ZjF5VHpXYVBDMGZ5SGdTL3B4NHQ2a3lENGti
|
MTkgL2h4L2RBIHc0UWp1WEdNWnJsaUpVTmU5Q2tITGNNYkRFcEllUENTMDgyOCtG
|
||||||
NXhZVGMKV3B0d0IwNm5qM0xDUWdOTEZ3Q3ErWGdNRmtIeTBMSjV1eDYzMUVYVFpY
|
NCtIM1kKVmV2MjB1WlAva0xKMDdrWE43NUV1YXNOc0FTV0NNbnZuaXpVTWhvc2ZL
|
||||||
cwotPiBzc2gtZWQyNTUxOSBPRXFNc2cgcm9ZOS81emFxd2toNTRFUW1LRi9jU3FF
|
bwotPiBzc2gtZWQyNTUxOSBXekxHSEEgdmJRZU5SVCsxelMxOXIwZ3FLeEhlYURX
|
||||||
VUdYRzRWTm5uV0ZjclJPZmsyQQoxZnRtTzZ5YzRJTVRGalU4NFZhQmZlMjhtY2Nv
|
cmptKzQvZkZUZVJzM1h0UENYbwpTempycGIrU3J0b0FvaTJOek5VZ2RzeTA5NGRC
|
||||||
Q1oyZURhQUpjR2dvRVNRCi0+IHNzaC1lZDI1NTE5IC9FSlh2ZyA5aUNiQk1FZUtU
|
N1JYY3hMYVVLM1diZE44Ci0+IHNzaC1lZDI1NTE5IEhKL0o3QSBSRkZyTXJ1djhJ
|
||||||
S0hta1lOMVlWL1RwdjUvQnl2MTg5VWQrMnNCVERkVkZrCkFraTR4UmFBbXpOR1lq
|
aWt4Mk9iaitSSTdtWk9Eeklvb1VLU2oxVmQ1L0NDYUZzCkdIcmJBQ0RqbFlDdHh4
|
||||||
TTFCSmZmV0R1VjhWb2V0RXdiYkpaek0rdGVsRE0KLT4gWDI1NTE5IEpZSlNOZk5D
|
a0o3Ujd2ZEZPbFJKOFozS3UxR3lldERYaVY5S0EKLT4gc3NoLWVkMjU1MTkgT0Vx
|
||||||
WlZ3VFFpVUx2RHlwblVEZHZyVVNGbHNrZ3hUY1FYQUJNMWMKYlo4dFVnS2hhYk1m
|
TXNnIHgwbTFxV3ZrR0h2SHBocEluZTZmSnBmUStKMkNtRVhTZTJNeVM3akZjVUEK
|
||||||
THZXMktudExKdEE1enlGWUgyM3FiMGpFbmR6RkNyawotPiBGImFlO2V5Ky1ncmVh
|
V3FKZGlxMmw3QzB3VnAxUXBnK2RzdUNUU3MwSHVIcXg3UHFVSyt1QklhSQotPiBz
|
||||||
c2UKdTZXVjRtcTR2TDFITzB1d3J3RG1hejk1am15SEswR05PMFdoTXR2UVpoRE9H
|
c2gtZWQyNTUxOSAvRUpYdmcgSVdJOVdaNW9PdVY5TE9iNjFlY0ZQN001dnNENWR6
|
||||||
dkMvQldlc1FPWlRFSURXN3ppSQoyU0pSRHFIS2I3d1dtTE5OTFFXSTYyR2tTbTZB
|
cU5DSTVpcEMvRHpSUQo5SnZmMmRCdldKQ2VCSC8zaG1yTVMra2p3QkZHNVF3WmNR
|
||||||
RDVROStRCi0tLSAvWEcyR09pQnRhSzN4d2kzSzduOUVtTXd2U25CWmZJdWt5NnNl
|
a2VMNlJpRXNJCi0+IFgyNTUxOSBYU3VVMkttYUc5NVpBU0FSZk5MNXIvRVIxd0w5
|
||||||
RHVieGNBCi411IjgmUKttjX6ljaZGWivstOajx2pkTVLV/zFiEj3jv+KDGy1psZQ
|
RHZxcmdLb2dmaVlTWGxZCm9lNUloSGJxcmYvcFVkc3dDNXNmUkZNbXB0elpwMVBB
|
||||||
no+eatGMO8LeJhGJ6H7TBKOmJhFMfoQp1XKJA8OGY+FGZ98bit04djo3jqbVSOms
|
dWdpSTJZQlgwUk0KLT4gPipeLWdyZWFzZSA8ZCBNcmxSP1BrCjZpcVoxcFNmV2Zi
|
||||||
JSPRTvTxxQx+40yO+ETV+2qkRU1OdJTobz9YvuqGlHrJS8UNN30QMPT0ienu3QTY
|
K0pxeHlKSmJ0ZWNIdm5mYk1Wano5VHZBb2wxTExxT0dKajFNMnZGU1JYODhXT3Jy
|
||||||
Tbo=
|
MElGNG4KUEh2V0MvZkg5UmN3SVJEajQ3NVhXUk5YWFZPVnFyZkpsRjhnCi0tLSBS
|
||||||
|
Ulh4WkhVY3BuYXFicjhoN29DbFJwemlrczFQcUxpMTNaRXFLOWc2YTJFCvATVcNx
|
||||||
|
h0TZBbnm6QBWZVNDRiU8yHFAgaS/25pRvcaixnji3NkeKYYuEEnVSw6oUthhVSSg
|
||||||
|
g222QeHfXortX7m+/zTD0uIhdVm7e+emA8LBxsQEOgeDy33XA3Hi9yX2BFGV7l82
|
||||||
|
NTBlLbCiH0mlFZ6ZO8rtA/nMcriCQUb2QZ+TaSGAop4RHObEeFw=
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
|||||||
Reference in New Issue
Block a user