Update. Big refactoring. Some data loss.

This commit is contained in:
Raphael Numbus
2025-11-14 19:36:36 +01:00
commit 300b0cbccb
16 changed files with 1591 additions and 0 deletions
+417
View File
@@ -0,0 +1,417 @@
#!/bin/bash
set -euo pipefail
cat <<EOF
██████ █████ ███ ███████ █████████
░░██████ ░░███ ░░░ ███░░░░░███ ███░░░░░███
░███░███ ░███ ████ █████ █████ ███ ░░███░███ ░░░
░███░░███░███ ░░███ ░░███ ░░███ ░███ ░███░░█████████
░███ ░░██████ ░███ ░░░█████░ ░███ ░███ ░░░░░░░░███
░███ ░░█████ ░███ ███░░░███ ░░███ ███ ███ ░███
█████ ░░█████ █████ █████ █████ ░░░███████░ ░░█████████
░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░░░░░
█████████ █████
███░░░░░███ ░░███
░███ ░███ ████████ █████ ████ █████ ███ █████ ░███████ ██████ ████████ ██████
░███████████ ░░███░░███ ░░███ ░███ ░░███ ░███░░███ ░███░░███ ███░░███░░███░░███ ███░░███
░███░░░░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███████ ░███ ░░░ ░███████
░███ ░███ ░███ ░███ ░███ ░███ ░░███████████ ░███ ░███ ░███░░░ ░███ ░███░░░
█████ █████ ████ █████ ░░███████ ░░████░████ ████ █████░░██████ █████ ░░██████
░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░███ ░░░░ ░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░
███ ░███
░░██████
░░░░░░
EOF
sleep 1
cleanup() {
echo -e "\n 🏗️ Cleaning up before exit..."
rm -rf /home/numbus-admin/.ssh/id_ed25519 /home/numbus-admin/.ssh/id_ed25519.pub
rm -rf /etc/nixos/*
rm -rf /var/lib/sops-nix/
echo -e "\n ✅ Cleanup done."
}
files_generation() {
echo -e "\n\n ✅ Generating new SSH for numbus-admin..."
mkdir -p /home/numbus-admin/.ssh/
ssh-keygen -t ed25519 -C numbus-admin@numbus-server -f /home/numbus-admin/.ssh/id_ed25519 -N "" -q
echo -e "\n\n ✅ Generating sops-nix keys..."
mkdir -p /var/lib/sops-nix/
age-keygen -o /var/lib/sops-nix/key.txt
SOPS_PUBLIC_KEY=$(age-keygen -y /var/lib/sops-nix/key.txt)
echo -e "\n\n ✅ Generating sops-nix configuration files..."
echo """# .sops.yaml
keys:
- &primary $SOPS_PUBLIC_KEY
creation_rules:
- path_regex: secrets/secrets.yaml$
key_groups:
- age:
- *primary""" > .sops.yaml
echo -e "\n\n ✅ Generating secure random database passwords..."
HOME_ASSISTANT_MQTT_USER=$(openssl rand -base64 29 | tr -d "123456789=+/" | cut -c1-10)
HOME_ASSISTANT_MQTT_PASSWORD=$(openssl rand -base64 29 | tr -d "=+/" | cut -c1-64)
PASSBOLT_MYSQL_DATABASE=$(openssl rand -base64 29 | tr -d "123456789=+/" | cut -c1-10)
PASSBOLT_MYSQL_USER=$(openssl rand -base64 29 | tr -d "123456789=+/" | cut -c1-10)
PASSBOLT_MYSQL_PASSWORD=$(openssl rand -base64 29 | tr -d "=+/" | cut -c1-64)
FTLCONF_webserver_api_password=$(openssl rand -base64 29 | tr -d "=+/" | cut -c1-64)
echo -e "\n\n ✅ Encrypting secrets in the correct file..."
mkdir -p secrets/
cd secrets/
echo """ssh-public-keys: $SSH_PUBLIC_KEY
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_MYSQL_DATABASE
PASSBOLT_MYSQL_USER: $PASSBOLT_MYSQL_USER
PASSBOLT_MYSQL_PASSWORD: $PASSBOLT_MYSQL_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_api_password""" | sops encrypt --filename-override secrets.yaml \
--input-type yaml --output-type yaml \
--age $SOPS_PUBLIC_KEY \
--output secrets.yaml
cd ../
echo -e "\n\n ✅ Writing correct disk to disk-config.nix..."
sed -i s+TARGET_DISK+$TARGET_DISK+g disk-config.nix
echo -e "\n\n ✅ Writing correct ips to configuration.nix..."
sed -i s+HOME_SERVER_IP+$HOME_SERVER_IP+g configuration.nix
sed -i s+HOME_ROUTER_IP+$HOME_ROUTER_IP+g configuration.nix
echo -e "\n\n ✅ Copying files to the new installation..."
mkdir -p extra-files/etc/nixos/
mkdir -p extra-files/home/numbus-admin/.ssh/
mkdir -p extra-files/var/lib/sops-nix/
mkdir -p extra-files/mnt/config-storage/docker-data/traefik/config/conf
mkdir -p extra-files/mnt/data-storage/docker-data/nextcloud
mkdir -p extra-files/mnt/data-storage/docker-data/immich
mkdir -p extra-files/mnt/config-storage/docker-data/hass/mqtt/config
mkdir -p extra-files/mnt/config-storage/docker-data/hass/mqtt/data
cp -ravu secrets/ docker/ .sops.yaml configuration.nix disk-config.nix flake.nix hardware-configuration.nix extra-files/etc/nixos/
cp -ravu /home/numbus-admin/.ssh/ extra-files/home/numbus-admin/
cp -ravu /var/lib/sops-nix/key.txt extra-files/var/lib/sops-nix/
echo -e "\n\n ✅ Writing docker configuration files..."
cat <<EOF > extra-files/mnt/config-storage/docker-data/traefik/config/traefik.yaml
global:
checkNewVersion: false
sendAnonymousUsage: false
# - level: [TRACE, DEBUG, INFO, WARN, ERROR, FATAL]
log:
level: ERROR
accesslog: {}
api:
dashboard: true
insecure: true
entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: :443
forwardedHeaders:
trustedIPs:
# Local IPs
- "127.0.0.1/32"
- "10.0.0.0/8"
- "192.168.0.0/16"
- "172.16.0.0/12"
certificatesResolvers:
cloudflare:
acme:
email: ${EMAIL_ADDRESS}
storage: /var/traefik/certs/cloudflare-acme.json
caServer: "https://acme-v02.api.letsencrypt.org/directory"
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "9.9.9.9:53"
serversTransport:
insecureSkipVerify: true
providers:
docker:
exposedByDefault: false
network: traefik_frigate, traefik_hass, traefik_nextcloud, traefik_passbolt, traefik_pihole
file:
directory: "/etc/traefik/conf/"
watch: true
EOF
cat <<EOF > extra-files/mnt/config-storage/docker-data/traefik/config/conf/nextcloud.yaml
http:
routers:
nextcloud:
rule: "Host(\`nextcloud.${DOMAIN_NAME}\`)"
entrypoints:
- "websecure"
service: nextcloud
middlewares:
- nextcloud-chain
tls:
certresolver: "cloudflare"
services:
nextcloud:
loadBalancer:
servers:
- url: "http://nextcloud-aio-apache:11000"
middlewares:
nextcloud-secure-headers:
headers:
hostsProxyHeaders:
- "X-Forwarded-Host"
referrerPolicy: "same-origin"
BrowserXssFilter: true
ContentTypeNosniff: true
ForceSTSHeader: true
STSIncludeSubdomains: true
STSPreload: true
STSSeconds: 315360000
https-redirect:
redirectscheme:
scheme: https
nextcloud-chain:
chain:
middlewares:
- https-redirect
- nextcloud-secure-headers
EOF
cat <<'EOF' > extra-files/mnt/config-storage/docker-data/traefik/config/conf/headers.yaml
http:
middlewares:
passbolt:
headers:
FrameDeny: true
AccessControlAllowMethods: 'GET,OPTIONS,PUT'
AccessControlAllowOriginList:
- origin-list-or-null
AccessControlMaxAge: 100
AddVaryHeader: true
BrowserXssFilter: true
ContentTypeNosniff: true
ForceSTSHeader: true
STSIncludeSubdomains: true
STSPreload: true
ContentSecurityPolicy: default-src 'self' 'unsafe-inline'
CustomFrameOptionsValue: SAMEORIGIN
ReferrerPolicy: same-origin
PermissionsPolicy: vibrate 'self'
STSSeconds: 315360000
EOF
cat <<'EOF' > extra-files/mnt/config-storage/docker-data/traefik/config/conf/tls.yaml
tls:
options:
default:
minVersion: VersionTLS12
sniStrict: true
curvePreferences:
- CurveP521
- CurveP384
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
EOF
cat <<EOF > extra-files/mnt/config-storage/docker-data/hass/mqtt/config/mosquitto.conf
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
listener 1883
## Authentication ##
allow_anonymous false
password_file /mosquitto/config/password.txt
EOF
touch extra-files/mnt/config-storage/docker-data/hass/mqtt/config/password.txt
chmod 0700 extra-files/mnt/config-storage/docker-data/hass/mqtt/config/password.txt
nix shell nixpkgs#mosquitto -c mosquitto_passwd -b extra-files/mnt/config-storage/docker-data/hass/mqtt/config/password.txt $HOME_ASSISTANT_MQTT_USER $HOME_ASSISTANT_MQTT_PASSWORD
}
deploy() {
echo -e "\n\n 🔄 Deploying to the remote server..."
nix run github:nix-community/nixos-anywhere -- \
--generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
--flake .#numbus-server \
--extra-files "extra-files/" \
--chown "/home/numbus-admin/" 1000:1000 \
--target-host $TARGET_USER@$TARGET_HOST
echo -e "\n\n ✅ Installation successfull !!"
sleep 1
}
nixos_deployment() {
echo -e "\n\n ➡️ On the target host : start the computer and boot into the NixOS iso.\n Launch a console and set up a new user password."
echo -e " Type 'done' when you have finished."
read -r SETUP_ANSWER
if [[ "$SETUP_ANSWER" == "done" ]]; then
:
else
echo "Aborting you did not type 'done'."
exit 1
fi
#TARGET SETTINGS
echo -e "\n\n ➡️ Please provide the IP address of the target host :"
read -r TARGET_HOST
echo -e "\n\n ➡️ Please provide the disk you want to install NixOS on (i.e. /dev/vda, /dev/sda, /dev/nvme0n1...) :"
read -r TARGET_DISK
echo -e "\n\n ➡️ Does the target server has graphics ? (integrated or discrete) :"
read -r TARGET_GRAPHICS
echo -e "\n\n ➡️ Please provide the public SSH key of an authorized device :"
read -r SSH_PUBLIC_KEY
# TRAEFIK SETTINGS
echo -e "\n\n ➡️ Please provide the domain name (FQDN) your home server will use (i.e. yourdomain.com):"
read -r DOMAIN_NAME
echo -e "\n\n ➡️ Please provide a valid email address (will be used for ACME, and your services) :"
read -r EMAIL_ADDRESS
echo -e "\n\n ➡️ Please provide a cloudflare API token with DNS zone permission :"
read -r CF_DNS_API_TOKEN
#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 (Gmail for example)."
echo -e " Please provide a valid sender email address :"
read -r SENDER_EMAIL_ADDRESS
echo -e "\n\n ➡️ Please provide the password of this email address :"
read -r SENDER_EMAIL_ADDRESS_PASSWORD
echo -e "\n\n ➡️ Please provide the smtp endpoint (for gmail : smtp.gmail.com) :"
read -r SENDER_EMAIL_DOMAIN
echo -e "\n\n ➡️ Please provide the smtp TLS port (for gmail : 587) :"
read -r SENDER_EMAIL_PORT
#NETWORK SETTINGS
echo -e "\n\n ➡️ Please provide your home network subnet (i.e. 192.168.1.1/24) :"
read -r HOME_ROUTER_SUBNET
echo -e "\n\n ➡️ Please provide the ip address of your router (i.e. 192.168.1.1) :"
read -r HOME_ROUTER_IP
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\n range that is not in use. 192.168.1.5 for example.) :"
read -r HOME_SERVER_IP
files_generation
deploy
}
nixos_deployment_with_config() {
echo -e "\n\n ➡️ On the target host : start the computer and boot into the NixOS iso.\n Launch a console and set up a new user password."
echo -e " Type 'done' when you have finished."
read -r SETUP_ANSWER
if [[ "$SETUP_ANSWER" == "done" ]]; then
:
else
echo "Aborting you did not type 'done'."
exit 1
fi
echo -e "\n\n ➡️ Please provide the path to a config file :"
read -rp "Enter the full path to the config file: " CONFIG_PATH
CONFIG_PATH=$(realpath -m "$CONFIG_PATH")
if [[ ! -f "$CONFIG_PATH" ]]; then
echo "Error: '$CONFIG_PATH' does not exist or is not a regular file."
exit 1
elif [[ ! -r "$CONFIG_PATH" ]]; then
echo "Error: '$CONFIG_PATH' is not readable."
exit 1
fi
if grep -qE '^[[:space:]]*[^[:space:]#][^=]*=' "$CONFIG_PATH"; then
source "$CONFIG_PATH"
else
echo "Config contains unsupported syntax."
exit 1
fi
REQUIRED_VARS=(TARGET_HOST TARGET_DISK 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
if [[ -v $VAR && -n ${!VAR} ]]; then
echo -e "\n\n ✅ $VAR imported successfully from the config file"
sleep 0.1
else
echo "\n\n ❌ $VAR is missing or empty"
sleep 0.1
MISSING=1
fi
done
if [[ "$MISSING" == "1" ]]; then
exit 1
fi
files_generation
deploy
}
trap cleanup EXIT
echo -e "\n\n Please choose an action (i.e. 1, 2 or 3) :\n"
echo -e " - [1] 🌐 Deploy NixOS on a remote machine"
echo -e " - [2] 💽 Deploy NixOS on a remote machine with a file configuration"
echo -e " - [3] 🛠️ Update a NixOS remote machine"
read -r ACTION_ANSWER
if [[ "$ACTION_ANSWER" == "1" ]]; then
echo -e "\n ➡️ Proceeding with deployment…"
TARGET_USER="nixos"
nixos_deployment
elif [[ "$ACTION_ANSWER" == "2" ]]; then
echo -e "\n ➡️ Proceeding with deployment using a config file…"
TARGET_USER="nixos"
nixos_deployment_with_config
elif [[ "$ACTION_ANSWER" == "3" ]]; then
echo -e "\n ➡️ Proceeding with update…"
TARGET_USER="numbus-admin"
nixos_deployment_with_config
else
echo "Aborting you did not type '1, 2 or 3'."
exit 1
fi