Laid a base for better hardware detection. Removed unnecessary packages. Removed unnecessary TUI.

This commit is contained in:
Raphaël Numbus
2026-05-17 13:40:08 +02:00
parent 30745adf4f
commit c5d81677a1
3 changed files with 268 additions and 231 deletions
+263 -171
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash nano coreutils gnused gum fastfetch xkcdpass sops ssh-to-age age sshpass envsubst pciutils usbutils mosquitto curl jq yq python3
#!nix-shell -i bash -p bash coreutils gnused gum xkcdpass sops ssh-to-age age sshpass envsubst pciutils usbutils mosquitto curl jq yq python3
@@ -14,7 +14,7 @@ echod() {
ssh_to_host() {
local COMMAND="${1}"
ssh -i "${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}" "${COMMAND}"
ssh -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}" "${COMMAND}"
}
get_valid_input() {
@@ -67,7 +67,7 @@ get_valid_input() {
cleanup() {
echo -e "\n ✅ Cleaning up..."
rm -${DIR_RM_FLAGS} ${TMP_FILES_PATH}/
rm -${DIR_RM_FLAGS} "/run/user/$(id -u)/numbus-installer"
if [[ -n "${BRIDGE_PID:-}" ]] && ps -p ${BRIDGE_PID} > /dev/null; then
kill ${BRIDGE_PID}
@@ -78,136 +78,247 @@ hierarchy_preparation() {
echod "\n 🔄 Preparing the folder hierarchy for the final configuration..."
# Extra files folders
mkdir -${MKDIR_FLAGS} ${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/
mkdir -${MKDIR_FLAGS} ${EXTRA_FILES_PATH}/var/lib/sops-nix/
mkdir -${MKDIR_FLAGS} ${EXTRA_FILES_PATH}/etc/nixos/secrets/disks
mkdir -${MKDIR_FLAGS} ${EXTRA_FILES_PATH}/etc/nixos/secrets/system
mkdir -${MKDIR_FLAGS} ${EXTRA_FILES_PATH}/etc/nixos/secrets/podman
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/var/lib/sops-nix/
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/etc/nixos/secrets/disks
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/etc/nixos/secrets/system
mkdir -${MKDIR_FLAGS} ${TMP_EXTRA_PATH}/etc/nixos/secrets/podman
echod "\n ✅ Folder hierarchy ready"
}
hardware_detection() {
local TMPFILE="/tmp/nixos-installation-hw-detection"
local TMPFILE="/tmp/nixos-installation-hw-detection.json"
ssh_to_host 'bash -s' << SSHEND
TARGET_GRAPHICS_BRAND=()
ssh_to_host "nix-shell -p jq pciutils usbutils smartmontools iproute2 --run 'bash -s'" << SSHEND
set -euo pipefail
for brand in Intel AMD NVIDIA; do
if lspci -nn 2>/dev/null | grep -i "vga" | grep -iq "\${brand}"; then
TARGET_GRAPHICS="true"
TARGET_GRAPHICS_BRAND+=("\${brand}")
else
TARGET_GRAPHICS="false"
fi
done
# --- Initialize Global JSON Output ---
HW_REPORT=\$(jq -n '{}')
ls /dev/dri/ > /dev/null 2>&1 | grep -iq "renderD128" && TARGET_GRAPHICS_RENDERER="true" || TARGET_GRAPHICS_RENDERER="false"
lsusb > /dev/null 2>&1 | grep -iq "google" && TARGET_USB_CORAL="true" || TARGET_USB_CORAL="false"
lspci -nn > /dev/null 2>&1 | grep -iq "089a" && TARGET_PCIE_CORAL="true" || TARGET_PCIE_CORAL="false"
ls /dev/serial/by-id/ > /dev/null 2>&1 | grep -i "zigbee" && TARGET_ZIGBEE_DEVICE=\$(ls /dev/serial/by-id/ > /dev/null 2>&1 | grep -i "zigbee" | head -n 1) || TARGET_ZIGBEE_DEVICE=""
# --- Helper: Add JSON array to the main report ---
append_to_report() {
local key="\$1"
local json_array="\$2"
HW_REPORT=\$(echo "\$HW_REPORT" | jq --argjson arr "\$json_array" --arg k "\$key" '.[\$k] = \$arr')
}
TARGET_INTERFACE=\$(ip -4 route show default | awk '{print \$5}' | head -n1)
# --- 1. Detect Graphics ---
detect_graphics() {
local gpus="[]"
# Iterate over all VGA and 3D controllers
while read -r pci_addr; do
local brand="Unknown"
local integrated="false"
local renderer=""
if ls -l /sys/class/tpm/tpm0/ > /dev/null 2>&1; then
TARGET_TPM="true"
TARGET_TPM_VERSION=\$(cat /sys/class/tpm/tpm0/tpm_version_major)
else
TARGET_TPM="false"
TARGET_TPM_VERSION="N/A"
fi
# Determine Brand
local vendor_info
vendor_info=\$(lspci -vms "\$pci_addr" | grep -i "^Vendor:" || true)
if echo "\$vendor_info" | grep -iq "intel"; then
brand="Intel"
integrated="true" # General heuristic for Intel
elif echo "\$vendor_info" | grep -iq "amd"; then
brand="AMD"
# If boot_vga is 1, it's likely the primary/integrated APU
if [[ -f "/sys/bus/pci/devices/0000:\$pci_addr/boot_vga" ]] && grep -q "1" "/sys/bus/pci/devices/0000:\$pci_addr/boot_vga"; then
integrated="true"
fi
elif echo "\$vendor_info" | grep -iq "nvidia"; then
brand="NVIDIA"
integrated="false" # NVIDIA is almost always discrete in these contexts
fi
HDD=1
DISK_DEVPATH=()
DISK_NAME=()
DISK_TYPE=()
DISK_HEALTH=()
DISK_ID=()
# Find Renderer (e.g., renderD128)
if [[ -d "/sys/bus/pci/devices/0000:\$pci_addr/drm" ]]; then
renderer=\$(find "/sys/bus/pci/devices/0000:\$pci_addr/drm" -maxdepth 1 -name "renderD*" -exec basename {} \; | head -n 1)
[[ -n "\$renderer" ]] && renderer="/dev/dri/\$renderer"
fi
for DISK in \$(lsblk -x SIZE -d -n -e 7,11 -o NAME); do
# Append to array
local gpu_obj
gpu_obj=\$(jq -n --arg b "\$brand" --arg r "\${renderer:-N/A}" --argjson i "\$integrated" \
'{brand: \$b, renderer: \$r, integrated: \$i}')
gpus=\$(echo "\$gpus" | jq --argjson obj "\$gpu_obj" '. += [\$obj]')
done < <(lspci -D | grep -iE 'VGA|3D' | awk '{print $1}')
# Disk name and simple path
DISK_DEVPATH+=("/dev/\$DISK")
DISK_NAME+=("\$DISK")
# Disk type
HDD=\$(cat /sys/block/\$DISK/queue/rotational)
TRANSPORT_PROTOCOL=\$(lsblk -x SIZE -d -n -e 7,11 -o TRAN /dev/\$DISK)
if [[ "\$DISK" == "nvme*" ]]; then DISK_TYPE+=("NVMe");
elif [[ "\$TRANSPORT_PROTOCOL" == "usb" ]]; then DISK_TYPE+=("USB");
elif [[ "\$HDD" -eq 1 ]]; then DISK_TYPE+=("HDD");
elif [[ "\$HDD" -eq 0 ]]; then DISK_TYPE+=("SSD");
else DISK_TYPE+=("Other")
append_to_report "graphics" "\$gpus"
}
# --- 2. Detect Coral TPUs ---
detect_corals() {
local corals="[]"
# Check PCIe Coral (Google ID 1ac1:089a)
if lspci -nn 2>/dev/null | grep -iq "1ac1:089a"; then
local pcie_count
pcie_count=\$(lspci -nn | grep -ic "1ac1:089a")
for ((i=1; i<=pcie_count; i++)); do
local obj=\$(jq -n --arg i "PCIe" '{interface: \$i, type: "Edge TPU"}')
corals=\$(echo "\$corals" | jq --argjson obj "\$obj" '. += [\$obj]')
done
fi
# Disk health
if [[ \$(echo "${LIVE_TARGET_PASSWORD}" | sudo -S smartctl -H /dev/\$DISK 2>/dev/null | grep 'self-assessment' | awk '{print \$6}') == "PASSED" ]]; then
DISK_HEALTH+=("PASSED")
else
DISK_HEALTH+=("N/A")
# Check USB Coral (Google ID 18d1:9302)
if lsusb 2>/dev/null | grep -iq "18d1:9302"; then
local usb_count
usb_count=\$(lsusb | grep -ic "18d1:9302")
for ((i=1; i<=usb_count; i++)); do
local obj=\$(jq -n --arg i "USB" '{interface: \$i, type: "Edge TPU"}')
corals=\$(echo "\$corals" | jq --argjson obj "\$obj" '. += [\$obj]')
done
fi
# Disk ID
DISK_ID+=("\$(ls -l /dev/disk/by-id | grep -m1 "../../\$DISK" | awk '{print "/dev/disk/by-id/" \$9}')")
DISK_SIZE+=("\$(lsblk -x SIZE -d -n -e 7,11 -o SIZE /dev/\$DISK)")
done
echo "# Hardware detection results on \$(date)" > "${TMPFILE}"
for var in \
TARGET_GRAPHICS \
TARGET_GRAPHICS_RENDERER \
TARGET_USB_CORAL \
TARGET_PCIE_CORAL \
TARGET_ZIGBEE_DEVICE \
TARGET_INTERFACE \
TARGET_TPM \
TARGET_TPM_VERSION; do
echo "export \${var}=\${!var}" >> "${TMPFILE}"
done
append_to_report "coral_devices" "\$corals"
}
for var in \
TARGET_GRAPHICS_BRAND \
DISK_DEVPATH \
DISK_NAME \
DISK_TYPE \
DISK_HEALTH \
DISK_ID \
DISK_SIZE; do
declare -p \${var} | sed 's/^declare /declare -g /' >> "${TMPFILE}"
done
SSHEND
# --- 3. Detect Zigbee Coordinators ---
detect_zigbee() {
local zigbees="[]"
local serial_dir="/dev/serial/by-id"
scp -i "${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${TMPFILE}" "${TMPFILE}" &> /dev/null
source "${TMPFILE}"
if [[ -d "\$serial_dir" ]]; then
for dev in "\$serial_dir"/*; do
[[ -e "\$dev" ]] || continue # skip if empty directory pattern matched
# Match common Zigbee adapter names (Sonoff, ConBee, ITead, CC2531, etc.)
if echo "\$dev" | grep -iE 'zigbee|conbee|sonoff|cc2531|efr32|itead'; then
local obj=\$(jq -n --arg p "\$dev" '{device_path: \$p}')
zigbees=\$(echo "\$zigbees" | jq --argjson obj "\$obj" '. += [\$obj]')
fi
done
fi
local DISK_FLAT_ARRAY=()
for i in "${!DISK_NAME[@]}"; do
DISK_FLAT_ARRAY+=("${DISK_NAME[$i]}" "${DISK_DEVPATH[$i]}" "${DISK_TYPE[$i]}" "${DISK_HEALTH[$i]}" "${DISK_ID[$i]}" "${DISK_SIZE[$i]}")
append_to_report "zigbee_devices" "\$zigbees"
}
# --- 4. Detect Network Interfaces ---
detect_network() {
local networks="[]"
local default_iface
default_iface=\$(ip -4 route show default 2>/dev/null | awk '{print \$5}' | head -n1)
for iface_path in /sys/class/net/*; do
[[ -e "\$iface_path" ]] || continue
local iface
iface=\$(basename "\$iface_path")
# Skip loopback and virtual interfaces
[[ "\$iface" == "lo" ]] && continue
[[ -L "\$iface_path" && \$(readlink "\$iface_path") == *"virtual"* ]] && continue
local type="wired"
[[ -d "\$iface_path/wireless" ]] && type="wireless"
local is_default="false"
[[ "\$iface" == "\$default_iface" ]] && is_default="true"
local obj=\$(jq -n --arg n "\$iface" --arg t "\$type" --argjson d "\$is_default" \
'{name: \$n, type: \$t, default: \$d}')
networks=\$(echo "\$networks" | jq --argjson obj "\$obj" '. += [\$obj]')
done
jq -n \
--argjson graphics_enabled "${TARGET_GRAPHICS:-false}" \
--argjson graphics_renderer "${TARGET_GRAPHICS_RENDERER:-false}" \
--argjson tpu_usb "${TARGET_USB_CORAL:-false}" \
--argjson tpu_pcie "${TARGET_PCIE_CORAL:-false}" \
--argjson tpm_enabled "${TARGET_TPM:-false}" \
--arg tpm_version "${TARGET_TPM_VERSION:-N/A}" \
--arg zigbee_device "${TARGET_ZIGBEE_DEVICE:-}" \
--arg interface "${TARGET_INTERFACE:-}" \
--argjson brands "$(jq -n '$ARGS.positional' --args ${TARGET_GRAPHICS_BRAND[@]:-})" \
'
{
graphics: { enabled: $graphics_enabled, brands: $brands, renderer: $graphics_renderer },
tpu: { usb: $tpu_usb, pcie: $tpu_pcie },
tpm: { enabled: $tpm_enabled, version: $tpm_version },
zigbee: { device: $zigbee_device },
network: { interface: $interface },
disks: [
$ARGS.positional | range(0; length; 6) as $i | {
name: .[$i], path: .[$i+1], type: .[$i+2], health: .[$i+3], id: .[$i+4], size: .[$i+5]
}
]
}' --args "${DISK_FLAT_ARRAY[@]:-}" > ${HARDWARE_DATA_PATH}
append_to_report "network_interfaces" "\$networks"
}
if ssh_to_host "sudo nixos-generate-config --no-filesystems --show-hardware-config" > ${EXTRA_FILES_PATH}/etc/nixos/hardware-configuration.nix; then
# --- 5. Detect TPM ---
detect_tpm() {
local tpms="[]"
for tpm_dir in /sys/class/tpm/tpm*; do
[[ -e "\$tpm_dir" ]] || continue
local name
name=\$(basename "\$tpm_dir")
local version="Unknown"
if [[ -f "\$tpm_dir/tpm_version_major" ]]; then
version=\$(cat "\$tpm_dir/tpm_version_major")
fi
local obj=\$(jq -n --arg n "\$name" --arg v "\$version" '{name: \$n, version: \$v}')
tpms=\$(echo "\$tpms" | jq --argjson obj "\$obj" '. += [\$obj]')
done
append_to_report "tpm" "\$tpms"
}
# --- 6. Detect Disks ---
detect_disks() {
local disks="[]"
# lsblk to loop through block devices (ignoring loops, rams, and cdroms)
while read -r disk; do
local dev_path="/dev/\$disk"
# Disk Type Mapping
local disk_type="Other"
local transport
transport=\$(lsblk -d -n -o TRAN "\$dev_path" 2>/dev/null || echo "")
local rotational
rotational=\$(lsblk -d -n -o ROTA "\$dev_path" 2>/dev/null || echo "1")
if [[ "\$disk" == nvme* ]]; then
disk_type="NVMe"
elif [[ "\$transport" == "usb" ]]; then
disk_type="USB"
elif [[ "\$rotational" == "1" ]]; then
disk_type="HDD"
elif [[ "\$rotational" == "0" ]]; then
disk_type="SSD"
fi
# Size in GB
local size_bytes
size_bytes=\$(lsblk -d -n -b -o SIZE "\$dev_path" 2>/dev/null || echo "0")
local size_gb
size_gb=\$(awk "BEGIN {printf \"%.2f\", \$size_bytes/1073741824}")
# ID via by-id
local disk_id="N/A"
if [[ -d "/dev/disk/by-id" ]]; then
local id_match
id_match=\$(find /dev/disk/by-id/ -type l -not -name "wwn-*" -not -name "nvme-eui*" -printf "%p %l\n" | grep -m1 "/\$disk\$" | awk '{print \$1}')
[[ -n "\$id_match" ]] && disk_id="\$id_match"
fi
# Health Assessment (Requires smartctl)
local health="N/A"
if echo "${LIVE_TARGET_PASSWORD}" | sudo -S smartctl -H "\$dev_path" >/dev/null 2>&1; then
if smartctl -H "\$dev_path" 2>/dev/null | grep -iq "PASSED\|OK"; then
health="PASSED"
else
health="FAILED"
fi
fi
local obj=\$(jq -n --arg n "\$disk" --arg dp "\$dev_path" --arg t "\$disk_type" \
--arg h "\$health" --arg id "\$disk_id" --arg s "\$size_gb" \
'{name: \$n, device_path: \$dp, type: \$t, health: \$h, id: \$id, size_gb: \$s}')
disks=\$(echo "\$disks" | jq --argjson obj "\$obj" '. += [\$obj]')
done < <(lsblk -d -n -o NAME -e 7,11,252) # Exclude loop(7), sr(11), zram(252)
append_to_report "disks" "\$disks"
}
# --- Execution ---
detect_graphics
detect_corals
detect_zigbee
detect_network
detect_tpm
detect_disks
# --- Output ---
echo "\$HW_REPORT" | jq '.' > "$TMPFILE"
SSHEND
scp -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${TMPFILE}" "${HW_DATA_FILE}" &> /dev/null
if ssh_to_host "sudo nixos-generate-config --no-filesystems --show-hardware-config" > ${TMP_EXTRA_PATH}/etc/nixos/hardware-configuration.nix; then
echo -e "\n✅ Hardware configuration generated"
else
echo -e "\n❌ Failed to generate hardware configuration"
@@ -235,18 +346,18 @@ launch_gui() {
# --- MAIN TUI FUNCTIONS --->
# --- MAIN SCRIPT FUNCTIONS --->
setup_ssh() {
echod "\n ✅ Generating new SSH key for numbus-admin..."
chmod 700 ${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/
ssh-keygen -t "ed25519" -C "numbus-admin@numbus-server" -f "${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/id_ed25519" -N "" -q
chmod 700 ${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/
ssh-keygen -t "ed25519" -C "numbus-admin@numbus-server" -f "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" -N "" -q
if [[ ${DEBUG} -eq 1 ]]; then
echo -e "\n ➡️ Copying SSH key to target host '${TARGET_USER}@${LIVE_TARGET_IP}'..."
fi
if sshpass -p "${LIVE_TARGET_PASSWORD}" ssh-copy-id -o StrictHostKeyChecking=no -i "${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}"; then
if sshpass -p "${LIVE_TARGET_PASSWORD}" ssh-copy-id -o StrictHostKeyChecking=no -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}"; then
if [[ ${DEBUG} -eq 1 ]]; then
echo -e "\n ✅ SSH key copied successfully"
fi
@@ -289,13 +400,13 @@ server_config_generation() {
enabledApps: $apps,
managementConsole: $cockpit_enabled
}
}' > "${EXTRA_FILES_PATH}/etc/nixos/settings.json"
}' > "${TMP_EXTRA_PATH}/etc/nixos/settings.json"
echo -e "{\n numbus.settings = builtins.fromJSON (builtins.readFile ./settings.json);\n}" > "${CONFIGURATION_PATH}"
# Ensure the settings file is writable by the management service
# and that the directory is prepared for local git tracking
chmod 664 "${EXTRA_FILES_PATH}/etc/nixos/settings.json"
chmod 664 "${TMP_EXTRA_PATH}/etc/nixos/settings.json"
}
# The existing network_config_generation and services_config_generation functions
@@ -331,8 +442,8 @@ disk_config_generation() {
keys_generation() {
for i in $(seq 1 "${#BOOT_DISKS_ID_LIST[@]}"); do
PASS="$(xkcdpass)"
echo -n "$PASS" > "${EXTRA_FILES_PATH}/etc/secrets/disks/boot-${i}"
chmod 600 "${EXTRA_FILES_PATH}/etc/secrets/disks/boot-${i}"
echo -n "$PASS" > "${TMP_EXTRA_PATH}/etc/secrets/disks/boot-${i}"
chmod 600 "${TMP_EXTRA_PATH}/etc/secrets/disks/boot-${i}"
ssh_to_host 'bash -s' << EOF
echo "$LIVE_TARGET_PASSWORD" | sudo -S mkdir -p /etc/secrets/disks/
echo "$LIVE_TARGET_PASSWORD" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/boot-${i}"
@@ -341,8 +452,8 @@ EOF
done
for i in $(seq 1 "$CONTENT_DISK_NUMBER"); do
PASS="$(xkcdpass)"
echo -n "$PASS" > "${EXTRA_FILES_PATH}/etc/secrets/disks/content-${i}"
chmod 600 "${EXTRA_FILES_PATH}/etc/secrets/disks/content-${i}"
echo -n "$PASS" > "${TMP_EXTRA_PATH}/etc/secrets/disks/content-${i}"
chmod 600 "${TMP_EXTRA_PATH}/etc/secrets/disks/content-${i}"
ssh_to_host 'bash -s' << EOF
echo "$LIVE_TARGET_PASSWORD" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/content-${i}"
echo "$LIVE_TARGET_PASSWORD" | sudo -S chmod 600 /etc/secrets/disks/content-${i}
@@ -350,8 +461,8 @@ EOF
done
for i in $(seq 1 "$PARITY_DISK_NUMBER"); do
PASS="$(xkcdpass)"
echo -n "$PASS" > "${EXTRA_FILES_PATH}/etc/secrets/disks/parity-${i}"
chmod 600 "${EXTRA_FILES_PATH}/etc/secrets/disks/parity-${i}"
echo -n "$PASS" > "${TMP_EXTRA_PATH}/etc/secrets/disks/parity-${i}"
chmod 600 "${TMP_EXTRA_PATH}/etc/secrets/disks/parity-${i}"
ssh_to_host 'bash -s' << EOF
echo "$LIVE_TARGET_PASSWORD" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/parity-${i}"
echo "$LIVE_TARGET_PASSWORD" | sudo -S chmod 600 /etc/secrets/disks/parity-${i}
@@ -369,18 +480,18 @@ EOF
export SSH_KEYS_FORMATTED
echo -e "\n ✅ Generating sops-nix keys..."
ssh-to-age -private-key -i ${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/id_ed25519 > ${EXTRA_FILES_PATH}/var/lib/sops-nix/key.txt
export SOPS_PUBLIC_KEY=$(age-keygen -y ${EXTRA_FILES_PATH}/var/lib/sops-nix/key.txt)
ssh-to-age -private-key -i ${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519 > ${TMP_EXTRA_PATH}/var/lib/sops-nix/key.txt
export SOPS_PUBLIC_KEY=$(age-keygen -y ${TMP_EXTRA_PATH}/var/lib/sops-nix/key.txt)
echo -e "\n ✅ Generating sops-nix configuration files..."
envsubst < templates/nix-config/sops-nix/.sops.yaml > ${EXTRA_FILES_PATH}/etc/nixos/.sops.yaml
envsubst < templates/nix-config/sops-nix/.sops.yaml > ${TMP_EXTRA_PATH}/etc/nixos/.sops.yaml
echo -e "\n ✅ Encrypting secrets in the correct file..."
envsubst < "templates/nix-config/sops-nix/secrets.yaml" \
| sops encrypt --filename-override secrets.yaml \
--input-type yaml --output-type yaml \
--age $SOPS_PUBLIC_KEY \
--output ${EXTRA_FILES_PATH}/etc/nixos/secrets/secrets.yaml
--output ${TMP_EXTRA_PATH}/etc/nixos/secrets/secrets.yaml
}
cloudflare_dns_setup() {
@@ -485,24 +596,24 @@ cloudflare_dns_setup() {
}
deploy() {
git -C . add -f "${EXTRA_FILES_PATH}/"
git -C . add -f "${TMP_EXTRA_PATH}/"
git -C . add -f "templates/"
git -C . add -f "deploy.conf"
# Initialize a git repo in the configuration to be deployed
# This allows the Management UI on the appliance to commit changes
# and provide a local history/rollback UI to the user.
if [ ! -d "${EXTRA_FILES_PATH}/etc/nixos/.git" ]; then
git -C "${EXTRA_FILES_PATH}/etc/nixos" init -q
git -C "${EXTRA_FILES_PATH}/etc/nixos" add .
git -C "${EXTRA_FILES_PATH}/etc/nixos" commit -m "Initial bootstrap via Numbus Deploy" -q
if [ ! -d "${TMP_EXTRA_PATH}/etc/nixos/.git" ]; then
git -C "${TMP_EXTRA_PATH}/etc/nixos" init -q
git -C "${TMP_EXTRA_PATH}/etc/nixos" add .
git -C "${TMP_EXTRA_PATH}/etc/nixos" commit -m "Initial bootstrap via Numbus Deploy" -q
fi
echo -e "\n\n🔄 Deploying to the remote server..."
nix flake update --flake ./${EXTRA_FILES_PATH}/etc/nixos
nix flake update --flake ./${TMP_EXTRA_PATH}/etc/nixos
nix run github:nix-community/nixos-anywhere -- \
--flake ${EXTRA_FILES_PATH}/etc/nixos#numbus-server \
--extra-files ${EXTRA_FILES_PATH} \
--flake ${TMP_EXTRA_PATH}/etc/nixos#numbus-server \
--extra-files ${TMP_EXTRA_PATH} \
--chown "/home/numbus-admin/" 1000:1000 \
--target-host ${TARGET_USER}@${LIVE_TARGET_IP}
@@ -550,7 +661,7 @@ postrun_action() {
Do you want to enable automatic disk decryption on boot ?"
if gum confirm "➡️ I understand, 'yes' to proceed."; then
sshpass -p "${LIVE_TARGET_PASSWORD}" ssh -i "${EXTRA_FILES_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}" 'bash -s' << EOF
sshpass -p "${LIVE_TARGET_PASSWORD}" ssh -i "${TMP_EXTRA_PATH}/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}" 'bash -s' << EOF
echo "Enrolling boot disk key to TPM..."
BOOT_DISKS_NAME=(${BOOT_DISKS_NAME[@]})
@@ -593,7 +704,7 @@ securely on a hidden sheet of paper or add it to your password manager (locally
echo $LIVE_TARGET_PASSWORD | sudo -S passwd numbus-admin
}
# --- MAIN FUNCTIONS ---<
# --- MAIN SCRIPT FUNCTIONS ---<
@@ -604,10 +715,11 @@ LIVE_DATA_FILE="../config/live.yaml"
HW_DATA_FILE="../config/hardware.yaml"
CONFIG_FILE="../config/numbus.yaml"
BRIDGE_SCRIPT="../web/logic/interactive.py"
TARGET_USER="nixos"
TMP_FILES_PATH="/run/user/$(id -u)/numbus-$(date +"%Y-%m-%d-%Hh%M")"
EXTRA_FILES_PATH="${TMP_FILES_PATH}/config"
TMP_EXTRA_PATH="../extra"
if [[ ${DEBUG-0} -eq 1 ]]; then
FILES_CP_FLAGS="vau"
@@ -656,37 +768,17 @@ echo """
/_/|_/\____/_/ /_/____/\____/___/
"""
DEPLOYMENT_STRATEGY=$(gum choose --header "Choose your preferred deployment strategy :" \
"I don't have a configuration" \
"I have a valid configuration hosted on a Git platform")
if [[ "${DEPLOYMENT_STRATEGY}" == "I don't have a configuration" ]]; then
BRIDGE_SCRIPT="../web/logic/interactive.py"
launch_gui
hierarchy_preparation
until [[ -e ../web/signals/hw_detection_ready ]]; do
sleep 5
done
LIVE_TARGET_IP="$(yq -r '.live_target_ip' ${LIVE_DATA_FILE})"
LIVE_TARGET_PASSWORD="$(yq -r '.live_target_password' ${LIVE_DATA_FILE})"
until [[ -e ../web/signals/configuration_ready ]]; do
sleep 5
done
until [[ -e ../web/signals/deployment_ready ]]; do
sleep 5
done
else
BRIDGE_SCRIPT="../web/logic/non-interactive.py"
launch_gui
hierarchy_preparation
until [[ -e ../web/signals/hw_detection_ready ]]; do
sleep 5
done
until [[ -e ../web/signals/hw_detection_ready ]]; do
sleep 5
done
until [[ -e ../web/signals/hw_detection_ready ]]; do
sleep 5
done
fi
launch_gui
hierarchy_preparation
until [[ -e ../web/signals/hw_detection_ready ]]; do
sleep 5
done
LIVE_TARGET_IP="$(yq -r '.live_target_ip' ${LIVE_DATA_FILE})"
LIVE_TARGET_PASSWORD="$(yq -r '.live_target_password' ${LIVE_DATA_FILE})"
until [[ -e ../web/signals/configuration_ready ]]; do
sleep 5
done
until [[ -e ../web/signals/deployment_ready ]]; do
sleep 5
done
# --- MAIN LOGIC ---<
+5 -5
View File
@@ -6,10 +6,10 @@ import sys
### Variables -->
SECRET_PATH = "/run/user/{}/numbus".format(os.getuid()) if os.path.exists("/run/user/{}".format(os.getuid())) else "../secrets"
os.makedirs(SECRET_PATH, exist_ok=True)
LOGS_DIR = "../web/logs/"
PAGES_DIR = "../web/pages/"
CONFIG_DIR = "../web/config/"
SIGNALS_DIR = "../web/signal/"
LOGS_DIR = "../logs/"
PAGES_DIR = "../pages/"
CONFIG_DIR = "../config/"
SIGNALS_DIR = "../signal/"
### <-- Variables
class BridgeHandler(http.server.SimpleHTTPRequestHandler):
@@ -37,7 +37,7 @@ class BridgeHandler(http.server.SimpleHTTPRequestHandler):
if self.path == '/discovery':
# Store secrets in memory-backed filesystem
with open(os.path.join(SECRET_PATH, "live_settings.json"), "wb") as f:
with open(os.path.join(SECRET_PATH, "live.yaml"), "wb") as f:
f.write(post_data)
self.send_response(200)
self.end_headers()
-55
View File
@@ -1,55 +0,0 @@
import http.server
import json
import os
import sys
### Variables -->
SECRET_PATH = "/run/user/{}/numbus".format(os.getuid()) if os.path.exists("/run/user/{}".format(os.getuid())) else "../secrets"
os.makedirs(SECRET_PATH, exist_ok=True)
LOGS_DIR = "../web/logs/"
PAGES_DIR = "../web/pages/"
CONFIG_DIR = "../web/config/"
SIGNALS_DIR = "../web/signal/"
### <-- Variables
class BridgeHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# Route for logs: /logs?type=out or /logs?type=err
if self.path.startswith('/logs'):
log_type = "out" if "type=err" not in self.path else "err"
log_path = os.path.join(LOGS_DIR, f'deploy-{log_type}.log')
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
if os.path.exists(log_path):
with open(log_path, 'r') as f:
# Read last 50 lines for better context during errors
self.wfile.write("".join(f.readlines()[-50:]).encode())
return
return http.server.SimpleHTTPRequestHandler.do_GET(self)
def do_POST(self):
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
if self.path == '/discovery':
# Store secrets in memory-backed filesystem
with open(os.path.join(SECRET_PATH, "live_settings.json"), "wb") as f:
f.write(post_data)
self.send_response(200)
self.end_headers()
# Signal Bash that discovery data is ready
with open(os.path.join(SIGNALS_DIR, ".discovery_ready"), "w") as f: f.write("1")
elif self.path == '/deploy':
with open(os.path.join(CONFIG_DIR, "numbus.yaml"), "wb") as f:
f.write(post_data)
self.send_response(200)
self.end_headers()
with open(os.path.join(SIGNALS_DIR, ".deploy_signal"), "w") as f: f.write("1")
os.chdir(PAGES_DIR)
http.server.HTTPServer(('localhost', 8088), BridgeHandler).serve_forever()