First attempt at LXC container deployment chain.
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
{ 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 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
self: super: {
|
||||||
|
# Custom packages and overrides for nixos-infra
|
||||||
|
# agenix is already available in nixpkgs — no custom overlay needed.
|
||||||
|
}
|
||||||
@@ -1,4 +1,107 @@
|
|||||||
# Scripts
|
# Scripts
|
||||||
|
|
||||||
Utility scripts for infrastructure management.
|
Utility scripts for infrastructure management.
|
||||||
Covers deployment, LXC container creation, and
|
Covers deployment, LXC container creation and bootstrap,
|
||||||
initial bootstrap of new NixOS machines.
|
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-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-configuration.nix` — Bootstrap NixOS configuration
|
||||||
|
|
||||||
|
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 unstable.
|
||||||
|
|
||||||
|
## Deployment workflow (LXC containers)
|
||||||
|
|
||||||
|
```
|
||||||
|
create-lxc-nixos.sh # Step 1: Create + bootstrap
|
||||||
|
└─ pct create
|
||||||
|
└─ pct push initial-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
|
||||||
@@ -10,35 +10,42 @@ if ! command -v docopts &> /dev/null; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Usage and Documentation ---
|
# --- 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:
|
Usage:
|
||||||
$0 <short_name> [options]
|
$0 <short_name> [options]
|
||||||
$0 -h|--help
|
$0 -h|--help
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Show this message.
|
-h, --help Show this message.
|
||||||
-t, --template TEMPLATE LXC template (e.g. local:vztmpl/nixos-unstable).
|
-t, --template TEMPLATE LXC template (e.g. local:vztmpl/nixos-unstable).
|
||||||
-r, --rootfs-size SIZE Root filesystem size (e.g. 8G).
|
-r, --rootfs-size SIZE Root filesystem size (e.g. 8G).
|
||||||
-c, --cores CORES Number of CPU cores.
|
-c, --cores CORES Number of CPU cores.
|
||||||
-m, --memory MEMORY RAM in MiB.
|
-m, --memory MEMORY RAM in MiB.
|
||||||
-s, --swap SWAP Swap in MiB.
|
-s, --swap SWAP Swap in MiB.
|
||||||
-p, --password PASSWORD Root password for the container.
|
-p, --password PASSWORD Root password for the container.
|
||||||
-b, --bridge BRIDGE Network bridge (e.g. vmbr0).
|
-b, --bridge BRIDGE Network bridge (e.g. vmbr0).
|
||||||
-v, --vlan VLAN VLAN tag (e.g. tag=10).
|
-v, --vlan VLAN VLAN tag (e.g. tag=10).
|
||||||
-d, --domain DOMAIN DNS domain.
|
-d, --domain DOMAIN DNS domain.
|
||||||
-u, --unprivileged UNPRIV Unprivileged container (0 or 1).
|
-u, --unprivileged UNPRIV Unprivileged container (0 or 1).
|
||||||
-i, --ip IP Static IP (e.g. 192.168.1.100/24).
|
-i, --ip IP Static IPv4 address (e.g. 192.168.1.100/24).
|
||||||
-C, --cmode CMODE Console mode (console or tty). Default: console.
|
--ip6 TOKEN IPv6 token for SLAAC (e.g. ::1:2:3:4).
|
||||||
-T, --tags TAGS Tags for the container (optional).
|
-C, --cmode CMODE Console mode (console or tty). Default: console.
|
||||||
-k, --ssh-public-keys KEYS SSH public keys for the container.
|
-T, --tags TAGS Tags for the container (optional).
|
||||||
--pve-host HOST Proxmox host (e.g. pve).
|
-k, --ssh-public-keys KEYS SSH public keys for the container.
|
||||||
--pve-user USER Proxmox user (default: admin).
|
--pve-host HOST Proxmox host (e.g. pve).
|
||||||
--pve-port PORT SSH port for Proxmox (default: 22).
|
--pve-user USER Proxmox user (default: admin).
|
||||||
--pve-ssh-key KEY SSH key file for authentication.
|
--pve-port PORT SSH port for Proxmox (default: 22).
|
||||||
--dry-run Simulate container creation without execution.
|
--pve-ssh-key KEY SSH key file for authentication.
|
||||||
|
--initial-config FILE Initial NixOS configuration file to push
|
||||||
|
[default: ./initial-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:
|
Optional configuration files (loaded in order, later overrides earlier):
|
||||||
/etc/nixos-infra/hosts/config
|
/etc/nixos-infra/hosts/config
|
||||||
\${XDG_CONFIG_HOME}/nixos-infra/hosts/config
|
\${XDG_CONFIG_HOME}/nixos-infra/hosts/config
|
||||||
./config
|
./config
|
||||||
@@ -64,10 +71,17 @@ VLAN="${VLAN:-}"
|
|||||||
DOMAIN="${DOMAIN:-lagraula.fr}"
|
DOMAIN="${DOMAIN:-lagraula.fr}"
|
||||||
UNPRIVILEGED="${UNPRIVILEGED:-0}"
|
UNPRIVILEGED="${UNPRIVILEGED:-0}"
|
||||||
IP="${IP:-}"
|
IP="${IP:-}"
|
||||||
|
IP6="${IP6:-}"
|
||||||
CMODE="${CMODE:-console}"
|
CMODE="${CMODE:-console}"
|
||||||
TAGS="${TAGS:-}"
|
TAGS="${TAGS:-}"
|
||||||
SSH_PUBLIC_KEYS="${SSH_PUBLIC_KEYS:-}"
|
SSH_PUBLIC_KEYS="${SSH_PUBLIC_KEYS:-}"
|
||||||
|
|
||||||
|
# Bootstrap
|
||||||
|
INITIAL_CONFIG="${INITIAL_CONFIG:-./initial-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) ---
|
# --- Parse Arguments with docopts (Highest priority) ---
|
||||||
# set +e is to prevent set -e from eating the error message from docopts.
|
# 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
|
# This code is up here to prevent useless error messages to be printed
|
||||||
@@ -90,18 +104,17 @@ for conffile in ${CONFIG_FILES[*]}; do
|
|||||||
source "$conffile"
|
source "$conffile"
|
||||||
set +a
|
set +a
|
||||||
else
|
else
|
||||||
echo "❌ $conffile not found."
|
echo "ℹ️ $conffile not found (optional)."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Proxmox Server
|
# Override with CLI arguments (have priority over config files)
|
||||||
PVE_HOST="${pve_host:-$PVE_HOST}"
|
PVE_HOST="${pve_host:-$PVE_HOST}"
|
||||||
PVE_USER="${pve_user:-$PVE_USER}"
|
PVE_USER="${pve_user:-$PVE_USER}"
|
||||||
PVE_PORT="${pve_port:-$PVE_PORT}"
|
PVE_PORT="${pve_port:-$PVE_PORT}"
|
||||||
PVE_SSH_KEY="${pve_ssh_key:-$PVE_SSH_KEY}"
|
PVE_SSH_KEY="${pve_ssh_key:-$PVE_SSH_KEY}"
|
||||||
DRY_RUN="${dry_run:-$DRY_RUN}"
|
DRY_RUN="${dry_run:-$DRY_RUN}"
|
||||||
|
|
||||||
# LXC Container
|
|
||||||
TEMPLATE="${template:-$TEMPLATE}"
|
TEMPLATE="${template:-$TEMPLATE}"
|
||||||
ROOTFS_SIZE="${rootfs_size:-$ROOTFS_SIZE}"
|
ROOTFS_SIZE="${rootfs_size:-$ROOTFS_SIZE}"
|
||||||
CORES="${cores:-$CORES}"
|
CORES="${cores:-$CORES}"
|
||||||
@@ -113,10 +126,16 @@ VLAN="${vlan:-$VLAN}"
|
|||||||
DOMAIN="${domain:-$DOMAIN}"
|
DOMAIN="${domain:-$DOMAIN}"
|
||||||
UNPRIVILEGED="${unprivileged:-$UNPRIVILEGED}"
|
UNPRIVILEGED="${unprivileged:-$UNPRIVILEGED}"
|
||||||
IP="${ip:-$IP}"
|
IP="${ip:-$IP}"
|
||||||
|
IP6="${ip6:-$IP6}"
|
||||||
CMODE="${cmode:-$CMODE}"
|
CMODE="${cmode:-$CMODE}"
|
||||||
TAGS="${tags:-$TAGS}"
|
TAGS="${tags:-$TAGS}"
|
||||||
SSH_PUBLIC_KEYS="${ssh_public_keys:-$SSH_PUBLIC_KEYS}"
|
SSH_PUBLIC_KEYS="${ssh_public_keys:-$SSH_PUBLIC_KEYS}"
|
||||||
|
|
||||||
|
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 ---
|
# --- SSH Key Default Logic ---
|
||||||
if [ "$PVE_SSH_KEY" = "default" ]; then
|
if [ "$PVE_SSH_KEY" = "default" ]; then
|
||||||
PVE_SSH_KEY="${HOME}/.ssh/id_${PVE_USER}"
|
PVE_SSH_KEY="${HOME}/.ssh/id_${PVE_USER}"
|
||||||
@@ -124,19 +143,19 @@ fi
|
|||||||
|
|
||||||
# --- Critical Parameters Validation ---
|
# --- Critical Parameters Validation ---
|
||||||
mandatory_params=(
|
mandatory_params=(
|
||||||
"TEMPLATE" \
|
"TEMPLATE"
|
||||||
"ROOTFS_SIZE" \
|
"ROOTFS_SIZE"
|
||||||
"CORES" \
|
"CORES"
|
||||||
"MEMORY" \
|
"MEMORY"
|
||||||
"SWAP" \
|
"SWAP"
|
||||||
"PASSWORD" \
|
"PASSWORD"
|
||||||
"BRIDGE" \
|
"BRIDGE"
|
||||||
"DOMAIN" \
|
"DOMAIN"
|
||||||
"UNPRIVILEGED" \
|
"UNPRIVILEGED"
|
||||||
"CMODE" \
|
"CMODE"
|
||||||
"SSH_PUBLIC_KEYS" \
|
"SSH_PUBLIC_KEYS"
|
||||||
"PVE_HOST" \
|
"PVE_HOST"
|
||||||
"PVE_USER" \
|
"PVE_USER"
|
||||||
"PVE_PORT"
|
"PVE_PORT"
|
||||||
)
|
)
|
||||||
missing_params=()
|
missing_params=()
|
||||||
@@ -145,7 +164,7 @@ for param in ${mandatory_params[*]}; do
|
|||||||
done
|
done
|
||||||
if [ ${#missing_params[@]} -gt 0 ]; then
|
if [ ${#missing_params[@]} -gt 0 ]; then
|
||||||
echo "❌ Error: The following necessary parameters are missing: ${missing_params[*]}" >&2
|
echo "❌ Error: The following necessary parameters are missing: ${missing_params[*]}" >&2
|
||||||
echo "❌ Error: Plesase provide them through one the proposed config file or the command line." >&2
|
echo "❌ Error: Please provide them through a config file or the command line." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -155,6 +174,22 @@ if [ ! -f "$PVE_SSH_KEY" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
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 ---
|
# --- SSH Connection to Proxmox Server ---
|
||||||
run_proxmox() {
|
run_proxmox() {
|
||||||
local ssh_cmd="ssh -p $PVE_PORT"
|
local ssh_cmd="ssh -p $PVE_PORT"
|
||||||
@@ -174,9 +209,15 @@ fi
|
|||||||
if [ -n "$IP" ]; then
|
if [ -n "$IP" ]; then
|
||||||
NET_OPTS="$NET_OPTS,ip=$IP"
|
NET_OPTS="$NET_OPTS,ip=$IP"
|
||||||
fi
|
fi
|
||||||
set -x
|
# 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 ---
|
# --- 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 \
|
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 \
|
--password $PASSWORD --unprivileged $UNPRIVILEGED --net0 $NET_OPTS \
|
||||||
@@ -192,12 +233,89 @@ echo "🔧 Command to execute on $PVE_HOST: $DISPLAY_CMD"
|
|||||||
|
|
||||||
# Execute or simulate
|
# Execute or simulate
|
||||||
if [ "$DRY_RUN" = "true" ]; then
|
if [ "$DRY_RUN" = "true" ]; then
|
||||||
echo "🧪 Dry run: Skipping actual execution."
|
echo "🧪 Dry run mode:"
|
||||||
else
|
echo " - Container creation skipped"
|
||||||
LXC_ID=$(run_proxmox "$CREATE_CMD" | grep -oP '\d+')
|
echo " - Bootstrap phase skipped"
|
||||||
if [ -z "$LXC_ID" ]; then
|
exit 0
|
||||||
echo "❌ Error: Failed to create the container." >&2
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "✅ LXC container $short_name created successfully (ID: $LXC_ID)."
|
sleep 2
|
||||||
fi
|
done
|
||||||
|
|
||||||
|
# 3. Push initial-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)"
|
||||||
@@ -1,42 +1,99 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
REPO_URL="https://gitea.lagraula.fr/xavier/nixos-infra.git"
|
# --- Default values (can be overridden by environment variables) ---
|
||||||
REPO_DIR="/etc/nixos-infra"
|
REPO_URL="${REPO_URL:-https://gitea.lagraula.fr/xavier/nixos-infra.git}"
|
||||||
BRANCH="main"
|
REPO_DIR="${REPO_DIR:-/etc/nixos-infra}"
|
||||||
|
BRANCH="${BRANCH:-main}"
|
||||||
|
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}]
|
||||||
|
-n, --dry-run Simulate deployment without making changes.
|
||||||
|
-h, --help Show this help message.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
REPO_URL, REPO_DIR, BRANCH, 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 ;;
|
||||||
|
-n|--dry-run) DRY_RUN="true"; shift ;;
|
||||||
|
-h|--help) usage ;;
|
||||||
|
*) echo "❌ Unknown option: $1" >&2; usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
HOSTNAME=$(hostname)
|
HOSTNAME=$(hostname)
|
||||||
|
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
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
|
exit 1
|
||||||
fi
|
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 " - Hostname: $HOSTNAME"
|
||||||
|
echo " - Expected config: $REPO_DIR/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
|
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"
|
cd "$REPO_DIR"
|
||||||
git fetch origin
|
git fetch origin
|
||||||
git checkout "$BRANCH"
|
git checkout "$BRANCH"
|
||||||
git pull origin "$BRANCH"
|
git pull origin "$BRANCH"
|
||||||
else
|
else
|
||||||
echo "Clonage du dépôt dans $REPO_DIR..."
|
echo "📥 Clonage du dépôt dans $REPO_DIR..."
|
||||||
mkdir -p "$REPO_DIR"
|
mkdir -p "$REPO_DIR"
|
||||||
git clone --branch "$BRANCH" "$REPO_URL" "$REPO_DIR"
|
git clone --branch "$BRANCH" "$REPO_URL" "$REPO_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Trouver la configuration pour cette machine
|
# --- Find the configuration for this machine ---
|
||||||
CONFIG_PATH="$REPO_DIR/hosts/servers/$HOSTNAME/configuration.nix"
|
CONFIG_PATH="$REPO_DIR/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/hosts/workstations/$HOSTNAME/configuration.nix"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "$CONFIG_PATH" ]; then
|
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 $REPO_DIR" >&2
|
||||||
|
echo " Checked paths :" >&2
|
||||||
|
echo " - $REPO_DIR/hosts/servers/$HOSTNAME/configuration.nix" >&2
|
||||||
|
echo " - $REPO_DIR/hosts/workstations/$HOSTNAME/configuration.nix" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Appliquer la configuration
|
# --- Apply the configuration ---
|
||||||
echo "Déploiement de la configuration pour $HOSTNAME..."
|
echo "🚀 Deploying the configuration for $HOSTNAME..."
|
||||||
nixos-rebuild switch -I nixos-config="$CONFIG_PATH"
|
nixos-rebuild switch -I nixos-config="$CONFIG_PATH"
|
||||||
|
|
||||||
echo "Déploiement terminé avec succès !"
|
echo "✅ Deployment was successful !"
|
||||||
@@ -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"
|
||||||
@@ -4,29 +4,24 @@
|
|||||||
# Install Git, curl, and other required tools
|
# Install Git, curl, and other required tools
|
||||||
environment.systemPackages = with pkgs; [ git curl ];
|
environment.systemPackages = with pkgs; [ git curl ];
|
||||||
|
|
||||||
# Enable unsecured SSH for initial deployment
|
# Enable SSH for initial deployment
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
permitRootLogin = "yes";
|
permitRootLogin = "yes";
|
||||||
passwordAuthentication = true;
|
passwordAuthentication = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Deployment script
|
# Clone the repository so deploy.sh can use it
|
||||||
system.activationScripts.setup-deploy = ''
|
system.activationScripts.setup-deploy = ''
|
||||||
#!${pkgs.bash}/bin/bash
|
#!${pkgs.bash}/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Télécharger le script de déploiement depuis Gitea
|
# Create the target directory
|
||||||
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
|
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
|
# The deploy script has already been pushed to /usr/local/bin/deploy-nixos
|
||||||
fi
|
# by create-lxc-nixos.sh; it will clone the repo and apply the config.
|
||||||
'';
|
'';
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Ignore all age-encrypted secret files
|
||||||
|
*.age
|
||||||
|
|
||||||
|
# But keep the pubkeys directory (for tracked .age pubkey files)
|
||||||
|
!pubkeys/
|
||||||
Reference in New Issue
Block a user