feat(core): initialize NaxOS declarative NixOS distribution, modules, and ISO installer
Test NaxOS Module Configurations / test-modules (push) Failing after 6m10s
Test NaxOS Module Configurations / test-modules (push) Failing after 6m10s
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.naxos.gitops;
|
||||
|
||||
# Safe switch script with canary validation and automatic rollback
|
||||
naxosRebuildWrapper = pkgs.writeShellScriptBin "naxos-rebuild-safe" ''
|
||||
set -euo pipefail
|
||||
CONFIG_DIR="''${CONFIG_DIR:-/etc/naxos/repo}"
|
||||
LOG_FILE="/var/log/naxos-rebuild.log"
|
||||
|
||||
echo "[$(date -Iseconds)] Starting NaxOS declarative rebuild..." | tee -a "$LOG_FILE"
|
||||
|
||||
cd "$CONFIG_DIR"
|
||||
|
||||
# Pre-flight check with nix flake check / nixos-rebuild dry-build
|
||||
echo "Running dry build validation..."
|
||||
if ! nixos-rebuild build --flake .#naxos 2>&1 | tee -a "$LOG_FILE"; then
|
||||
echo "Dry-build failed! Configuration aborted without changing running system." | tee -a "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Record current generation
|
||||
PREV_GEN=$(readlink -f /nix/var/nix/profiles/system)
|
||||
|
||||
echo "Applying system switch..."
|
||||
if nixos-rebuild switch --flake .#naxos 2>&1 | tee -a "$LOG_FILE"; then
|
||||
echo "System switch succeeded." | tee -a "$LOG_FILE"
|
||||
|
||||
# Canary health check: verify management daemon and storage pools are responsive
|
||||
if systemctl is-active --quiet naxos-api.service; then
|
||||
echo "Canary verification passed: NaxOS daemon active." | tee -a "$LOG_FILE"
|
||||
exit 0
|
||||
else
|
||||
echo "WARNING: NaxOS daemon failed canary test! Initiating safe automatic rollback..." | tee -a "$LOG_FILE"
|
||||
"$PREV_GEN/bin/switch-to-configuration" switch
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
echo "Switch command failed! Rolling back to $PREV_GEN..." | tee -a "$LOG_FILE"
|
||||
"$PREV_GEN/bin/switch-to-configuration" switch
|
||||
exit 3
|
||||
fi
|
||||
'';
|
||||
|
||||
# Automated remote push/pull service
|
||||
naxosGitSync = pkgs.writeShellScriptBin "naxos-git-sync" ''
|
||||
set -euo pipefail
|
||||
REPO_DIR="/etc/naxos/repo"
|
||||
|
||||
if [ ! -d "$REPO_DIR/.git" ]; then
|
||||
echo "Git repository not initialized in $REPO_DIR."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$REPO_DIR"
|
||||
|
||||
if [ -n "''${NAXOS_REMOTE_URL:-}" ]; then
|
||||
echo "Synchronizing with remote GitOps repository..."
|
||||
git fetch origin main || true
|
||||
git push origin main || true
|
||||
fi
|
||||
'';
|
||||
|
||||
in {
|
||||
options.services.naxos.gitops = {
|
||||
enable = mkEnableOption "NaxOS GitOps Configuration Synchronization Engine";
|
||||
|
||||
remoteUrl = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "ssh://git@git.lholz.de:2222/naxos/naxos-config.git";
|
||||
description = "Remote Git repository URL for automated backup, auditing, and disaster recovery.";
|
||||
};
|
||||
|
||||
branch = mkOption {
|
||||
type = types.str;
|
||||
default = "main";
|
||||
description = "GitOps target branch.";
|
||||
};
|
||||
|
||||
tokenFile = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Path to Gitea/Git access token or SSH private key.";
|
||||
};
|
||||
|
||||
autoPushOnCommit = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Automatically push to remote repository whenever web dashboard commits a change.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
git
|
||||
naxosRebuildWrapper
|
||||
naxosGitSync
|
||||
];
|
||||
|
||||
# Create config repo directory structure
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /etc/naxos 0755 root root -"
|
||||
"d /etc/naxos/repo 0750 root root -"
|
||||
"d /var/log 0755 root root -"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.naxos.selfUpdate;
|
||||
|
||||
updateScript = pkgs.writeShellScriptBin "naxos-self-update" ''
|
||||
set -euo pipefail
|
||||
CONFIG_DIR="''${CONFIG_DIR:-/etc/naxos/repo}"
|
||||
LOG_FILE="/var/log/naxos-update.log"
|
||||
|
||||
echo "[$(date -Iseconds)] Checking for NaxOS updates..." | tee -a "$LOG_FILE"
|
||||
|
||||
if [ ! -d "$CONFIG_DIR/.git" ]; then
|
||||
echo "Configuration directory is not a git repository. Skipping." | tee -a "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$CONFIG_DIR"
|
||||
git fetch origin main
|
||||
|
||||
LOCAL_HASH=$(git rev-parse HEAD)
|
||||
REMOTE_HASH=$(git rev-parse origin/main)
|
||||
|
||||
if [ "$LOCAL_HASH" = "$REMOTE_HASH" ]; then
|
||||
echo "System is already up to date ($LOCAL_HASH)." | tee -a "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "New updates detected ($LOCAL_HASH -> $REMOTE_HASH). Updating flake inputs and pulling..." | tee -a "$LOG_FILE"
|
||||
git merge origin/main --ff-only
|
||||
|
||||
echo "Rebuilding and applying configuration..." | tee -a "$LOG_FILE"
|
||||
naxos-rebuild-safe
|
||||
'';
|
||||
|
||||
in {
|
||||
options.services.naxos.selfUpdate = {
|
||||
enable = mkEnableOption "NaxOS Automated Background Self-Update Service";
|
||||
|
||||
schedule = mkOption {
|
||||
type = types.str;
|
||||
default = "*-*-* 04:00:00"; # Daily at 4:00 AM
|
||||
description = "Systemd calendar expression for scheduled update checks.";
|
||||
};
|
||||
|
||||
channel = mkOption {
|
||||
type = types.enum [ "stable" "beta" "nightly" ];
|
||||
default = "stable";
|
||||
description = "Update release channel.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ updateScript ];
|
||||
|
||||
systemd.services.naxos-self-update = {
|
||||
description = "NaxOS Self-Update Check & Apply";
|
||||
path = [ pkgs.git pkgs.nix pkgs.systemd pkgs.bash ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${updateScript}/bin/naxos-self-update";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.naxos-self-update = {
|
||||
description = "NaxOS Scheduled Self-Update Timer";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = cfg.schedule;
|
||||
Persistent = true;
|
||||
RandomizedDelaySec = "1800"; # 30 min random jitter
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.naxos.core;
|
||||
in {
|
||||
options.services.naxos.core = {
|
||||
enable = mkEnableOption "NaxOS Core Appliance Base";
|
||||
|
||||
hostName = mkOption {
|
||||
type = types.str;
|
||||
default = "naxos";
|
||||
description = "Appliance hostname.";
|
||||
};
|
||||
|
||||
hostId = mkOption {
|
||||
type = types.str;
|
||||
default = "8425f3a1";
|
||||
description = "32-bit Host ID required for OpenZFS safety locking.";
|
||||
};
|
||||
|
||||
timeZone = mkOption {
|
||||
type = types.str;
|
||||
default = "Europe/Berlin";
|
||||
description = "System timezone.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
networking.hostName = cfg.hostName;
|
||||
networking.hostId = cfg.hostId;
|
||||
time.timeZone = cfg.timeZone;
|
||||
|
||||
# Flakes and Nix CLI enablement
|
||||
nix.settings = {
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
auto-optimise-store = true;
|
||||
};
|
||||
|
||||
# High-Performance Storage & Network Kernel Tuning
|
||||
boot.kernel.sysctl = {
|
||||
# BBR Congestion Control
|
||||
"net.core.default_qdisc" = "fq";
|
||||
"net.ipv4.tcp_congestion_control" = "bbr";
|
||||
|
||||
# High-bandwidth 10G/25G network buffer tuning
|
||||
"net.core.rmem_max" = 67108864;
|
||||
"net.core.wmem_max" = 67108864;
|
||||
"net.ipv4.tcp_rmem" = "4096 87380 33554432";
|
||||
"net.ipv4.tcp_wmem" = "4096 65536 33554432";
|
||||
"net.core.netdev_max_backlog" = 10000;
|
||||
|
||||
# Storage & VM writeback tuning for ZFS
|
||||
"vm.swappiness" = 10;
|
||||
"vm.dirty_background_ratio" = 5;
|
||||
"vm.dirty_ratio" = 10;
|
||||
|
||||
# File handles limit
|
||||
"fs.file-max" = 2097152;
|
||||
};
|
||||
|
||||
# Core system tools
|
||||
environment.systemPackages = with pkgs; [
|
||||
curl
|
||||
wget
|
||||
git
|
||||
htop
|
||||
btop
|
||||
tmux
|
||||
jq
|
||||
pciutils
|
||||
usbutils
|
||||
ethtool
|
||||
iperf3
|
||||
rsync
|
||||
];
|
||||
|
||||
# Security & Firewall defaults
|
||||
networking.firewall = {
|
||||
enable = true;
|
||||
allowPing = true;
|
||||
allowedTCPPorts = [ 22 80 443 ];
|
||||
};
|
||||
|
||||
# SSH Server with modern secure defaults
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PermitRootLogin = "prohibit-password";
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.naxos.users;
|
||||
in {
|
||||
options.services.naxos.users = {
|
||||
enable = mkEnableOption "NaxOS Declarative User Management";
|
||||
|
||||
adminUser = mkOption {
|
||||
type = types.str;
|
||||
default = "admin";
|
||||
description = "Primary administrator account name.";
|
||||
};
|
||||
|
||||
adminSshKeys = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Public SSH keys for the administrator.";
|
||||
};
|
||||
|
||||
users = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = "User full name or comment.";
|
||||
};
|
||||
isAdmin = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether the user has sudo/wheel privileges.";
|
||||
};
|
||||
sshKeys = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Public SSH authorized keys.";
|
||||
};
|
||||
extraGroups = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Additional Linux groups.";
|
||||
};
|
||||
smbAccess = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Whether the user can access SMB shares.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "Appliance user accounts.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.mutableUsers = true;
|
||||
|
||||
# Admin user creation
|
||||
users.users.${cfg.adminUser} = {
|
||||
isNormalUser = true;
|
||||
description = "NaxOS Primary Administrator";
|
||||
extraGroups = [ "wheel" "docker" "video" "render" "users" ];
|
||||
openssh.authorizedKeys.keys = cfg.adminSshKeys;
|
||||
shell = pkgs.bashInteractive;
|
||||
};
|
||||
|
||||
# Additional users
|
||||
users.users = mapAttrs (name: ucfg: {
|
||||
isNormalUser = true;
|
||||
description = ucfg.description;
|
||||
extraGroups = (if ucfg.isAdmin then [ "wheel" ] else []) ++ ucfg.extraGroups ++ [ "users" ];
|
||||
openssh.authorizedKeys.keys = ucfg.sshKeys;
|
||||
}) cfg.users;
|
||||
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user