diff --git a/AGENTS.md b/AGENTS.md index 3cec9b0..367ea5c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,33 +2,37 @@ ## Project Overview -NixOS infrastructure managed with Nix Flakes. Two hosts, reusable modules, SOPS secrets, Comin auto-deployment. - -- **Hosts**: `cryodev-main` (x86_64 server), `cryodev-pi` (aarch64 Raspberry Pi 4) +This repository contains a NixOS configuration managed with Nix Flakes. It defines: +- **Hosts**: `cryodev-main` (x86_64 server), `cryodev-pi` (aarch64 Raspberry Pi) - **Modules**: Reusable NixOS modules in `modules/nixos/` -- **Apps**: `create`, `deploy`, `install`, `rebuild` in `apps/` +- **Packages**: Custom packages in `pkgs/` - **Templates**: `raspberry-pi`, `generic-server` for bootstrapping new hosts ## Build & Development Commands +### Prerequisites +- **Nix** with Flakes enabled +- **Git** + +### Core Commands + ```bash -# Format code (required before committing, runs nixfmt via pre-commit) -nix fmt - -# Run all checks (formatting, package builds, overlay builds) -nix flake check - -# Quick evaluation test (faster than full build, use to validate changes) -nix eval .#nixosConfigurations.cryodev-main.config.system.build.toplevel.name -nix eval .#nixosConfigurations.cryodev-pi.config.system.build.toplevel.name - -# Full build +# Build host configuration nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel nix build .#nixosConfigurations.cryodev-pi.config.system.build.toplevel # Build Raspberry Pi SD image (requires binfmt on x86_64) nix build .#nixosConfigurations.cryodev-pi.config.system.build.sdImage +# Format code (required before committing) +nix fmt + +# Run all checks (lint, formatting) +nix flake check + +# Quick evaluation test (faster than full build) +nix eval .#nixosConfigurations.cryodev-main.config.system.build.toplevel.name + # Update flake inputs nix flake update @@ -38,143 +42,176 @@ nix develop ### Deployment -Both hosts use **Comin** for automatic pull-based deployment (polls the git repo). -Manual deployment is only needed for initial setup or emergencies: - ```bash -# Deploy via deploy app (uses deploy.json, SSH port 2299, asks sudo password) +# Deploy all hosts via deploy app (uses deploy.json) +nix run .#deploy + +# Deploy a specific host nix run .#deploy -- -n cryodev-main -# Manual deployment via nixos-rebuild +# Manual deployment via SSH NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ --target-host @ --sudo --ask-sudo-password ``` +> **Note:** Both hosts use Comin for automatic pull-based deployment. +> Manual deployment is only needed for the initial setup or emergencies. + ### Apps ```bash -nix run .#create -- -t generic-server -n # Scaffold new host -nix run .#install -- -n -r # Install from NixOS ISO -nix run .#deploy -- -n # Deploy to host -nix run .#rebuild -- nixos # Rebuild locally +# Create a new host from template +nix run .#create -- -t generic-server -n + +# Install NixOS on a new machine (run from NixOS live ISO) +nix run .#install -- -n -r + +# Deploy to all configured hosts +nix run .#deploy + +# Rebuild NixOS/Home Manager configuration +nix run .#rebuild -- nixos ``` ## Code Style & Conventions ### Formatting -- **Tool**: `nixfmt` via `git-hooks.nix` (pre-commit) +- **Tool**: `nixfmt` via pre-commit hooks - **Run**: `nix fmt` before every commit -- **Indentation**: 2 spaces (enforced by formatter) +- **Indentation**: 2 spaces +- **Line length**: 80-100 characters (follow formatter) -### Naming Conventions - -| Type | Convention | Example | -|------|------------|---------| -| Files | kebab-case | `hardware-configuration.nix` | -| NixOS options | camelCase | `services.myService.enable` | -| Let bindings | camelCase | `let myValue = ...;` | -| Hosts | kebab-case | `cryodev-main`, `cryodev-pi` | -| Secret paths | kebab-case with `/` | `forgejo-runner/token` | - -### Module Pattern +### Module Structure ```nix -{ config, lib, ... }: +# Standard module pattern +{ config, lib, pkgs, inputs, outputs, constants, ... }: let cfg = config.services.myService; - inherit (lib) mkDefault mkEnableOption mkIf mkOption types; in { options.services.myService = { - enable = mkEnableOption "My service"; - port = mkOption { - type = types.port; + enable = lib.mkEnableOption "My service"; + port = lib.mkOption { + type = lib.types.port; default = 8080; description = "Port to listen on"; }; }; - config = mkIf cfg.enable { - assertions = [ - { assertion = cfg.port > 1024; message = "Port must be > 1024"; } - ]; - # Implementation + config = lib.mkIf cfg.enable { + # Implementation here }; } ``` -### Key Rules +### Naming Conventions +| Type | Convention | Example | +|------|------------|---------| +| Files | kebab-case | `hardware-configuration.nix` | +| Options | camelCase | `services.myService.enable` | +| Variables | camelCase | `let myValue = ...;` | +| Hosts | kebab-case | `cryodev-main`, `cryodev-pi` | -- **Use `lib.mkDefault`** for all module defaults (allows host-level overrides) -- **Use `constants.nix`** for domains, IPs, ports -- never hardcode these -- **Use `lib.utils`** helpers: `mkReverseProxyOption`, `mkVirtualHost`, `mkUrl` -- **Secrets via SOPS** only, never plaintext. Reference: `config.sops.secrets."path".path` -- **Imports**: relative paths for local files, `outputs.nixosModules.*` for shared modules, `inputs.*` for external -- **Assertions** for invalid configurations, `warnings` for non-critical issues -- **`lib.inherit`** pattern: extract needed functions in `let` block - -### Host Service Files - -Each service gets its own file in `hosts//services/`: +### Imports ```nix -# hosts/cryodev-main/services/myservice.nix -{ outputs, constants, ... }: +# Local modules: relative paths +imports = [ ./hardware.nix ./networking.nix ]; + +# Shared modules: via outputs +imports = [ outputs.nixosModules.common ]; + +# External inputs +imports = [ inputs.sops-nix.nixosModules.sops ]; +``` + +### Constants +Use `constants.nix` for domains, IPs, and ports: +```nix +{ constants, ... }: { - imports = [ outputs.nixosModules.myservice ]; - - services.myservice = { - enable = true; - port = constants.services.myservice.port; - }; - - services.nginx.virtualHosts."${constants.services.myservice.fqdn}" = { - forceSSL = true; - enableACME = true; - locations."/" = { - proxyPass = "http://127.0.0.1:${toString constants.services.myservice.port}"; - }; - }; + services.nginx.virtualHosts."${constants.services.forgejo.fqdn}" = { ... }; } ``` -### Special Args Available in Modules +### Error Handling +```nix +config = lib.mkIf cfg.enable { + assertions = [ + { assertion = cfg.port > 1024; message = "Port must be > 1024"; } + ]; + warnings = lib.optional (cfg.debug) "Debug mode enabled!"; +}; +``` -- `inputs` -- flake inputs (nixpkgs, sops-nix, comin, headplane, etc.) -- `outputs` -- this flake's outputs (nixosModules, packages) -- `constants` -- values from `constants.nix` (domain, hosts, services) -- `lib` -- nixpkgs.lib extended with `lib.utils` +### Option Conflicts +Use `lib.mkDefault` for default values that can be overridden: +```nix +services.nginx.enable = lib.mkDefault true; +``` ## Directory Structure ``` . -├── flake.nix # Entry point, inputs/outputs, mkNixosConfiguration +├── flake.nix # Entry point, inputs/outputs ├── constants.nix # Central config (domains, IPs, ports) ├── hosts/ -│ ├── cryodev-main/ # x86_64 server (services/, secrets.yaml, binfmt.nix) -│ └── cryodev-pi/ # aarch64 RPi (services/, secrets.yaml, sd-image.nix) -├── modules/nixos/ # Reusable modules (common, forgejo, headscale, ...) -├── users/ # User definitions (steffen, ralph, benjamin) -├── apps/ # Nix apps (create, deploy, install, rebuild) -├── lib/utils.nix # Helper functions (mkUrl, mkVirtualHost, ...) +│ ├── cryodev-main/ # x86_64 server +│ │ ├── default.nix # Host entry point +│ │ ├── hardware.nix # Hardware configuration +│ │ ├── services/ # Service configurations +│ │ └── secrets.yaml # SOPS-encrypted secrets +│ └── cryodev-pi/ # aarch64 Raspberry Pi +├── modules/nixos/ # Reusable modules +│ ├── common/ # Shared base configuration +│ ├── sops/ # Secret management +│ ├── forgejo/ # Git server +│ ├── headscale/ # VPN control server +│ └── ... +├── lib/utils.nix # Helper functions +├── apps/ # Nix apps (rebuild) ├── pkgs/ # Custom packages ├── overlays/ # Nixpkgs overlays -├── templates/ # Host templates (generic-server, raspberry-pi) -├── deploy.json # Deploy app config (hosts, SSH port) -├── .sops.yaml # SOPS encryption rules (age keys per host) -├── .forgejo/workflows/ # CI pipelines (ci.yml, deploy.yml) -└── docs/ # Documentation (English) +├── templates/ # Host templates +├── scripts/ # Helper scripts (install.sh) +└── docs/ # Documentation ``` +## Key Patterns + +### Adding a New Raspberry Pi Host +1. Copy template: `cp -r templates/raspberry-pi hosts/new-pi` +2. Update `hosts/new-pi/networking.nix` (hostname) +3. Add to `flake.nix`: `new-pi = mkNixosConfiguration "aarch64-linux" [ ./hosts/new-pi ];` +4. Add to `.forgejo/workflows/build-pi-image.yml` matrix +5. Push → SD image is built automatically + +### SOPS Secrets +- Secrets encrypted with age using SSH host keys +- Config in `.sops.yaml`, secrets in `hosts//secrets.yaml` +- Reference: `config.sops.secrets."path/to/secret".path` + +### Special Args Available in Modules +- `inputs`: Flake inputs (nixpkgs, sops-nix, etc.) +- `outputs`: This flake's outputs (nixosModules, packages) +- `constants`: Values from `constants.nix` +- `lib`: Extended nixpkgs.lib with `lib.utils` + +## Deployment Workflows + +| Host | Strategy | Trigger | +|------|----------|---------| +| `cryodev-main` | Pull via Comin | Automatic polling | +| `cryodev-pi` | Pull via Comin | Automatic polling | +| SD Images | Built in CI | Push to main (for Pi hosts) | + ## Verification Checklist Before committing: - [ ] `nix fmt` passes -- [ ] `nix flake check` passes (or at least `nix eval` works for both hosts) -- [ ] New hosts added to `flake.nix` nixosConfigurations +- [ ] `nix flake check` passes (or at least `nix eval` works) +- [ ] New hosts added to `flake.nix` - [ ] Constants in `constants.nix`, not hardcoded - [ ] Secrets use SOPS, not plaintext -- [ ] New services have their own file in `hosts//services/` -- [ ] New modules registered in `modules/nixos/default.nix` -- [ ] Documentation in English diff --git a/digest.txt b/digest.txt index f554e4b..e11d733 100644 --- a/digest.txt +++ b/digest.txt @@ -3,17 +3,10 @@ Directory structure: ├── README.md ├── AGENTS.md ├── constants.nix - ├── deploy.json ├── flake.lock ├── flake.nix ├── .sops.yaml ├── apps/ - │ ├── create/ - │ │ ├── create.sh - │ │ └── default.nix - │ ├── deploy/ - │ │ ├── default.nix - │ │ └── deploy.sh │ ├── install/ │ │ ├── default.nix │ │ └── install.sh @@ -51,9 +44,7 @@ Directory structure: │ │ ├── secrets.yaml │ │ ├── users.nix │ │ └── services/ - │ │ ├── comin.nix │ │ ├── default.nix - │ │ ├── forgejo-runner.nix │ │ ├── forgejo.nix │ │ ├── headplane.nix │ │ ├── headscale.nix @@ -174,9 +165,7 @@ Directory structure: │ ├── openssh.nix │ └── tailscale.nix ├── users/ - │ ├── benjamin/ - │ │ └── default.nix - │ ├── ralph/ + │ ├── cryotherm/ │ │ └── default.nix │ └── steffen/ │ ├── default.nix @@ -184,8 +173,10 @@ Directory structure: │ └── X670E.pub └── .forgejo/ └── workflows/ - ├── ci.yml - └── deploy.yml + ├── build-hosts.yml + ├── build-pi-image.yml + ├── deploy-main.yml + └── flake-check.yml ================================================ FILE: README.md @@ -198,8 +189,8 @@ Declarative NixOS infrastructure for the **cryodev** environment, managed with N ```bash # Clone repository -git clone https://git.cryodev.xyz/steffen/cryodev.git -cd cryodev +git clone https://git.cryodev.xyz/steffen/cryodev-server.git +cd cryodev-server # Check configuration nix flake check @@ -212,7 +203,7 @@ nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel | Host | Architecture | Deployment | Description | |------|--------------|------------|-------------| -| `cryodev-main` | x86_64 | Pull (Comin) | Main server | +| `cryodev-main` | x86_64 | Push (deploy-rs) | Main server | | `cryodev-pi` | aarch64 | Pull (Comin) | Raspberry Pi client | ## Services @@ -229,7 +220,7 @@ nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel SD card images for Raspberry Pi clients are **built automatically** on every push to `main`. -Download from: [Releases](https://git.cryodev.xyz/steffen/cryodev/releases) +Download from: [Releases](https://git.cryodev.xyz/steffen/cryodev-server/releases) ```bash # Flash to SD card @@ -300,33 +291,37 @@ FILE: AGENTS.md ## Project Overview -NixOS infrastructure managed with Nix Flakes. Two hosts, reusable modules, SOPS secrets, Comin auto-deployment. - -- **Hosts**: `cryodev-main` (x86_64 server), `cryodev-pi` (aarch64 Raspberry Pi 4) +This repository contains a NixOS configuration managed with Nix Flakes. It defines: +- **Hosts**: `cryodev-main` (x86_64 server), `cryodev-pi` (aarch64 Raspberry Pi) - **Modules**: Reusable NixOS modules in `modules/nixos/` -- **Apps**: `create`, `deploy`, `install`, `rebuild` in `apps/` +- **Packages**: Custom packages in `pkgs/` - **Templates**: `raspberry-pi`, `generic-server` for bootstrapping new hosts ## Build & Development Commands +### Prerequisites +- **Nix** with Flakes enabled +- **Git** + +### Core Commands + ```bash -# Format code (required before committing, runs nixfmt via pre-commit) -nix fmt - -# Run all checks (formatting, package builds, overlay builds) -nix flake check - -# Quick evaluation test (faster than full build, use to validate changes) -nix eval .#nixosConfigurations.cryodev-main.config.system.build.toplevel.name -nix eval .#nixosConfigurations.cryodev-pi.config.system.build.toplevel.name - -# Full build +# Build host configuration nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel nix build .#nixosConfigurations.cryodev-pi.config.system.build.toplevel # Build Raspberry Pi SD image (requires binfmt on x86_64) nix build .#nixosConfigurations.cryodev-pi.config.system.build.sdImage +# Format code (required before committing) +nix fmt + +# Run all checks (lint, formatting, deploy-rs validation) +nix flake check + +# Quick evaluation test (faster than full build) +nix eval .#nixosConfigurations.cryodev-main.config.system.build.toplevel.name + # Update flake inputs nix flake update @@ -336,146 +331,158 @@ nix develop ### Deployment -Both hosts use **Comin** for automatic pull-based deployment (polls the git repo). -Manual deployment is only needed for initial setup or emergencies: - ```bash -# Deploy via deploy app (uses deploy.json, SSH port 2299, asks sudo password) -nix run .#deploy -- -n cryodev-main +# Deploy to cryodev-main via deploy-rs +nix run github:serokell/deploy-rs -- .#cryodev-main -# Manual deployment via nixos-rebuild -NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ - --target-host @ --sudo --ask-sudo-password -``` - -### Apps - -```bash -nix run .#create -- -t generic-server -n # Scaffold new host -nix run .#install -- -n -r # Install from NixOS ISO -nix run .#deploy -- -n # Deploy to host -nix run .#rebuild -- nixos # Rebuild locally +# Manual deployment via SSH +nixos-rebuild switch --flake .# \ + --target-host @ --use-remote-sudo \ + --ssh-option="-p 2299" ``` ## Code Style & Conventions ### Formatting -- **Tool**: `nixfmt` via `git-hooks.nix` (pre-commit) +- **Tool**: `nixfmt` via pre-commit hooks - **Run**: `nix fmt` before every commit -- **Indentation**: 2 spaces (enforced by formatter) +- **Indentation**: 2 spaces +- **Line length**: 80-100 characters (follow formatter) -### Naming Conventions - -| Type | Convention | Example | -|------|------------|---------| -| Files | kebab-case | `hardware-configuration.nix` | -| NixOS options | camelCase | `services.myService.enable` | -| Let bindings | camelCase | `let myValue = ...;` | -| Hosts | kebab-case | `cryodev-main`, `cryodev-pi` | -| Secret paths | kebab-case with `/` | `forgejo-runner/token` | - -### Module Pattern +### Module Structure ```nix -{ config, lib, ... }: +# Standard module pattern +{ config, lib, pkgs, inputs, outputs, constants, ... }: let cfg = config.services.myService; - inherit (lib) mkDefault mkEnableOption mkIf mkOption types; in { options.services.myService = { - enable = mkEnableOption "My service"; - port = mkOption { - type = types.port; + enable = lib.mkEnableOption "My service"; + port = lib.mkOption { + type = lib.types.port; default = 8080; description = "Port to listen on"; }; }; - config = mkIf cfg.enable { - assertions = [ - { assertion = cfg.port > 1024; message = "Port must be > 1024"; } - ]; - # Implementation + config = lib.mkIf cfg.enable { + # Implementation here }; } ``` -### Key Rules +### Naming Conventions +| Type | Convention | Example | +|------|------------|---------| +| Files | kebab-case | `hardware-configuration.nix` | +| Options | camelCase | `services.myService.enable` | +| Variables | camelCase | `let myValue = ...;` | +| Hosts | kebab-case | `cryodev-main`, `cryodev-pi` | -- **Use `lib.mkDefault`** for all module defaults (allows host-level overrides) -- **Use `constants.nix`** for domains, IPs, ports -- never hardcode these -- **Use `lib.utils`** helpers: `mkReverseProxyOption`, `mkVirtualHost`, `mkUrl` -- **Secrets via SOPS** only, never plaintext. Reference: `config.sops.secrets."path".path` -- **Imports**: relative paths for local files, `outputs.nixosModules.*` for shared modules, `inputs.*` for external -- **Assertions** for invalid configurations, `warnings` for non-critical issues -- **`lib.inherit`** pattern: extract needed functions in `let` block - -### Host Service Files - -Each service gets its own file in `hosts//services/`: +### Imports ```nix -# hosts/cryodev-main/services/myservice.nix -{ outputs, constants, ... }: +# Local modules: relative paths +imports = [ ./hardware.nix ./networking.nix ]; + +# Shared modules: via outputs +imports = [ outputs.nixosModules.common ]; + +# External inputs +imports = [ inputs.sops-nix.nixosModules.sops ]; +``` + +### Constants +Use `constants.nix` for domains, IPs, and ports: +```nix +{ constants, ... }: { - imports = [ outputs.nixosModules.myservice ]; - - services.myservice = { - enable = true; - port = constants.services.myservice.port; - }; - - services.nginx.virtualHosts."${constants.services.myservice.fqdn}" = { - forceSSL = true; - enableACME = true; - locations."/" = { - proxyPass = "http://127.0.0.1:${toString constants.services.myservice.port}"; - }; - }; + services.nginx.virtualHosts."${constants.services.forgejo.fqdn}" = { ... }; } ``` -### Special Args Available in Modules +### Error Handling +```nix +config = lib.mkIf cfg.enable { + assertions = [ + { assertion = cfg.port > 1024; message = "Port must be > 1024"; } + ]; + warnings = lib.optional (cfg.debug) "Debug mode enabled!"; +}; +``` -- `inputs` -- flake inputs (nixpkgs, sops-nix, comin, headplane, etc.) -- `outputs` -- this flake's outputs (nixosModules, packages) -- `constants` -- values from `constants.nix` (domain, hosts, services) -- `lib` -- nixpkgs.lib extended with `lib.utils` +### Option Conflicts +Use `lib.mkDefault` for default values that can be overridden: +```nix +services.nginx.enable = lib.mkDefault true; +``` ## Directory Structure ``` . -├── flake.nix # Entry point, inputs/outputs, mkNixosConfiguration +├── flake.nix # Entry point, inputs/outputs ├── constants.nix # Central config (domains, IPs, ports) ├── hosts/ -│ ├── cryodev-main/ # x86_64 server (services/, secrets.yaml, binfmt.nix) -│ └── cryodev-pi/ # aarch64 RPi (services/, secrets.yaml, sd-image.nix) -├── modules/nixos/ # Reusable modules (common, forgejo, headscale, ...) -├── users/ # User definitions (steffen, ralph, benjamin) -├── apps/ # Nix apps (create, deploy, install, rebuild) -├── lib/utils.nix # Helper functions (mkUrl, mkVirtualHost, ...) +│ ├── cryodev-main/ # x86_64 server +│ │ ├── default.nix # Host entry point +│ │ ├── hardware.nix # Hardware configuration +│ │ ├── services/ # Service configurations +│ │ └── secrets.yaml # SOPS-encrypted secrets +│ └── cryodev-pi/ # aarch64 Raspberry Pi +├── modules/nixos/ # Reusable modules +│ ├── common/ # Shared base configuration +│ ├── sops/ # Secret management +│ ├── forgejo/ # Git server +│ ├── headscale/ # VPN control server +│ └── ... +├── lib/utils.nix # Helper functions +├── apps/ # Nix apps (rebuild) ├── pkgs/ # Custom packages ├── overlays/ # Nixpkgs overlays -├── templates/ # Host templates (generic-server, raspberry-pi) -├── deploy.json # Deploy app config (hosts, SSH port) -├── .sops.yaml # SOPS encryption rules (age keys per host) -├── .forgejo/workflows/ # CI pipelines (ci.yml, deploy.yml) -└── docs/ # Documentation (English) +├── templates/ # Host templates +├── scripts/ # Helper scripts (install.sh) +└── docs/ # Documentation ``` +## Key Patterns + +### Adding a New Raspberry Pi Host +1. Copy template: `cp -r templates/raspberry-pi hosts/new-pi` +2. Update `hosts/new-pi/networking.nix` (hostname) +3. Add to `flake.nix`: `new-pi = mkNixosConfiguration "aarch64-linux" [ ./hosts/new-pi ];` +4. Add to `.forgejo/workflows/build-pi-image.yml` matrix +5. Push → SD image is built automatically + +### SOPS Secrets +- Secrets encrypted with age using SSH host keys +- Config in `.sops.yaml`, secrets in `hosts//secrets.yaml` +- Reference: `config.sops.secrets."path/to/secret".path` + +### Special Args Available in Modules +- `inputs`: Flake inputs (nixpkgs, sops-nix, etc.) +- `outputs`: This flake's outputs (nixosModules, packages) +- `constants`: Values from `constants.nix` +- `lib`: Extended nixpkgs.lib with `lib.utils` + +## Deployment Workflows + +| Host | Strategy | Trigger | +|------|----------|---------| +| `cryodev-main` | Push via deploy-rs | Forgejo Actions on push to main | +| `cryodev-pi` | Pull via Comin | Automatic polling | +| SD Images | Built in CI | Push to main (for Pi hosts) | + ## Verification Checklist Before committing: - [ ] `nix fmt` passes -- [ ] `nix flake check` passes (or at least `nix eval` works for both hosts) -- [ ] New hosts added to `flake.nix` nixosConfigurations +- [ ] `nix flake check` passes (or at least `nix eval` works) +- [ ] New hosts added to `flake.nix` - [ ] Constants in `constants.nix`, not hardcoded - [ ] Secrets use SOPS, not plaintext -- [ ] New services have their own file in `hosts//services/` -- [ ] New modules registered in `modules/nixos/default.nix` -- [ ] Documentation in English @@ -523,22 +530,6 @@ FILE: constants.nix -================================================ -FILE: deploy.json -================================================ -{ - "sshPort": "2299", - "buildHost": "localhost", - "hosts": [ - { - "name": "cryodev-main", - "address": "steffen@cryodev.xyz" - } - ] -} - - - ================================================ FILE: flake.lock ================================================ @@ -582,6 +573,28 @@ FILE: flake.lock "type": "github" } }, + "deploy-rs": { + "inputs": { + "flake-compat": "flake-compat_2", + "nixpkgs": [ + "nixpkgs" + ], + "utils": "utils" + }, + "locked": { + "lastModified": 1770019181, + "narHash": "sha256-hwsYgDnby50JNVpTRYlF3UR/Rrpt01OrxVuryF40CFY=", + "owner": "serokell", + "repo": "deploy-rs", + "rev": "77c906c0ba56aabdbc72041bf9111b565cdd6171", + "type": "github" + }, + "original": { + "owner": "serokell", + "repo": "deploy-rs", + "type": "github" + } + }, "devshell": { "inputs": { "nixpkgs": [ @@ -620,6 +633,22 @@ FILE: flake.lock } }, "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_3": { "flake": false, "locked": { "lastModified": 1767039857, @@ -635,7 +664,7 @@ FILE: flake.lock "type": "github" } }, - "flake-compat_3": { + "flake-compat_4": { "flake": false, "locked": { "lastModified": 1767039857, @@ -674,7 +703,7 @@ FILE: flake.lock }, "flake-utils": { "inputs": { - "systems": "systems" + "systems": "systems_2" }, "locked": { "lastModified": 1731533236, @@ -692,7 +721,7 @@ FILE: flake.lock }, "flake-utils_2": { "inputs": { - "systems": "systems_2" + "systems": "systems_3" }, "locked": { "lastModified": 1731533236, @@ -710,7 +739,7 @@ FILE: flake.lock }, "git-hooks": { "inputs": { - "flake-compat": "flake-compat_2", + "flake-compat": "flake-compat_3", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -850,7 +879,7 @@ FILE: flake.lock "nixos-mailserver": { "inputs": { "blobs": "blobs", - "flake-compat": "flake-compat_3", + "flake-compat": "flake-compat_4", "git-hooks": "git-hooks_2", "nixpkgs": [ "nixpkgs" @@ -957,7 +986,7 @@ FILE: flake.lock "nixpkgs" ], "nuschtosSearch": "nuschtosSearch", - "systems": "systems_3" + "systems": "systems_4" }, "locked": { "lastModified": 1769049374, @@ -1000,6 +1029,7 @@ FILE: flake.lock "root": { "inputs": { "comin": "comin", + "deploy-rs": "deploy-rs", "git-hooks": "git-hooks", "headplane": "headplane", "nixos-mailserver": "nixos-mailserver", @@ -1075,6 +1105,21 @@ FILE: flake.lock "type": "github" } }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "treefmt-nix": { "inputs": { "nixpkgs": "nixpkgs" @@ -1092,6 +1137,24 @@ FILE: flake.lock "repo": "treefmt-nix", "type": "github" } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } } }, "root": "root", @@ -1120,6 +1183,9 @@ FILE: flake.nix comin.url = "github:nlewo/comin"; comin.inputs.nixpkgs.follows = "nixpkgs"; + deploy-rs.url = "github:serokell/deploy-rs"; + deploy-rs.inputs.nixpkgs.follows = "nixpkgs"; + nixvim.url = "github:nix-community/nixvim/nixos-25.11"; nixvim.inputs.nixpkgs.follows = "nixpkgs"; @@ -1179,8 +1245,6 @@ FILE: flake.nix }; in { - create = mkApp "create"; - deploy = mkApp "deploy"; install = mkApp "install"; rebuild = mkApp "rebuild"; } @@ -1221,6 +1285,18 @@ FILE: flake.nix pkgs.writeShellScriptBin "pre-commit-run" script ); + deploy = { + nodes = { + cryodev-main = { + hostname = constants.domain; + profiles.system = { + user = "root"; + path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations.cryodev-main; + }; + }; + }; + }; + checks = forAllSystems ( system: let @@ -1230,6 +1306,7 @@ FILE: flake.nix inherit system; overlays = [ self.overlays.modifications ]; }; + deployChecks = inputs.deploy-rs.lib.${system}.deployChecks self.deploy; in { pre-commit-check = inputs.git-hooks.lib.${system}.run { @@ -1243,6 +1320,7 @@ FILE: flake.nix # package = overlaidPkgs.package; }; } + // deployChecks ); }; } @@ -1253,299 +1331,19 @@ FILE: flake.nix FILE: .sops.yaml ================================================ keys: - - &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local) + - &admin_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t - &cryodev-main_key age1y6hushuapy0k04mrvvpev0t8lq44w904r596jus44nhkflky0yhqgq2xx6 creation_rules: - path_regex: hosts/cryodev-main/secrets.yaml$ key_groups: - age: - - *steffen_key + - *admin_key - *cryodev-main_key - path_regex: hosts/cryodev-pi/secrets.yaml$ key_groups: - age: - - *steffen_key - # - *cryodev-pi_key # Add after Pi installation - - - -================================================ -FILE: apps/create/create.sh -================================================ -#!/usr/bin/env bash - -# Create a new host from a template - -FLAKE_DIR="." -TEMPLATE="" -HOSTNAME="" -SYSTEM="" - -SEPARATOR="________________________________________" - -usage() { - cat <&2 - exit 1 -} - -while [[ $# -gt 0 ]]; do - case "$1" in - -t|--template) TEMPLATE="$2"; shift 2 ;; - -n|--hostname) HOSTNAME="$2"; shift 2 ;; - -s|--system) SYSTEM="$2"; shift 2 ;; - -f|--flake) FLAKE_DIR="$2"; shift 2 ;; - -h|--help) usage; exit 0 ;; - *) error "Unknown option: $1" ;; - esac -done - -# Validate -[[ -z "$TEMPLATE" ]] && error "Template is required (-t)" -[[ -z "$HOSTNAME" ]] && error "Hostname is required (-n)" - -TEMPLATE_DIR="$FLAKE_DIR/templates/$TEMPLATE" -HOST_DIR="$FLAKE_DIR/hosts/$HOSTNAME" - -[[ ! -d "$TEMPLATE_DIR" ]] && error "Template '$TEMPLATE' not found in $TEMPLATE_DIR" -[[ -d "$HOST_DIR" ]] && error "Host '$HOSTNAME' already exists in $HOST_DIR" - -# Derive system from template if not specified -if [[ -z "$SYSTEM" ]]; then - case "$TEMPLATE" in - generic-server) SYSTEM="x86_64-linux" ;; - raspberry-pi) SYSTEM="aarch64-linux" ;; - *) error "Cannot derive system for template '$TEMPLATE'. Use -s to specify." ;; - esac -fi - -echo "$SEPARATOR" -echo "Creating host '$HOSTNAME' from template '$TEMPLATE'" -echo " System: $SYSTEM" -echo " Target: $HOST_DIR" -echo "$SEPARATOR" - -# Copy template -cp -r "$TEMPLATE_DIR" "$HOST_DIR" - -# Remove template flake.nix (not needed in host dir) -rm -f "$HOST_DIR/flake.nix" - -# Replace hostname in networking.nix -sed -i "s/networking.hostName = \".*\"/networking.hostName = \"$HOSTNAME\"/" "$HOST_DIR/networking.nix" - -# Create empty secrets.yaml placeholder -touch "$HOST_DIR/secrets.yaml" - -# Add to git -git -C "$FLAKE_DIR" add "$HOST_DIR" - -echo "$SEPARATOR" -echo "Host '$HOSTNAME' created successfully." -echo "" -echo "Next steps:" -echo " 1. Add to flake.nix:" -echo "" -echo " $HOSTNAME = mkNixosConfiguration \"$SYSTEM\" [ ./hosts/$HOSTNAME ];" -echo "" -echo " 2. Update hardware.nix and disks.sh for your hardware" -echo " 3. Update .sops.yaml with creation rules for hosts/$HOSTNAME/secrets.yaml" -echo " 4. Follow the first-install guide: docs/getting-started/first-install.md" - - - -================================================ -FILE: apps/create/default.nix -================================================ -{ - writeShellApplication, - git, - gnused, - ... -}: - -let - name = "create"; - text = builtins.readFile ./${name}.sh; -in -writeShellApplication { - inherit name text; - meta.mainProgram = name; - - runtimeInputs = [ - git - gnused - ]; -} - - - -================================================ -FILE: apps/deploy/default.nix -================================================ -{ - writeShellApplication, - jq, - ... -}: - -let - name = "deploy"; - text = builtins.readFile ./${name}.sh; -in -writeShellApplication { - inherit name text; - meta.mainProgram = name; - - runtimeInputs = [ - jq - ]; -} - - - -================================================ -FILE: apps/deploy/deploy.sh -================================================ -#!/usr/bin/env bash - -# defaults -FLAKE_URI="." -CONFIG_FILE="./deploy.json" -ACTION="switch" -USE_SUDO=true -DO_BUILD=true -FILTER_HOSTS=() - -usage() { - cat < $1\033[0m"; } -success() { echo -e "\033[0;32m$1\033[0m"; } -error() { echo -e "\033[0;31mError: $1\033[0m" >&2; exit 1; } - -while [[ $# -gt 0 ]]; do - case "$1" in - switch|boot|test) ACTION="$1"; shift ;; - -n|--host) FILTER_HOSTS+=("$2"); shift 2 ;; - -f|--flake) FLAKE_URI="$2"; shift 2 ;; - -c|--config) CONFIG_FILE="$2"; shift 2 ;; - --no-sudo) USE_SUDO=false; shift ;; - --skip-build) DO_BUILD=false; shift ;; - -h|--help) usage; exit 0 ;; - *) error "Invalid argument '$1'" ;; - esac -done - -command -v jq &> /dev/null || error "jq is not installed." -[ -f "$CONFIG_FILE" ] || error "Config '$CONFIG_FILE' not found." - -BUILD_HOST=$(jq -r '.buildHost // "localhost"' "$CONFIG_FILE") -[[ "$BUILD_HOST" =~ ^(127\.0\.0\.1|::1)$ ]] && BUILD_HOST="localhost" - -SSH_PORT=$(jq -r '.sshPort // "22"' "$CONFIG_FILE") -export NIX_SSHOPTS="-p $SSH_PORT" - -mapfile -t ALL_ENTRIES < <(jq -r '.hosts[] | "\(.name) \(.address)"' "$CONFIG_FILE") -[ ${#ALL_ENTRIES[@]} -eq 0 ] && error "No hosts defined in $CONFIG_FILE" - -# Filter hosts if -n was provided -HOST_ENTRIES=() -if [ ${#FILTER_HOSTS[@]} -gt 0 ]; then - for entry in "${ALL_ENTRIES[@]}"; do - read -r name _address <<< "$entry" - for filter in "${FILTER_HOSTS[@]}"; do - if [[ "$name" == "$filter" ]]; then - HOST_ENTRIES+=("$entry") - break - fi - done - done - # Check for unknown hosts - for filter in "${FILTER_HOSTS[@]}"; do - found=false - for entry in "${ALL_ENTRIES[@]}"; do - read -r name _ <<< "$entry" - [[ "$name" == "$filter" ]] && found=true && break - done - [[ "$found" == false ]] && error "Host '$filter' not found in $CONFIG_FILE" - done - [ ${#HOST_ENTRIES[@]} -eq 0 ] && error "No matching hosts found" -else - HOST_ENTRIES=("${ALL_ENTRIES[@]}") -fi - -echo "Action: $ACTION" -echo "Flake: $FLAKE_URI" -echo "Builder: $BUILD_HOST" -echo "SSH Port: $SSH_PORT" -echo "Hosts: $(printf '%s ' "${HOST_ENTRIES[@]}" | sed 's/ [^ ]*//g; s/ */, /g')" - -if [ "$DO_BUILD" = true ]; then - _status "Building configurations..." - for entry in "${HOST_ENTRIES[@]}"; do - read -r name address <<< "$entry" - echo "------------------------------------------------" - echo "Building host '$name':" - - CMD=("nixos-rebuild" "build" "--flake" "${FLAKE_URI}#${name}") - [[ "$BUILD_HOST" != "localhost" ]] && CMD+=("--build-host" "$BUILD_HOST") - - "${CMD[@]}" || error "Build failed for $name" - success "Build for host '$name' successful." - done -fi - -_status "Deploying to targets..." -for entry in "${HOST_ENTRIES[@]}"; do - read -r name address <<< "$entry" - echo "------------------------------------------------" - echo "Deploying to host '$name' ($address):" - - CMD=("nixos-rebuild" "$ACTION" "--flake" "${FLAKE_URI}#${name}" "--target-host" "$address") - [[ "$BUILD_HOST" != "localhost" ]] && CMD+=("--build-host" "$BUILD_HOST") - [[ "$USE_SUDO" = true ]] && CMD+=("--sudo" "--ask-sudo-password") - - "${CMD[@]}" || error "Activation failed for $name" - success "Host '$name' updated." -done - -success "Deployment complete." + - *admin_key + # - *pi_key # Add pi key here once obtained @@ -2036,34 +1834,34 @@ FILE: docs/index.md ================================================ # Cryodev NixOS Configuration Documentation -Welcome to the documentation for the **cryodev** NixOS infrastructure. +Willkommen zur Dokumentation der **cryodev** NixOS-Infrastruktur. ## Quick Links ### Getting Started -- [Prerequisites](getting-started/prerequisites.md) - Required tools -- [Adding a New Raspberry Pi](getting-started/new-client.md) - Complete workflow for new clients -- [SD Image Reference](getting-started/sd-image.md) - Details on image building -- [First Installation (Server)](getting-started/first-install.md) - Bootstrap for x86_64 hosts -- [Reinstallation](getting-started/reinstall.md) - Reinstall with hardware changes +- [Voraussetzungen](getting-started/prerequisites.md) - Benötigte Tools +- [Neuen Raspberry Pi hinzufügen](getting-started/new-client.md) - Kompletter Workflow für neue Clients +- [SD-Image Referenz](getting-started/sd-image.md) - Details zum Image-Build +- [Erstinstallation (Server)](getting-started/first-install.md) - Bootstrap für x86_64 Hosts +- [Neuinstallation](getting-started/reinstall.md) - Reinstall mit Hardware-Änderungen ### Services -- [SOPS Secrets](services/sops.md) - Secret management with sops-nix -- [Headscale](services/headscale.md) - Self-hosted Tailscale server -- [Headplane](services/headplane.md) - Web UI for Headscale -- [Tailscale](services/tailscale.md) - Mesh VPN client -- [Mailserver](services/mailserver.md) - Email stack (Postfix/Dovecot) -- [Forgejo](services/forgejo.md) - Git hosting with CI/CD -- [Netdata](services/netdata.md) - Monitoring and alerting +- [SOPS Secrets](services/sops.md) - Geheimnisverwaltung mit sops-nix +- [Headscale](services/headscale.md) - Self-hosted Tailscale Server +- [Headplane](services/headplane.md) - Web-UI für Headscale +- [Tailscale](services/tailscale.md) - Mesh-VPN Client +- [Mailserver](services/mailserver.md) - E-Mail Stack (Postfix/Dovecot) +- [Forgejo](services/forgejo.md) - Git-Hosting mit CI/CD +- [Netdata](services/netdata.md) - Monitoring und Alerting ### Deployment -- [Continuous Deployment](deployment/cd.md) - Push- and pull-based deployment -- [DNS Configuration](deployment/dns.md) - Required DNS records +- [Continuous Deployment](deployment/cd.md) - Push- und Pull-basiertes Deployment +- [DNS-Konfiguration](deployment/dns.md) - Benötigte DNS-Einträge -## Architecture +## Architektur ``` Internet @@ -2093,41 +1891,41 @@ Welcome to the documentation for the **cryodev** NixOS infrastructure. +-------------------+ ``` -## Installation Scenarios +## Installations-Szenarien -| Scenario | Description | Guide | -|----------|-------------|-------| -| **New Raspberry Pi** | Create config, build image, flash | [new-client.md](getting-started/new-client.md) | -| **First Installation (Server)** | x86_64 host, manual installation | [first-install.md](getting-started/first-install.md) | -| **Reinstallation** | Existing host, new hardware | [reinstall.md](getting-started/reinstall.md) | +| Szenario | Beschreibung | Anleitung | +|----------|--------------|-----------| +| **Neuer Raspberry Pi** | Config erstellen → Image bauen → Flashen | [new-client.md](getting-started/new-client.md) | +| **Erstinstallation (Server)** | x86_64 Host, manuelle Installation | [first-install.md](getting-started/first-install.md) | +| **Neuinstallation** | Bestehender Host, neue Hardware | [reinstall.md](getting-started/reinstall.md) | -For Raspberry Pi: [SD Image Reference](getting-started/sd-image.md) +Für Raspberry Pi: [SD-Image Referenz](getting-started/sd-image.md) -## Directory Structure +## Verzeichnisstruktur ``` . ├── flake.nix # Entry point, inputs and outputs -├── constants.nix # Central configuration (domains, IPs, ports) -├── hosts/ # Host-specific configurations +├── constants.nix # Zentrale Config (Domains, IPs, Ports) +├── hosts/ # Host-spezifische Konfigurationen │ ├── cryodev-main/ │ └── cryodev-pi/ -├── modules/ # Reusable NixOS modules +├── modules/ # Wiederverwendbare NixOS-Module │ └── nixos/ -├── pkgs/ # Custom packages -├── overlays/ # Nixpkgs overlays -├── templates/ # Templates for new hosts -├── scripts/ # Helper scripts (install.sh) -├── apps/ # Nix apps (rebuild) -└── lib/ # Helper functions (utils.nix) +├── pkgs/ # Eigene Pakete +├── overlays/ # Nixpkgs Overlays +├── templates/ # Templates für neue Hosts +├── scripts/ # Helper-Scripts (install.sh) +├── apps/ # Nix Apps (rebuild) +└── lib/ # Helper-Funktionen (utils.nix) ``` -## Deployment Strategies +## Deployment-Strategien -| Host | Strategy | Tool | Description | -|------|----------|------|-------------| -| `cryodev-main` | Pull-based | Comin | Polls the repository for changes | -| `cryodev-pi` | Pull-based | Comin | Polls the repository for changes | +| Host | Strategie | Tool | Beschreibung | +|------|-----------|------|--------------| +| `cryodev-main` | Push-basiert | deploy-rs via Forgejo Actions | Sofortige Updates bei Push | +| `cryodev-pi` | Pull-basiert | Comin | Pollt Repository auf Änderungen | @@ -2136,39 +1934,122 @@ FILE: docs/deployment/cd.md ================================================ # Continuous Deployment -All hosts use **Comin** (pull-based) for automatic deployment. +The cryodev infrastructure uses two deployment strategies optimized for different host types. ## Overview | Host | Strategy | Tool | Trigger | |------|----------|------|---------| -| `cryodev-main` | Pull-based | Comin | Automatic polling | -| `cryodev-pi` | Pull-based | Comin | Automatic polling | +| `cryodev-main` | Push-based | deploy-rs | Git push via Forgejo Actions | +| `cryodev-pi` | Pull-based | Comin | Periodic polling | -## How It Works +## Push-based Deployment (cryodev-main) + +### How It Works 1. Developer pushes to `main` branch -2. CI (Forgejo Actions) runs flake-check and builds all hosts -3. Comin on each host periodically polls the Git repository -4. On changes, Comin builds and activates the new configuration +2. Forgejo Actions workflow triggers +3. `deploy-rs` connects via SSH and deploys -## Configuration +### Setup + +#### 1. Generate Deploy Key + +```bash +ssh-keygen -t ed25519 -f deploy_key -C "forgejo-actions" +``` + +#### 2. Add Public Key to Server + +On `cryodev-main`: + +```bash +echo "PUBLIC_KEY_CONTENT" >> /root/.ssh/authorized_keys +``` + +#### 3. Add Private Key to Forgejo + +1. Go to Repository Settings > Secrets +2. Add secret named `DEPLOY_SSH_KEY` +3. Paste the private key content + +#### 4. Workflow Configuration + +`.forgejo/workflows/deploy.yaml`: + +```yaml +name: Deploy +on: + push: + branches: [main] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v24 + - run: nix flake check + + deploy: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v24 + + - name: Setup SSH + env: + SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + run: | + mkdir -p ~/.ssh + echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh-keyscan cryodev-main >> ~/.ssh/known_hosts + + - name: Deploy + run: nix run github:serokell/deploy-rs -- .#cryodev-main +``` + +### Rollback + +deploy-rs automatically rolls back if the new configuration fails health checks. + +Manual rollback: + +```bash +# List generations +sudo nix-env -p /nix/var/nix/profiles/system --list-generations + +# Rollback to previous +sudo nixos-rebuild switch --rollback +``` + +## Pull-based Deployment (cryodev-pi) + +### How It Works + +1. Comin periodically polls the Git repository +2. On changes, it builds and activates the new configuration +3. Works through NAT without incoming connections + +### Configuration ```nix -# hosts//services/comin.nix +# hosts/cryodev-pi/services/comin.nix { services.comin = { enable = true; remotes = [{ name = "origin"; - url = "https://git.cryodev.xyz/steffen/cryodev.git"; + url = "https://git.cryodev.xyz/steffen/cryodev-server.git"; branches.main.name = "main"; }]; }; } ``` -## Monitoring +### Monitoring Check Comin status: @@ -2183,7 +2064,7 @@ Force immediate update: sudo systemctl restart comin ``` -## Troubleshooting +### Troubleshooting If Comin fails to build: @@ -2193,30 +2074,24 @@ sudo journalctl -u comin --since "1 hour ago" # Manual build test cd /var/lib/comin/repo -nix build .#nixosConfigurations..config.system.build.toplevel -``` - -## Rollback - -```bash -# List generations -sudo nix-env -p /nix/var/nix/profiles/system --list-generations - -# Rollback to previous -sudo nixos-rebuild switch --rollback +nix build .#nixosConfigurations.cryodev-pi.config.system.build.toplevel ``` ## Manual Deployment -For initial setup or emergencies: +For hosts not using automated deployment: ```bash -# Using the deploy app -nix run .#deploy -- -n +# Build locally +nix build .#nixosConfigurations..config.system.build.toplevel -# Or manually with nixos-rebuild -NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ - --target-host @ --sudo --ask-sudo-password +# Deploy with nixos-rebuild +nixos-rebuild switch --flake .# \ + --target-host @ --use-remote-sudo \ + --ssh-option="-p 2299" + +# Or using deploy-rs +nix run github:serokell/deploy-rs -- .# ``` ## Testing Changes @@ -2251,8 +2126,6 @@ Required DNS records for the cryodev infrastructure. |----------|------|-------|---------| | `@` | A | `` | Main server | | `@` | AAAA | `` | Main server (IPv6) | -| `www` | A | `` | www redirect | -| `www` | AAAA | `` | www redirect (IPv6) | | `mail` | A | `` | Mail server | | `mail` | AAAA | `` | Mail server (IPv6) | @@ -2272,32 +2145,7 @@ Required DNS records for the cryodev infrastructure. | `@` | MX | `10 mail.cryodev.xyz.` | Mail delivery | | `@` | TXT | `"v=spf1 mx ~all"` | SPF | | `_dmarc` | TXT | `"v=DMARC1; p=none"` | DMARC | -| `mail._domainkey` | TXT | *(see below)* | DKIM | - -### Reverse DNS (PTR) - -For reliable mail delivery, a **PTR record** must be configured at the hosting -provider (not in the domain's DNS panel): - -| IP | PTR Value | -|----|-----------| -| `` | `mail.cryodev.xyz` | -| `` | `mail.cryodev.xyz` | - -#### Hetzner Robot (Dedicated Server) - -1. [robot.hetzner.com](https://robot.hetzner.com) > **Server** > Select the server -2. **IPs** tab -3. Click the **pencil icon** next to the IPv4 address -4. Enter `mail.cryodev.xyz` and save -5. For IPv6: Under **Subnets**, repeat the same for the primary IPv6 address - -#### Hetzner Cloud - -1. [cloud.hetzner.com](https://cloud.hetzner.com) > Select the server -2. **Networking** tab -3. Under "Primary IP", click the IP > **Reverse DNS** -4. Enter `mail.cryodev.xyz` (for both IPv4 and IPv6) +| `mail._domainkey` | TXT | `"v=DKIM1; k=rsa; p=..."` | DKIM | ## Getting the DKIM Key @@ -2309,18 +2157,6 @@ sudo cat /var/dkim/cryodev.xyz.mail.txt Add this as a TXT record for `mail._domainkey.cryodev.xyz`. -## Complete Checklist - -- [ ] A/AAAA for `@` (root domain) -- [ ] A/AAAA for `www` -- [ ] A/AAAA for `mail` -- [ ] CNAME for `git`, `headscale`, `headplane`, `netdata` -- [ ] MX record -- [ ] TXT for SPF (`v=spf1 mx ~all`) -- [ ] TXT for DMARC (`v=DMARC1; p=none`) -- [ ] TXT for DKIM (`mail._domainkey` -- after first deployment) -- [ ] PTR record at hosting provider (reverse DNS) - ## Verification ### Check DNS Propagation @@ -2340,9 +2176,6 @@ dig TXT mail._domainkey.cryodev.xyz # DMARC dig TXT _dmarc.cryodev.xyz - -# Reverse DNS -dig -x ``` ### Online Tools @@ -2357,7 +2190,7 @@ For initial setup, use low TTLs (300 seconds) to allow quick changes. After verification, increase to: - A/AAAA records: 3600 (1 hour) -- CNAME records: 3600 (1 hour) +- CNAME records: 3600 (1 hour) - MX records: 3600 (1 hour) - TXT records: 3600 (1 hour) @@ -2367,7 +2200,7 @@ Ensure these ports are open on `cryodev-main`: | Port | Protocol | Service | |------|----------|---------| -| 2299 | TCP | SSH | +| 22 | TCP | SSH | | 80 | TCP | HTTP (ACME/redirect) | | 443 | TCP | HTTPS | | 25 | TCP | SMTP | @@ -2380,47 +2213,50 @@ Ensure these ports are open on `cryodev-main`: ================================================ FILE: docs/getting-started/first-install.md ================================================ -# Initial Installation (x86_64 Server) +# Erstinstallation (x86_64 Server) -This guide describes the **initial installation** of a new x86_64 server (e.g. cryodev-main). +Diese Anleitung beschreibt die **Erstinstallation** eines neuen x86_64 Servers (z.B. cryodev-main). -> **For Raspberry Pi:** See [Creating an SD Image](sd-image.md). +> **Fuer Raspberry Pi:** Siehe [SD-Image erstellen](sd-image.md). -## Overview +## Uebersicht -During initial installation there is a chicken-and-egg problem: -- SOPS secrets are encrypted with the SSH host key -- The SSH host key is only generated during installation -- Therefore: **Install without secrets first, then configure secrets** +Bei der Erstinstallation gibt es ein Henne-Ei-Problem: +- SOPS-Secrets werden mit dem SSH-Host-Key verschluesselt +- Der SSH-Host-Key wird erst bei der Installation generiert +- Daher: **Erst ohne Secrets installieren, dann Secrets konfigurieren** -### Process +### Ablauf ``` -1. Disable services (that require secrets) -2. Install NixOS -3. Extract SSH host key, configure SOPS, create immediately available secrets -4. Enable stage-1 services and deploy (Headscale, Forgejo, Mail, Nginx) -5. Generate remaining secrets (Tailscale, Headplane, Forgejo Runner) -6. Enable stage-2 services and perform final deployment +1. Services deaktivieren (die Secrets brauchen) +2. NixOS installieren +3. SSH-Host-Key extrahieren, SOPS konfigurieren, Secrets erstellen +4. Services reaktivieren und deployen ``` -## Step 1: Prepare Host Configuration +## Schritt 1: Host-Konfiguration vorbereiten -> If the host already exists in `hosts/` and `flake.nix`, skip 1.1-1.2. +> Falls der Host bereits in `hosts/` und `flake.nix` existiert, ueberspringe 1.1-1.3. -### 1.1 Create Host from Template +### 1.1 Template kopieren ```bash -nix run .#create -- -t generic-server -n +cp -r templates/generic-server hosts/ ``` -The script: -- Copies the template to `hosts//` -- Sets the hostname in `networking.nix` -- Creates an empty `secrets.yaml` -- Adds the files to Git +### 1.2 Hostname setzen -### 1.2 Register in flake.nix +`hosts//networking.nix`: + +```nix +{ + networking.hostName = ""; + networking.domain = "cryodev.xyz"; +} +``` + +### 1.3 In flake.nix registrieren ```nix nixosConfigurations = { @@ -2428,24 +2264,22 @@ nixosConfigurations = { }; ``` -Also adjust `hardware.nix` and `disks.sh` for the target hardware. +### 1.4 Services temporaer deaktivieren -### 1.4 Temporarily Disable Services +Alle Services, die SOPS-Secrets referenzieren, muessen fuer die Erstinstallation deaktiviert werden. Andernfalls schlaegt die Installation fehl, weil die Secrets noch nicht entschluesselt werden koennen. -All services that reference SOPS secrets must be disabled for the initial installation. Otherwise the installation will fail because the secrets cannot yet be decrypted. - -In `hosts//services/default.nix`, comment out the corresponding imports: +In `hosts//services/default.nix` die entsprechenden Imports auskommentieren: ```nix { imports = [ - # Disabled until SOPS secrets are configured: - # ./forgejo.nix # requires: forgejo-runner/token, forgejo/mail-pw - # ./headplane.nix # requires: headplane/cookie_secret, headplane/agent_pre_authkey - # ./mailserver.nix # requires: mailserver/accounts/* - # ./tailscale.nix # requires: tailscale/auth-key + # Deaktiviert bis SOPS-Secrets konfiguriert sind: + # ./forgejo.nix # braucht: forgejo-runner/token, forgejo/mail-pw + # ./headplane.nix # braucht: headplane/cookie_secret, headplane/agent_pre_authkey + # ./mailserver.nix # braucht: mailserver/accounts/* + # ./tailscale.nix # braucht: tailscale/auth-key - # These services do not require secrets: + # Diese Services brauchen keine Secrets: ./headscale.nix ./netdata.nix ./nginx.nix @@ -2455,7 +2289,7 @@ In `hosts//services/default.nix`, comment out the corresponding import } ``` -Additionally, in `hosts//services/sops.nix`, comment out the secret definitions: +Zusaetzlich in `hosts//services/sops.nix` die Secrets-Definitionen auskommentieren: ```nix sops = { @@ -2467,32 +2301,32 @@ sops = { }; ``` -### 1.5 Test the Configuration +### 1.5 Konfiguration testen ```bash nix eval .#nixosConfigurations..config.system.build.toplevel.name ``` -## Step 2: Perform Installation +## Schritt 2: Installation durchfuehren -### 2.1 Boot NixOS ISO +### 2.1 NixOS ISO booten -Boot from the [NixOS Minimal ISO](https://nixos.org/download/#nixos-iso) (USB/CD). +Vom [NixOS Minimal ISO](https://nixos.org/download/#nixos-iso) booten (USB/CD). -### 2.2 Set Up Network and SSH +### 2.2 Netzwerk und SSH einrichten ```bash -passwd # Set root password for SSH access -ip a # Determine IP address +passwd # Root-Passwort setzen fuer SSH-Zugang +ip a # IP-Adresse ermitteln ``` -Optionally connect via SSH (more convenient): +Optional per SSH verbinden (bequemer): ```bash ssh -o StrictHostKeyChecking=no root@ ``` -### 2.3 Install +### 2.3 Installieren ```bash nix --experimental-features "nix-command flakes" run \ @@ -2501,20 +2335,20 @@ nix --experimental-features "nix-command flakes" run \ -r ``` -Alternatively, if the repository has already been cloned to `/tmp/nixos`: +Alternativ, falls das Repository bereits unter `/tmp/nixos` geklont wurde: ```bash nix --experimental-features "nix-command flakes" run /tmp/nixos#install -- -n ``` -> **Note:** The disk ID in `hosts//disks.sh` must match the hardware. -> Verify with `ls -la /dev/disk/by-id/`. +> **Hinweis:** Die Disk-ID in `hosts//disks.sh` muss zur Hardware passen. +> Pruefen mit `ls -la /dev/disk/by-id/`. -The script: -1. Clones the repository (when using `-r`) -2. Partitions the disk (via `disks.nix` or `disks.sh`) -3. Generates `hardware.nix` (if not present) -4. Installs NixOS +Das Script: +1. Klont das Repository (bei `-r`) +2. Partitioniert die Disk (via `disks.nix` oder `disks.sh`) +3. Generiert `hardware.nix` (falls nicht vorhanden) +4. Installiert NixOS ### 2.4 Reboot @@ -2522,293 +2356,226 @@ The script: reboot ``` -## Step 3: Configure SOPS Secrets +## Schritt 3: SOPS-Secrets konfigurieren -After the first boot, log in (password: `changeme`, change immediately with `passwd`). +Nach dem ersten Boot einloggen (Passwort: `changeme`, sofort aendern mit `passwd`). -### 3.1 Convert SSH Host Key to Age Key +### 3.1 SSH-Host-Key zu Age-Key konvertieren -On the **new server**: +Auf dem **neuen Server**: ```bash nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' ``` -Note the output (e.g. `age1abc123...`). +Ausgabe notieren (z.B. `age1abc123...`). -Alternatively, remotely: +Alternativ remote: ```bash nix-shell -p ssh-to-age --run 'ssh-keyscan -p 2299 -t ed25519 | ssh-to-age' ``` -### 3.2 Update .sops.yaml +### 3.2 .sops.yaml aktualisieren -On the **development machine**, add the new host key to `.sops.yaml`: +Auf dem **Entwicklungsrechner** den neuen Host-Key in `.sops.yaml` eintragen: ```yaml keys: - - &steffen_key age1e8p... # steffen (local) - - &hostname_key age1abc... # Key from step 3.1 + - &admin_key age1e8p... # Dein lokaler Admin-Key + - &hostname_key age1abc... # Key von Schritt 3.1 creation_rules: - path_regex: hosts//secrets.yaml$ key_groups: - age: - - *steffen_key + - *admin_key - *hostname_key ``` -### 3.3 Create Secrets +### 3.3 Secrets erstellen -Open the secrets file: +Secrets-Datei oeffnen: ```bash sops hosts//secrets.yaml ``` -The following table shows all secrets for **cryodev-main** and how they are generated: +Die folgende Tabelle zeigt alle Secrets fuer **cryodev-main** und wie sie generiert werden: -#### Immediately Available Secrets +#### Sofort erstellbare Secrets -These secrets have no dependencies and can be generated directly: +Diese Secrets haben keine Abhaengigkeiten und koennen direkt generiert werden: -| Secret | Command | -|--------|---------| +| Secret | Befehl | +|--------|--------| | `headplane/cookie_secret` | `openssl rand -hex 16` | -| `mailserver/accounts/admin` | `mkpasswd -sm bcrypt` (remember the password!) | -| `mailserver/accounts/forgejo` | `mkpasswd -sm bcrypt` (remember the password!) | -| `forgejo/mail-pw` | Plaintext password matching the bcrypt hash of `mailserver/accounts/forgejo` | +| `mailserver/accounts/admin` | `mkpasswd -sm bcrypt` (Passwort merken!) | +| `mailserver/accounts/forgejo` | `mkpasswd -sm bcrypt` (Passwort merken!) | +| `forgejo/mail-pw` | Klartext-Passwort das zum bcrypt-Hash von `mailserver/accounts/forgejo` passt | -#### Secrets That Require Running Services +#### Secrets die laufende Services brauchen -These secrets can only be created after step 4. **Do not add them yet** -- they will be added later. +Diese Secrets koennen erst erstellt werden, nachdem die entsprechenden Services laufen. Bis dahin **Platzhalter** eintragen (z.B. `placeholder`): -| Secret | Command | Prerequisite | -|--------|---------|--------------| -| `tailscale/auth-key` | See steps 4.1-4.2 | Headscale is running | -| `headplane/agent_pre_authkey` | See steps 4.1-4.2 | Headscale is running | -| `forgejo-runner/token` | Forgejo Admin Panel > Actions > Runners > Create Runner | Forgejo is running | +| Secret | Befehl | Voraussetzung | +|--------|--------|---------------| +| `tailscale/auth-key` | `sudo headscale preauthkeys create --expiration 99y --reusable --user default` | Headscale laeuft | +| `headplane/agent_pre_authkey` | `sudo headscale users create headplane-agent && sudo headscale preauthkeys create --expiration 99y --user headplane-agent` | Headscale laeuft | +| `forgejo-runner/token` | Forgejo Admin Panel > Actions > Runners > Create Runner | Forgejo laeuft | -#### Example secrets.yaml (Plaintext Before Encryption) +#### Beispiel secrets.yaml (Klartext vor Verschluesselung) ```yaml +tailscale: + auth-key: "placeholder" +forgejo-runner: + token: "placeholder" headplane: cookie_secret: "a1b2c3d4e5f6..." + agent_pre_authkey: "placeholder" mailserver: accounts: admin: "$2b$05$..." forgejo: "$2b$05$..." forgejo: - mail-pw: "the-plaintext-password" + mail-pw: "das-klartext-passwort" ``` -### 3.4 Gradually Re-enable Services -- Stage 1 +### 3.4 Services reaktivieren -> **Important:** Services that require Headscale or Forgejo secrets (Tailscale, -> Headplane, Forgejo Runner) must **not** be enabled yet, as these -> secrets can only be generated once those services are running. - -On the **development machine**, in `hosts//services/default.nix`, enable -the services **without external dependencies**: +Auf dem **Entwicklungsrechner** die in Schritt 1.4 auskommentierten Imports in `hosts//services/default.nix` wieder aktivieren: ```nix { imports = [ - # Stage 1: Services without external dependencies ./forgejo.nix + ./headplane.nix ./headscale.nix ./mailserver.nix ./netdata.nix ./nginx.nix ./openssh.nix ./sops.nix - - # Stage 2: Enable only after step 4 - # ./forgejo-runner.nix # requires: forgejo-runner/token (Forgejo) - # ./headplane.nix # requires: headplane/agent_pre_authkey (Headscale) - # ./tailscale.nix # requires: tailscale/auth-key (Headscale) + ./tailscale.nix ]; } ``` -### 3.5 Deploy (Stage 1) +Ebenso in `hosts//services/sops.nix` die Secrets-Definitionen wieder einkommentieren. + +### 3.5 Deployen ```bash -nix run .#deploy -- -n +nixos-rebuild switch --flake .# \ + --target-host @ --use-remote-sudo \ + --ssh-option="-p 2299" ``` -This uses the configuration from `deploy.json`. Alternatively, deploy manually: +## Schritt 4: Platzhalter-Secrets ersetzen -```bash -NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ - --target-host @ --sudo --ask-sudo-password -``` +Nachdem der Server mit Headscale und Forgejo laeuft, die Platzhalter durch echte Werte ersetzen: -After this deployment, Headscale, Forgejo, Mailserver, and Nginx are running. - -### 3.6 Create Forgejo Admin Account - -On first start, Forgejo has no users. Create an admin account via CLI -(on the **server**): - -```bash -forgejo admin user create \ - --username \ - --email @ \ - --password \ - --admin -``` - -> **Note:** The `forgejo` shell alias is provided by the module and automatically -> runs the command as the `forgejo` user with the correct config. -> If the alias is not available, start a new shell (`bash` or `zsh`). -> -> Since `DISABLE_REGISTRATION = true` is set, new accounts -> can only be created via CLI. - -## Step 4: Generate Remaining Secrets and Enable All Services - -After the server is running with Headscale and Forgejo: - -1. **Create Headscale users** (on the server): +1. **Headscale-User anlegen** (auf dem Server): ```bash sudo headscale users create default sudo headscale users create headplane-agent ``` -2. **Determine user IDs** (needed for the preauth keys): +2. **Preauth-Keys generieren**: ```bash - sudo headscale users list + # Fuer Tailscale + sudo headscale preauthkeys create --expiration 99y --reusable --user default + + # Fuer Headplane Agent + sudo headscale preauthkeys create --expiration 99y --user headplane-agent ``` - The output shows the numeric IDs (e.g. `1` for default, `2` for headplane-agent). - -3. **Generate preauth keys** (using the IDs from step 2): - - ```bash - # For Tailscale (use the user ID of "default") - sudo headscale preauthkeys create --expiration 99y --reusable --user - - # For Headplane Agent (use the user ID of "headplane-agent") - sudo headscale preauthkeys create --expiration 99y --user - ``` - -4. **Create the Forgejo Runner token** via the Forgejo Admin Panel: +3. **Forgejo-Runner-Token** ueber das Forgejo Admin Panel erstellen: Administration > Actions > Runners > Create new Runner -5. **Add the remaining secrets**: +4. **Secrets aktualisieren**: ```bash sops hosts//secrets.yaml + # Platzhalter durch echte Werte ersetzen ``` - Add the missing secrets: - - ```yaml - tailscale: - auth-key: "tskey-..." - forgejo-runner: - token: "..." - headplane: - agent_pre_authkey: "..." - ``` - -6. **Enable stage-2 services** in `hosts//services/default.nix`: - - ```nix - { - imports = [ - ./forgejo.nix - ./forgejo-runner.nix - ./headplane.nix - ./headscale.nix - ./mailserver.nix - ./netdata.nix - ./nginx.nix - ./openssh.nix - ./sops.nix - ./tailscale.nix - ]; - } - ``` - -6. **Deploy again**: +5. **Erneut deployen**: ```bash - nix run .#deploy -- -n + nixos-rebuild switch --flake .# \ + --target-host @ --use-remote-sudo \ + --ssh-option="-p 2299" ``` -## Next Steps +## Naechste Schritte -- [SOPS Reference](../services/sops.md) -- Detailed documentation on secret management -- [Creating an SD Image](sd-image.md) -- Install Raspberry Pi -- [Set Up CD](../deployment/cd.md) -- Automatic deployment +- [SOPS-Referenz](../services/sops.md) -- Detail-Dokumentation zur Secret-Verwaltung +- [SD-Image erstellen](sd-image.md) -- Raspberry Pi installieren +- [CD einrichten](../deployment/cd.md) -- Automatisches Deployment ================================================ FILE: docs/getting-started/new-client.md ================================================ -# Adding a New Raspberry Pi Client +# Neuen Raspberry Pi Client hinzufügen -This guide describes how to add a **new Raspberry Pi client** to the infrastructure. +Diese Anleitung beschreibt das Hinzufügen eines **neuen Raspberry Pi Clients** zur Infrastruktur. -## Overview: The Process +## Übersicht: Der Ablauf ``` -1. Create configuration ──► Copy template, customize +1. Konfiguration erstellen ──► Template kopieren, anpassen │ ▼ -2. Add to image pipeline ──► Extend workflow matrix +2. Zur Image-Pipeline hinzufügen ──► Workflow-Matrix erweitern │ ▼ -3. Push to main ──► Forgejo automatically builds SD image +3. Push auf main ──► Forgejo baut automatisch SD-Image │ ▼ -4. Flash image & boot ──► Write SD card, start Pi +4. Image flashen & booten ──► SD-Karte beschreiben, Pi starten │ ▼ -5. Configure SOPS ──► Retrieve age key, create secrets +5. SOPS konfigurieren ──► Age-Key holen, Secrets erstellen │ ▼ -6. Final deployment ──► Activate Tailscale etc. +6. Finales Deployment ──► Tailscale etc. aktivieren ``` -## Prerequisites +## Voraussetzungen -- SSH access to cryodev-main (for Tailscale auth key) -- Development machine with repository access -- SD card (at least 8 GB) +- SSH-Zugang zu cryodev-main (für Tailscale Auth-Key) +- Entwicklungsrechner mit Repository-Zugriff +- SD-Karte (mindestens 8 GB) --- -## Step 1: Generate Tailscale Auth Key +## Schritt 1: Tailscale Auth-Key generieren -**On cryodev-main** (via SSH): +**Auf cryodev-main** (per SSH): ```bash -# Determine user ID -sudo headscale users list -# Create preauth key (use user ID of "default") -sudo headscale preauthkeys create --expiration 99y --reusable --user +sudo headscale preauthkeys create --expiration 99y --reusable --user default ``` -**Take note of the output!** (e.g. `tskey-preauth-abc123...`) +**Ausgabe notieren!** (z.B. `tskey-preauth-abc123...`) --- -## Step 2: Create Host Configuration +## Schritt 2: Host-Konfiguration erstellen -### 2.1 Copy Template +### 2.1 Template kopieren ```bash cp -r templates/raspberry-pi hosts/neuer-pi ``` -### 2.2 Set Hostname +### 2.2 Hostname setzen `hosts/neuer-pi/networking.nix`: @@ -2818,58 +2585,58 @@ cp -r templates/raspberry-pi hosts/neuer-pi } ``` -### 2.3 Register in flake.nix +### 2.3 In flake.nix registrieren ```nix nixosConfigurations = { - # ... existing hosts ... + # ... bestehende Hosts ... neuer-pi = mkNixosConfiguration "aarch64-linux" [ ./hosts/neuer-pi ]; }; ``` -### 2.4 Add to constants.nix +### 2.4 In constants.nix eintragen ```nix { hosts = { - # ... existing hosts ... + # ... bestehende Hosts ... neuer-pi = { - ip = "100.64.0.X"; # Assigned by Headscale + ip = "100.64.0.X"; # Wird von Headscale vergeben }; }; } ``` -### 2.5 Create Placeholder secrets.yaml +### 2.5 Placeholder secrets.yaml erstellen ```bash touch hosts/neuer-pi/secrets.yaml ``` -### 2.6 Temporarily Disable SOPS +### 2.6 SOPS temporär deaktivieren -In `hosts/neuer-pi/default.nix`, comment out the `sops.secrets.*` references so the image can be built without secrets. +In `hosts/neuer-pi/default.nix` die `sops.secrets.*` Referenzen auskommentieren, damit das Image ohne Secrets gebaut werden kann. --- -## Step 3: Add to Image Pipeline +## Schritt 3: Zur Image-Pipeline hinzufügen -Edit `.forgejo/workflows/build-pi-image.yml`: +Bearbeite `.forgejo/workflows/build-pi-image.yml`: ```yaml jobs: build-pi-images: strategy: matrix: - # Add new host here: + # Neuen Host hier hinzufügen: host: [cryodev-pi, neuer-pi] ``` --- -## Step 4: Push and Build Image +## Schritt 4: Push und Image bauen lassen ```bash git add . @@ -2877,118 +2644,118 @@ git commit -m "Add neuer-pi host configuration" git push ``` -The Forgejo workflow will now automatically build an SD image for `neuer-pi`. +Der Forgejo Workflow baut jetzt automatisch ein SD-Image für `neuer-pi`. -**Wait** until the workflow completes (30-60 minutes). Check the status at: +**Warten** bis der Workflow fertig ist (30-60 Minuten). Status prüfen unter: `https://git.cryodev.xyz/steffen/cryodev-server/actions` --- -## Step 5: Flash Image +## Schritt 5: Image flashen -### 5.1 Download Image +### 5.1 Image herunterladen -After a successful build, find the image under **Releases**: +Nach erfolgreichem Build unter **Releases**: ```bash wget https://git.cryodev.xyz/steffen/cryodev-server/releases/latest/download/neuer-pi-sd-image.img.zst ``` -### 5.2 Decompress +### 5.2 Dekomprimieren ```bash zstd -d neuer-pi-sd-image.img.zst -o neuer-pi.img ``` -### 5.3 Write to SD Card +### 5.3 Auf SD-Karte schreiben -**Warning:** Replace `/dev/sdX` with the correct device! +**Achtung:** `/dev/sdX` durch das richtige Gerät ersetzen! ```bash -lsblk # Identify the correct device +lsblk # Richtiges Gerät finden sudo dd if=neuer-pi.img of=/dev/sdX bs=4M conv=fsync status=progress ``` -### 5.4 Boot +### 5.4 Booten -1. Insert the SD card into the Raspberry Pi -2. Connect Ethernet -3. Connect power -4. Wait until booted (approximately 2 minutes) +1. SD-Karte in den Raspberry Pi einlegen +2. Ethernet anschließen +3. Strom anschließen +4. Warten bis gebootet (ca. 2 Minuten) --- -## Step 6: Configure SOPS +## Schritt 6: SOPS konfigurieren -### 6.1 Find IP Address +### 6.1 IP-Adresse finden -The Pi should receive an IP address via DHCP. Check your router or scan the network: +Der Pi sollte per DHCP eine IP bekommen. Prüfe deinen Router oder scanne das Netzwerk: ```bash nmap -sn 192.168.1.0/24 | grep -B2 "Raspberry" ``` -### 6.2 Connect via SSH +### 6.2 SSH verbinden ```bash -ssh steffen@ # or the configured user +ssh steffen@ # oder der konfigurierte User ``` -For the default password, see `hosts/neuer-pi/users.nix`. +Standard-Passwort siehe `hosts/neuer-pi/users.nix`. -### 6.3 Determine Age Key +### 6.3 Age-Key ermitteln -On the Pi: +Auf dem Pi: ```bash nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' ``` -**Take note of the output!** (e.g. `age1xyz...`) +**Ausgabe notieren!** (z.B. `age1xyz...`) -### 6.4 Update .sops.yaml +### 6.4 .sops.yaml aktualisieren -On the development machine: +Auf dem Entwicklungsrechner: ```yaml keys: - - &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local) - - &neuer_pi_key age1xyz... # The new key + - &admin_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t + - &neuer_pi_key age1xyz... # Der neue Key creation_rules: - # ... existing rules ... - + # ... bestehende Regeln ... + - path_regex: hosts/neuer-pi/secrets.yaml$ key_groups: - age: - - *steffen_key - - *neuer_pi_key + - *admin_key + - *neuer_pi_key ``` -### 6.5 Create Secrets +### 6.5 Secrets erstellen ```bash sops hosts/neuer-pi/secrets.yaml ``` -Contents: +Inhalt: ```yaml tailscale: - auth-key: "tskey-preauth-abc123..." # Key from Step 1 + auth-key: "tskey-preauth-abc123..." # Key aus Schritt 1 netdata: stream: child-uuid: "..." # uuidgen ``` -### 6.6 Activate SOPS References +### 6.6 SOPS-Referenzen aktivieren -Re-enable the `sops.secrets.*` references that were commented out in Step 2.6. +Die in Schritt 2.6 auskommentierten `sops.secrets.*` Referenzen wieder aktivieren. --- -## Step 7: Final Deployment +## Schritt 7: Finales Deployment ```bash git add . @@ -2996,48 +2763,49 @@ git commit -m "Configure SOPS secrets for neuer-pi" git push ``` -Since Comin is running on the Pi, it will automatically pull the new configuration. +Da Comin auf dem Pi läuft, wird er die neue Konfiguration automatisch pullen. -Alternatively, deploy manually: +Alternativ manuell: ```bash -NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .#neuer-pi \ - --target-host @ --sudo --ask-sudo-password +nixos-rebuild switch --flake .#neuer-pi \ + --target-host @ --use-remote-sudo \ + --ssh-option="-p 2299" ``` --- -## Step 8: Verify +## Schritt 8: Verifizieren -### Tailscale Connection +### Tailscale-Verbindung ```bash -# On the Pi +# Auf dem Pi tailscale status -# On cryodev-main +# Auf cryodev-main sudo headscale nodes list ``` -### Netdata Streaming +### Netdata-Streaming -Check whether the new client appears in the Netdata dashboard: +Prüfe ob der neue Client im Netdata-Dashboard erscheint: `https://netdata.cryodev.xyz` --- -## Checklist +## Checkliste -- [ ] Tailscale auth key generated on cryodev-main -- [ ] Host configuration created (template, flake.nix, constants.nix) -- [ ] Host added to workflow matrix -- [ ] Pushed and waited for image build -- [ ] SD card flashed and Pi booted -- [ ] Age key determined and added to .sops.yaml -- [ ] secrets.yaml created (Tailscale key, Netdata UUID) -- [ ] SOPS references activated and deployed -- [ ] Tailscale connection working -- [ ] Netdata streaming working +- [ ] Tailscale Auth-Key auf cryodev-main generiert +- [ ] Host-Konfiguration erstellt (Template, flake.nix, constants.nix) +- [ ] Host zur Workflow-Matrix hinzugefügt +- [ ] Gepusht und auf Image-Build gewartet +- [ ] SD-Karte geflasht und Pi gebootet +- [ ] Age-Key ermittelt und in .sops.yaml eingetragen +- [ ] secrets.yaml erstellt (Tailscale-Key, Netdata-UUID) +- [ ] SOPS-Referenzen aktiviert und deployed +- [ ] Tailscale-Verbindung funktioniert +- [ ] Netdata-Streaming funktioniert @@ -3113,73 +2881,73 @@ nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel --dry- ================================================ FILE: docs/getting-started/reinstall.md ================================================ -# Reinstallation +# Neuinstallation (Reinstall) -This guide describes the **reinstallation** of an existing host, e.g. after a hardware change or in case of issues. +Diese Anleitung beschreibt die **Neuinstallation** eines bestehenden Hosts, z.B. nach Hardwarewechsel oder bei Problemen. -## Difference from Initial Installation +## Unterschied zur Erstinstallation -| Aspect | Initial Installation | Reinstallation | -|--------|----------------------|----------------| -| SOPS Secrets | Not yet present | Already configured | -| SSH Host Key | Newly generated | **Must be restored!** | -| Disk IDs | Newly determined | Often changed (new hardware) | -| secrets.yaml | Will be created | Already exists | +| Aspekt | Erstinstallation | Neuinstallation | +|--------|------------------|-----------------| +| SOPS-Secrets | Noch nicht vorhanden | Bereits konfiguriert | +| SSH-Host-Key | Neu generiert | **Muss wiederhergestellt werden!** | +| Disk-IDs | Neu ermitteln | Oft geändert (neue Hardware) | +| secrets.yaml | Wird erstellt | Bereits vorhanden | -## Important: SSH Host Key Issue +## Wichtig: SSH-Host-Key Problem -During a reinstallation, a **new SSH host key** is generated. This key will no longer match the age key in `.sops.yaml`! +Bei einer Neuinstallation wird ein **neuer SSH-Host-Key** generiert. Dieser stimmt nicht mehr mit dem Age-Key in `.sops.yaml` überein! -### Possible Solutions +### Lösungsmöglichkeiten -**Option A: Back up and restore the old host key** (recommended) +**Option A: Alten Host-Key sichern und wiederherstellen** (empfohlen) -**Option B: Generate a new key and update SOPS** +**Option B: Neuen Key generieren und SOPS aktualisieren** -## Prerequisites +## Voraussetzungen -- Backup of the old SSH host key (if using Option A) -- Access to `.sops.yaml` and the admin age keys -- Bootable NixOS ISO +- Backup des alten SSH-Host-Keys (falls Option A) +- Zugriff auf `.sops.yaml` und die Admin-Age-Keys +- Bootbares NixOS ISO -## Step 1: Preparation (before the installation) +## Schritt 1: Vorbereitung (vor der Installation) -### 1.1 Back Up the Old SSH Host Key (Option A) +### 1.1 Alten SSH-Host-Key sichern (Option A) -If the old host is still running: +Falls der alte Host noch läuft: ```bash -# On the old host +# Auf dem alten Host sudo cat /etc/ssh/ssh_host_ed25519_key > ~/ssh_host_ed25519_key.backup sudo cat /etc/ssh/ssh_host_ed25519_key.pub > ~/ssh_host_ed25519_key.pub.backup ``` -Copy the files securely to the development machine. +Dateien sicher auf den Entwicklungsrechner kopieren. -### 1.2 Determine Disk IDs +### 1.2 Disk-IDs ermitteln -**With new hardware**, the disk IDs will change! +**Bei neuer Hardware** ändern sich die Disk-IDs! ```bash -# In the NixOS live system +# Im NixOS Live-System lsblk -o NAME,SIZE,MODEL,SERIAL ls -la /dev/disk/by-id/ ``` -Enter the new disk ID in `hosts//disks.sh` or `disks.nix`: +Die neue Disk-ID in `hosts//disks.sh` oder `disks.nix` eintragen: ```bash -# Example disks.sh +# Beispiel disks.sh DISK="/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_XXXXX" ``` -## Step 2: Perform the Installation +## Schritt 2: Installation durchführen -### 2.1 Boot the NixOS ISO +### 2.1 NixOS ISO booten -Boot from USB/CD, set a root password, and connect via SSH. +Von USB/CD booten, Root-Passwort setzen, per SSH verbinden. -### 2.2 Clone the Repository +### 2.2 Repository klonen ```bash sudo -i @@ -3188,30 +2956,30 @@ git clone /tmp/nixos cd /tmp/nixos ``` -### 2.3 Verify the Disk Configuration +### 2.3 Disk-Konfiguration prüfen ```bash -# Display current disk IDs +# Aktuelle Disk-IDs anzeigen ls -la /dev/disk/by-id/ -# Compare with the configuration +# Mit Konfiguration vergleichen cat hosts//disks.sh | grep DISK ``` -**If necessary:** Update the disk ID in the configuration. +**Falls nötig:** Disk-ID in der Konfiguration anpassen. -### 2.4 Run the Install Script +### 2.4 Install-Script ausführen ```bash bash scripts/install.sh -n ``` -### 2.5 Restore the SSH Host Key (Option A) +### 2.5 SSH-Host-Key wiederherstellen (Option A) -**Before rebooting!** +**Vor dem Reboot!** ```bash -# Restore the host key from backup +# Host-Key vom Backup wiederherstellen cp /path/to/ssh_host_ed25519_key.backup /mnt/etc/ssh/ssh_host_ed25519_key cp /path/to/ssh_host_ed25519_key.pub.backup /mnt/etc/ssh/ssh_host_ed25519_key.pub chmod 600 /mnt/etc/ssh/ssh_host_ed25519_key @@ -3225,176 +2993,177 @@ umount -Rl /mnt reboot ``` -## Step 3: After the Reboot +## Schritt 3: Nach dem Reboot -### Option A (Key Restored) +### Bei Option A (Key wiederhergestellt) -SOPS secrets should work automatically. Verify: +SOPS-Secrets sollten automatisch funktionieren. Testen: ```bash sudo cat /run/secrets/tailscale/auth-key ``` -### Option B (New Key) +### Bei Option B (Neuer Key) -The host cannot decrypt the secrets. Configure the new key: +Der Host kann die Secrets nicht entschlüsseln. Neuen Key konfigurieren: ```bash -# Determine the new age key +# Neuen Age-Key ermitteln nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' ``` -On the development machine: +Auf dem Entwicklungsrechner: ```bash -# Update .sops.yaml with the new key +# .sops.yaml aktualisieren mit neuem Key vim .sops.yaml -# Re-encrypt secrets with the new key +# Secrets mit neuem Key neu verschlüsseln sops updatekeys hosts//secrets.yaml ``` -Then redeploy the configuration: +Dann Konfiguration neu deployen: ```bash -NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ - --target-host @ --sudo --ask-sudo-password +nixos-rebuild switch --flake .# \ + --target-host @ --use-remote-sudo \ + --ssh-option="-p 2299" ``` -## Common Issues +## Häufige Probleme ### "No secret key available" -SOPS cannot decrypt the secrets. Cause: -- SSH host key does not match the age key in `.sops.yaml` +SOPS kann die Secrets nicht entschlüsseln. Ursache: +- SSH-Host-Key stimmt nicht mit Age-Key in `.sops.yaml` überein -Solution: Follow Option B (configure the new key). +Lösung: Option B durchführen (neuen Key konfigurieren). -### "Device not found" during partitioning +### "Device not found" beim Partitionieren -The disk ID in `disks.sh`/`disks.nix` is incorrect. +Disk-ID in `disks.sh`/`disks.nix` ist falsch. ```bash -# Find the correct ID +# Richtige ID finden ls -la /dev/disk/by-id/ ``` -### Outdated Hardware Config +### Hardware-Config veraltet -With new hardware, `hardware.nix` must be regenerated: +Bei neuer Hardware muss `hardware.nix` neu generiert werden: ```bash -# The install script regenerates automatically if the file is missing +# Install-Script generiert automatisch neu, falls Datei fehlt rm hosts//hardware.nix bash scripts/install.sh -n ``` -## Checklist +## Checkliste -- [ ] Old SSH host key backed up (if possible) -- [ ] Disk IDs in configuration verified/updated -- [ ] Installation completed -- [ ] SSH host key restored OR new key configured in SOPS -- [ ] Secrets are functional (`sudo cat /run/secrets/...`) -- [ ] Tailscale connected (`tailscale status`) +- [ ] Alten SSH-Host-Key gesichert (falls möglich) +- [ ] Disk-IDs in Konfiguration geprüft/aktualisiert +- [ ] Installation durchgeführt +- [ ] SSH-Host-Key wiederhergestellt ODER neuen Key in SOPS konfiguriert +- [ ] Secrets funktionieren (`sudo cat /run/secrets/...`) +- [ ] Tailscale verbunden (`tailscale status`) ================================================ FILE: docs/getting-started/sd-image.md ================================================ -# SD Card Images for Raspberry Pi +# SD-Karten-Images für Raspberry Pi -The repository automatically builds SD card images for all configured Raspberry Pi hosts. +Das Repository baut automatisch SD-Karten-Images für alle konfigurierten Raspberry Pi Hosts. -## Automatic Build +## Automatischer Build -When changes are pushed to `main`, images are automatically built for all Pi hosts and published as a release. +Bei Änderungen an `main` werden automatisch Images für alle Pi-Hosts gebaut und als Release veröffentlicht. -**Download:** [Releases on Forgejo](https://git.cryodev.xyz/steffen/cryodev-server/releases) +**Download:** [Releases auf Forgejo](https://git.cryodev.xyz/steffen/cryodev-server/releases) -## Available Images +## Verfügbare Images -| Host | Image Name | +| Host | Image-Name | |------|------------| | `cryodev-pi` | `cryodev-pi-sd-image.img.zst` | -New hosts are built automatically once they are added to the workflow matrix. +Neue Hosts werden automatisch gebaut, wenn sie zur Workflow-Matrix hinzugefügt werden. -## Flashing the Image +## Image flashen -### 1. Download +### 1. Herunterladen ```bash wget https://git.cryodev.xyz/.../releases/latest/download/-sd-image.img.zst wget https://git.cryodev.xyz/.../releases/latest/download/-sd-image.img.zst.sha256 -# Verify checksum +# Checksum prüfen sha256sum -c -sd-image.img.zst.sha256 ``` -### 2. Decompress +### 2. Dekomprimieren ```bash zstd -d -sd-image.img.zst -o .img ``` -### 3. Write to SD Card +### 3. Auf SD-Karte schreiben ```bash -# Identify the correct device +# Richtiges Gerät finden lsblk -# Write (WARNING: make sure to select the correct device!) +# Schreiben (ACHTUNG: richtiges Gerät wählen!) sudo dd if=.img of=/dev/sdX bs=4M conv=fsync status=progress ``` -Alternatively, use `balenaEtcher` or `Raspberry Pi Imager`. +Alternativ: `balenaEtcher` oder `Raspberry Pi Imager` verwenden. -## What Is Included in the Image? +## Was ist im Image? -- Complete NixOS installation for the specific host -- All configured services (except secrets) -- SSH server enabled -- Automatic root partition expansion on first boot -- Comin for automatic updates +- Vollständige NixOS-Installation für den spezifischen Host +- Alle konfigurierten Services (außer Secrets) +- SSH-Server aktiviert +- Automatische Root-Partition-Erweiterung beim ersten Boot +- Comin für automatische Updates -## What Is Missing? +## Was fehlt? -**SOPS secrets** cannot be included in the image (chicken-and-egg problem with the SSH host key). +**SOPS-Secrets** können nicht im Image enthalten sein (Henne-Ei-Problem mit SSH-Host-Key). -After the first boot: -1. Retrieve the age key from the Pi -2. Update `.sops.yaml` -3. Create `secrets.yaml` -4. Deploy the configuration +Nach dem ersten Boot: +1. Age-Key vom Pi holen +2. `.sops.yaml` aktualisieren +3. `secrets.yaml` erstellen +4. Konfiguration deployen -See [Adding a New Client](new-client.md) for the complete guide. +Siehe [Neuen Client hinzufügen](new-client.md) für die vollständige Anleitung. -## Adding a New Host to the Pipeline +## Neuen Host zur Pipeline hinzufügen -1. Create the host configuration in `hosts//` -2. Add it to the matrix in `.forgejo/workflows/build-pi-image.yml`: +1. Host-Konfiguration in `hosts//` erstellen +2. In `.forgejo/workflows/build-pi-image.yml` zur Matrix hinzufügen: ```yaml matrix: - host: [cryodev-pi, new-host] # <- add here + host: [cryodev-pi, neuer-host] # <- hier hinzufügen ``` -3. Push to `main` -- the image will be built automatically +3. Push auf `main` → Image wird automatisch gebaut -## Building Manually +## Manuell bauen ```bash -# On aarch64 (e.g., another Pi) +# Auf aarch64 (z.B. anderem Pi) nix build .#nixosConfigurations..config.system.build.sdImage -# On x86_64 with QEMU emulation (slow) +# Auf x86_64 mit QEMU-Emulation (langsam) nix build .#nixosConfigurations..config.system.build.sdImage \ --extra-platforms aarch64-linux ``` -Prerequisite on x86_64: +Voraussetzung auf x86_64: ```nix { @@ -3404,21 +3173,21 @@ Prerequisite on x86_64: ## Troubleshooting -### Workflow Fails +### Workflow schlägt fehl -- Check whether `sd-image.nix` is imported in the host configuration -- Check whether binfmt is enabled on cryodev-main +- Prüfe ob `sd-image.nix` in der Host-Konfiguration importiert wird +- Prüfe ob binfmt auf cryodev-main aktiviert ist -### Image Does Not Boot +### Image bootet nicht -- Was the SD card written correctly? -- Try a different SD card -- Check the power supply (minimum 3A for Pi 4) +- SD-Karte korrekt beschrieben? +- Andere SD-Karte versuchen +- Stromversorgung prüfen (min. 3A für Pi 4) -### No Network +### Kein Netzwerk -- Check the Ethernet cable -- Is there a DHCP server on the network? +- Ethernet-Kabel prüfen +- DHCP-Server im Netzwerk? @@ -3502,23 +3271,44 @@ forgejo-runner: ## CI/CD Workflows -CI runs on every push to `main` via Forgejo Actions: +### deploy-rs Workflow -1. **flake-check** -- validates the flake -2. **build-hosts** -- builds all host configurations +`.forgejo/workflows/deploy.yaml`: -Deployment is handled by **Comin** (pull-based), not by CI. -See [CD documentation](../deployment/cd.md) for details. +```yaml +name: Deploy +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v24 + + - name: Deploy + env: + SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + run: | + mkdir -p ~/.ssh + echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + nix run .#deploy +``` ## Administration ### Create Admin User ```bash -forgejo admin user create \ - --username \ - --email @ \ - --password \ +sudo -u forgejo forgejo admin user create \ + --username admin \ + --password changeme \ + --email admin@cryodev.xyz \ --admin ``` @@ -3586,10 +3376,9 @@ nix-shell -p openssl --run 'openssl rand -hex 16' ```bash # First, create a dedicated user sudo headscale users create headplane-agent -# Find the user ID -sudo headscale users list -# Then create a reusable pre-auth key (use the ID of headplane-agent) -sudo headscale preauthkeys create --expiration 99y --reusable --user + +# Then create a reusable pre-auth key +sudo headscale preauthkeys create --expiration 99y --reusable --user headplane-agent ``` ### Add to Secrets @@ -3663,7 +3452,7 @@ sudo journalctl -u headplane -f Verify the agent pre-auth key is valid: ```bash -sudo headscale preauthkeys list --user +sudo headscale preauthkeys list --user headplane-agent ``` If expired, create a new one and update the secrets file. @@ -4168,7 +3957,7 @@ Add the host key to `.sops.yaml`: ```yaml keys: - - &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local) + - &admin_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t - &main_key age1... # cryodev-main - &pi_key age1... # cryodev-pi @@ -4176,14 +3965,14 @@ creation_rules: - path_regex: hosts/cryodev-main/secrets.yaml$ key_groups: - age: - - *steffen_key - - *main_key - + - *admin_key + - *main_key + - path_regex: hosts/cryodev-pi/secrets.yaml$ key_groups: - age: - - *steffen_key - - *pi_key + - *admin_key + - *pi_key ``` ### 3. Create Secrets File @@ -4290,7 +4079,7 @@ netdata: | Mailserver password | `nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'` | | Random hex token | `nix-shell -p openssl --run 'openssl rand -hex 16'` | | UUID | `uuidgen` | -| Tailscale preauth | `sudo headscale preauthkeys create --expiration 99y --reusable --user ` | +| Tailscale preauth | `sudo headscale preauthkeys create --expiration 99y --reusable --user default` | ## Updating Keys @@ -4331,10 +4120,7 @@ Tailscale clients connect to the self-hosted Headscale server to join the mesh V On the Headscale server (cryodev-main): ```bash -# Look up user ID -sudo headscale users list -# Create preauth key (use the user ID for "default") -sudo headscale preauthkeys create --expiration 99y --reusable --user +sudo headscale preauthkeys create --expiration 99y --reusable --user default ``` ### Add to Secrets @@ -4431,7 +4217,7 @@ Check the auth key is valid: ```bash # On Headscale server -sudo headscale preauthkeys list --user +sudo headscale preauthkeys list --user default ``` Verify the login server URL is correct in the client configuration. @@ -4645,12 +4431,12 @@ FILE: hosts/cryodev-main/packages.nix FILE: hosts/cryodev-main/secrets.yaml ================================================ tailscale: - auth-key: ENC[AES256_GCM,data:v5C3DqYJsDKq6oUa/3G6WKxyKeIK4EJLNxWMbKjSbwe5MPtS4sZjFszMviKcEVGW,iv:4G8irABGuVhOYnK15EjbpNQ4B9VY/NdwCrfz+YAMzvA=,tag:0Vhq/TJgx+48frRy30yKFg==,type:str] + auth-key: ENC[AES256_GCM,data:APMZrLYEqywYTmc=,iv:KiFwgR3UXLXCdl9DlR5tJOr8XUyQEeDomPx9hOREhnw=,tag:32quLtu74EIxAgmjH3hvIw==,type:str] forgejo-runner: - token: ENC[AES256_GCM,data:sdnJcyRiTLxXoZDNbEzJAjpiK+iSUH0gV0XwbEQf94IE/6IZz5/zHw==,iv:py+qqp3VAwBGEpYiQwft3jnQS943JaBlrcckColv4f8=,tag:rtmRwW8rpXB6Pv+LSkp+Fw==,type:str] + token: ENC[AES256_GCM,data:/i9KVMeEXYwQnn0=,iv:pILMNbhDviifDUFRINi6n9dtGSAeqxKMdBgjYwtXXEM=,tag:JCj5v5BZdZteo0MdTVKREw==,type:str] headplane: cookie_secret: ENC[AES256_GCM,data:HICF31i6yCLZGNeOFYTR3Bp0a7i0UKOvGAvx/pD3NB4=,iv:ZtK8r1YUWnf5Af0Ls341k0w1mZm+D5Rb0E1uS5z/Gdo=,tag:vwM9+4dpcmnjn/wR6Ty/MQ==,type:str] - agent_pre_authkey: ENC[AES256_GCM,data:QvhPi2lhyP7w6HTeOSS8660NzIY9Q6AOhlOGQXnvz+qYu9vOAMQPOFMZfie5+e8g,iv:X60wVOEUIsTiMHrrd4lId0VpR7VfFDr74p8RGka3+18=,tag:kIvaHrOWIM+VQ+Qz1GiheQ==,type:str] + agent_pre_authkey: ENC[AES256_GCM,data:aYkPZTR4fwArcKQ=,iv:+OhbIpwsyCJ4i4k8eyCKYAHE25F4iUHfdM+CG0+BQd8=,tag:BkT73WPjOv5Lu6dCFBXxWg==,type:str] mailserver: accounts: admin: ENC[AES256_GCM,data:gY2k3x3sA98yGNLcSWUr9aC0566MJM2UXhwLtWPUL3PRvxQt0XOzjeiC7ddgbqTAol4dBNeaV0zbFInD,iv:rxp0M9kHMgD73K+RDC562sUpXaJ067eU1CeciAke+LM=,tag:VKobduo/ZULAk17M9LD3bw==,type:str] @@ -4677,8 +4463,8 @@ sops: MEpGbGlQbVRsM1NxN1JxY2J1MVNTTE0KuIvuM2c1VIXKv0LGLb0NwqtSyBYcRcb1 uiIjNV0UzEt/WvnCeUTMPgIXBHk6jWcaKe13v6MHeha+/CVZ9Su/Lw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-03-14T11:38:57Z" - mac: ENC[AES256_GCM,data:gmxyp3XaHeU/CT2lgo14wIbJsKs/JrZmUPhgHwo1XRN5Sf/Su6lHOpVlQS1M6R3+ZlBnS/oEur+y0gydCCqhJK1C3Y5YuUfPlOWOeQWMVxQBqxWkyemvz5KgGseDc9nG09FpoGEYa4sSeuD1J6vRsGcZiOStaA6s8NICWivdWcQ=,iv:cYILLrScr7cFiLx5INbc9z3BT7LaCjLnCH0wdn3lZ1k=,tag:IIRb/Tu8YqWNiHXH7CSOfQ==,type:str] + lastmodified: "2026-03-14T10:28:25Z" + mac: ENC[AES256_GCM,data:oeT8I9gMIAPnm8wlNUFjn/0UT6qfTA//fLp3USO33FMsNIOWmqt3kB4NsozS+n6ZeMxBVWQZPss8t819DYqv0xQarzfOqQe1idCGCB+7NBFcFP2VLFzkIH+9Wei9AJSlR3BRnzyVaQDi797P6pEXFn/IoQWPWZ8sX8ZKugOfY0w=,iv:RjsKhPcVZBHHLs1W3PDhcseGLV4eawafg0is6KrzhtE=,tag:ifkobUteslEZ78OvkZw8JQ==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0 @@ -4693,51 +4479,17 @@ FILE: hosts/cryodev-main/users.nix imports = [ outputs.nixosModules.normalUsers ../../users/steffen - ../../users/ralph - ../../users/benjamin ]; } -================================================ -FILE: hosts/cryodev-main/services/comin.nix -================================================ -{ - config, - pkgs, - outputs, - constants, - ... -}: - -{ - imports = [ - outputs.nixosModules.comin - ]; - - services.comin = { - enable = true; - remotes = [ - { - name = "origin"; - url = "https://${constants.services.forgejo.fqdn}/steffen/cryodev.git"; - branches.main.name = "main"; - } - ]; - }; -} - - - ================================================ FILE: hosts/cryodev-main/services/default.nix ================================================ { imports = [ - ./comin.nix ./forgejo.nix - ./forgejo-runner.nix ./headplane.nix ./headscale.nix ./mailserver.nix @@ -4751,40 +4503,6 @@ FILE: hosts/cryodev-main/services/default.nix -================================================ -FILE: hosts/cryodev-main/services/forgejo-runner.nix -================================================ -{ - config, - outputs, - constants, - ... -}: - -{ - imports = [ - outputs.nixosModules.forgejo-runner - ]; - - services.forgejo-runner = { - enable = true; - url = "http://127.0.0.1:${toString constants.services.forgejo.port}"; - tokenFile = config.sops.templates."forgejo-runner-token".path; - }; - - sops.secrets."forgejo-runner/token" = { - mode = "0400"; - }; - - sops.templates."forgejo-runner-token" = { - content = '' - TOKEN=${config.sops.placeholder."forgejo-runner/token"} - ''; - }; -} - - - ================================================ FILE: hosts/cryodev-main/services/forgejo.nix ================================================ @@ -4798,6 +4516,7 @@ FILE: hosts/cryodev-main/services/forgejo.nix { imports = [ outputs.nixosModules.forgejo + outputs.nixosModules.forgejo-runner ]; services.forgejo = { @@ -4821,6 +4540,17 @@ FILE: hosts/cryodev-main/services/forgejo.nix }; }; + services.forgejo-runner = { + enable = true; + url = "https://${constants.services.forgejo.fqdn}"; + tokenFile = config.sops.secrets."forgejo-runner/token".path; + }; + + sops.secrets."forgejo-runner/token" = { + # gitea-runner user is created by gitea-actions-runner service + mode = "0400"; + }; + services.nginx.virtualHosts."${constants.services.forgejo.fqdn}" = { forceSSL = true; enableACME = true; @@ -4853,7 +4583,6 @@ FILE: hosts/cryodev-main/services/headplane.nix headscale = { url = "http://127.0.0.1:${toString constants.services.headscale.port}"; public_url = "https://${constants.services.headscale.fqdn}"; - config_strict = false; }; }; }; @@ -5044,10 +4773,10 @@ FILE: hosts/cryodev-main/services/sops.nix sops = { defaultSopsFile = ../secrets.yaml; # age.keyFile is not set, sops-nix defaults to using /etc/ssh/ssh_host_ed25519_key - - # Secrets fuer Stufe-2-Services werden in deren eigenen Dateien definiert: - # forgejo-runner/token -> forgejo-runner.nix - # tailscale/auth-key -> tailscale.nix (via Modul) + secrets = { + "forgejo-runner/token" = { }; + "tailscale/auth-key" = { }; + }; }; } @@ -5210,15 +4939,11 @@ FILE: hosts/cryodev-pi/hardware.nix { boot = { kernelPackages = pkgs.linuxKernel.packages.linux_rpi4; - initrd = { - availableKernelModules = [ - "xhci_pci" - "usbhid" - "usb_storage" - ]; - # Disable default x86 modules that don't exist in the Pi kernel (e.g. dw-hdmi) - includeDefaultModules = false; - }; + initrd.availableKernelModules = [ + "xhci_pci" + "usbhid" + "usb_storage" + ]; }; fileSystems = { @@ -5288,14 +5013,6 @@ FILE: hosts/cryodev-pi/sd-image.nix "vfat" "ext4" ]; - - # sd-image.nix imports all-hardware.nix which adds x86 modules like dw-hdmi - # that don't exist in the RPi4 kernel. Filter them out. - boot.initrd.availableKernelModules = lib.mkForce [ - "xhci_pci" - "usbhid" - "usb_storage" - ]; } @@ -5326,6 +5043,7 @@ FILE: hosts/cryodev-pi/users.nix imports = [ outputs.nixosModules.normalUsers ../../users/steffen + ../../users/cryotherm ]; } @@ -5352,7 +5070,7 @@ FILE: hosts/cryodev-pi/services/comin.nix remotes = [ { name = "origin"; - url = "https://${constants.services.forgejo.fqdn}/steffen/cryodev.git"; + url = "https://${constants.services.forgejo.fqdn}/steffen/cryodev-server.git"; branches.main.name = "main"; } ]; @@ -6176,6 +5894,7 @@ in nix nodejs openssh + deploy-rs ]; settings = { @@ -6231,14 +5950,6 @@ in config = mkIf cfg.enable { nixpkgs.overlays = [ inputs.headplane.overlays.default - # Fix upstream pnpm-deps hash mismatch (https://github.com/tale/headplane) - (final: prev: { - headplane = prev.headplane.overrideAttrs (old: { - pnpmDeps = old.pnpmDeps.overrideAttrs { - outputHash = "sha256-lk/ezsrW6JHh5nXPSstqHUbaMTeOARBGZcBSoG1S5ns="; - }; - }); - }) ]; services.headplane = { @@ -6600,7 +6311,6 @@ in acceptTerms = true; defaults.email = mkDefault "postmaster@${config.networking.domain}"; defaults.webroot = mkDefault "/var/lib/acme/acme-challenge"; - defaults.group = mkDefault "nginx"; }; security.dhparams = mkIf cfg.forceSSL { @@ -7187,6 +6897,8 @@ FILE: modules/nixos/nixvim/plugins/default.nix ]; config.programs.nixvim.plugins = { + markdown-preview.enable = lib.mkDefault true; + # warning: Nixvim: `plugins.web-devicons` was enabled automatically because the following plugins are enabled. This behaviour is deprecated. Please explicitly define `plugins.web-devicons.enable` web-devicons.enable = true; }; } @@ -7871,11 +7583,12 @@ fi FILE: templates/generic-server/boot.nix ================================================ { - boot.loader.systemd-boot = { - enable = true; - configurationLimit = 10; + boot = { + loader = { + grub.enable = false; + generic-extlinux-compatible.enable = true; + }; }; - boot.loader.efi.canTouchEfiVariables = true; } @@ -7989,49 +7702,28 @@ FILE: templates/generic-server/flake.nix ================================================ FILE: templates/generic-server/hardware.nix ================================================ -{ - config, - lib, - pkgs, - modulesPath, - ... -}: +{ pkgs, lib, ... }: { - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot.initrd.availableKernelModules = [ - "ahci" - "nvme" - "sd_mod" - "usb_storage" - "xhci_pci" - ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ ]; - boot.extraModulePackages = [ ]; - - fileSystems."/" = { - device = "/dev/disk/by-label/ROOT"; - fsType = "ext4"; - }; - - fileSystems."/boot" = { - device = "/dev/disk/by-label/BOOT"; - fsType = "vfat"; - options = [ - "fmask=0022" - "dmask=0022" + boot = { + kernelPackages = pkgs.linuxKernel.packages.linux_rpi4; + initrd.availableKernelModules = [ + "xhci_pci" + "usbhid" + "usb_storage" ]; }; - swapDevices = [ { device = "/dev/disk/by-label/SWAP"; } ]; + fileSystems = { + "/" = { + device = "/dev/disk/by-label/NIXOS_SD"; + fsType = "ext4"; + options = [ "noatime" ]; + }; + }; - networking.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; + hardware.enableRedistributableFirmware = true; } @@ -8040,7 +7732,7 @@ FILE: templates/generic-server/hardware.nix FILE: templates/generic-server/networking.nix ================================================ { - networking.hostName = "HOSTNAME"; + networking.hostName = "cryodev-pi"; networking.domain = "cryodev.xyz"; } @@ -8065,8 +7757,8 @@ FILE: templates/generic-server/users.nix { imports = [ outputs.nixosModules.normalUsers - # Add users here, e.g.: - # ../../users/ + ../../users/steffen + ../../users/cryotherm ]; } @@ -8354,15 +8046,11 @@ FILE: templates/raspberry-pi/hardware.nix { boot = { kernelPackages = pkgs.linuxKernel.packages.linux_rpi4; - initrd = { - availableKernelModules = [ - "xhci_pci" - "usbhid" - "usb_storage" - ]; - # Disable default x86 modules that don't exist in the Pi kernel (e.g. dw-hdmi) - includeDefaultModules = false; - }; + initrd.availableKernelModules = [ + "xhci_pci" + "usbhid" + "usb_storage" + ]; }; fileSystems = { @@ -8383,7 +8071,7 @@ FILE: templates/raspberry-pi/hardware.nix FILE: templates/raspberry-pi/networking.nix ================================================ { - networking.hostName = "HOSTNAME"; + networking.hostName = "cryodev-pi"; networking.domain = "cryodev.xyz"; } @@ -8408,8 +8096,8 @@ FILE: templates/raspberry-pi/users.nix { imports = [ outputs.nixosModules.normalUsers - # Add users here, e.g.: - # ../../users/ + ../../users/steffen + ../../users/cryotherm ]; } @@ -8570,34 +8258,13 @@ FILE: templates/raspberry-pi/services/tailscale.nix ================================================ -FILE: users/benjamin/default.nix +FILE: users/cryotherm/default.nix ================================================ { - normalUsers.benjamin = { - extraGroups = [ - "wheel" - ]; - sshKeyFiles = [ - # TODO: Add benjamin's public key - # ./pubkeys/benjamin.pub - ]; - }; -} - - - -================================================ -FILE: users/ralph/default.nix -================================================ -{ - normalUsers.ralph = { - extraGroups = [ - "wheel" - ]; - sshKeyFiles = [ - # TODO: Add ralph's public key - # ./pubkeys/ralph.pub - ]; + normalUsers.cryotherm = { + extraGroups = [ ]; + # No sshKeyFiles, so password login only (if allowed) or local access + sshKeyFiles = [ ]; }; } @@ -8627,77 +8294,62 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKNTpsF9Z313gWHiHi4SvjeXI4Mh80mtq0bR0AjsZr ================================================ -FILE: .forgejo/workflows/ci.yml +FILE: .forgejo/workflows/build-hosts.yml ================================================ -name: CI +name: Build hosts -on: [pull_request] +on: + pull_request: + branches: + - main jobs: - flake-check: - runs-on: host - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Run flake check - run: nix flake check --impure - build-hosts: - needs: flake-check - runs-on: host + runs-on: docker steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Build cryodev-main run: nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel --impure - name: Build cryodev-pi - run: nix build .#nixosConfigurations.cryodev-pi.config.system.build.toplevel --impure --extra-platforms aarch64-linux + run: nix build .#nixosConfigurations.cryodev-pi.config.system.build.toplevel --impure ================================================ -FILE: .forgejo/workflows/deploy.yml +FILE: .forgejo/workflows/build-pi-image.yml ================================================ -name: Deploy +name: Build Raspberry Pi SD Images on: push: branches: - main + paths: + - 'hosts/**' + - 'modules/**' + - 'templates/**' + - 'flake.nix' + - 'flake.lock' + - 'constants.nix' + workflow_dispatch: jobs: - flake-check: - runs-on: host - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Run flake check - run: nix flake check --impure - - build-hosts: - needs: flake-check - runs-on: host - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Build cryodev-main - run: nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel --impure - - - name: Build cryodev-pi - run: nix build .#nixosConfigurations.cryodev-pi.config.system.build.toplevel --impure --extra-platforms aarch64-linux - build-pi-images: - needs: build-hosts runs-on: host strategy: matrix: + # Add new Pi hosts to this list when created host: [cryodev-pi] fail-fast: false + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -8705,19 +8357,21 @@ jobs: - name: Build SD image for ${{ matrix.host }} run: | echo "Building SD image for: ${{ matrix.host }}" + echo "This may take 30-60 minutes with emulation..." + nix build .#nixosConfigurations.${{ matrix.host }}.config.system.build.sdImage \ --extra-platforms aarch64-linux \ --out-link result-${{ matrix.host }} - + IMAGE_PATH=$(find result-${{ matrix.host }} -name "*.img.zst" -type f | head -1) if [ -z "$IMAGE_PATH" ]; then echo "Error: No image found!" exit 1 fi - + cp "$IMAGE_PATH" ./${{ matrix.host }}-sd-image.img.zst sha256sum ${{ matrix.host }}-sd-image.img.zst > ${{ matrix.host }}-sd-image.img.zst.sha256 - + echo "Image size:" ls -lh ${{ matrix.host }}-sd-image.img.zst @@ -8746,17 +8400,19 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | VERSION="v$(date +%Y-%m-%d)-$(git rev-parse --short HEAD)" - + + # Create release via API curl -s -X POST \ -H "Authorization: token ${GITHUB_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"tag_name\": \"${VERSION}\", \"name\": \"Pi Images ${VERSION}\", \"body\": \"Raspberry Pi SD card images. See docs for usage.\", \"draft\": false, \"prerelease\": false}" \ "https://git.cryodev.xyz/api/v1/repos/${GITHUB_REPOSITORY}/releases" \ -o release.json - + RELEASE_ID=$(jq -r '.id' release.json) echo "Release ID: $RELEASE_ID" - + + # Upload all files for file in $(find artifacts -type f); do echo "Uploading: $(basename $file)" curl -s -X POST \ @@ -8765,7 +8421,71 @@ jobs: --data-binary @"$file" \ "https://git.cryodev.xyz/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=$(basename $file)" done - + echo "Done: https://git.cryodev.xyz/${GITHUB_REPOSITORY}/releases/tag/${VERSION}" + +================================================ +FILE: .forgejo/workflows/deploy-main.yml +================================================ +name: Deploy cryodev-main + +on: + push: + branches: + - main + +jobs: + deploy-cryodev-main: + runs-on: docker + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:nixos-unstable + + - name: Set up SSH + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }} + run: | + mkdir -p ~/.ssh + echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + + # Add host key (replace with actual host key or use ssh-keyscan in unsafe environments) + ssh-keyscan -H cryodev.xyz >> ~/.ssh/known_hosts + + - name: Deploy with deploy-rs + run: | + # Deploy using deploy-rs + nix run github:serokell/deploy-rs -- -s .#cryodev-main + + + +================================================ +FILE: .forgejo/workflows/flake-check.yml +================================================ +name: Flake check + +on: [pull_request] + +jobs: + flake-check: + runs-on: docker + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:nixos-unstable + + - name: Run flake check + run: nix flake check --impure + + diff --git a/docs/deployment/dns.md b/docs/deployment/dns.md index 5f2fdc2..8d0f99b 100644 --- a/docs/deployment/dns.md +++ b/docs/deployment/dns.md @@ -31,12 +31,12 @@ Required DNS records for the cryodev infrastructure. | `@` | MX | `10 mail.cryodev.xyz.` | Mail delivery | | `@` | TXT | `"v=spf1 mx ~all"` | SPF | | `_dmarc` | TXT | `"v=DMARC1; p=none"` | DMARC | -| `mail._domainkey` | TXT | *(see below)* | DKIM | +| `mail._domainkey` | TXT | *(siehe unten)* | DKIM | ### Reverse DNS (PTR) -For reliable mail delivery, a **PTR record** must be configured at the hosting -provider (not in the domain's DNS panel): +Fuer zuverlaessige Mail-Zustellung muss ein **PTR Record** beim Hosting-Provider +konfiguriert werden (nicht im DNS-Panel der Domain): | IP | PTR Value | |----|-----------| @@ -45,18 +45,18 @@ provider (not in the domain's DNS panel): #### Hetzner Robot (Dedicated Server) -1. [robot.hetzner.com](https://robot.hetzner.com) > **Server** > Select the server -2. **IPs** tab -3. Click the **pencil icon** next to the IPv4 address -4. Enter `mail.cryodev.xyz` and save -5. For IPv6: Under **Subnets**, repeat the same for the primary IPv6 address +1. [robot.hetzner.com](https://robot.hetzner.com) > **Server** > Server auswaehlen +2. **IPs** Tab +3. Bei der IPv4-Adresse auf das **Stift-Symbol** klicken +4. `mail.cryodev.xyz` eintragen und speichern +5. Fuer IPv6: Unter **Subnets** dasselbe fuer die primaere IPv6-Adresse #### Hetzner Cloud -1. [cloud.hetzner.com](https://cloud.hetzner.com) > Select the server -2. **Networking** tab -3. Under "Primary IP", click the IP > **Reverse DNS** -4. Enter `mail.cryodev.xyz` (for both IPv4 and IPv6) +1. [cloud.hetzner.com](https://cloud.hetzner.com) > Server auswaehlen +2. **Networking** Tab +3. Bei "Primary IP" auf die IP klicken > **Reverse DNS** +4. `mail.cryodev.xyz` eintragen (fuer IPv4 und IPv6) ## Getting the DKIM Key @@ -70,15 +70,15 @@ Add this as a TXT record for `mail._domainkey.cryodev.xyz`. ## Complete Checklist -- [ ] A/AAAA for `@` (root domain) -- [ ] A/AAAA for `www` -- [ ] A/AAAA for `mail` -- [ ] CNAME for `git`, `headscale`, `headplane`, `netdata` -- [ ] MX record -- [ ] TXT for SPF (`v=spf1 mx ~all`) -- [ ] TXT for DMARC (`v=DMARC1; p=none`) -- [ ] TXT for DKIM (`mail._domainkey` -- after first deployment) -- [ ] PTR record at hosting provider (reverse DNS) +- [ ] A/AAAA fuer `@` (Root-Domain) +- [ ] A/AAAA fuer `www` +- [ ] A/AAAA fuer `mail` +- [ ] CNAME fuer `git`, `headscale`, `headplane`, `netdata` +- [ ] MX Record +- [ ] TXT fuer SPF (`v=spf1 mx ~all`) +- [ ] TXT fuer DMARC (`v=DMARC1; p=none`) +- [ ] TXT fuer DKIM (`mail._domainkey` -- nach erstem Deploy) +- [ ] PTR Record beim Hosting-Provider (Reverse DNS) ## Verification diff --git a/docs/getting-started/first-install.md b/docs/getting-started/first-install.md index d029a63..f50c51a 100644 --- a/docs/getting-started/first-install.md +++ b/docs/getting-started/first-install.md @@ -1,44 +1,44 @@ -# Initial Installation (x86_64 Server) +# Erstinstallation (x86_64 Server) -This guide describes the **initial installation** of a new x86_64 server (e.g. cryodev-main). +Diese Anleitung beschreibt die **Erstinstallation** eines neuen x86_64 Servers (z.B. cryodev-main). -> **For Raspberry Pi:** See [Creating an SD Image](sd-image.md). +> **Fuer Raspberry Pi:** Siehe [SD-Image erstellen](sd-image.md). -## Overview +## Uebersicht -During initial installation there is a chicken-and-egg problem: -- SOPS secrets are encrypted with the SSH host key -- The SSH host key is only generated during installation -- Therefore: **Install without secrets first, then configure secrets** +Bei der Erstinstallation gibt es ein Henne-Ei-Problem: +- SOPS-Secrets werden mit dem SSH-Host-Key verschluesselt +- Der SSH-Host-Key wird erst bei der Installation generiert +- Daher: **Erst ohne Secrets installieren, dann Secrets konfigurieren** -### Process +### Ablauf ``` -1. Disable services (that require secrets) -2. Install NixOS -3. Extract SSH host key, configure SOPS, create immediately available secrets -4. Enable stage-1 services and deploy (Headscale, Forgejo, Mail, Nginx) -5. Generate remaining secrets (Tailscale, Headplane, Forgejo Runner) -6. Enable stage-2 services and perform final deployment +1. Services deaktivieren (die Secrets brauchen) +2. NixOS installieren +3. SSH-Host-Key extrahieren, SOPS konfigurieren, sofort erstellbare Secrets anlegen +4. Stufe-1-Services aktivieren und deployen (Headscale, Forgejo, Mail, Nginx) +5. Restliche Secrets generieren (Tailscale, Headplane, Forgejo-Runner) +6. Stufe-2-Services aktivieren und final deployen ``` -## Step 1: Prepare Host Configuration +## Schritt 1: Host-Konfiguration vorbereiten -> If the host already exists in `hosts/` and `flake.nix`, skip 1.1-1.2. +> Falls der Host bereits in `hosts/` und `flake.nix` existiert, ueberspringe 1.1-1.2. -### 1.1 Create Host from Template +### 1.1 Host aus Template erstellen ```bash nix run .#create -- -t generic-server -n ``` -The script: -- Copies the template to `hosts//` -- Sets the hostname in `networking.nix` -- Creates an empty `secrets.yaml` -- Adds the files to Git +Das Script: +- Kopiert das Template nach `hosts//` +- Setzt den Hostname in `networking.nix` +- Erstellt eine leere `secrets.yaml` +- Fuegt die Dateien zu Git hinzu -### 1.2 Register in flake.nix +### 1.2 In flake.nix registrieren ```nix nixosConfigurations = { @@ -46,24 +46,24 @@ nixosConfigurations = { }; ``` -Also adjust `hardware.nix` and `disks.sh` for the target hardware. +Ausserdem `hardware.nix` und `disks.sh` fuer die Zielhardware anpassen. -### 1.4 Temporarily Disable Services +### 1.4 Services temporaer deaktivieren -All services that reference SOPS secrets must be disabled for the initial installation. Otherwise the installation will fail because the secrets cannot yet be decrypted. +Alle Services, die SOPS-Secrets referenzieren, muessen fuer die Erstinstallation deaktiviert werden. Andernfalls schlaegt die Installation fehl, weil die Secrets noch nicht entschluesselt werden koennen. -In `hosts//services/default.nix`, comment out the corresponding imports: +In `hosts//services/default.nix` die entsprechenden Imports auskommentieren: ```nix { imports = [ - # Disabled until SOPS secrets are configured: - # ./forgejo.nix # requires: forgejo-runner/token, forgejo/mail-pw - # ./headplane.nix # requires: headplane/cookie_secret, headplane/agent_pre_authkey - # ./mailserver.nix # requires: mailserver/accounts/* - # ./tailscale.nix # requires: tailscale/auth-key + # Deaktiviert bis SOPS-Secrets konfiguriert sind: + # ./forgejo.nix # braucht: forgejo-runner/token, forgejo/mail-pw + # ./headplane.nix # braucht: headplane/cookie_secret, headplane/agent_pre_authkey + # ./mailserver.nix # braucht: mailserver/accounts/* + # ./tailscale.nix # braucht: tailscale/auth-key - # These services do not require secrets: + # Diese Services brauchen keine Secrets: ./headscale.nix ./netdata.nix ./nginx.nix @@ -73,7 +73,7 @@ In `hosts//services/default.nix`, comment out the corresponding import } ``` -Additionally, in `hosts//services/sops.nix`, comment out the secret definitions: +Zusaetzlich in `hosts//services/sops.nix` die Secrets-Definitionen auskommentieren: ```nix sops = { @@ -85,32 +85,32 @@ sops = { }; ``` -### 1.5 Test the Configuration +### 1.5 Konfiguration testen ```bash nix eval .#nixosConfigurations..config.system.build.toplevel.name ``` -## Step 2: Perform Installation +## Schritt 2: Installation durchfuehren -### 2.1 Boot NixOS ISO +### 2.1 NixOS ISO booten -Boot from the [NixOS Minimal ISO](https://nixos.org/download/#nixos-iso) (USB/CD). +Vom [NixOS Minimal ISO](https://nixos.org/download/#nixos-iso) booten (USB/CD). -### 2.2 Set Up Network and SSH +### 2.2 Netzwerk und SSH einrichten ```bash -passwd # Set root password for SSH access -ip a # Determine IP address +passwd # Root-Passwort setzen fuer SSH-Zugang +ip a # IP-Adresse ermitteln ``` -Optionally connect via SSH (more convenient): +Optional per SSH verbinden (bequemer): ```bash ssh -o StrictHostKeyChecking=no root@ ``` -### 2.3 Install +### 2.3 Installieren ```bash nix --experimental-features "nix-command flakes" run \ @@ -119,20 +119,20 @@ nix --experimental-features "nix-command flakes" run \ -r ``` -Alternatively, if the repository has already been cloned to `/tmp/nixos`: +Alternativ, falls das Repository bereits unter `/tmp/nixos` geklont wurde: ```bash nix --experimental-features "nix-command flakes" run /tmp/nixos#install -- -n ``` -> **Note:** The disk ID in `hosts//disks.sh` must match the hardware. -> Verify with `ls -la /dev/disk/by-id/`. +> **Hinweis:** Die Disk-ID in `hosts//disks.sh` muss zur Hardware passen. +> Pruefen mit `ls -la /dev/disk/by-id/`. -The script: -1. Clones the repository (when using `-r`) -2. Partitions the disk (via `disks.nix` or `disks.sh`) -3. Generates `hardware.nix` (if not present) -4. Installs NixOS +Das Script: +1. Klont das Repository (bei `-r`) +2. Partitioniert die Disk (via `disks.nix` oder `disks.sh`) +3. Generiert `hardware.nix` (falls nicht vorhanden) +4. Installiert NixOS ### 2.4 Reboot @@ -140,34 +140,34 @@ The script: reboot ``` -## Step 3: Configure SOPS Secrets +## Schritt 3: SOPS-Secrets konfigurieren -After the first boot, log in (password: `changeme`, change immediately with `passwd`). +Nach dem ersten Boot einloggen (Passwort: `changeme`, sofort aendern mit `passwd`). -### 3.1 Convert SSH Host Key to Age Key +### 3.1 SSH-Host-Key zu Age-Key konvertieren -On the **new server**: +Auf dem **neuen Server**: ```bash nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' ``` -Note the output (e.g. `age1abc123...`). +Ausgabe notieren (z.B. `age1abc123...`). -Alternatively, remotely: +Alternativ remote: ```bash nix-shell -p ssh-to-age --run 'ssh-keyscan -p 2299 -t ed25519 | ssh-to-age' ``` -### 3.2 Update .sops.yaml +### 3.2 .sops.yaml aktualisieren -On the **development machine**, add the new host key to `.sops.yaml`: +Auf dem **Entwicklungsrechner** den neuen Host-Key in `.sops.yaml` eintragen: ```yaml keys: - - &steffen_key age1e8p... # steffen (local) - - &hostname_key age1abc... # Key from step 3.1 + - &steffen_key age1e8p... # steffen (lokal) + - &hostname_key age1abc... # Key von Schritt 3.1 creation_rules: - path_regex: hosts//secrets.yaml$ @@ -177,38 +177,38 @@ creation_rules: - *hostname_key ``` -### 3.3 Create Secrets +### 3.3 Secrets erstellen -Open the secrets file: +Secrets-Datei oeffnen: ```bash sops hosts//secrets.yaml ``` -The following table shows all secrets for **cryodev-main** and how they are generated: +Die folgende Tabelle zeigt alle Secrets fuer **cryodev-main** und wie sie generiert werden: -#### Immediately Available Secrets +#### Sofort erstellbare Secrets -These secrets have no dependencies and can be generated directly: +Diese Secrets haben keine Abhaengigkeiten und koennen direkt generiert werden: -| Secret | Command | -|--------|---------| +| Secret | Befehl | +|--------|--------| | `headplane/cookie_secret` | `openssl rand -hex 16` | -| `mailserver/accounts/admin` | `mkpasswd -sm bcrypt` (remember the password!) | -| `mailserver/accounts/forgejo` | `mkpasswd -sm bcrypt` (remember the password!) | -| `forgejo/mail-pw` | Plaintext password matching the bcrypt hash of `mailserver/accounts/forgejo` | +| `mailserver/accounts/admin` | `mkpasswd -sm bcrypt` (Passwort merken!) | +| `mailserver/accounts/forgejo` | `mkpasswd -sm bcrypt` (Passwort merken!) | +| `forgejo/mail-pw` | Klartext-Passwort das zum bcrypt-Hash von `mailserver/accounts/forgejo` passt | -#### Secrets That Require Running Services +#### Secrets die laufende Services brauchen -These secrets can only be created after step 4. **Do not add them yet** -- they will be added later. +Diese Secrets koennen erst nach Schritt 4 erstellt werden. **Jetzt noch nicht eintragen** -- sie werden spaeter ergaenzt. -| Secret | Command | Prerequisite | -|--------|---------|--------------| -| `tailscale/auth-key` | See steps 4.1-4.2 | Headscale is running | -| `headplane/agent_pre_authkey` | See steps 4.1-4.2 | Headscale is running | -| `forgejo-runner/token` | Forgejo Admin Panel > Actions > Runners > Create Runner | Forgejo is running | +| Secret | Befehl | Voraussetzung | +|--------|--------|---------------| +| `tailscale/auth-key` | Siehe Schritt 4.1-4.2 | Headscale laeuft | +| `headplane/agent_pre_authkey` | Siehe Schritt 4.1-4.2 | Headscale laeuft | +| `forgejo-runner/token` | Forgejo Admin Panel > Actions > Runners > Create Runner | Forgejo laeuft | -#### Example secrets.yaml (Plaintext Before Encryption) +#### Beispiel secrets.yaml (Klartext vor Verschluesselung) ```yaml headplane: @@ -218,22 +218,22 @@ mailserver: admin: "$2b$05$..." forgejo: "$2b$05$..." forgejo: - mail-pw: "the-plaintext-password" + mail-pw: "das-klartext-passwort" ``` -### 3.4 Gradually Re-enable Services -- Stage 1 +### 3.4 Services stufenweise reaktivieren -- Stufe 1 -> **Important:** Services that require Headscale or Forgejo secrets (Tailscale, -> Headplane, Forgejo Runner) must **not** be enabled yet, as these -> secrets can only be generated once those services are running. +> **Wichtig:** Services die Headscale- oder Forgejo-Secrets brauchen (Tailscale, +> Headplane, Forgejo-Runner) duerfen noch **nicht** aktiviert werden, da diese +> Secrets erst generiert werden koennen, wenn die Services laufen. -On the **development machine**, in `hosts//services/default.nix`, enable -the services **without external dependencies**: +Auf dem **Entwicklungsrechner** in `hosts//services/default.nix` die +Services **ohne externe Abhaengigkeiten** aktivieren: ```nix { imports = [ - # Stage 1: Services without external dependencies + # Stufe 1: Services ohne externe Abhaengigkeiten ./forgejo.nix ./headscale.nix ./mailserver.nix @@ -242,88 +242,88 @@ the services **without external dependencies**: ./openssh.nix ./sops.nix - # Stage 2: Enable only after step 4 - # ./forgejo-runner.nix # requires: forgejo-runner/token (Forgejo) - # ./headplane.nix # requires: headplane/agent_pre_authkey (Headscale) - # ./tailscale.nix # requires: tailscale/auth-key (Headscale) + # Stufe 2: Erst nach Schritt 4 aktivieren + # ./forgejo-runner.nix # braucht: forgejo-runner/token (Forgejo) + # ./headplane.nix # braucht: headplane/agent_pre_authkey (Headscale) + # ./tailscale.nix # braucht: tailscale/auth-key (Headscale) ]; } ``` -### 3.5 Deploy (Stage 1) +### 3.5 Deployen (Stufe 1) ```bash nix run .#deploy -- -n ``` -This uses the configuration from `deploy.json`. Alternatively, deploy manually: +Dies nutzt die Konfiguration aus `deploy.json`. Alternativ manuell: ```bash NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ --target-host @ --sudo --ask-sudo-password ``` -After this deployment, Headscale, Forgejo, Mailserver, and Nginx are running. +Nach diesem Deploy laufen Headscale, Forgejo, Mailserver und Nginx. -### 3.6 Create Forgejo Admin Account +### 3.6 Forgejo Admin-Account erstellen -On first start, Forgejo has no users. Create an admin account via CLI -(on the **server**): +Beim ersten Start hat Forgejo noch keine Benutzer. Admin-Account per CLI anlegen +(auf dem **Server**): ```bash forgejo admin user create \ - --username \ + --username \ --email @ \ - --password \ + --password \ --admin ``` -> **Note:** The `forgejo` shell alias is provided by the module and automatically -> runs the command as the `forgejo` user with the correct config. -> If the alias is not available, start a new shell (`bash` or `zsh`). +> **Hinweis:** Das `forgejo` Shell-Alias wird vom Modul bereitgestellt und fuehrt +> automatisch den Befehl als `forgejo`-User mit der richtigen Config aus. +> Falls der Alias nicht verfuegbar ist, neue Shell starten (`bash` oder `zsh`). > -> Since `DISABLE_REGISTRATION = true` is set, new accounts -> can only be created via CLI. +> Da `DISABLE_REGISTRATION = true` gesetzt ist, koennen neue Accounts +> nur per CLI erstellt werden. -## Step 4: Generate Remaining Secrets and Enable All Services +## Schritt 4: Restliche Secrets generieren und alle Services aktivieren -After the server is running with Headscale and Forgejo: +Nachdem der Server mit Headscale und Forgejo laeuft: -1. **Create Headscale users** (on the server): +1. **Headscale-User anlegen** (auf dem Server): ```bash sudo headscale users create default sudo headscale users create headplane-agent ``` -2. **Determine user IDs** (needed for the preauth keys): +2. **User-IDs ermitteln** (wird fuer die Preauth-Keys benoetigt): ```bash sudo headscale users list ``` - The output shows the numeric IDs (e.g. `1` for default, `2` for headplane-agent). + Die Ausgabe zeigt die numerischen IDs (z.B. `1` fuer default, `2` fuer headplane-agent). -3. **Generate preauth keys** (using the IDs from step 2): +3. **Preauth-Keys generieren** (mit den IDs aus Schritt 2): ```bash - # For Tailscale (use the user ID of "default") + # Fuer Tailscale (User-ID von "default" einsetzen) sudo headscale preauthkeys create --expiration 99y --reusable --user - # For Headplane Agent (use the user ID of "headplane-agent") + # Fuer Headplane Agent (User-ID von "headplane-agent" einsetzen) sudo headscale preauthkeys create --expiration 99y --user ``` -4. **Create the Forgejo Runner token** via the Forgejo Admin Panel: +4. **Forgejo-Runner-Token** ueber das Forgejo Admin Panel erstellen: Administration > Actions > Runners > Create new Runner -5. **Add the remaining secrets**: +5. **Secrets ergaenzen**: ```bash sops hosts//secrets.yaml ``` - Add the missing secrets: + Die fehlenden Secrets eintragen: ```yaml tailscale: @@ -334,7 +334,7 @@ After the server is running with Headscale and Forgejo: agent_pre_authkey: "..." ``` -6. **Enable stage-2 services** in `hosts//services/default.nix`: +6. **Stufe-2-Services aktivieren** in `hosts//services/default.nix`: ```nix { @@ -353,14 +353,14 @@ After the server is running with Headscale and Forgejo: } ``` -6. **Deploy again**: +6. **Erneut deployen**: ```bash nix run .#deploy -- -n ``` -## Next Steps +## Naechste Schritte -- [SOPS Reference](../services/sops.md) -- Detailed documentation on secret management -- [Creating an SD Image](sd-image.md) -- Install Raspberry Pi -- [Set Up CD](../deployment/cd.md) -- Automatic deployment +- [SOPS-Referenz](../services/sops.md) -- Detail-Dokumentation zur Secret-Verwaltung +- [SD-Image erstellen](sd-image.md) -- Raspberry Pi installieren +- [CD einrichten](../deployment/cd.md) -- Automatisches Deployment diff --git a/docs/getting-started/new-client.md b/docs/getting-started/new-client.md index dffb30a..8cb29a8 100644 --- a/docs/getting-started/new-client.md +++ b/docs/getting-started/new-client.md @@ -1,60 +1,60 @@ -# Adding a New Raspberry Pi Client +# Neuen Raspberry Pi Client hinzufügen -This guide describes how to add a **new Raspberry Pi client** to the infrastructure. +Diese Anleitung beschreibt das Hinzufügen eines **neuen Raspberry Pi Clients** zur Infrastruktur. -## Overview: The Process +## Übersicht: Der Ablauf ``` -1. Create configuration ──► Copy template, customize +1. Konfiguration erstellen ──► Template kopieren, anpassen │ ▼ -2. Add to image pipeline ──► Extend workflow matrix +2. Zur Image-Pipeline hinzufügen ──► Workflow-Matrix erweitern │ ▼ -3. Push to main ──► Forgejo automatically builds SD image +3. Push auf main ──► Forgejo baut automatisch SD-Image │ ▼ -4. Flash image & boot ──► Write SD card, start Pi +4. Image flashen & booten ──► SD-Karte beschreiben, Pi starten │ ▼ -5. Configure SOPS ──► Retrieve age key, create secrets +5. SOPS konfigurieren ──► Age-Key holen, Secrets erstellen │ ▼ -6. Final deployment ──► Activate Tailscale etc. +6. Finales Deployment ──► Tailscale etc. aktivieren ``` -## Prerequisites +## Voraussetzungen -- SSH access to cryodev-main (for Tailscale auth key) -- Development machine with repository access -- SD card (at least 8 GB) +- SSH-Zugang zu cryodev-main (für Tailscale Auth-Key) +- Entwicklungsrechner mit Repository-Zugriff +- SD-Karte (mindestens 8 GB) --- -## Step 1: Generate Tailscale Auth Key +## Schritt 1: Tailscale Auth-Key generieren -**On cryodev-main** (via SSH): +**Auf cryodev-main** (per SSH): ```bash -# Determine user ID +# User-ID ermitteln sudo headscale users list -# Create preauth key (use user ID of "default") +# Preauth-Key erstellen (User-ID von "default" einsetzen) sudo headscale preauthkeys create --expiration 99y --reusable --user ``` -**Take note of the output!** (e.g. `tskey-preauth-abc123...`) +**Ausgabe notieren!** (z.B. `tskey-preauth-abc123...`) --- -## Step 2: Create Host Configuration +## Schritt 2: Host-Konfiguration erstellen -### 2.1 Copy Template +### 2.1 Template kopieren ```bash cp -r templates/raspberry-pi hosts/neuer-pi ``` -### 2.2 Set Hostname +### 2.2 Hostname setzen `hosts/neuer-pi/networking.nix`: @@ -64,58 +64,58 @@ cp -r templates/raspberry-pi hosts/neuer-pi } ``` -### 2.3 Register in flake.nix +### 2.3 In flake.nix registrieren ```nix nixosConfigurations = { - # ... existing hosts ... + # ... bestehende Hosts ... neuer-pi = mkNixosConfiguration "aarch64-linux" [ ./hosts/neuer-pi ]; }; ``` -### 2.4 Add to constants.nix +### 2.4 In constants.nix eintragen ```nix { hosts = { - # ... existing hosts ... + # ... bestehende Hosts ... neuer-pi = { - ip = "100.64.0.X"; # Assigned by Headscale + ip = "100.64.0.X"; # Wird von Headscale vergeben }; }; } ``` -### 2.5 Create Placeholder secrets.yaml +### 2.5 Placeholder secrets.yaml erstellen ```bash touch hosts/neuer-pi/secrets.yaml ``` -### 2.6 Temporarily Disable SOPS +### 2.6 SOPS temporär deaktivieren -In `hosts/neuer-pi/default.nix`, comment out the `sops.secrets.*` references so the image can be built without secrets. +In `hosts/neuer-pi/default.nix` die `sops.secrets.*` Referenzen auskommentieren, damit das Image ohne Secrets gebaut werden kann. --- -## Step 3: Add to Image Pipeline +## Schritt 3: Zur Image-Pipeline hinzufügen -Edit `.forgejo/workflows/build-pi-image.yml`: +Bearbeite `.forgejo/workflows/build-pi-image.yml`: ```yaml jobs: build-pi-images: strategy: matrix: - # Add new host here: + # Neuen Host hier hinzufügen: host: [cryodev-pi, neuer-pi] ``` --- -## Step 4: Push and Build Image +## Schritt 4: Push und Image bauen lassen ```bash git add . @@ -123,86 +123,86 @@ git commit -m "Add neuer-pi host configuration" git push ``` -The Forgejo workflow will now automatically build an SD image for `neuer-pi`. +Der Forgejo Workflow baut jetzt automatisch ein SD-Image für `neuer-pi`. -**Wait** until the workflow completes (30-60 minutes). Check the status at: +**Warten** bis der Workflow fertig ist (30-60 Minuten). Status prüfen unter: `https://git.cryodev.xyz/steffen/cryodev-server/actions` --- -## Step 5: Flash Image +## Schritt 5: Image flashen -### 5.1 Download Image +### 5.1 Image herunterladen -After a successful build, find the image under **Releases**: +Nach erfolgreichem Build unter **Releases**: ```bash wget https://git.cryodev.xyz/steffen/cryodev-server/releases/latest/download/neuer-pi-sd-image.img.zst ``` -### 5.2 Decompress +### 5.2 Dekomprimieren ```bash zstd -d neuer-pi-sd-image.img.zst -o neuer-pi.img ``` -### 5.3 Write to SD Card +### 5.3 Auf SD-Karte schreiben -**Warning:** Replace `/dev/sdX` with the correct device! +**Achtung:** `/dev/sdX` durch das richtige Gerät ersetzen! ```bash -lsblk # Identify the correct device +lsblk # Richtiges Gerät finden sudo dd if=neuer-pi.img of=/dev/sdX bs=4M conv=fsync status=progress ``` -### 5.4 Boot +### 5.4 Booten -1. Insert the SD card into the Raspberry Pi -2. Connect Ethernet -3. Connect power -4. Wait until booted (approximately 2 minutes) +1. SD-Karte in den Raspberry Pi einlegen +2. Ethernet anschließen +3. Strom anschließen +4. Warten bis gebootet (ca. 2 Minuten) --- -## Step 6: Configure SOPS +## Schritt 6: SOPS konfigurieren -### 6.1 Find IP Address +### 6.1 IP-Adresse finden -The Pi should receive an IP address via DHCP. Check your router or scan the network: +Der Pi sollte per DHCP eine IP bekommen. Prüfe deinen Router oder scanne das Netzwerk: ```bash nmap -sn 192.168.1.0/24 | grep -B2 "Raspberry" ``` -### 6.2 Connect via SSH +### 6.2 SSH verbinden ```bash -ssh steffen@ # or the configured user +ssh steffen@ # oder der konfigurierte User ``` -For the default password, see `hosts/neuer-pi/users.nix`. +Standard-Passwort siehe `hosts/neuer-pi/users.nix`. -### 6.3 Determine Age Key +### 6.3 Age-Key ermitteln -On the Pi: +Auf dem Pi: ```bash nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' ``` -**Take note of the output!** (e.g. `age1xyz...`) +**Ausgabe notieren!** (z.B. `age1xyz...`) -### 6.4 Update .sops.yaml +### 6.4 .sops.yaml aktualisieren -On the development machine: +Auf dem Entwicklungsrechner: ```yaml keys: - &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local) - - &neuer_pi_key age1xyz... # The new key + - &neuer_pi_key age1xyz... # Der neue Key creation_rules: - # ... existing rules ... + # ... bestehende Regeln ... - path_regex: hosts/neuer-pi/secrets.yaml$ key_groups: @@ -211,30 +211,30 @@ creation_rules: - *neuer_pi_key ``` -### 6.5 Create Secrets +### 6.5 Secrets erstellen ```bash sops hosts/neuer-pi/secrets.yaml ``` -Contents: +Inhalt: ```yaml tailscale: - auth-key: "tskey-preauth-abc123..." # Key from Step 1 + auth-key: "tskey-preauth-abc123..." # Key aus Schritt 1 netdata: stream: child-uuid: "..." # uuidgen ``` -### 6.6 Activate SOPS References +### 6.6 SOPS-Referenzen aktivieren -Re-enable the `sops.secrets.*` references that were commented out in Step 2.6. +Die in Schritt 2.6 auskommentierten `sops.secrets.*` Referenzen wieder aktivieren. --- -## Step 7: Final Deployment +## Schritt 7: Finales Deployment ```bash git add . @@ -242,9 +242,9 @@ git commit -m "Configure SOPS secrets for neuer-pi" git push ``` -Since Comin is running on the Pi, it will automatically pull the new configuration. +Da Comin auf dem Pi läuft, wird er die neue Konfiguration automatisch pullen. -Alternatively, deploy manually: +Alternativ manuell: ```bash NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .#neuer-pi \ @@ -253,34 +253,34 @@ NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .#neuer-pi \ --- -## Step 8: Verify +## Schritt 8: Verifizieren -### Tailscale Connection +### Tailscale-Verbindung ```bash -# On the Pi +# Auf dem Pi tailscale status -# On cryodev-main +# Auf cryodev-main sudo headscale nodes list ``` -### Netdata Streaming +### Netdata-Streaming -Check whether the new client appears in the Netdata dashboard: +Prüfe ob der neue Client im Netdata-Dashboard erscheint: `https://netdata.cryodev.xyz` --- -## Checklist +## Checkliste -- [ ] Tailscale auth key generated on cryodev-main -- [ ] Host configuration created (template, flake.nix, constants.nix) -- [ ] Host added to workflow matrix -- [ ] Pushed and waited for image build -- [ ] SD card flashed and Pi booted -- [ ] Age key determined and added to .sops.yaml -- [ ] secrets.yaml created (Tailscale key, Netdata UUID) -- [ ] SOPS references activated and deployed -- [ ] Tailscale connection working -- [ ] Netdata streaming working +- [ ] Tailscale Auth-Key auf cryodev-main generiert +- [ ] Host-Konfiguration erstellt (Template, flake.nix, constants.nix) +- [ ] Host zur Workflow-Matrix hinzugefügt +- [ ] Gepusht und auf Image-Build gewartet +- [ ] SD-Karte geflasht und Pi gebootet +- [ ] Age-Key ermittelt und in .sops.yaml eingetragen +- [ ] secrets.yaml erstellt (Tailscale-Key, Netdata-UUID) +- [ ] SOPS-Referenzen aktiviert und deployed +- [ ] Tailscale-Verbindung funktioniert +- [ ] Netdata-Streaming funktioniert diff --git a/docs/getting-started/reinstall.md b/docs/getting-started/reinstall.md index cb7775f..091378c 100644 --- a/docs/getting-started/reinstall.md +++ b/docs/getting-started/reinstall.md @@ -1,70 +1,70 @@ -# Reinstallation +# Neuinstallation (Reinstall) -This guide describes the **reinstallation** of an existing host, e.g. after a hardware change or in case of issues. +Diese Anleitung beschreibt die **Neuinstallation** eines bestehenden Hosts, z.B. nach Hardwarewechsel oder bei Problemen. -## Difference from Initial Installation +## Unterschied zur Erstinstallation -| Aspect | Initial Installation | Reinstallation | -|--------|----------------------|----------------| -| SOPS Secrets | Not yet present | Already configured | -| SSH Host Key | Newly generated | **Must be restored!** | -| Disk IDs | Newly determined | Often changed (new hardware) | -| secrets.yaml | Will be created | Already exists | +| Aspekt | Erstinstallation | Neuinstallation | +|--------|------------------|-----------------| +| SOPS-Secrets | Noch nicht vorhanden | Bereits konfiguriert | +| SSH-Host-Key | Neu generiert | **Muss wiederhergestellt werden!** | +| Disk-IDs | Neu ermitteln | Oft geändert (neue Hardware) | +| secrets.yaml | Wird erstellt | Bereits vorhanden | -## Important: SSH Host Key Issue +## Wichtig: SSH-Host-Key Problem -During a reinstallation, a **new SSH host key** is generated. This key will no longer match the age key in `.sops.yaml`! +Bei einer Neuinstallation wird ein **neuer SSH-Host-Key** generiert. Dieser stimmt nicht mehr mit dem Age-Key in `.sops.yaml` überein! -### Possible Solutions +### Lösungsmöglichkeiten -**Option A: Back up and restore the old host key** (recommended) +**Option A: Alten Host-Key sichern und wiederherstellen** (empfohlen) -**Option B: Generate a new key and update SOPS** +**Option B: Neuen Key generieren und SOPS aktualisieren** -## Prerequisites +## Voraussetzungen -- Backup of the old SSH host key (if using Option A) -- Access to `.sops.yaml` and the admin age keys -- Bootable NixOS ISO +- Backup des alten SSH-Host-Keys (falls Option A) +- Zugriff auf `.sops.yaml` und die Admin-Age-Keys +- Bootbares NixOS ISO -## Step 1: Preparation (before the installation) +## Schritt 1: Vorbereitung (vor der Installation) -### 1.1 Back Up the Old SSH Host Key (Option A) +### 1.1 Alten SSH-Host-Key sichern (Option A) -If the old host is still running: +Falls der alte Host noch läuft: ```bash -# On the old host +# Auf dem alten Host sudo cat /etc/ssh/ssh_host_ed25519_key > ~/ssh_host_ed25519_key.backup sudo cat /etc/ssh/ssh_host_ed25519_key.pub > ~/ssh_host_ed25519_key.pub.backup ``` -Copy the files securely to the development machine. +Dateien sicher auf den Entwicklungsrechner kopieren. -### 1.2 Determine Disk IDs +### 1.2 Disk-IDs ermitteln -**With new hardware**, the disk IDs will change! +**Bei neuer Hardware** ändern sich die Disk-IDs! ```bash -# In the NixOS live system +# Im NixOS Live-System lsblk -o NAME,SIZE,MODEL,SERIAL ls -la /dev/disk/by-id/ ``` -Enter the new disk ID in `hosts//disks.sh` or `disks.nix`: +Die neue Disk-ID in `hosts//disks.sh` oder `disks.nix` eintragen: ```bash -# Example disks.sh +# Beispiel disks.sh DISK="/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_XXXXX" ``` -## Step 2: Perform the Installation +## Schritt 2: Installation durchführen -### 2.1 Boot the NixOS ISO +### 2.1 NixOS ISO booten -Boot from USB/CD, set a root password, and connect via SSH. +Von USB/CD booten, Root-Passwort setzen, per SSH verbinden. -### 2.2 Clone the Repository +### 2.2 Repository klonen ```bash sudo -i @@ -73,30 +73,30 @@ git clone /tmp/nixos cd /tmp/nixos ``` -### 2.3 Verify the Disk Configuration +### 2.3 Disk-Konfiguration prüfen ```bash -# Display current disk IDs +# Aktuelle Disk-IDs anzeigen ls -la /dev/disk/by-id/ -# Compare with the configuration +# Mit Konfiguration vergleichen cat hosts//disks.sh | grep DISK ``` -**If necessary:** Update the disk ID in the configuration. +**Falls nötig:** Disk-ID in der Konfiguration anpassen. -### 2.4 Run the Install Script +### 2.4 Install-Script ausführen ```bash bash scripts/install.sh -n ``` -### 2.5 Restore the SSH Host Key (Option A) +### 2.5 SSH-Host-Key wiederherstellen (Option A) -**Before rebooting!** +**Vor dem Reboot!** ```bash -# Restore the host key from backup +# Host-Key vom Backup wiederherstellen cp /path/to/ssh_host_ed25519_key.backup /mnt/etc/ssh/ssh_host_ed25519_key cp /path/to/ssh_host_ed25519_key.pub.backup /mnt/etc/ssh/ssh_host_ed25519_key.pub chmod 600 /mnt/etc/ssh/ssh_host_ed25519_key @@ -110,75 +110,75 @@ umount -Rl /mnt reboot ``` -## Step 3: After the Reboot +## Schritt 3: Nach dem Reboot -### Option A (Key Restored) +### Bei Option A (Key wiederhergestellt) -SOPS secrets should work automatically. Verify: +SOPS-Secrets sollten automatisch funktionieren. Testen: ```bash sudo cat /run/secrets/tailscale/auth-key ``` -### Option B (New Key) +### Bei Option B (Neuer Key) -The host cannot decrypt the secrets. Configure the new key: +Der Host kann die Secrets nicht entschlüsseln. Neuen Key konfigurieren: ```bash -# Determine the new age key +# Neuen Age-Key ermitteln nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' ``` -On the development machine: +Auf dem Entwicklungsrechner: ```bash -# Update .sops.yaml with the new key +# .sops.yaml aktualisieren mit neuem Key vim .sops.yaml -# Re-encrypt secrets with the new key +# Secrets mit neuem Key neu verschlüsseln sops updatekeys hosts//secrets.yaml ``` -Then redeploy the configuration: +Dann Konfiguration neu deployen: ```bash NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ --target-host @ --sudo --ask-sudo-password ``` -## Common Issues +## Häufige Probleme ### "No secret key available" -SOPS cannot decrypt the secrets. Cause: -- SSH host key does not match the age key in `.sops.yaml` +SOPS kann die Secrets nicht entschlüsseln. Ursache: +- SSH-Host-Key stimmt nicht mit Age-Key in `.sops.yaml` überein -Solution: Follow Option B (configure the new key). +Lösung: Option B durchführen (neuen Key konfigurieren). -### "Device not found" during partitioning +### "Device not found" beim Partitionieren -The disk ID in `disks.sh`/`disks.nix` is incorrect. +Disk-ID in `disks.sh`/`disks.nix` ist falsch. ```bash -# Find the correct ID +# Richtige ID finden ls -la /dev/disk/by-id/ ``` -### Outdated Hardware Config +### Hardware-Config veraltet -With new hardware, `hardware.nix` must be regenerated: +Bei neuer Hardware muss `hardware.nix` neu generiert werden: ```bash -# The install script regenerates automatically if the file is missing +# Install-Script generiert automatisch neu, falls Datei fehlt rm hosts//hardware.nix bash scripts/install.sh -n ``` -## Checklist +## Checkliste -- [ ] Old SSH host key backed up (if possible) -- [ ] Disk IDs in configuration verified/updated -- [ ] Installation completed -- [ ] SSH host key restored OR new key configured in SOPS -- [ ] Secrets are functional (`sudo cat /run/secrets/...`) -- [ ] Tailscale connected (`tailscale status`) +- [ ] Alten SSH-Host-Key gesichert (falls möglich) +- [ ] Disk-IDs in Konfiguration geprüft/aktualisiert +- [ ] Installation durchgeführt +- [ ] SSH-Host-Key wiederhergestellt ODER neuen Key in SOPS konfiguriert +- [ ] Secrets funktionieren (`sudo cat /run/secrets/...`) +- [ ] Tailscale verbunden (`tailscale status`) diff --git a/docs/getting-started/sd-image.md b/docs/getting-started/sd-image.md index 2bde0d1..f6a5c70 100644 --- a/docs/getting-started/sd-image.md +++ b/docs/getting-started/sd-image.md @@ -1,95 +1,95 @@ -# SD Card Images for Raspberry Pi +# SD-Karten-Images für Raspberry Pi -The repository automatically builds SD card images for all configured Raspberry Pi hosts. +Das Repository baut automatisch SD-Karten-Images für alle konfigurierten Raspberry Pi Hosts. -## Automatic Build +## Automatischer Build -When changes are pushed to `main`, images are automatically built for all Pi hosts and published as a release. +Bei Änderungen an `main` werden automatisch Images für alle Pi-Hosts gebaut und als Release veröffentlicht. -**Download:** [Releases on Forgejo](https://git.cryodev.xyz/steffen/cryodev-server/releases) +**Download:** [Releases auf Forgejo](https://git.cryodev.xyz/steffen/cryodev-server/releases) -## Available Images +## Verfügbare Images -| Host | Image Name | +| Host | Image-Name | |------|------------| | `cryodev-pi` | `cryodev-pi-sd-image.img.zst` | -New hosts are built automatically once they are added to the workflow matrix. +Neue Hosts werden automatisch gebaut, wenn sie zur Workflow-Matrix hinzugefügt werden. -## Flashing the Image +## Image flashen -### 1. Download +### 1. Herunterladen ```bash wget https://git.cryodev.xyz/.../releases/latest/download/-sd-image.img.zst wget https://git.cryodev.xyz/.../releases/latest/download/-sd-image.img.zst.sha256 -# Verify checksum +# Checksum prüfen sha256sum -c -sd-image.img.zst.sha256 ``` -### 2. Decompress +### 2. Dekomprimieren ```bash zstd -d -sd-image.img.zst -o .img ``` -### 3. Write to SD Card +### 3. Auf SD-Karte schreiben ```bash -# Identify the correct device +# Richtiges Gerät finden lsblk -# Write (WARNING: make sure to select the correct device!) +# Schreiben (ACHTUNG: richtiges Gerät wählen!) sudo dd if=.img of=/dev/sdX bs=4M conv=fsync status=progress ``` -Alternatively, use `balenaEtcher` or `Raspberry Pi Imager`. +Alternativ: `balenaEtcher` oder `Raspberry Pi Imager` verwenden. -## What Is Included in the Image? +## Was ist im Image? -- Complete NixOS installation for the specific host -- All configured services (except secrets) -- SSH server enabled -- Automatic root partition expansion on first boot -- Comin for automatic updates +- Vollständige NixOS-Installation für den spezifischen Host +- Alle konfigurierten Services (außer Secrets) +- SSH-Server aktiviert +- Automatische Root-Partition-Erweiterung beim ersten Boot +- Comin für automatische Updates -## What Is Missing? +## Was fehlt? -**SOPS secrets** cannot be included in the image (chicken-and-egg problem with the SSH host key). +**SOPS-Secrets** können nicht im Image enthalten sein (Henne-Ei-Problem mit SSH-Host-Key). -After the first boot: -1. Retrieve the age key from the Pi -2. Update `.sops.yaml` -3. Create `secrets.yaml` -4. Deploy the configuration +Nach dem ersten Boot: +1. Age-Key vom Pi holen +2. `.sops.yaml` aktualisieren +3. `secrets.yaml` erstellen +4. Konfiguration deployen -See [Adding a New Client](new-client.md) for the complete guide. +Siehe [Neuen Client hinzufügen](new-client.md) für die vollständige Anleitung. -## Adding a New Host to the Pipeline +## Neuen Host zur Pipeline hinzufügen -1. Create the host configuration in `hosts//` -2. Add it to the matrix in `.forgejo/workflows/build-pi-image.yml`: +1. Host-Konfiguration in `hosts//` erstellen +2. In `.forgejo/workflows/build-pi-image.yml` zur Matrix hinzufügen: ```yaml matrix: - host: [cryodev-pi, new-host] # <- add here + host: [cryodev-pi, neuer-host] # <- hier hinzufügen ``` -3. Push to `main` -- the image will be built automatically +3. Push auf `main` → Image wird automatisch gebaut -## Building Manually +## Manuell bauen ```bash -# On aarch64 (e.g., another Pi) +# Auf aarch64 (z.B. anderem Pi) nix build .#nixosConfigurations..config.system.build.sdImage -# On x86_64 with QEMU emulation (slow) +# Auf x86_64 mit QEMU-Emulation (langsam) nix build .#nixosConfigurations..config.system.build.sdImage \ --extra-platforms aarch64-linux ``` -Prerequisite on x86_64: +Voraussetzung auf x86_64: ```nix { @@ -99,18 +99,18 @@ Prerequisite on x86_64: ## Troubleshooting -### Workflow Fails +### Workflow schlägt fehl -- Check whether `sd-image.nix` is imported in the host configuration -- Check whether binfmt is enabled on cryodev-main +- Prüfe ob `sd-image.nix` in der Host-Konfiguration importiert wird +- Prüfe ob binfmt auf cryodev-main aktiviert ist -### Image Does Not Boot +### Image bootet nicht -- Was the SD card written correctly? -- Try a different SD card -- Check the power supply (minimum 3A for Pi 4) +- SD-Karte korrekt beschrieben? +- Andere SD-Karte versuchen +- Stromversorgung prüfen (min. 3A für Pi 4) -### No Network +### Kein Netzwerk -- Check the Ethernet cable -- Is there a DHCP server on the network? +- Ethernet-Kabel prüfen +- DHCP-Server im Netzwerk? diff --git a/docs/index.md b/docs/index.md index e7b9258..c63bb6c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,33 +1,33 @@ # Cryodev NixOS Configuration Documentation -Welcome to the documentation for the **cryodev** NixOS infrastructure. +Willkommen zur Dokumentation der **cryodev** NixOS-Infrastruktur. ## Quick Links ### Getting Started -- [Prerequisites](getting-started/prerequisites.md) - Required tools -- [Adding a New Raspberry Pi](getting-started/new-client.md) - Complete workflow for new clients -- [SD Image Reference](getting-started/sd-image.md) - Details on image building -- [First Installation (Server)](getting-started/first-install.md) - Bootstrap for x86_64 hosts -- [Reinstallation](getting-started/reinstall.md) - Reinstall with hardware changes +- [Voraussetzungen](getting-started/prerequisites.md) - Benötigte Tools +- [Neuen Raspberry Pi hinzufügen](getting-started/new-client.md) - Kompletter Workflow für neue Clients +- [SD-Image Referenz](getting-started/sd-image.md) - Details zum Image-Build +- [Erstinstallation (Server)](getting-started/first-install.md) - Bootstrap für x86_64 Hosts +- [Neuinstallation](getting-started/reinstall.md) - Reinstall mit Hardware-Änderungen ### Services -- [SOPS Secrets](services/sops.md) - Secret management with sops-nix -- [Headscale](services/headscale.md) - Self-hosted Tailscale server -- [Headplane](services/headplane.md) - Web UI for Headscale -- [Tailscale](services/tailscale.md) - Mesh VPN client -- [Mailserver](services/mailserver.md) - Email stack (Postfix/Dovecot) -- [Forgejo](services/forgejo.md) - Git hosting with CI/CD -- [Netdata](services/netdata.md) - Monitoring and alerting +- [SOPS Secrets](services/sops.md) - Geheimnisverwaltung mit sops-nix +- [Headscale](services/headscale.md) - Self-hosted Tailscale Server +- [Headplane](services/headplane.md) - Web-UI für Headscale +- [Tailscale](services/tailscale.md) - Mesh-VPN Client +- [Mailserver](services/mailserver.md) - E-Mail Stack (Postfix/Dovecot) +- [Forgejo](services/forgejo.md) - Git-Hosting mit CI/CD +- [Netdata](services/netdata.md) - Monitoring und Alerting ### Deployment -- [Continuous Deployment](deployment/cd.md) - Push- and pull-based deployment -- [DNS Configuration](deployment/dns.md) - Required DNS records +- [Continuous Deployment](deployment/cd.md) - Push- und Pull-basiertes Deployment +- [DNS-Konfiguration](deployment/dns.md) - Benötigte DNS-Einträge -## Architecture +## Architektur ``` Internet @@ -57,38 +57,38 @@ Welcome to the documentation for the **cryodev** NixOS infrastructure. +-------------------+ ``` -## Installation Scenarios +## Installations-Szenarien -| Scenario | Description | Guide | -|----------|-------------|-------| -| **New Raspberry Pi** | Create config, build image, flash | [new-client.md](getting-started/new-client.md) | -| **First Installation (Server)** | x86_64 host, manual installation | [first-install.md](getting-started/first-install.md) | -| **Reinstallation** | Existing host, new hardware | [reinstall.md](getting-started/reinstall.md) | +| Szenario | Beschreibung | Anleitung | +|----------|--------------|-----------| +| **Neuer Raspberry Pi** | Config erstellen → Image bauen → Flashen | [new-client.md](getting-started/new-client.md) | +| **Erstinstallation (Server)** | x86_64 Host, manuelle Installation | [first-install.md](getting-started/first-install.md) | +| **Neuinstallation** | Bestehender Host, neue Hardware | [reinstall.md](getting-started/reinstall.md) | -For Raspberry Pi: [SD Image Reference](getting-started/sd-image.md) +Für Raspberry Pi: [SD-Image Referenz](getting-started/sd-image.md) -## Directory Structure +## Verzeichnisstruktur ``` . ├── flake.nix # Entry point, inputs and outputs -├── constants.nix # Central configuration (domains, IPs, ports) -├── hosts/ # Host-specific configurations +├── constants.nix # Zentrale Config (Domains, IPs, Ports) +├── hosts/ # Host-spezifische Konfigurationen │ ├── cryodev-main/ │ └── cryodev-pi/ -├── modules/ # Reusable NixOS modules +├── modules/ # Wiederverwendbare NixOS-Module │ └── nixos/ -├── pkgs/ # Custom packages -├── overlays/ # Nixpkgs overlays -├── templates/ # Templates for new hosts -├── scripts/ # Helper scripts (install.sh) -├── apps/ # Nix apps (rebuild) -└── lib/ # Helper functions (utils.nix) +├── pkgs/ # Eigene Pakete +├── overlays/ # Nixpkgs Overlays +├── templates/ # Templates für neue Hosts +├── scripts/ # Helper-Scripts (install.sh) +├── apps/ # Nix Apps (rebuild) +└── lib/ # Helper-Funktionen (utils.nix) ``` -## Deployment Strategies +## Deployment-Strategien -| Host | Strategy | Tool | Description | -|------|----------|------|-------------| -| `cryodev-main` | Pull-based | Comin | Polls the repository for changes | -| `cryodev-pi` | Pull-based | Comin | Polls the repository for changes | +| Host | Strategie | Tool | Beschreibung | +|------|-----------|------|--------------| +| `cryodev-main` | Pull-basiert | Comin | Pollt Repository auf Aenderungen | +| `cryodev-pi` | Pull-basiert | Comin | Pollt Repository auf Aenderungen | diff --git a/docs/services/forgejo.md b/docs/services/forgejo.md index 7f1191f..5955405 100644 --- a/docs/services/forgejo.md +++ b/docs/services/forgejo.md @@ -89,9 +89,9 @@ See [CD documentation](../deployment/cd.md) for details. ```bash forgejo admin user create \ - --username \ + --username \ --email @ \ - --password \ + --password \ --admin ``` diff --git a/docs/services/tailscale.md b/docs/services/tailscale.md index fea4252..768a21c 100644 --- a/docs/services/tailscale.md +++ b/docs/services/tailscale.md @@ -14,9 +14,9 @@ Tailscale clients connect to the self-hosted Headscale server to join the mesh V On the Headscale server (cryodev-main): ```bash -# Look up user ID +# User-ID ermitteln sudo headscale users list -# Create preauth key (use the user ID for "default") +# Preauth-Key erstellen (User-ID von "default" einsetzen) sudo headscale preauthkeys create --expiration 99y --reusable --user ```