1
0
Fork
You've already forked distributio
0
A Go REST API that generates **Debian preseed** files on the fly from **Netbox** data, keyed by MAC address, and serves **static files** (kernel, initrd, iPXE scripts...) resolved according to the device role.
  • Go 82.1%
  • Go Template 9.3%
  • Dockerfile 4.5%
  • Shell 4.1%
spn109 11e0db6f2d Actualiser README.md
Changement du titre
2026年06月23日 10:16:14 +02:00
cmd - Changement des url d'import de distribution 2026年06月11日 18:04:51 +02:00
deploy Debug docker-compose 2026年06月18日 11:44:06 +02:00
internal use template name is now in config file 2026年06月12日 11:18:00 +02:00
static Debug docker-compose 2026年06月18日 11:44:06 +02:00
templates/preseed first commit 2026年06月11日 17:42:16 +02:00
.dockerignore - Changement des url d'import de distribution 2026年06月11日 18:04:51 +02:00
.gitignore config.yaml to config.yaml.example 2026年06月12日 14:21:23 +02:00
_pttest_main.go - Changement des url d'import de distribution 2026年06月11日 18:04:51 +02:00
config.yaml.example config.yaml to config.yaml.example 2026年06月12日 14:21:23 +02:00
docker-compose.yml Change Git ignore 2026年06月11日 18:08:21 +02:00
Dockerfile Debug docker-compose 2026年06月18日 11:44:06 +02:00
go.mod - Changement des url d'import de distribution 2026年06月11日 18:04:51 +02:00
go.sum first commit 2026年06月11日 17:42:16 +02:00
LICENSE-2.0.txt Ajout de la licence 2026年06月16日 09:55:49 +02:00
README.md Actualiser README.md 2026年06月23日 10:16:14 +02:00

Distributio

🇫🇷 Version française · 🇬🇧 English version


Français

API REST en Go qui génère à la volée des fichiers preseed Debian depuis les données Netbox, identifiées par adresse MAC, et qui sert des fichiers statiques (noyau, initrd, scripts iPXE...) résolus selon le rôle du device.

Fonctionnalités

  • Génération de preseed Debian depuis les données Netbox (hostname, IP, rôle).
  • Surcharges de configuration par rôle (DNS, NTP, paquets, gateway, etc.).
  • Service de fichiers statiques avec résolution par rôle via static_dir, type MIME automatique et support des requêtes Range (http.ServeContent).
  • Rechargement à chaud de la configuration (modification de config.yaml sans redémarrage).
  • Protection contre le path traversal sur les fichiers statiques.
  • Variables d'environnement dans config.yaml (${VAR}) pour les secrets.
  • Cache TTL des résolutions Netbox (MAC→device) pour réduire la charge API.
  • Logs structurés (slog, texte ou JSON) et métriques Prometheus sur /metrics.
  • Arrêt propre (graceful shutdown) sur SIGINT/SIGTERM et timeouts HTTP.

Architecture

netbox-pxe-api/
├── cmd/main.go # Point d'entrée
├── config.yaml # Configuration
├── Dockerfile
├── internal/
│ ├── config/config.go # Chargement, validation, hot reload, env vars
│ ├── netbox/client.go # Client HTTP Netbox + cache TTL
│ ├── preseed/generator.go # Génération preseed (text/template)
│ ├── shared/template_data.go # Construction des données de template
│ ├── metrics/metrics.go # Compteurs au format Prometheus
│ └── api/ # Handlers HTTP, middleware (logs/metrics)
├── templates/
│ └── preseed/ansible.preseed.tmpl # Template preseed
└── static/ # Fichiers statiques
 ├── boot.ipxe
 └── <static_dir>/... # Sous-répertoires par rôle

Configuration (config.yaml)

server:listen:"0.0.0.0:8080"debug:falsestatic_dir:"./static"# racine des fichiers statiquestemplate_dir:"./templates"# racine des templatesnetbox:url:"https://netbox.example.com"token:"${NETBOX_TOKEN}"# ou valeur en clair ; ${VAR} = variable d'envtimeout_seconds:10tls_skip_verify:truecache_ttl_seconds:30# cache MAC→device (0 = désactivé)api_paths:device_by_mac:"/api/dcim/interfaces/?mac_address={mac}"device_detail:"/api/dcim/devices/{id}/"ip_addresses:"/api/ipam/ip-addresses/?interface_id={interface_id}"defaults:# valeurs appliquées à tous les devicesdns_servers:["8.8.8.8","8.8.4.4"]ntp_servers:["pool.ntp.org"]domain:"local.domain"locale:"fr_FR.UTF-8"timezone:"Europe/Paris"gateway:"192.168.1.254"root_password_crypted:"6ドル$..."ansible_password_crypted:"6ドル$..."ansible_ssh_key:"ssh-ed25519 AAAA..."system_disk_max_size:"500T"# disques > seuil exclus du partitionnementpackages:["curl","vim","sudo"]short_hostname:falseroles:# surcharges par rôle Netbox (slug)osd:dns_servers:["1.1.1.1"]# concaténé aux defaultsgateway:"192.168.1.253"# remplace le defaultsystem_disk_max_size:"100G"short_hostname:truestatic_dir:"debian13"# static/debian13/<filename>

Règles de fusion defaultsroles

Type Comportement
Chaîne Remplacée si le rôle la définit
Slice Concaténée (defaults + role)
Booléen Remplacé uniquement si explicitement défini

Endpoints

Méthode URL Description
GET /api/v1/preseed/:mac Fichier preseed Debian
GET /api/v1/device/:mac Infos Netbox + valeurs effectives (debug)
GET /api/v1/static/:filename?mac=... Fichier statique (MAC optionnel)
GET /api/v1/health Healthcheck
GET /metrics Métriques Prometheus

Résolution des fichiers statiques :

  • GET /api/v1/static/boot.ipxestatic/boot.ipxe
  • GET /api/v1/static/linux?mac=AA:BB:CC:DD:EE:FF → si le rôle du device définit static_dir: debian13, sert static/debian13/linux ; sinon static/linux.

Formats MAC acceptés : AA:BB:CC:DD:EE:FF, AA-BB-CC-DD-EE-FF, AABBCCDDEEFF.

Exploitation

  • Secrets : NETBOX_TOKEN=... ./netbox-pxe-api avec token: "${NETBOX_TOKEN}" dans config.yaml.
  • Logs : texte par défaut, JSON via LOG_FORMAT=json.
  • Métriques : curl http://localhost:8080/metrics (compteurs requêtes, erreurs, preseed, fichiers servis, hits/miss du cache).
  • Arrêt propre : SIGINT/SIGTERM déclenchent un graceful shutdown (10 s max).

Build & Run

go mod tidy
go test ./... # tests
go run ./cmd/main.go --config config.yaml # local
go build -o netbox-pxe-api ./cmd/main.go # binaire
NETBOX_TOKEN=xxx LOG_FORMAT=json ./netbox-pxe-api # secret + logs JSON
# Docker (API seule)
docker build -t distributio .
docker run -p 8080:8080 -e NETBOX_TOKEN=xxx \
 -v $(pwd)/config.yaml:/app/config.yaml \
 -v $(pwd)/templates:/app/templates \
 -v $(pwd)/static:/app/static \
 distributio

Déploiement PXE complet (dnsmasq + iPXE + distributio)

L'image embarque dnsmasq (proxyDHCP + TFTP), les binaires iPXE et le serveur distributio. Un seul conteneur orchestre l'ensemble via deploy/entrypoint.sh. Le déploiement recommandé passe par Docker Compose :

docker compose up -d --build
docker compose logs -f

Le conteneur tourne en network_mode: host (indispensable : le proxyDHCP/TFTP repose sur des broadcasts réseau qui ne traversent pas le bridge NAT de Docker). Ports utilisés sur l'hôte : 67/udp (DHCP), 69/udp (TFTP), 4011/udp (PXE), 8080/tcp (API HTTP).

Fichiers accessibles depuis l'hôte (bind-mounts, voir docker-compose.yml) :

Hôte Conteneur Rôle
deploy/dnsmasq.conf /etc/dnsmasq.d/distributio.conf Conf dnsmasq (proxyDHCP/TFTP)
deploy/tftp/ /srv/tftp Racine TFTP (binaires iPXE)
config.yaml /app/config.yaml Conf distributio
templates/ /app/templates Templates preseed
static/ /app/static Fichiers statiques (kernel/initrd/boot.ipxe)

À adapter avant le premier démarrage :

  1. deploy/dnsmasq.conf :
    • dhcp-range=<RÉSEAU>,proxy → adresse réseau de votre sous-réseau PXE ;
    • éventuellement interface=<iface> ;
    • l'URL de chainload iPXE (dhcp-boot=tag:ipxe,http://<IP_DISTRIBUTIO>:8080/api/v1/static/boot.ipxe).
  2. static/boot.ipxe : l'IP du serveur distributio dans les URL kernel/initrd/url.
  3. config.yaml : URL/token Netbox (le token peut venir de ${NETBOX_TOKEN}).

Chaîne d'amorçage :

Client PXE --(proxyDHCP)--> dnsmasq --(TFTP)--> undionly.kpxe / ipxe.efi
 | |
 | iPXE chargé, re-DHCP avec user-class "iPXE" |
 v v
dnsmasq renvoie http://<distributio>:8080/api/v1/static/boot.ipxe (HTTP)
 |
 v
boot.ipxe --> kernel + initrd + preseed servis par distributio (par MAC)

Exemple iPXE

#!ipxe
dhcp
kernel http://SERVER:8080/api/v1/static/linux?mac=${mac} \
 auto=true priority=critical \
 url=http://SERVER:8080/api/v1/preseed/${mac}
initrd http://SERVER:8080/api/v1/static/initrd.gz?mac=${mac}
boot

Licence

Ce projet est distribué sous la licence Apache License 2.0. Voir le fichier LICENSE-2.0.txt pour le texte complet.

Copyright 2026 distributio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

English

A Go REST API that generates Debian preseed files on the fly from Netbox data, keyed by MAC address, and serves static files (kernel, initrd, iPXE scripts...) resolved according to the device role.

Features

  • Debian preseed generation from Netbox data (hostname, IP, role).
  • Per-role configuration overrides (DNS, NTP, packages, gateway, etc.).
  • Static file serving with per-role resolution via static_dir, automatic MIME type and Range request support (http.ServeContent).
  • Hot reload of the configuration (edit config.yaml without restart).
  • Path-traversal protection for static files.
  • Environment variables in config.yaml (${VAR}) for secrets.
  • TTL cache of Netbox lookups (MAC→device) to reduce API load.
  • Structured logs (slog, text or JSON) and Prometheus metrics at /metrics.
  • Graceful shutdown on SIGINT/SIGTERM and HTTP timeouts.

Architecture

netbox-pxe-api/
├── cmd/main.go # Entry point
├── config.yaml # Configuration
├── Dockerfile
├── internal/
│ ├── config/config.go # Loading, validation, hot reload, env vars
│ ├── netbox/client.go # Netbox HTTP client + TTL cache
│ ├── preseed/generator.go # Preseed generation (text/template)
│ ├── shared/template_data.go # Template data builder
│ ├── metrics/metrics.go # Prometheus-format counters
│ └── api/ # HTTP handlers, middleware (logs/metrics)
├── templates/
│ └── preseed/ansible.preseed.tmpl # Preseed template
└── static/ # Static files
 ├── boot.ipxe
 └── <static_dir>/... # Per-role subdirectories

Configuration (config.yaml)

server:listen:"0.0.0.0:8080"debug:falsestatic_dir:"./static"# static files roottemplate_dir:"./templates"# templates rootnetbox:url:"https://netbox.example.com"token:"${NETBOX_TOKEN}"# plain value or ${VAR} = environment variabletimeout_seconds:10tls_skip_verify:truecache_ttl_seconds:30# MAC→device cache (0 = disabled)api_paths:device_by_mac:"/api/dcim/interfaces/?mac_address={mac}"device_detail:"/api/dcim/devices/{id}/"ip_addresses:"/api/ipam/ip-addresses/?interface_id={interface_id}"defaults:# applied to every devicedns_servers:["8.8.8.8","8.8.4.4"]ntp_servers:["pool.ntp.org"]domain:"local.domain"locale:"en_US.UTF-8"timezone:"Europe/Paris"gateway:"192.168.1.254"root_password_crypted:"6ドル$..."ansible_password_crypted:"6ドル$..."ansible_ssh_key:"ssh-ed25519 AAAA..."system_disk_max_size:"500T"# disks above threshold are excluded from partitioningpackages:["curl","vim","sudo"]short_hostname:falseroles:# per-role overrides (Netbox slug)osd:dns_servers:["1.1.1.1"]# concatenated with defaultsgateway:"192.168.1.253"# replaces the defaultsystem_disk_max_size:"100G"short_hostname:truestatic_dir:"debian13"# static/debian13/<filename>

Merge rules defaultsroles

Type Behaviour
String Replaced if the role defines it
Slice Concatenated (defaults + role)
Boolean Replaced only when explicitly set

Endpoints

Method URL Description
GET /api/v1/preseed/:mac Debian preseed file
GET /api/v1/device/:mac Netbox info + effective values (debug)
GET /api/v1/static/:filename?mac=... Static file (MAC optional)
GET /api/v1/health Healthcheck
GET /metrics Prometheus metrics

Static file resolution:

  • GET /api/v1/static/boot.ipxestatic/boot.ipxe
  • GET /api/v1/static/linux?mac=AA:BB:CC:DD:EE:FF → if the device role defines static_dir: debian13, serves static/debian13/linux; otherwise static/linux.

Accepted MAC formats: AA:BB:CC:DD:EE:FF, AA-BB-CC-DD-EE-FF, AABBCCDDEEFF.

Operations

  • Secrets: NETBOX_TOKEN=... ./netbox-pxe-api with token: "${NETBOX_TOKEN}" in config.yaml.
  • Logs: text by default, JSON via LOG_FORMAT=json.
  • Metrics: curl http://localhost:8080/metrics (request, error, preseed, static-served and cache hit/miss counters).
  • Graceful shutdown: SIGINT/SIGTERM trigger a clean shutdown (10 s max).

Build & Run

go mod tidy
go test ./... # tests
go run ./cmd/main.go --config config.yaml # local
go build -o netbox-pxe-api ./cmd/main.go # binary
NETBOX_TOKEN=xxx LOG_FORMAT=json ./netbox-pxe-api # secret + JSON logs
# Docker
docker build -t netbox-pxe-api .
docker run -p 8080:8080 -e NETBOX_TOKEN=xxx \
 -v $(pwd)/config.yaml:/app/config.yaml \
 -v $(pwd)/templates:/app/templates \
 -v $(pwd)/static:/app/static \
 netbox-pxe-api

iPXE example

#!ipxe
dhcp
kernel http://SERVER:8080/api/v1/static/linux?mac=${mac} \
 auto=true priority=critical \
 url=http://SERVER:8080/api/v1/preseed/${mac}
initrd http://SERVER:8080/api/v1/static/initrd.gz?mac=${mac}
boot

License

This project is distributed under the Apache License 2.0. See the LICENSE-2.0.txt file for the full text.

Copyright 2026 distributio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.