Compare commits

...

10 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
102 changed files with 1384 additions and 275 deletions
@@ -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.).
@@ -1,14 +0,0 @@
# Project Rules: nixos-infra
## Directories
- `/nixos-infra/hosts/servers`: Server-specific configurations (.nix, lxc.config).
- `/nixos-infra/hosts/workstations`: Workstation-specific configurations (.nix, lxc.config).
- `/nixos-infra/modules/machine-types`: Nix modules standardizing different host types (hypervisor, VM, LXC, workstations, etc.).
- `/nixos-infra/modules/services`: Nix modules standardizing different network infrastructure services (DNS, etc.).
- `/nixos-infra/modules/user-profiles`: Nix modules standardizing different user profiles (admin, dev, office, etc.).
- `/nixos-infra/overlays`: Nix overlays (last resort).
- `/nixos-infra/pkgs`: Custom packages not present in Nixpkgs.
- `/nixos-infra/lib`: Nix utility functions.
- `/nixos-infra/scripts`: Operational and helper scripts.
The entry point for each machine is `hosts/<servers|workstations>/<hostname>/configuration.nix`.
+96 -26
View File
@@ -8,7 +8,7 @@
- **Scope**: Centralized management of **NixOS** configurations for a complete infrastructure (hypervisors, workstations, LXC containers, services). - **Scope**: Centralized management of **NixOS** configurations for a complete infrastructure (hypervisors, workstations, LXC containers, services).
- **Environment**: - **Environment**:
- One **Proxmox** hypervisor currently, a second one coming soon. - One **Proxmox** hypervisor currently, a second one coming soon.
- Several **workstations** (e.g., `dev-xavier`, `mao-julien`, `office`). - Several **workstations** (e.g., `sting`, `gaia`, `PC-FRIDA`).
- **LXC Containers** (on Proxmox) VE for infrastructure services (DNS, Gitea, Bitwarden, etc.) and applications. - **LXC Containers** (on Proxmox) VE for infrastructure services (DNS, Gitea, Bitwarden, etc.) and applications.
- **2 main users** (Xavier, Frida) + system users (`root`, `admin`, `guest`). - **2 main users** (Xavier, Frida) + system users (`root`, `admin`, `guest`).
- **Hardware**: ThinkCentre m710q (16 GB RAM, i3) with **KDE 6.3**. - **Hardware**: ThinkCentre m710q (16 GB RAM, i3) with **KDE 6.3**.
@@ -35,14 +35,16 @@
| **No flakes** | Simplify onboarding and avoid a steep learning curve. | Configuration via `configuration.nix` + `callPackage`. | | **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. | | **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`). | | **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. | | **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. | | **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`. | | **`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. | | **`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. | | **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. | | **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. | | **Unprivileged LXC containers (`--unprivileged 0`)** | NixOS requires privileges to function correctly in LXC. | Functional containers with NixOS. |
| **IPv4, IPv6, VLAN support** | Final target is IPv6-first, or even IPv6-only. | IP plan management to be designed. | | **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. |
--- ---
@@ -82,12 +84,23 @@ nixos-infra/
│ └── gaia/ # Xavier's laptop (audio) │ └── gaia/ # Xavier's laptop (audio)
│ └── configuration.nix │ └── configuration.nix
├── 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 ├── modules/ # Autonomous modules
│ ├── machine-types/ # Machine types │ ├── machine-types/ # Machine types
│ │ ├── hypervisor.nix # Module for hypervisors │ │ ├── hypervisor/
│ │ ├── vm.nix # Module for VMs │ │ │ └── default.nix # Module for hypervisors
│ │ ├── lxc.nix # Module for LXC containers │ │ ├── vm/
│ │ └── workstation.nix # Module for workstations │ │ │ └── default.nix # Module for VMs
│ │ ├── lxc/
│ │ │ └── default.nix # Module for LXC containers
│ │ └── workstation/
│ │ └── default.nix # Module for workstations
│ │ │ │
│ ├── services/ # Services │ ├── services/ # Services
│ │ ├── dns/ │ │ ├── dns/
@@ -104,7 +117,7 @@ nixos-infra/
│ └── user-profiles/ # User profiles (roles) │ └── user-profiles/ # User profiles (roles)
│ ├── admin.nix # Administrator profile │ ├── admin.nix # Administrator profile
│ ├── dev.nix # Developer profile │ ├── dev.nix # Developer profile
│ ├── mao.nix # Audio production profile │ ├── cam.nix # Audio production profile
│ └── standard.nix # Standard profile │ └── standard.nix # Standard profile
├── users/ # Specific users ├── users/ # Specific users
@@ -138,16 +151,16 @@ nixos-infra/
### **Machine Profiles** ### **Machine Profiles**
| Profile | Description | File | Usage | | Profile | Description | File | Usage |
| --------------| --------------------------------------------------------------| ----------------------------------------| ---------------------------------------------| | --------------| --------------------------------------------------------------| ----------------------------------------| ---------------------------------------------|
| `hypervisor` | Common configuration for hypervisors (KVM, libvirtd, etc.). | `modules/machine-types/hypervisor.nix` | Proxmox hypervisors. | | `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.nix` | VMs under Proxmox. | | `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.nix` | LXC containers 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.nix` | Development, audio production, office stations. | | `workstation` | Common configuration for workstations. | `modules/machine-types/workstation/default.nix` | Development, audio production, office stations. |
| Profile | Description | File | | Profile | Description | File |
| ------------| --------------------------------------------------| --------------------------------------| | ------------| --------------------------------------------------| --------------------------------------|
| `admin` | Administrator access (sudo, service management). | `modules/user-profiles/admin.nix` | | `admin` | Administrator access (sudo, service management). | `modules/user-profiles/admin.nix` |
| `dev` | Development environment (tools, permissions). | `modules/user-profiles/dev.nix` | | `dev` | Development environment (tools, permissions). | `modules/user-profiles/dev.nix` |
| `cam` | Computer assisted music (cam). | `modules/user-profiles/cam.nix` | | `cam` | Computer-assisted music (CAM). | `modules/user-profiles/cam.nix` |
| `standard` | Standard user profile (basic access). | `modules/user-profiles/standard.nix` | | `standard` | Standard user profile (basic access). | `modules/user-profiles/standard.nix` |
@@ -157,20 +170,35 @@ nixos-infra/
### **1. Machine List** ### **1. Machine List**
| Name | Machine Type | Services | IPv4 | IPv6 Token | | Name | Machine Type | VLAN | IPv4 | IPv6 Token |
| ----------| --------------------| -------------| ------| ------------| | ----------| --------------------| ---------| ------------------| ------------|
| hyper01 | Hypervisor | Proxmox | | | | hyper01 | Hypervisor | ADMIN | 10.10.128.10/16 | ::10 |
| hyper02 | Hypervisor | Proxmox | | | | hyper02 | Hypervisor | ADMIN | 10.10.128.11/16 | ::11 |
| dns01 | LXC Container | DNS | | | | dns01 | LXC Container | DMZ | 10.40.128.10/16 | ::10 |
| git01 | LXC Container | Git forge | | | | dns02 | LXC Container | DMZ | 10.40.128.11/16 | ::11 |
| pass01 | LXC Container | Password mgr| | | | git01 | LXC Container | DMZ | 10.40.128.20/16 | ::20 |
| rp01 | LXC Container | Reverse proxy| | | | pass01 | LXC Container | DMZ | 10.40.128.30/16 | ::30 |
| sting | Workstation | | | | | rp01 | LXC Container | DMZ | 10.40.128.199/16 | ::199 |
| PC-FRIDA | Workstation | | | | | 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. List of User Profiles** ### **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 | | Name | Role |
@@ -183,7 +211,7 @@ nixos-infra/
--- ---
### **3. User List** ### **4. User List**
| Name | Role | SSH Access | | Name | Role | SSH Access |
| --------| -----------------------| -----------| | --------| -----------------------| -----------|
| root | Superuser | ❌ | | root | Superuser | ❌ |
@@ -194,7 +222,7 @@ nixos-infra/
--- ---
### **4. User/Machine/Profile Mappings** ### **5. User/Machine/Profile Mappings**
| User | Machine | Applied Profiles | Role | | User | Machine | Applied Profiles | Role |
@@ -214,6 +242,43 @@ nixos-infra/
--- ---
## **🌐 Network Architecture**
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`)
### Prefix rules
| 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` |
> **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** ## **🔄 Deployment Workflow**
### **For LXC containers (Proxmox)** ### **For LXC containers (Proxmox)**
@@ -276,6 +341,7 @@ nixos-infra/
| Write the DNS module | Module for the DNS service (Bind). | ⬜ | ⭐⭐⭐ | | Write the DNS module | Module for the DNS service (Bind). | ⬜ | ⭐⭐⭐ |
| Configure `agenix` | Encrypt the first secrets. | ⬜ | ⭐⭐ | | Configure `agenix` | Encrypt the first secrets. | ⬜ | ⭐⭐ |
| Document the process | `README.md` to explain deployment. | ⬜ | ⭐⭐ | | 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. | ⬜ | ⭐ | | Automate with Ansible | Playbook to create multiple containers. | ⬜ | ⭐ |
| Integrate CI/CD | Test configurations before deployment. | ⬜ | ⭐ | | Integrate CI/CD | Test configurations before deployment. | ⬜ | ⭐ |
@@ -293,6 +359,10 @@ nixos-infra/
- **Security** : - **Security** :
- Disable root SSH access once deployment is complete. - Disable root SSH access once deployment is complete.
- Use SSH keys for authentication. - 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** : - **Maintenance** :
- Update `nixpkgs` regularly (`nix-channel --update`). - Update `nixpkgs` regularly (`nix-channel --update`).
- Document changes in the `CHANGELOG.md`. - Document changes in the `CHANGELOG.md`.
+40 -4
View File
@@ -1,5 +1,41 @@
# nixos-infra # nixos-infra — Private environments
Central infrastructure-as-code repository for the NixOS fleet (servers and This repository contains **environment-specific declarations** (hosts, network
workstations). Reproductible deployments via NixOS, agenix for secrets, layout, secrets, users) for the NixOS fleet. Reusable technical components
Proxmox for hypervision. See `../README.md` for an overview. (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,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,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,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
../../../modules/machine-types/lxc.nix
# Module pour le reverse proxy
../../../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 = "git"; internalHost = "git01"; port = 3000; }
{ host = "pass"; internalHost = "pass01"; 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
};
}
-4
View File
@@ -1,4 +0,0 @@
# Lib
Custom Nix utility functions and helpers.
Used across the configuration to factorize code.
Simplifies data and string manipulation.
-2
View File
@@ -1,2 +0,0 @@
# Modules
Reusable NixOS modules for the infrastructure.
-48
View File
@@ -1,48 +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
'';
};
# 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";
}
-22
View File
@@ -1,22 +0,0 @@
{ config, pkgs, lib, ... }:
{
options.secrets = {
enable = lib.mkEnableOption "agenix secret management";
identity = lib.mkOption {
type = lib.types.path;
default = "/etc/ssh/ssh_host_ed25519_key";
description = "Path to the SSH host private key used for age decryption.";
};
};
config = lib.mkIf config.secrets.enable {
age = {
identityPaths = [ config.secrets.identity ];
secrets = { };
};
environment.systemPackages = with pkgs; [ agenix ];
};
}
@@ -1,56 +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}";
tls = {
email = config.services.caddy.email or "xavier@lagraula.fr";
};
}) (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 = [];
};
}
-4
View File
@@ -1,4 +0,0 @@
# Overlays
Custom modifications and extensions to Nixpkgs.
Applies patches or version overrides to existing packages.
Applied globally across the infrastructure.
-4
View File
@@ -1,4 +0,0 @@
self: super: {
# Custom packages and overrides for nixos-infra
# agenix is already available in nixpkgs — no custom overlay needed.
}
-4
View File
@@ -1,4 +0,0 @@
# Packages
Custom software packages not found in upstream Nixpkgs.
Contains project-specific derivations (default.nix).
Can be referenced via overlays or directly by hosts.
-8
View File
@@ -1,8 +0,0 @@
let
spec = builtins.fromJSON (builtins.readFile ./nixpkgs.json);
in
import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/${spec.rev}.tar.gz";
sha256 = spec.sha256;
}) {}
# TODO: add a nixos-infra module
-5
View File
@@ -1,5 +0,0 @@
{
"url": "https://github.com/NixOS/nixpkgs/archive/755f5aa91337890c432639c60b6064bb7fe67769.tar.gz",
"rev": "755f5aa91337890c432639c60b6064bb7fe67769",
"sha256": "1lmn8dicfwmsfdaiw18xjjys78bal6yjy3a41j02my7kw0wlb76a"
}
-22
View File
@@ -1,22 +0,0 @@
bridge=
cmode=
cores=
domain=
dry_run=false
help=false
ip=
memory=
password=
pve_host=
pve_password=
pve_port=
pve_ssh_key=
pve_user=
rootfs_size=
ssh_public_keys=
swap=
tags=
template=
unprivileged=
vlan=
short_name='2'
+3 -3
View File
@@ -30,7 +30,7 @@ to apply the host-specific configuration.
1. `pct create` — create the container from the NixOS template 1. `pct create` — create the container from the NixOS template
2. `pct start <CT_ID>` — start the container 2. `pct start <CT_ID>` — start the container
3. Wait for the container to be ready (polling `pct exec`) 3. Wait for the container to be ready (polling `pct exec`)
4. `pct push initial-configuration.nix``/etc/nixos/configuration.nix` 4. `pct push initial-lxc-configuration.nix``/etc/nixos/configuration.nix`
5. `pct push deploy.sh``/usr/local/bin/deploy-nixos` 5. `pct push deploy.sh``/usr/local/bin/deploy-nixos`
6. `pct exec nixos-rebuild switch` — apply initial config (SSH, git, curl) 6. `pct exec nixos-rebuild switch` — apply initial config (SSH, git, curl)
7. `pct exec deploy-nixos` — clone repo and apply host-specific config 7. `pct exec deploy-nixos` — clone repo and apply host-specific config
@@ -56,7 +56,7 @@ finds the corresponding configuration file, and applies it with
1. `hosts/servers/<hostname>/configuration.nix` 1. `hosts/servers/<hostname>/configuration.nix`
2. `hosts/workstations/<hostname>/configuration.nix` 2. `hosts/workstations/<hostname>/configuration.nix`
### `initial-configuration.nix` — Bootstrap NixOS configuration ### `initial-lxc-configuration.nix` — Bootstrap NixOS configuration (LXC)
Minimal NixOS configuration pushed to a new LXC container during the Minimal NixOS configuration pushed to a new LXC container during the
bootstrap phase. Installs SSH, git, and curl so the container can bootstrap phase. Installs SSH, git, and curl so the container can
@@ -93,7 +93,7 @@ Updates `pkgs/nixpkgs.json` with the latest commit from nixpkgs stable.
``` ```
create-lxc-nixos.sh # Step 1: Create + bootstrap create-lxc-nixos.sh # Step 1: Create + bootstrap
└─ pct create └─ pct create
└─ pct push initial-configuration.nix └─ pct push initial-lxc-configuration.nix
└─ pct push deploy.sh └─ pct push deploy.sh
└─ pct exec nixos-rebuild switch └─ pct exec nixos-rebuild switch
└─ pct exec deploy.sh # Step 2: Clone repo + apply config └─ pct exec deploy.sh # Step 2: Clone repo + apply config
+3 -3
View File
@@ -38,7 +38,7 @@ Options:
--pve-port PORT SSH port for Proxmox (default: 22). --pve-port PORT SSH port for Proxmox (default: 22).
--pve-ssh-key KEY SSH key file for authentication. --pve-ssh-key KEY SSH key file for authentication.
--initial-config FILE Initial NixOS configuration file to push --initial-config FILE Initial NixOS configuration file to push
[default: ./initial-configuration.nix]. [default: ./initial-lxc-configuration.nix].
--repo-url URL Git repository URL for deploy.sh --repo-url URL Git repository URL for deploy.sh
[default: https://gitea.lagraula.fr/xavier/nixos-infra.git]. [default: https://gitea.lagraula.fr/xavier/nixos-infra.git].
--branch BRANCH Git branch for deploy.sh [default: main]. --branch BRANCH Git branch for deploy.sh [default: main].
@@ -77,7 +77,7 @@ TAGS="${TAGS:-}"
SSH_PUBLIC_KEYS="${SSH_PUBLIC_KEYS:-}" SSH_PUBLIC_KEYS="${SSH_PUBLIC_KEYS:-}"
# Bootstrap # Bootstrap
INITIAL_CONFIG="${INITIAL_CONFIG:-./initial-configuration.nix}" INITIAL_CONFIG="${INITIAL_CONFIG:-./initial-lxc-configuration.nix}"
REPO_URL="${REPO_URL:-https://gitea.lagraula.fr/xavier/nixos-infra.git}" REPO_URL="${REPO_URL:-https://gitea.lagraula.fr/xavier/nixos-infra.git}"
BRANCH="${BRANCH:-main}" BRANCH="${BRANCH:-main}"
SKIP_DEPLOY="${SKIP_DEPLOY:-false}" SKIP_DEPLOY="${SKIP_DEPLOY:-false}"
@@ -279,7 +279,7 @@ for i in $(seq 1 30); do
sleep 2 sleep 2
done done
# 3. Push initial-configuration.nix # 3. Push initial-lxc-configuration.nix
echo "📄 Pushing initial NixOS configuration..." echo "📄 Pushing initial NixOS configuration..."
run_proxmox "pct push $LXC_ID '$INITIAL_CONFIG' /etc/nixos/configuration.nix" || { run_proxmox "pct push $LXC_ID '$INITIAL_CONFIG' /etc/nixos/configuration.nix" || {
echo "❌ Error: Failed to push initial configuration." >&2 echo "❌ Error: Failed to push initial configuration." >&2
+19 -14
View File
@@ -2,9 +2,10 @@
set -euo pipefail set -euo pipefail
# --- Default values (can be overridden by environment variables) --- # --- Default values (can be overridden by environment variables) ---
REPO_URL="${REPO_URL:-https://gitea.lagraula.fr/xavier/nixos-infra.git}" REPO_URL="${REPO_URL:-ssh://git@gitea.prod.lagraula.fr:2222/xavier/nixos-infra.git}"
REPO_DIR="${REPO_DIR:-/etc/nixos-infra}" REPO_DIR="${REPO_DIR:-/etc/nixos-infra}"
BRANCH="${BRANCH:-main}" BRANCH="${BRANCH:-main}"
ENVIRONMENT="${ENVIRONMENT:-production}"
DRY_RUN="${DRY_RUN:-false}" DRY_RUN="${DRY_RUN:-false}"
# --- Usage --- # --- Usage ---
@@ -22,11 +23,13 @@ Options:
[default: ${REPO_DIR}] [default: ${REPO_DIR}]
-b, --branch BRANCH Git branch to deploy -b, --branch BRANCH Git branch to deploy
[default: ${BRANCH}] [default: ${BRANCH}]
-e, --environment ENV Environment name (production, dev, staging, etc.)
[default: ${ENVIRONMENT}]
-n, --dry-run Simulate deployment without making changes. -n, --dry-run Simulate deployment without making changes.
-h, --help Show this help message. -h, --help Show this help message.
Environment variables: Environment variables:
REPO_URL, REPO_DIR, BRANCH, DRY_RUN (same as options above). REPO_URL, REPO_DIR, BRANCH, ENVIRONMENT, DRY_RUN (same as options above).
EOF EOF
exit 0 exit 0
} }
@@ -34,11 +37,12 @@ EOF
# --- Parse arguments --- # --- Parse arguments ---
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
-u|--repo-url) REPO_URL="$2"; shift 2 ;; -u|--repo-url) REPO_URL="$2"; shift 2 ;;
-d|--repo-dir) REPO_DIR="$2"; shift 2 ;; -d|--repo-dir) REPO_DIR="$2"; shift 2 ;;
-b|--branch) BRANCH="$2"; shift 2 ;; -b|--branch) BRANCH="$2"; shift 2 ;;
-n|--dry-run) DRY_RUN="true"; shift ;; -e|--environment) ENVIRONMENT="$2"; shift 2 ;;
-h|--help) usage ;; -n|--dry-run) DRY_RUN="true"; shift ;;
-h|--help) usage ;;
*) echo "❌ Unknown option: $1" >&2; usage ;; *) echo "❌ Unknown option: $1" >&2; usage ;;
esac esac
done done
@@ -56,8 +60,9 @@ if [ "$DRY_RUN" = "true" ]; then
echo " - Repository URL: $REPO_URL" echo " - Repository URL: $REPO_URL"
echo " - Repository dir: $REPO_DIR" echo " - Repository dir: $REPO_DIR"
echo " - Branch: $BRANCH" echo " - Branch: $BRANCH"
echo " - Environment: $ENVIRONMENT"
echo " - Hostname: $HOSTNAME" echo " - Hostname: $HOSTNAME"
echo " - Expected config: $REPO_DIR/hosts/servers/$HOSTNAME/configuration.nix" echo " - Expected config: $REPO_DIR/environments/$ENVIRONMENT/hosts/servers/$HOSTNAME/configuration.nix"
echo "" echo ""
echo " Would execute:" echo " Would execute:"
echo " git clone --branch $BRANCH $REPO_URL $REPO_DIR" echo " git clone --branch $BRANCH $REPO_URL $REPO_DIR"
@@ -79,21 +84,21 @@ else
fi fi
# --- Find the configuration for this machine --- # --- Find the configuration for this machine ---
CONFIG_PATH="$REPO_DIR/hosts/servers/$HOSTNAME/configuration.nix" CONFIG_PATH="$REPO_DIR/environments/$ENVIRONMENT/hosts/servers/$HOSTNAME/configuration.nix"
if [ ! -f "$CONFIG_PATH" ]; then 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 fi
if [ ! -f "$CONFIG_PATH" ]; then if [ ! -f "$CONFIG_PATH" ]; then
echo "❌ Error : No configuration found for $HOSTNAME in $REPO_DIR" >&2 echo "❌ Error : No configuration found for $HOSTNAME in environment '$ENVIRONMENT'" >&2
echo " Checked paths :" >&2 echo " Checked paths :" >&2
echo " - $REPO_DIR/hosts/servers/$HOSTNAME/configuration.nix" >&2 echo " - $REPO_DIR/environments/$ENVIRONMENT/hosts/servers/$HOSTNAME/configuration.nix" >&2
echo " - $REPO_DIR/hosts/workstations/$HOSTNAME/configuration.nix" >&2 echo " - $REPO_DIR/environments/$ENVIRONMENT/hosts/workstations/$HOSTNAME/configuration.nix" >&2
exit 1 exit 1
fi fi
# --- Apply the configuration --- # --- Apply the configuration ---
echo "🚀 Deploying the configuration for $HOSTNAME..." echo "🚀 Deploying the configuration for $HOSTNAME (environment: $ENVIRONMENT)..."
nixos-rebuild switch -I nixos-config="$CONFIG_PATH" nixos-rebuild switch -I nixos-config="$CONFIG_PATH"
echo "✅ Deployment was successful !" echo "✅ Deployment was successful !"

Some files were not shown because too many files have changed in this diff Show More