Compare commits

..

21 Commits

Author SHA1 Message Date
xavier ff9d839609 feat: add placeholder files for common host settings
- Add hosts/default.nix: common settings for all hosts
- Add hosts/servers/default.nix: common settings for all servers
- Add hosts/workstations/default.nix: common settings for all workstations
- Created for production, dev, and stage environments

These files are placeholders for now. Later, common settings can be moved
here to reduce duplication across host configurations.
2026-05-11 11:58:16 +02:00
xavier a00c981ecd refactor: centralize production environment configuration
- Add network/dns.nix, network/time.nix, network/proxy.nix, network/smtp.nix
- Update network/default.nix to import new files
- Update dns01, dns02, rp01 to use centralized DNS parameters
- Update gaia workstation to use centralized time parameters
- Servers use en_US.UTF-8 (set in host configs), workstations use fr_FR.UTF-8

This centralizes environment-specific settings while keeping host-type-specific
settings (like locale) in the host configurations.
2026-05-11 11:47:24 +02:00
xavier 13c3d63785 feat: add centralized environment configuration
- Add dns.nix: environment-specific DNS domain, name servers, forwarders
- Add time.nix: NTP servers (gateway), timezone, hardware clock
- Add proxy.nix: placeholder for future proxy settings (no proxy for now)
- Add smtp.nix: SMTP relay configuration for system emails
- Add default.nix: unified entry point for all network parameters

These files allow environment-specific settings to be managed in one place
and imported by host configurations.
2026-05-11 11:31:41 +02:00
xavier 236e454e22 feat: add dev and stage environment skeletons
- Create environments/dev/ and environments/stage/ with configuration.nix,
  network/, secrets/, and hosts/ directories
- Both environments pin to the same framework commit as production
- Dev and stage subdomains (dev.lagraula.fr, stage.lagraula.fr) ready for
  DNS server configurations when containers are deployed
2026-05-10 22:27:06 +02:00
xavier 5877a51436 docs: update .clinerules to reflect new two-repo structure 2026-05-10 22:23:16 +02:00
xavier 6a2ca62398 refactor: separate framework from production environment
- Create nixos-infra-framework repo with reusable modules, lib, pkgs,
  overlays, scripts, and a sample environment
- Restructure private repo: move hosts/, network/, secrets/, users/
  into environments/production/
- Update all host configs to import from framework repo via fetchGit
- Update deploy.sh with --environment flag (default: production)
- Create hypervisor inventory records (Proxmox, non-NixOS)
- Add environment entry point: environments/production/configuration.nix
- Remove duplicated technical components (now in framework repo)
2026-05-10 20:02:16 +02:00
xavier 0cfdcffc59 Wrote initial configuration for the password manager, the git forge and the dns. 2026-05-09 14:04:16 +02:00
xavier 4d4ac1487c Moved machine-type content to subdirectories. Renamed initial-configuration.nix to intial-lxc-configuration.nix 2026-05-08 23:13:15 +02:00
xavier bd41a33bae Added network management. 2026-05-08 22:26:37 +02:00
xavier 09d2522bfd Fixed typo in .clinrules folder name. Fixed root README.md translation. 2026-05-07 23:52:08 +02:00
xavier dac1336e4c Refreshed and refactored AI rules. 2026-05-07 23:32:24 +02:00
xavier 13dfdf01d0 Remove applciation names from services definition names. 2026-05-06 22:52:30 +02:00
xavier ddf3fdfb53 Renamed services following the same logic as hosts, without reference to the actual applciation. 2026-05-06 22:50:28 +02:00
xavier a3172f477f Servers named by service provided instead of by application. 2026-05-06 22:34:10 +02:00
xavier 834f727882 Named servers by service provided instead of the application used. 2026-05-06 22:32:44 +02:00
xavier bf96cfbee0 First attempt at LXC container deployment chain. 2026-05-06 21:19:13 +02:00
xavier 7fd43cf252 Removed useless artifact (create-lxc.sh and some folders). Removed references to nix-sops. 2026-05-06 18:18:13 +02:00
xavier 2adbe4751d Created/update all README.md files. 2026-05-06 17:13:19 +02:00
xavier 23c22c4bc6 Added AI rules file, apkgs folder, update-nixpkgs.sh script. Updated documentation. 2026-05-03 10:56:03 +02:00
xavier 6e0e86b17a Refactored parameters handling. Removed useless SSH password parameter. 2026-05-01 16:00:02 +02:00
xavier ff60f331fd Properly handle the lack of "shot name" parameter. 2026-05-01 09:43:36 +02:00
98 changed files with 2229 additions and 473 deletions
+32
View File
@@ -0,0 +1,32 @@
## Nix
## General Instructions
1. Do not use Nix flakes.
2. Always check if an option (https://search.nixos.org/options), a package (https://search.nixos.org/packages) or a function (https://noogle.dev/) exists before inventing one.
3. Do not assume the development or administration workstations themselves run NixOS. The development environment is more likely Debian Trixie.
4. Use `nix-instantiate` to verify syntax and evaluation of Nix files:
```
nix-instantiate --parse path/to/file.nix # Check syntax
nix-instantiate --eval -E 'import ./hosts/machine/configuration.nix {}' # Check evaluation
```
## Code Conventions
**Naming:** camelCase for variables, PascalCase for components and types. UPPER_CASE and "_" for shell environment variables.
**Types:** N/A
**Imports:** N/A
**Formatting:**
- Use exclusively `alejandra` or `nixfmt-rfc-style`. Never leave trailing whitespace.
- Prefer `inherit` for same-name variables.
- Use `with lib;` or `with builtins;` sparingly inside functions, but prefer explicit paths for clarity.
- Always declare arguments at the beginning of the file: `{ config, pkgs, lib, ... }:`.
**Modularity:** Each service must be encapsulated in a module with an `enable` option (e.g., `services.mon-service.enable = true;`).
## Secret Management
**Strict Prohibition:** NEVER write passwords, API keys, or tokens in plaintext in `.nix` files.
**Tool:** Use **agenix**.
## Specific Guidelines
1. **Never** expose passwords, API keys, or any secrets in plaintext. Use appropriate secret management/protection mechanisms for the context.
2. Prefer SSH keys for system authentication.
3. Limit line lengths to 78 printable characters.
4. Report any inconsistencies in practices within the project or with best practices.
@@ -0,0 +1,12 @@
# Project Rules: nixos-infra
## Directories
### Private environment repo (nixos-infra)
- `/nixos-infra/environments/production/hosts/servers`: Server-specific configurations (.nix).
- `/nixos-infra/environments/production/hosts/workstations`: Workstation-specific configurations (.nix).
- `/nixos-infra/environments/production/network`: Production network layout (VLANs, subnets, host IPs).
- `/nixos-infra/environments/production/secrets`: agenix-encrypted secrets.
- `/nixos-infra/environments/production/users`: Production user configurations.
- `/nixos-infra/scripts`: Operational scripts (deploy.sh with --environment flag, etc.).
+10
View File
@@ -0,0 +1,10 @@
# Project Rules: nixos-infra
## Goals
The goal is to enable the deployment and maintenance of network infrastructure and application services
(DNS, git repository server, password manager, file server, backup server, reverse proxy,
workstations, etc.). The git repository contains all files needed to configure and install
any machine in the fleet (OS images, configuration files, deployment and maintenance scripts).
The target is to be able to rebuild the entire fleet from a Linux workstation (not necessarily NixOS)
where this repository has been cloned.
@@ -0,0 +1,7 @@
# Project Rules: nixos-infra
## Response Preferences
- Language: English
- Style: Technical, concise, Infrastructure as Code oriented.
- Always briefly explain *why* a specific NixOS option was chosen (reference to official options).
- Notify me if and when it was difficult or impossible to follow the instructions provided in `.ai-rules.md`.
@@ -0,0 +1,9 @@
# Project Rules: nixos-infra
## Technical Context
**Stack:** NixOS 25.11, agenix, bash, SSH, Proxmox VE, LXC, KVM, IPv4, IPv6
**Environment:** VSCodium (VSCode) on Debian 13 + Cline extension for AI, bash, SSH, Gitea, IPv4, IPv6
**Package Manager:** Nix
**Style:** Favor modularity, in the Unix philosophy. Use bash for operational tasks, with a fallback to Python if bash becomes unmaintainable.
**Tests:** Test Driven Development whenever possible.
**Hardware:** X86-64 desktop computers, an old Dell T620, an old Dell T330, a Rpi 3B
+35
View File
@@ -0,0 +1,35 @@
# Role
You are an exceptional product manager with 20 years of experience and an engineer proficient in all programming languages, skilled in assisting junior developers.
# Goal
Help users complete their product design and development tasks in an easily understandable way, proactively completing all tasks without waiting for repeated prompting.
## Step 1: Project Initialization
- When a user makes a request, first review the readme.md file and all code documents in the root directory to understand the project's goals, architecture, and implementation methods. If a readme file doesn't exist, create one.
- This file will serve as a manual for users to understand all provided functions and your project plan.
- Clearly describe the purpose, usage, parameter descriptions, and return value descriptions of all functions in the readme.md file to ensure user-friendly understanding and usage.
## Step 2: Task Understanding and Execution
### Understanding User Needs
- Fully understand user needs from their perspective. Consider: "If I were the user, what would I need?"
- As a product manager, identify any gaps in user needs. Discuss and refine requirements with users until satisfaction is achieved.
- Prioritize the simplest solutions to meet user needs, avoiding overly complex or advanced approaches.
### Code Development
- Plan step-by-step, considering user needs and existing codebase.
- Choose appropriate programming languages and frameworks to implement user requirements.
- Design code structure based on SOLID principles and use design patterns to address common problems.
- Write comprehensive comments for all code modules and include necessary monitoring to track errors.
- Opt for simple, controllable solutions over complex ones.
### Problem Solving
- Thoroughly read the entire code file library to understand all code functions and logic.
- Analyze the root causes of user-reported code errors and propose solutions.
- Engage in multiple interactions with users, summarizing previous interactions and adjusting solutions based on feedback until user satisfaction.
- Initiate "System 2 Thinking Mode" for persistent bugs:
1. Systematically analyze potential root causes and list all hypotheses.
2. Design verification methods for each hypothesis.
3. Provide three distinct solutions, detailing pros and cons for user choice.
## Step 3: Project Summary and Optimization
- After completing the user's task, reflect on the task completion process, identify potential issues and improvements, and update the readme.md file accordingly.
+32
View File
@@ -0,0 +1,32 @@
# RTK - Rust Token Killer (Cline)
**Usage**: Token-optimized CLI proxy for shell commands.
## Rule
Always prefix shell commands with `rtk` to minimize token consumption.
Examples:
```bash
rtk git status
rtk cargo test
rtk ls src/
rtk grep "pattern" src/
rtk find "*.rs" .
rtk docker ps
rtk gh pr list
```
## Meta Commands
```bash
rtk gain # Show token savings
rtk gain --history # Command history with savings
rtk discover # Find missed RTK opportunities
rtk proxy <cmd> # Run raw (no filtering, for debugging)
```
## Why
RTK filters and compresses command output before it reaches the LLM context, saving 60-90% tokens on common operations. Always use `rtk <cmd>` instead of raw commands.
+263 -210
View File
@@ -1,106 +1,112 @@
# 🏗️ **NixOS Infrastructure Project Canvas**
*Projet de gestion centralisée des configurations NixOS pour l'infrastructure de Xavier Lagraula*
*Centralized NixOS configuration management project for Xavier Lagraula's infrastructure*
---
## **📌 Context and Objectives**
- **Scope**: Centralized management of **NixOS** configurations for a complete infrastructure (hypervisors, workstations, LXC containers, services).
- **Environment**:
- One **Proxmox** hypervisor currently, a second one coming soon.
- Several **workstations** (e.g., `sting`, `gaia`, `PC-FRIDA`).
- **LXC Containers** (on Proxmox) VE for infrastructure services (DNS, Gitea, Bitwarden, etc.) and applications.
- **2 main users** (Xavier, Frida) + system users (`root`, `admin`, `guest`).
- **Hardware**: ThinkCentre m710q (16 GB RAM, i3) with **KDE 6.3**.
### **Objectives**
| Objective | Description | Priority |
| ---------------------------| ------------------------------------------------------------------| ----------|
| Centralize configurations | A single Git repository for all machines. | ⭐⭐⭐ |
| Modularity | Reusable modules for services, machine types, and user profiles. | ⭐⭐⭐ |
| Automation | Scripts to deploy configurations to machines and containers. | ⭐⭐⭐ |
| Security | Secret management with `sops-nix` or `agenix`. | ⭐⭐ |
| Portability | Autonomous modules via `callPackage`. | ⭐⭐ |
| Maintenance | Clear documentation and simplified update processes. | ⭐ |
---
## **📌 Contexte et Objectifs**
### **Contexte**
## **🔍 Key Decisions**
- **Portée** : Gestion centralisée des configurations **NixOS** pour une infrastructure complète (hyperviseurs, stations de travail, conteneurs LXC, services).
- **Environnement** :
- Un hyperviseur **Proxmox** actuellement, un deuxième à venir.
- Plusieurs **stations de travail** (ex: `dev-xavier`, `mao-julien`, `bureautique`).
- **Conteneurs LXC** (sur Proxmox) pour les services d'infrastructure (DNS, Gitea, Bitwarden, etc.) et applicatifs.
- **2 utilisateurs principaux** (Xavier, Frida) + utilisateurs système (`root`, `admin`, `guest`).
- **Matériel** : ThinkCentre m710q (16 Go RAM, i3) avec **KDE 6.3**.
### **Objectifs**
| Objectif | Description | Priorité |
| ------------------------------ | ------------------------------------------------------------------------------------ | -------- |
| Centraliser les configurations | Un dépôt Git unique pour toutes les machines. | ⭐⭐⭐ |
| Modularité | Modules réutilisables pour les services, types de machines, et profils utilisateurs. | ⭐⭐⭐ |
| Automatisation | Scripts pour déployer les configurations sur les machines et conteneurs. | ⭐⭐⭐ |
| Sécurité | Gestion des secrets avec `sops-nix`. | ⭐⭐ |
| Portabilité | Modules autonomes via `callPackage`. | ⭐⭐ |
| Maintenance | Documentation claire et processus de mise à jour simplifiés. | ⭐ |
| Decision | Justification | Impact |
| ---------------------------------------------------------| -------------------------------------------------------------------------------------------------------------------------------------------| ----------------------------------------------------------------------------|
| **No flakes** | Simplify onboarding and avoid a steep learning curve. | Configuration via `configuration.nix` + `callPackage`. |
| **Modular structure** | Separate configurations by machine type and service. | Clear and maintainable directory tree. |
| **Servers named by service, not by application** | Indicate what the machine does (git01 = Git forge) rather than the software (Gitea). Allows changing the underlying app without renaming. | Hosts under `hosts/servers/<service>01` (e.g. `git01`, `pass01`, `dns01`). |
| **Modules** | Make modules autonomous, portable, and reusable. | Each module is an independent Nix package. |
| **Separation of `user-profiles/` and `users/`** | Distinguish generic roles (e.g., `admin`) from concrete users (e.g., `xavier`). | Flexibility to apply profiles to multiple users. |
| **`deploy.sh` script** | Automate deployment on existing machines. | Clone/update the repo + `nixos-rebuild switch`. |
| **`create-lxc-nixos.sh` script** | Automate the creation of NixOS LXC containers on Proxmox. | Ready-to-use container with initial configuration. |
| **Initial configuration (`initial-configuration.nix`)** | Prepare an LXC container so it can update itself via Git. | Self-sufficient containers. |
| **Secret management with `agenix`** | Encrypt secrets (passwords, keys) for secure storage. | Enhanced security for sensitive data. |
| **Unprivileged LXC containers (`--unprivileged 0`)** | NixOS requires privileges to function correctly in LXC. | Functional containers with NixOS. |
| **Centralised network data** | A single declarative network data source (`network/`) consumed by NixOS configs and scripts. | VLANs, subnets, host addresses defined once in `network/hosts.nix`. |
| **IPv6-first with dual-stack** | Both ULA (fdb2:) and GUA (2a01:) prefixes defined per VLAN. | Consistent internal addressing + global reachability for DMZ services. |
| **DHCP-based IPv4 assignment** | LXC containers use `useDHCP = true`. Addresses are reserved in `hosts.nix` as source of truth. | Centralised address management, no static IPs in per-host configs. |
---
---
## **🔍 Décisions Clés**
## **📂 File Structure**
| Décision | Justification | Impact |
| ----------------------------------------------------------| -----------------------------------------------------------------------------------------| ------------------------------------------------------------------|
| **Pas de flakes** | Simplifier la prise en main et éviter une courbe d'apprentissage abrupte. | Configuration via `configuration.nix` + `callPackage`. |
| **Structure modulaire** | Séparer les configurations par type de machine et service. | Arborescence claire et maintenable. |
| `**callPackage` pour les modules** | Rendre les modules autonomes, portables et réutilisables. | Chaque module est un paquetage Nix indépendant. |
| **Séparation `user-profiles/` et `users/**` | Distinguer les rôles génériques (ex: `admin`) des utilisateurs concrets (ex: `xavier`). | Flexibilité pour appliquer des profils à plusieurs utilisateurs. |
| **Script `deploy.sh**` | Automatiser le déploiement sur les machines existantes. | Clone/mise à jour du dépôt + `nixos-rebuild switch`. |
| **Script `create-lxc-nixos.sh**` | Automatiser la création de conteneurs LXC sous Proxmox. | Conteneur prêt à l'emploi avec configuration initiale. |
| **Configuration initiale (`initial-configuration.nix`)** | Préparer un conteneur LXC pour qu'il puisse se mettre à jour via Git. | Conteneurs auto-suffisants. |
| **Gestion des secrets avec `agenix**` | Chiffrer les secrets (mots de passe, clés) pour un stockage sécurisé. | Sécurité renforcée pour les données sensibles. |
| **Conteneurs LXC non privilégés (`--unprivileged 0`)** | NixOS a besoin de privilèges pour fonctionner correctement. | Conteneurs fonctionnels avec NixOS. |
| **Support IPv4, IPv6, VLANs** | La cible finale est IPv6 first, voire IPv6 only. | Gestion du plan IP à concevoir. |
---
---
## **📂 Arborescences de Fichiers**
### **Structure du dépôt Git**
### **Git Repository Structure**
```bash
nixos-infra/
├── configuration.nix # Point d'entrée principal
├── configuration.nix # Main entry point
├── README.md
├── hosts/ # Configurations par machine
│ ├── servers/ # Serveurs (hyperviseurs, VMs, LXCs)
│ │ ├── pve01/ # Hyperviseur 1
├── hosts/ # Machine configurations
│ ├── servers/ # Servers (hypervisors, VMs, LXCs)
│ │ ├── hyper01/ # Hypervisor 1
│ │ │ └── configuration.nix
│ │ ├── pve02/ # Hyperviseur 2
│ │ ├── hyper02/ # Hypervisor 2
│ │ │ └── configuration.nix
│ │ ├── dns01/ # Conteneur LXC pour DNS (master)
│ │ ├── dns01/ # LXC container for DNS (master)
│ │ │ └── configuration.nix
│ │ ├── dns02/ # Conteneur LXC pour DNS (slave)
│ │ ├── dns02/ # LXC container for DNS (slave)
│ │ │ └── configuration.nix
│ │ ├── gitea01/ # Conteneur LXC pour Gitea
│ │ ├── git01/ # LXC container for Git forge (e.g. Gitea)
│ │ │ └── configuration.nix
│ │ ── vaultwarden/ # Conteneur LXC pour Vaultwarden
│ │ └── configuration.nix
│ │ └── rp01/ # Conteneur LXC reverse proxy
│ │ ── pass01/ # LXC container for password manager (e.g. Vaultwarden)
│ │ └── configuration.nix
│ │ └── rp01/ # LXC container for reverse proxy
│ │ └── configuration.nix
│ │
│ └── workstations/ # Stations de travail
│ ├── sting/ # Station de Xavier (admin, dev, mao)
│ └── workstations/ # Workstations
│ ├── sting/ # Xavier's station (admin, dev, audio)
│ │ └── configuration.nix
│ ├── PC-FRIDA/ # Station de Frida (bureautique)
│ ├── PC-FRIDA/ # Frida's station (office)
│ │ └── configuration.nix
│ └── gaia/ # Portable de Xavier (MAO)
│ └── gaia/ # Xavier's laptop (audio)
│ └── configuration.nix
├── modules/ # Modules autonomes
│ ├── machine-types/ # Types de machines
│ ├── hypervisor.nix # Module pour hyperviseurs
│ ├── vm.nix # Module pour VMs
│ ├── lxc.nix # Module pour conteneurs LXC
│ └── workstation.nix # Module pour stations de travail
├── network/ # Centralised network data (VLANs, subnets, hosts)
│ ├── default.nix # Aggregator — imports vlans, subnets, hosts
│ ├── vlans.nix # VLAN definitions
│ ├── subnets.nix # Subnet prefix allocations
│ ├── hosts.nix # Per-machine IP/address assignments
│ └── README.md # Network architecture documentation
├── modules/ # Autonomous modules
│ ├── machine-types/ # Machine types
│ │ ├── hypervisor/
│ │ │ └── default.nix # Module for hypervisors
│ │ ├── vm/
│ │ │ └── default.nix # Module for VMs
│ │ ├── lxc/
│ │ │ └── default.nix # Module for LXC containers
│ │ └── workstation/
│ │ └── default.nix # Module for workstations
│ │
│ ├── services/ # Services
│ │ ├── dns/
│ │ │ ├── default.nix # Implémentation
│ │ │ └── options.nix # Options exposées
│ │ ├── gitea/
│ │ │ ├── default.nix # Implementation
│ │ │ └── options.nix # Exposed options
│ │ ├── git-forge/
│ │ │ └── default.nix
│ │ ├── password-manager/
│ │ │ └── default.nix
@@ -108,13 +114,13 @@ nixos-infra/
│ │ │ └── default.nix
│ │ └── ...
│ │
│ └── user-profiles/ # Profils utilisateurs (rôles)
│ ├── admin.nix # Profil administrateur
│ ├── dev.nix # Profil développeur
│ ├── mao.nix # Profil MAO
│ └── standard.nix # Profil standard
│ └── user-profiles/ # User profiles (roles)
│ ├── admin.nix # Administrator profile
│ ├── dev.nix # Developer profile
│ ├── cam.nix # Audio production profile
│ └── standard.nix # Standard profile
├── users/ # Utilisateurs concrets
├── users/ # Specific users
│ ├── root/
│ │ └── configuration.nix
│ ├── xavier/
@@ -124,15 +130,15 @@ nixos-infra/
│ └── guest/
│ └── configuration.nix
├── scripts/ # Scripts utilitaires
│ ├── deploy.sh # Déploiement pour machines existantes
│ ├── create-lxc-nixos.sh # Création de conteneurs LXC sous Proxmox
│ └── initial-configuration.nix # Configuration initiale pour nouveaux conteneurs
├── scripts/ # Utility scripts
│ ├── deploy.sh # Deployment for existing machines
│ ├── create-lxc-nixos.sh # LXC container creation on Proxmox
│ └── initial-configuration.nix # Initial configuration for new containers
├── secrets/ # Secrets (exclus de Git)
├── secrets/ # Secrets (excluded from Git)
│ └── .gitignore
└── overlays/ # Overlays pour paquets personnalisés
└── overlays/ # Overlays for custom packages
└── custom-pkgs.nix
```
@@ -140,193 +146,240 @@ nixos-infra/
---
## **🖥️ Profils de Machines et Services**
## **🖥️ Machine Profiles and Services**
### **Profils de Machines**
### **Machine Profiles**
| Profile | Description | File | Usage |
| --------------| --------------------------------------------------------------| ----------------------------------------| ---------------------------------------------|
| `hypervisor` | Common configuration for hypervisors (KVM, libvirtd, etc.). | `modules/machine-types/hypervisor/default.nix` | Proxmox hypervisors. |
| `vm` | Common configuration for virtual machines. | `modules/machine-types/vm/default.nix` | VMs under Proxmox. |
| `lxc` | Common configuration for LXC containers. | `modules/machine-types/lxc/default.nix` | LXC containers under Proxmox. |
| `workstation` | Common configuration for workstations. | `modules/machine-types/workstation/default.nix` | Development, audio production, office stations. |
| Profile | Description | File |
| ------------| --------------------------------------------------| --------------------------------------|
| `admin` | Administrator access (sudo, service management). | `modules/user-profiles/admin.nix` |
| `dev` | Development environment (tools, permissions). | `modules/user-profiles/dev.nix` |
| `cam` | Computer-assisted music (CAM). | `modules/user-profiles/cam.nix` |
| `standard` | Standard user profile (basic access). | `modules/user-profiles/standard.nix` |
| Profil | Description | Fichier | Utilisation |
| ---------------| ---------------------------------------------------------------| -----------------------------------------| ----------------------------------------------|
| `hypervisor` | Configuration commune aux hyperviseurs (KVM, libvirtd, etc.). | `modules/machine-types/hypervisor.nix` | Hyperviseurs Proxmox. |
| `vm` | Configuration commune aux machines virtuelles. | `modules/machine-types/vm.nix` | VMs sous Proxmox. |
| `lxc` | Configuration commune aux conteneurs LXC. | `modules/machine-types/lxc.nix` | Conteneurs LXC sous Proxmox. |
| `workstation` | Configuration commune aux stations de travail. | `modules/machine-types/workstation.nix` | Stations de développement, MAO, bureautique. |
---
## **📋 Reference Tables**
### **1. Machine List**
| Name | Machine Type | VLAN | IPv4 | IPv6 Token |
| ----------| --------------------| ---------| ------------------| ------------|
| hyper01 | Hypervisor | ADMIN | 10.10.128.10/16 | ::10 |
| hyper02 | Hypervisor | ADMIN | 10.10.128.11/16 | ::11 |
| dns01 | LXC Container | DMZ | 10.40.128.10/16 | ::10 |
| dns02 | LXC Container | DMZ | 10.40.128.11/16 | ::11 |
| git01 | LXC Container | DMZ | 10.40.128.20/16 | ::20 |
| pass01 | LXC Container | DMZ | 10.40.128.30/16 | ::30 |
| rp01 | LXC Container | DMZ | 10.40.128.199/16 | ::199 |
| sting | Workstation | INTERNAL | 10.50.128.10/16 | ::10 |
| PC-FRIDA | Workstation | INTERNAL | 10.50.128.11/16 | ::11 |
| gaia | Workstation | INTERNAL | 10.50.128.12/16 | ::12 |
---
### **2. VLANs**
| VLAN | ID | Effective ID | IPv4 Prefix | IPv6 ULA | IPv6 GUA |
| ----------| ---| --------------| -----------------| ----------------------------------| ----------------------------------|
| INET | 1 | 1 | Out of scope | Out of scope | Out of scope |
| ADMIN | 90 | **100** | 10.10.0.0/16 | fdb2:ae63:d45:d941::/64 | 2a01:e0a:2ea:d941::/64 |
| IOT | 200| 200 | 10.20.0.0/16 | fdb2:ae63:d45:d942::/64 | 2a01:e0a:2ea:d942::/64 |
| GUEST | 300| 300 | 10.30.0.0/16 | fdb2:ae63:d45:d943::/64 | 2a01:e0a:2ea:d943::/64 |
| DMZ | 400| 400 | 10.40.0.0/16 | fdb2:ae63:d45:d944::/64 | 2a01:e0a:2ea:d944::/64 |
| INTERNAL | 500| 500 | 10.50.0.0/16 | fdb2:ae63:d45:d945::/64 | 2a01:e0a:2ea:d945::/64 |
---
### **3. List of User Profiles**
| Name | Role |
| ------------| -------------------|
| `admin` | Superuser |
| `dev` | Developer |
| `standard` | Web & Office |
| `guest` | Guest |
---
### **Profils Utilisateurs**
| Profil | Description | Fichier |
| ------------| --------------------------------------------------------| --------------------------------------|
| `admin` | Accès administrateur (sudo, gestion des services). | `modules/user-profiles/admin.nix` |
| `dev` | Environnement de développement (outils, permissions). | `modules/user-profiles/dev.nix` |
| `mao` | Configuration pour la musique assistée par ordinateur. | `modules/user-profiles/mao.nix` |
| `standard` | Profil utilisateur standard (accès basique). | `modules/user-profiles/standard.nix` |
---
## **📋 Tableaux de Référence**
### **1. Liste des Machines**
| Nom | Type de Machine | Services | IPv4 | Token IPv6 |
| ---------| --------------------| -------------| ------| ------------|
| pve01 | Hyperviseur | Proxmox | | |
| pve02 | Hyperviseur | Proxmox | | |
| dns01 | Conteneur LXC | DNS | | |
| gitea01 | Conteneur LXC | Gitea | | |
| vault01 | Conteneur LXC | Vaultwarden | | |
| sting | Station de travail | | | |
| | | | | |
| | | | | |
| | | | | |
| marley | Station de travail | | | |
---
### **2. Liste des profils utilisateurs**
| Nom | Rôle |
| ---------- | ----------------- |
| `admin` | Superutilisateur |
| `dev` | Développeur |
| `standard` | Web & bureautique |
| `guest` | Invité |
---
### **3. Liste des Utilisateurs**
| Nom | Rôle | Accès SSH |
### **4. User List**
| Name | Role | SSH Access |
| --------| -----------------------| -----------|
| root | Superutilisateur | ❌ |
| xavier | Utilisateur principal | ✅ |
| frida | Utilisateur | ✅ |
| guest | Invité | ❌ |
| root | Superuser | ❌ |
| xavier | Main user | ✅ |
| frida | User | ✅ |
| guest | Guest | ❌ |
---
### **3. Liaisons Utilisateur/Machine/Profil**
### **5. User/Machine/Profile Mappings**
| Utilisateur | Machine | Profils Appliqués | Rôle |
| -------------| ---------| -------------------| --------------------------------|
| root | Toutes | - | Superutilisateur |
| xavier | Toutes | `admin` | Développement + administration |
| xavier | sting | `admin`, `dev` | Gestion des hyperviseurs |
| frida | marley | `standard` | Développement |
| guest | aucune | `guest` | Utilisateur invité |
| User | Machine | Applied Profiles | Role |
| --------| ----------| ------------------| ------------------------------|
| root | All | - | Superuser |
| xavier | All | `admin` | Development + administration |
| xavier | sting | `admin`, `dev` | Hypervisor management |
| frida | PC-FRIDA | `standard` | Development |
| guest | None | `guest` | Guest user |
| xavier | sting | `admin`, `dev` | Hypervisor management |
| frida | PC-FRIDA | `standard` | Development |
| guest | None | `guest` | Guest user |
---
---
## **🔄 Workflow de Déploiement**
## **🌐 Network Architecture**
### **Pour les conteneurs LXC (Proxmox)**
The network topology and addressing are defined **declaratively** in `network/`, the single source of truth for:
- VLANs (`network/vlans.nix`)
- Subnet prefixes (`network/subnets.nix`)
- Host address assignments (`network/hosts.nix`)
1. **Créer le conteneur** :
- Utiliser `create-lxc-nixos.sh` pour créer un conteneur à partir du template NixOS.
- Le script installe la configuration initiale (`initial-configuration.nix`).
2. **Déployer la configuration finale** :
- Le script `deploy.sh` est exécuté automatiquement pour appliquer la configuration spécifique au conteneur (ex: `hosts/servers/dns01/configuration.nix`).
3. **Mettre à jour** :
- `git pull` dans `/etc/nixos-infra` + `nixos-rebuild switch`.
### Prefix rules
### **Pour les stations de travail et hyperviseurs**
| Type | Formula | Example (DMZ, effectiveId=400) |
|--------|-----------------------------------------------------------|--------------------------------|
| IPv4 | `10.<effectiveId>.0.0/16` — hosts in `10.<v>.128.0/17` | `10.40.0.0/16` |
| IPv6 ULA | `fdb2:ae63:d45:d94<N>::/64` where N = effectiveId/100 | `fdb2:ae63:d45:d944::/64` |
| IPv6 GUA | `2a01:e0a:2ea:d94<N>::/64` where N = effectiveId/100 | `2a01:e0a:2ea:d944::/64` |
1. **Cloner le dépôt** :
```bash
git clone https://github.com/xlagraula/nixos-infra.git /etc/nixos-infra
```
2. **Lier la configuration** :
```bash
ln -s /etc/nixos-infra/hosts/workstations/dev-xavier/configuration.nix /etc/nixos/configuration.nix
```
3. **Appliquer la configuration** :
```bash
sudo nixos-rebuild switch
```
> **ADMIN exception:** VLAN ID is 90 but treated as effective ID 100 for
> prefix computation. This avoids a risky production renumbering.
### Consumption
**From a NixOS configuration:**
```nix
let network = import ../../network { };
in {
networking.hostName = "dns01";
networking.useDHCP = true;
}
```
**From a shell script:**
```bash
nix eval --json -f network/default.nix hosts | jq '.dns01.ipv4'
# → "10.40.128.10"
```
---
## **🔄 Deployment Workflow**
### **For LXC containers (Proxmox)**
1. **Create the container :**
- Use `create-lxc-nixos.sh` to create a container from the NixOS template.
- The script installs the initial configuration (`initial-configuration.nix`).
2. **Deploy the final configuration :**
- The `deploy.sh` script is executed automatically to apply the specific configuration to the container (e.g., `hosts/servers/dns01/configuration.nix`).
3. **Update :**
- `git pull` in `/etc/nixos-infra` + `nixos-rebuild switch`.
### **For workstations and hypervisors**
1. **Clone the repository :**
```bash
git clone https://github.com/xlagraula/nixos-infra.git /etc/nixos-infra
```
2. **Link the configuration :**
```bash
ln -s /etc/nixos-infra/hosts/workstations/dev-xavier/configuration.nix /etc/nixos/configuration.nix
```
3. **Apply the configuration :**
```bash
sudo nixos-rebuild switch
```
---
---
## **🔐 Gestion des Secrets**
## **🔐 Secret Management**
- **Outil** : `agenix` (recommandé) ou `sops-nix`.
- **Processus** :
1. Chiffrer les secrets avec `age` :
- **Tool** : `agenix`.
- **Process** :
1. Encrypt secrets with `age` :
```bash
echo "mon-secret" | age -r age1... -o secrets/bitwarden/password.age
echo "my-secret" | age -r age1... -o secrets/bitwarden/password.age
```
2. Intégrer dans la configuration :
2. Integrate into the configuration :
```nix
age.secrets.bitwarden-password = {
path = ./secrets/bitwarden/password.age;
mode = "600";
};
```
3. **Ne jamais commiter les secrets en clair** (ajouter `secrets/` à `.gitignore`).
3. **Never commit secrets in plain text** (add `secrets/` to `.gitignore`).
---
---
## **📅 Feuille de Route**
## **📅 Roadmap**
| Étape | Description | Statut | Priorité |
| ------------------------------- | ------------------------------------------------- | ------ | -------- |
| Tester le template LXC NixOS | Vérifier que le template fonctionne sous Proxmox. | ⬜ | ⭐⭐⭐ |
| Finaliser `create-lxc-nixos.sh` | Tester la création d'un conteneur LXC. | ⬜ | ⭐⭐⭐ |
| Écrire le module DNS | Module pour le service DNS (Bind). | ⬜ | ⭐⭐⭐ |
| Configurer `agenix` | Chiffrer les premiers secrets. | ⬜ | ⭐⭐ |
| Documenter le processus | `README.md` pour expliquer le déploiement. | ⬜ | ⭐⭐ |
| Automatiser avec Ansible | Playbook pour créer plusieurs conteneurs. | ⬜ | ⭐ |
| Intégrer un CI/CD | Tester les configurations avant déploiement. | ⬜ | ⭐ |
| Step | Description | Status | Priority |
| --------------------------------| -----------------------------------------------| --------| ----------|
| Test the NixOS LXC template | Verify that the template works under Proxmox. | ⬜ | ⭐⭐⭐ |
| Finalize `create-lxc-nixos.sh` | Test the creation of an LXC container. | ⬜ | ⭐⭐⭐ |
| Write the DNS module | Module for the DNS service (Bind). | ⬜ | ⭐⭐⭐ |
| Configure `agenix` | Encrypt the first secrets. | ⬜ | ⭐⭐ |
| Document the process | `README.md` to explain deployment. | ⬜ | ⭐⭐ |
| DHCP service module | Kea or ISC DHCP consuming `network/hosts.nix` | ⬜ | ⭐ |
| Automate with Ansible | Playbook to create multiple containers. | ⬜ | ⭐ |
| Integrate CI/CD | Test configurations before deployment. | ⬜ | ⭐ |
---
---
## **💡 Notes et Bonnes Pratiques**
## **💡 Notes and Best Practices**
- **Nommage** :
- Utilisez des noms explicites pour les machines (ex: `dns01`, `gitea01`).
- Pour les conteneurs LXC, préférez des noms courts et descriptifs.
- **Sécurité** :
- Désactivez l'accès root en SSH une fois le déploiement terminé.
- Utilisez des clés SSH pour l'authentification.
- **Naming** :
- **Servers**: Name by service, not by application (e.g., `git01`, `pass01`, `dns01`).
- **Workstations**: Use descriptive hostnames (e.g., `sting`, `gaia`).
- **Hypervisors**: Prefix with `hyper` (e.g., `hyper01`, `hyper02`).
- **Security** :
- Disable root SSH access once deployment is complete.
- Use SSH keys for authentication.
- **Network** :
- IPv4 addresses are allocated from the upper half of each VLAN (`10.<v>.128.0/17`).
- Addresses are assigned via DHCP reservations — `network/hosts.nix` is the source of truth.
- See `network/README.md` for the complete architecture documentation.
- **Maintenance** :
- Mettez à jour régulièrement `nixpkgs` (`nix-channel --update`).
- Documentez les changements dans le `CHANGELOG.md`.
- **Sauvegardes** :
- Sauvegardez les configurations (`/etc/nixos-infra`) et les secrets (`secrets/`).
- Pour les conteneurs LXC sous Proxmox, utilisez les sauvegardes Proxmox.
- Update `nixpkgs` regularly (`nix-channel --update`).
- Document changes in the `CHANGELOG.md`.
- **Backups** :
- Backup configurations (`/etc/nixos-infra`) and secrets (`secrets/`).
- For LXC containers under Proxmox, use Proxmox backups.
---
---
## **📚 Ressources Utiles**
## **📚 Useful Resources**
- [NixOS Manual](https://nixos.org/manual/)
- [Nix Flakes (pour référence future)](https://nixos.wiki/wiki/Flakes)
- [Agenix pour les secrets](https://github.com/ryantm/agenix)
- [Nix Flakes (for future reference)](https://nixos.wiki/wiki/Flakes)
- [Agenix for secrets](https://github.com/ryantm/agenix)
- [Proxmox + LXC Documentation](https://pve.proxmox.com/wiki/Linear_Container)
- [Exemple de dépôt NixOS Infrastructure](https://github.com/NixOS/nixos-infrastructure)
---
- [Example NixOS Infrastructure Repository](https://github.com/NixOS/nixos-infrastructure)
---
+41
View File
@@ -0,0 +1,41 @@
# nixos-infra — Private environments
This repository contains **environment-specific declarations** (hosts, network
layout, secrets, users) for the NixOS fleet. Reusable technical components
(modules, lib, pkgs, scripts) live in the **public framework repository**:
`nixos-infra-framework` (ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git)
## Structure
```
nixos-infra/
├── environments/
│ └── production/ # Production infrastructure
│ ├── configuration.nix # Environment entry point
│ ├── hosts/
│ │ ├── servers/ # Server configurations (LXC, hypervisors)
│ │ └── workstations/ # Workstation configurations
│ ├── network/ # VLANs, subnets, host IPs
│ ├── secrets/ # agenix-encrypted secrets
│ └── users/ # User configurations
├── scripts/ # Environment-specific script wrappers
└── README.md
```
## Adding a New Environment
1. Create `environments/<name>/` with the same sub-structure as `production/`.
2. Set up its own `network/`, `secrets/`, and `users/`.
3. Deploy with:
```
deploy.sh --environment <name>
```
## Deployment
```
deploy.sh --environment production
```
See the framework repo for the deploy script and reusable modules.
@@ -0,0 +1,19 @@
{ config, pkgs, lib, ... }:
let
# Pin to the same framework commit as production — update when testing
# new framework features in dev before rolling to production.
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
in {
imports = [
(framework + "/lib")
(framework + "/overlays/custom-pkgs.nix")
];
# Dev environment-wide settings
system.stateVersion = "25.11";
}
@@ -0,0 +1,14 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all hosts in the dev environment
# This file is imported by all host configurations (servers and workstations)
# TODO: Move common settings here later
# Examples:
# - Common users/groups
# - Common packages
# - Common services
# - Common security policies
# - Common monitoring/alerting
}
@@ -0,0 +1,15 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all servers in the dev environment
# This file is imported by all server configurations
# TODO: Move common server settings here later
# Examples:
# - Server-specific users/groups
# - Server-specific packages
# - Server-specific services
# - Server-specific security policies
# - Server-specific monitoring/alerting
# - Locale: en_US.UTF-8 (for servers)
}
@@ -0,0 +1,15 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all workstations in the dev environment
# This file is imported by all workstation configurations
# TODO: Move common workstation settings here later
# Examples:
# - Workstation-specific users/groups
# - Workstation-specific packages
# - Workstation-specific services
# - Workstation-specific security policies
# - Workstation-specific monitoring/alerting
# - Locale: fr_FR.UTF-8 (for workstations)
}
@@ -0,0 +1,16 @@
# Central network data source for the dev environment.
#
# Usage from a configuration.nix:
# network = import ../../network { };
# network.dns.domain → "dev.lagraula.fr"
# network.time.timeZone → "Europe/Paris"
#
# Usage from a shell script (via `nix eval`):
# nix eval --json -f network/default.nix dns
{
dns = import ./dns.nix;
time = import ./time.nix;
proxy = import ./proxy.nix;
smtp = import ./smtp.nix;
}
@@ -0,0 +1,23 @@
{ config, pkgs, lib, ... }:
{
# Environment-specific DNS domain
domain = "dev.lagraula.fr";
# Default DNS servers for this environment
# These are the IPs of the DNS servers (e.g., dns-dev01, dns-dev02)
# that will be deployed in this environment.
defaultNameServers = [ "10.40.128.10" "10.40.128.11" ];
# Forwarders for this environment (e.g., upstream DNS)
forwarders = [ "1.1.1.1" "8.8.8.8" ];
# Allow zone transfers only to secondary DNS servers in this environment
allowZoneTransfer = [ "10.40.128.11" ];
# Recursion policy for this environment
recursion = "AllowOnlyForPrivateNetworks";
# Email for Let's Encrypt (Caddy) — can be environment-specific
letsEncryptEmail = "xavier@lagraula.fr";
}
@@ -0,0 +1,27 @@
{ config, pkgs, lib, ... }:
{
# Proxy configuration for this environment
# Currently no proxy is used — direct access for all hosts
# Workstations: no proxy
workstations = {
httpProxy = "";
httpsProxy = "";
noProxy = "";
};
# Servers: no proxy
servers = {
httpProxy = "";
httpsProxy = "";
noProxy = "";
};
# To enable a proxy later, uncomment and set the proxy URLs:
# workstations = {
# httpProxy = "http://proxy.dev.lagraula.fr:3128";
# httpsProxy = "http://proxy.dev.lagraula.fr:3128";
# noProxy = "localhost,127.0.0.1,::1,10.0.0.0/8";
# };
}
@@ -0,0 +1,22 @@
{ config, pkgs, lib, ... }:
{
# SMTP relay configuration for this environment
# Used by services that need to send email (e.g., monitoring alerts)
# SMTP relay host — can be an internal relay or external service
relayHost = "smtp.lagraula.fr";
relayPort = 587;
# TLS settings
useTLS = true;
useSTARTTLS = true;
# Authentication — currently none (open relay for internal use)
# To add authentication later:
# username = "noreply@dev.lagraula.fr";
# passwordFile = config.age.secrets.smtp-password.path;
# Default "From" address for system emails
fromAddress = "noreply@dev.lagraula.fr";
}
@@ -0,0 +1,16 @@
{ config, pkgs, lib, ... }:
{
# NTP servers for this environment
# The gateway acts as the NTP server (IPv4 .1, IPv6 ::1)
ntpServers = [
"10.10.128.1" # IPv4 gateway
"fd00::1" # IPv6 gateway
];
# Time zone for this environment
timeZone = "Europe/Paris";
# Hardware clock setting
hardwareClock = "UTC";
}
@@ -0,0 +1,18 @@
{ config, pkgs, lib, ... }:
let
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
in {
imports = [
# Import the framework utilities
(framework + "/lib")
(framework + "/overlays/custom-pkgs.nix")
];
# Production environment-wide settings
system.stateVersion = "25.11";
}
@@ -0,0 +1,2 @@
# Host definition.
Hosts belong to a machine type defined in `nixos-infra/modules/machine-types` and can either be `servers` running services defined in `nixos-infra/modules/services` or `workstations` used by users to access servcies and run applications depending on their profile.
@@ -0,0 +1,14 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all hosts in the production environment
# This file is imported by all host configurations (servers and workstations)
# TODO: Move common settings here later
# Examples:
# - Common users/groups
# - Common packages
# - Common services
# - Common security policies
# - Common monitoring/alerting
}
@@ -0,0 +1,4 @@
# Servers
NixOS configurations for infrastructure servers.
Defines service roles (DNS, Gitea, reverse-proxy, Vaultwarden)
and hypervisor configurations (Proxmox pve01, pve02).
@@ -0,0 +1,15 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all servers in the production environment
# This file is imported by all server configurations
# TODO: Move common server settings here later
# Examples:
# - Server-specific users/groups
# - Server-specific packages
# - Server-specific services
# - Server-specific security policies
# - Server-specific monitoring/alerting
# - Locale: en_US.UTF-8 (for servers)
}
@@ -0,0 +1,44 @@
{ config, pkgs, lib, ... }:
let
# Pin to a specific commit of the framework repo for reproducibility.
# Update this hash when you want to pull in new framework changes.
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
# Import environment-specific network parameters
env = import ../../../network { };
in {
imports = [
# Module for LXC containers
(framework + "/modules/machine-types/lxc")
# Technitium DNS Server service module
(framework + "/modules/services/dns/default.nix")
];
# Explicitly enable LXC machine type
lxc.enable = true;
# Host identity (IP address 10.40.128.10/16 assigned via DHCP reservation)
networking.hostName = "dns01";
networking.domain = env.dns.domain;
networking.useDHCP = true;
# Technitium DNS Server — primary DNS server
services.dns = {
enable = true;
recursion = env.dns.recursion;
forwarders = env.dns.forwarders;
# Uncomment and configure with agenix secret:
# adminPasswordFile = config.age.secrets.dns-admin-password.path;
allowZoneTransfer = env.dns.allowZoneTransfer; # Allow secondary to dns02
listenAddresses = [ "10.40.128.10" "127.0.0.1" "::1" ];
};
# Caddy-specific configuration (optional)
services.caddy = {
email = env.dns.letsEncryptEmail; # Email for Let's Encrypt
};
}
@@ -0,0 +1,36 @@
{ config, pkgs, lib, ... }:
let
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
# Import environment-specific network parameters
env = import ../../../network { };
in {
imports = [
# Module for LXC containers
(framework + "/modules/machine-types/lxc")
# Technitium DNS Server service module
(framework + "/modules/services/dns/default.nix")
];
# Explicitly enable LXC machine type
lxc.enable = true;
# Host identity (IP address 10.40.128.11/16 assigned via DHCP reservation)
networking.hostName = "dns02";
networking.domain = env.dns.domain;
networking.useDHCP = true;
# Technitium DNS Server — secondary (replica) DNS server
services.dns = {
enable = true;
recursion = env.dns.recursion;
forwarders = env.dns.forwarders;
# Uncomment and configure with agenix secret:
# adminPasswordFile = config.age.secrets.dns-admin-password.path;
listenAddresses = [ "10.40.128.11" "127.0.0.1" "::1" ];
};
}
@@ -0,0 +1,33 @@
{ config, pkgs, lib, ... }:
let
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
in {
imports = [
# Module for LXC containers
(framework + "/modules/machine-types/lxc")
# Module for the git forge service (Forgejo)
(framework + "/modules/services/git-forge/default.nix")
];
# Explicitly enable LXC machine type
lxc.enable = true;
# Host identity (IP address 10.40.128.20/16 assigned via DHCP reservation)
networking.hostName = "git01";
networking.domain = "prod.lagraula.fr";
networking.useDHCP = true;
# Forgejo — self-hosted git forge
services.git-forge = {
enable = true;
domain = "git.lagraula.fr";
sshPort = 2222;
httpPort = 3000;
databaseType = "sqlite3";
};
}
@@ -0,0 +1,19 @@
{ config, pkgs, lib, ... }:
{
# ╔══════════════════════════════════════════════════════════╗
# ║ This machine runs Proxmox VE (not NixOS). ║
# ║ This configuration serves as an inventory record ║
# ║ documenting the machine's role and network settings. ║
# ╚══════════════════════════════════════════════════════════╝
networking.hostName = "hyper01";
networking.hostId = "deadbeef01"; # Unique 8-char hex identifier
# Hypervisor management network
# Proxmox management interface: 10.10.128.10/16
# SSH: root@10.10.128.10:22
# Proxmox web UI: https://10.10.128.10:8006
system.stateVersion = "25.11";
}
@@ -0,0 +1,19 @@
{ config, pkgs, lib, ... }:
{
# ╔══════════════════════════════════════════════════════════╗
# ║ This machine runs Proxmox VE (not NixOS). ║
# ║ This configuration serves as an inventory record ║
# ║ documenting the machine's role and network settings. ║
# ╚══════════════════════════════════════════════════════════╝
networking.hostName = "hyper02";
networking.hostId = "deadbeef02"; # Unique 8-char hex identifier
# Hypervisor management network
# Proxmox management interface: 10.10.128.11/16
# SSH: root@10.10.128.11:22
# Proxmox web UI: https://10.10.128.11:8006
system.stateVersion = "25.11";
}
@@ -0,0 +1,35 @@
{ config, pkgs, lib, ... }:
let
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
in {
imports = [
# Module for LXC containers
(framework + "/modules/machine-types/lxc")
# Module for password manager service (Vaultwarden)
(framework + "/modules/services/password-manager/default.nix")
];
# Explicitly enable LXC machine type
lxc.enable = true;
# Host identity (IP address 10.40.128.30/16 assigned via DHCP reservation)
networking.hostName = "pass01";
networking.domain = "prod.lagraula.fr";
networking.useDHCP = true;
# Vaultwarden — Bitwarden-compatible password manager
services.password-manager = {
enable = true;
domain = "pass.lagraula.fr";
port = 8080;
dbBackend = "sqlite";
signupsAllowed = false; # Only admin creates accounts
# Uncomment and configure with agenix secret:
# adminTokenFile = config.age.secrets.vaultwarden-admin-token.path;
};
}
@@ -0,0 +1,35 @@
{ config, pkgs, lib, ... }:
let
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
# Import environment-specific network parameters
env = import ../../../network { };
in {
imports = [
# Module for LXC containers
(framework + "/modules/machine-types/lxc")
# Module for the reverse proxy
(framework + "/modules/services/reverse-proxy/default.nix")
];
# Host identity (IP address assigned via DHCP reservation)
networking.hostName = "rp01";
networking.domain = env.dns.domain;
networking.useDHCP = true;
# Services to expose via the reverse proxy
services.reverse-proxy.publicServices = [
{ host = "git"; internalHost = "git01"; port = 3000; }
{ host = "pass"; internalHost = "pass01"; port = 80; }
# Add other services here (e.g. dns01, etc.)
];
# Caddy-specific configuration (optional)
services.caddy = {
email = env.dns.letsEncryptEmail; # Email for Let's Encrypt
};
}
@@ -0,0 +1,8 @@
{ config, pkgs, lib, ... }:
{
# Workstation configuration for PC-FRIDA
# TODO: Fill in workstation-specific settings
networking.hostName = "PC-FRIDA";
networking.domain = "prod.lagraula.fr";
}
@@ -0,0 +1,15 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all workstations in the production environment
# This file is imported by all workstation configurations
# TODO: Move common workstation settings here later
# Examples:
# - Workstation-specific users/groups
# - Workstation-specific packages
# - Workstation-specific services
# - Workstation-specific security policies
# - Workstation-specific monitoring/alerting
# - Locale: fr_FR.UTF-8 (for workstations)
}
@@ -0,0 +1,21 @@
{ config, pkgs, lib, ... }:
let
# Import environment-specific network parameters
env = import ../../../network { };
in {
# Workstation configuration for gaia
# TODO: Fill in workstation-specific settings
networking.hostName = "gaia";
networking.domain = env.dns.domain;
# Time settings
time.timeZone = env.time.timeZone;
time.hardwareClock = env.time.hardwareClock;
# NTP settings
services.ntp.servers = env.time.ntpServers;
# Locale for workstations (French)
i18n.defaultLocale = "fr_FR.UTF-8";
}
@@ -0,0 +1,8 @@
{ config, pkgs, lib, ... }:
{
# Workstation configuration for sting
# TODO: Fill in workstation-specific settings
networking.hostName = "sting";
networking.domain = "prod.lagraula.fr";
}
@@ -0,0 +1,123 @@
# Network Architecture
This directory contains the centralised, declarative network topology data for
the infrastructure. It is the single source of truth for VLANs, subnets, and
host addresses, consumable both by NixOS configurations (via `import`) and by
external scripts (via `nix eval --json`).
## Files
| File | Purpose |
|------|---------|
| `vlans.nix` | VLAN definitions (ID, name, effective ID, description) |
| `subnets.nix` | IPv4 and IPv6 prefix allocations per VLAN |
| `hosts.nix` | Per-machine address assignments |
| `default.nix` | Aggregator — imports all three files |
## Consumption
### From a NixOS configuration
```nix
{ config, pkgs, lib, ... }:
let
network = import ../../network { };
in {
networking.hostName = "dns01";
networking.useDHCP = true;
}
```
### From a shell script
```bash
# Export all host data as JSON
nix eval --json -f nixos-infra/network/default.nix hosts
# Get a specific host's IPv4 address
nix eval -f nixos-infra/network/default.nix 'hosts.dns01.ipv4'
# Use with jq
nix eval --json -f nixos-infra/network/default.nix hosts | jq '.dns01'
```
## VLANs
| VLAN | ID | Effective ID | Description |
|------|----|--------------|-------------|
| INET | 1 | 1 | ISP uplink — untagged, out of project scope |
| ADMIN | 90 | **100** | Management / hypervisors |
| IOT | 200 | 200 | IoT devices |
| GUEST | 300 | 300 | Guest network |
| DMZ | 400 | 400 | Public-facing servers (LXC containers) |
| INTERNAL | 500 | 500 | Internal workstations |
> **ADMIN exception:** VLAN 90 is treated as effective ID 100 for prefix
> computation. This is a documented exception to avoid a risky production
> change. A future renumbering may bring `id` and `effectiveId` into line.
## Addressing Scheme
### IPv4
The formula is `10.<effectiveId>.0.0/16` per VLAN.
To avoid conflicts with the lower half (DHCP pools, infrastructure
services), **all static host addresses are allocated from the upper half**:
`10.<effectiveId>.128.0/17`.
The gateway (`.1`) is distributed by DHCPv4 and is not statically assigned.
### IPv6
IPv6 prefixes are derived from `effectiveId / 100`:
| Type | Formula | Example (DMZ, effectiveId=400) |
|------|---------|-------------------------------|
| ULA | `fdb2:ae63:d45:d94<N>::/64` | `fdb2:ae63:d45:d944::/64` |
| GUA | `2a01:e0a:2ea:d94<N>::/64` | `2a01:e0a:2ea:d944::/64` |
where `<N>` = `effectiveId / 100` (so 1 for ADMIN, 2 for IOT, …, 5 for
INTERNAL).
IPv6 addresses are formed by appending the host's 64-bit token to the
prefix, e.g. `fdb2:ae63:d45:d944::10` for dns01.
The gateway (`::1` in each subnet) is distributed via Router Advertisement.
### Host Address Allocation
IPv4 addresses are reserved via DHCP reservations. The `hosts.nix` file is
the source of truth for these reservations and is intended to drive DHCP
server configuration (Kea, ISC DHCP, or similar) in a future iteration.
IPv6 tokens are the last 64 bits of the host's address (expressed as `::XX`
shorthand) and are expected to be distributed via SLAAC with the corresponding
prefix.
## Key Design Decisions
1. **Why effectiveId?** The ADMIN VLAN has ID 90 but its IP prefixes are
computed as if ID were 100. Rather than fixing the VLAN ID in production,
the `effectiveId` field captures the exception explicitly and documents it.
2. **Why /16 per VLAN?** Each VLAN has a full `/16` IPv4 prefix, which is
larger than typical container/workstation subnets. This is intentional:
it simplifies VLAN-level routing and leaves room for future growth.
3. **Why DHCP and not static?** IPv4 addresses are distributed by DHCP, not
statically configured on each host. This centralises address management
and makes it easier to audit and change. The `hosts.nix` file is the
source of truth for the DHCP server's configuration.
4. **Why NO static IP in configuration.nix?** In the current state, LXC
containers have no static IP configuration at all — they rely entirely on
DHCP. This is intentional: the addressing plan lives centrally in
`network/hosts.nix` and the per-host `.nix` files only set `hostName` and
`useDHCP = true`. A future DHCP service module (Kea, etc.) will consume
`hosts.nix` to generate reservations.
5. **Why ULA + GUA for IPv6?** Both ULA (fd prefix) and GUA (2a01 prefix)
are defined for each VLAN. ULA provides stable internal addressing
independent of ISP changes; GUA provides global reachability for DMZ
services and outbound connectivity for workstations.
@@ -0,0 +1,18 @@
# Central network data source.
#
# Usage from a configuration.nix:
# network = import ../../network { };
# network.hosts.rp01.ipv4 → "10.40.128.199"
#
# Usage from a shell script (via `nix eval`):
# nix eval --json -f network/default.nix hosts
{
vlans = (import ./vlans.nix).vlans;
subnets = (import ./subnets.nix).subnets;
hosts = (import ./hosts.nix).hosts;
dns = import ./dns.nix;
time = import ./time.nix;
proxy = import ./proxy.nix;
smtp = import ./smtp.nix;
}
@@ -0,0 +1,21 @@
{ config, pkgs, lib, ... }:
{
# Environment-specific DNS domain
domain = "prod.lagraula.fr";
# Default DNS servers for this environment
defaultNameServers = [ "10.40.128.10" "10.40.128.11" ];
# Forwarders for this environment (e.g., upstream DNS)
forwarders = [ "1.1.1.1" "8.8.8.8" ];
# Allow zone transfers only to secondary DNS servers in this environment
allowZoneTransfer = [ "10.40.128.11" ];
# Recursion policy for this environment
recursion = "AllowOnlyForPrivateNetworks";
# Email for Let's Encrypt (Caddy) — can be environment-specific
letsEncryptEmail = "xavier@lagraula.fr";
}
@@ -0,0 +1,87 @@
{
# Host-to-network assignments.
#
# Each entry has:
# vlan - Key referencing the VLAN in vlans.nix
# type - Host role (hypervisor, lxc, workstation)
# ipv4 - Static IPv4 address (reserved via DHCP reservation)
# ipv6Token - Last 64-bit suffix of the IPv6 address (::XX notation)
#
# IPv4 addresses are allocated from the 10.<v>.128.0/17 upper-half
# range to avoid conflicts with lower-half DHCP pools.
#
# IPv6 addresses are formed by appending the token to the VLAN's
# ULA or GUA prefix, e.g. fdb2:ae63:d45:d944::10 for dns01.
hosts = {
hyper01 = {
vlan = "admin";
type = "hypervisor";
ipv4 = "10.10.128.10";
ipv6Token = "::10";
};
hyper02 = {
vlan = "admin";
type = "hypervisor";
ipv4 = "10.10.128.11";
ipv6Token = "::11";
};
rp01 = {
vlan = "dmz";
type = "lxc";
ipv4 = "10.40.128.199";
ipv6Token = "::199";
};
dns01 = {
vlan = "dmz";
type = "lxc";
ipv4 = "10.40.128.10";
ipv6Token = "::10";
};
dns02 = {
vlan = "dmz";
type = "lxc";
ipv4 = "10.40.128.11";
ipv6Token = "::11";
};
git01 = {
vlan = "dmz";
type = "lxc";
ipv4 = "10.40.128.20";
ipv6Token = "::20";
};
pass01 = {
vlan = "dmz";
type = "lxc";
ipv4 = "10.40.128.30";
ipv6Token = "::30";
};
gaia = {
vlan = "internal";
type = "workstation";
ipv4 = "10.50.128.10";
ipv6Token = "::10";
};
"PC-FRIDA" = {
vlan = "internal";
type = "workstation";
ipv4 = "10.50.128.11";
ipv6Token = "::11";
};
sting = {
vlan = "internal";
type = "workstation";
ipv4 = "10.50.128.12";
ipv6Token = "::12";
};
};
}
@@ -0,0 +1,27 @@
{ config, pkgs, lib, ... }:
{
# Proxy configuration for this environment
# Currently no proxy is used — direct access for all hosts
# Workstations: no proxy
workstations = {
httpProxy = "";
httpsProxy = "";
noProxy = "";
};
# Servers: no proxy
servers = {
httpProxy = "";
httpsProxy = "";
noProxy = "";
};
# To enable a proxy later, uncomment and set the proxy URLs:
# workstations = {
# httpProxy = "http://proxy.prod.lagraula.fr:3128";
# httpsProxy = "http://proxy.prod.lagraula.fr:3128";
# noProxy = "localhost,127.0.0.1,::1,10.0.0.0/8";
# };
}
@@ -0,0 +1,22 @@
{ config, pkgs, lib, ... }:
{
# SMTP relay configuration for this environment
# Used by services that need to send email (e.g., monitoring alerts)
# SMTP relay host — can be an internal relay or external service
relayHost = "smtp.lagraula.fr";
relayPort = 587;
# TLS settings
useTLS = true;
useSTARTTLS = true;
# Authentication — currently none (open relay for internal use)
# To add authentication later:
# username = "noreply@prod.lagraula.fr";
# passwordFile = config.age.secrets.smtp-password.path;
# Default "From" address for system emails
fromAddress = "noreply@prod.lagraula.fr";
}
@@ -0,0 +1,141 @@
{
# Subnet definitions per VLAN.
#
# Prefix rules:
# IPv4: 10.<effectiveId>.0.0/16 (hosts in the 10.<effectiveId>.128.0/17 range)
# IPv6 ULA: fdb2:ae63:d45:d94<effectiveId/100>::/64
# IPv6 GUA: 2a01:e0a:2ea:d94<effectiveId/100>::/64
#
# Gateways (.1 on each subnet) are distributed by DHCPv4 and Router
# Advertisement (SLAAC). They are listed in comments for documentation.
#
# Minimum host prefix is /16 for IPv4 (the whole VLAN). Host addresses
# are allocated from the upper half (10.<v>.128.0/17) to avoid conflicts
# with future lower-half allocations (DHCP pools, etc.).
#
# INET (VLAN 1) is the untagged ISP uplink. It is out of scope of this
# project and is listed for documentation only.
subnets = {
inet = {
vlan = "inet";
description = "ISP uplink out of scope";
# No prefixes defined: inet is outside this project's addressing plan.
};
admin = {
vlan = "admin";
description = "Management / hypervisors";
ipv4 = {
prefix = "10.10.0.0";
prefixLength = 16;
hostRange = "10.10.128.0/17";
# gateway = "10.10.128.1";
};
ipv6 = {
ula = {
prefix = "fdb2:ae63:d45:d941::";
prefixLength = 64;
# gateway = "fdb2:ae63:d45:d941::1";
};
gua = {
prefix = "2a01:e0a:2ea:d941::";
prefixLength = 64;
# gateway = "2a01:e0a:2ea:d941::1";
};
};
};
iot = {
vlan = "iot";
description = "IoT devices";
ipv4 = {
prefix = "10.20.0.0";
prefixLength = 16;
hostRange = "10.20.128.0/17";
# gateway = "10.20.128.1";
};
ipv6 = {
ula = {
prefix = "fdb2:ae63:d45:d942::";
prefixLength = 64;
# gateway = "fdb2:ae63:d45:d942::1";
};
gua = {
prefix = "2a01:e0a:2ea:d942::";
prefixLength = 64;
# gateway = "2a01:e0a:2ea:d942::1";
};
};
};
guest = {
vlan = "guest";
description = "Guest network";
ipv4 = {
prefix = "10.30.0.0";
prefixLength = 16;
hostRange = "10.30.128.0/17";
# gateway = "10.30.128.1";
};
ipv6 = {
ula = {
prefix = "fdb2:ae63:d45:d943::";
prefixLength = 64;
# gateway = "fdb2:ae63:d45:d943::1";
};
gua = {
prefix = "2a01:e0a:2ea:d943::";
prefixLength = 64;
# gateway = "2a01:e0a:2ea:d943::1";
};
};
};
dmz = {
vlan = "dmz";
description = "Public-facing servers (LXC containers)";
ipv4 = {
prefix = "10.40.0.0";
prefixLength = 16;
hostRange = "10.40.128.0/17";
# gateway = "10.40.128.1";
};
ipv6 = {
ula = {
prefix = "fdb2:ae63:d45:d944::";
prefixLength = 64;
# gateway = "fdb2:ae63:d45:d944::1";
};
gua = {
prefix = "2a01:e0a:2ea:d944::";
prefixLength = 64;
# gateway = "2a01:e0a:2ea:d944::1";
};
};
};
internal = {
vlan = "internal";
description = "Internal workstations and trusted devices";
ipv4 = {
prefix = "10.50.0.0";
prefixLength = 16;
hostRange = "10.50.128.0/17";
# gateway = "10.50.128.1";
};
ipv6 = {
ula = {
prefix = "fdb2:ae63:d45:d945::";
prefixLength = 64;
# gateway = "fdb2:ae63:d45:d945::1";
};
gua = {
prefix = "2a01:e0a:2ea:d945::";
prefixLength = 64;
# gateway = "2a01:e0a:2ea:d945::1";
};
};
};
};
}
@@ -0,0 +1,16 @@
{ config, pkgs, lib, ... }:
{
# NTP servers for this environment
# The gateway acts as the NTP server (IPv4 .1, IPv6 ::1)
ntpServers = [
"10.10.128.1" # IPv4 gateway
"fd00::1" # IPv6 gateway
];
# Time zone for this environment
timeZone = "Europe/Paris";
# Hardware clock setting
hardwareClock = "UTC";
}
@@ -0,0 +1,59 @@
{
# VLANs used in the infrastructure.
#
# Each VLAN entry has:
# id - The actual IEEE 802.1Q VLAN ID on the network equipment
# name - Short label
# effectiveId - The numeric value used to compute IPv4/IPv6 prefixes.
# This matches `id` for most VLANs, but can differ when
# the VLAN ID does not follow the mathematical scheme.
# e.g. ADMIN is VLAN 90 but prefixes are computed as if VLAN 100.
# description - Free-text purpose of the VLAN
#
# VLAN ID 1 (INET) is the untagged ISP uplink. It is listed here for
# documentation only and is out of scope of this project.
vlans = {
inet = {
id = 1;
name = "INET";
effectiveId = 1;
description = "ISP uplink untagged, out of project scope";
};
admin = {
id = 90;
name = "ADMIN";
effectiveId = 100; # Exception: treated as 100 for prefix computation
description = "Management / hypervisors";
};
iot = {
id = 200;
name = "IOT";
effectiveId = 200;
description = "IoT devices";
};
guest = {
id = 300;
name = "GUEST";
effectiveId = 300;
description = "Guest network";
};
dmz = {
id = 400;
name = "DMZ";
effectiveId = 400;
description = "Public-facing servers (LXC containers)";
};
internal = {
id = 500;
name = "INTERNAL";
effectiveId = 500;
description = "Internal workstations and trusted devices";
};
};
}
@@ -0,0 +1,5 @@
# Ignore all age-encrypted secret files
*.age
# But keep the pubkeys directory (for tracked .age pubkey files)
!pubkeys/
@@ -0,0 +1,4 @@
# Users
User account configurations and environment setup.
Manages SSH keys, sudo permissions, and Home Manager dotfiles.
Centralizes access control and admin profiles.
@@ -0,0 +1,11 @@
{ config, pkgs, lib, ... }:
{
# User configuration for frida
# TODO: Fill in user-specific settings
users.users.frida = {
isNormalUser = true;
description = "Frida";
extraGroups = [ "wheel" ];
};
}
@@ -0,0 +1,10 @@
{ config, pkgs, lib, ... }:
{
# User configuration for guest
# TODO: Fill in user-specific settings
users.users.guest = {
isNormalUser = true;
description = "Guest";
};
}
@@ -0,0 +1,11 @@
{ config, pkgs, lib, ... }:
{
# User configuration for root
# TODO: Fill in root-specific settings
users.root = {
openssh.authorizedKeys.keys = [
# Add SSH public keys for root access
];
};
}
@@ -0,0 +1,11 @@
{ config, pkgs, lib, ... }:
{
# User configuration for xavier
# TODO: Fill in user-specific settings
users.users.xavier = {
isNormalUser = true;
description = "Xavier";
extraGroups = [ "wheel" ];
};
}
@@ -0,0 +1,19 @@
{ config, pkgs, lib, ... }:
let
# Pin to the same framework commit as production — update when testing
# new framework features in staging before rolling to production.
frameworkRev = "c53d997d075236f6d8c2a8e9db0238e46391735a";
framework = builtins.fetchGit {
url = "ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra-framework.git";
rev = frameworkRev;
};
in {
imports = [
(framework + "/lib")
(framework + "/overlays/custom-pkgs.nix")
];
# Staging environment-wide settings
system.stateVersion = "25.11";
}
@@ -0,0 +1,14 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all hosts in the stage environment
# This file is imported by all host configurations (servers and workstations)
# TODO: Move common settings here later
# Examples:
# - Common users/groups
# - Common packages
# - Common services
# - Common security policies
# - Common monitoring/alerting
}
@@ -0,0 +1,15 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all servers in the stage environment
# This file is imported by all server configurations
# TODO: Move common server settings here later
# Examples:
# - Server-specific users/groups
# - Server-specific packages
# - Server-specific services
# - Server-specific security policies
# - Server-specific monitoring/alerting
# - Locale: en_US.UTF-8 (for servers)
}
@@ -0,0 +1,15 @@
{ config, pkgs, lib, ... }:
{
# Common settings for all workstations in the stage environment
# This file is imported by all workstation configurations
# TODO: Move common workstation settings here later
# Examples:
# - Workstation-specific users/groups
# - Workstation-specific packages
# - Workstation-specific services
# - Workstation-specific security policies
# - Workstation-specific monitoring/alerting
# - Locale: fr_FR.UTF-8 (for workstations)
}
@@ -0,0 +1,16 @@
# Central network data source for the stage environment.
#
# Usage from a configuration.nix:
# network = import ../../network { };
# network.dns.domain → "stage.lagraula.fr"
# network.time.timeZone → "Europe/Paris"
#
# Usage from a shell script (via `nix eval`):
# nix eval --json -f network/default.nix dns
{
dns = import ./dns.nix;
time = import ./time.nix;
proxy = import ./proxy.nix;
smtp = import ./smtp.nix;
}
@@ -0,0 +1,23 @@
{ config, pkgs, lib, ... }:
{
# Environment-specific DNS domain
domain = "stage.lagraula.fr";
# Default DNS servers for this environment
# These are the IPs of the DNS servers (e.g., dns-stage01, dns-stage02)
# that will be deployed in this environment.
defaultNameServers = [ "10.40.128.10" "10.40.128.11" ];
# Forwarders for this environment (e.g., upstream DNS)
forwarders = [ "1.1.1.1" "8.8.8.8" ];
# Allow zone transfers only to secondary DNS servers in this environment
allowZoneTransfer = [ "10.40.128.11" ];
# Recursion policy for this environment
recursion = "AllowOnlyForPrivateNetworks";
# Email for Let's Encrypt (Caddy) — can be environment-specific
letsEncryptEmail = "xavier@lagraula.fr";
}
@@ -0,0 +1,27 @@
{ config, pkgs, lib, ... }:
{
# Proxy configuration for this environment
# Currently no proxy is used — direct access for all hosts
# Workstations: no proxy
workstations = {
httpProxy = "";
httpsProxy = "";
noProxy = "";
};
# Servers: no proxy
servers = {
httpProxy = "";
httpsProxy = "";
noProxy = "";
};
# To enable a proxy later, uncomment and set the proxy URLs:
# workstations = {
# httpProxy = "http://proxy.stage.lagraula.fr:3128";
# httpsProxy = "http://proxy.stage.lagraula.fr:3128";
# noProxy = "localhost,127.0.0.1,::1,10.0.0.0/8";
# };
}
@@ -0,0 +1,22 @@
{ config, pkgs, lib, ... }:
{
# SMTP relay configuration for this environment
# Used by services that need to send email (e.g., monitoring alerts)
# SMTP relay host — can be an internal relay or external service
relayHost = "smtp.lagraula.fr";
relayPort = 587;
# TLS settings
useTLS = true;
useSTARTTLS = true;
# Authentication — currently none (open relay for internal use)
# To add authentication later:
# username = "noreply@stage.lagraula.fr";
# passwordFile = config.age.secrets.smtp-password.path;
# Default "From" address for system emails
fromAddress = "noreply@stage.lagraula.fr";
}
@@ -0,0 +1,16 @@
{ config, pkgs, lib, ... }:
{
# NTP servers for this environment
# The gateway acts as the NTP server (IPv4 .1, IPv6 ::1)
ntpServers = [
"10.10.128.1" # IPv4 gateway
"fd00::1" # IPv6 gateway
];
# Time zone for this environment
timeZone = "Europe/Paris";
# Hardware clock setting
hardwareClock = "UTC";
}
@@ -1,32 +0,0 @@
{ config, pkgs, lib, ... }:
{
imports = [
# Module pour les conteneurs LXC
(builtins.callPackage ../../../modules/machine-types/lxc.nix {})
# Module pour le reverse proxy
(builtins.callPackage ../../../modules/services/reverse-proxy/default.nix {})
];
# Configuration réseau (IPv4 + IPv6)
networking.hostName = "rp01";
networking.interfaces.eth0.ipv4.addresses = [
{ address = "10.40.0.199"; prefixLength = 24; }
];
# networking.interfaces.eth0.ipv6.addresses = [
# { address = "2001:db8::1"; prefixLength = 64; }
# ];
# Liste des services à exposer via le reverse proxy
services.reverse-proxy.publicServices = [
{ host = "gitea"; internalHost = "gitea01"; port = 3000; }
{ host = "vaultwarden"; internalHost = "vault01"; port = 80; }
# Ajoutez ici d'autres services (ex: dns01, etc.)
];
# Configuration spécifique à Caddy (optionnelle)
services.caddy = {
# Vous pouvez surcharger des paramètres ici si besoin
email = "xavier@lagraula.fr"; # Email pour Let's Encrypt
};
}
-52
View File
@@ -1,52 +0,0 @@
{ config, modulesPath, pkgs, lib, ... }:
{
imports = [ (modulesPath + "/virtualisation/proxmox-lxc.nix") ];
nix.settings = { sandbox = false; };
proxmoxLXC = {
manageNetwork = false;
privileged = true;
};
# Enable LXC specific options
options.lxc = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable LXC machine type";
};
};
services.fstrim.enable = false; # Let Proxmox host handle fstrim
# Cache DNS lookups to improve performance
services.resolved = {
extraConfig = ''
Cache=true
CacheFromLocalhost=true
'';
};
# Prise en charge IPv6
# TODO : check whether explicitly enabling IPv6 is still encessary in 2026
networking.ipv6.forwarding = true;
# Default configuration for a LXC container
config = lib.mkIf config.lxc.enable {
# Disabling useless services
services.avahi.daemon.enable = false; # TODO : review the need for avahi in a container
services.bluetooth.enable = false;
services.printing.enable = false;
# Optimzing for conainters
boot.kernelModules = [ ]; # TODO : review the disabling of all kernelModules in a container
powerManagement.enable = false;
# Limiter les ressources si nécessaire
# TODO : review the need to limit ZFS pools in the LXC container configuration, in my ZFSless context
boot.zfs.extraPools = [ ];
};
system.stateVersion = "25.11";
}
@@ -1,57 +0,0 @@
{ config, pkgs, lib, ... }:
let
# Récupère la liste des services depuis la configuration
publicServices = config.services.reverse-proxy.publicServices or [];
in
{
# Options pour le module reverse-proxy
options.services.reverse-proxy = {
publicServices = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options = {
host = lib.mkOption { type = lib.types.str; };
internalHost = lib.mkOption { type = lib.types.str; };
port = lib.mkOption { type = lib.types.int; default = 80; };
};
});
default = [];
description = "Liste des services à exposer via le reverse proxy";
};
};
# Configuration de Caddy
config = lib.mkIf (config.services.reverse-proxy.publicServices or []) != [] {
services.caddy = {
enable = true;
virtualHosts = map (service: {
host = "${service.host}.lagraula.fr";
reverseProxy = "http://${service.internalHost}.lagraula.fr:${toString service.port}";
# Challenge ACME HTTP-01 (par défaut)
tls = {
email = "xavier@lagraula.fr"; # À adapter
};
}) (config.services.reverse-proxy.publicServices or []);
# Configuration globale pour Caddy
extraConfig = ''
{
# Rate limiting global (optionnel)
rate_limit {
requests 100
burst 200
interval 1m
}
# Logging
log {
output file /var/log/caddy/access.log
}
}
'';
};
# Ouvrir les ports firewall pour HTTP/HTTPS
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.firewall.allowedUDPPorts = [];
};
}
+107
View File
@@ -0,0 +1,107 @@
# Scripts
Utility scripts for infrastructure management.
Covers deployment, LXC container creation and bootstrap,
initial configuration of new NixOS machines, and age key generation.
## Scripts Overview
### `create-lxc-nixos.sh` — Create and deploy a NixOS LXC container
Creates a NixOS LXC container on a remote Proxmox VE hypervisor, then
bootstraps it with the initial NixOS configuration and runs `deploy.sh`
to apply the host-specific configuration.
```bash
# Usage
./create-lxc-nixos.sh <short_name> [options]
# Example: create dns01 with static IPv4 and IPv6 token
./create-lxc-nixos.sh dns01 \
--ip 10.40.0.10/24 \
--ip6 ::a:b:c:d \
--pve-host pve01.prod.lagraula.fr
# Dry run to preview the commands
./create-lxc-nixos.sh dns01 --dry-run
```
**Bootstrap process:**
1. `pct create` — create the container from the NixOS template
2. `pct start <CT_ID>` — start the container
3. Wait for the container to be ready (polling `pct exec`)
4. `pct push initial-lxc-configuration.nix``/etc/nixos/configuration.nix`
5. `pct push deploy.sh``/usr/local/bin/deploy-nixos`
6. `pct exec nixos-rebuild switch` — apply initial config (SSH, git, curl)
7. `pct exec deploy-nixos` — clone repo and apply host-specific config
### `deploy.sh` — Deploy NixOS configuration from Git repository
Clones or updates the nixos-infra repository, detects the hostname,
finds the corresponding configuration file, and applies it with
`nixos-rebuild switch`.
```bash
# Usage
./deploy.sh [options]
# Options
-u, --repo-url URL Git repository URL (default: https://gitea.lagraula.fr/...)
-d, --repo-dir DIR Local directory (default: /etc/nixos-infra)
-b, --branch BRANCH Git branch (default: main)
-n, --dry-run Simulate without making changes
```
**Configuration lookup order:**
1. `hosts/servers/<hostname>/configuration.nix`
2. `hosts/workstations/<hostname>/configuration.nix`
### `initial-lxc-configuration.nix` — Bootstrap NixOS configuration (LXC)
Minimal NixOS configuration pushed to a new LXC container during the
bootstrap phase. Installs SSH, git, and curl so the container can
clone the repository and apply its specific configuration.
**Pushed to `/etc/nixos/configuration.nix` by `create-lxc-nixos.sh`.**
### `gen-secrets-keys.sh` — Generate age public keys for agenix
Connects to each host in the infrastructure, retrieves its SSH host
key via `ssh-keyscan`, converts it to an age public key with
`ssh-to-age`, and stores it in `secrets/pubkeys/<hostname>.age`.
```bash
# Usage
./gen-secrets-keys.sh
# Prerequisites
# nix-shell -p ssh-to-age
```
**After generating keys, encrypt secrets with:**
```bash
age -r $(cat secrets/pubkeys/<hostname>.age) -o secrets/<name>.age
agenix -e secrets/<name>.age
```
### `update-nixpkgs.sh` — Update the nixpkgs pin
Updates `pkgs/nixpkgs.json` with the latest commit from nixpkgs stable.
## Deployment workflow (LXC containers)
```
create-lxc-nixos.sh # Step 1: Create + bootstrap
└─ pct create
└─ pct push initial-lxc-configuration.nix
└─ pct push deploy.sh
└─ pct exec nixos-rebuild switch
└─ pct exec deploy.sh # Step 2: Clone repo + apply config
└─ git clone
└─ nixos-rebuild switch (host-specific)
```
For subsequent updates on an already-deployed container:
```bash
ssh <hostname>
sudo /usr/local/bin/deploy-nixos
+225 -76
View File
@@ -10,33 +10,45 @@ if ! command -v docopts &> /dev/null; then
fi
# --- Usage and Documentation ---
usage="Create and configure an LXC container on a remote Proxmox VE 9 server.
usage="Create, bootstrap and deploy a NixOS LXC container on a remote Proxmox VE 9 server.
Usage:
$0 <short_name> [options]
$0 -h|--help
Options:
-h, --help Show this message.
-t, --template TEMPLATE LXC template (e.g. local:vztmpl/nixos-unstable).
-r, --rootfs-size SIZE Root filesystem size (e.g. 8G).
-c, --cores CORES Number of CPU cores.
-m, --memory MEMORY RAM in MiB.
-s, --swap SWAP Swap in MiB.
-p, --password PASSWORD Root password for the container.
-b, --bridge BRIDGE Network bridge (e.g. vmbr0).
-v, --vlan VLAN VLAN tag (e.g. tag=10).
-d, --domain DOMAIN DNS domain.
-u, --unprivileged UNPRIV Unprivileged container (0 or 1).
-i, --ip IP Static IP (e.g. 192.168.1.100/24).
-C, --cmode CMODE Console mode (console or tty). Default: console.
-T, --tags TAGS Tags for the container (optional).
-k, --ssh-public-keys KEYS SSH public keys for the container.
--pve-host HOST Proxmox host (e.g. pve).
--pve-user USER Proxmox user (default: admin).
--pve-port PORT SSH port for Proxmox (default: 22).
--pve-password PASSWORD Password for SSH authentication on Proxmox.
--pve-ssh-key KEY SSH key file for authentication.
--dry-run Simulate container creation without execution.
-h, --help Show this message.
-t, --template TEMPLATE LXC template (e.g. local:vztmpl/nixos-unstable).
-r, --rootfs-size SIZE Root filesystem size (e.g. 8G).
-c, --cores CORES Number of CPU cores.
-m, --memory MEMORY RAM in MiB.
-s, --swap SWAP Swap in MiB.
-p, --password PASSWORD Root password for the container.
-b, --bridge BRIDGE Network bridge (e.g. vmbr0).
-v, --vlan VLAN VLAN tag (e.g. tag=10).
-d, --domain DOMAIN DNS domain.
-u, --unprivileged UNPRIV Unprivileged container (0 or 1).
-i, --ip IP Static IPv4 address (e.g. 192.168.1.100/24).
--ip6 TOKEN IPv6 token for SLAAC (e.g. ::1:2:3:4).
-C, --cmode CMODE Console mode (console or tty). Default: console.
-T, --tags TAGS Tags for the container (optional).
-k, --ssh-public-keys KEYS SSH public keys for the container.
--pve-host HOST Proxmox host (e.g. pve).
--pve-user USER Proxmox user (default: admin).
--pve-port PORT SSH port for Proxmox (default: 22).
--pve-ssh-key KEY SSH key file for authentication.
--initial-config FILE Initial NixOS configuration file to push
[default: ./initial-lxc-configuration.nix].
--repo-url URL Git repository URL for deploy.sh
[default: https://gitea.lagraula.fr/xavier/nixos-infra.git].
--branch BRANCH Git branch for deploy.sh [default: main].
--skip-deploy Skip the post-creation bootstrap (push + nixos-rebuild).
--dry-run Simulate container creation without execution.
Optional configuration files (loaded in order, later overrides earlier):
/etc/nixos-infra/hosts/config
\${XDG_CONFIG_HOME}/nixos-infra/hosts/config
./config
"
# --- Default Parameters (Environment Variables) ---
@@ -44,7 +56,6 @@ Options:
PVE_HOST="${PVE_HOST:-}"
PVE_USER="${PVE_USER:-admin}"
PVE_PORT="${PVE_PORT:-22}"
PVE_PASSWORD="${PVE_PASSWORD:-}"
PVE_SSH_KEY="${PVE_SSH_KEY:-}"
DRY_RUN="${DRY_RUN:-false}"
@@ -60,77 +71,132 @@ VLAN="${VLAN:-}"
DOMAIN="${DOMAIN:-lagraula.fr}"
UNPRIVILEGED="${UNPRIVILEGED:-0}"
IP="${IP:-}"
IP6="${IP6:-}"
CMODE="${CMODE:-console}"
TAGS="${TAGS:-}"
SSH_PUBLIC_KEYS="${SSH_PUBLIC_KEYS:-}"
# --- Parse Arguments with docopts (Lowest Priority) ---
# Bootstrap
INITIAL_CONFIG="${INITIAL_CONFIG:-./initial-lxc-configuration.nix}"
REPO_URL="${REPO_URL:-https://gitea.lagraula.fr/xavier/nixos-infra.git}"
BRANCH="${BRANCH:-main}"
SKIP_DEPLOY="${SKIP_DEPLOY:-false}"
# --- Parse Arguments with docopts (Highest priority) ---
# set +e is to prevent set -e from eating the error message from docopts.
# This code is up here to prevent useless error messages to be printed
# in case the "-h" or "--help" argument is used.
set +e
args=$(docopts -h "$usage" : "$@")
eval "$args"
set -e
# Short name of the machine (mandatory parameter)
SHORT_NAME="${argv[0]:-}"
if [ -z "$SHORT_NAME" ]; then
echo "❌ Error: The short name of the machine is required." >&2
echo "$usage" >&2
exit 1
fi
# --- Apply Configuration Files (by increasing priority) ---
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
CONFIG_FILES=(\
"/etc/nixos-infra/hosts/config" \
"$XDG_CONFIG_HOME/nixos-infra/hosts/config" \
"./config")
for conffile in ${CONFIG_FILES[*]}; do
if [ -f "$conffile" ]; then
echo "📄 Applying parameters from $conffile..."
set -a
source "$conffile"
set +a
else
echo "$conffile not found (optional)."
fi
done
# --- Override with /etc/nixos-infra/hosts/<short_name> (Medium Priority)
if [ -f "/etc/nixos-infra/hosts/$SHORT_NAME" ]; then
echo "📄 Applying parameters from /etc/nixos-infra/hosts/$SHORT_NAME..."
set -a
source "/etc/nixos-infra/hosts/$SHORT_NAME"
set +a
fi
# Override with CLI arguments (have priority over config files)
PVE_HOST="${pve_host:-$PVE_HOST}"
PVE_USER="${pve_user:-$PVE_USER}"
PVE_PORT="${pve_port:-$PVE_PORT}"
PVE_SSH_KEY="${pve_ssh_key:-$PVE_SSH_KEY}"
DRY_RUN="${dry_run:-$DRY_RUN}"
# --- Override with ./<short_name> (Medium Priority) ---
if [ -f "./$SHORT_NAME" ]; then
echo "📄 Applying parameters from ./$SHORT_NAME..."
set -a
source "./$SHORT_NAME"
set +a
fi
TEMPLATE="${template:-$TEMPLATE}"
ROOTFS_SIZE="${rootfs_size:-$ROOTFS_SIZE}"
CORES="${cores:-$CORES}"
MEMORY="${memory:-$MEMORY}"
SWAP="${swap:-$SWAP}"
PASSWORD="${password:-$PASSWORD}"
BRIDGE="${bridge:-$BRIDGE}"
VLAN="${vlan:-$VLAN}"
DOMAIN="${domain:-$DOMAIN}"
UNPRIVILEGED="${unprivileged:-$UNPRIVILEGED}"
IP="${ip:-$IP}"
IP6="${ip6:-$IP6}"
CMODE="${cmode:-$CMODE}"
TAGS="${tags:-$TAGS}"
SSH_PUBLIC_KEYS="${ssh_public_keys:-$SSH_PUBLIC_KEYS}"
# --- Apply Command-Line Arguments (Highest Priority) ---
eval "$args"
INITIAL_CONFIG="${initial_config:-$INITIAL_CONFIG}"
REPO_URL="${repo_url:-$REPO_URL}"
BRANCH="${branch:-$BRANCH}"
SKIP_DEPLOY="${skip_deploy:-$SKIP_DEPLOY}"
# --- SSH Key Default Logic ---
if [ "$PVE_SSH_KEY" = "default" ]; then
PVE_SSH_KEY="${HOME}/.ssh/id_${PVE_USER}"
elif [ -z "$PVE_SSH_KEY" ] && [ -z "$PVE_PASSWORD" ]; then
PVE_SSH_KEY="${HOME}/.ssh/id_${PVE_USER}"
fi
# --- Critical Parameters Validation ---
if [ -z "$TEMPLATE" ] || [ -z "$ROOTFS_SIZE" ] || [ -z "$CORES" ] || \
[ -z "$MEMORY" ] || [ -z "$SWAP" ] || [ -z "$PASSWORD" ] || \
[ -z "$BRIDGE" ] || [ -z "$DOMAIN" ] || [ -z "$UNPRIVILEGED" ] || \
[ -z "$CMODE" ] || [ -z "$SSH_PUBLIC_KEYS" ] || \
[ -z "$PVE_HOST" ] || [ -z "$PVE_USER" ] || [ -z "$PVE_PORT" ]; then
echo "❌ Error: One or more critical parameters are missing." >&2
mandatory_params=(
"TEMPLATE"
"ROOTFS_SIZE"
"CORES"
"MEMORY"
"SWAP"
"PASSWORD"
"BRIDGE"
"DOMAIN"
"UNPRIVILEGED"
"CMODE"
"SSH_PUBLIC_KEYS"
"PVE_HOST"
"PVE_USER"
"PVE_PORT"
)
missing_params=()
for param in ${mandatory_params[*]}; do
if [ -z "${!param}" ]; then missing_params+=("$param"); fi
done
if [ ${#missing_params[@]} -gt 0 ]; then
echo "❌ Error: The following necessary parameters are missing: ${missing_params[*]}" >&2
echo "❌ Error: Please provide them through a config file or the command line." >&2
exit 1
fi
# Authentication Validation
if [ -z "$PVE_PASSWORD" ]; then
if [ -z "$PVE_SSH_KEY" ]; then
echo "❌ Error: No authentication parameter is defined." >&2
exit 1
elif [ ! -f "$PVE_SSH_KEY" ]; then
echo "❌ Error: SSH key file '$PVE_SSH_KEY' does not exist." >&2
exit 1
fi
if [ ! -f "$PVE_SSH_KEY" ]; then
echo "❌ Error: SSH key file '$PVE_SSH_KEY' does not exist." >&2
exit 1
fi
# Validate initial-config.nix exists (unless --skip-deploy)
if [ "$SKIP_DEPLOY" != "true" ] && [ ! -f "$INITIAL_CONFIG" ]; then
echo "❌ Error: Initial NixOS configuration '$INITIAL_CONFIG' not found." >&2
echo " Provide a valid path with --initial-config or use --skip-deploy." >&2
exit 1
fi
# Validate deploy.sh exists (unless --skip-deploy)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEPLOY_SCRIPT="${SCRIPT_DIR}/deploy.sh"
if [ "$SKIP_DEPLOY" != "true" ] && [ ! -f "$DEPLOY_SCRIPT" ]; then
echo "❌ Error: deploy.sh not found at '$DEPLOY_SCRIPT'." >&2
echo " The bootstrap phase requires deploy.sh to be present." >&2
exit 1
fi
# --- SSH Connection to Proxmox Server ---
run_proxmox() {
local ssh_cmd="ssh -p $PVE_PORT"
if [ -n "$PVE_SSH_KEY" ] && [ -f "$PVE_SSH_KEY" ]; then
ssh_cmd="$ssh_cmd -i $PVE_SSH_KEY"
ssh_cmd="$ssh_cmd -i $PVE_SSH_KEY "
else
ssh_cmd="$ssh_cmd -o PreferredAuthentications=password \
-o StrictHostKeyChecking=no"
ssh_cmd="$ssh_cmd -o PreferredAuthentications=password "
fi
$ssh_cmd "$PVE_USER@$PVE_HOST" "$1"
}
@@ -143,13 +209,19 @@ fi
if [ -n "$IP" ]; then
NET_OPTS="$NET_OPTS,ip=$IP"
fi
# IPv6: use SLAAC with a token if provided, otherwise DHCP
if [ -n "$IP6" ]; then
NET_OPTS="$NET_OPTS,ip6=auto,token6=${IP6}"
else
NET_OPTS="$NET_OPTS,ip6=dhcp"
fi
# --- Container Creation ---
echo "🚀 Creating LXC container $SHORT_NAME on $PVE_HOST..."
echo "🚀 Creating LXC container $short_name on $PVE_HOST (domain: $DOMAIN)..."
CREATE_CMD="pct create $ROOTFS_SIZE $TEMPLATE --cores $CORES \
--memory $MEMORY --swap $SWAP --hostname $SHORT_NAME.$DOMAIN \
--memory $MEMORY --swap $SWAP --hostname $short_name.$DOMAIN \
--password $PASSWORD --unprivileged $UNPRIVILEGED --net0 $NET_OPTS \
--onboot 1 --cmode $CMODE --ssh-public-keys $SSH_PUBLIC_KEYS"
--onboot 0 --cmode $CMODE --ssh-public-keys $SSH_PUBLIC_KEYS"
if [ -n "$TAGS" ]; then
CREATE_CMD="$CREATE_CMD --tags $TAGS"
fi
@@ -161,12 +233,89 @@ echo "🔧 Command to execute on $PVE_HOST: $DISPLAY_CMD"
# Execute or simulate
if [ "$DRY_RUN" = "true" ]; then
echo "🧪 Dry run: Skipping actual execution."
else
LXC_ID=$(run_proxmox "$CREATE_CMD" | grep -oP '\d+')
if [ -z "$LXC_ID" ]; then
echo "❌ Error: Failed to create the container." >&2
echo "🧪 Dry run mode:"
echo " - Container creation skipped"
echo " - Bootstrap phase skipped"
exit 0
fi
LXC_ID=$(run_proxmox "$CREATE_CMD" | grep -oP '\d+')
if [ -z "$LXC_ID" ]; then
echo "❌ Error: Failed to create the container." >&2
exit 1
fi
echo "✅ LXC container $short_name created successfully (ID: $LXC_ID)."
# --- Bootstrap Phase (unless --skip-deploy) ---
if [ "$SKIP_DEPLOY" = "true" ]; then
echo "⏭️ --skip-deploy set. Container created but not bootstrapped."
echo " Start it manually with: pct start $LXC_ID"
echo " Then apply a configuration manually."
exit 0
fi
echo ""
echo "🚀 Starting bootstrap phase for CT $LXC_ID..."
# 1. Start the container
echo "▶️ Starting container $LXC_ID..."
run_proxmox "pct start $LXC_ID" || {
echo "❌ Error: Failed to start container $LXC_ID." >&2
exit 1
}
# 2. Wait for the container to be ready (SSH or pct exec available)
echo "⏳ Waiting for container to be ready..."
for i in $(seq 1 30); do
if run_proxmox "pct exec $LXC_ID -- true" 2>/dev/null; then
echo "✅ Container $LXC_ID is ready."
break
fi
if [ "$i" -eq 30 ]; then
echo "❌ Error: Container $LXC_ID did not become ready in time." >&2
echo " You can retry bootstrap manually." >&2
exit 1
fi
echo "✅ LXC container $SHORT_NAME created successfully (ID: $LXC_ID)."
fi
sleep 2
done
# 3. Push initial-lxc-configuration.nix
echo "📄 Pushing initial NixOS configuration..."
run_proxmox "pct push $LXC_ID '$INITIAL_CONFIG' /etc/nixos/configuration.nix" || {
echo "❌ Error: Failed to push initial configuration." >&2
exit 1
}
# 4. Push deploy.sh
echo "📄 Pushing deploy script..."
run_proxmox "pct push $LXC_ID '$DEPLOY_SCRIPT' /usr/local/bin/deploy-nixos" || {
echo "❌ Error: Failed to push deploy script." >&2
exit 1
}
run_proxmox "pct exec $LXC_ID -- chmod +x /usr/local/bin/deploy-nixos" || {
echo "❌ Error: Failed to make deploy script executable." >&2
exit 1
}
# 5. Apply initial configuration (nixos-rebuild switch)
echo "⚙️ Applying initial NixOS configuration (this may take a while)..."
if ! run_proxmox "pct exec $LXC_ID -- nixos-rebuild switch" 2>&1; then
echo "⚠️ Warning: Initial nixos-rebuild may have issues." >&2
echo " Check the container manually: pct exec $LXC_ID -- nixos-rebuild switch" >&2
# Continue anyway — deploy.sh might still work
fi
echo "✅ Initial configuration applied."
# 6. Run deploy.sh to clone the repo and apply the specific configuration
echo "🌐 Running deploy.sh to clone repo and apply specific configuration..."
# Pass REPO_URL and BRANCH as environment variables to deploy.sh inside the container
DEPLOY_CMD="REPO_URL='$REPO_URL' BRANCH='$BRANCH' /usr/local/bin/deploy-nixos"
if ! run_proxmox "pct exec $LXC_ID -- bash -c '$DEPLOY_CMD'" 2>&1; then
echo "⚠️ Warning: deploy.sh encountered issues." >&2
echo " You can retry manually: pct exec $LXC_ID -- /usr/local/bin/deploy-nixos" >&2
exit 1
fi
echo ""
echo "🎉 Container $short_name (CT $LXC_ID) successfully created and deployed!"
echo " Connect with: ssh root@${IP%%/*} (or use pct exec $LXC_ID)"
+76 -14
View File
@@ -1,42 +1,104 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_URL="https://gitea.lagraula.fr/xavier/nixos-infra.git"
REPO_DIR="/etc/nixos-infra"
BRANCH="main"
# --- Default values (can be overridden by environment variables) ---
REPO_URL="${REPO_URL:-ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra.git}"
REPO_DIR="${REPO_DIR:-/etc/nixos-infra}"
BRANCH="${BRANCH:-main}"
ENVIRONMENT="${ENVIRONMENT:-production}"
DRY_RUN="${DRY_RUN:-false}"
# --- Usage ---
usage() {
cat <<EOF
Deploy NixOS configuration from the nixos-infra repository.
Usage:
$0 [options]
Options:
-u, --repo-url URL Git repository URL
[default: ${REPO_URL}]
-d, --repo-dir DIR Local directory for the repository
[default: ${REPO_DIR}]
-b, --branch BRANCH Git branch to deploy
[default: ${BRANCH}]
-e, --environment ENV Environment name (production, dev, staging, etc.)
[default: ${ENVIRONMENT}]
-n, --dry-run Simulate deployment without making changes.
-h, --help Show this help message.
Environment variables:
REPO_URL, REPO_DIR, BRANCH, ENVIRONMENT, DRY_RUN (same as options above).
EOF
exit 0
}
# --- Parse arguments ---
while [[ $# -gt 0 ]]; do
case "$1" in
-u|--repo-url) REPO_URL="$2"; shift 2 ;;
-d|--repo-dir) REPO_DIR="$2"; shift 2 ;;
-b|--branch) BRANCH="$2"; shift 2 ;;
-e|--environment) ENVIRONMENT="$2"; shift 2 ;;
-n|--dry-run) DRY_RUN="true"; shift ;;
-h|--help) usage ;;
*) echo "❌ Unknown option: $1" >&2; usage ;;
esac
done
HOSTNAME=$(hostname)
if [ "$(id -u)" -ne 0 ]; then
echo "Ce script doit être exécuté en tant que root." >&2
echo "❌ This script must be run as root." >&2
exit 1
fi
# Cloner ou mettre à jour le dépôt
# --- Dry run mode ---
if [ "$DRY_RUN" = "true" ]; then
echo "🧪 Dry run mode:"
echo " - Repository URL: $REPO_URL"
echo " - Repository dir: $REPO_DIR"
echo " - Branch: $BRANCH"
echo " - Environment: $ENVIRONMENT"
echo " - Hostname: $HOSTNAME"
echo " - Expected config: $REPO_DIR/environments/$ENVIRONMENT/hosts/servers/$HOSTNAME/configuration.nix"
echo ""
echo " Would execute:"
echo " git clone --branch $BRANCH $REPO_URL $REPO_DIR"
echo " nixos-rebuild switch -I nixos-config=...$HOSTNAME/configuration.nix"
exit 0
fi
# --- Clone or update the repository ---
if [ -d "$REPO_DIR/.git" ]; then
echo "Mise à jour du dépôt dans $REPO_DIR..."
echo "🔄 Mise à jour du dépôt dans $REPO_DIR..."
cd "$REPO_DIR"
git fetch origin
git checkout "$BRANCH"
git pull origin "$BRANCH"
else
echo "Clonage du dépôt dans $REPO_DIR..."
echo "📥 Clonage du dépôt dans $REPO_DIR..."
mkdir -p "$REPO_DIR"
git clone --branch "$BRANCH" "$REPO_URL" "$REPO_DIR"
fi
# Trouver la configuration pour cette machine
CONFIG_PATH="$REPO_DIR/hosts/servers/$HOSTNAME/configuration.nix"
# --- Find the configuration for this machine ---
CONFIG_PATH="$REPO_DIR/environments/$ENVIRONMENT/hosts/servers/$HOSTNAME/configuration.nix"
if [ ! -f "$CONFIG_PATH" ]; then
CONFIG_PATH="$REPO_DIR/hosts/workstations/$HOSTNAME/configuration.nix"
CONFIG_PATH="$REPO_DIR/environments/$ENVIRONMENT/hosts/workstations/$HOSTNAME/configuration.nix"
fi
if [ ! -f "$CONFIG_PATH" ]; then
echo "Erreur : Aucune configuration trouvée pour $HOSTNAME dans $REPO_DIR" >&2
echo "Error : No configuration found for $HOSTNAME in environment '$ENVIRONMENT'" >&2
echo " Checked paths :" >&2
echo " - $REPO_DIR/environments/$ENVIRONMENT/hosts/servers/$HOSTNAME/configuration.nix" >&2
echo " - $REPO_DIR/environments/$ENVIRONMENT/hosts/workstations/$HOSTNAME/configuration.nix" >&2
exit 1
fi
# Appliquer la configuration
echo "Déploiement de la configuration pour $HOSTNAME..."
# --- Apply the configuration ---
echo "🚀 Deploying the configuration for $HOSTNAME (environment: $ENVIRONMENT)..."
nixos-rebuild switch -I nixos-config="$CONFIG_PATH"
echo "ploiement terminé avec succès !"
echo "✅ Deployment was successful !"
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail
# --- gen-secrets-keys.sh ---
# Generate age public keys from SSH host keys for all known hosts.
#
# This script retrieves each host's SSH host key, converts it to an
# age public key using ssh-to-age, and stores it in
# secrets/pubkeys/<hostname>.age for use with agenix.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
PUBKEYS_DIR="${PROJECT_DIR}/secrets/pubkeys"
# Ensure ssh-to-age is available
if ! command -v ssh-to-age &> /dev/null; then
echo "❌ Error: 'ssh-to-age' is required."
echo " Install it with: nix-shell -p ssh-to-age"
exit 1
fi
mkdir -p "$PUBKEYS_DIR"
echo "🔑 Generating age public keys from SSH host keys..."
echo " Output directory: $PUBKEYS_DIR"
echo ""
# Known hosts (hostname, user@host, ssh port)
# Add entries as hosts are deployed in the infrastructure
HOSTS=(
# Hypervisors
# "pve01:root@pve01.prod.lagraula.fr:22"
# "pve02:root@pve02.prod.lagraula.fr:22"
# LXC containers (once deployed)
# "dns01:root@dns01.lagraula.fr:22"
# "gitea01:root@gitea01.lagraula.fr:22"
# "vault01:root@vault01.lagraula.fr:22"
# "rp01:root@rp01.lagraula.fr:22"
# Workstations
# "sting:root@sting.lagraula.fr:22"
)
if [ ${#HOSTS[@]} -eq 0 ]; then
echo "⚠️ No hosts configured. Edit the HOSTS array in this script first."
echo ""
echo "For a single host, you can also run manually:"
echo " ssh-keyscan <host> 2>/dev/null | grep ed25519 | awk '{print \$3}' | ssh-to-age > $PUBKEYS_DIR/<hostname>.age"
exit 0
fi
for entry in "${HOSTS[@]}"; do
IFS=':' read -r hostname ssh_user_port <<< "$entry"
IFS='@' read -r ssh_user ssh_host <<< "$ssh_user_port"
echo "🖥️ Processing $hostname ($ssh_user@$ssh_host)..."
age_key=$(ssh-keyscan -t ed25519 "$ssh_host" 2>/dev/null | \
grep "ed25519" | \
awk '{print $3}' | \
ssh-to-age 2>/dev/null || true)
if [ -z "$age_key" ]; then
echo " ⚠️ Could not retrieve age key for $hostname. Skipping."
continue
fi
echo "$age_key" > "$PUBKEYS_DIR/$hostname.age"
echo " ✅ Saved age public key: $age_key"
done
echo ""
echo "🎉 Done! Generated $(ls -1 "$PUBKEYS_DIR"/*.age 2>/dev/null | wc -l) key(s)."
echo ""
echo "To encrypt a secret for specific hosts:"
echo " age -r \$(cat $PUBKEYS_DIR/<hostname>.age) -o secrets/<name>.age"
echo ""
echo "Or with agenix:"
echo " agenix -e secrets/<name>.age"
@@ -1,32 +0,0 @@
{ config, pkgs, ... }:
{
# Install Git, curl, and other required tools
environment.systemPackages = with pkgs; [ git curl ];
# Enable unsecured SSH for initial deployment
services.openssh = {
enable = true;
permitRootLogin = "yes";
passwordAuthentication = true;
};
# Deployment script
system.activationScripts.setup-deploy = ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
# Télécharger le script de déploiement depuis Gitea
curl -o /usr/local/bin/deploy-nixos https://gitea.lagraula.fr/xavier/nixos-infra/raw/main/scripts/deploy.sh
chmod +x /usr/local/bin/deploy-nixos
# Cloner le dépôt (si ce n'est pas déjà fait)
mkdir -p /etc/nixos-infra
if [ ! -d "/etc/nixos-infra/.git" ]; then
git clone https://gitea.lagraula.fr/xavier/nixos-infra.git /etc/nixos-infra
fi
'';
system.stateVersion = "25.11";
}
@@ -0,0 +1,27 @@
{ config, pkgs, ... }:
{
# Install Git, curl, and other required tools
environment.systemPackages = with pkgs; [ git curl ];
# Enable SSH for initial deployment
services.openssh = {
enable = true;
permitRootLogin = "yes";
passwordAuthentication = true;
};
# Clone the repository so deploy.sh can use it
system.activationScripts.setup-deploy = ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
# Create the target directory
mkdir -p /etc/nixos-infra
# The deploy script has already been pushed to /usr/local/bin/deploy-nixos
# by create-lxc-nixos.sh; it will clone the repo and apply the config.
'';
system.stateVersion = "25.11";
}
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# Met à jour le commit de référence pour Nixpkgs
BRANCH="nixos-25.11"
REPO="https://github.com/NixOS/nixpkgs"
JSON_FILE="$(dirname "$0")/../pkgs/nixpkgs.json"
echo "Récupération du dernier commit sur $BRANCH..."
REV=$(git ls-remote $REPO refs/heads/$BRANCH | cut -f1)
if [ -z "$REV" ]; then
echo "Erreur : Impossible de récupérer le commit."
exit 1
fi
echo "Calcul du hash (cela peut prendre un moment)..."
SHA256=$(nix-prefetch-url --unpack "https://github.com/NixOS/nixpkgs/archive/$REV.tar.gz")
cat <<EOF > "$JSON_FILE"
{
"url": "https://github.com/NixOS/nixpkgs/archive/$REV.tar.gz",
"rev": "$REV",
"sha256": "$SHA256"
}
EOF
echo "Succès ! Nixpkgs est maintenant épinglé au commit : $REV"
View File
+17
View File
@@ -0,0 +1,17 @@
{
"name": "nixos-infra",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"help": "^3.0.2"
}
},
"node_modules/help": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/help/-/help-3.0.2.tgz",
"integrity": "sha512-jDd0MU+9xzvOQRC6CIzdjvb+agCvpzQY/Fp11quDnugDO4QQzh134EsLkRQMvFIJBleFkvnXagHFm4MTefkkpA=="
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"dependencies": {
"help": "^3.0.2"
}
}