diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..b64acd0 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index d16adf0..20a22f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ agents/ -ai-production/ extra-files/ -test* -deploy.conf \ No newline at end of file +final-nix-config/ +test* \ No newline at end of file diff --git a/config-files/examples/deploy.example.conf b/config-files/examples/deploy.example.conf deleted file mode 100644 index 664d0fc..0000000 --- a/config-files/examples/deploy.example.conf +++ /dev/null @@ -1,16 +0,0 @@ -#TARGET SETTINGS -TARGET_HOST="192.168.1.10" -SSH_PUBLIC_KEY="ssh-ed25519 AAAAoefzefpoipoeCEZJCPEACPAcjapjcpajepcjAPJECJPEJAPJAZ yours@yourdomain.com" -# TRAEFIK SETTINGS -DOMAIN_NAME="yourdomain.com" -EMAIL_ADDRESS="your-mail@yourdomain.com" -CF_DNS_API_TOKEN="yourToken" -#SMTP SETTINGS -SENDER_EMAIL_ADDRESS="youraddress@gmail.com" -SENDER_EMAIL_ADDRESS_PASSWORD="emrp raps vzoi vnoe" -SENDER_EMAIL_DOMAIN="smtp.yourdomain.com" -SENDER_EMAIL_PORT="587" -#NETWORK SETTINGS -HOME_ROUTER_SUBNET="192.168.1.0/24" -HOME_ROUTER_IP="192.168.1.1" -HOME_SERVER_IP="192.168.1.5" \ No newline at end of file diff --git a/config-files/scripts/numbus-server.sh b/config-files/scripts/numbus-server.sh deleted file mode 100644 index cadc12d..0000000 --- a/config-files/scripts/numbus-server.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env nix-shell -#!nix-shell -i bash -p gum openssl sops ssh-to-age age sshpass envsubst pciutils usbutils mosquitto - -gum confirm "Welcome to the Numbus-Server administration interface. Would you like to change a setting ?" \ -|| { echo " ❌ Aborting as requested."; exit 1; } - -ACTIONS_LIST=("Update networking settings" "Update backup settings" "Add/Remove services" \ -"Add/Remove/Replace boot disk" "Add/Remove/Replace data disk(s)" "Add/Remove devices (i.e. coral TPUs)" \ -"Test email notifications" "Check disk(s) health" "Export current configuration to a git server") \ No newline at end of file diff --git a/config-files/sops-nix/secrets.yaml b/config-files/sops-nix/secrets.yaml deleted file mode 100644 index e71a062..0000000 --- a/config-files/sops-nix/secrets.yaml +++ /dev/null @@ -1,66 +0,0 @@ -ssh_public_keys: $SSH_PUBLIC_KEY -sender_email_address_password: $SENDER_EMAIL_ADDRESS_PASSWORD - -docker: - nextcloud: | - DOMAIN_NAME=$DOMAIN_NAME - NEXTCLOUD_ENABLE_DRI_DEVICE=$TARGET_GRAPHICS - frigate: | - DOMAIN_NAME=$DOMAIN_NAME - FRIGATE_MQTT_USER=$HOME_ASSISTANT_MQTT_USER - FRIGATE_MQTT_PASSWORD=$HOME_ASSISTANT_MQTT_PASSWORD - traefik: | - DOMAIN_NAME=$DOMAIN_NAME - CF_DNS_API_TOKEN=$CF_DNS_API_TOKEN - hass: | - DOMAIN_NAME=$DOMAIN_NAME - HOME_ASSISTANT_MQTT_USER=$HOME_ASSISTANT_MQTT_USER - HOME_ASSISTANT_MQTT_PASSWORD=$HOME_ASSISTANT_MQTT_PASSWORD - passbolt: | - DOMAIN_NAME=$DOMAIN_NAME - TZ=Europe/Paris - PASSBOLT_MYSQL_DATABASE=$PASSBOLT_DB_NAME - PASSBOLT_MYSQL_USER=$PASSBOLT_DB_USERNAME - PASSBOLT_MYSQL_PASSWORD=$PASSBOLT_DB_PASSWORD - SENDER_EMAIL_ADDRESS=$SENDER_EMAIL_ADDRESS - SENDER_EMAIL_ADDRESS_PASSWORD=$SENDER_EMAIL_ADDRESS_PASSWORD - SENDER_EMAIL_DOMAIN=$SENDER_EMAIL_DOMAIN - SENDER_EMAIL_PORT=$SENDER_EMAIL_PORT - EMAIL_ADDRESS=$EMAIL_ADDRESS - pihole: | - DOMAIN_NAME=$DOMAIN_NAME - TZ=Europe/Paris - HOME_ROUTER_SUBNET=$HOME_ROUTER_SUBNET - HOME_ROUTER_IP=$HOME_ROUTER_IP - HOME_SERVER_IP=$HOME_SERVER_IP - FTLCONF_webserver_api_password=$FTLCONF_WEBSERVER_PASSWORD - immich: | - DOMAIN_NAME=$DOMAIN_NAME - TZ=Europe/Paris - UPLOAD_LOCATION=/mnt/data-storage/docker-data/immich - IMMICH_VERSION=release - IMMICH_TRUSTED_PROXIES=172.16.50.253 - REDIS_HOSTNAME=immich-redis - DB_HOSTNAME=immich-database - DB_DATABASE_NAME=$IMMICH_DB_NAME - DB_USERNAME=$IMMICH_DB_USERNAME - DB_PASSWORD=$IMMICH_DB_PASSWORD - DB_DATA_LOCATION=/mnt/config-storage/docker-data/immich/database - gitea: | - DOMAIN_NAME=$DOMAIN_NAME - POSTGRES_HOST=gitea-database - POSTGRES_PORT=5432 - DB_NAME=$GITEA_DB_NAME - DB_USERNAME=$GITEA_DB_USERNAME - DB_PASSWORD=$GITEA_DB_PASSWORD - -disks: - content-disk-1: $CONTENT_DISK_1_KEY - content-disk-2: $CONTENT_DISK_2_KEY - content-disk-3: $CONTENT_DISK_3_KEY - content-disk-4: $CONTENT_DISK_4_KEY - content-disk-5: $CONTENT_DISK_5_KEY - content-disk-6: $CONTENT_DISK_6_KEY - parity-disk-1: $PARITY_DISK_1_KEY - parity-disk-2: $PARITY_DISK_2_KEY - parity-disk-3: $PARITY_DISK_3_KEY diff --git a/deploy.conf b/deploy.conf new file mode 100644 index 0000000..bf51106 --- /dev/null +++ b/deploy.conf @@ -0,0 +1,16 @@ +#TARGET SETTINGS +TARGET_HOST="192.168.1.10" +TARGET_SSH_PUBLIC_KEY="ssh-ed25519 AAAAoefzefpoipoeCEZJCPEACPAcjapjcpajepcjAPJECJPEJAPJAZ yours@yourdomain.com" +# TRAEFIK SETTINGS +DOMAIN_NAME="yourdomain.com" +DOMAIN_EMAIL_ADDRESS="your-mail@yourdomain.com" +DOMAIN_CF_DNS_API_TOKEN="yourToken" +#SMTP SETTINGS +SENDER_EMAIL_ADDRESS="youraddress@gmail.com" +SENDER_EMAIL_ADDRESS_PASSWORD="emrp raps vzoi vnoe" +SENDER_EMAIL_DOMAIN="smtp.yourdomain.com" +SENDER_EMAIL_PORT="587" +#NETWORK SETTINGS +NETWORK_HOME_ROUTER_SUBNET="192.168.1.0/24" +NETWORK_HOME_ROUTER_IP="192.168.1.1" +NETWORK_HOME_SERVER_IP="192.168.1.5" \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index dab49f9..b72fa51 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,266 +1,321 @@ #!/usr/bin/env nix-shell -#!nix-shell -i bash -p gum xkcdpass sops ssh-to-age age sshpass envsubst pciutils usbutils mosquitto +#!nix-shell -i bash -p gum fastfetch xkcdpass sops ssh-to-age age sshpass envsubst pciutils usbutils mosquitto -NECESSARY_VARIABLES_LIST=("TARGET_HOST" "SSH_PUBLIC_KEY" "DOMAIN_NAME" "EMAIL_ADDRESS" \ -"CF_DNS_API_TOKEN" "SENDER_EMAIL_ADDRESS" "SENDER_EMAIL_ADDRESS_PASSWORD" \ + + +### --> Default settings +export GUM_SPIN_SPINNER="minidot" +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" "HOME_ROUTER_SUBNET" "HOME_ROUTER_IP" \ "HOME_SERVER_IP") +### Default settings <-- -INSTALLED_REMOTE_PASS="changeMe!" -necessary_credentials() { - #TARGET SETTINGS - echo -e "\n\n ➡️ Please provide the IP address of the target host :" - export TARGET_HOST="$(gum input --placeholder "192.168.1.100")" - echo -e "\n\n ➡️ Please provide the public SSH key of an authorized device :" - export SSH_PUBLIC_KEY="$(gum input --placeholder "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGhcYDmjMo5YApLkk/3P3HZCnOSzm0uYewNAbxL8Fci8 user@your-pc")" - # TRAEFIK SETTINGS - echo -e "\n\n ➡️ Please provide the domain name (FQDN) your home server will use :" - export DOMAIN_NAME="$(gum input --placeholder "yourdomain.com")" - echo -e "\n\n ➡️ Please provide a valid email address (will be used for ACME, and your services) :" - export EMAIL_ADDRESS="$(gum input --placeholder "myemail@gmail.com")" - echo -e "\n\n ➡️ Please provide a cloudflare API token with DNS zone permission :" - export CF_DNS_API_TOKEN="$(gum input --placeholder "bA7hdvCOuXGytlNKohi3ZGtlVpf5CHpLuCMiJrE")" +user_input() { + local VAR_NAME="${1}" + local HEADER="${2}" + local PLACEHOLDER="${3}" + local REGEX="${4}" + local ERROR_MSG="${5}" + local SENSITIVE="${6:-false}" - # SMTP SETTINGS - echo -e "\n\n ➡️ Some services will be able to send you emails. For that you need an email that supports sending emails.\n Please provide a valid sender email address :" - export SENDER_EMAIL_ADDRESS="$(gum input --placeholder "myemail@gmail.com")" - echo -e "\n\n ➡️ Please provide the password of this email address :" - export SENDER_EMAIL_ADDRESS_PASSWORD="$(gum input --placeholder "abcd efgh ijkl mnop")" - echo -e "\n\n ➡️ Please provide the SMTP server endpoint :" - export SENDER_EMAIL_DOMAIN="$(gum input --placeholder "smtp.gmail.com")" - echo -e "\n\n ➡️ Please provide the smtp TLS port (for gmail : 587) :" - export SENDER_EMAIL_PORT="$(gum input --placeholder "587")" + while true; do + [[ "$SENSITIVE" -eq "true" ]] && INPUT_VALUE=$(gum input --placeholder "${PLACEHOLDER}" --header "${HEADER}") + [[ "$SENSITIVE" -eq "false" ]] && INPUT_VALUE=$(gum input --password --placeholder "${PLACEHOLDER}" --header "${HEADER}") - # NETWORK SETTINGS - echo -e "\n\n ➡️ Please provide your home network subnet :" - export HOME_ROUTER_SUBNET="$(gum input --placeholder "192.168.1.1/24")" - echo -e "\n\n ➡️ Please provide the ip address of your router :" - export HOME_ROUTER_IP="$(gum input --placeholder "192.168.1.1")" - echo -e "\n\n ➡️ 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.) :" - export HOME_SERVER_IP="$(gum input --placeholder "192.168.1.5")" + if [[ -z "${INPUT_VALUE}" ]]; then + echo "❌ Error: Input cannot be empty. Please provide the necessary information." + continue + fi + + if [[ -n "${REGEX}" ]]; then + if [[ ! "${INPUT_VALUE}" =~ ${REGEX} ]]; then + echo "❌ Error: ${ERROR_MSG}" + continue + fi + fi + + export "${VAR_NAME}"="${INPUT_VALUE}" + break + done } -necessary_credentials_with_config() { - echo -e "\n\n ➡️ Please choose your configuration file :" - CONFIG_PATH="$(gum file)" - source "$CONFIG_PATH" - MISSING=0 + +necessary_credentials() { + # Regex Definitions + local IP_REGEX='^([0-9]{1,3}\.){3}[0-9]{1,3}$' + local SUBNET_REGEX='^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$' + local DOMAIN_REGEX='^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$' + local EMAIL_REGEX='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' + local PORT_REGEX='^[0-9]{1,5}$' + local SSH_KEY_REGEX='^ssh-[a-z0-9]+ [A-Za-z0-9+/]+.*' + + #TARGET SETTINGS + user_input "TARGET_HOST" "➡️ Please provide the IP address of the target host :" "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 :" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGhcYDmjMo5YApLkk/3P3HZCnOSzm0uYewNAbxL8Fci8 user@your-pc" "${SSH_KEY_REGEX}" "Invalid SSH key format (must start with ssh-...)." + + # TRAEFIK SETTINGS + user_input "DOMAIN_NAME" "➡️ Please provide the domain name (FQDN) your home server will use :" "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) :" "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 :" "bA7hdvCOuXGytlNKohi3ZGtlVpf5CHpLuCMiJrE" "" "" + + # SMTP SETTINGS + 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)." + user_input "SENDER_EMAIL_ADDRESS" "➡️ Please provide a valid sender email address :" "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" "" "" + user_input "SENDER_EMAIL_DOMAIN" "➡️ Please provide the SMTP server endpoint :" "smtp.gmail.com" "${DOMAIN_REGEX}" "Invalid domain name format." + user_input "SENDER_EMAIL_PORT" "➡️ Please provide the smtp TLS port (for gmail : 587) :" "587" "${PORT_REGEX}" "Invalid port number." + + # NETWORK SETTINGS + user_input "HOME_ROUTER_SUBNET" "➡️ Please provide your home network subnet :" "192.168.1.1/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 :" "192.168.1.1" "${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.) :" "192.168.1.5" "${IP_REGEX}" "Invalid IP address format." +} + + + +necessary_credentials_with_config() { + echo -e "\n\n➡️ Please choose your configuration file :" + local CONFIG_PATH="$(gum file)" + + source "${CONFIG_PATH}" + local MISSING=0 for VAR in "${NECESSARY_VARIABLES_LIST[@]}"; do - if [[ -v $VAR && -n ${!VAR} ]]; then - echo -e "\n ✅ $VAR imported successfully from the config file" - export $VAR + if [[ -v "${VAR}" && -n "${!VAR}" ]]; then + gum spin --title "✅ "${VAR}" imported successfully from the config file" -- sleep 0.5 else - echo "\n ❌ $VAR is missing or empty" + gum spin --title "❌ "${VAR}" is missing or empty" -- sleep 0.5 MISSING=1 fi done - if [[ "$MISSING" -eq "1" ]]; then + if [[ "${MISSING}" -eq "1" ]]; then + echo -e "\n❌ Please check your configuration file to include all necessary variables" exit 1 fi } + + +generate_folder_tree() { + mkdir -p final-nix-config/ + mkdir -p final-nix-config/home/ + mkdir -p final-nix-config/home/numbus-admin/ + mkdir -p final-nix-config/home/numbus-admin/.ssh/ + + 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/ + mkdir -p final-nix-config/etc/nixos/ + mkdir -p final-nix-config/etc/secrets/ + mkdir -p final-nix-config/etc/numbus-server/ + mkdir -p final-nix-config/etc/nixos/misc/ + 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 +} + + + setup_ssh() { - echo -e "\n\n ✅ Generating new SSH for numbus-admin..." - mkdir -p extra-files/home/numbus-admin/.ssh/ - chmod 700 extra-files/home/numbus-admin/.ssh/ - ssh-keygen -t "ed25519" -C "numbus-admin@numbus-server" -f "extra-files/home/numbus-admin/.ssh/id_ed25519" -N "" -q + echo -e "\n\n✅ Generating new SSH key for numbus-admin..." + 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 - LIVE_REMOTE_PASS=$(gum input --password --placeholder "Enter password for 'nixos@$TARGET_HOST'") - if [ -z "$LIVE_REMOTE_PASS" ]; then - echo " ❌ Password is required to proceed. Aborting." - exit 1 - fi - echo -e "\n\n ➡️ Copying SSH key to target host 'nixos@$TARGET_HOST'..." - if sshpass -p "$LIVE_REMOTE_PASS" ssh-copy-id -o StrictHostKeyChecking=no -i "extra-files/home/numbus-admin/.ssh/id_ed25519" "nixos@$TARGET_HOST"; then - echo " ✅ SSH key copied successfully." + 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 "✅ SSH key copied successfully." else - echo " ❌ Failed to copy SSH key. Please check the host IP and password." + echo "❌ Failed to copy SSH key. Please check the host IP and password." exit 1 fi - export LIVE_REMOTE_PASS } -ssh_to_live_host() { - ARG="$1" - ssh -i "extra-files/home/numbus-admin/.ssh/id_ed25519" "nixos@$TARGET_HOST" $ARG -} -ssh_to_installed_host() { - ARG="$1" - ssh -i "extra-files/home/numbus-admin/.ssh/id_ed25519" "numbus-admin@$TARGET_HOST" $ARG + +ssh_to_host() { + local COMMAND="${1}" + ssh -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${TARGET_HOST}" "${COMMAND}" } hardware_detection() { - echo -e "\n\n 🔎 Detecting graphics card on target host..." - VGA_INFO=$(ssh_to_live_host "lspci -nn | grep -i 'vga'") - if echo "$VGA_INFO" | grep -iq "intel" 2>/dev/null; then - echo -e " ✅ Intel graphics card detected." - export TARGET_GRAPHICS="true" - elif echo "$VGA_INFO" | grep -iq "amd" 2>/dev/null; then - echo -e " ✅ AMD graphics card detected." - export TARGET_GRAPHICS="true" - elif echo "$VGA_INFO" | grep -iq "nvidia" 2>/dev/null; then - echo -e " ✅ NVIDIA graphics card detected." - export TARGET_GRAPHICS="true" + ### --> Get hardware information + local TMPFILE="/tmp/nixos-installation-hardware-detection-temp-file" + + ssh_to_host 'bash -s' << SSHEND +for brand in Intel AMD NVIDIA; do + if [[ lspci -nn | grep -i "vga" | grep -iq "\${brand}" ]]; then + TARGET_GRAPHICS="true" + TARGET_GRAPHICS_BRAND+=("\${brand}") else - echo -e " ⚠️ No dedicated graphics card detected." - export TARGET_GRAPHICS="false" + TARGET_GRAPHICS="false" fi - echo -e "\n\n 🔎 Detecting transconding acceleration on target host..." - if ssh_to_live_host "ls /dev/dri/ | grep -iq 'renderD128'" 2>/dev/null; then - echo -e " ✅ Transcoding capable card detected." - TARGET_GRAPHICS_RENDERER="true" +done + +[[ ls /dev/dri/ | grep -iq "renderD128" ]] && TARGET_GRAPHICS_RENDERER="true" || TARGET_GRAPHICS_RENDERER="false" +[[ lsusb | grep -iq "google" ]] && TARGET_USB_CORAL="true" || TARGET_USB_CORAL="false" +[[ lspci -nn | grep -iq "089a" ]] && TARGET_PCIE_CORAL="true" || TARGET_PCIE_CORAL="false" +[[ ls /dev/serial/by-id/ | grep -i "zigbee" ]] && TARGET_ZIGBEE_DEVICE=\$(ls /dev/serial/by-id/ | grep -i "zigbee" | head -n 1) || TARGET_ZIGBEE_DEVICE="" + +for var in TARGET_GRAPHICS TARGET_GRAPHICS_BRAND TARGET_GRAPHICS_RENDERER TARGET_USB_CORAL TARGET_PCIE_CORAL TARGET_ZIGBEE_DEVICE; do + echo "export \${var}=\${!var}" >> "${TMPFILE}" +done +SSHEND + ### Get hardware information <-- + + scp -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${TARGET_HOST}":"${TMPFILE}" "${TMPFILE}" &> /dev/null + source "${TMPFILE}" && rm "${TMPFILE}" + + ### --> Generate hardware-configuration.nix + echo -e "\n\n 🔎 Generating hardware-configuration.nix from target host..." + if ssh_to_host "sudo nixos-generate-config --no-filesystems --show-hardware-config" > final-nix-config/etc/nixos/hardware-configuration.nix; then + echo -e "✅ Hardware configuration generated" else - echo -e " ⚠️ No transcoding capable card detected." - TARGET_GRAPHICS_RENDERER="false" - fi - echo -e "\n\n 🔎 Detecting USB Google Coral TPU on target host..." - if ssh_to_live_host "lsusb | grep -iq 'google'" 2>/dev/null; then - echo -e " ✅ USB Google Coral TPU detected." - TARGET_USB_CORAL="true" - else - echo -e " ⚠️ No USB Google Coral TPU detected." - TARGET_USB_CORAL="false" - fi - echo -e "\n\n 🔎 Detecting Zigbee coordinator on target host..." - if ssh_to_live_host "ls /dev/serial/by-id/ | grep -i 'zigbee'" 2>/dev/null; then - echo -e " ✅ Zigbee device found in /dev/serial/by-id/." - TARGET_ZIGBEE_DEVICE=$(ssh_to_live_host "ls /dev/serial/by-id/ | grep -i 'zigbee'") - else - echo -e " ⚠️ No Zigbee device found." - TARGET_ZIGBEE_DEVICE="" + echo -e "❌ Failed to generate hardware configuration" + exit 1 fi + ### Generate hardware-configuration.nix <-- } services_selection() { - echo -e "\n\n ➡️ You will now select the services you want installed on your server:" + echo -e "\n\n➡️ You will now select the services you want installed on your server:" - AVAILABLE_SERVICES=( "frigate" "gitea" "home-assistant" "immich" "it-tools" \ + local AVAILABLE_SERVICES=( "frigate" "gitea" "home-assistant" "immich" "it-tools" \ "nextcloud" "passbolt" "pi-hole" ) - AVAILABLE_SERVICES_NUMBER=${#AVAILABLE_SERVICES[@]} - SERVICES_DESCRIPTION=( "Pi-Hole : Block ads on all your devices" \ + local SERVICES_DESCRIPTION=( "Pi-Hole : Block ads on all your devices" \ "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" \ -) + "IT-tools : A set of useful tools when doing IT" + ) - SELECTED_SERVICES_DESCRIPTION=$(gum choose --no-limit --header "Homelab services:" "${SERVICES_DESCRIPTION[@]}") + local SELECTED_SERVICES_DESCRIPTION=$(gum choose --no-limit --header "Homelab services:" "${SERVICES_DESCRIPTION[@]}") - for i in $(seq 0 $((${#AVAILABLE_SERVICES[@]} - 1))); do + for i in ${!AVAILABLE_SERVICES[@]}; do if printf '%s' "$SELECTED_SERVICES_DESCRIPTION" | grep -iq "${AVAILABLE_SERVICES[$i]}"; then - SELECTED_SERVICES+=(${AVAILABLE_SERVICES[$i]}) + export SELECTED_SERVICES+=(${AVAILABLE_SERVICES[$i]}) fi done + + for service in ${SELECTED_SERVICES[@]}; do + mkdir -p final-nix-config/mnt/config/"${service}" + mkdir -p final-nix-config/mnt/data/"${service}" + done } files_generation() { - echo -e "\n ✅ Writing configuration files for the selected homelab services..." - # Traefik - mkdir -p extra-files/mnt/config-storage/traefik/config/conf/ - envsubst < config-files/docker/config/traefik/traefik.yaml > extra-files/mnt/config-storage/traefik/config/traefik.yaml + # Helper to generate standard DB credentials + 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 "-")" + } - for service in "${SELECTED_SERVICES[@]}"; do - # Frigate - if [[ "$service" -eq "frigate" ]]; then - echo -e "\n ✅ Adapting the docker configuration to your hardware..." - FRIGATE_DEVICES_BLOCK="" - if [[ "$TARGET_GRAPHICS_RENDERER" -eq "true" ]]; then - FRIGATE_DEVICES_BLOCK+=" - /dev/dri:/dev/dri\n" - fi - if [[ "$TARGET_USB_CORAL" -eq "true" ]]; then - FRIGATE_DEVICES_BLOCK+=" - /dev/bus/usb:/dev/bus/usb\n" - fi - if [[ -n "$FRIGATE_DEVICES_BLOCK" ]]; then - REPLACEMENT="devices:\n${FRIGATE_DEVICES_BLOCK%\\n}" - sed -i.bak "s|# --- frigate devices --- #|$REPLACEMENT|" ./config-files/docker/compose/frigate.nix - else - sed -i.bak "/# --- frigate devices --- #/d" ./config-files/docker/compose/frigate.nix - fi - # Home-Assistant - elif [[ "$service" -eq "home-assistant" ]]; then - if [[ -n "$TARGET_ZIGBEE_DEVICE" ]]; then - REPLACEMENT="devices:\n - /dev/serial/by-id/${TARGET_ZIGBEE_DEVICE}:/dev/ttyUSB0" - sed -i.bak "s|# --- hass devices --- #|$REPLACEMENT|" ./config-files/docker/compose/home-assistant.nix - else - sed -i.bak "/# --- hass devices --- #/d" ./config-files/docker/compose/home-assistant.nix - fi - export HOME_ASSISTANT_MQTT_USER="$(xkcdpass -d "-" -n 2)" - export HOME_ASSISTANT_MQTT_PASSWORD="$(xkcdpass -d "-")" - mkdir -p extra-files/mnt/config-storage/hass/mqtt/config/ - mkdir -p extra-files/mnt/config-storage/hass/mqtt/data/ - envsubst < config-files/docker/config/hass/mosquitto.conf > extra-files/mnt/config-storage/hass/mqtt/config/mosquitto.conf - touch extra-files/mnt/config-storage/hass/mqtt/config/password.txt - chmod 0700 extra-files/mnt/config-storage/hass/mqtt/config/password.txt - mosquitto_passwd -b extra-files/mnt/config-storage/hass/mqtt/config/password.txt $HOME_ASSISTANT_MQTT_USER $HOME_ASSISTANT_MQTT_PASSWORD - # Passbolt - elif [[ "$service" -eq "passbolt" ]]; then - export PASSBOLT_DB_NAME="$(xkcdpass -d "-" -n 2)" - export PASSBOLT_DB_USERNAME="$(xkcdpass -d "-" -n 2)" - export PASSBOLT_DB_PASSWORD="$(xkcdpass -d "-")" - envsubst < config-files/docker/config/traefik/headers.yaml > extra-files/mnt/config-storage/traefik/config/conf/headers.yaml - envsubst < config-files/docker/config/traefik/tls.yaml > extra-files/mnt/config-storage/traefik/config/conf/tls.yaml - # Pi-Hole - elif [[ "$service" -eq "pi-hole" ]]; then - export FTLCONF_WEBSERVER_PASSWORD="$(xkcdpass -d "-")" - # Immich - elif [[ "$service" -eq "immich" ]]; then - IMMICH_DEVICES_BLOCK="" - if [[ "$TARGET_GRAPHICS_RENDERER" -eq "true" ]]; then - IMMICH_DEVICES_BLOCK+=" - /dev/dri:/dev/dri\n" - fi - if [[ -n "$IMMICH_DEVICES_BLOCK" ]]; then - REPLACEMENT="devices:\n${IMMICH_DEVICES_BLOCK%\\n}" - sed -i.bak "s|# --- immich devices --- #|$REPLACEMENT|" ./config-files/docker/compose/immich.nix - else - sed -i.bak "/# --- immich devices --- #/d" ./config-files/docker/compose/immich.nix - fi - export IMMICH_DB_NAME="$(xkcdpass -d "-" -n 2)" - export IMMICH_DB_USERNAME="$(xkcdpass -d "-" -n 2)" - export IMMICH_DB_PASSWORD="$(xkcdpass -d "-")" - mkdir -p extra-files/mnt/data-storage/immich/ - elif [[ "$service" -eq "gitea" ]]; then - export GITEA_DB_NAME="$(xkcdpass -d "-" -n 2)" - export GITEA_DB_USERNAME="$(xkcdpass -d "-" -n 2)" - export GITEA_DB_PASSWORD="$(xkcdpass -d "-")" - elif [[ "$service" -eq "nextcloud" ]]; then - envsubst < config-files/docker/config/traefik/nextcloud.yaml > extra-files/mnt/config-storage/traefik/config/conf/nextcloud.yaml - mkdir -p extra-files/mnt/data-storage/nextcloud/ - fi - cp ./config-files/docker/compose/${service}.nix ./nix-config/docker/${service}.nix - done + echo -e "\n✅ Copying the configuration to the new machine..." + cp -avu templates/final-nix-config/etc/nixos/configuration.nix final-nix-config/etc/nixos/ + cp -avu templates/nix-config/flake.nix final-nix-config/etc/nixos/ - echo -e "\n ✅ Generating sops-nix keys..." - mkdir -p extra-files/etc/secrets/disks/ - mkdir -p extra-files/var/lib/sops-nix/ - mkdir -p extra-files/etc/nixos/secrets/ + 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 + export SOPS_PUBLIC_KEY=$(age-keygen -y final-nix-config/var/lib/sops-nix/key.txt) - ssh-to-age -private-key -i extra-files/home/numbus-admin/.ssh/id_ed25519 > extra-files/var/lib/sops-nix/key.txt - export SOPS_PUBLIC_KEY=$(age-keygen -y extra-files/var/lib/sops-nix/key.txt) + echo -e "\n✅ Generating sops-nix configuration files..." + envsubst < templates/nix-config/sops-nix/.sops.yaml > final-nix-config/etc/nixos/.sops.yaml - echo -e "\n ✅ Generating sops-nix configuration files..." - envsubst < config-files/sops-nix/.sops.yaml > extra-files/etc/nixos/.sops.yaml - - echo -e "\n ✅ Encrypting secrets in the correct file..." - envsubst < "config-files/sops-nix/secrets.yaml" | sops encrypt --filename-override secrets.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/etc/nixos/secrets/secrets.yaml + --output final-nix-config/etc/nixos/secrets/secrets.yaml - echo -e "\n ✅ Writing correct ips to configuration.nix..." - sed -i s+HOME_SERVER_IP+$HOME_SERVER_IP+g ./nix-config/misc/networking.nix - sed -i s+HOME_ROUTER_IP+$HOME_ROUTER_IP+g ./nix-config/misc/networking.nix + 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 - echo -e "\n ✅ Copying the configuration to the new machine..." - cp -ravu ./nix-config/* extra-files/etc/nixos/ + echo -e "\n✅ Writing configuration files for the selected homelab services..." + envsubst < templates/podman-config/traefik/traefik.yaml > final-nix-config/mnt/config/traefik/traefik.yaml + + for service in "${SELECTED_SERVICES[@]}"; do + cp templates/nix-config/podman/${service}.nix final-nix-config/etc/nixos/podman/${service}.nix + case "${service}" in + frigate) + local FRIGATE_DEVICES_BLOCK="" + [[ "$TARGET_GRAPHICS_RENDERER" -eq "true" ]] && local FRIGATE_DEVICES_BLOCK+=" - /dev/dri:/dev/dri\n" + [[ "$TARGET_USB_CORAL" -eq "true" ]] && local FRIGATE_DEVICES_BLOCK+=" - /dev/bus/usb:/dev/bus/usb\n" + if [[ "$TARGET_PCIE_CORAL" -eq "true" ]]; then + local 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 -avu 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 + ;; + home-assistant) + if [[ -n "$TARGET_ZIGBEE_DEVICE" ]]; then + local REPLACEMENT="devices:\n - /dev/serial/by-id/${TARGET_ZIGBEE_DEVICE}:/dev/ttyUSB0" + sed -i "s|# --- hass 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/hass/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" + ;; + passbolt) + 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 + ;; + pi-hole) + export FTLCONF_WEBSERVER_PASSWORD="$(xkcdpass -d "-")" + ;; + immich) + local IMMICH_DEVICES_BLOCK="" + if [[ "$TARGET_GRAPHICS_RENDERER" -eq "true" ]]; then + local 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 + generate_db_creds "IMMICH" + ;; + gitea) + generate_db_creds "GITEA" + ;; + nextcloud) + envsubst < templates/podman-config/traefik/nextcloud.yaml > final-nix-config/mnt/config/traefik/rules/nextcloud.yaml + ;; + esac + done } disk_config_generation() { @@ -271,15 +326,15 @@ disk_config_generation() { !! ALL DATA WILL BE WIPED ON THE DISKS YOU CHOOSE !! Please press CTRL+C to abort. " - gum confirm "Do you understand and wish to proceed?" || { echo -e "\n\n ❌ Aborting as requested."; exit 1; } + 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 <-- - TMPFILE="/tmp/nixos-deployment-temp-file" - ### --> Get disk information - DISK_DETAILS=$(ssh_to_live_host 'bash -s' </dev/null | grep 'self-assessment' | awk '{print \$6}') -eq "PASSED" ]]; then + if [[ \$(echo "$REMOTE_PASS" | sudo -S smartctl -H /dev/\$DISK 2>/dev/null | grep 'self-assessment' | awk '{print \$6}') -eq "PASSED" ]]; then DISK_HEALTH+=("PASSED") else DISK_HEALTH+=("N/A") @@ -314,79 +369,69 @@ for DISK in \$(lsblk -x SIZE -d -n -e 7,11 -o NAME); do DISK_SIZE+=("\$(lsblk -x SIZE -d -n -e 7,11 -o SIZE /dev/\$DISK)") done -echo "DISK_DEVPATH=(\${DISK_DEVPATH[@]})" > $TMPFILE -echo "DISK_NAME=(\${DISK_NAME[@]})" >> $TMPFILE -echo "DISK_TYPE=(\${DISK_TYPE[@]})" >> $TMPFILE -echo "DISK_HEALTH=(\${DISK_HEALTH[@]})" >> $TMPFILE -echo "DISK_ID=(\${DISK_ID[@]})" >> $TMPFILE -echo "DISK_SIZE=(\${DISK_SIZE[@]})" >> $TMPFILE +echo "DISK_DEVPATH=(\${DISK_DEVPATH[@]})" > "${TMPFILE}" +echo "DISK_NAME=(\${DISK_NAME[@]})" >> "${TMPFILE}" +echo "DISK_TYPE=(\${DISK_TYPE[@]})" >> "${TMPFILE}" +echo "DISK_HEALTH=(\${DISK_HEALTH[@]})" >> "${TMPFILE}" +echo "DISK_ID=(\${DISK_ID[@]})" >> "${TMPFILE}" +echo "DISK_SIZE=(\${DISK_SIZE[@]})" >> "${TMPFILE}" EOF -) - scp -i "extra-files/home/numbus-admin/.ssh/id_ed25519" nixos@$TARGET_HOST:$TMPFILE $TMPFILE &> /dev/null - source $TMPFILE && rm $TMPFILE + scp -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${TARGET_HOST}":"${TMPFILE}" "${TMPFILE}" &> /dev/null + source "${TMPFILE}" && rm "${TMPFILE}" ### --> Disk selection if [[ "${#DISK_NAME[@]}" -eq 0 ]]; then - echo -e "\n\n ❌ No disks found on the target host. Aborting." + echo -e "\n\n❌ No disks found on the target host. Aborting." exit 1 fi - HEADER=$(printf " %-12s %-12s %-12s %-12s %s" "Device" "Type" "Size" "SMART" "Path") + local HEADER=$(printf " %-12s %-12s %-12s %-12s %s" "Device" "Type" "Size" "SMART" "Path") for i in ${!DISK_NAME[@]}; do - GUM_PRINTED_ELEMENT=$(printf "%-12s %-12s %-12s %-12s %s" \ + local GUM_PRINTED_ELEMENT=$(printf "%-12s %-12s %-12s %-12s %s" \ "${DISK_NAME[${i}]}" "${DISK_TYPE[${i}]}" "${DISK_SIZE[${i}]}" \ "${DISK_HEALTH[${i}]}" "${DISK_DEVPATH[${i}]}") - GUM_PRINTED_ELEMENTS+=("$GUM_PRINTED_ELEMENT") + local GUM_PRINTED_ELEMENTS+=("$GUM_PRINTED_ELEMENT") done - gum style --foreground 212 " ➡️ Please choose one (stripe) or two (mirror) disks for your NixOS boot installation :" + gum style --foreground 212 "➡️ Please choose one (stripe) or two (mirror) disks for your NixOS boot installation :" - SELECTED_BOOT_DISK=$(gum choose --limit 2 --header "$HEADER" "${GUM_PRINTED_ELEMENTS[@]}") + local SELECTED_BOOT_DISK=$(gum choose --limit 2 --header "$HEADER" "${GUM_PRINTED_ELEMENTS[@]}") for i in ${!DISK_NAME[@]}; do if printf '%s' "$SELECTED_BOOT_DISK" | grep -iqw "${DISK_NAME[${i}]}"; then - if [[ -n "${DISK_ID[${i}]}" ]]; then - export BOOT_DISKS_ID+=("${DISK_NAME[${i}]}") - else - export BOOT_DISKS_ID+=("${DISK_ID[${i}]}") - fi + export BOOT_DISKS_ID+=("${DISK_ID[${i}]:-${DISK_DEVPATH[${i}]}}") unset "GUM_PRINTED_ELEMENTS[${i}]" fi done if [[ "${#BOOT_DISKS_ID[@]}" -eq 0 ]]; then - echo -e "\n\n ❌ No boot disk selected. Aborting." + 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]} 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✅ 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]} else - echo -e "\n\n ❌ Unexpected bug. Please contact the developer. Aborting." + echo -e "\n\n❌ Unexpected bug. Please contact the developer. Aborting." exit 1 fi - gum style --foreground 212 " ➡️ Please choose data and parity disks (up to 9 total) :" + gum style --foreground 212 "➡️ Please choose data and parity disks (up to 9 total) :" - SELECTED_DATA_DISK=$(gum choose --limit 9 --header "$HEADER" "${GUM_PRINTED_ELEMENTS[@]}") + local SELECTED_DATA_DISK=$(gum choose --limit 9 --header "$HEADER" "${GUM_PRINTED_ELEMENTS[@]}") for i in ${!DISK_NAME[@]}; do if printf '%s' "$SELECTED_DATA_DISK" | grep -iq "${DISK_NAME[${i}]}"; then - if [[ -n ${DISK_ID[${i}]} ]]; then - export DATA_DISKS_ID+=("${DISK_NAME[${i}]}") - export DATA_DISKS_TYPE+=("${DISK_TYPE[${i}]}") - else - export DATA_DISKS_ID+=("${DISK_ID[${i}]}") - export DATA_DISKS_TYPE+=("${DISK_TYPE[${i}]}") - fi + export DATA_DISKS_ID+=("${DISK_ID[${i}]:-${DISK_DEVPATH[${i}]}}") + export DATA_DISKS_TYPE+=("${DISK_TYPE[${i}]}") fi done @@ -395,7 +440,7 @@ the one of the smallest disk." ### Disk selection <-- ### --> Selection recap - RECAP_CONTENT=$(cat < Config generation - echo -e "\n\n ✅ Generating disko configuration from templates..." - TEMPLATE_FILE="config-files/disks/templates/boot-${#BOOT_DISKS_ID[@]}.nix" - (envsubst < "$TEMPLATE_FILE") > ./nix-config/disks/disko.nix - echo -e "\n ✅ Generated boot disk configuration." + 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 # 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]}" - (envsubst < "config-files/disks/templates/content.nix") >> ./nix-config/disks/disko.nix - sed -i "s|/mnt/content-1|/mnt/data-storage|" ./nix-config/disks/disko.nix + (envsubst < "templates/nix-config/disks/content.nix") >> final-nix-config/etc/nixos/disks/disko.nix + sed -i "s|/mnt/content-1|/mnt/data-storage|" final-nix-config/etc/nixos/disks/disko.nix # Mirror configuration elif [[ "$CONTENT_DISK_NUMBER" -eq 1 && "$PARITY_DISK_NUMBER" -eq 1 ]]; then export CONTENT_DISK_ID="${DATA_DISKS_ID[0]}" export PARITY_DISK_ID="${DATA_DISKS_ID[1]}" - (envsubst < "config-files/disks/templates/mirror.nix") >> ./nix-config/disks/disko.nix + (envsubst < "templates/nix-config/disks/mirror.nix") >> final-nix-config/etc/nixos/disks/disko.nix # SnapRAID configuration elif [[ "$CONTENT_DISK_NUMBER" -gt 1 ]]; then # Enable SnapRAID - sed -i "s|# ./disks/snapraid.nix| ./disks/snapraid.nix|" ./nix-config/configuration.nix - sed -i '$ d' ./config-files/disks/snapraid.nix - cat <> ./config-files/disks/snapraid.nix + cp -avu templates/nix-config/disks/snapraid.nix final-nix-config/etc/nixos/disks/ + cp -avu templates/nix-config/disks/pcr-check.nix final-nix-config/etc/nixos/disks/ + sed -i "s|# ./disks/snapraid.nix| ./disks/snapraid.nix|" final-nix-config/etc/nixos/configuration.nix + sed -i '$ d' final-nix-config/etc/nixos/disks/snapraid.nix + cat << EOF >> final-nix-config/etc/nixos/disks/snapraid.nix # --> Automatic data disks unlock, generated by deploy.sh on $(date) boot.initrd.luks.devices = { EOF j=0 for i in $(seq 0 $(($CONTENT_DISK_NUMBER - 1))); do export ((j++)) - LOOP_DISK="${DATA_DISKS_ID[${i}]}" - export CONTENT_DISK_ID=${!LOOP_DISK} - (envsubst < "config-files/disks/templates/content.nix") >> ./nix-config/disks/disko.nix - cat <> ./config-files/disks/snapraid.nix + export CONTENT_DISK_ID="${DATA_DISKS_ID[${i}]}" + (envsubst < "templates/nix-config/disks/content.nix") >> final-nix-config/etc/nixos/disks/disko.nix + cat << EOF >> final-nix-config/etc/nixos/disks/snapraid.nix "crypted-content-disk-${j}" = { - device = "${!LOOP_DISK}"; + device = "${CONTENT_DISK_ID}"; keyFile = "/etc/secrets/disks/content-disk-${j}"; }; EOF done - echo -e "\n ✅ Generated $CONTENT_DISK_NUMBER data disk configuration(s)." + echo -e "\n✅ Generated $CONTENT_DISK_NUMBER data disk configuration(s)." j=0 for i in $(seq $PARITY_DISK_NUMBER $((${#DATA_DISKS_ID[@]} - 1))); do export ((j++)) - LOOP_DISK="${DATA_DISKS_ID[${i}]}" - export PARITY_DISK_ID=${!LOOP_DISK} - (envsubst < "config-files/disks/templates/parity.nix") >> ./nix-config/disks/disko.nix - cat <> ./config-files/disks/snapraid.nix + export PARITY_DISK_ID="${DATA_DISKS_ID[${i}]}" + (envsubst < "templates/nix-config/disks/parity.nix") >> final-nix-config/etc/nixos/disks/disko.nix + cat << EOF >> final-nix-config/etc/nixos/disks/snapraid.nix "crypted-parity-disk-${j}" = { - device = "${!LOOP_DISK}"; + device = "${PARITY_DISK_ID}"; keyFile = "/etc/secrets/disks/parity-disk-${j}}"; }; EOF done - echo -e "\n ✅ Generated $PARITY_DISK_NUMBER parity disk configuration(s)." + echo -e "\n✅ Generated $PARITY_DISK_NUMBER parity disk configuration(s)." # Close the snapraid.nix block - cat <<'EOF' >> ./config-files/disks/snapraid.nix + cat <<'EOF' >> final-nix-config/etc/nixos/disks/snapraid.nix # Automatic data disks unlock <-- }; } EOF - cp -avu ./config-files/disks/snapraid.nix ./nix-config/disks/ fi - # Close the disko.nix block - cat <<'EOF' >> ./nix-config/disks/disko.nix + cat <<'EOF' >> final-nix-config/etc/nixos/disks/disko.nix }; }; } EOF - echo -e "\n ✅ Final disko configuration created." + echo -e "\n✅ Final disko configuration created." if [[ -n "${DATA_DISKS_ID[@]}" ]]; then for i in ${!DATA_DISKS_ID[@]}; do @@ -497,41 +539,56 @@ EOF fi done if [[ -n "${DISK_ID_LIST[@]}" ]]; then - sed -i "s|DISK_ID_LIST|${DISK_ID_LIST[@]}|" ./config-files/disks/spindown.nix - cp -avu ./config-files/disks/spindown.nix ./nix-config/disks/spindown.nix - echo -e "\n ✅ Disk spindown configuration created." + cp -avu templates/nix-config/disks/spindown.nix final-nix-config/etc/nixos/disks/ + sed -i "s|DISK_ID_LIST|${DISK_ID_LIST[@]}|" final-nix-config/etc/nixos/disks/spindown.nix + echo -e "\n✅ Disk spindown configuration created." fi fi ### Config generation <-- ### --> Generate unlock keys - for i in ${#BOOT_DISKS_ID[@]}; do - declare "/etc/secrets/disks/boot-disk-${i}=$(xkcdpass -d "-")" - done - for i in $CONTENT_DISK_NUMBER; do - declare "/etc/secrets/disks/content-disk-${i}=$(xkcdpass -d "-")" - done - for i in $PARITY_DISK_NUMBER; do - declare "/etc/secrets/disks/parity-disk-${i}=$(xkcdpass -d "-")" + for i in $(seq 1 "${#BOOT_DISKS_ID[@]}"); do + PASS="$(xkcdpass -d "-")" + echo -n "$PASS" > "final-nix-config/etc/secrets/disks/boot-disk-${i}" + chmod 600 "final-nix-config/etc/secrets/disks/boot-disk-${i}" done + if [[ "$CONTENT_DISK_NUMBER" -gt 0 ]]; then + for i in $(seq 1 "$CONTENT_DISK_NUMBER"); do + PASS="$(xkcdpass -d "-")" + echo -n "$PASS" > "final-nix-config/etc/secrets/disks/content-disk-${i}" + chmod 600 "final-nix-config/etc/secrets/disks/content-disk-${i}" + done + fi + if [[ "$PARITY_DISK_NUMBER" -gt 0 ]]; then + for i in $(seq 1 "$PARITY_DISK_NUMBER"); do + PASS="$(xkcdpass -d "-")" + echo -n "$PASS" > "final-nix-config/etc/secrets/disks/parity-disk-${i}" + chmod 600 "final-nix-config/etc/secrets/disks/parity-disk-${i}" + done + fi ### Generate unlock keys <-- } -deploy() { - echo -e "\n\n 🔄 Deploying to the remote server..." - nix run github:nix-community/nixos-anywhere -- \ - --generate-hardware-config nixos-generate-config ./nix-config/hardware-configuration.nix \ - --flake ./nix-config#numbus-server \ - --extra-files extra-files \ - --chown "/home/numbusing a us-admin/" 1000:1000 \ - --target-host nixos@$TARGET_HOST +export_configuration() { + cp deploy.conf final-nix-config/etc/numbus-server/numbus-server.conf - echo -e "\n\n ✅ Installation successfull !" - sleep 1 + local CONFIG_EXPORT_DIR="final-nix-config/etc/numbus-server/" + local CONFIG_EXPORT_FILE="${CONFIG_EXPORT_DIR}/numbus-server.conf" + + cp -ravu templates/post-install/numbus-server.sh "$CONFIG_EXPORT_DIR" + + echo "# SERVICE SETTINGS" >> $CONFIG_EXPORT_FILE + echo "SELECTED_SERVICES=(${SELECTED_SERVICES[@]})" >> $CONFIG_EXPORT_FILE + echo "# DISK SETTINGS" >> $CONFIG_EXPORT_FILE + echo "BOOT_DISK_ID_LIST=(${BOOT_DISKS_ID[@]})" >> $CONFIG_EXPORT_FILE + echo "DATA_DISKS_ID_LIST=(${DATA_DISKS_ID[@]})" >> $CONFIG_EXPORT_FILE + echo "SPINDOWN_DISKS_ID_LIST=(${DISK_ID_LIST[@]})" >> $CONFIG_EXPORT_FILE + echo "CONTENT_DISK_NUMBER=$CONTENT_DISK_NUMBER" >> $CONFIG_EXPORT_FILE + echo "PARITY_DISK_NUMBER=$PARITY_DISK_NUMBER" >> $CONFIG_EXPORT_FILE } sum_up() { - RECAP_CONTENT=$(cat </dev/null 2>&1; then FOUND="true" exit 0 (i++) if [[ "\${i}" -gt 150 ]]; then - echo -e "\n\n ❌ Could not connect to the server after 150 retries. \ + echo -e "\n\n❌ Could not connect to the server after 150 retries. \ This is most likely due to a networking issue. Please double check your network settings. Aborting." exit 1 fi @@ -580,17 +655,17 @@ This is most likely due to a networking issue. Please double check your network done EOF - ssh_to_installed_host 'bash -s' <