Compare commits
3 Commits
bc3652c782
...
a3f7a19cc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
a3f7a19cc2
|
|||
|
e019f2d4fb
|
|||
|
22282691e7
|
21
AGENTS.md
21
AGENTS.md
@@ -36,10 +36,11 @@ lib/
|
|||||||
overlays.nix # jellyfin-exporter, igpu-exporter, reflac, ensureZfsMounts
|
overlays.nix # jellyfin-exporter, igpu-exporter, reflac, ensureZfsMounts
|
||||||
patches/nixpkgs/ # applied to nixpkgs-stable for muffin builds
|
patches/nixpkgs/ # applied to nixpkgs-stable for muffin builds
|
||||||
secrets/
|
secrets/
|
||||||
desktop/ # git-crypt: mreow + yarn share these (wifi, nix-cache-netrc, secureboot.tar, password-hash, disk-password)
|
secrets.nix # agenix recipients (who can decrypt each .age)
|
||||||
|
desktop/ # agenix *.age (mreow + yarn) + disk-password (install-time only, git-crypt)
|
||||||
home/ # git-crypt: per-user HM secrets (api keys, steam id)
|
home/ # git-crypt: per-user HM secrets (api keys, steam id)
|
||||||
server/ # agenix *.age + git-crypt *.nix/*.tar/livekit_keys
|
server/ # agenix *.age + git-crypt *.nix/*.tar/livekit_keys (muffin)
|
||||||
usb-secrets/ # USB-resident agenix identity key (git-crypt inside the repo)
|
usb-secrets/ # USB-resident agenix identity for muffin (git-crypt inside the repo)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Never read or write files under `secrets/`.** They are encrypted at rest (git-crypt for plaintext, agenix for `.age`). The git-crypt key is delivered to `muffin` at runtime as `/run/agenix/git-crypt-key-nixos.age`.
|
**Never read or write files under `secrets/`.** They are encrypted at rest (git-crypt for plaintext, agenix for `.age`). The git-crypt key is delivered to `muffin` at runtime as `/run/agenix/git-crypt-key-nixos.age`.
|
||||||
@@ -89,7 +90,7 @@ If Nix complains about a missing file, `git add` it first — flakes only see tr
|
|||||||
| `common-` | imported by ALL hosts | `common-doas.nix`, `common-nix.nix`, `common-shell-fish.nix` |
|
| `common-` | imported by ALL hosts | `common-doas.nix`, `common-nix.nix`, `common-shell-fish.nix` |
|
||||||
| `desktop-` | imported by mreow + yarn only | `desktop-common.nix`, `desktop-steam.nix`, `desktop-networkmanager.nix` |
|
| `desktop-` | imported by mreow + yarn only | `desktop-common.nix`, `desktop-steam.nix`, `desktop-networkmanager.nix` |
|
||||||
| `server-` | imported by muffin only | `server-security.nix`, `server-power.nix`, `server-impermanence.nix`, `server-lanzaboote-agenix.nix` |
|
| `server-` | imported by muffin only | `server-security.nix`, `server-power.nix`, `server-impermanence.nix`, `server-lanzaboote-agenix.nix` |
|
||||||
| *(none)* | host-specific filename-scoped; see file contents | `age-secrets.nix`, `zfs.nix`, `no-rgb.nix` (yarn + muffin) |
|
| *(none)* | host-specific filename-scoped; see file contents | `zfs.nix`, `no-rgb.nix` (yarn + muffin) |
|
||||||
|
|
||||||
New modules: pick the narrowest prefix that's true, then add the import explicitly in the host's `default.nix` (there is no auto-discovery).
|
New modules: pick the narrowest prefix that's true, then add the import explicitly in the host's `default.nix` (there is no auto-discovery).
|
||||||
|
|
||||||
@@ -117,14 +118,18 @@ New modules: pick the narrowest prefix that's true, then add the import explicit
|
|||||||
## Secrets
|
## Secrets
|
||||||
|
|
||||||
- **git-crypt** covers `secrets/**` per the root `.gitattributes`. Initialized with a single symmetric key checked into `secrets/server/git-crypt-key-nixos.age` (agenix-encrypted to the USB SSH identity).
|
- **git-crypt** covers `secrets/**` per the root `.gitattributes`. Initialized with a single symmetric key checked into `secrets/server/git-crypt-key-nixos.age` (agenix-encrypted to the USB SSH identity).
|
||||||
- **agenix** decrypts `secrets/server/*.age` at activation into `/run/agenix/` on muffin.
|
- **agenix** decrypts `*.age` into `/run/agenix/` at activation on every host:
|
||||||
- **USB identity**: `/mnt/usb-secrets/usb-secrets-key` on muffin; the age identity path is wired in `modules/usb-secrets.nix`.
|
- **muffin**: identity is `/mnt/usb-secrets/usb-secrets-key` (ssh-ed25519 on a physical USB). Wired in `modules/usb-secrets.nix`.
|
||||||
- **Encrypting a new agenix secret** uses the SSH public key directly with `age -R`:
|
- **mreow + yarn**: identity is `/var/lib/agenix/tpm-identity` (an `age-plugin-tpm` handle sealed by the host's TPM 2.0). Wired in `modules/desktop-age-secrets.nix`; yarn persists `/var/lib/agenix` through impermanence.
|
||||||
|
- **Recipients** are declared in `secrets/secrets.nix`. Desktop secrets are encrypted to the admin SSH key + each host's TPM recipient; server secrets stay encrypted to the muffin USB key.
|
||||||
|
- **Bootstrap a new desktop**: run `doas scripts/bootstrap-desktop-tpm.sh` on the host. It generates a TPM-sealed identity at `/var/lib/agenix/tpm-identity` and prints an `age1tpm1…` recipient. Append it to the `tpm` list in `secrets/secrets.nix`, run `agenix -r` to re-encrypt, commit, `./deploy.sh switch`.
|
||||||
|
- **Encrypting a new server secret** uses the SSH public key directly with `age -R`:
|
||||||
```sh
|
```sh
|
||||||
age -R <(ssh-keygen -y -f secrets/usb-secrets/usb-secrets-key) \
|
age -R <(ssh-keygen -y -f secrets/usb-secrets/usb-secrets-key) \
|
||||||
-o secrets/server/<name>.age \
|
-o secrets/server/<name>.age \
|
||||||
/path/to/plaintext
|
/path/to/plaintext
|
||||||
```
|
```
|
||||||
|
For desktop secrets, prefer `agenix -e secrets/desktop/<name>.age` from a shell with `age-plugin-tpm` on PATH — it reads `secrets/secrets.nix` and encrypts to every recipient listed there.
|
||||||
- **DO NOT use `ssh-to-age`**. It produces `X25519` recipient stanzas, which the SSH private key on muffin cannot decrypt (it only decrypts `ssh-ed25519` stanzas produced by `age -R` against the SSH pubkey). Mismatched stanzas show up as `age: error: no identity matched any of the recipients` at deploy time.
|
- **DO NOT use `ssh-to-age`**. It produces `X25519` recipient stanzas, which the SSH private key on muffin cannot decrypt (it only decrypts `ssh-ed25519` stanzas produced by `age -R` against the SSH pubkey). Mismatched stanzas show up as `age: error: no identity matched any of the recipients` at deploy time.
|
||||||
- Never read or commit plaintext secrets. Never log secret values.
|
- Never read or commit plaintext secrets. Never log secret values.
|
||||||
|
|
||||||
@@ -210,7 +215,7 @@ Prior art: the 3-path `{kernel,initrd,kernel-modules}` diff is lifted from nixpk
|
|||||||
|
|
||||||
- **Privilege escalation**: `doas` everywhere; `sudo` is disabled on every host.
|
- **Privilege escalation**: `doas` everywhere; `sudo` is disabled on every host.
|
||||||
- **Shell**: fish. `bash` login shells re-exec into fish via `programs.bash.interactiveShellInit` (see `modules/common-shell-fish.nix`).
|
- **Shell**: fish. `bash` login shells re-exec into fish via `programs.bash.interactiveShellInit` (see `modules/common-shell-fish.nix`).
|
||||||
- **Secure boot**: lanzaboote. Desktops extract keys from `secrets/desktop/secureboot.tar`; muffin extracts from an agenix-decrypted tar (see `modules/server-lanzaboote-agenix.nix`).
|
- **Secure boot**: lanzaboote. Every host extracts keys from an agenix-decrypted tar at activation — desktops via `modules/desktop-lanzaboote-agenix.nix`, muffin via `modules/server-lanzaboote-agenix.nix`.
|
||||||
- **Impermanence**: muffin is tmpfs-root with `/persistent` surviving reboots (`modules/server-impermanence.nix`); yarn binds `/home/primary` from `/persistent` (`hosts/yarn/impermanence.nix`).
|
- **Impermanence**: muffin is tmpfs-root with `/persistent` surviving reboots (`modules/server-impermanence.nix`); yarn binds `/home/primary` from `/persistent` (`hosts/yarn/impermanence.nix`).
|
||||||
- **Disks**: disko.
|
- **Disks**: disko.
|
||||||
- **Binary cache**: muffin runs harmonia; desktops consume it at `https://nix-cache.sigkill.computer`.
|
- **Binary cache**: muffin runs harmonia; desktops consume it at `https://nix-cache.sigkill.computer`.
|
||||||
|
|||||||
84
flake.lock
generated
84
flake.lock
generated
@@ -92,16 +92,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776182890,
|
"lastModified": 1776192490,
|
||||||
"narHash": "sha256-+/VOe8XGq5klpU+I19D+3TcaR7o+Cwbq67KNF7mcFak=",
|
"narHash": "sha256-5gYQNEs0/vDkHhg63aHS5g0IwG/8HNvU1Vr00cElofk=",
|
||||||
"owner": "Mic92",
|
"owner": "nix-community",
|
||||||
"repo": "bun2nix",
|
"repo": "bun2nix",
|
||||||
"rev": "648d293c51e981aec9cb07ba4268bc19e7a8c575",
|
"rev": "6ef9f144616eedea90b364bb408ef2e1de7b310a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "Mic92",
|
"owner": "nix-community",
|
||||||
"ref": "catalog-support",
|
"ref": "staging-2.1.0",
|
||||||
"repo": "bun2nix",
|
"repo": "bun2nix",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -222,11 +222,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776849698,
|
"lastModified": 1776967792,
|
||||||
"narHash": "sha256-t2I9ZhBuAcaLV1Z65aVd/5BmDFGvyzLY5kpiSedx2uY=",
|
"narHash": "sha256-O3YfkXQz8P2kec6Ani8fmuXvuXRAyl5/qPdt0kDNFWk=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "emacs-overlay",
|
"repo": "emacs-overlay",
|
||||||
"rev": "87dff52c245cba0c5103cf89b964e508ed9bb720",
|
"rev": "0041dd571ebebe8fa779b940fb13b6d447a48b87",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -266,11 +266,11 @@
|
|||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "pkgs/firefox-addons",
|
"dir": "pkgs/firefox-addons",
|
||||||
"lastModified": 1776830588,
|
"lastModified": 1776916994,
|
||||||
"narHash": "sha256-1X4L6+F7DgYTUDah+PDs7IYJiQrb7MwYfateq2fBxGY=",
|
"narHash": "sha256-FgqUwRZ2bwbE5w1bCUv9MB3gvwqZ4oEyCgZ6z/6jdTY=",
|
||||||
"owner": "rycee",
|
"owner": "rycee",
|
||||||
"repo": "nur-expressions",
|
"repo": "nur-expressions",
|
||||||
"rev": "f3db83bc13aee22474fab41fa838e50a691dfbc5",
|
"rev": "a2236006e5c70e2fc06e9acb016b1ac9c0fd5935",
|
||||||
"type": "gitlab"
|
"type": "gitlab"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -484,11 +484,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776891022,
|
"lastModified": 1776964438,
|
||||||
"narHash": "sha256-vEe2f4NEhMvaNDpM1pla4hteaIIGQyAMKUfIBPLasr0=",
|
"narHash": "sha256-AF0cby9Xuijr5qaFpYKbm1mExV956Hk233bel6QxpFw=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "508daf831ab8d1b143d908239c39a7d8d39561b2",
|
"rev": "e09259dd2e147d35ef889784b51e89b0a10ffe15",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -564,11 +564,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776874528,
|
"lastModified": 1776962372,
|
||||||
"narHash": "sha256-X4Y2vMbVBuyUQzbZnl72BzpZMYUsWdE78JuDg2ySDxE=",
|
"narHash": "sha256-Y2imW4kyIhupx8myNSeNCzDbEx2X+h+AmhNjWXA/7Yw=",
|
||||||
"owner": "Jovian-Experiments",
|
"owner": "Jovian-Experiments",
|
||||||
"repo": "Jovian-NixOS",
|
"repo": "Jovian-NixOS",
|
||||||
"rev": "4c8ccc482a3665fb4a3b2cadbbe7772fb7cc2629",
|
"rev": "ee3a1184a978e311194a2d3d352c5e6aba67a4b5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -631,11 +631,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776862155,
|
"lastModified": 1776955347,
|
||||||
"narHash": "sha256-EDvbwsGNE/N5ul+9ul1dJP3Ouf72+Ub2C0UMbDWcxyQ=",
|
"narHash": "sha256-VCPA/1RWMZggfXjpMcEMC2QfDrYp6eHgqvsPfDSKGSI=",
|
||||||
"owner": "TheTom",
|
"owner": "TheTom",
|
||||||
"repo": "llama-cpp-turboquant",
|
"repo": "llama-cpp-turboquant",
|
||||||
"rev": "9e3fb40e8bc0f873ad4d3d8329b17dacff28e4ca",
|
"rev": "67559e580b10e4e47e9a6fd6218873997976886d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -657,11 +657,11 @@
|
|||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776883427,
|
"lastModified": 1776966087,
|
||||||
"narHash": "sha256-prHCm++hniRcoqzvWTEFyAiLKT6m+EUVCRaDLrsuEgM=",
|
"narHash": "sha256-P+39paxTvpYiMv5wqGKte7YbmxJKoihcXssV1IhkSAo=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "llm-agents.nix",
|
"repo": "llm-agents.nix",
|
||||||
"rev": "6fd26c9cb50d9549f3791b3d35e4f72f97677103",
|
"rev": "547d51c282c15a7c9b86c8388a1adb1695b1df59",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -787,11 +787,11 @@
|
|||||||
"systems": "systems_6"
|
"systems": "systems_6"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776851701,
|
"lastModified": 1776938345,
|
||||||
"narHash": "sha256-tdtOcU2Hz/eLqAhkzUcEocgX0WpjKSbl2SkVjOZGZw0=",
|
"narHash": "sha256-3/BFiytDNoIXMUQHcJLoxa7JK0Q1/49M0ffOR9pbzvw=",
|
||||||
"owner": "marienz",
|
"owner": "marienz",
|
||||||
"repo": "nix-doom-emacs-unstraightened",
|
"repo": "nix-doom-emacs-unstraightened",
|
||||||
"rev": "7ac65a49eec5e3f87d27396e645eddbf9dc626de",
|
"rev": "eb25c754986165e509ad2ab8c6b6729f4a861f0c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -846,11 +846,11 @@
|
|||||||
"systems": "systems_7"
|
"systems": "systems_7"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776828595,
|
"lastModified": 1776915193,
|
||||||
"narHash": "sha256-LkFpFnPTK6H0gwyfYezN3kEKHVxjSdPp/tBnrQRFP3E=",
|
"narHash": "sha256-bYyOT3OIWIKvDV+pOVd0hdCEG8orf85QX4b21LWUSEs=",
|
||||||
"owner": "Infinidoge",
|
"owner": "Infinidoge",
|
||||||
"repo": "nix-minecraft",
|
"repo": "nix-minecraft",
|
||||||
"rev": "28f0f2369655a5910e810c35c698dfaa9ccec692",
|
"rev": "40c972ce0f45b8c05bf245d5065647b17552312c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1133,11 +1133,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776827647,
|
"lastModified": 1776914043,
|
||||||
"narHash": "sha256-sYixYhp5V8jCajO8TRorE4fzs7IkL4MZdfLTKgkPQBk=",
|
"narHash": "sha256-qug5r56yW1qOsjSI99l3Jm15JNT9CvS2otkXNRNtrPI=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "40e6ccc06e1245a4837cbbd6bdda64e21cc67379",
|
"rev": "2d35c4358d7de3a0e606a6e8b27925d981c01cc3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1190,11 +1190,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776653059,
|
"lastModified": 1776912132,
|
||||||
"narHash": "sha256-K3tWnUj6FXaK95sBUajedutJrFVrOzYhvrQwQjJ0FbU=",
|
"narHash": "sha256-UDR6PtHacMhAQJ8SPNbPROaxbtl2Pgjww0TzipTsTZE=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "srvos",
|
"repo": "srvos",
|
||||||
"rev": "4968d2a44c84edfc9a38a2494cc7f85ad2c7122b",
|
"rev": "e9ff039a72ff2c06271d5002eb431c443abf69fa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1356,11 +1356,11 @@
|
|||||||
"trackerlist": {
|
"trackerlist": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776809383,
|
"lastModified": 1776895782,
|
||||||
"narHash": "sha256-r4V5l+Yk3jxVfZNQk2Ddu8Vlyshd9FWcnGGFyaL4UCw=",
|
"narHash": "sha256-iHdp9lRoV3ejsTC96z7Pns/JvQKWyp+V0fdVcVOv8Xw=",
|
||||||
"owner": "ngosang",
|
"owner": "ngosang",
|
||||||
"repo": "trackerslist",
|
"repo": "trackerslist",
|
||||||
"rev": "37d5c0552c25abf50f05cc6b377345e65a588dc2",
|
"rev": "e1a89caab7d4c5af3870a49ddc494cda745b236e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1524,11 +1524,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776844129,
|
"lastModified": 1776922304,
|
||||||
"narHash": "sha256-DaYSEBVzTvUhTuoVe70NHphoq5JKUHqUhlNlN5XnTuU=",
|
"narHash": "sha256-T1r7GWzeqX0C6YauIMN6D0sdr5voDAPMg8jvn59Wm7g=",
|
||||||
"owner": "0xc000022070",
|
"owner": "0xc000022070",
|
||||||
"repo": "zen-browser-flake",
|
"repo": "zen-browser-flake",
|
||||||
"rev": "90706e6ab801e4fb7bc53343db67583631936192",
|
"rev": "91cc9ed57a893b2e944de60812511f05fd408ce6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
../../modules/zfs.nix
|
../../modules/zfs.nix
|
||||||
../../modules/server-impermanence.nix
|
../../modules/server-impermanence.nix
|
||||||
../../modules/usb-secrets.nix
|
../../modules/usb-secrets.nix
|
||||||
../../modules/age-secrets.nix
|
../../modules/server-age-secrets.nix
|
||||||
../../modules/server-lanzaboote-agenix.nix
|
../../modules/server-lanzaboote-agenix.nix
|
||||||
../../modules/no-rgb.nix
|
../../modules/no-rgb.nix
|
||||||
../../modules/server-security.nix
|
../../modules/server-security.nix
|
||||||
|
|||||||
@@ -196,6 +196,10 @@ rec {
|
|||||||
port = 9563;
|
port = 9563;
|
||||||
proto = "tcp";
|
proto = "tcp";
|
||||||
};
|
};
|
||||||
|
minecraft_exporter = {
|
||||||
|
port = 9567;
|
||||||
|
proto = "tcp";
|
||||||
|
};
|
||||||
prometheus_zfs = {
|
prometheus_zfs = {
|
||||||
port = 9134;
|
port = 9134;
|
||||||
proto = "tcp";
|
proto = "tcp";
|
||||||
|
|||||||
@@ -12,6 +12,14 @@
|
|||||||
"/var/lib/systemd/coredump"
|
"/var/lib/systemd/coredump"
|
||||||
"/var/lib/nixos"
|
"/var/lib/nixos"
|
||||||
"/var/lib/systemd/timers"
|
"/var/lib/systemd/timers"
|
||||||
|
# agenix identity sealed by the TPM. Must survive the tmpfs root
|
||||||
|
# wipe so decryption at activation finds the right handle.
|
||||||
|
{
|
||||||
|
directory = "/var/lib/agenix";
|
||||||
|
mode = "0700";
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
files = [
|
files = [
|
||||||
|
|||||||
@@ -75,4 +75,19 @@ final: prev: {
|
|||||||
'';
|
'';
|
||||||
meta.mainProgram = "igpu-exporter";
|
meta.mainProgram = "igpu-exporter";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mc-monitor = prev.buildGoModule rec {
|
||||||
|
pname = "mc-monitor";
|
||||||
|
version = "0.16.1";
|
||||||
|
src = prev.fetchFromGitHub {
|
||||||
|
owner = "itzg";
|
||||||
|
repo = "mc-monitor";
|
||||||
|
rev = version;
|
||||||
|
hash = "sha256-/94+Z9FTFOzQHynHiJuaGFiidkOxmM0g/FIpHn+xvJM=";
|
||||||
|
};
|
||||||
|
vendorHash = "sha256-qq7rIpvGRi3AMnBbi8uAhiPcfSF4McIuqozdtxB5CeQ=";
|
||||||
|
# upstream tests probe live Minecraft servers
|
||||||
|
doCheck = false;
|
||||||
|
meta.mainProgram = "mc-monitor";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
70
modules/desktop-age-secrets.nix
Normal file
70
modules/desktop-age-secrets.nix
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# rage cannot invoke age-plugin-tpm unless the plugin binary is on PATH at
|
||||||
|
# activation time. Wrap rage so the activation scripts (and anything else
|
||||||
|
# that picks up `age.ageBin`) get age-plugin-tpm for free.
|
||||||
|
rageWithTpm = pkgs.writeShellScriptBin "rage" ''
|
||||||
|
export PATH="${pkgs.age-plugin-tpm}/bin:$PATH"
|
||||||
|
exec ${pkgs.rage}/bin/rage "$@"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
inputs.agenix.nixosModules.default
|
||||||
|
];
|
||||||
|
|
||||||
|
# Expose the plugin + agenix CLI for interactive edits (`agenix -e …`).
|
||||||
|
environment.systemPackages = [
|
||||||
|
inputs.agenix.packages.${pkgs.system}.default
|
||||||
|
pkgs.age-plugin-tpm
|
||||||
|
];
|
||||||
|
|
||||||
|
age.ageBin = "${rageWithTpm}/bin/rage";
|
||||||
|
|
||||||
|
# Primary identity: TPM-sealed key, generated by scripts/bootstrap-desktop-tpm.sh.
|
||||||
|
# Fallback identity: admin SSH key. age tries paths in order, so if the TPM
|
||||||
|
# is wiped or the board is replaced the SSH key keeps secrets accessible until
|
||||||
|
# the TPM is re-bootstrapped. Both are encrypted recipients on every .age file.
|
||||||
|
age.identityPaths = [
|
||||||
|
"/var/lib/agenix/tpm-identity"
|
||||||
|
"/home/primary/.ssh/id_ed25519"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Ensure the identity directory exists before agenix activation so a fresh
|
||||||
|
# bootstrap doesn't race the directory creation.
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /var/lib/agenix 0700 root root -"
|
||||||
|
];
|
||||||
|
|
||||||
|
age.secrets = {
|
||||||
|
# Secureboot PKI bundle (db/KEK/PK keys + certs) consumed by lanzaboote
|
||||||
|
# via desktop-lanzaboote-agenix.nix at activation time.
|
||||||
|
secureboot-tar = {
|
||||||
|
file = ../secrets/desktop/secureboot.tar.age;
|
||||||
|
mode = "0400";
|
||||||
|
owner = "root";
|
||||||
|
group = "root";
|
||||||
|
};
|
||||||
|
|
||||||
|
# netrc for the private nix binary cache.
|
||||||
|
nix-cache-netrc = {
|
||||||
|
file = ../secrets/desktop/nix-cache-netrc.age;
|
||||||
|
mode = "0400";
|
||||||
|
owner = "root";
|
||||||
|
group = "root";
|
||||||
|
};
|
||||||
|
|
||||||
|
# yescrypt hash for the primary user.
|
||||||
|
password-hash = {
|
||||||
|
file = ../secrets/desktop/password-hash.age;
|
||||||
|
mode = "0400";
|
||||||
|
owner = "root";
|
||||||
|
group = "root";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
./desktop-vm.nix
|
./desktop-vm.nix
|
||||||
./desktop-steam.nix
|
./desktop-steam.nix
|
||||||
./desktop-networkmanager.nix
|
./desktop-networkmanager.nix
|
||||||
|
./desktop-age-secrets.nix
|
||||||
|
./desktop-lanzaboote-agenix.nix
|
||||||
|
|
||||||
inputs.disko.nixosModules.disko
|
inputs.disko.nixosModules.disko
|
||||||
inputs.lanzaboote.nixosModules.lanzaboote
|
|
||||||
|
|
||||||
inputs.nixos-hardware.nixosModules.common-cpu-amd-pstate
|
inputs.nixos-hardware.nixosModules.common-cpu-amd-pstate
|
||||||
inputs.nixos-hardware.nixosModules.common-cpu-amd-zenpower
|
inputs.nixos-hardware.nixosModules.common-cpu-amd-zenpower
|
||||||
@@ -50,16 +51,6 @@
|
|||||||
mkdir -p /nix/var/nix/profiles/per-user/root/channels
|
mkdir -p /nix/var/nix/profiles/per-user/root/channels
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# extract all my secureboot keys
|
|
||||||
# TODO! proper secrets management
|
|
||||||
"secureboot-keys".text = ''
|
|
||||||
#!/usr/bin/env sh
|
|
||||||
rm -fr ${config.boot.lanzaboote.pkiBundle} || true
|
|
||||||
mkdir -p ${config.boot.lanzaboote.pkiBundle}
|
|
||||||
${lib.getExe pkgs.gnutar} xf ${../secrets/desktop/secureboot.tar} -C ${config.boot.lanzaboote.pkiBundle}
|
|
||||||
chown -R root:wheel ${config.boot.lanzaboote.pkiBundle}
|
|
||||||
chmod -R 500 ${config.boot.lanzaboote.pkiBundle}
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
swapDevices = [ ];
|
swapDevices = [ ];
|
||||||
@@ -71,7 +62,7 @@
|
|||||||
trusted-public-keys = [
|
trusted-public-keys = [
|
||||||
site_config.binary_cache.public_key
|
site_config.binary_cache.public_key
|
||||||
];
|
];
|
||||||
netrc-file = "${../secrets/desktop/nix-cache-netrc}";
|
netrc-file = config.age.secrets.nix-cache-netrc.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
# cachyos kernel overlay
|
# cachyos kernel overlay
|
||||||
@@ -896,8 +887,7 @@
|
|||||||
"camera"
|
"camera"
|
||||||
"adbusers"
|
"adbusers"
|
||||||
];
|
];
|
||||||
# TODO! this is really bad :( I should really figure out how to do proper secrets management
|
hashedPasswordFile = config.age.secrets.password-hash.path;
|
||||||
hashedPasswordFile = "${../secrets/desktop/password-hash}";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services.gvfs.enable = true;
|
services.gvfs.enable = true;
|
||||||
|
|||||||
49
modules/desktop-lanzaboote-agenix.nix
Normal file
49
modules/desktop-lanzaboote-agenix.nix
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
inputs.lanzaboote.nixosModules.lanzaboote
|
||||||
|
];
|
||||||
|
|
||||||
|
boot = {
|
||||||
|
loader.systemd-boot.enable = lib.mkForce false;
|
||||||
|
|
||||||
|
lanzaboote = {
|
||||||
|
enable = true;
|
||||||
|
# sbctl expects the bundle at /var/lib/sbctl; muffin uses /etc/secureboot
|
||||||
|
# because it is wiped on every activation there (impermanence) — desktops
|
||||||
|
# extract to the historical sbctl path so existing tooling keeps working.
|
||||||
|
pkiBundle = "/var/lib/sbctl";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
system.activationScripts = {
|
||||||
|
# Extract the secureboot PKI bundle from the agenix-decrypted tar. Mirrors
|
||||||
|
# modules/server-lanzaboote-agenix.nix; skip when keys are already present
|
||||||
|
# (e.g., disko-install staged them via --extra-files).
|
||||||
|
"secureboot-keys" = {
|
||||||
|
deps = [ "agenix" ];
|
||||||
|
text = ''
|
||||||
|
#!/bin/sh
|
||||||
|
(
|
||||||
|
umask 077
|
||||||
|
if [[ -d ${config.boot.lanzaboote.pkiBundle} && -f ${config.boot.lanzaboote.pkiBundle}/db.key ]]; then
|
||||||
|
echo "secureboot keys already present, skipping extraction"
|
||||||
|
else
|
||||||
|
echo "extracting secureboot keys from agenix"
|
||||||
|
rm -fr ${config.boot.lanzaboote.pkiBundle} || true
|
||||||
|
install -d -o root -g wheel -m 0500 ${config.boot.lanzaboote.pkiBundle}
|
||||||
|
${pkgs.gnutar}/bin/tar xf ${config.age.secrets.secureboot-tar.path} -C ${config.boot.lanzaboote.pkiBundle}
|
||||||
|
fi
|
||||||
|
chown -R root:wheel ${config.boot.lanzaboote.pkiBundle}
|
||||||
|
chmod -R 500 ${config.boot.lanzaboote.pkiBundle}
|
||||||
|
)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{ hostname, site_config, ... }:
|
{ hostname, ... }:
|
||||||
{
|
{
|
||||||
# speed up boot times (by about three seconds)
|
# speed up boot times (by about three seconds)
|
||||||
systemd.services.NetworkManager-wait-online.enable = false;
|
systemd.services.NetworkManager-wait-online.enable = false;
|
||||||
@@ -9,7 +9,10 @@
|
|||||||
networkmanager = {
|
networkmanager = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
appendNameservers = site_config.dns_servers;
|
appendNameservers = [
|
||||||
|
"1.1.1.1"
|
||||||
|
"9.9.9.9"
|
||||||
|
];
|
||||||
|
|
||||||
wifi = {
|
wifi = {
|
||||||
scanRandMacAddress = true;
|
scanRandMacAddress = true;
|
||||||
|
|||||||
68
scripts/bootstrap-desktop-tpm.sh
Executable file
68
scripts/bootstrap-desktop-tpm.sh
Executable file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Bootstrap the age-plugin-tpm identity for a desktop host (mreow / yarn).
|
||||||
|
#
|
||||||
|
# Produces a TPM-sealed age identity at /var/lib/agenix/tpm-identity and
|
||||||
|
# prints the legacy `age1tpm1…` recipient. The identity file is a TPM
|
||||||
|
# handle, not key material — the actual key never leaves the TPM.
|
||||||
|
#
|
||||||
|
# --tpm-recipient is required: nixpkgs only ships `age-plugin-tpm`, not the
|
||||||
|
# `age-plugin-tag` binary that rage looks up when it sees the new p256tag
|
||||||
|
# `age1tag1…` format. Until a packaged age-plugin-tag lands, every recipient
|
||||||
|
# stays in the legacy form so encryption works with off-the-shelf nixpkgs.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# doas scripts/bootstrap-desktop-tpm.sh
|
||||||
|
#
|
||||||
|
# After running:
|
||||||
|
# 1. Append the printed recipient to `tpm` in secrets/secrets.nix:
|
||||||
|
# "age1tpm1… <hostname>"
|
||||||
|
# 2. `agenix -r` (from a shell with age-plugin-tpm on PATH) to re-encrypt
|
||||||
|
# every desktop secret with the new recipient list.
|
||||||
|
# 3. Commit + `./deploy.sh switch`.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "this script must run as root (access to /dev/tpmrm0 + /var/lib/agenix)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
host=$(hostname -s)
|
||||||
|
id_file=/var/lib/agenix/tpm-identity
|
||||||
|
|
||||||
|
install -d -m 0700 -o root -g root /var/lib/agenix
|
||||||
|
|
||||||
|
if [[ -f "$id_file" ]]; then
|
||||||
|
echo "existing identity found at $id_file — preserving"
|
||||||
|
else
|
||||||
|
echo "generating TPM-sealed age identity..."
|
||||||
|
nix run nixpkgs#age-plugin-tpm -- --generate --tpm-recipient --output "$id_file"
|
||||||
|
chmod 0400 "$id_file"
|
||||||
|
chown root:root "$id_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always derive the legacy age1tpm1… recipient, even if the identity file
|
||||||
|
# was generated with the newer p256tag comment (Recipient line starts with
|
||||||
|
# age1tag1…). `--convert --tpm-recipient` uses the same TPM object and just
|
||||||
|
# serializes the public key point in the old format.
|
||||||
|
recipient=$(nix run nixpkgs#age-plugin-tpm -- --convert --tpm-recipient < "$id_file" 2>/dev/null | grep -o 'age1tpm1[0-9a-z]*' | head -n1)
|
||||||
|
if [[ -z "$recipient" ]]; then
|
||||||
|
# fallback to parsing the header comment (only works when the identity was
|
||||||
|
# already generated with --tpm-recipient).
|
||||||
|
recipient=$(grep '^# Recipient:' "$id_file" | awk '{print $3}')
|
||||||
|
fi
|
||||||
|
if [[ -z "$recipient" ]]; then
|
||||||
|
echo "failed to derive recipient for $id_file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
recipient for $host:
|
||||||
|
"$recipient $host"
|
||||||
|
|
||||||
|
next steps (run on a workstation with git-crypt unlocked):
|
||||||
|
1. edit secrets/secrets.nix and append the line above inside the \`tpm\` list.
|
||||||
|
2. nix run nixpkgs#agenix -- -r # re-encrypts every .age file.
|
||||||
|
3. git commit + ./deploy.sh switch
|
||||||
|
EOF
|
||||||
Binary file not shown.
BIN
secrets/desktop/nix-cache-netrc.age
Normal file
BIN
secrets/desktop/nix-cache-netrc.age
Normal file
Binary file not shown.
Binary file not shown.
BIN
secrets/desktop/password-hash.age
Normal file
BIN
secrets/desktop/password-hash.age
Normal file
Binary file not shown.
Binary file not shown.
BIN
secrets/desktop/secureboot.tar.age
Normal file
BIN
secrets/desktop/secureboot.tar.age
Normal file
Binary file not shown.
Binary file not shown.
BIN
secrets/secrets.nix
Normal file
BIN
secrets/secrets.nix
Normal file
Binary file not shown.
@@ -687,6 +687,188 @@ let
|
|||||||
overrides = [ ];
|
overrides = [ ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# -- Row 6: Minecraft --
|
||||||
|
{
|
||||||
|
id = 14;
|
||||||
|
type = "stat";
|
||||||
|
title = "Minecraft Players";
|
||||||
|
gridPos = {
|
||||||
|
h = 8;
|
||||||
|
w = 6;
|
||||||
|
x = 0;
|
||||||
|
y = 40;
|
||||||
|
};
|
||||||
|
datasource = promDs;
|
||||||
|
targets = [
|
||||||
|
{
|
||||||
|
datasource = promDs;
|
||||||
|
expr = "sum(minecraft_status_players_online_count) or vector(0)";
|
||||||
|
refId = "A";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
fieldConfig = {
|
||||||
|
defaults = {
|
||||||
|
thresholds = {
|
||||||
|
mode = "absolute";
|
||||||
|
steps = [
|
||||||
|
{
|
||||||
|
color = "green";
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
color = "yellow";
|
||||||
|
value = 3;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
color = "red";
|
||||||
|
value = 6;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
overrides = [ ];
|
||||||
|
};
|
||||||
|
options = {
|
||||||
|
reduceOptions = {
|
||||||
|
calcs = [ "lastNotNull" ];
|
||||||
|
fields = "";
|
||||||
|
values = false;
|
||||||
|
};
|
||||||
|
colorMode = "value";
|
||||||
|
graphMode = "area";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
id = 15;
|
||||||
|
type = "stat";
|
||||||
|
title = "Minecraft Server";
|
||||||
|
gridPos = {
|
||||||
|
h = 8;
|
||||||
|
w = 6;
|
||||||
|
x = 6;
|
||||||
|
y = 40;
|
||||||
|
};
|
||||||
|
datasource = promDs;
|
||||||
|
targets = [
|
||||||
|
{
|
||||||
|
datasource = promDs;
|
||||||
|
expr = "max(minecraft_status_healthy) or vector(0)";
|
||||||
|
refId = "A";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
fieldConfig = {
|
||||||
|
defaults = {
|
||||||
|
mappings = [
|
||||||
|
{
|
||||||
|
type = "value";
|
||||||
|
options = {
|
||||||
|
"0" = {
|
||||||
|
text = "Offline";
|
||||||
|
color = "red";
|
||||||
|
index = 0;
|
||||||
|
};
|
||||||
|
"1" = {
|
||||||
|
text = "Online";
|
||||||
|
color = "green";
|
||||||
|
index = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
thresholds = {
|
||||||
|
mode = "absolute";
|
||||||
|
steps = [
|
||||||
|
{
|
||||||
|
color = "red";
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
color = "green";
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
overrides = [ ];
|
||||||
|
};
|
||||||
|
options = {
|
||||||
|
reduceOptions = {
|
||||||
|
calcs = [ "lastNotNull" ];
|
||||||
|
fields = "";
|
||||||
|
values = false;
|
||||||
|
};
|
||||||
|
colorMode = "value";
|
||||||
|
graphMode = "none";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
id = 16;
|
||||||
|
type = "timeseries";
|
||||||
|
title = "Minecraft Player Activity";
|
||||||
|
gridPos = {
|
||||||
|
h = 8;
|
||||||
|
w = 12;
|
||||||
|
x = 12;
|
||||||
|
y = 40;
|
||||||
|
};
|
||||||
|
datasource = promDs;
|
||||||
|
targets = [
|
||||||
|
{
|
||||||
|
datasource = promDs;
|
||||||
|
expr = "sum(minecraft_status_players_online_count) or vector(0)";
|
||||||
|
legendFormat = "Online players";
|
||||||
|
refId = "A";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
datasource = promDs;
|
||||||
|
expr = "max(minecraft_status_players_max_count) or vector(0)";
|
||||||
|
legendFormat = "Max players";
|
||||||
|
refId = "B";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
fieldConfig = {
|
||||||
|
defaults = {
|
||||||
|
unit = "short";
|
||||||
|
min = 0;
|
||||||
|
decimals = 0;
|
||||||
|
color.mode = "palette-classic";
|
||||||
|
custom = {
|
||||||
|
lineWidth = 2;
|
||||||
|
fillOpacity = 15;
|
||||||
|
spanNulls = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
overrides = [
|
||||||
|
{
|
||||||
|
matcher = {
|
||||||
|
id = "byFrameRefID";
|
||||||
|
options = "B";
|
||||||
|
};
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
id = "custom.lineStyle";
|
||||||
|
value = {
|
||||||
|
fill = "dash";
|
||||||
|
dash = [
|
||||||
|
8
|
||||||
|
4
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
id = "custom.fillOpacity";
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
id = "custom.lineWidth";
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ let
|
|||||||
jellyfinExporterPort = service_configs.ports.private.jellyfin_exporter.port;
|
jellyfinExporterPort = service_configs.ports.private.jellyfin_exporter.port;
|
||||||
qbitExporterPort = service_configs.ports.private.qbittorrent_exporter.port;
|
qbitExporterPort = service_configs.ports.private.qbittorrent_exporter.port;
|
||||||
igpuExporterPort = service_configs.ports.private.igpu_exporter.port;
|
igpuExporterPort = service_configs.ports.private.igpu_exporter.port;
|
||||||
|
minecraftExporterPort = service_configs.ports.private.minecraft_exporter.port;
|
||||||
|
minecraftServerName = service_configs.minecraft.server_name;
|
||||||
|
minecraftServerPort = service_configs.ports.public.minecraft.port;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# -- Jellyfin Prometheus Exporter --
|
# -- Jellyfin Prometheus Exporter --
|
||||||
@@ -109,4 +112,45 @@ in
|
|||||||
REFRESH_PERIOD_MS = "30000";
|
REFRESH_PERIOD_MS = "30000";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# -- Minecraft Prometheus Exporter --
|
||||||
|
# itzg/mc-monitor queries the local server via SLP on each scrape and exposes
|
||||||
|
# minecraft_status_{healthy,response_time_seconds,players_online_count,players_max_count}.
|
||||||
|
# mc-monitor binds to 0.0.0.0 (no listen-address flag); the firewall keeps
|
||||||
|
# 9567 internal and IPAddressAllow pins the socket to loopback as defense-in-depth.
|
||||||
|
systemd.services.minecraft-exporter =
|
||||||
|
lib.mkIf (config.services.grafana.enable && config.services.minecraft-servers.enable)
|
||||||
|
{
|
||||||
|
description = "Prometheus exporter for Minecraft (mc-monitor SLP)";
|
||||||
|
after = [
|
||||||
|
"network.target"
|
||||||
|
"minecraft-server-${minecraftServerName}.service"
|
||||||
|
];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${lib.getExe pkgs.mc-monitor} export-for-prometheus";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
DynamicUser = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
RestrictAddressFamilies = [
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
];
|
||||||
|
IPAddressAllow = [
|
||||||
|
"127.0.0.0/8"
|
||||||
|
"::1/128"
|
||||||
|
];
|
||||||
|
IPAddressDeny = "any";
|
||||||
|
};
|
||||||
|
environment = {
|
||||||
|
EXPORT_SERVERS = "127.0.0.1:${toString minecraftServerPort}";
|
||||||
|
EXPORT_PORT = toString minecraftExporterPort;
|
||||||
|
TIMEOUT = "5s";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ in
|
|||||||
{ targets = [ "127.0.0.1:${toString service_configs.ports.private.igpu_exporter.port}" ]; }
|
{ targets = [ "127.0.0.1:${toString service_configs.ports.private.igpu_exporter.port}" ]; }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
job_name = "minecraft";
|
||||||
|
static_configs = [
|
||||||
|
{ targets = [ "127.0.0.1:${toString service_configs.ports.private.minecraft_exporter.port}" ]; }
|
||||||
|
];
|
||||||
|
}
|
||||||
{
|
{
|
||||||
job_name = "zfs";
|
job_name = "zfs";
|
||||||
static_configs = [
|
static_configs = [
|
||||||
|
|||||||
Reference in New Issue
Block a user