From 29d7eac98187d39ddfcd30773e58d74d86e4716e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Numbus?= Date: Sat, 28 Mar 2026 21:49:24 +0100 Subject: [PATCH] Moving to a web based configurator. Huge changes. Surely a lot of bugfixing to do. --- README.md | 5 +- .../numbus-backup-server.conf | 0 .../numbus-computer.conf | 0 configuration-files/numbus-server.conf | 101 ++ .../numbus-tv.conf | 0 configurator/favicon.ico | Bin 0 -> 25484 bytes configurator/index.html | 1225 +++++++++++++++++ configurator/logo.png | Bin 0 -> 32548 bytes deploy.sh | 517 ++----- .../{ => services}/automatic_ssl_certs.md | 0 .../{ => services}/custom_subdomain.md | 0 .../configuration/services/index.md | 10 +- numbus-server.conf | 119 -- templates/numbus-server/configuration.nix | 19 +- .../numbus-server/custom-configuration.nix | 5 + templates/numbus-server/flake.nix | 12 +- templates/numbus-server/numbus-generated.nix | 10 + templates/numbus-server/sops-nix/secrets.yaml | 8 +- wg0.conf | 1 - 19 files changed, 1469 insertions(+), 563 deletions(-) rename numbus-backup-server.conf => configuration-files/numbus-backup-server.conf (100%) rename numbus-computer.conf => configuration-files/numbus-computer.conf (100%) create mode 100644 configuration-files/numbus-server.conf rename numbus-tv.conf => configuration-files/numbus-tv.conf (100%) create mode 100644 configurator/favicon.ico create mode 100644 configurator/index.html create mode 100644 configurator/logo.png rename docs/numbus-server/configuration/{ => services}/automatic_ssl_certs.md (100%) rename docs/numbus-server/configuration/{ => services}/custom_subdomain.md (100%) delete mode 100644 numbus-server.conf create mode 100644 templates/numbus-server/custom-configuration.nix create mode 100644 templates/numbus-server/numbus-generated.nix delete mode 100644 wg0.conf diff --git a/README.md b/README.md index 83a55dc..838e5a3 100644 --- a/README.md +++ b/README.md @@ -98,10 +98,13 @@ The script will guide you through the setup process, including choosing a deploy #### Desktop-centric features : - **[GNOME](https://www.gnome.org):** A modern, elegant desktop environment. - **[KDE Plasma](https://kde.org):** A full-featured and highly customizable desktop environment. +- **[XFCE](https://xfce.org)**: A super lightweight desktop to breathe new life into old computers. +- **[Wide offering of free and open-source apps](https://flathub.org/en/apps)**: If you need to get something done, there is an app for it. +- **[Windows games compatibility](https://www.protondb.com)**: Most games run on Linux thanks to Proton. #### TV-centric features : - **[KDE Plasma Bigscreen](https://plasma-bigscreen.org):** An open-source TV interface for Linux. - +- **[Web applications](https://flathub.org/en/apps/net.codelogistics.webapps)**: Install websites as apps. ## πŸ”§ Deployment Modes diff --git a/numbus-backup-server.conf b/configuration-files/numbus-backup-server.conf similarity index 100% rename from numbus-backup-server.conf rename to configuration-files/numbus-backup-server.conf diff --git a/numbus-computer.conf b/configuration-files/numbus-computer.conf similarity index 100% rename from numbus-computer.conf rename to configuration-files/numbus-computer.conf diff --git a/configuration-files/numbus-server.conf b/configuration-files/numbus-server.conf new file mode 100644 index 0000000..94ff582 --- /dev/null +++ b/configuration-files/numbus-server.conf @@ -0,0 +1,101 @@ +### MANDATORY SETTINGS ### + +## πŸ“¦ Live target settings +# See docs/numbus-server/configuration/live_target.md +LIVE_TARGET_IP="192.168.1.10" +LIVE_TARGET_PASSWD="example" + + +## βš™οΈ Server settings +# See docs/numbus-server/configuration/server.md +SERVER_LANGUAGE="FR" +SERVER_LOCALE="fr_FR" +SERVER_TIMEZONE="Europe/Paris" +SERVER_OWNER_NAME="yourName" +SERVER_ADMIN_EMAIL="admin@your-domain.com" +SERVER_AUTHORIZED_SSH_PUBKEYS=( "ssh-ed25519 AAAAoefzefpoipoeCEZJCPEACPAcjapjcpajepcjAPJECJPEJAPJAZ yours@yourdomain.com" ) + + +## πŸ“¬ Mail settings +# See docs/numbus-server/configuration/mail.md +SMTP_SERVER_USERNAME="your-address@your-domain.com" +SMTP_SERVER_PASSWORD="emrp raps vzoi vnoe" +SMTP_SERVER_HOST="smtp.yourdomain.com" +SMTP_SERVER_PORT="587" + + +## πŸ›œ Network settings +# See docs/numbus-server/configuration/network.md +NETWORK_SUBNET="192.168.1.0/24" +NETWORK_ROUTER_IP="192.168.1.1" +HOME_SERVER_IP="192.168.1.5" + + +## πŸ› οΈ Services settings +# See docs/numbus-server/configuration/services/index.md +DOMAIN_NAME="yourdomain.com" +SELECTED_DNS_SERVICE="pi-hole" # or SELECTED_DNS_SERVICE="adguard" +SELECTED_WEB_APPLICATIONS=( + "crafty" + "frigate" + "gitea" + "home-assistant" + "homepage" + "immich" + "it-tools" + "jellyfin" + "n8n" + "netbootxyz" + "nextcloud" + "ntfy" + "odoo" + "passbolt" + "uptime-kuma" + "vscodium" +) +SELECTED_SYSTEM_SERVICES=( + "clamav" + "virtualization" +) + + +## 🚦 Traefik settings +# See docs/numbus-server/configuration/services/automatic_ssl_certs.md +CLOUDFLARE_DNS_API_TOKEN="yourToken" + + + +### OPTIONAL SETTINGS ### + +## ⛏️ Crafty settings +# See docs/numbus-server/configuration/services/crafty.md +DYNMAP_ENABLED="false" +WANTED_NUMBER_OF_JAVA_MINECRAFT_SERVERS="1" +WANTED_NUMBER_OF_BEDROCK_MINECRAFT_SERVERS="0" + + +## πŸ“œ Script settings +# See docs/numbus-server/configuration/script.md +VERBOSE="true" + + +## πŸ—Ί Custom subdomains +# See docs/numbus-server/configuration/services/custom_subdomain.md +PI_HOLE_SUBDOMAIN="pi-hole" +ADGUARD_SUBDOMAIN="adguard" +CRAFFY_SUBDOMAIN="crafty" +FRIGATE_SUBDOMAIN="frigate" +GITEA_SUBDOMAIN="gitea" +HOME_ASSISTANT_SUBDOMAIN="home-assistant" +HOMEPAGE_SUBDOMAIN="homepage" +IMMICH_SUBDOMAIN="immich" +IT_TOOLS_SUBDOMAIN="it-tools" +JELLYFIN_SUBDOMAIN="jellyfin" +N8N_SUBDOMAIN="n8n" +NETBOOTXYZ_SUBDOMAIN="netbootxyz" +NEXTCLOUD_SUBDOMAIN="nextcloud" +NTFY_SUBDOMAIN="ntfy" +ODOO_SUBDOMAIN="odoo" +PASSBOLT_SUBDOMAIN="passbolt" +UPTIME_KUMA_SUBDOMAIN="uptime-kuma" +VSCODIUM_SUBDOMAIN="vscodium" \ No newline at end of file diff --git a/numbus-tv.conf b/configuration-files/numbus-tv.conf similarity index 100% rename from numbus-tv.conf rename to configuration-files/numbus-tv.conf diff --git a/configurator/favicon.ico b/configurator/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d7151a8c3c14ee419ae0c78eafcc28c1cf321a5a GIT binary patch literal 25484 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 literal 0 HcmV?d00001 diff --git a/configurator/index.html b/configurator/index.html new file mode 100644 index 0000000..bee35e5 --- /dev/null +++ b/configurator/index.html @@ -0,0 +1,1225 @@ + + + + + + Numbus Configurator + + + + + + + + + + + +
+ + + + + +
+ + +
+ Numbus Logo +

Welcome to Numbus

+

Let's transform your hardware into a powerful, private appliance. We'll guide you through discovery and configuration.

+ +
+ + + +

Privacy First: No data entered here ever leaves your device. This configurator runs entirely locally in your browser and is fully private.

+
+ + +
+ + +
+
+ + +
+
+

Deployment Mode

+

How would you like to set up your device?

+
+
+ + +
+
+ + +
+
+

Device Type

+

Select the personality for your new Numbus machine.

+
+
+ +
+
+ + +
+
+

Live Setup

+

Follow these steps to prepare your hardware for discovery.

+
+
+
+
+
1
+

Create a NixOS Boot ISO and flash it to a USB drive.

+
+
+
2
+
+

In BIOS, enable UEFI, VT-x/SVM, VT-d/IOMMU, TPM 2.0 and Disable Secure Boot.

+
+
+
+
3
+

Boot the device. Type ip a to get the IP, then passwd to set a temporary password.

+
+
+
+
+ + +
+
+ + +
+ +
+
+
+ + +
+
+
+
+
+

Hardware Discovery in Progress

+

We're remotely probing your machine for disks and interfaces. This usually takes 1-2 minutes.

+
+ + +
+
+

Language

+

Set your regional preferences to ensure correct time and language display.

+
+
+
+
+ + +
+

Choose the language for your server's system and primary interfaces.

+ +
+
+
+ + +
+

Defines regional formats for dates, currencies, and numbers (e.g., en_US for USA).

+ +
+
+
+ + +
+

Used to synchronize server logs, backups, and scheduled updates with your local time.

+ +
+
+
+ + +
+
+
+

Users

+

Manage people and their access permissions.

+
+ +
+ +
+ +
+ + +
+ + +
+
+ + +
+ + +
+
+
+ + +
+
+

Mail

+

Configure system notifications and service alerts.

+
+
+
+
+ + +
+

The email address used to send alerts (e.g. system@yourdomain.com).

+ +
+
+
+ + +
+

The password or app-token for the sender email account.

+ +
+
+
+ + +
+

The server address of your email provider (e.g. smtp.gmail.com).

+ +
+
+
+ + +
+

The network port for secure mail transmission. Common: 587 (TLS) or 465 (SSL).

+ +
+
+
+ + +
+
+

Network

+

Target host networking and server IP allocation.

+
+
+
+
+ + +
+

The fixed address of your server on your local network. It should be outside your router's DHCP range.

+ +
+
+
+ + +
+

The address of the device providing internet (the gateway). Usually 192.168.1.1.

+ +
+
+
+ + +
+
+

Remote Access

+

Choose how you will access your server from outside your home.

+
+ +
+
+ + + + +
+
+ +
+ +
+

How to get your token:

+
    +
  1. Create an account at NetBird
  2. +
  3. Verify your email via the link sent to you
  4. +
  5. Navigate to Teams > Service Users
  6. +
  7. Click "Create Service User", name it "numbus-server" and set role to "Network Admin"
  8. +
  9. Click on the new user, then "Add Token". Name it "deployment" and set expiry to 7 days
  10. +
  11. Copy the token and paste it here!
  12. +
+
+
+
+ +
+ + + + + + +
+
+
+ + +
+
+

Security

+

Manage infrastructure access and advanced protection.

+
+ +
+ +
+
+
+ +
+ +
+ One login for everything. Centralized identity management via LLDAP and Authelia. Provides enterprise-grade security and 2FA for all your apps. +
+
+
+ +
+
+ + +
+
+
+ +
+ +
+ Allows you to share links (like Nextcloud folders) with friends who don't have NetBird. Only specific URLs are exposed; everything else remains locked behind the VPN. +
+
+
+ +
+
+
+ +
+
+ + +
+

A secure way to log in without a password. Like a physical key, but digital.

+

Paste here the public keys that are allowed to connect to your server's admin account (one per line).

+ +
+ + +
+
+ +
+

Security Warning: Anyone who possesses one of these public keys AND their corresponding private key will have full administrator access to your server. Protect your private keys as if they were physical keys to your home.

+
+
+ + +
+
+

Services

+

Choose the applications to deploy.

+
+ +
+
+
+ + +
+ +
+ +

DNS Filter

+
+ +
+ +

Web Applications

+
+ +
+ +

System Services

+
+ +
+
+ + +
+

+ + Crafty Control Settings +

+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+ + +
+
+

Configuration Source

+

Link your Git repository containing the numbus.yaml file.

+
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+

Setup Type

+

Are we creating a new server instance or restoring an existing one?

+
+
+ + +
+
+ +

Pull data from an existing numbus-backup-server.

+
+ +
+ +
+ + + +
+
+
+ + +
+
+

Environment Overrides

+

Apply specific tweaks to this local deployment.

+
+

Select settings you wish to override from the Git configuration.

+
+ +
+

tweaks: []

+
+
+
+ + +
+
+
+
+

Deploying Numbus...

+
+
+ Live Terminal Logs + +
+
+

> Initializing nixos-anywhere...

+

> Checking connection to target...

+

> Copying closure to remote...

+
+
+ + +
+
+

Configuration Source

+

Link your Git repository containing the numbus.yaml file.

+
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+

Setup Type

+

Are we creating a new server instance or restoring an existing one?

+
+
+ + +
+
+ +

Pull data from an existing numbus-backup-server.

+
+ +
+ +
+ + + +
+
+
+ + +
+
+

Environment Overrides

+

Apply specific tweaks to this local deployment.

+
+
+

Select settings you wish to override from the Git configuration.

+
+ tweaks: [] +
+
+
+ + +
+
+
+
+

Deploying Numbus...

+
+
+ Live Terminal Logs + +
+
+

[INFO] Starting deployment sequence...

+

[CMD] nixos-anywhere --flake .#numbus-server

+

[STDOUT] Initializing remote connection...

+
+
+
+
+ + +
+
+ +
+

Ready to Deploy

+

Your configuration has been generated successfully. Download the file and place it in your Numbus project root.

+ +
+
+

+                        
+
+
+ + +
+ + + + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/configurator/logo.png b/configurator/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..28581fae8652e470a39818f546fffb7c6b525438 GIT binary patch literal 32548 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 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 +launch_configurator() { + local PORT=8088 + local CONFIG_FILE="numbus.yaml" + local BRIDGE_SCRIPT="configurator/bridge.py" -NECESSARY_BACKUP_SERVER_VARIABLES_LIST=( - #LIVE TARGET SETTINGS - LIVE_TARGET_IP - LIVE_TARGET_PASSWD - #SERVER SETTINGS - SERVER_LANGUAGE - SERVER_LOCALE - SERVER_TIMEZONE - SERVER_OWNER_NAME - SERVER_USER_EMAIL - SERVER_ADMIN_EMAIL - SERVER_AUTHORIZED_SSH_PUBKEYS - # TRAEFIK SETTINGS - TRAEFIK_CLOUDFLARE_TOKEN - # SMTP SETTINGS - SMTP_SERVER_USERNAME - SMTP_SERVER_PASSWORD - SMTP_SERVER_HOST - SMTP_SERVER_PORT - #NETWORK SETTINGS - NETWORK_SUBNET - NETWORK_ROUTER_IP - NETWORK_HOME_SERVER_IP -) + # Create a more robust Python Bridge + cat << EOF > "${BRIDGE_SCRIPT}" +import http.server +import json +import os -OPTIONAL_BACKUP_SERVER_VARIABLES_LIST=( - # SERVICES SETTINGS - SERVICES_DOMAIN_NAME - SERVICES_SELECTED_SYSTEM_PACKAGES - SERVICES_SELECTED_SYSTEM_SERVICES - SERVICES_SELECTED_WEB_APPLICATIONS - SERVIVCES_SELECTED_WEB_APPLICATIONS_SUBDOMAIN -) +class BridgeHandler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + if self.path == '/logs': + self.send_response(200) + self.send_header('Content-type', 'text/plain') + self.send_header('Access-Control-Allow-Origin', '*') + self.end_headers() + if os.path.exists('deploy.log'): + with open('deploy.log', 'r') as f: + lines = f.readlines() + self.wfile.write("".join(lines[-20:]).encode()) + return + return http.server.SimpleHTTPRequestHandler.do_GET(self) -NECESSARY_COMPUTER_VARIABLES_LIST=( - # LIVE TARGET SETTINGS - LIVE_TARGET_IP - LIVE_TARGET_PASSWD - # COMPUTER SETTINGS - COMPUTER_LANGUAGE - COMPUTER_LOCALE - COMPUTER_TIMEZONE - COMPUTER_OWNER_NAME - COMPUTER_USER_EMAIL - COMPUTER_ADMIN_EMAIL - COMPUTER_AUTHORIZED_SSH_PUBKEYS - # USER SETTINGS - USER_ADMINISTRATORS - USER_NORMAL_USERS -) + def do_POST(self): + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + + if self.path == '/discovery': + with open("live_settings.json", "wb") as f: + f.write(post_data) + self.send_response(200) + self.end_headers() + # Signal Bash that discovery data is ready + with open(".discovery_ready", "w") as f: f.write("1") -OPTIONAL_COMPUTER_VARIABLES_LIST=( - # NETWORK SETTINGS - NETWORK_SUBNET - NETWORK_ROUTER_IP - NETWORK_HOME_COMPUTER_IP - # SERVICES SETTINGS - SERVICES_SELECTED_SYSTEM_PACKAGES - SERVICES_SELECTED_DESKTOP_ENVIRONMENT - SERVICE_SELECTED_GNOME_EXTENSIONS - SERVICES_SELECTED_FLATPAK_APPLICATIONS - SERVICES_SELECTED_WEB_APPLICATIONS -) + elif self.path == '/deploy': + with open("${CONFIG_FILE}", "wb") as f: + f.write(post_data) + self.send_response(200) + self.end_headers() + with open(".deploy_signal", "w") as f: f.write("1") -NECESSARY_SERVER_VARIABLES_LIST=( - #LIVE TARGET SETTINGS - LIVE_TARGET_IP - LIVE_TARGET_PASSWD - #SERVER SETTINGS - SERVER_LANGUAGE - SERVER_LOCALE - SERVER_TIMEZONE - SERVER_OWNER_NAME - SERVER_USER_EMAIL - SERVER_ADMIN_EMAIL - SERVER_AUTHORIZED_SSH_PUBKEYS - # TRAEFIK SETTINGS - TRAEFIK_CLOUDFLARE_TOKEN - # SMTP SETTINGS - SMTP_SERVER_USERNAME - SMTP_SERVER_PASSWORD - SMTP_SERVER_HOST - SMTP_SERVER_PORT - #NETWORK SETTINGS - NETWORK_SUBNET - NETWORK_ROUTER_IP - NETWORK_HOME_SERVER_IP - # SERVICES SETTINGS - SERVICES_DOMAIN_NAME - SERVICES_SELECTED_DNS - SERVICES_SELECTED_SYSTEM - SERVICES_SELECTED_WEB_APPLICATIONS -) +os.chdir("configurator") +http.server.HTTPServer(('localhost', ${PORT}), BridgeHandler).serve_forever() +EOF -OPTIONAL_SERVER_VARIABLES_LIST=( - # SERVICES SETTINGS - SELECTED_DNS_SERVICE_SUBDOMAIN - SELECTED_WEB_APPLICATIONS_SUBDOMAIN -) + # Cleanup old signals + rm -f configurator/.discovery_ready configurator/.deploy_signal configurator/live_settings.json configurator/hardware.json -NECESSARY_TV_VARIABLES_LIST=( - #LIVE TARGET SETTINGS - LIVE_TARGET_IP - LIVE_TARGET_PASSWD - #TV SETTINGS - TV_LANGUAGE - TV_LOCALE - TV_TIMEZONE - TV_OWNER_NAME - TV_USER_EMAIL - TV_ADMIN_EMAIL - TV_AUTHORIZED_SSH_PUBKEYS - #NETWORK SETTINGS - NETWORK_SUBNET - NETWORK_ROUTER_IP - NETWORK_HOME_TV_IP -) + echo -e "πŸš€ Launching Numbus Configurator..." + python3 "${BRIDGE_SCRIPT}" > /dev/null 2>&1 & + BRIDGE_PID=$! -OPTIONAL_TV_VARIABLES_LIST=( - # SERVICES SETTINGS - SERVICES_SELECTED_SYSTEM_PACKAGES - SERVICES_SELECTED_FLATPAK_APPLICATIONS - SERVICES_SELECTED_WEB_APPLICATIONS -) - -# Available DNS services -DNS_SERVICES_LIST=( - "pi-hole" - "adguard" -) - -# Available services -WEB_APPLICATIONS_LIST=( - "crafty" - "frigate" - "gitea" - "home-assistant" - "homepage" - "immich" - "it-tools" - "jellyfin" - "n8n" - "netbootxyz" - "nextcloud" - "ntfy" - "odoo" - "passbolt" - "uptime-kuma" - "vscodium" -) - -# Available system services -SYSTEM_SERVICES_LIST=( - "clamav" - "virtualization" -) - -# Services descriptions -DNS_SERVICES_DESCRIPTION=( - "Pi-hole : Simple, fully open network-wide Ad Blocker" - "AdGuard : Feature-rich network-wide Ad Blocker" -) - -WEB_APPLICATIONS_DESCRIPTION=( - "Crafty : A web-based control panel for Minecraft servers" - "Frigate [Home Assistant required] : NVR with real-time local object detection for IP cameras" - "Gitea : Painless self-hosted Git service" - "Home-Assistant : Open source home automation that puts local control and privacy first" - "Homepage : A modern, secure, highly customizable application dashboard" - "Immich : High performance self-hosted photo and video management solution" - "IT-tools : Handy collection of online tools for developers" - "Jellyfin : The Free Software Media System" - "N8n : Workflow automation for technical people" - "netboot.xyz : Network boot various operating system installers and utilities" - "Nextcloud : The most popular self-hosted collaboration platform" - "Ntfy : Send push notifications to your phone or desktop via PUT/POST" - "Odoo : Open Source ERP and CRM" - "Passbolt : Open source password manager for teams" - "Uptime-Kuma : A fancy self-hosted monitoring tool" - "VSCodium : Free/Libre Open Source Software Binaries of VS Code" -) - -SYSTEM_SERVICES_DESCRIPTION=( - "ClamAV : An open-source anti-virus" - "Virtualization : Run Virtual Machines (KVM/QEMU) with Libvirt" -) -### Default settings <-- - -user_input() { - local VAR_NAME="${1}" - local HEADER="${2}" - local PLACEHOLDER="${3}" - local REGEX="${4}" - local ERROR_MSG="${5}" - local SENSITIVE="${6:-false}" - - while true; do - [[ "${SENSITIVE}" == "false" ]] && INPUT_VALUE=$(gum input --placeholder "${PLACEHOLDER}" --header "${HEADER}") - [[ "${SENSITIVE}" == "true" ]] && INPUT_VALUE=$(gum input --password --placeholder "${PLACEHOLDER}" --header "${HEADER}") - - 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 -} - -strictly_necessary_information() { - export IP_REGEX='^([0-9]{1,3}\.){3}[0-9]{1,3}$' - user_input "LIVE_TARGET_IP" " Please provide the IP address of the target host :" "For example : 192.168.1.100" "${IP_REGEX}" "Invalid IP address format." - user_input "LIVE_TARGET_PASSWD" " Please enter the password for '${TARGET_USER}@${LIVE_TARGET_IP}' :" "${LIVE_TARGET_IP}'s password" "" "" "true" -} - -necessary_information() { - # Regex Definitions - 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+/]+.*' - - echo -e "\n\n➑️ This script needs information about the target you want to install NixOS on\n" - # LIVE TARGET SETTINGS - user_input "LIVE_TARGET_IP" " Please provide the IP address of the target host :" "For example : 192.168.1.100" "${IP_REGEX}" "Invalid IP address format." - user_input "LIVE_TARGET_PASSWD" " Please enter the password for '${TARGET_USER}@${LIVE_TARGET_IP}' :" "${LIVE_TARGET_IP}'s password" "" "" "true" - - echo -e "\n\n➑️ Now provide some information about the server you are deploying\n" - # SERVER SETTINGS - user_input "TIMEZONE" " Please provide the wanted timezone :" "For example : Europe/Paris, Europe/Berlin" "" "" - user_input "LANGUAGE" " Please provide the wanted language :" "For example : FR (for french), EN (for english), DE, IT, etc" "" "" - user_input "LOCALE" " Please provide your locale :" "For example : fr_FR for France, de_DE for Germany, en_US for USA or en_GB for Great-Britain, etc" "" "" - user_input "SERVER_OWNER_NAME" " Please provide the name of the owner of this server :" "For example : Steve" "" "" - user_input "SERVER_USER_EMAIL" " Please provide a valid user email address (to stay informed about your server's health) :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format." - user_input "SERVER_ADMIN_EMAIL" " Please provide a valid admin email address (will be used for ACME, and system failures notifications) :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format." - user_input "AUTHORIZED_SSH_PUBLIC_KEY" " Please provide a list of SSH public keys of authorized devices :" "For example : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGhcYDmjMo5YApLkk/3P3HZCnOSzm0uYewNAbxL8Fci8 user@your-pc" "${SSH_KEY_REGEX}" "Invalid SSH key format (must start with ssh-...)." "true" - - echo -e "\n\n➑️ You will access your services via a domain name (e.g. cloud.mydomain.com) and containers need credentials to create those subdomains\n" - # TRAEFIK SETTINGS - user_input "DOMAIN_NAME" " Please provide the domain name (FQDN) your home server will use :" "For example : yourdomain.com" "${DOMAIN_REGEX}" "Invalid domain name format." - user_input "CLOUDFLARE_DNS_API_TOKEN" " Please provide a cloudflare API token with DNS zone permission :" "For example : bA7hdvCOuXGytlNKohi3ZGtlVpf5CHpLuCMiJrE" "" "" "true" - - 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)\n" - # SMTP SETTINGS - user_input "SMTP_SERVER_USERNAME" " Please provide a valid sender email address :" "For example : myemail@gmail.com" "${EMAIL_REGEX}" "Invalid email address format." - user_input "SMTP_SERVER_PASSWORD" " Please provide the password of this email address :" "abcd efgh ijkl mnop" "" "" "true" - user_input "SMTP_SERVER_HOST" " Please provide the SMTP server endpoint :" "For Gmail : smtp.gmail.com" "${DOMAIN_REGEX}" "Invalid domain name format." - user_input "SMTP_SERVER_PORT" " Please provide the smtp TLS port :" "For Gmail : 587" "${PORT_REGEX}" "Invalid port number." - - echo -e "\n\n➑️ This server will connect to your local network and you will configure its IP address\n" - # NETWORK SETTINGS - user_input "NETWORK_SUBNET" " Please provide your network subnet :" "For example 192.168.1.0/24" "${SUBNET_REGEX}" "Invalid subnet format (e.g. 192.168.1.1/24)." - user_input "NETWORK_ROUTER_IP" " Please provide the ip address of your router :" "Most likely 192.168.1.1 or 192.168.1.254" "${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.) :" "For example 192.168.1.5" "${IP_REGEX}" "Invalid IP address format." -} - -import_variables() { - VARIABLES_LIST="${1}" - NECESSARY="${2:-false}" - - echo -e "\n\n➑️ Please choose your configuration file :" - local CONFIG_PATH="$(gum file)" - - source "${CONFIG_PATH}" - local MISSING=false - for VAR in "${VARIABLES_LIST[@]}"; do - if [[ -v "${VAR}" && -n "${!VAR}" ]]; then - gum style "βœ… "${VAR}" imported successfully from the config file" - else - gum style "❌ "${VAR}" is missing or empty" - MISSING=true - fi - done - - if [[ "${MISSING}" == "true" ]]; then - if [[ "${NECESSARY}" = "true" ]]; then - echo -e "\n❌ Please check your configuration file to include all necessary variables" - exit 1 - fi - fi - - if [[ "${DEBUG:-false}" == "true" ]]; then - echo -e "\nβœ… Debugging enabled." - export DIR_COPY_FLAGS="ravu" - export FILES_COPY_FLAGS="avu" - else - export DIR_COPY_FLAGS="rau" - export FILES_COPY_FLAGS="au" - fi + echo -e "➑️ Open your browser at: $(gum style --foreground 212 "http://localhost:${PORT}")" + xdg-open "http://localhost:${PORT}" 2>/dev/null || open "http://localhost:${PORT}" 2>/dev/null || true } hierarchy_preparation() { @@ -359,15 +112,15 @@ ssh_to_host() { hardware_detection() { ### --> Get hardware information - local TMPFILE="/tmp/nixos-installation-hardware-detection-temp-file" + local TMPFILE="/tmp/hw_detection.json" ssh_to_host 'bash -s' << SSHEND +TARGET_GRAPHICS="false" +TARGET_GRAPHICS_BRAND=() for brand in Intel AMD NVIDIA; do if lspci -nn 2>/dev/null | grep -i "vga" | grep -iq "\${brand}"; then TARGET_GRAPHICS="true" TARGET_GRAPHICS_BRAND+=("\${brand}") - else - TARGET_GRAPHICS="false" fi done @@ -420,34 +173,47 @@ for DISK in \$(lsblk -x SIZE -d -n -e 7,11 -o NAME); do done -echo "# Hardware detection results on \$(date)" > "${TMPFILE}" -for var in \ - TARGET_GRAPHICS \ - TARGET_GRAPHICS_BRAND \ - TARGET_GRAPHICS_RENDERER \ - TARGET_USB_CORAL \ - TARGET_PCIE_CORAL \ - TARGET_ZIGBEE_DEVICE \ - TARGET_INTERFACE \ - TARGET_TPM \ - TARGET_TPM_VERSION; do - echo "export \${var}=\${!var}" >> "${TMPFILE}" -done - -for var in \ - DISK_DEVPATH \ - DISK_NAME \ - DISK_TYPE \ - DISK_HEALTH \ - DISK_ID \ - DISK_SIZE; do - declare -p \${var} | sed 's/^declare /declare -g /' >> "${TMPFILE}" -done +# Build organized JSON output for yq +cat << EOF > "\${TMPFILE}" +{ + "graphics": { + "enabled": \${TARGET_GRAPHICS}, + "brands": [ \$(printf '"%s",' "\${TARGET_GRAPHICS_BRAND[@]}" | sed 's/,\$//') ], + "renderer": \${TARGET_GRAPHICS_RENDERER} + }, + "tpu": { + "usb": \${TARGET_USB_CORAL}, + "pcie": \${TARGET_PCIE_CORAL} + }, + "tpm": { + "enabled": \${TARGET_TPM}, + "version": "\${TARGET_TPM_VERSION}" + }, + "zigbee": { + "device": "\${TARGET_ZIGBEE_DEVICE}" + }, + "network": { + "interface": "\${TARGET_INTERFACE}" + }, + "disks": [ +\$( + count=\${#DISK_NAME[@]} + for i in "\${!DISK_NAME[@]}"; do + echo " {\"name\": \"\${DISK_NAME[\$i]}\", \"path\": \"\${DISK_DEVPATH[\$i]}\", \"type\": \"\${DISK_TYPE[\$i]}\", \"health\": \"\${DISK_HEALTH[\$i]}\", \"id\": \"\${DISK_ID[\$i]}\", \"size\": \"\${DISK_SIZE[\$i]}\"}\$( [[ \$i -lt \$((count-1)) ]] && echo ',' )" + done +) + ] +} +EOF SSHEND ### Get hardware information <-- - scp -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${TMPFILE}" "${TMPFILE}" &> /dev/null - source "${TMPFILE}" && rm -rf "${TMPFILE}" + scp -i "final-nix-config/home/numbus-admin/.ssh/id_ed25519" "${TARGET_USER}@${LIVE_TARGET_IP}":"${TMPFILE}" "hardware.json" &> /dev/null + + # Create YAML for NixOS and JSON for the Configurator Website + yq -P '.' hardware.json > hardware.yaml + yq -o=json '.' hardware.yaml > configurator/hardware.json + rm hardware.json ### --> Generate hardware-configuration.nix if ssh_to_host "sudo nixos-generate-config --no-filesystems --show-hardware-config" > final-nix-config/etc/nixos/hardware-configuration.nix; then @@ -1026,32 +792,19 @@ nix_update() { --use-remote-sudo switch --flake final-nix-config/etc/nixos#numbus-server } -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 !!" -} - set -euo pipefail clear -fastfetch --logo nixos --logo-padding-left 4 --structure ' ' gum style --align center --width 80 --foreground 212 " β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ - β–’β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–’β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–’ β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–’β–’β–’β–ˆβ–ˆβ–ˆ - β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ -β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ +β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆ @@ -1062,85 +815,7 @@ gum style --align center --width 80 --foreground 212 " β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’ " + sleep 1 -SELECTED_DEVICE=$(gum choose --header "πŸ“¦ Select the device type to deploy:" \ - "numbus-server" \ - "numbus-backup-server" \ - "numbus-computer" \ - "numbus-tv" \ -) - -SELECTED_MODE=$(gum choose --header "πŸ› οΈ Select the deployment strategy for ${SELECTED_DEVICE}:" \ - "Semi-interactive (recommended - use a config file)" \ - "Interactive (manual input)" \ - "Update and Maintain (existing installation)" \ -) - -if [[ "${SELECTED_MODE}" == "Update and Maintain"* ]]; then - TARGET_USER="numbus-admin" - echo -e "\n➑️ Proceeding with maintenance/update for ${SELECTED_DEVICE}..." - gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 \ - "➑️ Ensure the remote device is powered on and accessible via SSH." - - gum confirm "Ready to proceed?" || { echo "❌ Aborted."; exit 1; } - - strictly_necessary_information - setup_ssh - # Maintain legacy update sequence - more_information_config - folder_tree_generation - nix_generation - nix_update - congrats - -else - TARGET_USER="nixos" - echo -e "\n➑️ Proceeding with new deployment for ${SELECTED_DEVICE}..." - gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 \ - "➑️ On the target host: Boot into the NixOS ISO, launch a console, and set a temporary user password." - - gum confirm "Ready to proceed?" || { echo "❌ Aborted."; exit 1; } - - if [[ "${SELECTED_MODE}" == "Semi-interactive"* ]]; then - import_variables "${VARS_LIST[@]}" "true" - else - strictly_necessary_information - necessary_information - fi - - # Standard Deployment Pipeline - hierarchy_preparation - setup_ssh - hardware_detection - - # Server-specific logic - if [[ "${SELECTED_DEVICE}" == "numbus-server" ]]; then - services_selection - fi - - disks_selection - server_config_generation - network_config_generation - - if [[ "${SELECTED_DEVICE}" == "numbus-server" ]]; then - services_config_generation - fi - - # Mail setup for server-grade devices - if [[ "${SELECTED_DEVICE}" == *"server"* ]]; then - mail_config_generation - fi - - disk_config_generation - keys_generation - sum_up - - if [[ "${SELECTED_DEVICE}" == "numbus-server" ]]; then - cloudflare_dns_setup - fi - - export_configuration - deploy - postrun_action -fi \ No newline at end of file +launch_configurator diff --git a/docs/numbus-server/configuration/automatic_ssl_certs.md b/docs/numbus-server/configuration/services/automatic_ssl_certs.md similarity index 100% rename from docs/numbus-server/configuration/automatic_ssl_certs.md rename to docs/numbus-server/configuration/services/automatic_ssl_certs.md diff --git a/docs/numbus-server/configuration/custom_subdomain.md b/docs/numbus-server/configuration/services/custom_subdomain.md similarity index 100% rename from docs/numbus-server/configuration/custom_subdomain.md rename to docs/numbus-server/configuration/services/custom_subdomain.md diff --git a/docs/numbus-server/configuration/services/index.md b/docs/numbus-server/configuration/services/index.md index 44b8713..d5050b7 100644 --- a/docs/numbus-server/configuration/services/index.md +++ b/docs/numbus-server/configuration/services/index.md @@ -4,11 +4,11 @@ | Variable | Description | Values | Default | | -------- | ----------- | ------ | --------- | +| DOMAIN_NAMEΒ | The domain name that will be used to access the different services.Β | your-domain.comΒ | | | SELECTED_DNS_SERVICE | The DNS service to install (AdBlocking). | ( "pi-hole" ), ( "adguard" ) | ( "pi-hole" ) | | SELECTED_WEB_APPLICATIONS | The list of web applications to install. | ( "nextcloud" ), ( "homepage" jellyfin" "it-tools" "netbootxyz" ), ... [see the full list below](./index.md#web-applications-list) | | | SELECTED_SYSTEM_SERVICES | The list of system services to install. | ( "clamav" ), ( "virtualization" "clamav" ), ... [see the full list below](./index.md#system-services-list) | | -| SELECTED_DNS_SERVICE_[subdomain](../custom_subdomain.md) | Custom [subdomain](../custom_subdomain.md) for the DNS service. | "my-dns" | Will be the name of the service. I.e. pi-hole.your-domain.com or adguard.your-domain.com | -| SELECTED_WEB_APPLICATIONS_[subdomain](../custom_subdomain.md) | Custom [subdomain](../custom_subdomain.md)s for the web applications (must match the order of SELECTED_WEB_APPLICATIONS). | "my-cloud", "my-photos" | Will be the name of the service. I.e. nextcloud.your-domain.com, immich.your-domain.com, ... | + ## Web applications list @@ -37,7 +37,7 @@ This is the list of **all the available apps** that can be enabled on the numbus ## System services list -| Name | Description | Additional settings ? | +| Name | Description | Additional settings | | -------- | ----------- | ------ | -| clamav | Open-source anti-virus software. | No | -| virtualization | Run Virtual Machines (KVM/QEMU). | No | \ No newline at end of file +| clamav | Open-source anti-virus software. | | +| virtualization | Run Virtual Machines (KVM/QEMU). | | \ No newline at end of file diff --git a/numbus-server.conf b/numbus-server.conf deleted file mode 100644 index 537a19a..0000000 --- a/numbus-server.conf +++ /dev/null @@ -1,119 +0,0 @@ -## πŸ“¦ Live target settings -# See docs/numbus-server/configuration/live_target.md -export LIVE_TARGET_IP="192.168.1.10" -export LIVE_TARGET_PASSWD="example" - - -## βš™οΈ Server settings -# See docs/numbus-server/configuration/server.md -export SERVER_LANGUAGE="FR" -export SERVER_LOCALE="fr_FR" -export SERVER_TIMEZONE="Europe/Paris" -export SERVER_OWNER_NAME="yourName" -export SERVER_USER_EMAIL="user@your-domain.com" -export SERVER_ADMIN_EMAIL="admin@your-domain.com" -export SERVER_AUTHORIZED_SSH_PUBKEYS=( "ssh-ed25519 AAAAoefzefpoipoeCEZJCPEACPAcjapjcpajepcjAPJECJPEJAPJAZ yours@yourdomain.com" ) - - -## πŸ“¬ Mail settings -# See docs/numbus-server/configuration/mail.md -export SMTP_SERVER_USERNAME="your-address@your-domain.com" -export SMTP_SERVER_PASSWORD="emrp raps vzoi vnoe" -export SMTP_SERVER_HOST="smtp.yourdomain.com" -export SMTP_SERVER_PORT="587" - - -## 🚦 Traefik settings -# See docs/numbus-server/configuration/services/traefik.md -export CLOUDFLARE_DNS_API_TOKEN="yourToken" - - - - - -## πŸ›œ Network settings -# See docs/numbus-server/configuration/network.md -export NETWORK_SUBNET="192.168.1.0/24" -export NETWORK_ROUTER_IP="192.168.1.1" -export HOME_SERVER_IP="192.168.1.5" - - -## πŸ› οΈ Services settings -# See docs/numbus-server/configuration/services/index.md -export DOMAIN_NAME="yourdomain.com" - - -## DNS service -export SELECTED_DNS_SERVICE=( - "pi-hole" - "adguard" -) - - -## Web applications -export SELECTED_WEB_APPLICATIONS=( - "crafty" - "frigate" - "gitea" - "home-assistant" - "homepage" - "immich" - "it-tools" - "jellyfin" - "n8n" - "netbootxyz" - "nextcloud" - "ntfy" - "odoo" - "passbolt" - "uptime-kuma" - "vscodium" -) - - -## System services -export SELECTED_SYSTEM_SERVICES=( - "clamav" - "virtualization" -) - - -## DNS service subdomain -# See docs/numbus-server/configuration/services/index.md -export SELECTED_DNS_SERVICE_SUBDOMAIN=( - "my-pi-hole-subdomain" # or "my-adguard-subdomain" -) - - -## Web applications subdomain -# ⚠️ The order must strictly match the SELECTED_WEB_APPLICATIONS array above. -export SELECTED_WEB_APPLICATIONS_SUBDOMAIN=( - "my-crafty-subdomain" - "my-frigate-subdomain" - "my-gitea-subdomain" - "my-home-assistant-subdomain" # Example : your Home-assistant URL will be ; https://my-home-assistant-subdomain.yourdomain.com/ - "my-homepage-subdomain" - "my-immich-subdomain" - "my-it-tools-subdomain" - "my-jellyfin-subdomain" # Example : your Jellyfin URL will be ; https://my-jellyfin-subdomain.yourdomain.com/ - "my-n8n-subdomain" - "my-netbootxyz-subdomain" - "my-nextcloud-subdomain" - "my-ntfy-subdomain" - "my-odoo-subdomain" - "my-passbolt-subdomain" - "my-uptime-kuma-subdomain" - "my-vscodium-subdomain" -) - - -## ⛏️ Crafty settings -# See docs/numbus-server/configuration/services/crafty.md -export DYNMAP_ENABLED="false" -export WANTED_NUMBER_OF_JAVA_MINECRAFT_SERVERS="1" -export WANTED_NUMBER_OF_BEDROCK_MINECRAFT_SERVERS="0" - - -## πŸ“œ Script settings -# See docs/numbus-server/configuration/script.md -export VERBOSE="true" \ No newline at end of file diff --git a/templates/numbus-server/configuration.nix b/templates/numbus-server/configuration.nix index a519138..8c1c408 100644 --- a/templates/numbus-server/configuration.nix +++ b/templates/numbus-server/configuration.nix @@ -1,20 +1,17 @@ -{ modulesPath, config, pkgs, inputs, ... }: +# Do NOT edit this file manually. +# Please use the dedicated script : https://gittea.dev/numbus/numbus. +# This could compromise system stability and is not supported by numbus. + +{ config, modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") (modulesPath + "/profiles/qemu-guest.nix") - inputs.sops-nix.nixosModules.sops + ./custom-configuration.nix + ./numbus-generated.nix ]; # System system.stateVersion = "25.11"; - - # Secrets management - sops.defaultSopsFile = ./secrets/secrets.yaml; - sops.age.sshKeyPaths = [ "/home/numbus-admin/.ssh/id_ed25519" ]; - sops.age.keyFile = "/var/lib/sops-nix/key.txt"; - # Secrets - sops.secrets."authorizedSshPublicKeys" = { owner = "numbus-admin"; path = "/home/numbus-admin/.ssh/authorized_keys"; mode = "0600"; }; - sops.secrets."smtpPassword" = { owner = "numbus-admin"; mode = "0600"; }; - sops.secrets."cloudflareDnsApiToken" = { owner = "numbus-admin"; mode = "0600"; }; \ No newline at end of file +} \ No newline at end of file diff --git a/templates/numbus-server/custom-configuration.nix b/templates/numbus-server/custom-configuration.nix new file mode 100644 index 0000000..23e44ad --- /dev/null +++ b/templates/numbus-server/custom-configuration.nix @@ -0,0 +1,5 @@ +# This file is reserved for ADVANCED USERS ONLY. +# Editing could compromise system stability and is not supported by numbus. +# Do NOT set options already managed by numbus. i.e. config.numbus.* and other options (networking, storage, etc.) +# Please use the dedicated script for those options : https://gittea.dev/numbus/numbus. + diff --git a/templates/numbus-server/flake.nix b/templates/numbus-server/flake.nix index 0296512..68454d4 100644 --- a/templates/numbus-server/flake.nix +++ b/templates/numbus-server/flake.nix @@ -1,10 +1,14 @@ +# Do NOT edit this file manually. +# Please use the dedicated script : https://gittea.dev/numbus/numbus. +# This could compromise system stability and is not supported by numbus. + { inputs = { # Core Nixpkgs nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; # Numbus server configuration - numbus.url = "git+https://gittea.dev/numbus/numbus-server"; - numbus.inputs.nixpkgs.follows = "nixpkgs"; + numbus-server.url = "git+https://gittea.dev/numbus/numbus-server"; + numbus-server.inputs.nixpkgs.follows = "nixpkgs"; # Disk-partitioning helper disko.url = "github:nix-community/disko"; disko.inputs.nixpkgs.follows = "nixpkgs"; @@ -16,7 +20,7 @@ autoaspm.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = { self, nixpkgs, numbus, disko, sops-nix, autoaspm, ... }@inputs: let + outputs = { self, nixpkgs, numbus-server, disko, sops-nix, autoaspm, ... }@inputs: let # System definition system = "x86_64-linux"; pkgs = import nixpkgs { @@ -31,7 +35,7 @@ specialArgs = { inherit inputs; }; modules = [ # Numbus server configuration - numbus.nixosModules.numbus + numbus-server.nixosModules.numbus-server # Disk-partitioning helper disko.nixosModules.disko # Secrets handling diff --git a/templates/numbus-server/numbus-generated.nix b/templates/numbus-server/numbus-generated.nix new file mode 100644 index 0000000..4302f12 --- /dev/null +++ b/templates/numbus-server/numbus-generated.nix @@ -0,0 +1,10 @@ +# Do NOT edit this file manually. +# Please use the dedicated script : https://gittea.dev/numbus/numbus. +# This could compromise system stability and is not supported by numbus. + +{ config, pkgs, inputs, ... }: + +{ + imports = [ + inputs.sops-nix.nixosModules.sops + ]; \ No newline at end of file diff --git a/templates/numbus-server/sops-nix/secrets.yaml b/templates/numbus-server/sops-nix/secrets.yaml index 38c83a2..1a2671b 100644 --- a/templates/numbus-server/sops-nix/secrets.yaml +++ b/templates/numbus-server/sops-nix/secrets.yaml @@ -1,4 +1,10 @@ +# SSH authorizedSshPublicKeys: | $SSH_KEYS_FORMATTED + +# SMTP smtpPassword: "$SMTP_SERVER_PASSWORD" -cloudflareDnsApiToken: "$CLOUDFLARE_DNS_API_TOKEN" \ No newline at end of file + +# CLOUDFLARE +cloudflareDnsApiToken: "$CLOUDFLARE_DNS_API_TOKEN" + diff --git a/wg0.conf b/wg0.conf deleted file mode 100644 index d23d6d7..0000000 --- a/wg0.conf +++ /dev/null @@ -1 +0,0 @@ -# Populate this file with a valid WireGuard tunnel configuration if you chose to deploy a numbus-backup-server. \ No newline at end of file