Lots of changes. Needs more work.

This commit is contained in:
Raphaël Numbus
2026-02-23 16:35:24 +01:00
parent 30b6ce5f9c
commit bc8ad796de
51 changed files with 186 additions and 4083 deletions
+145 -400
View File
@@ -7,10 +7,12 @@ export GUM_SPIN_SPINNER_BOLD=true
export GUM_SPIN_SHOW_ERROR=true
export GUM_SPIN_TITLE_BOLD=true
NECESSARY_VARIABLES_LIST=( "TARGET_HOST" "REMOTE_PASS" "SSH_PUBLIC_KEY" "DOMAIN_NAME" \
"EMAIL_ADDRESS" "CF_DNS_API_TOKEN" "SENDER_EMAIL_ADDRESS" "SENDER_EMAIL_ADDRESS_PASSWORD" \
"SENDER_EMAIL_DOMAIN" "SENDER_EMAIL_PORT" "SERVER_OWNER_NAME" "SELECTED_SERVICES" \
"HOME_ROUTER_SUBNET" "HOME_ROUTER_IP" "HOME_SERVER_IP" )
NECESSARY_VARIABLES_LIST=( LIVE_TARGET_IP LIVE_TARGET_PASSWD \
SERVER_OWNER_NAME SERVER_USER_EMAIL SERVER_ADMIN_EMAIL AUTHORIZED_SSH_PUBLIC_KEY \
DOMAIN_NAME CLOUDFLARE_DNS_API_TOKEN \
SMTP_SERVER_USERNAME SMTP_SERVER_PASSWORD SMTP_SERVER_HOST SMTP_SERVER_PORT \
NETWORK_SUBNET NETWORK_ROUTER_IP HOME_SERVER_IP)
### Default settings <--
user_input() {
@@ -44,8 +46,8 @@ user_input() {
strictly_necessary_information() {
export IP_REGEX='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
user_input "TARGET_HOST" " Please provide the IP address of the target host :" "For example : 192.168.1.100" "${IP_REGEX}" "Invalid IP address format."
user_input "REMOTE_PASS" " Please enter the password for '${TARGET_USER}@${TARGET_HOST}' :" "${TARGET_HOST}'s password" "" "" "true"
user_input "LIVE_TARGET_IP" " Please provide the IP address of the target host :" "For example : 192.168.1.100" "${IP_REGEX}" "Invalid IP address format."
user_input "LIVE_TARGET_PASSWD" " Please enter the password for '${TARGET_USER}@${LIVE_TARGET_IP}' :" "${LIVE_TARGET_IP}'s password" "" "" "true"
}
necessary_information() {
@@ -57,29 +59,34 @@ necessary_information() {
local SSH_KEY_REGEX='^ssh-[a-z0-9]+ [A-Za-z0-9+/]+.*'
echo -e "\n\n➡️ This script needs information about the target you want to install NixOS on\n"
#TARGET SETTINGS
user_input "TARGET_HOST" " Please provide the IP address of the target host :" "For example : 192.168.1.100" "${IP_REGEX}" "Invalid IP address format."
user_input "REMOTE_PASS" " Please enter the password for '${TARGET_USER}@${TARGET_HOST}' :" "${TARGET_HOST}'s password" "" "" "true"
user_input "SSH_PUBLIC_KEY" " Please provide the public SSH key of an authorized device :" "For example : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGhcYDmjMo5YApLkk/3P3HZCnOSzm0uYewNAbxL8Fci8 user@your-pc" "${SSH_KEY_REGEX}" "Invalid SSH key format (must start with ssh-...)." "true"
# LIVE TARGET SETTINGS
user_input "LIVE_TARGET_IP" " Please provide the IP address of the target host :" "For example : 192.168.1.100" "${IP_REGEX}" "Invalid IP address format."
user_input "LIVE_TARGET_PASSWD" " Please enter the password for '${TARGET_USER}@${LIVE_TARGET_IP}' :" "${LIVE_TARGET_IP}'s password" "" "" "true"
echo -e "\n\n➡️ Now provide some information about the server you are deploying\n"
# SERVER SETTINGS
user_input "TIMEZONE" " Please provide the wanted timezone :" "For example : Europe/Paris, Europe/Berlin" "" ""
user_input "SERVER_OWNER_NAME" " Please provide the name of the owner of this server :" "For example : Steve" "" ""
user_input "SERVER_USER_EMAIL" " Please provide a valid user email address (to stay informed about your server's health) :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format."
user_input "SERVER_ADMIN_EMAIL" " Please provide a valid admin email address (will be used for ACME, and system failures notifications) :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format."
user_input "AUTHORIZED_SSH_PUBLIC_KEY" " Please provide a list of SSH public keys of authorized devices :" "For example : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGhcYDmjMo5YApLkk/3P3HZCnOSzm0uYewNAbxL8Fci8 user@your-pc" "${SSH_KEY_REGEX}" "Invalid SSH key format (must start with ssh-...)." "true"
echo -e "\n\n➡️ You will access your services via a domain name (e.g. cloud.mydomain.com) and containers need credentials to create those subdomains\n"
# TRAEFIK SETTINGS
user_input "DOMAIN_NAME" " Please provide the domain name (FQDN) your home server will use :" "For example : yourdomain.com" "${DOMAIN_REGEX}" "Invalid domain name format."
user_input "EMAIL_ADDRESS" " Please provide a valid email address (will be used for ACME, and your services) :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format."
user_input "CF_DNS_API_TOKEN" " Please provide a cloudflare API token with DNS zone permission :" "For example : bA7hdvCOuXGytlNKohi3ZGtlVpf5CHpLuCMiJrE" "" "" "true"
user_input "SERVER_OWNER_NAME" " Please provide the name of the server owner :" "For example : Steve" "" ""
user_input "CLOUDFLARE_DNS_API_TOKEN" " Please provide a cloudflare API token with DNS zone permission :" "For example : bA7hdvCOuXGytlNKohi3ZGtlVpf5CHpLuCMiJrE" "" "" "true"
echo -e "\n\n➡️ Some services will be able to send you emails. For that you need an email that supports sending emails (like Gmail for example)\n"
# SMTP SETTINGS
user_input "SENDER_EMAIL_ADDRESS" " Please provide a valid sender email address :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format."
user_input "SENDER_EMAIL_ADDRESS_PASSWORD" " Please provide the password of this email address :" "abcd efgh ijkl mnop" "" "" "true"
user_input "SENDER_EMAIL_DOMAIN" " Please provide the SMTP server endpoint :" "For Gmail : smtp.gmail.com" "${DOMAIN_REGEX}" "Invalid domain name format."
user_input "SENDER_EMAIL_PORT" " Please provide the smtp TLS port :" "For Gmail : 587" "${PORT_REGEX}" "Invalid port number."
user_input "SMTP_SERVER_USERNAME" " Please provide a valid sender email address :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format."
user_input "SMTP_SERVER_PASSWORD" " Please provide the password of this email address :" "abcd efgh ijkl mnop" "" "" "true"
user_input "SMTP_SERVER_HOST" " Please provide the SMTP server endpoint :" "For Gmail : smtp.gmail.com" "${DOMAIN_REGEX}" "Invalid domain name format."
user_input "SMTP_SERVER_PORT" " Please provide the smtp TLS port :" "For Gmail : 587" "${PORT_REGEX}" "Invalid port number."
echo -e "\n\n➡️ This server will connect to your local network and you will configure its IP address\n"
# NETWORK SETTINGS
user_input "HOME_ROUTER_SUBNET" " Please provide your home network subnet :" "For example 192.168.1.0/24" "${SUBNET_REGEX}" "Invalid subnet format (e.g. 192.168.1.1/24)."
user_input "HOME_ROUTER_IP" " Please provide the ip address of your router :" "Most likely 192.168.1.1 or 192.168.1.254" "${IP_REGEX}" "Invalid IP address format."
user_input "NETWORK_SUBNET" " Please provide your network subnet :" "For example 192.168.1.0/24" "${SUBNET_REGEX}" "Invalid subnet format (e.g. 192.168.1.1/24)."
user_input "NETWORK_ROUTER_IP" " Please provide the ip address of your router :" "Most likely 192.168.1.1 or 192.168.1.254" "${IP_REGEX}" "Invalid IP address format."
user_input "HOME_SERVER_IP" " Please choose the ip address that your server will use (i.e. any address in the 192.168.1.1/24 range that is not in use.) :" "For example 192.168.1.5" "${IP_REGEX}" "Invalid IP address format."
}
@@ -113,11 +120,6 @@ necessary_information_config() {
fi
}
more_information_config() {
sshpass -p "${REMOTE_PASS}" scp ${TARGET_USER}@${TARGET_HOST}:/etc/numbus-server/numbus-server.conf .
source "numbus-server.conf"
}
setup_ssh() {
mkdir -p final-nix-config/
mkdir -p final-nix-config/etc/
@@ -130,8 +132,8 @@ setup_ssh() {
chmod 700 final-nix-config/home/numbus-admin/.ssh/
ssh-keygen -t "ed25519" -C "numbus-admin@numbus-server" -f "final-nix-config/home/numbus-admin/.ssh/id_ed25519" -N "" -q
echo -e "\n\n➡️ Copying SSH key to target host '${TARGET_USER}@${TARGET_HOST}'..."
if sshpass -p "${REMOTE_PASS}" ssh-copy-id -o StrictHostKeyChecking=no -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${TARGET_HOST}"; then
echo -e "\n\n➡️ Copying SSH key to target host '${TARGET_USER}@${LIVE_TARGET_IP}'..."
if sshpass -p "${LIVE_TARGET_PASSWD}" ssh-copy-id -o StrictHostKeyChecking=no -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}"; then
echo -e "\n✅ SSH key copied successfully"
else
echo -e "\n❌ Failed to copy SSH key. Please check the host IP and password."
@@ -141,7 +143,18 @@ setup_ssh() {
ssh_to_host() {
local COMMAND="${1}"
ssh -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${TARGET_HOST}" "${COMMAND}"
ssh -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}" "${COMMAND}"
}
hierarchy_preparation() {
mkdir -p /final-nix-config/
mkdir -p /final-nix-config/etc
mkdir -p /final-nix-config/etc/nixos
mkdir -p /final-nix-config/etc/nixos/secrets
echo -e "\n ✅ Writing configuration..."
cp -${FILES_COPY_FLAGS} templates/nix-config/configuration.nix final-nix-config/etc/nixos/configuration.nix
export CONFIGURATION_PATH="final-nix-config/etc/nixos/configuration.nix"
}
hardware_detection() {
@@ -196,7 +209,7 @@ for DISK in \$(lsblk -x SIZE -d -n -e 7,11 -o NAME); do
fi
# Disk health
if [[ \$(echo "$REMOTE_PASS" | sudo -S smartctl -H /dev/\$DISK 2>/dev/null | grep 'self-assessment' | awk '{print \$6}') == "PASSED" ]]; then
if [[ \$(echo "$LIVE_TARGET_PASSWD" | 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")
@@ -232,7 +245,7 @@ done
SSHEND
### Get hardware information <--
scp -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${TARGET_HOST}":"${TMPFILE}" "${TMPFILE}" &> /dev/null
scp -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${TMPFILE}" "${TMPFILE}" &> /dev/null
source "${TMPFILE}" && rm -rf "${TMPFILE}"
### --> Generate hardware-configuration.nix
@@ -248,34 +261,46 @@ SSHEND
services_selection() {
echo -e "\n\n ➡️ You will now select the services you want installed on your server:"
local AVAILABLE_DNS_SERVICES=( "pi-hole" "adguard" )
local AVAILABLE_SERVICES=( "frigate" "gitea" "home-assistant" "immich" "it-tools" \
"nextcloud" "passbolt" "virtualization" )
local SERVICES_DESCRIPTION=( "Pi-Hole [Always included] : Block ads on all your devices" \
"Immich : Pictures and videos backup with local machine-learning" \
local DNS_SERVICES_DESCRIPTION=( "Pi-Hole : Simple open-source DNS black hole" \
"AdGuard " : Feature rich DNS service )
local SERVICES_DESCRIPTION=( "Immich : Pictures and videos backup with local machine-learning" \
"Nextcloud : No fuss Office 365 replacement" \
"Passbolt: Security-first password manager with collaboration features" \
"Home-Assistant : Manage your smart home and security cameras" \
"Frigate [Home Assistant required] : Secure your house with security cameras" \
"Gitea : Your own git platform" \
"IT-tools : A set of useful tools when doing IT" \
"Virtualization : Run Virtual Machines (KVM/QEMU) with Libvirt"
)
"Virtualization : Run Virtual Machines (KVM/QEMU) with Libvirt" )
SELECTED_SERVICES=()
local SELECTED_SERVICES_DESCRIPTION=$(gum choose --no-limit --header "Homelab services:" "${SERVICES_DESCRIPTION[@]}")
SELECTED_DNS_SERVICE=""
local SELECTED_DNS_SERVICE_DESCRIPTION=$(gum choose --no-limit --header "Homelab services:" "${DNS_SERVICES_DESCRIPTION[@]}")
for i in ${!AVAILABLE_SERVICES[@]}; do
if printf '%s' "${SELECTED_SERVICES_DESCRIPTION}" | grep -iq "${AVAILABLE_SERVICES[${i}]}"; then
SELECTED_SERVICES+=("${AVAILABLE_SERVICES[${i}]}")
fi
done
for i in ${!AVAILABLE_DNS_SERVICES[@]}; do
if printf '%s' "${SELECTED_DNS_SERVICE_DESCRIPTION}" | grep -iq "${AVAILABLE_DNS_SERVICES[${i}]}"; then
SELECTED_DNS_SERVICE="${AVAILABLE_DNS_SERVICES[${i}]}"
fi
done
export SELECTED_SERVICES
export SELECTED_DNS_SERVICE
}
disks_selection() {
### --> Disk wiping warning
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "
⚠️ $(gum style --foreground 212 'WARNING:') You will choose the disks to install NixOS on.
!! PLEASE MAKE SURE YOU BACKED UP ANY IMPORTANT DATA !!
@@ -285,9 +310,7 @@ disks_selection() {
gum confirm "Do you understand and wish to proceed?" || { echo -e "\n\n ❌ Aborting as requested."; exit 1; }
echo -e "\n\n 🔎 Fetching and analyzing disks from target host... (This may take a moment)"
### Disk wiping warning <--
### --> Disk selection
if [[ "${#DISK_NAME[@]}" -eq 0 ]]; then
echo -e "\n ❌ No disks found on the target host. Aborting."
exit 1
@@ -307,46 +330,19 @@ disks_selection() {
local SELECTED_BOOT_DISK=$(gum choose --limit 2 --header "${HEADER}" "${GUM_PRINTED_ELEMENTS[@]}")
BOOT_DISKS_ID=()
BOOT_DISKS_NAME=()
for i in ${!DISK_NAME[@]}; do
if printf '%s' "$SELECTED_BOOT_DISK" | grep -iqw "${DISK_NAME[${i}]}"; then
BOOT_DISKS_ID+=("${DISK_ID[${i}]:-${DISK_DEVPATH[${i}]}}")
BOOT_DISKS_ID_LIST+=("${DISK_ID[${i}]:-${DISK_DEVPATH[${i}]}}")
BOOT_DISKS_NAME+=("${DISK_NAME[${i}]}")
unset "GUM_PRINTED_ELEMENTS[${i}]"
fi
done
if [[ "${#BOOT_DISKS_ID[@]}" -eq 0 ]]; then
echo -e "\n\n ❌ No boot disk selected. Aborting."
exit 1
elif [[ "${#BOOT_DISKS_ID[@]}" -eq 1 ]]; then
echo -e "\n\n ⚠️ One boot disk selected, continuing with striped boot disk configuration."
echo -e "Consider using 2 boot disks instead to get data protection features on the boot disks."
export BOOT_DISK_1_ID="${BOOT_DISKS_ID[0]}"
export BOOT_DISK_1_NAME="${BOOT_DISKS_NAME[0]}"
export BOOT_DISK_2_NAME=""
elif [[ "${#BOOT_DISKS_ID[@]}" -eq 2 ]]; then
echo -e "\n\n ✅ Two boot disks selected, continuing with mirrored boot disks configuration."
echo -e "\n\n ⚠️ If the two disks are different sizes, the resulting usable space size will be \
the one of the smallest disk."
export BOOT_DISK_1_ID="${BOOT_DISKS_ID[0]}"
export BOOT_DISK_2_ID="${BOOT_DISKS_ID[1]}"
export BOOT_DISK_1_NAME="${BOOT_DISKS_NAME[0]}"
export BOOT_DISK_2_NAME="${BOOT_DISKS_NAME[1]}"
else
echo -e "\n\n ❌ Unexpected bug. Please contact the developer. Aborting."
exit 1
fi
echo ""
gum style --foreground 212 "➡️ Please choose data and parity disks (up to 9 total) :"
local SELECTED_DATA_DISK=$(gum choose --limit 9 --header "$HEADER" "${GUM_PRINTED_ELEMENTS[@]}")
### Disk selection <--
DATA_DISKS_ID=()
DATA_DISKS_TYPE=()
for i in ${!DISK_NAME[@]}; do
if printf '%s' "$SELECTED_DATA_DISK" | grep -iq "${DISK_NAME[${i}]}"; then
DATA_DISKS_ID+=("${DISK_ID[${i}]:-${DISK_DEVPATH[${i}]}}")
@@ -357,280 +353,88 @@ disks_selection() {
if [[ "${#DATA_DISKS_ID[@]}" -eq 1 ]]; then
export PARITY_DISK_NUMBER=0
export CONTENT_DISK_NUMBER=1
export PARITY_DISK_NUMBER_LIST=()
export CONTENT_DISK_NUMBER_LIST=("${#DATA_DISKS_ID[0]}")
else
export PARITY_DISK_NUMBER=$(((${#DATA_DISKS_ID[@]} + 2) / 3))
export CONTENT_DISK_NUMBER=$((${#DATA_DISKS_ID[@]} - PARITY_DISK_NUMBER))
for i in $(seq 0 $(($CONTENT_DISK_NUMBER - 1))); do
CONTENT_DISK_NUMBER_LIST+=("${#DATA_DISKS_ID[${i}]}")
done
for i in $(seq $CONTENT_DISK_NUMBER $((${#DATA_DISKS_ID[@]} - 1))); do
PARITY_DISK_NUMBER_LIST+=("${#DATA_DISKS_ID[${i}]}")
done
fi
export DATA_DISKS_ID
export DATA_DISKS_TYPE
}
folder_tree_generation() {
mkdir -p final-nix-config/mnt/
mkdir -p final-nix-config/mnt/config/
mkdir -p final-nix-config/mnt/data/
mkdir -p final-nix-config/mnt/config/traefik/
mkdir -p final-nix-config/mnt/config/traefik/rules/
mkdir -p final-nix-config/mnt/config/traefik/certs/
mkdir -p final-nix-config/etc/secrets/
mkdir -p final-nix-config/etc/secrets/disks/
mkdir -p final-nix-config/etc/numbus-server/
mkdir -p final-nix-config/etc/nixos/misc/
mkdir -p final-nix-config/etc/nixos/disks/
mkdir -p final-nix-config/etc/nixos/pcie-coral/
mkdir -p final-nix-config/etc/nixos/podman/
mkdir -p final-nix-config/etc/nixos/secrets/
mkdir -p final-nix-config/var/
mkdir -p final-nix-config/var/lib/
mkdir -p final-nix-config/var/lib/sops-nix
}
services_generation() {
generate_db_creds() {
local SERVICE_UPPER="${1}"
export "${SERVICE_UPPER}_DB_NAME"="$(xkcdpass -d "-" -n 2)"
export "${SERVICE_UPPER}_DB_USERNAME"="$(xkcdpass -d "-" -n 2)"
export "${SERVICE_UPPER}_DB_PASSWORD"="$(xkcdpass -d "-")"
}
generate_network() {
local SERVICE="${1}"
local HAS_BACKEND="${2:-0}"
local NETWORK_NAME_OVERRIDE="${3:-}"
if [[ -z "${NETWORK_NAME_OVERRIDE}" ]]; then
NETWORK_ID=$((NETWORK_ID + 1))
PODMAN_NETWORKS+=" sudo -u numbus-admin podman network exists \"${SERVICE}_frontend\" || sudo -u numbus-admin podman network create --driver=\"bridge\" --subnet=\"10.89.${NETWORK_ID}.0/24\" --ip-range=\"10.89.${NETWORK_ID}.0/24\" --gateway=\"10.89.${NETWORK_ID}.254\" \"${SERVICE}_frontend\""$'\n'
TRAEFIK_NETWORKS+=" ${SERVICE}_frontend:"$'\n'
TRAEFIK_NETWORKS+=" ipv4_address: 10.89.${NETWORK_ID}.253"$'\n'
TRAEFIK_REF_NETWORKS+=" ${SERVICE}_frontend:"$'\n'
TRAEFIK_REF_NETWORKS+=" external: true"$'\n'
if [[ "${HAS_BACKEND}" == "1" ]]; then
NETWORK_ID=$((NETWORK_ID + 1))
PODMAN_NETWORKS+=" sudo -u numbus-admin podman network exists \"${SERVICE}_backend\" || sudo -u numbus-admin podman network create --driver=\"bridge\" --subnet=\"10.89.${NETWORK_ID}.0/24\" --ip-range=\"10.89.${NETWORK_ID}.0/24\" --gateway=\"10.89.${NETWORK_ID}.254\" \"${SERVICE}_backend\""$'\n'
SERVICES_NETWORK_IDS+=("$(( ${NETWORK_ID} - 1 )),${NETWORK_ID}:${SERVICE}")
else
SERVICES_NETWORK_IDS+=("${NETWORK_ID}:${SERVICE}")
fi
else
NETWORK_ID=$((NETWORK_ID + 1))
PODMAN_NETWORKS+=" sudo -u numbus-admin podman network exists \"${NETWORK_NAME_OVERRIDE}\" || sudo -u numbus-admin podman network create --driver=\"bridge\" --subnet=\"10.89.${NETWORK_ID}.0/24\" --ip-range=\"10.89.${NETWORK_ID}.0/24\" --gateway=\"10.89.${NETWORK_ID}.254\" \"${NETWORK_NAME_OVERRIDE}\""$'\n'
TRAEFIK_NETWORKS+=" ${NETWORK_NAME_OVERRIDE}:"$'\n'
TRAEFIK_NETWORKS+=" ipv4_address: 10.89.${NETWORK_ID}.253"$'\n'
TRAEFIK_REF_NETWORKS+=" ${NETWORK_NAME_OVERRIDE}:"$'\n'
TRAEFIK_REF_NETWORKS+=" external: true"$'\n'
SERVICES_NETWORK_IDS+=("${NETWORK_ID}:${SERVICE}")
fi
export NETWORK_ID
export PODMAN_NETWORKS
export TRAEFIK_NETWORKS
export TRAEFIK_REF_NETWORKS
export SERVICES_NETWORK_IDS
}
NETWORK_ID=0
PODMAN_NETWORKS=""
TRAEFIK_NETWORKS=""
TRAEFIK_REF_NETWORKS=""
SERVICES_NETWORK_IDS=()
echo -e "\n ✅ Writing configuration files for the selected homelab services..."
cp -${FILES_COPY_FLAGS} templates/nix-config/configuration.nix final-nix-config/etc/nixos/configuration.nix
cp -${FILES_COPY_FLAGS} templates/nix-config/podman/traefik.nix final-nix-config/etc/nixos/podman/traefik.nix
cp -${FILES_COPY_FLAGS} templates/nix-config/podman/pi-hole.nix final-nix-config/etc/nixos/podman/pi-hole.nix
envsubst < templates/podman-config/traefik/traefik.yaml > final-nix-config/mnt/config/traefik/traefik.yaml
generate_network "pi-hole"
export FTLCONF_WEBSERVER_PASSWORD="$(xkcdpass -d "-")"
for service in "${SELECTED_SERVICES[@]}"; do
# Copy podman container file
[[ "${service}" != "virtualization" ]] && cp -${FILES_COPY_FLAGS} templates/nix-config/podman/"${service}".nix final-nix-config/etc/nixos/podman/"${service}".nix
# Frigate config
if [[ "${service}" == "frigate" ]]; then
local FRIGATE_DEVICES_BLOCK=""
[[ "${TARGET_GRAPHICS_RENDERER}" == "true" ]] && FRIGATE_DEVICES_BLOCK+=" - /dev/dri:/dev/dri\n"
[[ "${TARGET_USB_CORAL}" == "true" ]] && FRIGATE_DEVICES_BLOCK+=" - /dev/bus/usb:/dev/bus/usb\n"
if [[ "${TARGET_PCIE_CORAL}" == "true" ]]; then
FRIGATE_DEVICES_BLOCK+=" - /dev/apex_0:/dev/apex_0\n"
sed -i "s|# ./pcie-coral/coral.nix| ./pcie-coral/coral.nix|" final-nix-config/etc/nixos/configuration.nix
cp -${FILES_COPY_FLAGS} templates/nix-config/pcie-coral/* final-nix-config/etc/nixos/pcie-coral/
fi
if [[ -n "${FRIGATE_DEVICES_BLOCK}" ]]; then
local REPLACEMENT="devices:\n${FRIGATE_DEVICES_BLOCK%\\n}"
sed -i "s|# --- frigate devices --- #|$REPLACEMENT|" final-nix-config/etc/nixos/podman/frigate.nix
fi
# Gitea config
elif [[ "${service}" == "gitea" ]]; then
generate_network "${service}" "1"
generate_db_creds "GITEA"
# Home Assistant config
elif [[ "${service}" == "home-assistant" ]]; then
generate_network "${service}" "1"
if [[ -n "${TARGET_ZIGBEE_DEVICE}" ]]; then
local REPLACEMENT="devices:\n - /dev/serial/by-id/${TARGET_ZIGBEE_DEVICE}:/dev/ttyUSB0"
sed -i "s|# --- home-assistant devices --- #|$REPLACEMENT|" final-nix-config/etc/nixos/podman/home-assistant.nix
fi
export HOME_ASSISTANT_MQTT_USER="$(xkcdpass -d "-" -n 2)"
export HOME_ASSISTANT_MQTT_PASSWORD="$(xkcdpass -d "-")"
mkdir -p final-nix-config/mnt/config/mqtt/
envsubst < templates/podman-config/home-assistant/mosquitto.conf > final-nix-config/mnt/config/mqtt/mosquitto.conf
touch final-nix-config/mnt/config/mqtt/password.txt
chmod 0700 final-nix-config/mnt/config/mqtt/password.txt
mosquitto_passwd -b final-nix-config/mnt/config/mqtt/password.txt "$HOME_ASSISTANT_MQTT_USER" "$HOME_ASSISTANT_MQTT_PASSWORD"
# Immich config
elif [[ "${service}" == "immich" ]]; then
generate_network "${service}" "1"
generate_db_creds "IMMICH"
local IMMICH_DEVICES_BLOCK=""
if [[ "$TARGET_GRAPHICS_RENDERER" == "true" ]]; then
IMMICH_DEVICES_BLOCK+=" - /dev/dri:/dev/dri\n"
fi
if [[ -n "$IMMICH_DEVICES_BLOCK" ]]; then
local REPLACEMENT="devices:\n${IMMICH_DEVICES_BLOCK%\\n}"
sed -i "s|# --- immich devices --- #|$REPLACEMENT|" final-nix-config/etc/nixos/podman/immich.nix
fi
# Nextcloud config
elif [[ "${service}" == "nextcloud" ]]; then
generate_network "${service}" "1"
generate_db_creds "NEXTCLOUD"
export "NEXTCLOUD_REDIS_PASSWORD"="$(xkcdpass -d "-")"
# envsubst < templates/podman-config/traefik/nextcloud.yaml > final-nix-config/mnt/config/traefik/rules/nextcloud.yaml
# Passbolt config
elif [[ "${service}" == "passbolt" ]]; then
generate_network "${service}" "1"
generate_db_creds "PASSBOLT"
envsubst < templates/podman-config/traefik/headers.yaml > final-nix-config/mnt/config/traefik/rules/headers.yaml
envsubst < templates/podman-config/traefik/tls.yaml > final-nix-config/mnt/config/traefik/rules/tls.yaml
# Virtualization config
elif [[ "${service}" == "virtualization" ]]; then
sed -i "s|# virtualisation.libvirtd.enable = true;| virtualisation.libvirtd.enable = true;|" final-nix-config/etc/nixos/configuration.nix
sed -i "s|# programs.virt-manager.enable = true;| programs.virt-manager.enable = true;|" final-nix-config/etc/nixos/configuration.nix
sed -i 's|extraGroups = \[ "wheel" \];|extraGroups = [ "wheel" "libvirtd" ];|' final-nix-config/etc/nixos/configuration.nix
# Other podman containers with no special configuration
else
generate_network "${service}"
fi
done
export PODMAN_NETWORKS
export TRAEFIK_NETWORKS
export TRAEFIK_REF_NETWORKS
}
disks_generation() {
# Boot disk(s)
echo -e "\n\n ✅ Generating disko configuration from templates..."
local TEMPLATE_FILE="templates/nix-config/disks/boot-${#BOOT_DISKS_ID[@]}.nix"
(envsubst < "$TEMPLATE_FILE") > final-nix-config/etc/nixos/disks/disko.nix
SNAPRAID_CONTENT_FILES=""
SNAPRAID_DATA_DISKS=""
SNAPRAID_PARITY_FILES=""
MOUNT_DEPENDENCIES_START=""
MOUNT_DEPENDENCIES_STOP=""
# Striped configuration
if [[ "$CONTENT_DISK_NUMBER" -eq 1 && "$PARITY_DISK_NUMBER" -eq 0 ]]; then
export j=1
export CONTENT_DISK_ID="${DATA_DISKS_ID[0]}"
if [[ "${DATA_DISKS_TYPE[0]}" == "HDD" ]]; then export ALLOW_DISCARDS="false"; else export ALLOW_DISCARDS="true"; fi
(envsubst < "templates/nix-config/disks/content.nix") >> final-nix-config/etc/nixos/disks/disko.nix
sed -i "s|/mnt/content-1|/mnt/data|" final-nix-config/etc/nixos/disks/disko.nix
# SnapRAID configuration
elif [[ ${CONTENT_DISK_NUMBER} -gt 0 && ${PARITY_DISK_NUMBER} -gt 0 ]]; then
sed -i "s|# ./disks/snapraid.nix| ./disks/snapraid.nix|" final-nix-config/etc/nixos/configuration.nix
j=0
for i in $(seq 0 $(($CONTENT_DISK_NUMBER - 1))); do
export j=$((j + 1))
export CONTENT_DISK_ID="${DATA_DISKS_ID[${i}]}"
if [[ "${DATA_DISKS_TYPE[${i}]}" == "HDD" ]]; then export ALLOW_DISCARDS="false"; else export ALLOW_DISCARDS="true"; fi
(envsubst < "templates/nix-config/disks/content.nix") >> final-nix-config/etc/nixos/disks/disko.nix
SNAPRAID_CONTENT_FILES+=" \"/mnt/content-${j}/snapraid.content\""$'\n'
SNAPRAID_DATA_DISKS+=" d${j} = \"/mnt/content-${j}\";"$'\n'
MOUNT_DEPENDENCIES_START+=" \${pkgs.cryptsetup}/bin/cryptsetup open ${CONTENT_DISK_ID}-part1 crypted-content-${j} --key-file /etc/secrets/disks/content-${j}"$'\n'
MOUNT_DEPENDENCIES_START+=" \${pkgs.coreutils}/bin/mkdir -p /mnt/content-${j}"$'\n'
MOUNT_DEPENDENCIES_START+=" \${pkgs.util-linux}/bin/mount /mnt/content-${j}"$'\n'
MOUNT_DEPENDENCIES_STOP+=" \${pkgs.util-linux}/bin/umount /mnt/content-${j}"$'\n'
MOUNT_DEPENDENCIES_STOP+=" \${pkgs.cryptsetup}/bin/cryptsetup close crypted-content-${j}"$'\n'
done
echo -e "\n ✅ Generated $CONTENT_DISK_NUMBER data disk configuration(s)."
j=0
for i in $(seq $CONTENT_DISK_NUMBER $((${#DATA_DISKS_ID[@]} - 1))); do
export j=$((j + 1))
export PARITY_DISK_ID="${DATA_DISKS_ID[${i}]}"
if [[ "${DATA_DISKS_TYPE[${i}]}" == "HDD" ]]; then export ALLOW_DISCARDS="false"; else export ALLOW_DISCARDS="true"; fi
(envsubst < "templates/nix-config/disks/parity.nix") >> final-nix-config/etc/nixos/disks/disko.nix
SNAPRAID_PARITY_FILES+=" \"/mnt/parity-${j}/snapraid.parity\""$'\n'
MOUNT_DEPENDENCIES_START+=" \${pkgs.cryptsetup}/bin/cryptsetup open ${PARITY_DISK_ID}-part1 crypted-parity-${j} --key-file /etc/secrets/disks/parity-${j}"$'\n'
MOUNT_DEPENDENCIES_START+=" \${pkgs.coreutils}/bin/mkdir -p /mnt/parity-${j}"$'\n'
MOUNT_DEPENDENCIES_START+=" \${pkgs.util-linux}/bin/mount /mnt/parity-${j}"$'\n'
MOUNT_DEPENDENCIES_STOP+=" \${pkgs.util-linux}/bin/umount /mnt/parity-${j}"$'\n'
MOUNT_DEPENDENCIES_STOP+=" \${pkgs.cryptsetup}/bin/cryptsetup close crypted-parity-${j}"$'\n'
done
echo -e "\n ✅ Generated $PARITY_DISK_NUMBER parity disk configuration(s)."
[[ ${CONTENT_DISK_NUMBER} -eq 1 && ${PARITY_DISK_NUMBER} -eq 1 ]] && SNAPRAID_CONTENT_FILES+=" \"/mnt/content-0/snapraid.content\""$'\n' && SNAPRAID_DATA_DISKS+=" d0 = \"/mnt/content-0\";"$'\n'
export SNAPRAID_CONTENT_FILES
export SNAPRAID_DATA_DISKS
export SNAPRAID_PARITY_FILES
export MOUNT_DEPENDENCIES_START
export MOUNT_DEPENDENCIES_STOP
fi
envsubst < templates/nix-config/disks/snapraid.nix > final-nix-config/etc/nixos/disks/snapraid.nix
# Close the disko.nix block
cat <<'EOF' >> final-nix-config/etc/nixos/disks/disko.nix
};
};
}
EOF
echo -e "\n ✅ Final disko configuration created."
SPINDOWN_DISKS_ID=()
if [[ "${#DATA_DISKS_ID[@]}" -gt 0 ]]; then
for i in ${!DATA_DISKS_ID[@]}; do
if [[ "${DATA_DISKS_TYPE[${i}]}" == "HDD" ]]; then
SPINDOWN_DISKS_ID+=("${DATA_DISKS_ID[${i}]}")
fi
done
if [[ "${#SPINDOWN_DISKS_ID[@]}" -gt 0 ]]; then
cp -${FILES_COPY_FLAGS} templates/nix-config/disks/spindown.nix final-nix-config/etc/nixos/disks/
local FORMATTED_DISKS=""
for disk in "${SPINDOWN_DISKS_ID[@]}"; do
FORMATTED_DISKS+=" \"$disk\"\n"
done
sed -i "s|DISK_LIST|${FORMATTED_DISKS}|" final-nix-config/etc/nixos/disks/spindown.nix
echo -e "\n ✅ Disk spindown configuration created."
fi
fi
### Config generation <--
export SPINDOWN_DISKS_ID
export PARITY_DISK_NUMBER_LIST
export CONTENT_DISK_NUMBER_LIST
}
server_config_generation() {
echo -e "\n # Server settings" >> ${CONFIGURATION_PATH}
echo -e " time.timeZone = \"${TIMEZONE}\";" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.owner = \"${SERVER_OWNER_NAME}\";" >> ${CONFIGURATION_PATH}
}
network_config_generation() {
echo -e "\n # Network settings" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.networking.ipAddress = \"${HOME_SERVER_IP}\";" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.networking.interface = \"${TARGET_INTERFACE}\";" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.networking.routerIpAddress = \"${NETWORK_ROUTER_IP}\";" >> ${CONFIGURATION_PATH}
}
services_config_generation() {
echo -e "\n # DNS settings" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.dns = \"${SELECTED_DNS_SERVICE}\";" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.services.${SELECTED_DNS_SERVICE} = true;" >> ${CONFIGURATION_PATH}
echo -e "\n # Services settings" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.services.domain = \"${DOMAIN_NAME}\";" >> ${CONFIGURATION_PATH}
for service in "${SELECTED_SERVICES[@]}"; do
echo -e " config.numbus.services.${service}.enable = true;" >> ${CONFIGURATION_PATH}
done
}
mail_config_generation() {
echo -e "\n # Mail settings" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.mail.enable = true;" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.mail.userAddress = \"${SERVER_USER_EMAIL}\";" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.mail.adminAddress = \"${SERVER_ADMIN_EMAIL}\";" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.mail.smtpUsername = \"${SMTP_SERVER_USERNAME}\";" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.mail.smtpPasswordPath = config.sops.secrets.smtpPassword.path;" >> ${CONFIGURATION_PATH}
if [[ "${SMTP_SERVER_HOST}" != "smtp.gmail.com" ]]; then
echo -e " config.numbus.mail.smtpServer = \"${SMTP_SERVER_HOST}\";" >> ${CONFIGURATION_PATH}
if [[ "${SMTP_SERVER_PORT}" != "587" ]]; then
echo -e " config.numbus.mail.smtpPort = ${SMTP_SERVER_PORT};" >> ${CONFIGURATION_PATH}
fi
}
disk_config_generation() {
echo -e "\n # Hardware settings" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.hardware.bootDisksList = [ ${BOOT_DISKS_ID_LIST[@]} ];" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.hardware.dataDisksList = [ ${CONTENT_DISK_NUMBER_LIST[@]} ];" >> ${CONFIGURATION_PATH}
echo -e " config.numbus.hardware.parityDisksList = [ ${PARITY_DISK_NUMBER_LIST[@]} ];" >> ${CONFIGURATION_PATH}
}
keys_generation() {
### --> Generate disk keys
for i in $(seq 1 "${#BOOT_DISKS_ID[@]}"); do
for i in $(seq 1 "${#BOOT_DISKS_ID_LIST[@]}"); do
PASS="$(xkcdpass)"
echo -n "$PASS" > "final-nix-config/etc/secrets/disks/boot-${i}"
chmod 600 "final-nix-config/etc/secrets/disks/boot-${i}"
ssh_to_host 'bash -s' << EOF
echo "$REMOTE_PASS" | sudo -S mkdir -p /etc/secrets/disks/
echo "$REMOTE_PASS" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/boot-${i}"
echo "$LIVE_TARGET_PASSWD" | sudo -S mkdir -p /etc/secrets/disks/
echo "$LIVE_TARGET_PASSWD" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/boot-${i}"
echo "$LIVE_TARGET_PASSWD" | sudo -S chmod 600 /etc/secrets/disks/boot-${i}"
EOF
done
for i in $(seq 1 "$CONTENT_DISK_NUMBER"); do
@@ -638,7 +442,8 @@ EOF
echo -n "$PASS" > "final-nix-config/etc/secrets/disks/content-${i}"
chmod 600 "final-nix-config/etc/secrets/disks/content-${i}"
ssh_to_host 'bash -s' << EOF
echo "$REMOTE_PASS" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/content-${i}"
echo "$LIVE_TARGET_PASSWD" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/content-${i}"
echo "$LIVE_TARGET_PASSWD" | sudo -S chmod 600 /etc/secrets/disks/content-${i}"
EOF
done
for i in $(seq 1 "$PARITY_DISK_NUMBER"); do
@@ -646,10 +451,10 @@ EOF
echo -n "$PASS" > "final-nix-config/etc/secrets/disks/parity-${i}"
chmod 600 "final-nix-config/etc/secrets/disks/parity-${i}"
ssh_to_host 'bash -s' << EOF
echo "$REMOTE_PASS" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/parity-${i}"
echo "$LIVE_TARGET_PASSWD" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/parity-${i}"
echo "$LIVE_TARGET_PASSWD" | sudo -S chmod 600 /etc/secrets/disks/parity-${i}"
EOF
done
### Generate disk keys <--
echo -e "\n ✅ Generating sops-nix keys..."
ssh-to-age -private-key -i final-nix-config/home/numbus-admin/.ssh/id_ed25519 > final-nix-config/var/lib/sops-nix/key.txt
@@ -666,41 +471,15 @@ EOF
--output final-nix-config/etc/nixos/secrets/secrets.yaml
}
nix_generation() {
echo -e "\n ✅ Copying the configuration to the new machine..."
cp -${FILES_COPY_FLAGS} templates/nix-config/flake.nix final-nix-config/etc/nixos/
cp -${FILES_COPY_FLAGS} templates/nix-config/misc/* final-nix-config/etc/nixos/misc/
echo "${SERVER_OWNER_NAME:-User}" > final-nix-config/etc/numbus-server/owner
echo -e "\n ✅ Writing correct ips to configuration.nix..."
sed -i "s|HOME_SERVER_IP|${HOME_SERVER_IP}|g" final-nix-config/etc/nixos/misc/networking.nix
sed -i "s|HOME_ROUTER_IP|${HOME_ROUTER_IP}|g" final-nix-config/etc/nixos/misc/networking.nix
sed -i "s|TARGET_INTERFACE|${TARGET_INTERFACE}|g" final-nix-config/etc/nixos/misc/networking.nix
sed -i "s|DOMAIN_NAME|${DOMAIN_NAME}|" final-nix-config/etc/nixos/misc/mail.nix
sed -i "s|EMAIL_ADDRESS|${EMAIL_ADDRESS}|" final-nix-config/etc/nixos/misc/mail.nix
sed -i "s|SENDER_MAIL_DOMAIN|${SENDER_EMAIL_DOMAIN}|" final-nix-config/etc/nixos/misc/mail.nix
sed -i "s|SENDER_MAIL_ADDRESS|${SENDER_EMAIL_ADDRESS}|" final-nix-config/etc/nixos/misc/mail.nix
sed -i "s*PODMAN_NETWORKS*${PODMAN_NETWORKS//$'\n'/\\n}*" final-nix-config/etc/nixos/misc/activation.nix
sed -i "s|TRAEFIK_NETWORKS|${TRAEFIK_NETWORKS//$'\n'/\\n}|" final-nix-config/etc/nixos/podman/traefik.nix
sed -i "s|TRAEFIK_REF_NETWORKS|${TRAEFIK_REF_NETWORKS//$'\n'/\\n}|" final-nix-config/etc/nixos/podman/traefik.nix
if [[ "${TARGET_TPM}" == "true" && ${TARGET_TPM_VERSION} -eq 2 ]]; then
sed -i "s|# ./disks/pcr-check.nix| ./disks/pcr-check.nix|" final-nix-config/etc/nixos/configuration.nix
sed -i "s|# boot.initrd.systemd.tpm2.enable = true;| boot.initrd.systemd.tpm2.enable = true;|" final-nix-config/etc/nixos/configuration.nix
sed -i "s|# systemIdentity.enable = true;| systemIdentity.enable = true;|" final-nix-config/etc/nixos/configuration.nix
cp -${FILES_COPY_FLAGS} templates/nix-config/disks/pcr-check.nix final-nix-config/etc/nixos/disks/
fi
}
sum_up() {
### --> Disk selection recap
DISK_RECAP_CONTENT=$(cat << EOF
### Disk Configuration Summary
Please review the selected disk layout before proceeding.
**Boot Disks (${#BOOT_DISKS_ID[@]}):**
* **Boot 1:** \`${BOOT_DISKS_ID[0]}\`
$( [[ -n "${BOOT_DISKS_ID[1]:-}" ]] && echo "* **Boot 2:** \`${BOOT_DISKS_ID[1]}\`" || echo "* **Boot 2:** *Not configured*" )
**Boot Disks (${#BOOT_DISKS_ID_LIST[@]}):**
* **Boot 1:** \`${BOOT_DISKS_ID_LIST[0]}\`
$( [[ -n "${BOOT_DISKS_ID_LIST[1]:-}" ]] && echo "* **Boot 2:** \`${BOOT_DISKS_ID_LIST[1]}\`" || echo "* **Boot 2:** *Not configured*" )
**Data Disks ($CONTENT_DISK_NUMBER):**
$( j=1 && for i in $(seq 0 $(($CONTENT_DISK_NUMBER - 1))); do echo "* **Data ${j}:** \`${DATA_DISKS_ID[${i}]}\`" && j=$((j + 1)); done )
@@ -714,40 +493,6 @@ EOF
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "$(gum format <<< "${DISK_RECAP_CONTENT}")"
gum confirm "Proceed with this disk configuration?" || { echo -e "\n\n ❌ Aborting as requested."; exit 1; }
### Disk selection recap <--
### Keys recap <--
KEYS_RECAP_CONTENT=$(cat << EOF
### Generated Secrets Summary
Please save these secrets in a secure location (e.g., a password manager).
**Service Credentials:**
$( [[ -n ${HOME_ASSISTANT_MQTT_USER:-} ]] && echo "* **Home Assistant MQTT User:** \`${HOME_ASSISTANT_MQTT_USER}\`" && \
echo "* **Home Assistant MQTT Password:** \`$HOME_ASSISTANT_MQTT_PASSWORD\`" \
|| echo "* **Home assistant:** *Not configured*" )
$( [[ -n ${FTLCONF_WEBSERVER_PASSWORD:-} ]] && echo "* **Pi-hole Web Password:** \`${FTLCONF_WEBSERVER_PASSWORD}\`" \
|| echo "* **Pi-hole:** *Not configured*" )
$( [[ -n ${PASSBOLT_DB_NAME:-} ]] && echo "* **Passbolt DB Name:** \`${PASSBOLT_DB_NAME}\`" && \
echo "* **Passbolt DB User:** \`${PASSBOLT_DB_USERNAME}\`" && echo "* **Passbolt DB Password:** \`${PASSBOLT_DB_PASSWORD}\`" \
|| echo "* **Passbolt:** *Not configured*" )
$( [[ -n ${IMMICH_DB_NAME:-} ]] && echo "* **Immich DB Name:** \`${IMMICH_DB_NAME}\`" && \
echo "* **Immich DB User:** \`${IMMICH_DB_USERNAME}\`" && echo "* **Immich DB Password:** \`${IMMICH_DB_PASSWORD}\`" \
|| echo "* **Immich:** *Not configured*" )
$( [[ -n ${GITEA_DB_NAME:-} ]] && echo "* **Gitea DB Name:** \`${GITEA_DB_NAME}\`" && \
echo "* **Gitea DB User:** \`${GITEA_DB_USERNAME}\`" && echo "* **Gitea DB Password:** \`${GITEA_DB_PASSWORD}\`" \
|| echo "* **Gitea:** *Not configured*" )
**Disk Encryption Keys:**
$( for i in $(seq 1 "${#BOOT_DISKS_ID[@]}"); do f="final-nix-config/etc/secrets/disks/boot-${i}"; [[ -f "$f" ]] && echo "* **Boot Disk $i Key:** \`$(cat "$f")\`"; done )
$( for i in $(seq 1 "$CONTENT_DISK_NUMBER"); do f="final-nix-config/etc/secrets/disks/content-${i}"; [[ -f "$f" ]] && echo "* **Content Disk $i Key:** \`$(cat "$f")\`"; done )
$( for i in $(seq 1 "$PARITY_DISK_NUMBER"); do f="final-nix-config/etc/secrets/disks/parity-${i}"; [[ -f "$f" ]] && echo "* **Parity Disk $i Key:** \`$(cat "$f")\`"; done )
EOF
)
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "$(gum format <<< "${KEYS_RECAP_CONTENT}")"
gum confirm "Do you want to deploy NixOS on the target host?" || { echo -e "\n\n ❌ Aborting as requested"; exit 1; }
### Keys recap <--
}
cloudflare_dns_setup() {
@@ -761,7 +506,7 @@ cloudflare_dns_setup() {
local CREATION_STATUS
CREATION_STATUS=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
-H "Authorization: Bearer ${CF_DNS_API_TOKEN}" \
-H "Authorization: Bearer ${CLOUDFLARE_DNS_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"${SUBDOMAIN}\",\"content\":\"${HOME_SERVER_IP}\",\"ttl\":1,\"proxied\":false}" | jq -r '.success')
@@ -784,12 +529,12 @@ cloudflare_dns_setup() {
|| { echo -e "\n ⚠️ DNS records for ${SUBDOMAIN} will not be updated"; return 0; }
RECORD_IDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=${SUBDOMAIN}&type=A" \
-H "Authorization: Bearer ${CF_DNS_API_TOKEN}" \
-H "Authorization: Bearer ${CLOUDFLARE_DNS_API_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.result[].id')
for id in ${RECORD_IDS}; do
curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${id}" \
-H "Authorization: Bearer ${CF_DNS_API_TOKEN}" \
-H "Authorization: Bearer ${CLOUDFLARE_DNS_API_TOKEN}" \
-H "Content-Type: application/json" > /dev/null 2>&1
done
@@ -812,7 +557,7 @@ cloudflare_dns_setup() {
# Get Zone ID
ZONE_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${DOMAIN_NAME}" \
-H "Authorization: Bearer ${CF_DNS_API_TOKEN}" \
-H "Authorization: Bearer ${CLOUDFLARE_DNS_API_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.result[0].id')
if [[ "${ZONE_ID}" == "null" || -z "${ZONE_ID}" ]]; then
@@ -823,7 +568,7 @@ cloudflare_dns_setup() {
# Check for existing records and create them if non-existent
for service_domain in "${SELECTED_SERVICES_DNS[@]}"; do
DNS_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=${service_domain}&type=A" \
-H "Authorization: Bearer ${CF_DNS_API_TOKEN}" \
-H "Authorization: Bearer ${CLOUDFLARE_DNS_API_TOKEN}" \
-H "Content-Type: application/json")
RECORD_COUNT=$(echo "${DNS_RECORDS}" | jq '.result | length')
@@ -856,7 +601,7 @@ export_configuration() {
echo -e "\n# SERVER SETTINGS" >> $CONFIG_EXPORT_FILE
echo "export SERVER_OWNER_NAME=\"${SERVER_OWNER_NAME:-User}\"" >> $CONFIG_EXPORT_FILE
echo -e "\n# DISK SETTINGS" >> $CONFIG_EXPORT_FILE
echo "export BOOT_DISKS_ID=\"(${BOOT_DISKS_ID[@]})\"" >> $CONFIG_EXPORT_FILE
echo "export BOOT_DISKS_ID_LIST=\"(${BOOT_DISKS_ID_LIST[@]})\"" >> $CONFIG_EXPORT_FILE
echo "export DATA_DISKS_ID=\"(${DATA_DISKS_ID[@]})\"" >> $CONFIG_EXPORT_FILE
echo "export DATA_DISKS_TYPE=\"(${DATA_DISKS_TYPE[@]})\"" >> $CONFIG_EXPORT_FILE
echo "export SPINDOWN_DISKS_ID=\"(${SPINDOWN_DISKS_ID[@]})\"" >> $CONFIG_EXPORT_FILE
@@ -880,7 +625,7 @@ deploy() {
--flake ./final-nix-config/etc/nixos#numbus-server \
--extra-files final-nix-config \
--chown "/home/numbus-admin/" 1000:1000 \
--target-host ${TARGET_USER}@${TARGET_HOST}
--target-host ${TARGET_USER}@${LIVE_TARGET_IP}
echo -e "\n\n✅ Installation successfull !"
sleep 1
@@ -888,8 +633,8 @@ deploy() {
postrun_action() {
TARGET_USER="numbus-admin"
TARGET_HOST="${HOME_SERVER_IP}"
REMOTE_PASS="changeMe!"
LIVE_TARGET_IP="${HOME_SERVER_IP}"
LIVE_TARGET_PASSWD="changeMe!"
echo -e "\n\n Now the remote machine will reboot. You will need to input the boot disk(s) passphrase.
This will be the only time you will have to do so, it will be automatic in the future."
@@ -926,7 +671,7 @@ postrun_action() {
Do you want to enable automatic disk decryption on boot ?"
if gum confirm "➡️ I understand, 'yes' to proceed."; then
sshpass -p "${REMOTE_PASS}" ssh -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${TARGET_HOST}" 'bash -s' << EOF
sshpass -p "${LIVE_TARGET_PASSWD}" ssh -i "final-nix-config/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[@]})
@@ -943,14 +688,14 @@ for i in \${!BOOT_DISKS_NAME[@]}; do
DISK_PATH="/dev/\${BOOT_DISKS_NAME[\${i}]}2"
fi
[[ "\${DEBUG}" == "true" ]] && echo "Issuing enroll command for disk \${DISK_PATH}..."
echo ${REMOTE_PASS} | sudo -S systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 --unlock-key-file=/etc/secrets/disks/boot-\${j} \${DISK_PATH}
echo ${LIVE_TARGET_PASSWD} | sudo -S systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 --unlock-key-file=/etc/secrets/disks/boot-\${j} \${DISK_PATH}
j=\$((j + 1))
done
echo "Getting PCRS 15 hash..."
PCR_HASH=\$(echo ${REMOTE_PASS} | sudo -S systemd-analyze pcrs 15 --json=short)
PCR_HASH=\$(echo ${LIVE_TARGET_PASSWD} | sudo -S systemd-analyze pcrs 15 --json=short)
echo ${REMOTE_PASS} | sudo -S sed -i "s|PCR_HASH|\${PCR_HASH}|" /etc/nixos/configuration.nix
echo ${LIVE_TARGET_PASSWD} | sudo -S sed -i "s|PCR_HASH|\${PCR_HASH}|" /etc/nixos/configuration.nix
EOF
else
echo "Skipping TPM configuration."
@@ -967,13 +712,13 @@ securely on a hidden sheet of paper or add it to your password manager (locally
gum confirm "➡️ I understand, 'yes' to proceed." || { echo -e "\n\n❌ Aborting as requested."; exit 1; }
echo $REMOTE_PASS | sudo -S passwd numbus-admin
echo $LIVE_TARGET_PASSWD | sudo -S passwd numbus-admin
}
nix_update() {
echo -e "\n\n🔄 Updating NixOS on the remote server..."
nixos-rebuild --target-host numbus-admin@${TARGET_HOST} \
nixos-rebuild --target-host numbus-admin@${LIVE_TARGET_IP} \
--use-remote-sudo switch --flake final-nix-config/etc/nixos#numbus-server
}