Updated mail alerts. Added automatic DNS records creation with cloudflare

This commit is contained in:
Raphaël Numbus
2026-01-17 14:51:41 +01:00
parent 6d1d3be3b3
commit 98a144d408
4 changed files with 127 additions and 85 deletions
+108 -73
View File
@@ -7,10 +7,10 @@ export GUM_SPIN_SPINNER_BOLD=true
export GUM_SPIN_SHOW_ERROR=true export GUM_SPIN_SHOW_ERROR=true
export GUM_SPIN_TITLE_BOLD=true export GUM_SPIN_TITLE_BOLD=true
NECESSARY_VARIABLES_LIST=("TARGET_HOST" "REMOTE_PASS" "SSH_PUBLIC_KEY" "DOMAIN_NAME" \ 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" \ "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" \ "SENDER_EMAIL_DOMAIN" "SENDER_EMAIL_PORT" "HOME_ROUTER_SUBNET" "HOME_ROUTER_IP" \
"HOME_SERVER_IP" "SELECTED_SERVICES" "SERVER_OWNER_NAME") "HOME_SERVER_IP" "SELECTED_SERVICES" "SERVER_OWNER_NAME" )
### Default settings <-- ### Default settings <--
user_input() { user_input() {
@@ -141,7 +141,7 @@ hardware_detection() {
ssh_to_host 'bash -s' << SSHEND ssh_to_host 'bash -s' << SSHEND
for brand in Intel AMD NVIDIA; do for brand in Intel AMD NVIDIA; do
if lspci -nn 2>/dev/null | grep -i "vga" | grep -iq "\${brand}"; then if lspci -nn > /dev/null 2>&1 | grep -i "vga" | grep -iq "\${brand}"; then
TARGET_GRAPHICS="true" TARGET_GRAPHICS="true"
TARGET_GRAPHICS_BRAND+=("\${brand}") TARGET_GRAPHICS_BRAND+=("\${brand}")
else else
@@ -149,14 +149,14 @@ for brand in Intel AMD NVIDIA; do
fi fi
done done
ls /dev/dri/ 2>/dev/null | grep -iq "renderD128" && TARGET_GRAPHICS_RENDERER="true" || TARGET_GRAPHICS_RENDERER="false" ls /dev/dri/ > /dev/null 2>&1 | grep -iq "renderD128" && TARGET_GRAPHICS_RENDERER="true" || TARGET_GRAPHICS_RENDERER="false"
lsusb 2>/dev/null | grep -iq "google" && TARGET_USB_CORAL="true" || TARGET_USB_CORAL="false" lsusb > /dev/null 2>&1 | grep -iq "google" && TARGET_USB_CORAL="true" || TARGET_USB_CORAL="false"
lspci -nn 2>/dev/null | grep -iq "089a" && TARGET_PCIE_CORAL="true" || TARGET_PCIE_CORAL="false" lspci -nn > /dev/null 2>&1 | grep -iq "089a" && TARGET_PCIE_CORAL="true" || TARGET_PCIE_CORAL="false"
ls /dev/serial/by-id/ 2>/dev/null | grep -i "zigbee" && TARGET_ZIGBEE_DEVICE=\$(ls /dev/serial/by-id/ 2>/dev/null | grep -i "zigbee" | head -n 1) || TARGET_ZIGBEE_DEVICE="" ls /dev/serial/by-id/ > /dev/null 2>&1 | grep -i "zigbee" && TARGET_ZIGBEE_DEVICE=\$(ls /dev/serial/by-id/ > /dev/null 2>&1 | grep -i "zigbee" | head -n 1) || TARGET_ZIGBEE_DEVICE=""
TARGET_INTERFACE=\$(ip -4 route show default | awk '{print \$5}' | head -n1) TARGET_INTERFACE=\$(ip -4 route show default | awk '{print \$5}' | head -n1)
if ls -l /sys/class/tpm/tpm0/ >/dev/null 2>&1; then if ls -l /sys/class/tpm/tpm0/ > /dev/null 2>&1; then
TARGET_TPM="true" TARGET_TPM="true"
TARGET_TPM_VERSION=\$(cat /sys/class/tpm/tpm0/tpm_version_major) TARGET_TPM_VERSION=\$(cat /sys/class/tpm/tpm0/tpm_version_major)
else else
@@ -186,7 +186,7 @@ for DISK in \$(lsblk -x SIZE -d -n -e 7,11 -o NAME); do
else DISK_TYPE+=("Other") else DISK_TYPE+=("Other")
fi fi
# Disk health # 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 "$REMOTE_PASS" | sudo -S smartctl -H /dev/\$DISK > /dev/null 2>&1 | grep 'self-assessment' | awk '{print \$6}') == "PASSED" ]]; then
DISK_HEALTH+=("PASSED") DISK_HEALTH+=("PASSED")
else else
DISK_HEALTH+=("N/A") DISK_HEALTH+=("N/A")
@@ -239,7 +239,7 @@ SSHEND
} }
services_selection() { 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:"
local AVAILABLE_SERVICES=( "frigate" "gitea" "home-assistant" "immich" "it-tools" \ local AVAILABLE_SERVICES=( "frigate" "gitea" "home-assistant" "immich" "it-tools" \
"nextcloud" "passbolt" "pi-hole" "virtualization" ) "nextcloud" "passbolt" "pi-hole" "virtualization" )
@@ -258,12 +258,20 @@ services_selection() {
local 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 ${!AVAILABLE_SERVICES[@]}; do for i in ${!AVAILABLE_SERVICES[@]}; do
if printf '%s' "$SELECTED_SERVICES_DESCRIPTION" | grep -iq "${AVAILABLE_SERVICES[$i]}"; then if printf '%s' "${SELECTED_SERVICES_DESCRIPTION}" | grep -iq "${AVAILABLE_SERVICES[${i}]}"; then
SELECTED_SERVICES+=(${AVAILABLE_SERVICES[$i]}) SELECTED_SERVICES+=("${AVAILABLE_SERVICES[${i}]}")
if [[ "${AVAILABLE_SERVICES[${i}]}" == "nextcloud" ]]; then
SELECTED_SERVICES_DNS+=("nextcloud.${DOMAIN_NAME}" "nextcloud-aio.${DOMAIN_NAME}")
elif [[ "${AVAILABLE_SERVICES[${i}]}" == "virtualization" ]]; then
:
else
SELECTED_SERVICES_DNS+=("${AVAILABLE_SERVICES[${i}]}.${DOMAIN_NAME}")
fi
fi fi
done done
export SELECTED_SERVICES export SELECTED_SERVICES
export SELECTED_SERVICES_DNS
} }
disks_selection() { disks_selection() {
@@ -274,14 +282,14 @@ disks_selection() {
!! ALL DATA WILL BE WIPED ON THE DISKS YOU CHOOSE !! !! ALL DATA WILL BE WIPED ON THE DISKS YOU CHOOSE !!
Please press CTRL+C to abort. 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)" echo -e "\n\n 🔎 Fetching and analyzing disks from target host... (This may take a moment)"
### Disk wiping warning <-- ### Disk wiping warning <--
### --> Disk selection ### --> Disk selection
if [[ "${#DISK_NAME[@]}" -eq 0 ]]; then if [[ "${#DISK_NAME[@]}" -eq 0 ]]; then
echo -e "\n❌ No disks found on the target host. Aborting." echo -e "\n ❌ No disks found on the target host. Aborting."
exit 1 exit 1
fi fi
@@ -291,13 +299,13 @@ disks_selection() {
local 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_NAME[${i}]}" "${DISK_TYPE[${i}]}" "${DISK_SIZE[${i}]}" \
"${DISK_HEALTH[${i}]}" "${DISK_DEVPATH[${i}]}") "${DISK_HEALTH[${i}]}" "${DISK_DEVPATH[${i}]}")
local GUM_PRINTED_ELEMENTS+=("$GUM_PRINTED_ELEMENT") local GUM_PRINTED_ELEMENTS+=("${GUM_PRINTED_ELEMENT}")
done done
echo "" echo ""
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 :"
local 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 for i in ${!DISK_NAME[@]}; do
if printf '%s' "$SELECTED_BOOT_DISK" | grep -iqw "${DISK_NAME[${i}]}"; then if printf '%s' "$SELECTED_BOOT_DISK" | grep -iqw "${DISK_NAME[${i}]}"; then
@@ -308,23 +316,23 @@ disks_selection() {
done done
if [[ "${#BOOT_DISKS_ID[@]}" -eq 0 ]]; then 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 exit 1
elif [[ "${#BOOT_DISKS_ID[@]}" -eq 1 ]]; then elif [[ "${#BOOT_DISKS_ID[@]}" -eq 1 ]]; then
echo -e "\n\n⚠️ One boot disk selected, continuing with striped boot disk configuration." 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." 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_ID="${BOOT_DISKS_ID[0]}"
export BOOT_DISK_1_NAME="${DISK_NAME[0]}" export BOOT_DISK_1_NAME="${DISK_NAME[0]}"
elif [[ "${#BOOT_DISKS_ID[@]}" -eq 2 ]]; then 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 \ echo -e "\n\n ⚠️ If the two disks are different sizes, the resulting usable space size will be \
the one of the smallest disk." the one of the smallest disk."
export BOOT_DISK_1_ID="${BOOT_DISKS_ID[0]}" export BOOT_DISK_1_ID="${BOOT_DISKS_ID[0]}"
export BOOT_DISK_2_ID="${BOOT_DISKS_ID[1]}" export BOOT_DISK_2_ID="${BOOT_DISKS_ID[1]}"
export BOOT_DISK_1_NAME="${BOOT_DISKS_NAME[0]}" export BOOT_DISK_1_NAME="${BOOT_DISKS_NAME[0]}"
export BOOT_DISK_2_NAME="${BOOT_DISKS_NAME[1]}" export BOOT_DISK_2_NAME="${BOOT_DISKS_NAME[1]}"
else 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 exit 1
fi fi
@@ -384,7 +392,7 @@ services_generation() {
export "${SERVICE_UPPER}_DB_PASSWORD"="$(xkcdpass -d "-")" export "${SERVICE_UPPER}_DB_PASSWORD"="$(xkcdpass -d "-")"
} }
echo -e "\n✅ Writing configuration files for the selected homelab services..." echo -e "\n ✅ Writing configuration files for the selected homelab services..."
cp templates/nix-config/podman/traefik.nix final-nix-config/etc/nixos/podman/traefik.nix cp templates/nix-config/podman/traefik.nix final-nix-config/etc/nixos/podman/traefik.nix
cp -avu templates/nix-config/configuration.nix final-nix-config/etc/nixos/configuration.nix cp -avu templates/nix-config/configuration.nix final-nix-config/etc/nixos/configuration.nix
envsubst < templates/podman-config/traefik/traefik.yaml > final-nix-config/mnt/config/traefik/traefik.yaml envsubst < templates/podman-config/traefik/traefik.yaml > final-nix-config/mnt/config/traefik/traefik.yaml
@@ -473,7 +481,7 @@ services_generation() {
disks_generation() { disks_generation() {
# Boot disk(s) # Boot disk(s)
echo -e "\n\n✅ Generating disko configuration from templates..." echo -e "\n\n ✅ Generating disko configuration from templates..."
local TEMPLATE_FILE="templates/nix-config/disks/boot-${#BOOT_DISKS_ID[@]}.nix" local TEMPLATE_FILE="templates/nix-config/disks/boot-${#BOOT_DISKS_ID[@]}.nix"
(envsubst < "$TEMPLATE_FILE") > final-nix-config/etc/nixos/disks/disko.nix (envsubst < "$TEMPLATE_FILE") > final-nix-config/etc/nixos/disks/disko.nix
@@ -527,7 +535,7 @@ disks_generation() {
MOUNT_DEPENDENCIES_STOP+=" \${pkgs.util-linux}/bin/umount /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' MOUNT_DEPENDENCIES_STOP+=" \${pkgs.cryptsetup}/bin/cryptsetup close crypted-content-${j}"$'\n'
done 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 j=0
for i in $(seq $CONTENT_DISK_NUMBER $((${#DATA_DISKS_ID[@]} - 1))); do for i in $(seq $CONTENT_DISK_NUMBER $((${#DATA_DISKS_ID[@]} - 1))); do
export j=$((j + 1)) export j=$((j + 1))
@@ -541,7 +549,7 @@ disks_generation() {
MOUNT_DEPENDENCIES_STOP+=" \${pkgs.util-linux}/bin/umount /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' MOUNT_DEPENDENCIES_STOP+=" \${pkgs.cryptsetup}/bin/cryptsetup close crypted-parity-${j}"$'\n'
done done
echo -e "\n✅ Generated $PARITY_DISK_NUMBER parity disk configuration(s)." echo -e "\n ✅ Generated $PARITY_DISK_NUMBER parity disk configuration(s)."
export SNAPRAID_CONTENT_FILES export SNAPRAID_CONTENT_FILES
export SNAPRAID_DATA_DISKS export SNAPRAID_DATA_DISKS
export SNAPRAID_PARITY_FILES export SNAPRAID_PARITY_FILES
@@ -557,7 +565,7 @@ disks_generation() {
} }
EOF EOF
echo -e "\n✅ Final disko configuration created." echo -e "\n ✅ Final disko configuration created."
if [[ -n "${DATA_DISKS_ID[@]}" ]]; then if [[ -n "${DATA_DISKS_ID[@]}" ]]; then
for i in ${!DATA_DISKS_ID[@]}; do for i in ${!DATA_DISKS_ID[@]}; do
@@ -572,7 +580,7 @@ EOF
FORMATTED_DISKS+=" \"$disk\"\n" FORMATTED_DISKS+=" \"$disk\"\n"
done done
sed -i "s|DISK_LIST|${FORMATTED_DISKS}|" final-nix-config/etc/nixos/disks/spindown.nix sed -i "s|DISK_LIST|${FORMATTED_DISKS}|" final-nix-config/etc/nixos/disks/spindown.nix
echo -e "\n✅ Disk spindown configuration created." echo -e "\n ✅ Disk spindown configuration created."
fi fi
fi fi
### Config generation <-- ### Config generation <--
@@ -601,20 +609,20 @@ EOF
PASS="$(xkcdpass)" PASS="$(xkcdpass)"
echo -n "$PASS" > "final-nix-config/etc/secrets/disks/parity-${i}" echo -n "$PASS" > "final-nix-config/etc/secrets/disks/parity-${i}"
chmod 600 "final-nix-config/etc/secrets/disks/parity-${i}" chmod 600 "final-nix-config/etc/secrets/disks/parity-${i}"
ssh_to_host 'bash -s' << EOF ssh_to_host 'bash -s' << EOF
echo "$REMOTE_PASS" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/parity-${i}" echo "$REMOTE_PASS" | sudo -S bash -c "printf '%s' '$PASS' > /etc/secrets/disks/parity-${i}"
EOF EOF
done done
### Generate disk keys <-- ### Generate disk keys <--
echo -e "\n✅ Generating sops-nix 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 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) export SOPS_PUBLIC_KEY=$(age-keygen -y final-nix-config/var/lib/sops-nix/key.txt)
echo -e "\n✅ Generating sops-nix configuration files..." echo -e "\n ✅ Generating sops-nix configuration files..."
envsubst < templates/nix-config/sops-nix/.sops.yaml > final-nix-config/etc/nixos/.sops.yaml envsubst < templates/nix-config/sops-nix/.sops.yaml > final-nix-config/etc/nixos/.sops.yaml
echo -e "\n✅ Encrypting secrets in the correct file..." echo -e "\n ✅ Encrypting secrets in the correct file..."
envsubst < "templates/nix-config/sops-nix/secrets.yaml" \ envsubst < "templates/nix-config/sops-nix/secrets.yaml" \
| sops encrypt --filename-override secrets.yaml \ | sops encrypt --filename-override secrets.yaml \
--input-type yaml --output-type yaml \ --input-type yaml --output-type yaml \
@@ -623,12 +631,12 @@ EOF
} }
nix_generation() { nix_generation() {
echo -e "\n✅ Copying the configuration to the new machine..." echo -e "\n ✅ Copying the configuration to the new machine..."
cp -avu templates/nix-config/flake.nix final-nix-config/etc/nixos/ cp -avu templates/nix-config/flake.nix final-nix-config/etc/nixos/
cp -avu templates/nix-config/misc/* final-nix-config/etc/nixos/misc/ cp -avu templates/nix-config/misc/* final-nix-config/etc/nixos/misc/
echo "${SERVER_OWNER_NAME:-User}" > final-nix-config/etc/numbus-server/owner echo "${SERVER_OWNER_NAME:-User}" > final-nix-config/etc/numbus-server/owner
echo -e "\n✅ Writing correct ips to configuration.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_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|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|TARGET_INTERFACE|${TARGET_INTERFACE}|g" final-nix-config/etc/nixos/misc/networking.nix
@@ -668,7 +676,7 @@ EOF
) )
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "$(gum format <<< "${DISK_RECAP_CONTENT}")" 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; } gum confirm "Proceed with this disk configuration?" || { echo -e "\n\n ❌ Aborting as requested."; exit 1; }
### Disk selection recap <-- ### Disk selection recap <--
### Keys recap <-- ### Keys recap <--
@@ -701,7 +709,7 @@ EOF
) )
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "$(gum format <<< "${KEYS_RECAP_CONTENT}")" 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; } gum confirm "Do you want to deploy NixOS on the target host?" || { echo -e "\n\n ❌ Aborting as requested"; exit 1; }
### Keys recap <-- ### Keys recap <--
} }
@@ -729,59 +737,86 @@ export_configuration() {
echo -e "\n# Podman SETTINGS" >> $CONFIG_EXPORT_FILE echo -e "\n# Podman SETTINGS" >> $CONFIG_EXPORT_FILE
echo "export PODMAN_NETWORKS=\"${PODMAN_NETWORKS}\"" >> $CONFIG_EXPORT_FILE echo "export PODMAN_NETWORKS=\"${PODMAN_NETWORKS}\"" >> $CONFIG_EXPORT_FILE
echo "export TRAEFIK_NETWORKS=\"${TRAEFIK_NETWORKS}\"" >> $CONFIG_EXPORT_FILE echo "export TRAEFIK_NETWORKS=\"${TRAEFIK_NETWORKS}\"" >> $CONFIG_EXPORT_FILE
echo "export TRAEFIK_REF_NETWORKS=\"${TRAEFIK_REF_NETWORKS}\"" >> $CONFIG_EXPORT_FILE echo "export TRAEFIK_REF_NETWORKS=\"${TRAEFIK_REF_NETWORKS}\"" >> $CONFIG_EXPORT_FILE
} }
cloudflare_dns_setup() { cloudflare_dns_setup() {
echo -e "\n\n☁️ Configuring Cloudflare DNS records..." local ZONE_ID && local RECORD_COUNT && local IS_MATCHING
local DNS_RECORDS && local CREATION_STATUS
# 1. Get Zone ID create_records() {
local ZONE_ID local SUBDOMAIN="${1}"
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 "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"${SUBDOMAIN}\",\"content\":\"${HOME_SERVER_IP}\",\"ttl\":1,\"proxied\":false}" | jq -r '.success')
if [[ "${CREATION_STATUS}" == "true" ]]; then
echo " ✅ Successfully create a DNS record for ${SUBDOMAIN}"
else
echo -e " ❌ Failed to create a DNS record for ${SUBDOMAIN}. Check documentation to \n
learn how you can create them manually."
fi
}
erase_records() {
local SUBDOMAIN="${1}"
local DELETION_STATUS
gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "
⚠️ $(gum style --foreground 212 'WARNING:') One or more existing type A DNS records found for \`${SUBDOMAIN}\`.
This script can clear those DNS records for you and create the correct ones needed for the server.
If you are unsure that these records are actually in use, please select \"no\"."
gum confirm "Select \"yes\" to clear ALL EXISTING type A DNS records for this subdomain and automatically create the correct ones." \
|| { 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 "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 "Content-Type: application/json" > /dev/null 2>&1
done
create_records "${SUBDOMAIN}"
}
echo -e "\n\n ☁️ Configuring Cloudflare DNS records..."
# Get Zone ID
ZONE_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${DOMAIN_NAME}" \ 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 ${CF_DNS_API_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.result[0].id') -H "Content-Type: application/json" | jq -r '.result[0].id')
if [[ "${ZONE_ID}" == "null" || -z "${ZONE_ID}" ]]; then if [[ "${ZONE_ID}" == "null" || -z "${ZONE_ID}" ]]; then
echo -e "\n\n⚠️ Could not fetch Zone ID for ${DOMAIN_NAME}. Please check your Cloudflare \"DNS ZONE\" API token" echo -e "\n\n ⚠️ Could not fetch Zone ID for ${DOMAIN_NAME}. Please check your Cloudflare \"DNS ZONE\" API token"
echo "Check out the Numbus-Server documentation to see out to get one." echo "Check the Numbus-Server documentation to learn how to get one."
fi fi
# 2. Iterate services # Check for existing records and create them if non-existent
for service in "${SELECTED_SERVICES[@]}"; do for service_domain in "${SELECTED_SERVICES_DNS[@]}"; do
if [[ "${service}" == "virtualization" ]]; then continue; fi DNS_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=${service_domain}&type=A" \
local SUBDOMAIN="${service}.${DOMAIN_NAME}"
echo -n " - Checking for existing record : ${SUBDOMAIN}..."
# Check existence
local RECORD_ID
RECORD_ID=$(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 ${CF_DNS_API_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.result[0].id') -H "Content-Type: application/json")
if [[ "${RECORD_ID}" != "null" && -n "${RECORD_ID}" ]]; then RECORD_COUNT=$(echo "${DNS_RECORDS}" | jq '.result | length')
RECORD_ID_CONTENT=$(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 "Content-Type: application/json" | jq -r '.result[0].content')
if [[ "${RECORD_ID_CONTENT}" == "${HOME_SERVER_IP}" ]]; then
echo " ✅ Already configured"
else
echo " ⚠️ A DNS record is configured but does not point to the correct IP"
echo "Do you want to update it? It could break past DNS record you defined"
fi
else
echo -n " ⏳ Creating..."
local CREATE_RES
CREATE_RES=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
-H "Authorization: Bearer ${CF_DNS_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"${SUBDOMAIN}\",\"content\":\"${HOME_SERVER_IP}\",\"ttl\":1,\"proxied\":false}" | jq -r '.success')
if [[ "${CREATE_RES}" == "true" ]]; then if [[ "${RECORD_COUNT}" -eq 0 ]]; then
echo " ✅ Created." echo -e "\n ⚠️ No DNS record found for ${service_domain}"
create_records "${service_domain}"
elif [[ "${RECORD_COUNT}" -eq 1 ]]; then
if [[ $(echo "${DNS_RECORDS}" | jq ".result[0].content == \"${HOME_SERVER_IP}\"") == "true" ]]; then
echo -e "\n ✅ DNS record already configured for ${service_domain}"
else else
echo " ❌ Failed." echo -e "\n ⚠️ No DNS record found for ${service_domain}"
erase_records "${service_domain}"
fi fi
elif [[ "${RECORD_COUNT}" -gt 1 ]]; then
erase_records "${service_domain}"
fi fi
done done
} }
+1 -1
View File
@@ -44,7 +44,7 @@
boot.initrd.systemd.enable = true; boot.initrd.systemd.enable = true;
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
boot.swraid.mdadmConf = "MAILADDR ${config.email.toAddress}"; boot.swraid.mdadmConf = "MAILADDR ${config.email.userAddress},${config.email.adminAddress}";
# boot.initrd.systemd.tpm2.enable = true; # boot.initrd.systemd.tpm2.enable = true;
# TPM2 PCR check # TPM2 PCR check
+3 -3
View File
@@ -13,7 +13,7 @@ in
type = lib.types.str; type = lib.types.str;
default = "no-reply@DOMAIN_NAME"; default = "no-reply@DOMAIN_NAME";
}; };
toAddress = lib.mkOption { userAddress = lib.mkOption {
description = "The 'to' address"; description = "The 'to' address";
type = lib.types.str; type = lib.types.str;
default = "EMAIL_ADDRESS"; default = "EMAIL_ADDRESS";
@@ -47,8 +47,8 @@ in
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
environment.etc."aliases".text = '' environment.etc."aliases".text = ''
root: ${config.email.toAddress}, ${config.email.adminAddress} root: ${config.email.userAddress}, ${config.email.adminAddress}
default: ${config.email.toAddress}, ${config.email.adminAddress} default: ${config.email.userAddress}, ${config.email.adminAddress}
''; '';
programs.msmtp = { programs.msmtp = {
+15 -8
View File
@@ -10,6 +10,7 @@ let
TECH_BODY=" TECH_BODY="
SMARTD Alert Details: SMARTD Alert Details:
Server owner: $OWNER_NAME
Device: $SMARTD_DEVICE Device: $SMARTD_DEVICE
Type: $SMARTD_DEVICETYPE Type: $SMARTD_DEVICETYPE
Failure Type: $SMARTD_FAILTYPE Failure Type: $SMARTD_FAILTYPE
@@ -22,17 +23,23 @@ let
# 2. Send Friendly Email to Owner # 2. Send Friendly Email to Owner
OWNER_NAME=$(cat /etc/numbus-server/owner 2>/dev/null || echo "User") OWNER_NAME=$(cat /etc/numbus-server/owner 2>/dev/null || echo "User")
USER_EMAIL="${config.email.toAddress}" USER_EMAIL="${config.email.userAddress}"
FRIENDLY_BODY="Hello $OWNER_NAME, FRIENDLY_BODY="Cher/Chère $OWNER_NAME,
We detected a potential hardware issue on your server ($SMARTD_DEVICE). Votre serveur a automatiquement détecté une panne matérielle de disque dur.
Don't panic! The administrator has been notified and received the technical details. Ce genre de panne est tout à fait normal selon l'âge de votre matériel et n'entraîne
They will contact you if any action is required on your part. dans la grande majorité des cas aucune perte de données grâce au système de
stockage redondant préventif.
Your Numbus Server" Votre administrateur a été notifié de cette panne. Il vous recontactera dans de très
brefs délais afin de procéder au remplacement, si nécessaire, du disque dur défaillant.
printf "Subject: [Alert] Hardware check on your server\n\n$FRIENDLY_BODY" | /run/wrappers/bin/sendmail -t "$USER_EMAIL" Merci de votre confiance,
L'équipe de support,
Numbus-Server."
printf "Subject: [Alerte] Défaillance matérielle sur votre serveur Numbus\n\n$FRIENDLY_BODY" | /run/wrappers/bin/sendmail -t "$USER_EMAIL"
''; '';
in in
{ {
@@ -47,7 +54,7 @@ in
mail = { mail = {
enable = true; enable = true;
sender = config.email.fromAddress; sender = config.email.fromAddress;
recipient = "${config.email.toAddress},${config.email.adminAddress}"; recipient = "${config.email.userAddress},${config.email.adminAddress}";
}; };
}; };
}; };