feat: massive refactoring...

This commit is contained in:
Xavier Morel
2025-11-09 00:45:00 +01:00
parent f9446df46d
commit 3245b6b89f
77 changed files with 3233 additions and 582 deletions

View File

@@ -0,0 +1,49 @@
{
config,
tools,
container,
def,
pkgs,
...
}:
let
lib = pkgs.lib;
mergeConf = c: (lib.foldl' (acc: e: lib.recursiveUpdate acc e) { } c);
in
{
services.alloy = {
enable = true;
extraFlags = [
"--server.http.listen-addr=0.0.0.0:12345"
"--disable-reporting"
];
};
environment.etc = mergeConf (
[
{
"alloy/config.alloy".text = (import ../config/alloy/config.alloy.nix { inherit config tools; }).out;
"alloy/metrics.alloy".text =
if (def.logging.metricsEnable) then
(import ../config/alloy/metrics.alloy.nix { inherit config tools container; }).out
else
"";
}
]
++ (lib.mapAttrsToList (filename: file: {
"alloy/${filename}.alloy".text = (import file { inherit config tools container; }).out;
}) def.logging.alloyConfig)
++ (lib.mapAttrsToList (service: additional_stages: {
"alloy/${container}-${service}.alloy".text =
import ../config/alloy/default-journal-logger.alloy.nix
{
inherit
tools
container
service
additional_stages
;
};
}) def.logging.journalLoggers)
);
}

View File

@@ -0,0 +1,8 @@
{
config,
tools,
lib,
...
}:
{
}

View File

@@ -0,0 +1,35 @@
{ config, lib, ... }:
let
cfg = config.my-lxc;
in
{
postgresql_role = lib.filterAttrs (_: v: v != { }) (
lib.mapAttrs (
containerName: def:
lib.optionalAttrs (def.db.enable) {
name = containerName;
login = true;
password = def.db.password;
}
) cfg
);
postgresql_database = lib.foldl' (acc: elem: acc // elem) { } (
lib.mapAttrsToList (
containerName: def:
lib.optionalAttrs (def.db.enable) (
# mkIf ?
lib.listToAttrs (
lib.map (db: {
name = db;
value = {
name = db;
owner = containerName;
lc_ctype = "C";
lc_collate = "C";
};
}) (def.db.additionalDB ++ [ containerName ])
)
)
) cfg
);
}

View File

@@ -0,0 +1,45 @@
{
config,
tools,
lib,
...
}:
let
cfg = config.my-lxc;
in
{
proxmox_lxc = lib.mapAttrs (
name: def:
let
c = def.container;
in
lib.mkIf (c.enable) {
hostname = name;
memory = c.memory;
cores = c.cores;
ostemplate = "local:vztmpl/\${var.ostemplate}.tar.xz";
unprivileged = true;
password = "changeme";
features.nesting = true;
target_node = "\${var.pve_node}";
network = {
name = "eth0";
bridge = "vmbr0";
ip = tools.build_ip_cidr name;
gw = config.globals.gateway;
type = "veth";
};
protection = c.protection;
onboot = true;
rootfs = {
storage = "local-lvm";
size = c.disk;
};
swap = c.swap;
vmid = config.id.${name};
tags = lib.strings.join ";" ([ "terraform" ] ++ c.tags);
}
// c.overrides
) cfg;
}

364
modules/containers.nix Normal file
View File

@@ -0,0 +1,364 @@
{
config,
system,
nixpkgs,
...
}:
let
pkgs = nixpkgs.legacyPackages.${system};
lib = pkgs.lib;
inherit (lib) types mkOption mkEnableOption;
cfg = config.my-lxc or { };
realcfg = config;
nixosTemplate = import ./lxc-template.nix { inherit pkgs; };
tools = import ./tools.nix { inherit config lib; };
mergeConf = c: (lib.foldl' (acc: e: lib.recursiveUpdate acc e) { } c);
secrets = import ../secrets/secrets.nix;
in
{
options = with types; {
# Inputs
my-lxc = mkOption {
type = attrsOf (submodule {
options = {
container = mkOption {
type = submodule {
options = {
enable = mkOption {
type = bool;
description = "Enable the container terraform generation";
default = true;
};
cores = mkOption {
type = int;
description = "Number of CPU";
default = 1;
};
memory = mkOption {
type = int;
description = "Ram in MB";
default = 512;
};
unprivileged = mkOption {
type = bool;
default = true;
description = "Make an unprivileged container";
};
disk = mkOption {
type = str;
description = "Disk size";
default = "4G";
};
swap = mkOption {
type = nullOr int;
description = "Optional swap size in MB";
default = null;
};
tags = mkOption {
type = listOf str;
description = "Container tags (terraform is automatically added)";
default = [ ];
};
overrides = mkOption {
type = attrs;
description = "Overrides to the Proxmox LXC Terraform resource";
default = { };
};
protection = mkOption {
type = bool;
description = "Whether container should be protected against changes.";
default = true;
};
# TODO: Device passthrough & mount points => use overrides for now - or do it manually...
# => in /etc/pve/lxc/{id}.conf
# mp{number}: /pve/path,mp=/container/path # <- for bind-mount
# dev{number}: /dev/{...} # <- for device pass-through
};
};
};
system = mkOption {
type = submodule {
options = {
port = mkOption {
type = nullOr int;
description = "Main exposed HTTP port";
default = null;
};
additionalPorts = mkOption {
type = listOf int;
description = "TCP ports to open";
default = [ ];
};
udpPorts = mkOption {
type = listOf int;
description = "UDP ports to open";
default = [ ];
};
services = mkOption {
type = attrs;
description = "NixOS services to enable";
default = { };
};
packages = mkOption {
type = listOf package;
description = "NixOS packages to add";
default = [ ];
};
etcFiles = mkOption {
type = attrs;
description = "Files to create on NixOS (=> environment.etc)";
default = { };
};
additional = mkOption {
type = attrs;
description = "Additional NixOS definitions to be merged to nixosModule";
default = { };
};
importConfig = mkOption {
type = listOf path;
description = "Modules to import and merge to NixOS module";
default = [ ];
};
};
};
};
db = mkOption {
type = submodule {
options = {
enable = mkEnableOption "Create a DB";
password = mkOption { type = str; };
additionalDB = mkOption {
type = listOf str;
default = [ ];
};
};
};
default = { };
};
logging = mkOption {
type = submodule {
options = {
enable = mkEnableOption "Whether to enable default logs collection";
metricsEnable = mkEnableOption "Whether to enable default metrics collection";
prometheusPorts = mkOption {
type = listOf int;
description = "Ports of Prometheus Exporters";
default = [ ];
};
alloyConfig = mkOption {
type = attrsOf path;
description = "Name => paths to add to Alloy";
default = { };
};
journalLoggers = mkOption {
type = attrsOf (nullOr str);
description = "Service => routing stages (if any) as str";
default = { };
};
};
};
};
private = mkOption {
type = bool;
description = "Should be only exposed to the private network";
default = true;
};
auth = mkOption {
type = bool;
description = "Should be accessed through the auth middleware";
default = true;
};
otherDomains = mkOption {
type = listOf (submodule {
options = {
subdomain = mkOption { type = str; };
port = mkOption { type = int; };
private = mkOption {
type = bool;
default = true;
};
auth = mkOption {
type = bool;
default = true;
};
customRule = mkOption {
type = nullOr str;
default = null;
};
raw_tcp = mkOption {
type = bool;
default = false;
};
};
});
default = [ ];
};
sso = mkOption {
type = attrs;
description = "SSO parameters (TBD)";
default = { };
};
secrets = mkOption {
type = attrs;
description = "Secrets to import {name}: {path}";
default = { };
};
};
});
};
# in _id.nix
id = mkOption {
type = attrsOf int;
};
# in _config.nix
globals = mkOption {
type = submodule {
options = {
ip_prefix = mkOption { type = str; };
cidr = mkOption { type = int; };
gateway = mkOption { type = str; };
domains = mkOption {
type = submodule {
options = {
internal = mkOption { type = str; };
external = mkOption { type = str; };
};
};
};
master = mkOption {
type = submodule {
options = {
login = mkOption { type = str; };
email = mkOption { type = str; };
public_ssh_key = mkOption { type = str; };
initial_htpasswd = mkOption { type = str; };
};
};
};
default_tz = mkOption { type = str; };
country_code = mkOption { type = str; };
currency = mkOption { type = str; };
services = mkOption {
type = submodule {
log_sink = mkOption { type = str; }; # ip:port
metrics_sink = mkOption { type = str; }; # ip:port
};
};
dns_provider = mkOption { type = str; };
other_hosts = mkOption {
type = listOf (submodule {
options = {
hostname = mkOption { type = str; };
private = mkOption {
type = bool;
default = true;
};
auth = mkOption {
type = bool;
default = true;
};
addr = mkOption {
type = str;
description = "ip:port for the service";
};
useCustomCA = mkOption {
type = bool;
default = false;
description = "Whether to use a custom CA (pretty much hardcoded)";
};
};
});
default = [ ];
};
};
};
};
# Outputs
tf = mkOption {
type = types.attrs;
description = "Terraform resources";
default = { };
};
nixosModule = mkOption {
type = types.attrs;
description = "NixOS system";
default = { };
};
};
config = {
tf.resource = mergeConf [
(import ./containers-terraform-postgres.nix { inherit config tools lib; })
(import ./containers-terraform-proxmox.nix { inherit config tools lib; })
(import ./containers-terraform-authentik.nix { inherit config tools lib; })
];
nixosModule = lib.mapAttrs (
container: def:
{ config, pkgs, ... }:
let
keys = import ../config/_keys.nix;
ownKey = if (lib.hasAttr container keys) then keys.${container} else null;
in
mergeConf [
(mergeConf (
lib.attrValues (
lib.mapAttrs (
secretName': _:
let
secretName = lib.removeSuffix ".age" secretName';
in
{
age.secrets.${secretName}.file = ../secrets/${secretName'};
}
) (lib.filterAttrs (_: entry: builtins.elem ownKey entry.publicKeys) secrets)
)
))
nixosTemplate
(lib.optionalAttrs (def.logging.enable) import ./containers-nixos-logging.nix {
inherit
config
tools
container
def
pkgs
;
})
{
networking.hostName = container;
networking.firewall = {
enable = true;
allowedTCPPorts =
def.system.additionalPorts
++ (if (def.system.port != null) then [ def.system.port ] else [ ])
++ (if (def.logging.enable) then [ 12345 ] else [ ]);
allowedUDPPorts = def.system.udpPorts;
};
services = def.system.services;
environment.etc = def.system.etcFiles;
environment.systemPackages = def.system.packages;
}
def.system.additional
(mergeConf (
map (
p:
import p {
inherit pkgs tools;
config = mergeConf [
realcfg
config
];
}
) def.system.importConfig
))
]
) cfg;
};
}

53
modules/lxc-template.nix Normal file
View File

@@ -0,0 +1,53 @@
{
pkgs,
...
}:
let
lib = pkgs.lib;
modulesPath = pkgs.path + "/nixos/modules";
config = import ../config/_globals.nix { };
in
{
imports = [
(modulesPath + "/virtualisation/proxmox-lxc.nix")
];
boot.isContainer = true;
systemd.suppressedSystemUnits = [
"dev-mqueue.mount"
"sys-kernel-debug.mount"
"sys-fs-fuse-connections.mount"
];
environment.systemPackages = with pkgs; [
vim
openssl
coreutils
];
services.openssh.enable = true;
nix.settings = {
experimental-features = [
"nix-command"
"flakes"
];
auto-optimise-store = true;
};
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
time.timeZone = config.globals.default_tz;
users.users.root = {
openssh.authorizedKeys.keys = [
config.globals.master.public_ssh_key
];
initialPassword = "nixos";
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
system.stateVersion = "25.11";
}

View File

@@ -0,0 +1,53 @@
{
pkgs,
...
}:
let
lib = pkgs.lib;
modulesPath = pkgs.path + "/nixos/modules";
config = import ../config/_globals.nix { };
in
{
imports = [
(modulesPath + "/virtualisation/proxmox-lxc.nix")
];
boot.isContainer = true;
systemd.suppressedSystemUnits = [
"dev-mqueue.mount"
"sys-kernel-debug.mount"
"sys-fs-fuse-connections.mount"
];
environment.systemPackages = with pkgs; [
vim
openssl
coreutils
];
services.openssh.enable = true;
nix.settings = {
experimental-features = [
"nix-command"
"flakes"
];
auto-optimise-store = true;
};
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
time.timeZone = config.globals.default_tz;
users.users.root = {
openssh.authorizedKeys.keys = [
config.globals.master.public_ssh_key
];
initialPassword = "nixos";
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
system.stateVersion = "25.11";
}

View File

@@ -0,0 +1,42 @@
{ lib, ... }:
{
terraform.required_providers = {
proxmox = {
source = "Telmate/proxmox";
version = "~> 2.9.11";
};
postgresql = {
source = "cyrilgdn/postgresql";
version = "~> 1.26.0";
};
};
provider.proxmox = {
pm_api_url = "\${var.pm_api_url}";
pm_api_token_id = "\${var.pm_api_token_id}";
pm_api_token_secret = "\${var.pm_api_token_secret}";
pm_tls_insecure = "\${var.pm_tls_insecure}";
};
variable.pm_api_url.type = "string";
variable.pm_api_token_id.type = "string";
variable.pm_api_token_secret.type = "string";
variable.pm_tls_insecure.type = "bool";
variable.pve_node.type = "string";
variable.ostemplate.type = "string";
provider.postgresql = {
host = "\${var.pg_host}";
port = 5432;
database = "postgres";
username = "\${var.pg_user}";
password = "\${var.pg_pass}";
sslmode = "disable";
connect_timeout = 15;
};
variable.pg_host.type = "string";
variable.pg_user.type = "string";
variable.pg_pass.type = "string";
}

31
modules/tools.nix Normal file
View File

@@ -0,0 +1,31 @@
{
config,
lib,
...
}:
let
build_ip =
arg:
(
if (!lib.strings.isString arg) then
"${config.globals.ip_prefix}${toString arg}"
else
let
id = config.id.${arg};
ip = if (id > 1000) then id - 1000 else id;
in
"${config.globals.ip_prefix}${toString ip}"
);
build_ip_cidr = arg: "${build_ip arg}/${toString config.globals.cidr}";
mask_cidr = build_ip_cidr 0;
build_hostname = arg: "${arg}${config.globals.domains.external}";
in
{
build_ip = build_ip;
build_ip_cidr = build_ip_cidr;
mask_cidr = mask_cidr;
build_hostname = build_hostname;
loki_addr = "${build_ip "monitoring"}:3100";
metrics_addr = "${build_ip "metrics"}:9090";
}