🧶
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clarification of notes, remove old TODOs, fix port number

+107 -47
+2 -2
.sops.yaml
··· 1 1 keys: 2 - - &desktop age1g0504l29u3rv05vqvdqkeup2m8l6k2hth8j79e6eyrg28nzjtvhqknl2kq 2 + - &sauce age1g0504l29u3rv05vqvdqkeup2m8l6k2hth8j79e6eyrg28nzjtvhqknl2kq 3 3 - &mikan age10t7j8zw0xa35ruu4n7qfgxfc025s7j0d8yryl23fkq89dlyz9f8szugcxm 4 4 5 5 creation_rules: 6 6 - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ 7 7 key_groups: 8 8 - age: 9 - - *desktop 9 + - *sauce 10 10 - *mikan
+6 -2
README.md
··· 1 1 # (ノ◕ヮ◕)ノ*:・゚✧dotfiles(⌒_⌒;) ♥ 2 2 3 - ## Targets: 3 + Reproducible NixOS config for the Pi with secrets kept encrypted in git and 4 + deployed remotely from my Macbook. 4 5 5 - - Mikan → Raspberry Pi 4B 6 + | Alias | Role | Machine | 7 + |---|---|---| 8 + | Sauce | Laptop (admin) | macOS (aarch64-darwin) | 9 + | Mikan | Target | Raspberry Pi 4 (aarch64-linux) |
+1 -3
configuration.nix
··· 23 23 }; 24 24 25 25 sops = { 26 - # TODO: Explain why this works even having the file located in the Pi's folder 27 26 age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; 28 27 defaultSopsFile = ./secrets/mikan.yaml; 29 28 secrets = { ··· 45 44 services = { 46 45 openssh = { 47 46 enable = true; 48 - ports = [ 2200 ]; 47 + ports = [ 22 2200 ]; 49 48 settings = { 50 - # TODO: Don't forget to turn this off after testing the config 51 49 PasswordAuthentication = true; 52 50 PermitRootLogin = "no"; 53 51 };
+97 -39
docs/notes.md
··· 1 1 # sops-nix setup on NixOS (Raspberry Pi 4 from macOS) 2 2 3 - ## Goal 4 - 5 3 Migrate a plain `configuration.nix` to Nix Flakes and encrypt secrets using 6 4 `sops-nix`, deploying from macOS to a Raspberry Pi 4. 7 5 8 - ## Stack 6 + ## 1. Overview 7 + 8 + ### 1.1 Goal 9 + 10 + Reproducible NixOS config for the Pi with secrets kept encrypted in git and 11 + deployed remotely from the laptop. 12 + 13 + ### 1.2 Stack 9 14 10 15 - **sops** — encrypts secrets in git 11 16 - **age** — encryption backend (derived from existing ED25519 SSH keys via `ssh-to-age`) 12 17 - **sops-nix** — NixOS module that decrypts secrets at boot 13 18 - **deploy-rs** — deploys from macOS to the Pi over SSH with automatic rollback 14 19 20 + ### 1.3 Hosts 21 + 22 + | Alias | Role | Machine | 23 + |---|---|---| 24 + | Sauce | Laptop (admin) | macOS (aarch64-darwin) | 25 + | Mikan | Target | Raspberry Pi 4 (aarch64-linux) | 26 + 15 27 --- 16 28 17 - ## Setup steps 29 + ## 2. Initial setup 18 30 19 - ### 1. Desktop — derive age identity from your SSH key 31 + ### 2.1 Sauce — derive age identity from SSH key 20 32 21 33 ```bash 22 34 mkdir -p $HOME/.config/sops/age/ ··· 24 36 nix run nixpkgs#ssh-to-age -- -private-key -i $HOME/.ssh/id_ed25519 -o $HOME/.config/sops/age/keys.txt 25 37 ``` 26 38 27 - Get your age public key: 39 + Get the age public key: 28 40 29 41 ```bash 30 42 nix shell nixpkgs#age --command age-keygen -y $HOME/.config/sops/age/keys.txt 31 43 ``` 32 44 33 - ### 2. Raspberry Pi — get the host age public key 45 + ### 2.2 Mikan — get the host age public key 34 46 35 47 ```bash 36 48 cat /etc/ssh/ssh_host_ed25519_key.pub | nix run nixpkgs#ssh-to-age 37 49 ``` 38 50 39 - ### 3. Desktop — create `.sops.yaml` 51 + ### 2.3 Sauce — create `.sops.yaml` 40 52 41 53 ```yaml 42 54 keys: 43 - - &admin_you age1... # from step 1 44 - - &raspberry age1... # from step 2 55 + - &admin_you age1... # from 2.1 56 + - &raspberry age1... # from 2.2 45 57 creation_rules: 46 58 - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ 47 59 key_groups: ··· 50 62 - *raspberry 51 63 ``` 52 64 53 - ### 4. Desktop — create and edit secrets 65 + ### 2.4 Sauce — create and edit secrets 54 66 55 67 ```bash 56 68 mkdir -p secrets ··· 59 71 60 72 --- 61 73 62 - ## Secrets in `configuration.nix` 74 + ## 3. Consuming secrets in `configuration.nix` 63 75 64 76 Sops secrets are **files on disk at runtime**, not Nix strings. They cannot be 65 77 interpolated with `${}` at build time. Each type of secret needs a different ··· 71 83 | WiFi credentials | `networking.wireless.secretsFile` + `@VARIABLE@` placeholders | 72 84 | SSH public key | Plain text in config — it's public, no need to encrypt | 73 85 74 - ### WiFi 86 + ### 3.1 User password 87 + 88 + The secret must have `neededForUsers = true` so it is available before user 89 + activation runs: 90 + 91 + ```nix 92 + sops.secrets.USER_PASSWORD = { neededForUsers = true; }; 93 + 94 + users.users.alice = { 95 + hashedPasswordFile = config.sops.secrets.USER_PASSWORD.path; 96 + }; 97 + ``` 98 + 99 + ### 3.2 WiFi credentials 75 100 76 101 WiFi credentials need a single file containing both values: 77 102 ··· 90 115 }; 91 116 ``` 92 117 93 - ### User password 118 + ### 3.3 SSH public keys 94 119 95 - The secret must have `neededForUsers = true` so it is available before user 96 - activation runs: 120 + Public keys are not sensitive — inline them in the config instead of routing 121 + through sops (see also [4.2](#42-nix--run-is-forbidden-in-pure-evaluation-mode)). 97 122 98 123 ```nix 99 - sops.secrets.USER_PASSWORD = { neededForUsers = true; }; 100 - 101 - users.users.alice = { 102 - hashedPasswordFile = config.sops.secrets.USER_PASSWORD.path; 103 - }; 124 + openssh.authorizedKeys.keys = [ 125 + "ssh-ed25519 AAAA... you@sauce" 126 + ]; 104 127 ``` 105 128 106 129 --- 107 130 108 - ## Errors encountered and fixes 131 + ## 4. Troubleshooting 109 132 110 - ### sops can't decrypt the file on second open 133 + ### 4.1 sops — missing age key file on decrypt 111 134 112 135 ``` 113 136 Did not find keys in locations 'SOPS_AGE_KEY_FILE', '/Users/.../.ssh/id_rsa' ··· 115 138 116 139 **Cause:** sops looks for `id_rsa` by default and doesn't find `keys.txt`. 117 140 118 - **Fix:** export the variable pointing to the age keys file and add it to your shell config: 141 + **Fix:** export the variable pointing to the age keys file and add it to the 142 + shell config: 119 143 120 144 ```bash 121 145 export SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt" 122 146 ``` 123 147 124 - --- 125 - 126 - ### `/run` is forbidden in pure evaluation mode 148 + ### 4.2 Nix — `/run` is forbidden in pure evaluation mode 127 149 128 150 ``` 129 151 error: access to absolute path '/run' is forbidden in pure evaluation mode ··· 134 156 Nix evaluates this path at build time, but `/run` only exists at runtime. 135 157 136 158 **Fix:** SSH public keys are not sensitive. Remove `SSH_PUBLIC_KEY` from sops 137 - and put the key directly in the config: 138 - 139 - ```nix 140 - openssh.authorizedKeys.keys = [ 141 - "ssh-ed25519 AAAA... you@desktop" 142 - ]; 143 - ``` 144 - 145 - --- 159 + and put the key directly in the config (see [3.3](#33-ssh-public-keys)). 146 160 147 - ### current system differs from required 161 + ### 4.3 deploy-rs — target architecture mismatch 148 162 149 163 ``` 150 164 error: Cannot build '/nix/store/lc2x2l1wvzwlv48sh2vdp4khynvldlmf-builder.pl.drv'. ··· 153 167 Current system: 'aarch64-darwin' with features {apple-virt, benchmark, big-parallel, nixos-test} 154 168 ``` 155 169 156 - **Cause:** `deploy-rs` was trying to build in my `aarch64-darwin` system (Macbook M1) a `aarch64-linux` target. 170 + **Cause:** `deploy-rs` was trying to build a `aarch64-linux` target on the 171 + `aarch64-darwin` laptop (Macbook M1). 157 172 158 - **Fix:** Enable remote builds to run them on the Raspberry instead. 173 + **Fix:** Enable remote builds so the Pi builds its own closure. 159 174 160 175 ```nix 161 176 outputs.deploy.nodes.raspberry = { 162 177 remoteBuild = true; 163 178 } 164 179 ``` 180 + 181 + --- 182 + 183 + ## 5. Clarifications 184 + 185 + ### 5.1 `age.sshKeyPaths` points to a file on the Pi — do I need it locally? 186 + 187 + ```nix 188 + age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; 189 + ``` 190 + 191 + **No.** This path is read at **activation time on the target** (Mikan), not 192 + during local evaluation. `sops-nix` converts the host's SSH ed25519 key into an 193 + age key and uses it to decrypt `/run/secrets/*` when the system boots. 194 + 195 + Two different keys are at play: 196 + 197 + | Where | Key | Purpose | 198 + |---|---|---| 199 + | Sauce | `~/.config/sops/age/keys.txt` → `&sauce` | Encrypt/edit secrets locally with `sops` | 200 + | Mikan | `/etc/ssh/ssh_host_ed25519_key` → `&mikan` | Decrypt secrets at boot via `sops-nix` | 201 + 202 + Both public keys are listed in `.sops.yaml` so the encrypted file carries two 203 + recipients. Missing the file locally is fine; missing it on the Pi would fail 204 + activation with "no age key found". 205 + 206 + ### 5.2 How does `deploy.nodes.mikan.profiles.system.path` work? 207 + 208 + ```nix 209 + path = deploy-rs.lib.aarch64-linux.activate.nixos 210 + self.nixosConfigurations.mikan; 211 + ``` 212 + 213 + - `deploy-rs.lib.aarch64-linux.activate.nixos` is a function from the deploy-rs 214 + flake, specific to the target architecture. It wraps a NixOS configuration 215 + into an activation script that swaps the system profile, runs 216 + `switch-to-configuration`, and rolls back on failure. 217 + - `self.nixosConfigurations.mikan` is the argument — `self` refers to this 218 + flake, so it picks up the `mikan` config defined above. 219 + - In Nix, `f x` (juxtaposition) is function application. The two lines are a 220 + single call spread across lines. 221 + - The architecture `aarch64-linux` must match `nixosSystem.system`, otherwise 222 + deploy-rs will try to activate a closure built for the wrong platform.
+1 -1
flake.nix
··· 32 32 }; 33 33 }; 34 34 }; 35 - } 35 + }