chore: split nixos and opentofu
This commit is contained in:
parent
d125848b82
commit
06500a8f01
68 changed files with 44 additions and 61 deletions
26
nixos/modules/common/default.nix
Normal file
26
nixos/modules/common/default.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
imports = [
|
||||
./docker.nix
|
||||
./gitops.nix
|
||||
./monitoring.nix
|
||||
./nfs.nix
|
||||
./traefik.nix
|
||||
./users.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
homelab = {
|
||||
services.openssh.enable = true;
|
||||
users.admin.enable = true;
|
||||
gitops.enable = true;
|
||||
};
|
||||
|
||||
nix.settings.experimental-features = [
|
||||
"flakes"
|
||||
"nix-command"
|
||||
];
|
||||
|
||||
# Set your time zone.
|
||||
time.timeZone = "Europe/Brussels";
|
||||
};
|
||||
}
|
||||
38
nixos/modules/common/docker.nix
Normal file
38
nixos/modules/common/docker.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
options.homelab.dockerNetworks = lib.mkOption {
|
||||
description = "Declarative Docker networks to create before containers start.";
|
||||
default = {};
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
requiredBy = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
description = "List of systemd services that require this network (e.g., docker-containerName.service).";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
config = {
|
||||
systemd.services = lib.mapAttrs' (networkName: cfg:
|
||||
lib.nameValuePair "docker-${networkName}-create-network" {
|
||||
description = "Create Docker network for ${networkName}";
|
||||
requiredBy = cfg.requiredBy;
|
||||
after = [ "network.target" "docker.service" ];
|
||||
requires = [ "docker.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = pkgs.writeShellScript "create-${networkName}-docker-network" ''
|
||||
if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then
|
||||
${pkgs.docker}/bin/docker network create ${networkName}
|
||||
fi
|
||||
'';
|
||||
};
|
||||
}
|
||||
) config.homelab.dockerNetworks;
|
||||
};
|
||||
}
|
||||
141
nixos/modules/common/gitops.nix
Normal file
141
nixos/modules/common/gitops.nix
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.homelab.gitops;
|
||||
|
||||
updateScript = pkgs.writeShellApplication {
|
||||
name = "homelab-gitops-update";
|
||||
runtimeInputs = [ pkgs.git pkgs.nixos-rebuild pkgs.jq pkgs.coreutils ];
|
||||
text = ''
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_URL="${cfg.repoUrl}"
|
||||
BRANCH="${cfg.branch}"
|
||||
|
||||
echo "Checking remote hash for $REMOTE_URL branch $BRANCH..."
|
||||
|
||||
# Fetch remote hash, fallback to unknown if it fails
|
||||
REMOTE_HASH=$(git ls-remote "$REMOTE_URL" "refs/heads/$BRANCH" | awk '{print $1}' || true)
|
||||
|
||||
if [ -z "$REMOTE_HASH" ]; then
|
||||
echo "WARNING: Could not fetch remote hash. Forcing rebuild to be safe."
|
||||
REMOTE_HASH="unknown_remote"
|
||||
fi
|
||||
|
||||
LOCAL_HASH="unknown_local"
|
||||
if [ -f /run/current-system/configurationRevision ]; then
|
||||
LOCAL_HASH=$(cat /run/current-system/configurationRevision)
|
||||
fi
|
||||
|
||||
echo "Remote hash: $REMOTE_HASH"
|
||||
echo "Local hash: $LOCAL_HASH"
|
||||
|
||||
if [ "$REMOTE_HASH" = "$LOCAL_HASH" ] && [ "$REMOTE_HASH" != "unknown_remote" ] && [ "$LOCAL_HASH" != "unknown" ]; then
|
||||
echo "Hashes match. No update needed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Hashes differ or unknown. Triggering nixos-rebuild..."
|
||||
|
||||
# Trigger the build and switch
|
||||
nixos-rebuild switch --flake "git+$REMOTE_URL?dir=nixos&ref=$BRANCH"
|
||||
|
||||
echo "Update successful."
|
||||
'';
|
||||
};
|
||||
|
||||
in {
|
||||
options.homelab.gitops = {
|
||||
enable = lib.mkEnableOption "Custom GitOps native deployment system";
|
||||
|
||||
repoUrl = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "https://git.depeuter.dev/Bos55/nix-config.git";
|
||||
description = "The repository URL to pull configurations from.";
|
||||
};
|
||||
|
||||
branch = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "v2";
|
||||
description = "The branch to deploy.";
|
||||
};
|
||||
|
||||
useBuilder = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to use the central Builder host to compile packages.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# 1. Systemd Service and Timer for polling
|
||||
systemd.services.homelab-gitops = {
|
||||
description = "Homelab GitOps Update Service";
|
||||
wants = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${updateScript}/bin/homelab-gitops-update";
|
||||
# Must run as root to rebuild the system
|
||||
User = "root";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.homelab-gitops = {
|
||||
description = "Timer for Homelab GitOps Update Service";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnBootSec = "5m";
|
||||
OnUnitActiveSec = "5m";
|
||||
RandomizedDelaySec = "30s";
|
||||
};
|
||||
};
|
||||
|
||||
# 2. Webhook listener for instant trigger
|
||||
sops.secrets."webhook-secret" = {};
|
||||
|
||||
services.webhook = {
|
||||
enable = true;
|
||||
port = 9000;
|
||||
hooks = {
|
||||
gitops = {
|
||||
execute-command = "${pkgs.systemd}/bin/systemctl";
|
||||
pass-arguments-to-command = [
|
||||
{ source = "string"; name = "start"; }
|
||||
{ source = "string"; name = "homelab-gitops.service"; }
|
||||
];
|
||||
trigger-rule = {
|
||||
match = {
|
||||
type = "payload-hash-sha256";
|
||||
secret = "{{ getenv \"WEBHOOK_SECRET\" }}";
|
||||
parameter = {
|
||||
source = "header";
|
||||
name = "X-Forgejo-Signature";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Inject the secret as an environment variable into the webhook service
|
||||
systemd.services.webhook.serviceConfig.EnvironmentFile = config.sops.secrets."webhook-secret".path;
|
||||
|
||||
# 3. Builder Configuration
|
||||
sops.secrets."builder-ssh-key" = lib.mkIf cfg.useBuilder {};
|
||||
|
||||
nix.buildMachines = lib.mkIf cfg.useBuilder [
|
||||
{
|
||||
hostName = "builder.depeuter.dev"; # Must be routable from nodes
|
||||
system = "x86_64-linux";
|
||||
sshUser = "builder";
|
||||
sshKey = config.sops.secrets."builder-ssh-key".path;
|
||||
maxJobs = 4;
|
||||
speedFactor = 2;
|
||||
supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
|
||||
}
|
||||
];
|
||||
|
||||
nix.distributedBuilds = lib.mkIf cfg.useBuilder true;
|
||||
};
|
||||
}
|
||||
47
nixos/modules/common/monitoring.nix
Normal file
47
nixos/modules/common/monitoring.nix
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
# Prometheus Node Exporter for hardware metrics
|
||||
services.prometheus.exporters = {
|
||||
node = {
|
||||
enable = true;
|
||||
enabledCollectors = [ "systemd" ];
|
||||
port = 9100;
|
||||
};
|
||||
};
|
||||
|
||||
# Promtail to ship logs to Loki
|
||||
services.promtail = {
|
||||
enable = true;
|
||||
configuration = {
|
||||
server = {
|
||||
http_listen_port = 28183;
|
||||
grpc_listen_port = 0;
|
||||
};
|
||||
positions = {
|
||||
filename = "/tmp/positions.yaml";
|
||||
};
|
||||
clients = [{
|
||||
# Use the internal DNS name for the Loki ingress
|
||||
url = "http://loki.lab.depeuter.dev/loki/api/v1/push";
|
||||
}];
|
||||
scrape_configs = [{
|
||||
job_name = "journal";
|
||||
journal = {
|
||||
max_age = "12h";
|
||||
labels = {
|
||||
job = "systemd-journal";
|
||||
host = config.networking.hostName;
|
||||
};
|
||||
};
|
||||
relabel_configs = [{
|
||||
source_labels = [ "__journal__systemd_unit" ];
|
||||
target_label = "unit";
|
||||
}];
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
# Open firewall ports for node-exporter so Prometheus can scrape it
|
||||
networking.firewall.allowedTCPPorts = [ 9100 ];
|
||||
}
|
||||
46
nixos/modules/common/nfs.nix
Normal file
46
nixos/modules/common/nfs.nix
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
options.homelab.nfsMounts = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
device = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The NFS device, e.g. 192.168.0.11:/mnt/POOL/DATA";
|
||||
};
|
||||
readOnly = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to mount the NFS share read-only";
|
||||
};
|
||||
extraOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [
|
||||
"async"
|
||||
"soft"
|
||||
"timeo=100"
|
||||
"retry=50"
|
||||
"actimeo=1800"
|
||||
"lookupcache=all"
|
||||
];
|
||||
description = "Extra NFS mount options to append";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "NFS mounts to automatically configure with standard homelab options";
|
||||
};
|
||||
|
||||
config = {
|
||||
fileSystems = lib.mapAttrs (path: cfg: {
|
||||
device = cfg.device;
|
||||
fsType = "nfs";
|
||||
options = [
|
||||
"nfsvers=4.2"
|
||||
"nosuid"
|
||||
"tcp"
|
||||
] ++ (if cfg.readOnly then [ "ro" ] else [ "rw" ])
|
||||
++ cfg.extraOptions;
|
||||
}) config.homelab.nfsMounts;
|
||||
};
|
||||
}
|
||||
65
nixos/modules/common/traefik.nix
Normal file
65
nixos/modules/common/traefik.nix
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
proxyNet = config.homelab.apps.traefik.sharedNetworkName;
|
||||
in {
|
||||
options.homelab.traefikRouters = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
rule = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The Traefik router rule, e.g. Host(`example.com`)";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
description = "The port the service listens on";
|
||||
};
|
||||
middlewares = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
description = "Middlewares to apply";
|
||||
};
|
||||
tls = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to enable TLS";
|
||||
};
|
||||
entryPoints = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
description = "Entrypoints to use";
|
||||
};
|
||||
extraLabels = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = {};
|
||||
description = "Extra labels to apply";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "Declarative Traefik router configuration";
|
||||
};
|
||||
|
||||
config = {
|
||||
# Generate labels for containers based on homelab.traefikRouters
|
||||
# This assumes that the name in homelab.traefikRouters matches the container name.
|
||||
virtualisation.oci-containers.containers = lib.mapAttrs (name: router: {
|
||||
labels = {
|
||||
"traefik.enable" = "true";
|
||||
"traefik.docker.network" = proxyNet;
|
||||
"traefik.http.routers.${name}.rule" = router.rule;
|
||||
"traefik.http.services.${name}.loadbalancer.server.port" = toString router.port;
|
||||
}
|
||||
// lib.optionalAttrs (router.middlewares != []) {
|
||||
"traefik.http.routers.${name}.middlewares" = builtins.concatStringsSep "," router.middlewares;
|
||||
}
|
||||
// lib.optionalAttrs router.tls {
|
||||
"traefik.http.routers.${name}.tls" = "true";
|
||||
}
|
||||
// lib.optionalAttrs (router.entryPoints != []) {
|
||||
"traefik.http.routers.${name}.entryPoints" = builtins.concatStringsSep "," router.entryPoints;
|
||||
}
|
||||
// router.extraLabels;
|
||||
}) config.homelab.traefikRouters;
|
||||
};
|
||||
}
|
||||
37
nixos/modules/common/users.nix
Normal file
37
nixos/modules/common/users.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
options.homelab.appUsers = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
uid = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
description = "The user ID for the app user";
|
||||
};
|
||||
group = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config.users.groups.apps.name;
|
||||
description = "The primary group for the app user";
|
||||
};
|
||||
extraGroups = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
description = "Extra groups for the app user";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "App users to automatically create with standard homelab options";
|
||||
};
|
||||
|
||||
config = {
|
||||
users.users = lib.mapAttrs (name: cfg: {
|
||||
uid = lib.mkForce cfg.uid;
|
||||
isSystemUser = true;
|
||||
group = cfg.group;
|
||||
extraGroups = cfg.extraGroups;
|
||||
home = "/var/empty";
|
||||
shell = null;
|
||||
}) config.homelab.appUsers;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue