feat: add sd flash command and rekey ages
This commit is contained in:
parent
36720e2ba5
commit
faf9701a86
11 changed files with 190 additions and 67 deletions
94
flake.nix
94
flake.nix
|
|
@ -98,10 +98,88 @@
|
|||
cargoArtifacts = piArtifacts;
|
||||
}
|
||||
);
|
||||
|
||||
flash-pi-sd = pkgs.writeShellApplication {
|
||||
name = "flash-pi-sd";
|
||||
runtimeInputs = [
|
||||
agenix.packages.${system}.default
|
||||
pkgs.coreutils
|
||||
pkgs.nix
|
||||
pkgs.systemd
|
||||
pkgs.util-linux
|
||||
pkgs.zstd
|
||||
];
|
||||
text = ''
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: flash-pi-sd /dev/sdX" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
device="$1"
|
||||
flake_path=${builtins.toString ./.}
|
||||
image_link="$(mktemp -u /tmp/noisebell-sd-image.XXXXXX)"
|
||||
mount_dir="$(mktemp -d)"
|
||||
key_file="${builtins.toString ./secrets/bootstrap-identity.age}"
|
||||
rules_file="${builtins.toString ./secrets/secrets.nix}"
|
||||
|
||||
cleanup() {
|
||||
if mountpoint -q "$mount_dir"; then
|
||||
sudo umount "$mount_dir"
|
||||
fi
|
||||
rm -rf "$mount_dir"
|
||||
rm -f "$image_link"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [ ! -b "$device" ]; then
|
||||
echo "not a block device: $device" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
boot_part="''${device}1"
|
||||
case "$device" in
|
||||
*[0-9]) boot_part="''${device}p1" ;;
|
||||
esac
|
||||
|
||||
echo "Building bootstrap SD image..."
|
||||
nix build "$flake_path#nixosConfigurations.bootstrap.config.system.build.sdImage" -o "$image_link"
|
||||
|
||||
image="$(echo "$image_link"/sd-image/*.img*)"
|
||||
if [ ! -f "$image" ]; then
|
||||
echo "failed to locate SD image under $image_link/sd-image" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Flashing $image to $device..."
|
||||
if [ "''${image##*.}" = "zst" ]; then
|
||||
zstd -d --stdout "$image" | sudo dd of="$device" bs=4M conv=fsync status=progress
|
||||
else
|
||||
sudo dd if="$image" of="$device" bs=4M conv=fsync status=progress
|
||||
fi
|
||||
sync
|
||||
|
||||
sudo partprobe "$device"
|
||||
sudo udevadm settle
|
||||
|
||||
if findmnt -rn "$boot_part" >/dev/null 2>&1; then
|
||||
sudo umount "$boot_part"
|
||||
fi
|
||||
|
||||
echo "Installing bootstrap age identity onto $boot_part..."
|
||||
sudo mount "$boot_part" "$mount_dir"
|
||||
RULES="$rules_file" agenix -d "$key_file" | sudo tee "$mount_dir/noisebell-bootstrap.agekey" >/dev/null
|
||||
sudo chmod 600 "$mount_dir/noisebell-bootstrap.agekey"
|
||||
sync
|
||||
|
||||
echo "Done. You can now move the card to the Pi and boot it."
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.${system} = {
|
||||
inherit noisebell-cache noisebell-discord;
|
||||
inherit noisebell-cache noisebell-discord flash-pi-sd;
|
||||
default = noisebell-cache;
|
||||
};
|
||||
|
||||
|
|
@ -136,7 +214,14 @@
|
|||
|
||||
nixosConfigurations.bootstrap = nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
modules = [ ./pi/bootstrap.nix ];
|
||||
modules = [
|
||||
agenix.nixosModules.default
|
||||
(import ./pi/module.nix {
|
||||
pkg = noisebell-pi;
|
||||
rev = self.shortRev or "dirty";
|
||||
})
|
||||
./pi/bootstrap.nix
|
||||
];
|
||||
};
|
||||
|
||||
devShells.${system}.default = craneLib.devShell {
|
||||
|
|
@ -145,5 +230,10 @@
|
|||
agenix.packages.${system}.default
|
||||
];
|
||||
};
|
||||
|
||||
apps.${system}.flash-pi-sd = {
|
||||
type = "app";
|
||||
program = "${flash-pi-sd}/bin/flash-pi-sd";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
49
pi/README.md
49
pi/README.md
|
|
@ -26,20 +26,25 @@ boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
|
|||
|
||||
### 1. Flash the SD card
|
||||
|
||||
Preferred: one command builds the bootstrap image, writes it to the SD card, and installs the
|
||||
bootstrap agenix identity onto the boot partition so the full Pi system can come up on first boot:
|
||||
|
||||
```sh
|
||||
nix run .#flash-pi-sd -- /dev/sdX
|
||||
```
|
||||
|
||||
This bootstrap image already includes the normal Noisebell service, Tailscale, and the Pi config.
|
||||
|
||||
Manual build if you need it:
|
||||
|
||||
```sh
|
||||
nix build .#nixosConfigurations.bootstrap.config.system.build.sdImage
|
||||
dd if=result/sd-image/*.img of=/dev/sdX bs=4M status=progress
|
||||
```
|
||||
|
||||
Boot the Pi. It connects to the Noisebridge WiFi automatically and is discoverable via mDNS as `noisebridge-pi.local`.
|
||||
Boot the Pi. It connects to the Noisebridge WiFi automatically.
|
||||
|
||||
### 2. Find the Pi
|
||||
|
||||
```sh
|
||||
ping noisebridge-pi.local
|
||||
```
|
||||
|
||||
### 3. SSH host key
|
||||
### 2. SSH host key
|
||||
|
||||
Grab the key and add it to `secrets/secrets.nix`:
|
||||
|
||||
|
|
@ -59,7 +64,7 @@ in
|
|||
}
|
||||
```
|
||||
|
||||
### 4. Create secrets
|
||||
### 3. Create secrets
|
||||
|
||||
```sh
|
||||
cd secrets
|
||||
|
|
@ -68,6 +73,29 @@ agenix -e inbound-api-key.age # key the cache uses to poll us
|
|||
agenix -e tailscale-auth-key.age # tailscale auth key
|
||||
```
|
||||
|
||||
### 4. Bootstrap agenix identity
|
||||
|
||||
The Pi uses a dedicated bootstrap age identity stored at `/boot/noisebell-bootstrap.agekey` to
|
||||
decrypt its runtime secrets, so first boot does not depend on the machine's freshly generated SSH
|
||||
host key.
|
||||
|
||||
To refresh recipients after changing `secrets/secrets.nix`:
|
||||
|
||||
```sh
|
||||
cd secrets
|
||||
agenix -r
|
||||
```
|
||||
|
||||
If you use `nix run .#flash-pi-sd -- /dev/sdX`, this file is installed automatically.
|
||||
|
||||
To install the bootstrap identity manually onto a flashed card before first boot:
|
||||
|
||||
```sh
|
||||
cd secrets
|
||||
agenix -d bootstrap-identity.age > /boot/noisebell-bootstrap.agekey
|
||||
chmod 600 /boot/noisebell-bootstrap.agekey
|
||||
```
|
||||
|
||||
### 5. SSH access
|
||||
|
||||
Add your public key to `configuration.nix`:
|
||||
|
|
@ -80,6 +108,9 @@ users.users.root.openssh.authorizedKeys.keys = [
|
|||
|
||||
### 6. Deploy
|
||||
|
||||
After first boot, the Pi should already be running the normal service stack from the flashed image.
|
||||
Use this only for later updates:
|
||||
|
||||
```sh
|
||||
nixos-rebuild switch --flake .#pi --target-host root@noisebell
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,32 +1,8 @@
|
|||
{ modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports = [ "${modulesPath}/installer/sd-card/sd-image-aarch64.nix" ];
|
||||
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
|
||||
networking.hostName = "noisebridge-pi";
|
||||
|
||||
networking.wireless = {
|
||||
enable = true;
|
||||
networks = {
|
||||
"Noisebridge" = {
|
||||
psk = "noisebridge";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.avahi = {
|
||||
enable = true;
|
||||
nssmdns4 = true;
|
||||
publish = {
|
||||
enable = true;
|
||||
addresses = true;
|
||||
};
|
||||
};
|
||||
|
||||
services.openssh.enable = true;
|
||||
users.users.root.openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE40ISu3ydCqfdpb26JYD5cIN0Fu0id/FDS+xjB5zpqu"
|
||||
imports = [
|
||||
"${modulesPath}/installer/sd-card/sd-image-aarch64.nix"
|
||||
./configuration.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{ config, pkgs, ... }:
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
system.stateVersion = "24.11";
|
||||
|
|
@ -10,16 +10,14 @@
|
|||
networks."Noisebridge".psk = "noisebridge";
|
||||
};
|
||||
|
||||
services.avahi = {
|
||||
enable = true;
|
||||
nssmdns4 = true;
|
||||
publish = {
|
||||
enable = true;
|
||||
addresses = true;
|
||||
};
|
||||
};
|
||||
services.avahi.enable = false;
|
||||
|
||||
# Decrypted at runtime by agenix
|
||||
age.identityPaths = [
|
||||
"/boot/noisebell-bootstrap.agekey"
|
||||
"/etc/ssh/ssh_host_ed25519_key"
|
||||
];
|
||||
|
||||
age.secrets.tailscale-auth-key.file = ../secrets/tailscale-auth-key.age;
|
||||
age.secrets.pi-to-cache-key.file = ../secrets/pi-to-cache-key.age;
|
||||
age.secrets.cache-to-pi-key.file = ../secrets/cache-to-pi-key.age;
|
||||
|
|
@ -32,7 +30,10 @@
|
|||
inboundApiKeyFile = config.age.secrets.cache-to-pi-key.path;
|
||||
};
|
||||
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
nix.settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
|
||||
services.tailscale = {
|
||||
enable = true;
|
||||
|
|
|
|||
BIN
secrets/bootstrap-identity.age
Normal file
BIN
secrets/bootstrap-identity.age
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -1,7 +1,8 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 Ziw7aw luObn0XSH0tR4UpGDc2QWUFGSpwVuBuGhmgCWW/IlGs
|
||||
hJRSk4yw3EzD0meybEcpJ8CVmnROuriLVmTJAtd+mdM
|
||||
-> ssh-ed25519 uKftJg t/1U0LiOFgtiMzxELdnv4NZKWR3O8Oj1zQKi1nWWXHg
|
||||
BuBBODNVO8bq9yf5idOC7/dUTgsxPd4a56JNcbTQUIQ
|
||||
--- jhW7YACeM6wl4AUih6GQ9Qx9eaOHkNIS8BYp8vroD7k
|
||||
s–lHhu9ûGš¥8<êX†j車{^žÎ„”N oÞ9êœÆy†ÉŠ}¤ÔB𠍮È}ZëE<C3AB>†aܧ:Öžð#úÔpÕ8ZÛåyÚNåí3-×NdP³$JÅ€é<E282AC>
PìÛ\
|
||||
-> ssh-ed25519 Ziw7aw W4nd317o0ON6n006hwUNIops3L7VngNqDqJ1bG7tCyg
|
||||
xyjvZOOzA0u3e+Cba99LR8J5JAkl6muHuspJ+b8dEog
|
||||
-> ssh-ed25519 uKftJg bX0GHUPdD4hR2yxLNx8ho689or7FNHXPA8iV9Af1q2s
|
||||
B91XIdsnHfAKdKfu8jmHKKptA5OomQ5sHvXfLLUeNbs
|
||||
--- 8zoH1w5ywsbDN5Xt50sE4BUgXoq12mr5b4rSVYMLEB8
|
||||
CŽb;*œã‚´*;í5œ#BL^ñƒT¯úE¢<45>%ÚÎçáZ8†r‰wݧ#‚r-îG¢éÖKÔ—t…|Ê
|
||||
pí³ÿÑòâÙ_ßÄÊ»\s»N´C‚UG
|º‡*i(6ÇÔA•k/µy
|
||||
Binary file not shown.
|
|
@ -1,9 +1,11 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 Ziw7aw fWhlaYGRUca7VIr427vosdJGvOyWsywZrfhYRbV2hiU
|
||||
qUKuoolDRtKRs27nCSbzrDGO9q7JVuIK8LcyVlqFj/o
|
||||
-> ssh-ed25519 jcT/MQ yEqLDa+E44c/PSY4bGCHKsJiPGcPUNxE5ihFUcBRwVI
|
||||
svHSjYLKfGvbQgQXk/P4yfo4Rh8iQP446iibaIz82Po
|
||||
-> ssh-ed25519 uKftJg HC2fqTtYg6WDUUuXdMKwHRBvD+bDrwtiuTbNCzOUV1I
|
||||
5UX0bDfIjN2TXfZLBy7dmy8WUuoGBmkPrcx6EH2j0WA
|
||||
--- uiiOp5m+x+lJR2mjawNrZgOtTs1F1EGaLKmre7BIopE
|
||||
¼×D<08>7|7@}TD>>[i˜’_äE~Ë»0…<30>Ö´ê<C2B4>ª–}0çþTã£ã.ìÝZšmò±Øb¶E|<®½ »ñýªjó.Ž
|
||||
-> ssh-ed25519 Ziw7aw TMz4xBHmy5btMk5ETWyvjZcjj0QwF3F6Iqt2QmkwVwk
|
||||
KvcY409ZakEUO7OrmDrwseMQv/w7i3B7uDiXjls/5qE
|
||||
-> ssh-ed25519 jcT/MQ Y5TqVlHy3fM+CmQIBu1x18C3wjHtRZCHJ9dhFyVAR0I
|
||||
WPqxEZrpAm0wLYT4s9e1uxSuDjhwUwIOL4BvDtvqytU
|
||||
-> X25519 ZqSHgIStt6Ru3osVvMA1M5sydoY+CeZ56temQyCFIVY
|
||||
4tPA6VITlAzJxCFGVreKK1B6rrHm+ka4ELwnzYrMKbQ
|
||||
-> ssh-ed25519 uKftJg OR8VgPUuEvS/0Gc5c92IlAp4DKKYcRzBbSh1tX9ddzg
|
||||
DZYYx9ngwEUTmj/JaP2XnCQHjpPY8WYgOEDlOfZPLeA
|
||||
--- 4iDfaqdSLiW0doVijoZC5ckxiCmsmVWJi5Kvaxic2Ng
|
||||
è÷VV;2†h<>&e@sÏx#d=Y?iðw½´ÉÍF<15>OÝc×/ûš(™¨àœ›¼føkØí‹ƒ—¿}[¡¤'=B@ô‡¨Â!wÔ
|
||||
|
|
@ -2,11 +2,33 @@ let
|
|||
jet = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE40ISu3ydCqfdpb26JYD5cIN0Fu0id/FDS+xjB5zpqu";
|
||||
pi = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKmJFZAVJUjDziQ7XytVhQIv+Pm9hnbVU0flxZK17K5E";
|
||||
server = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAING219cDKTDLaZefmqvOHfXvYloA/ErsCGE0pM022vlB";
|
||||
piBootstrap = "age1sfqn46dgztr35dtyhpzyzzam5m6kcqu495qs7fcsdxtac56pc4dsj3t862";
|
||||
in
|
||||
{
|
||||
"pi-to-cache-key.age".publicKeys = [ jet pi server ];
|
||||
"cache-to-pi-key.age".publicKeys = [ jet pi server ];
|
||||
"tailscale-auth-key.age".publicKeys = [ jet pi ];
|
||||
"discord-token.age".publicKeys = [ jet server ];
|
||||
"discord-webhook-secret.age".publicKeys = [ jet server ];
|
||||
"bootstrap-identity.age".publicKeys = [ jet ];
|
||||
"pi-to-cache-key.age".publicKeys = [
|
||||
jet
|
||||
pi
|
||||
piBootstrap
|
||||
server
|
||||
];
|
||||
"cache-to-pi-key.age".publicKeys = [
|
||||
jet
|
||||
pi
|
||||
piBootstrap
|
||||
server
|
||||
];
|
||||
"tailscale-auth-key.age".publicKeys = [
|
||||
jet
|
||||
pi
|
||||
piBootstrap
|
||||
];
|
||||
"discord-token.age".publicKeys = [
|
||||
jet
|
||||
server
|
||||
];
|
||||
"discord-webhook-secret.age".publicKeys = [
|
||||
jet
|
||||
server
|
||||
];
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue