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
+80 -45
View File
@@ -141,7 +141,7 @@ hardware_detection() {
ssh_to_host 'bash -s' << SSHEND
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_BRAND+=("\${brand}")
else
@@ -149,10 +149,10 @@ for brand in Intel AMD NVIDIA; do
fi
done
ls /dev/dri/ 2>/dev/null | 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"
lspci -nn 2>/dev/null | 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/dri/ > /dev/null 2>&1 | grep -iq "renderD128" && TARGET_GRAPHICS_RENDERER="true" || TARGET_GRAPHICS_RENDERER="false"
lsusb > /dev/null 2>&1 | grep -iq "google" && TARGET_USB_CORAL="true" || TARGET_USB_CORAL="false"
lspci -nn > /dev/null 2>&1 | grep -iq "089a" && TARGET_PCIE_CORAL="true" || TARGET_PCIE_CORAL="false"
ls /dev/serial/by-id/ > /dev/null 2>&1 | grep -i "zigbee" && TARGET_ZIGBEE_DEVICE=\$(ls /dev/serial/by-id/ > /dev/null 2>&1 | grep -i "zigbee" | head -n 1) || TARGET_ZIGBEE_DEVICE=""
TARGET_INTERFACE=\$(ip -4 route show default | awk '{print \$5}' | head -n1)
@@ -186,7 +186,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}') == "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")
else
DISK_HEALTH+=("N/A")
@@ -258,12 +258,20 @@ services_selection() {
local SELECTED_SERVICES_DESCRIPTION=$(gum choose --no-limit --header "Homelab services:" "${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]})
if printf '%s' "${SELECTED_SERVICES_DESCRIPTION}" | grep -iq "${AVAILABLE_SERVICES[${i}]}"; then
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
done
export SELECTED_SERVICES
export SELECTED_SERVICES_DNS
}
disks_selection() {
@@ -291,13 +299,13 @@ disks_selection() {
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}]}")
local GUM_PRINTED_ELEMENTS+=("$GUM_PRINTED_ELEMENT")
local GUM_PRINTED_ELEMENTS+=("${GUM_PRINTED_ELEMENT}")
done
echo ""
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
if printf '%s' "$SELECTED_BOOT_DISK" | grep -iqw "${DISK_NAME[${i}]}"; then
@@ -733,55 +741,82 @@ export_configuration() {
}
cloudflare_dns_setup() {
local ZONE_ID && local RECORD_COUNT && local IS_MATCHING
local DNS_RECORDS && local CREATION_STATUS
create_records() {
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..."
# 1. Get Zone ID
local ZONE_ID
# 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 "Content-Type: application/json" | jq -r '.result[0].id')
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 "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
# 2. Iterate services
for service in "${SELECTED_SERVICES[@]}"; do
if [[ "${service}" == "virtualization" ]]; then continue; fi
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" \
# 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 "Content-Type: application/json" | jq -r '.result[0].id')
-H "Content-Type: application/json")
if [[ "${RECORD_ID}" != "null" && -n "${RECORD_ID}" ]]; then
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"
RECORD_COUNT=$(echo "${DNS_RECORDS}" | jq '.result | length')
if [[ "${RECORD_COUNT}" -eq 0 ]]; then
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
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
echo " ✅ Created."
else
echo " ❌ Failed."
echo -e "\n ⚠️ No DNS record found for ${service_domain}"
erase_records "${service_domain}"
fi
elif [[ "${RECORD_COUNT}" -gt 1 ]]; then
erase_records "${service_domain}"
fi
done
}
+1 -1
View File
@@ -44,7 +44,7 @@
boot.initrd.systemd.enable = true;
boot.loader.systemd-boot.enable = 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;
# TPM2 PCR check
+3 -3
View File
@@ -13,7 +13,7 @@ in
type = lib.types.str;
default = "no-reply@DOMAIN_NAME";
};
toAddress = lib.mkOption {
userAddress = lib.mkOption {
description = "The 'to' address";
type = lib.types.str;
default = "EMAIL_ADDRESS";
@@ -47,8 +47,8 @@ in
config = lib.mkIf cfg.enable {
environment.etc."aliases".text = ''
root: ${config.email.toAddress}, ${config.email.adminAddress}
default: ${config.email.toAddress}, ${config.email.adminAddress}
root: ${config.email.userAddress}, ${config.email.adminAddress}
default: ${config.email.userAddress}, ${config.email.adminAddress}
'';
programs.msmtp = {
+15 -8
View File
@@ -10,6 +10,7 @@ let
TECH_BODY="
SMARTD Alert Details:
Server owner: $OWNER_NAME
Device: $SMARTD_DEVICE
Type: $SMARTD_DEVICETYPE
Failure Type: $SMARTD_FAILTYPE
@@ -22,17 +23,23 @@ let
# 2. Send Friendly Email to Owner
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).
Don't panic! The administrator has been notified and received the technical details.
They will contact you if any action is required on your part.
Votre serveur a automatiquement détecté une panne matérielle de disque dur.
Ce genre de panne est tout à fait normal selon l'âge de votre matériel et n'entraîne
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
{
@@ -47,7 +54,7 @@ in
mail = {
enable = true;
sender = config.email.fromAddress;
recipient = "${config.email.toAddress},${config.email.adminAddress}";
recipient = "${config.email.userAddress},${config.email.adminAddress}";
};
};
};