diff --git a/.agent/rules/ci-cd-networking-constraints.md b/.agent/rules/ci-cd-networking-constraints.md new file mode 100644 index 0000000..89c1866 --- /dev/null +++ b/.agent/rules/ci-cd-networking-constraints.md @@ -0,0 +1,13 @@ +--- +name: cicd-networking +description: Networking constraints for CI/CD workflow files (Gitea/GitHub Actions). +globs: [".github/workflows/.yml", ".github/workflows/.yaml", ".gitea/workflows/.yml", ".gitea/workflows/.yaml"] +--- + +# Bos55 CI/CD Networking Constraints + +When generating or modifying CI/CD workflows, strictly follow these networking practices: + +1. **IP-Based Login for Reliability** + - When CI runners (like Gitea Actions) need to interact with internal services for authentication or deployment, always use direct IP addresses (e.g., `192.168.0.25`) for machine-to-machine login steps. + - **Why?** This bypasses potential DNS resolution issues or delays within the isolated runner environment, ensuring maximum robustness during automated CI/CD runs. diff --git a/.agent/rules/dns-management.md b/.agent/rules/dns-management.md new file mode 100644 index 0000000..e8e6a7b --- /dev/null +++ b/.agent/rules/dns-management.md @@ -0,0 +1,14 @@ +--- +name: dns-management +description: Hard constraints for modifying Bind9 DNS zone files. +globs: ["db.", ".zone"] +--- + +# Bos55 DNS Management Constraints + +When modifying or generating Bind9 zone files, you MUST strictly adhere to the following rules: + +1. **Serial Increment (CRITICAL)** + - Every single time you modify a Bind9 zone file (e.g., `db.depeuter.dev`), you MUST increment the Serial number in the SOA record. Failure to do so will cause DNS propagation to fail. +2. **Domain Name Specificity** + - Prefer a single, well-defined explicit domain (e.g., `nix-cache.depeuter.dev`) instead of creating multiple aliases or using magic values. Keep records clean and explicit. diff --git a/.agent/rules/git-workflow.md b/.agent/rules/git-workflow.md new file mode 100644 index 0000000..6d41ee2 --- /dev/null +++ b/.agent/rules/git-workflow.md @@ -0,0 +1,21 @@ +--- +name: git-workflow +description: Rules for generating Git commit messages and managing branch workflows. +globs: ["COMMIT_EDITMSG", ".git/*"] +--- + +# Git Workflow Constraints + +When generating commit messages, reviewing code for a commit, or planning a branch workflow, strictly follow these standards: + +1. **Commit Formatting** + - **Conventional Commits**: You MUST format all commit messages using conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `ci:`, `meta:`. + - **Clarity**: Ensure the message clearly explains *what* changed and *why*. +2. **Atomic Commits** + - Group changes by a single logical concern. + - NEVER mix documentation updates, core infrastructure code, and style guide changes in the same commit. + - Ensure that the generated commit is easily revertible without breaking unrelated features. +3. **Branching Workflow** + - Always assume changes will be pushed to a feature branch to create a Pull Request. + - Do not suggest or generate commands that push directly to the main branch. + diff --git a/.agent/skills/nixos-architecture/SKILL.md b/.agent/skills/nixos-architecture/SKILL.md new file mode 100644 index 0000000..2eb63bf --- /dev/null +++ b/.agent/skills/nixos-architecture/SKILL.md @@ -0,0 +1,47 @@ +--- +name: bos55-nix-architecture +description: Implementation patterns for NixOS configurations, networking, and service modules. +globs: [".nix", "hosts/**/", "modules//*", "secrets//*"] +--- + +# NixOS Architecture Skill + +When generating or modifying NixOS configuration files for the Bos55 project, strictly adhere to the following architectural patterns: + +## 1. Minimal Hardcoding & Dynamic Discovery + +- **Local IP Ownership**: Define IPv4/IPv6 addresses **only** within their respective host configuration files (e.g., `hosts//default.nix`). Do not use global IP mapping modules. +- **Inter-Host Discovery**: Resolve a host's IP or port by evaluating its configuration at build time. Never hardcode another host's IP. + **Pattern Example**: + ``` + let + bcConfig = inputs.self.nixosConfigurations.BinaryCache.config; + bcIp = (pkgs.lib.head bcConfig.networking.interfaces.ens18.ipv4.addresses).address; + in "http://${bcIp}:8080" + ``` +- **Unified Variables**: Use local variables (e.g., `let dbName = "attic"; in ...`) for shared values between host services and containers to ensure consistency. + +## 2. Modular Service Encapsulation + +- **Self-Contained Modules**: Service modules (`modules/services//default.nix`) must manage their own configurations. Prefer `lib.mkOption` over hardcoded strings for domains, ports, and credentials. +- **Firewall Responsibility**: Open ports (e.g., TCP 8080, SSH 22) directly within the service module based on its own options. Do not open service ports manually in host files. +- **Remote Builders**: Define `nix.settings.trusted-users`, `builder` user, and SSH rules directly within the service module if it supports remote building (e.g., Attic). + +## 3. Networking & Connectivity + +- **Container-to-Host**: Host services must connect to companion containers using the container name, not the bridge IP or `localhost`. +- **Host Resolution**: Map the container name to `127.0.0.1` using `networking.extraHosts` in the host service module to route traffic seamlessly. +- **Domain Deferral**: Client modules must defer their default domain settings to the server module's defined domain option. + +## 4. Secrets Management + +- **Sops-Nix Exclusivity**: Manage all secrets via `sops-nix`. +- **Centralized Config**: Rely on `modules/common/default.nix` for fleet-wide settings like `defaultSopsFile` and `age.keyFile`. +- **References**: Always reference credentials dynamically using `config.sops.secrets."path/to/secret".path`. + +## 5. Security & Documentation + +- **Supply Chain Protection**: Always verify and lock Nix flake inputs. Use fixed-output derivations for external resource downloads. +- **Assumptions Documentation**: Clearly document environment assumptions (e.g., Proxmox virtualization, Tailscale networking, and specific IP ranges) in host or service READMEs. +- **Project Structure**: Maintain the strict separation of `hosts/`, `modules/`, `users/`, and `secrets/` to ensure clear ownership and security boundaries. + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0236ced..74d6457 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,40 +1,43 @@ -name: Build - +name: "Build" on: - push: - branches: - - main - - 'test-*' pull_request: + push: jobs: - # Job to find all hosts that should be built - get-hosts: + determine-hosts: + name: "Determining hosts to build" runs-on: ubuntu-latest container: catthehacker/ubuntu:act-24.04 outputs: - hosts: ${{ steps.set-hosts.outputs.hosts }} + hosts: ${{ steps.hosts.outputs.hostnames }} steps: - - uses: actions/checkout@v4 - - name: Install Nix - uses: cachix/install-nix-action@v27 - - id: set-hosts + - uses: actions/checkout@v5 + - uses: https://github.com/cachix/install-nix-action@v31 + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: "Determine hosts" + id: hosts run: | - # Extract host names from nixosConfigurations - HOSTS=$(nix eval .#nixosConfigurations --apply "builtins.attrNames" --json) - echo "hosts=$HOSTS" >> $GITHUB_OUTPUT + hostnames="$(nix eval .#nixosConfigurations --apply builtins.attrNames --json)" + printf "hostnames=%s\n" "${hostnames}" >> "${GITHUB_OUTPUT}" build: - needs: get-hosts runs-on: ubuntu-latest container: catthehacker/ubuntu:act-24.04 + needs: determine-hosts strategy: - fail-fast: false matrix: - host: ${{ fromJson(needs.get-hosts.outputs.hosts) }} + hostname: [ + Development, + Testing + ] + steps: - - uses: actions/checkout@v4 - - name: Install Nix - uses: cachix/install-nix-action@v27 - - name: Build NixOS configuration - run: nix build .#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel + - uses: actions/checkout@v5 + - uses: https://github.com/cachix/install-nix-action@v31 + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: "Build host" + run: | + nix build ".#nixosConfigurations.${{ matrix.hostname }}.config.system.build.toplevel" --verbose + diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml deleted file mode 100644 index 4cc892e..0000000 --- a/.github/workflows/check.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Check - -on: - push: - branches: - - '**' - pull_request: - -jobs: - check: - runs-on: ubuntu-latest - container: catthehacker/ubuntu:act-24.04 - steps: - - uses: actions/checkout@v4 - - - name: Install Nix - uses: cachix/install-nix-action@v27 - with: - extra_nix_config: | - experimental-features = nix-command flakes - access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - - - name: Flake check - run: nix flake check diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index a037a7a..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Deploy - -on: - push: - branches: - - main - - 'test-*' - workflow_dispatch: - inputs: - mode: - description: 'Activation mode (switch, boot, test)' - default: 'switch' - required: true - -jobs: - deploy: - runs-on: ubuntu-latest - container: catthehacker/ubuntu:act-24.04 - steps: - - uses: actions/checkout@v4 - - - name: Install Nix - uses: cachix/install-nix-action@v27 - with: - extra_nix_config: | - experimental-features = nix-command flakes - - - name: Setup SSH - run: | - mkdir -p ~/.ssh - echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh-keyscan -H 192.168.0.0/24 >> ~/.ssh/known_hosts || true - # Disable strict host key checking for the local network if needed, - # or rely on known_hosts. For homelab, we can be slightly more relaxed - # but let's try to be secure. - echo "StrictHostKeyChecking no" >> ~/.ssh/config - - - name: Verify Commit Signature - if: github.event.sender.login != 'renovate[bot]' - run: | - # TODO Hugo: Export your public GPG/SSH signing keys to a runner secret named 'TRUSTED_SIGNERS'. - # For GPG: gpg --export --armor | base64 -w0 - - if [ -z "${{ secrets.TRUSTED_SIGNERS }}" ]; then - echo "::error::TRUSTED_SIGNERS secret is missing. Deployment aborted for safety." - exit 1 - fi - - # Implementation note: This step expects a keyring in the TRUSTED_SIGNERS secret. - # We use git to verify the signature of the current commit. - echo "${{ secrets.TRUSTED_SIGNERS }}" | base64 -d > /tmp/trusted_keys.gpg - gpg --import /tmp/trusted_keys.gpg - - if ! git verify-commit HEAD; then - echo "::error::Commit signature verification failed. Only signed commits from trusted maintainers can be deployed." - exit 1 - fi - echo "Commit signature verified successfully." - - - name: Install deploy-rs - run: nix profile install github:serokell/deploy-rs - - - name: Deploy to hosts - run: | - # Determine profile based on branch - if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then - # Main site: persistent deployment - deploy . --skip-checks --targets $(deploy . --list | grep '.system$' | tr '\n' ' ') - elif [[ "${{ github.ref }}" == "refs/heads/test-"* ]]; then - # Test branch: non-persistent deployment (test profile) - # The branch name should be test- - HOSTNAME="${GITHUB_REF#refs/heads/test-}" - deploy .#${HOSTNAME}.test --skip-checks - fi - - - name: Manual Deploy - if: github.event_name == 'workflow_dispatch' - run: | - # TODO: Implement manual dispatch logic if needed - deploy . --skip-checks diff --git a/README.md b/README.md deleted file mode 100644 index 19f9892..0000000 --- a/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Bos55 NixOS Config - -Automated CI/CD deployment for NixOS homelab using `deploy-rs`. - -## Repository Structure - -- `hosts/`: Host-specific configurations. -- `modules/`: Shared NixOS modules. -- `users/`: User definitions (including the `deploy` user). -- `secrets/`: Encrypted secrets via `sops-nix`. - -## Deployment Workflow - -### Prerequisites -- SSH access to the `deploy` user on target hosts. -- `deploy-rs` installed locally (`nix profile install github:serokell/deploy-rs`). - -### Deployment Modes - -1. **Production Deployment (main branch):** - Triggered on push to `main`. Automatically builds and switches all hosts. bootloader is updated. - Manual: `deploy .` - -2. **Test Deployment (test- branch):** - Triggered on push to `test-`. Builds and activates the configuration on the specific host **without** updating the bootloader. Reboots will revert to the previous generation. - Manual: `deploy .#.test` - -3. **Kernel Upgrades / Maintenance:** - Use `deploy .#.system --boot` to update the bootloader without immediate activation, followed by a manual reboot. - -## Local Development - -### 1. Developer Shell -This repository includes a standardized development environment containing all necessary tools (`deploy-rs`, `sops`, `age`, etc.). -```bash -nix develop -# or if using direnv -direnv allow -``` - -### 2. Build a host VM -You can build a QEMU VM for any host configuration to test changes locally: -```bash -nix build .#nixosConfigurations..config.system.build.vm -./result/bin/run--vm -``` - -> [!WARNING] -> **Network Conflict**: Default VMs use user-mode networking (NAT) which is safe. However, if you configure the VM to use bridge networking, it will attempt to use the static IP defined in `hostIp`. Ensure you do not have a physical host with that IP active on the same bridge to avoid network interference. - -### 3. Run Integration Tests -Run the automated test suite: -```bash -nix-build test/vm-test.nix -``` - -### 3. Test CI Workflows Locally -Use `act` to test the GitHub Actions workflows: -```bash -act -W .github/workflows/check.yml -``` - -## Security -See [SECURITY.md](SECURITY.md) for details on the trust model and secret management. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index ca1c208..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,93 +0,0 @@ -# Security and Trust Model - -This document outlines the security architecture, trust boundaries, and assumptions of the Bos55 NixOS deployment pipeline. This model is designed to support a multi-member infrastructure team and remains secure even if the repository is published publicly. - -## Trust Zones - -The system is partitioned into three distinct trust zones, each with specific controls to prevent lateral movement and privilege escalation. - -### 🔴 Zone 1: Trusted Maintainers (Source of Truth) -* **Actors:** Infrastructure Team / Maintainers. -* **Capabilities:** - * Full access to the Git repository. - * Ownership of `sops-nix` master keys (GPG or Age). - * Direct root access to NixOS hosts via personal SSH keys for emergency maintenance. -* **Trust:** Root of trust. All changes must originate from or be approved by a Trusted Maintainer. -* **Security Controls:** - * **Signed Commits:** All contributions must be cryptographically signed by a trusted GPG/SSH key to be eligible for deployment. - - **MFA:** Hardware-based multi-factor authentication for repository access. - - **Metadata Redaction:** Sensitive identifiers like SSH `authorizedKeys` are stored in `sops-nix`. This prevents **infrastructure fingerprinting**, where an attacker could link your public keys to your personal identities or other projects. - -### 🟡 Zone 2: CI/CD Pipeline (Automation Layer) -* **Actor:** GitHub Actions / Forgejo Runners. -* **Capabilities:** - * Builds Nix derivations from the repository. - * Access to the `DEPLOY_SSH_KEY` (allowing SSH access to the `deploy` user on target hosts). - * **Trusted Signers:** The public keys for verifying signatures are stored as a **Runner Secret** (`TRUSTED_SIGNERS`). This hides the identities of the infrastructure team even in a public repository. - * **NO ACCESS** to `sops-nix` decryption keys. Secrets remain encrypted during the build. -* **Security Controls:** - * **Signature Enforcement:** The `deploy.yml` workflow verifies the cryptographic signature of every maintainer commit. Deployment is aborted if the signature is missing or untrusted. - * **Sandboxing:** Runners execute in ephemeral, isolated containers. - * **Branch Protection:** Deployments to production (`main`) require approved Pull Requests. - * **Fork Protection:** CI workflows (and secrets) are explicitly disabled for forks. - -### 🟢 Zone 3: Target NixOS Hosts (Runtime) -* **Actor:** Production, Testing, and Service nodes. -* **Capabilities:** Decrypt secrets locally using host-specific `age` keys. -* **Trust:** Consumers of builds. They trust Zone 2 only for the pushing of store paths and triggering activation scripts. -* **Security Controls:** - * **Restricted `deploy` User:** The SSH user for automation is non-root. Sudo access is strictly policed via `sudoers` rules to allow only `nix-env` and `switch-to-configuration`. - * **Immutable Store:** Building on Nix ensures that the system state is derived from a cryptographically hashed store, preventing unauthorized local modifications from persisting across reboots. - ---- - -## Security Assumptions & Policies - -### 1. Public Repository Safety -The repository is designed to be safe for public viewing. No unencrypted secrets should ever be committed. The deployment pipeline is protected against "malicious contributors" via: -- **Mandatory PR Reviews:** No code can reach the `main` branch without peer review. -- **Secret Scoping:** Deployment keys are only available to authorized runs on protected branches. - -### 2. Supply Chain & Dependencies -- **Flake Lockfiles:** All dependencies (Nixpkgs, `deploy-rs`, etc.) are pinned to specific git revisions. -- **Renovate Bot:** Automated version upgrades allow for consistent tracking of upstream changes, though they require manual review or successful status checks for minor/patch versions. - -### 3. Signed Commit Enforcement -To prevent "force-push" attacks or runner compromises from injecting malicious code into the history, the pipeline should be configured to only deploy commits signed by a known "Trusted Maintainer" key. This ensures that even if a git account is compromised, the attacker cannot deploy code without the physical/cryptographic signing key. - ---- - -## Trust Boundary Diagram - -```mermaid -graph TD - subgraph "Zone 1: Trusted Workstations" - DEV["Maintainers (Team)"] - SOPS_KEYS["Master SOPS Keys"] - SIGN_KEYS["Signing Keys (GPG/SSH)"] - end - - subgraph "Zone 2: CI/CD Runner (Sandboxed)" - CI["Automated Runner"] - SSH_KEY["Deploy SSH Key"] - end - - subgraph "Zone 3: NixOS Target Hosts" - HOST["Target Host"] - HOST_AGE["Host Age Key"] - end - - DEV -- "Signed Push / PR" --> CI - CI -- "Push Store Paths & Activate" --> HOST - HOST_AGE -- "Local Decrypt" --> HOST - - style DEV fill:#f96,stroke:#333 - style CI fill:#ff9,stroke:#333 - style HOST fill:#9f9,stroke:#333 -``` - -## Security Best Practices for Maintainers - -1. **Keep Master Keys Offline:** Never store `sops-nix` master keys on the CI runner or public servers. -2. **Audit Runner Logs:** Periodically review CI execution logs for unexpected behavior. -3. **Rotate Deployment Keys:** Rotate the `DEPLOY_SSH_KEY` if maintainer membership changes significantly. diff --git a/flake.nix b/flake.nix index 7e7e68c..446f4ce 100644 --- a/flake.nix +++ b/flake.nix @@ -13,85 +13,52 @@ url = "github:gytis-ivaskevicius/flake-utils-plus"; inputs.flake-utils.follows = "flake-utils"; }; - deploy-rs = { - url = "github:serokell/deploy-rs"; - inputs.nixpkgs.follows = "nixpkgs"; - }; }; outputs = inputs@{ self, nixpkgs, - flake-utils, sops-nix, utils, deploy-rs, + flake-utils, sops-nix, utils, ... }: let system = utils.lib.system.x86_64-linux; - lib = nixpkgs.lib; in - utils.lib.mkFlake { - inherit self inputs; + utils.lib.mkFlake { + inherit self inputs; - hostDefaults.modules = [ + hostDefaults = { + inherit system; + + modules = [ ./modules ./users + sops-nix.nixosModules.sops - ({ self, ... }: { - sops.defaultSopsFile = "${self}/secrets/secrets.yaml"; - sops.age.keyFile = "/var/lib/sops-nix/key.txt"; - }) - ({ self, ... }: { - sops.defaultSopsFile = "${self}/secrets/secrets.yaml"; - sops.age.keyFile = "/var/lib/sops-nix/key.txt"; - }) ]; - - hosts = { - # Infrastructure - Niko.modules = [ ./hosts/Niko ]; - Ingress.modules = [ ./hosts/Ingress ]; - Gitea.modules = [ ./hosts/Gitea ]; - Vaultwarden.modules = [ ./hosts/Vaultwarden ]; - - # Production - Binnenpost.modules = [ ./hosts/Binnenpost ]; - Production.modules = [ ./hosts/Production ]; - ProductionGPU.modules = [ ./hosts/ProductionGPU ]; - ProductionArr.modules = [ ./hosts/ProductionArr ]; - ACE.modules = [ ./hosts/ACE ]; - - # Lab - Template.modules = [ ./hosts/Template ]; - Development.modules = [ ./hosts/Development ]; - Testing.modules = [ ./hosts/Testing ]; - }; - - deploy.nodes = let - pkg = deploy-rs.lib.${system}; - isDeployable = nixos: (nixos.config.homelab.users.deploy.enable or false) && (nixos.config.homelab.networking.hostIp != null); - in - builtins.mapAttrs (_: nixos: { - hostname = nixos.config.homelab.networking.hostIp; - sshUser = "deploy"; - user = "root"; - profiles.system.path = pkg.activate.nixos nixos; - profiles.test.path = pkg.activate.custom nixos.config.system.build.toplevel '' - $PROFILE/bin/switch-to-configuration test - ''; - }) (lib.filterAttrs (_: isDeployable) self.nixosConfigurations); - - checks = builtins.mapAttrs (_: lib: lib.deployChecks self.deploy) deploy-rs.lib; - - outputsBuilder = channels: { - formatter = channels.nixpkgs.alejandra; - devShells.default = channels.nixpkgs.mkShell { - name = "homelab-dev"; - buildInputs = [ - deploy-rs.packages.${system}.deploy-rs - channels.nixpkgs.sops - channels.nixpkgs.age - ]; - shellHook = "echo '🛡️ Homelab Development Shell Loaded'"; - }; - }; }; + + hosts = { + # Physical hosts + Niko.modules = [ ./hosts/Niko ]; + + # Virtual machines + + # Single-service + Ingress.modules = [ ./hosts/Ingress ]; + Gitea.modules = [ ./hosts/Gitea ]; + Vaultwarden.modules = [ ./hosts/Vaultwarden ]; + + # Production multi-service + Binnenpost.modules = [ ./hosts/Binnenpost ]; + Production.modules = [ ./hosts/Production ]; + ProductionGPU.modules = [ ./hosts/ProductionGPU ]; + ProductionArr.modules = [ ./hosts/ProductionArr ]; + ACE.modules = [ ./hosts/ACE ]; + + # Others + Template.modules = [ ./hosts/Template ]; + Development.modules = [ ./hosts/Development ]; + Testing.modules = [ ./hosts/Testing ]; + }; + }; } diff --git a/hosts/ACE/default.nix b/hosts/ACE/default.nix index 094b077..04aa284 100644 --- a/hosts/ACE/default.nix +++ b/hosts/ACE/default.nix @@ -1,12 +1,10 @@ -{ config, pkgs, ... }: +{ pkgs, ... }: { config = { homelab = { - networking.hostIp = "192.168.0.41"; services.actions.enable = true; virtualisation.guest.enable = true; - users.deploy.enable = true; }; networking = { @@ -26,7 +24,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.41"; prefixLength = 24; } ]; diff --git a/hosts/Binnenpost/default.nix b/hosts/Binnenpost/default.nix index 624608b..561fbe1 100644 --- a/hosts/Binnenpost/default.nix +++ b/hosts/Binnenpost/default.nix @@ -1,4 +1,4 @@ -{ config, pkgs, ... }: +{ pkgs, ... }: { config = { @@ -13,14 +13,12 @@ }; homelab = { - networking.hostIp = "192.168.0.89"; apps = { speedtest.enable = true; technitiumDNS.enable = true; traefik.enable = true; }; virtualisation.guest.enable = true; - users.deploy.enable = true; }; networking = { @@ -45,7 +43,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.89"; prefixLength = 24; } ]; diff --git a/hosts/Development/default.nix b/hosts/Development/default.nix index 68c5fea..fda8e57 100644 --- a/hosts/Development/default.nix +++ b/hosts/Development/default.nix @@ -3,7 +3,6 @@ { config = { homelab = { - networking.hostIp = "192.168.0.91"; apps = { bind9.enable = true; homepage = { @@ -14,7 +13,6 @@ plex.enable = true; }; virtualisation.guest.enable = true; - users.deploy.enable = true; }; networking = { @@ -38,7 +36,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.91"; prefixLength = 24; } ]; @@ -61,8 +59,7 @@ environment = { # NOTE Required # The email address used when setting up the initial administrator account to login to pgAdmin. - # TODO Hugo: Populate 'pgadmin_email' in sops. - PGADMIN_DEFAULT_EMAIL = config.sops.placeholder.pgadmin_email or "pgadmin-admin@example.com"; + PGADMIN_DEFAULT_EMAIL = "kmtl.hugo+pgadmin@gmail.com"; # NOTE Required # The password used when setting up the initial administrator account to login to pgAdmin. PGADMIN_DEFAULT_PASSWORD = "ChangeMe"; diff --git a/hosts/Gitea/default.nix b/hosts/Gitea/default.nix index d6996b2..c6c9b43 100644 --- a/hosts/Gitea/default.nix +++ b/hosts/Gitea/default.nix @@ -3,12 +3,9 @@ { config = { homelab = { - networking.hostIp = "192.168.0.24"; apps.gitea.enable = true; virtualisation.guest.enable = true; - users.deploy.enable = true; - users.admin = { enable = true; authorizedKeys = [ @@ -31,7 +28,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.24"; prefixLength = 24; } ]; diff --git a/hosts/Ingress/default.nix b/hosts/Ingress/default.nix index c16f151..c0a3ac9 100644 --- a/hosts/Ingress/default.nix +++ b/hosts/Ingress/default.nix @@ -2,11 +2,7 @@ { config = { - homelab = { - networking.hostIp = "192.168.0.10"; - virtualisation.guest.enable = true; - users.deploy.enable = true; - }; + homelab.virtualisation.guest.enable = true; networking = { hostName = "Ingress"; @@ -23,8 +19,8 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; - prefixLength = 24; + address = "192.168.0.10"; +prefixLength = 24; } ]; }; @@ -43,7 +39,6 @@ }; }; - security.acme = { acceptTerms = true; defaults = { @@ -51,7 +46,7 @@ dnsPropagationCheck = true; dnsProvider = "cloudflare"; dnsResolver = "1.1.1.1:53"; - email = config.sops.placeholder.acme_email or "acme-email@example.com"; + email = "tibo.depeuter@telenet.be"; credentialFiles = { CLOUDFLARE_DNS_API_TOKEN_FILE = "/var/lib/secrets/depeuter-dev-cloudflare-api-token"; }; diff --git a/hosts/Isabel/default.nix b/hosts/Isabel/default.nix index f275b0e..0a1f50f 100644 --- a/hosts/Isabel/default.nix +++ b/hosts/Isabel/default.nix @@ -165,7 +165,7 @@ providers: # Certificates "--certificatesresolvers.letsencrypt.acme.dnschallenge=true" "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare" - "--certificatesresolvers.letsencrypt.acme.email=${config.sops.placeholder.acme_email or "acme-email@example.com"}" + "--certificatesresolvers.letsencrypt.acme.email=tibo.depeuter@telenet.be" "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" # Additional routes @@ -176,8 +176,8 @@ providers: # "8080:8080/tcp" # The Web UI (enabled by --api.insecure=true) ]; environment = { - # TODO Hugo: Populate 'cloudflare_dns_token' in sops. - "CLOUDFLARE_DNS_API_TOKEN" = config.sops.placeholder.cloudflare_dns_token or "CLOUDFLARE_TOKEN_PLACEHOLDER"; + # TODO Hide this! + "CLOUDFLARE_DNS_API_TOKEN" = "6Vz64Op_a6Ls1ljGeBxFoOVfQ-yB-svRbf6OyPv2"; }; environmentFiles = [ ]; diff --git a/hosts/Niko/default.nix b/hosts/Niko/default.nix index c08ccdc..910f325 100644 --- a/hosts/Niko/default.nix +++ b/hosts/Niko/default.nix @@ -7,7 +7,6 @@ ]; homelab = { - networking.hostIp = "192.168.0.11"; apps = { technitiumDNS.enable = true; traefik.enable = true; diff --git a/hosts/Production/default.nix b/hosts/Production/default.nix index a4ebc75..9bb565d 100644 --- a/hosts/Production/default.nix +++ b/hosts/Production/default.nix @@ -3,13 +3,11 @@ { config = { homelab = { - networking.hostIp = "192.168.0.31"; apps = { calibre.enable = true; traefik.enable = true; }; virtualisation.guest.enable = true; - users.deploy.enable = true; }; networking = { @@ -33,7 +31,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.31"; prefixLength = 24; } ]; diff --git a/hosts/ProductionArr/default.nix b/hosts/ProductionArr/default.nix index 1168bc8..ff4f4c2 100644 --- a/hosts/ProductionArr/default.nix +++ b/hosts/ProductionArr/default.nix @@ -3,13 +3,11 @@ { config = { homelab = { - networking.hostIp = "192.168.0.33"; apps = { arr.enable = true; traefik.enable = true; }; virtualisation.guest.enable = true; - users.deploy.enable = true; }; networking = { @@ -33,7 +31,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.33"; prefixLength = 24; } ]; diff --git a/hosts/ProductionGPU/default.nix b/hosts/ProductionGPU/default.nix index 5f8ad82..fa9ca8c 100644 --- a/hosts/ProductionGPU/default.nix +++ b/hosts/ProductionGPU/default.nix @@ -3,10 +3,8 @@ { config = { homelab = { - networking.hostIp = "192.168.0.94"; apps.jellyfin.enable = true; virtualisation.guest.enable = true; - users.deploy.enable = true; }; networking = { @@ -30,7 +28,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.94"; prefixLength = 24; } ]; diff --git a/hosts/Testing/default.nix b/hosts/Testing/default.nix index cc4efcf..cc353f6 100644 --- a/hosts/Testing/default.nix +++ b/hosts/Testing/default.nix @@ -3,13 +3,11 @@ { config = { homelab = { - networking.hostIp = "192.168.0.92"; apps = { freshrss.enable = true; traefik.enable = true; }; virtualisation.guest.enable = true; - users.deploy.enable = true; }; networking = { @@ -34,7 +32,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.92"; prefixLength = 24; } ]; diff --git a/hosts/Vaultwarden/default.nix b/hosts/Vaultwarden/default.nix index b24ef6d..5ded575 100644 --- a/hosts/Vaultwarden/default.nix +++ b/hosts/Vaultwarden/default.nix @@ -3,7 +3,6 @@ { config = { homelab = { - networking.hostIp = "192.168.0.22"; apps.vaultwarden = { enable = true; domain = "https://vault.depeuter.dev"; @@ -11,15 +10,11 @@ }; virtualisation.guest.enable = true; - users = { - deploy.enable = true; - - admin = { - enable = true; - authorizedKeys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJnihoyozOCnm6T9OzL2xoMeMZckBYR2w43us68ABA93" - ]; - }; + users.admin = { + enable = true; + authorizedKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJnihoyozOCnm6T9OzL2xoMeMZckBYR2w43us68ABA93" + ]; }; }; @@ -37,7 +32,7 @@ interfaces.ens18 = { ipv4.addresses = [ { - address = config.homelab.networking.hostIp; + address = "192.168.0.22"; prefixLength = 24; } ]; diff --git a/modules/apps/gitea/default.nix b/modules/apps/gitea/default.nix index 34d52e4..0361bd5 100644 --- a/modules/apps/gitea/default.nix +++ b/modules/apps/gitea/default.nix @@ -496,8 +496,7 @@ in { #FORGEJO__mailer__CLIENT_KEY_FILE = "custom/mailer/key.pem"; # Mail from address, RFC 5322. This can be just an email address, or the # `"Name" ` format. - # TODO Hugo: Populate 'gitea_mailer_from' in sops. - FORGEJO__mailer__FROM = config.sops.placeholder.gitea_mailer_from or "git@example.com"; + FORGEJO__mailer__FROM = ''"${title}" ''; # Sometimes it is helpful to use a different address on the envelope. Set this to use # ENVELOPE_FROM as the from on the envelope. Set to `<>` to send an empty address. #FORGEJO__mailer__ENVELOPE_FROM = ""; diff --git a/modules/apps/traefik/default.nix b/modules/apps/traefik/default.nix index 54588c1..7f6ce38 100644 --- a/modules/apps/traefik/default.nix +++ b/modules/apps/traefik/default.nix @@ -72,7 +72,7 @@ in { # Certificates "--certificatesresolvers.letsencrypt.acme.dnschallenge=true" "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare" - "--certificatesresolvers.letsencrypt.acme.email=${config.sops.placeholder.acme_email or "acme-email@example.com"}" + "--certificatesresolvers.letsencrypt.acme.email=tibo.depeuter@telenet.be" "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" ]; volumes = [ diff --git a/modules/apps/vaultwarden/default.nix b/modules/apps/vaultwarden/default.nix index 6c55d0a..907dda4 100644 --- a/modules/apps/vaultwarden/default.nix +++ b/modules/apps/vaultwarden/default.nix @@ -344,7 +344,6 @@ in { # ORG_CREATION_USERS=none ## A comma-separated list means only those users can create orgs: # ORG_CREATION_USERS=admin1@example.com,admin2@example.com - # TODO Hugo: Redact org creation users if needed. ## Invitations org admins to invite users, even when signups are disabled # INVITATIONS_ALLOWED=true @@ -591,7 +590,7 @@ in { ## To make sure the email links are pointing to the correct host, set the DOMAIN variable. ## Note: if SMTP_USERNAME is specified, SMTP_PASSWORD is mandatory SMTP_HOST = "smtp.gmail.com"; - SMTP_FROM = config.sops.placeholder.vaultwarden_smtp_from or "vaultwarden@example.com"; + SMTP_FROM = "vault@depeuter.dev"; SMTP_FROM_NAME = cfg.name; # SMTP_USERNAME=username # SMTP_PASSWORD=password diff --git a/modules/common/default.nix b/modules/common/default.nix index e8c60a6..44309f5 100644 --- a/modules/common/default.nix +++ b/modules/common/default.nix @@ -1,9 +1,4 @@ { - imports = [ - ./networking.nix - ./secrets.nix - ]; - config = { homelab = { services.openssh.enable = true; diff --git a/modules/common/networking.nix b/modules/common/networking.nix deleted file mode 100644 index 837684e..0000000 --- a/modules/common/networking.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ config, lib, ... }: - -{ - options.homelab.networking = { - hostIp = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = '' - The primary IP address of the host. - Used for automated deployment and internal service discovery. - ''; - }; - }; - - config = lib.mkIf (config.homelab.networking.hostIp != null) { - # If a hostIp is provided, we can potentially use it to configure - # networking interfaces or firewall rules automatically here in the future. - }; -} diff --git a/modules/common/secrets.nix b/modules/common/secrets.nix deleted file mode 100644 index 10b6473..0000000 --- a/modules/common/secrets.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ config, lib, ... }: - -{ - sops.secrets = { - # -- User Public Keys (Anti-Fingerprinting) -- - "user_keys_admin" = { neededForUsers = true; }; - "user_keys_deploy" = { neededForUsers = true; }; - "user_keys_backup" = { neededForUsers = true; }; - - # -- Infrastructure Metadata -- - # Hugo TODO: Populate these in your .sops.yaml / secrets file - "acme_email" = {}; - "cloudflare_dns_token" = {}; - "pgadmin_email" = {}; - "gitea_mailer_from" = {}; - "vaultwarden_smtp_from" = {}; - }; -} diff --git a/users/admin/default.nix b/users/admin/default.nix index 14766da..dc01c81 100644 --- a/users/admin/default.nix +++ b/users/admin/default.nix @@ -26,9 +26,7 @@ in { config.users.groups.wheel.name # Enable 'sudo' for the user. ]; initialPassword = "ChangeMe"; - openssh.authorizedKeys.keyFiles = [ - config.sops.secrets.user_keys_admin.path - ]; + openssh.authorizedKeys.keys = cfg.authorizedKeys; packages = with pkgs; [ curl git diff --git a/users/backup/default.nix b/users/backup/default.nix index 8c20374..acae033 100644 --- a/users/backup/default.nix +++ b/users/backup/default.nix @@ -12,8 +12,9 @@ in { extraGroups = [ "docker" # Allow access to the docker socket. ]; - openssh.authorizedKeys.keyFiles = [ - config.sops.secrets.user_keys_backup.path + openssh.authorizedKeys.keys = [ + # Hugo + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICms6vjhE9kOlqV5GBPGInwUHAfCSVHLI2Gtzee0VXPh" ]; }; }; diff --git a/users/deploy/default.nix b/users/deploy/default.nix index 5c28561..0509d1e 100644 --- a/users/deploy/default.nix +++ b/users/deploy/default.nix @@ -3,19 +3,7 @@ let cfg = config.homelab.users.deploy; in { - options.homelab.users.deploy = { - enable = lib.mkEnableOption "user Deploy"; - - authorizedKeys = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = '' - Additional SSH public keys authorized for the deploy user. - The CI runner key should be provided as a base key; personal - workstation keys can be appended here per host or globally. - ''; - }; - }; + options.homelab.users.deploy.enable = lib.mkEnableOption "user Deploy"; config = lib.mkIf cfg.enable { users = { @@ -27,15 +15,12 @@ in { isSystemUser = true; home = "/var/empty"; shell = pkgs.bashInteractive; - openssh.authorizedKeys.keyFiles = [ - config.sops.secrets.user_keys_deploy.path + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPrG+ldRBdCeHEXrsy/qHXIJYg8xQXVuiUR0DxhFjYNg" ]; }; }; - # Allow the deploy user to push closures to the nix store - nix.settings.trusted-users = [ "deploy" ]; - security.sudo.extraRules = [ { groups = [