Update to logic. Added postrun actions. Have to work on postinstall script.

This commit is contained in:
Raphaël Numbus
2025-12-22 16:45:31 +01:00
parent fb751ca89b
commit 9559b232dc
5 changed files with 99 additions and 26 deletions
-1
View File
@@ -35,7 +35,6 @@ in
'systemctl default'
to continue booting
'';
example = "6214de8c3d861c4b451acc8c4e24294c95d55bcec516bbf15c077ca3bffb6547";
};
};
boot.initrd.luks.devices = lib.mkOption {
+6
View File
@@ -1,3 +1,9 @@
#!/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")
Executable → Regular
+90 -22
View File
@@ -1,5 +1,12 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p gum xkcdpass openssl sops ssh-to-age age sshpass envsubst pciutils usbutils mosquitto rsync
#!nix-shell -i bash -p gum 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" \
"SENDER_EMAIL_DOMAIN" "SENDER_EMAIL_PORT" "HOME_ROUTER_SUBNET" "HOME_ROUTER_IP" \
"HOME_SERVER_IP")
INSTALLED_REMOTE_PASS="changeMe!"
necessary_credentials() {
#TARGET SETTINGS
@@ -40,9 +47,8 @@ necessary_credentials_with_config() {
CONFIG_PATH="$(gum file)"
source "$CONFIG_PATH"
REQUIRED_VARS=(TARGET_HOST 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)
MISSING=0
for VAR in "${REQUIRED_VARS[@]}"; do
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
@@ -63,29 +69,34 @@ setup_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
REMOTE_PASS=$(gum input --password --placeholder "Enter password for 'nixos' on '$TARGET_HOST'")
if [ -z "$REMOTE_PASS" ]; then
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 "$REMOTE_PASS" ssh-copy-id -o StrictHostKeyChecking=no -i "extra-files/home/numbus-admin/.ssh/id_ed25519" "nixos@$TARGET_HOST"; then
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."
else
echo " ❌ Failed to copy SSH key. Please check the host IP and password."
exit 1
fi
export REMOTE_PASS
export LIVE_REMOTE_PASS
}
ssh_to_host() {
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
}
hardware_detection() {
echo -e "\n\n 🔎 Detecting graphics card on target host..."
VGA_INFO=$(ssh_to_host "lspci -nn | grep -i 'vga'")
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"
@@ -100,7 +111,7 @@ hardware_detection() {
export TARGET_GRAPHICS="false"
fi
echo -e "\n\n 🔎 Detecting transconding acceleration on target host..."
if ssh_to_host "ls /dev/dri/ | grep -iq 'renderD128'" 2>/dev/null; then
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"
else
@@ -108,7 +119,7 @@ hardware_detection() {
TARGET_GRAPHICS_RENDERER="false"
fi
echo -e "\n\n 🔎 Detecting USB Google Coral TPU on target host..."
if ssh_to_host "lsusb | grep -iq 'google'" 2>/dev/null; then
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
@@ -116,9 +127,9 @@ hardware_detection() {
TARGET_USB_CORAL="false"
fi
echo -e "\n\n 🔎 Detecting Zigbee coordinator on target host..."
if ssh_to_host "ls /dev/serial/by-id/ | grep -i 'zigbee'" 2>/dev/null; then
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_host "ls /dev/serial/by-id/ | grep -i 'zigbee'")
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=""
@@ -268,7 +279,7 @@ disk_config_generation() {
TMPFILE="/tmp/nixos-deployment-temp-file"
### --> Get disk information
DISK_DETAILS=$(ssh_to_host 'bash -s' <<EOF
DISK_DETAILS=$(ssh_to_live_host 'bash -s' <<EOF
HDD=1
DISK_DEVPATH=()
@@ -292,7 +303,7 @@ for DISK in \$(lsblk -x SIZE -d -n -e 7,11 -o NAME); do
else DISK_TYPE+=("Other")
fi
# Disk health
if [[ \$(echo "$REMOTE_PASS" | sudo -S smartctl -H /dev/\$DISK 2>/dev/null | grep 'self-assessment' | awk '{print \$6}') -eq "PASSED" ]]; then
if [[ \$(echo "$LIVE_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")
@@ -345,16 +356,19 @@ EOF
fi
done
echo "Boot ; ${BOOT_DISKS_ID[@]}, ${#BOOT_DISKS_ID[@]}"
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]}
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]}
else
echo -e "\n\n ❌ Unexpected bug. Please contact the developer. Aborting."
exit 1
@@ -491,7 +505,7 @@ EOF
### Config generation <--
### --> Generate unlock keys
for i in ${!BOOT_DISKS_ID[@]}; do
for i in ${#BOOT_DISKS_ID[@]}; do
declare "/etc/secrets/disks/boot-disk-${i}=$(xkcdpass -d "-")"
done
for i in $CONTENT_DISK_NUMBER; do
@@ -547,11 +561,63 @@ postrun_action() {
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."
gum spin --spinner dot --title "Rebooting the remote..." -- sleep 60
gum spin --spinner dot --title "Rebooting the remote..." -- sleep 120
ssh
# Add TPM2 boot disk decryption
# Add pcr-check.nix
gum confirm " ➡️ Select 'yes' once the machine rebooted and you unlocked the disks." || { echo -e "\n\n ❌ Aborting as requested."; exit 1; }
gum spin --spinner dot --title "\n\n 🔄 Waiting for the server to boot up..." --auto <<EOF
while FOUND="false"; do
if ping -c1 -W1 $HOME_SERVER_IP >/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. \
This is most likely due to a networking issue. Please double check your network settings. Aborting."
exit 1
fi
fi
done
EOF
ssh_to_installed_host 'bash -s' <<EOF
sed -i "s|# ./disks/pcr-check.nix| ./disks/pcr-check.nix|" /etc/nixos/configuration.nix
if [[ ${#BOOT_DISKS_ID[@]} -eq 1 ]]; then
echo $INSTALLED_REMOTE_PASS | sudo -S systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 /dev/mapper/crypted-boot-1
elif [[ ${#BOOT_DISKS_ID[@]} -eq 2 ]]; then
echo $INSTALLED_REMOTE_PASS | sudo -S systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 /dev/mapper/crypted-boot-1
echo $INSTALLED_REMOTE_PASS | sudo -S systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 /dev/mapper/crypted-boot-2
fi
PCR_HASH=\$(echo $INSTALLED_REMOTE_PASS | sudo -S systemd-analyze pcrs 15 --json=short)
sed -i "s|# systemIdentity.enable = true;| systemIdentity.enable = true;|" /etc/nixos/configuration.nix
sed -i "s|# systemIdentity.pcr15 = "PCR_HASH";| systemIdentity.pcr15 = "PCR_HASH";|" /etc/nixos/configuration.nix
sed -i "s|PCR_HASH|\${PCR_HASH}|" /etc/nixos/configuration.nix
EOF
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "
⚠️ $(gum style --foreground 212 'WARNING:') You will now set the password of the numbus-admin user. \
You will almost never user it. Consider using a very strong password : you can write it down \
securely on a hidden sheet of paper or add it to your password manager (local with Passbolt \
any other online password manager provider.)."
gum confirm " ➡️ I understand, 'yes' to proceed." || { echo -e "\n\n ❌ Aborting as requested."; exit 1; }
echo $INSTALLED_REMOTE_PASS | sudo -S passwd numbus-admin
}
congrats() {
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "
⚠️ $(gum style --foreground 212 'CONGRATULATIONS !!:') You now have a working home server. \
Data stored on there will be fully yours and protected. Keep in my mind this comes with the \
responsability of managing it and keeping it secure. Now, you have to log in the webpages of \
the services you installed. Create an admin account for all of them and configure them (or keep \
it simple and use defaults) and take care to note down all the passwords. Change all default passwords \
and create user accounts for your family or friends that will use the server.
Cheers !!"
}
nixos_update() {
@@ -605,6 +671,7 @@ if [[ "$ACTION_ANSWER" -eq "[1] 🌐 Deploy NixOS on a remote machine" ]]; then
disk_config_generation
deploy
postrun_action
congrats
elif [[ "$ACTION_ANSWER" -eq "[2] 💽 Deploy NixOS on a remote machine with a file configuration" ]]; then
echo -e "\n ➡️ Proceeding with deployment using a config file…"
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "➡️ On the target host : start the computer and boot into the NixOS iso.
@@ -619,6 +686,7 @@ elif [[ "$ACTION_ANSWER" -eq "[2] 💽 Deploy NixOS on a remote machine with a f
deploy
sum_up
postrun_action
congrats
elif [[ "$ACTION_ANSWER" -eq "[3] 🛠️ Update a NixOS remote machine" ]]; then
echo -e "\n ➡️ Proceeding with update…"
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "➡️ On the target host : make sure the NixOS installation you want
+2 -2
View File
@@ -38,7 +38,7 @@
# TPM2 PCR check
# systemIdentity.enable = true;
# systemIdentity.pcr15 = null;
# systemIdentity.pcr15 = "PCR_HASH";
# Timezone
time.timeZone = "Europe/Paris";
@@ -116,7 +116,7 @@
description = "Numbus Admin";
extraGroups = [ "networkmanager" "wheel" "docker" ];
uid = 1000;
initialPassword = "nixos-at-numbus";
initialPassword = "changeMe!";
};
# Login message
+1 -1
View File
@@ -55,7 +55,7 @@ in
### --> SMART disk heath
services.smartd = {
enable = true;
defaults.autodetected = "-a -o on -S on -s (S/../.././02|L/../../6/03) -n standby,q";
defaults.autodetected = "-a -o on -S on -s (S/../.././00|L/../../6/01) -n standby,q";
notifications = {
wall = {
enable = true;