Compare commits
3 Commits
e2bd366bb3
...
480f188d86
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
480f188d86 | ||
|
|
f80c8a0481 | ||
|
|
d14e25c29f |
104
iso/installer.py
104
iso/installer.py
@@ -930,7 +930,107 @@ class InstallerWindow(Adw.ApplicationWindow):
|
|||||||
path = os.path.join(nixos_dir, entry)
|
path = os.path.join(nixos_dir, entry)
|
||||||
run(["sudo", "rm", "-rf", path])
|
run(["sudo", "rm", "-rf", path])
|
||||||
|
|
||||||
GLib.idle_add(self.push_complete)
|
GLib.idle_add(self.push_create_password)
|
||||||
|
|
||||||
|
# ── Step 5b: Create Password ──────────────────────────────────────────
|
||||||
|
|
||||||
|
def push_create_password(self):
|
||||||
|
outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
||||||
|
|
||||||
|
status = Adw.StatusPage()
|
||||||
|
status.set_title("Create Your Password")
|
||||||
|
status.set_description(
|
||||||
|
"Choose a password for your 'free' user account. "
|
||||||
|
"This will be your login password."
|
||||||
|
)
|
||||||
|
status.set_vexpand(True)
|
||||||
|
|
||||||
|
form_group = Adw.PreferencesGroup()
|
||||||
|
form_group.set_margin_start(40)
|
||||||
|
form_group.set_margin_end(40)
|
||||||
|
|
||||||
|
pw_row = Adw.PasswordEntryRow()
|
||||||
|
pw_row.set_title("Password")
|
||||||
|
form_group.add(pw_row)
|
||||||
|
|
||||||
|
confirm_row = Adw.PasswordEntryRow()
|
||||||
|
confirm_row.set_title("Confirm Password")
|
||||||
|
form_group.add(confirm_row)
|
||||||
|
|
||||||
|
error_lbl = Gtk.Label()
|
||||||
|
error_lbl.set_margin_start(40)
|
||||||
|
error_lbl.set_margin_end(40)
|
||||||
|
error_lbl.set_margin_top(8)
|
||||||
|
error_lbl.set_visible(False)
|
||||||
|
error_lbl.add_css_class("error")
|
||||||
|
|
||||||
|
content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16)
|
||||||
|
content_box.append(status)
|
||||||
|
content_box.append(form_group)
|
||||||
|
content_box.append(error_lbl)
|
||||||
|
outer.append(content_box)
|
||||||
|
|
||||||
|
def on_submit(btn):
|
||||||
|
password = pw_row.get_text()
|
||||||
|
confirm = confirm_row.get_text()
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
error_lbl.set_text("Password cannot be empty.")
|
||||||
|
error_lbl.set_visible(True)
|
||||||
|
return
|
||||||
|
if len(password) < 8:
|
||||||
|
error_lbl.set_text("Password must be at least 8 characters.")
|
||||||
|
error_lbl.set_visible(True)
|
||||||
|
return
|
||||||
|
if password != confirm:
|
||||||
|
error_lbl.set_text("Passwords do not match.")
|
||||||
|
error_lbl.set_visible(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
btn.set_sensitive(False)
|
||||||
|
error_lbl.set_visible(False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
run(["sudo", "mkdir", "-p", "/mnt/var/lib/secrets"])
|
||||||
|
proc = subprocess.run(
|
||||||
|
["sudo", "tee", "/mnt/var/lib/secrets/free-password"],
|
||||||
|
input=password, text=True, capture_output=True
|
||||||
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise RuntimeError(proc.stderr.strip() or "Failed to write password file")
|
||||||
|
run(["sudo", "chmod", "600", "/mnt/var/lib/secrets/free-password"])
|
||||||
|
|
||||||
|
proc = subprocess.run(
|
||||||
|
["sudo", "chroot", "/mnt", "chpasswd"],
|
||||||
|
input=f"free:{password}",
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise RuntimeError(proc.stderr.strip() or "Failed to set password in chroot")
|
||||||
|
|
||||||
|
run(["sudo", "touch", "/mnt/var/lib/sovran-customer-onboarded"])
|
||||||
|
except Exception as e:
|
||||||
|
error_lbl.set_text(str(e))
|
||||||
|
error_lbl.set_visible(True)
|
||||||
|
btn.set_sensitive(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
GLib.idle_add(self.push_complete)
|
||||||
|
|
||||||
|
submit_btn = Gtk.Button(label="Set Password & Continue")
|
||||||
|
submit_btn.add_css_class("suggested-action")
|
||||||
|
submit_btn.add_css_class("pill")
|
||||||
|
submit_btn.connect("clicked", on_submit)
|
||||||
|
|
||||||
|
nav = Gtk.Box()
|
||||||
|
nav.set_margin_bottom(24)
|
||||||
|
nav.set_margin_end(40)
|
||||||
|
nav.set_halign(Gtk.Align.END)
|
||||||
|
nav.append(submit_btn)
|
||||||
|
outer.append(nav)
|
||||||
|
|
||||||
|
self.push_page("Create Password", outer)
|
||||||
|
return False
|
||||||
|
|
||||||
# ── Step 6: Complete ───────────────────────────────────────────────────
|
# ── Step 6: Complete ───────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -954,7 +1054,7 @@ class InstallerWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
pass_row = Adw.ActionRow()
|
pass_row = Adw.ActionRow()
|
||||||
pass_row.set_title("Password")
|
pass_row.set_title("Password")
|
||||||
pass_row.set_subtitle("free")
|
pass_row.set_subtitle("The password you just created")
|
||||||
creds_group.add(pass_row)
|
creds_group.add(pass_row)
|
||||||
|
|
||||||
note_row = Adw.ActionRow()
|
note_row = Adw.ActionRow()
|
||||||
|
|||||||
115
modules/core/factory-seal.nix
Normal file
115
modules/core/factory-seal.nix
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
sovran-factory-seal = pkgs.writeShellScriptBin "sovran-factory-seal" ''
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo "Error: must be run as root." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ⚠ SOVRAN FACTORY SEAL — WARNING ⚠ ║"
|
||||||
|
echo "╠══════════════════════════════════════════════════════════════╣"
|
||||||
|
echo "║ This command will PERMANENTLY DELETE: ║"
|
||||||
|
echo "║ • All generated passwords and secrets ║"
|
||||||
|
echo "║ • LND wallet data (seed words, channels, macaroons) ║"
|
||||||
|
echo "║ • SSH factory login key ║"
|
||||||
|
echo "║ • Application databases (Matrix, Nextcloud, WordPress) ║"
|
||||||
|
echo "║ • Vaultwarden database ║"
|
||||||
|
echo "║ ║"
|
||||||
|
echo "║ After sealing, all credentials will be regenerated fresh ║"
|
||||||
|
echo "║ when the customer boots the device for the first time. ║"
|
||||||
|
echo "║ ║"
|
||||||
|
echo "║ DO NOT run this on a customer's live system. ║"
|
||||||
|
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo -n "Type SEAL to confirm: "
|
||||||
|
read -r CONFIRM
|
||||||
|
if [ "$CONFIRM" != "SEAL" ]; then
|
||||||
|
echo "Aborted." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Sealing system..."
|
||||||
|
|
||||||
|
# ── 1. Delete all generated secrets ──────────────────────────────
|
||||||
|
echo " Wiping secrets..."
|
||||||
|
[ -d /var/lib/secrets ] && find /var/lib/secrets -mindepth 1 -delete || true
|
||||||
|
rm -rf /var/lib/matrix-synapse/registration-secret
|
||||||
|
rm -rf /var/lib/matrix-synapse/db-password
|
||||||
|
rm -rf /var/lib/gnome-remote-desktop/rdp-password
|
||||||
|
rm -rf /var/lib/gnome-remote-desktop/rdp-username
|
||||||
|
rm -rf /var/lib/gnome-remote-desktop/rdp-credentials
|
||||||
|
rm -rf /var/lib/livekit/livekit_keyFile
|
||||||
|
rm -rf /etc/nix-bitcoin-secrets/*
|
||||||
|
|
||||||
|
# ── 2. Wipe LND wallet (seed words, wallet DB, macaroons) ────────
|
||||||
|
echo " Wiping LND wallet data..."
|
||||||
|
rm -rf /var/lib/lnd/*
|
||||||
|
|
||||||
|
# ── 3. Wipe SSH factory key so it regenerates with new passphrase ─
|
||||||
|
echo " Removing SSH factory key..."
|
||||||
|
rm -f /home/free/.ssh/factory_login /home/free/.ssh/factory_login.pub
|
||||||
|
if [ -f /root/.ssh/authorized_keys ]; then
|
||||||
|
sed -i '/factory_login/d' /root/.ssh/authorized_keys
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4. Drop application databases ────────────────────────────────
|
||||||
|
echo " Dropping application databases..."
|
||||||
|
sudo -u postgres psql -c "DROP DATABASE IF EXISTS \"matrix-synapse\";" 2>/dev/null || true
|
||||||
|
sudo -u postgres psql -c "DROP DATABASE IF EXISTS nextclouddb;" 2>/dev/null || true
|
||||||
|
mysql -u root -e "DROP DATABASE IF EXISTS wordpressdb;" 2>/dev/null || true
|
||||||
|
|
||||||
|
# ── 5. Remove application config files (so init services re-run) ─
|
||||||
|
echo " Removing application config files..."
|
||||||
|
rm -rf /var/lib/www/wordpress/wp-config.php
|
||||||
|
rm -rf /var/lib/www/nextcloud/config/config.php
|
||||||
|
|
||||||
|
# ── 6. Wipe Vaultwarden database ──────────────────────────────────
|
||||||
|
echo " Wiping Vaultwarden data..."
|
||||||
|
rm -rf /var/lib/bitwarden_rs/*
|
||||||
|
rm -rf /var/lib/vaultwarden/*
|
||||||
|
|
||||||
|
# ── 7. Set sealed flag and remove onboarded flag ─────────────────
|
||||||
|
echo " Setting sealed flag..."
|
||||||
|
touch /var/lib/sovran-factory-sealed
|
||||||
|
rm -f /var/lib/sovran-customer-onboarded
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "System sealed. Power off now or the system will shut down in 10 seconds."
|
||||||
|
sleep 10
|
||||||
|
poweroff
|
||||||
|
'';
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
environment.systemPackages = [ sovran-factory-seal ];
|
||||||
|
|
||||||
|
# ── Legacy security check: warn existing (pre-seal) machines ───────
|
||||||
|
systemd.services.sovran-legacy-security-check = {
|
||||||
|
description = "Check for legacy (pre-factory-seal) security status";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "local-fs.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
path = [ pkgs.coreutils ];
|
||||||
|
script = ''
|
||||||
|
# If already onboarded or sealed, nothing to do
|
||||||
|
[ -f /var/lib/sovran-customer-onboarded ] && exit 0
|
||||||
|
[ -f /var/lib/sovran-factory-sealed ] && exit 0
|
||||||
|
|
||||||
|
# If secrets exist but no sealed/onboarded flag, this is a legacy machine
|
||||||
|
if [ -f /var/lib/secrets/root-password ]; then
|
||||||
|
mkdir -p /var/lib/sovran
|
||||||
|
echo "legacy" > /var/lib/sovran/security-status
|
||||||
|
echo "This system was deployed before the factory seal feature. Your passwords may be known to the factory. Please change your passwords through the Sovran Hub." > /var/lib/sovran/security-warning
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ let
|
|||||||
{ label = "Free Account — Username"; value = "free"; }
|
{ label = "Free Account — Username"; value = "free"; }
|
||||||
{ label = "Free Account — Password"; file = "/var/lib/secrets/free-password"; }
|
{ label = "Free Account — Password"; file = "/var/lib/secrets/free-password"; }
|
||||||
{ label = "Root Password"; file = "/var/lib/secrets/root-password"; }
|
{ label = "Root Password"; file = "/var/lib/secrets/root-password"; }
|
||||||
{ label = "SSH Local Access"; value = "ssh root@localhost / Passphrase: gosovransystems"; }
|
{ label = "SSH Passphrase"; file = "/var/lib/secrets/ssh-passphrase"; }
|
||||||
]; }
|
]; }
|
||||||
]
|
]
|
||||||
# ── Infrastructure — Caddy + Tor (NOT desktop-only) ────────
|
# ── Infrastructure — Caddy + Tor (NOT desktop-only) ────────
|
||||||
|
|||||||
@@ -12,9 +12,29 @@ lib.mkIf userExists {
|
|||||||
"d /home/${userName}/.ssh 0700 ${userName} users -"
|
"d /home/${userName}/.ssh 0700 ${userName} users -"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
systemd.services.ssh-passphrase-setup = {
|
||||||
|
description = "Generate per-device SSH key passphrase";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
before = [ "factory-ssh-keygen.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
path = [ pkgs.pwgen pkgs.coreutils ];
|
||||||
|
script = ''
|
||||||
|
if [ ! -f "/var/lib/secrets/ssh-passphrase" ]; then
|
||||||
|
mkdir -p /var/lib/secrets
|
||||||
|
pwgen -s 20 1 > /var/lib/secrets/ssh-passphrase
|
||||||
|
chmod 600 /var/lib/secrets/ssh-passphrase
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
systemd.services.factory-ssh-keygen = {
|
systemd.services.factory-ssh-keygen = {
|
||||||
description = "Generate factory SSH key for ${userName} if missing";
|
description = "Generate factory SSH key for ${userName} if missing";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "ssh-passphrase-setup.service" ];
|
||||||
|
requires = [ "ssh-passphrase-setup.service" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
RemainAfterExit = true;
|
RemainAfterExit = true;
|
||||||
@@ -22,7 +42,8 @@ lib.mkIf userExists {
|
|||||||
path = [ pkgs.openssh pkgs.coreutils ];
|
path = [ pkgs.openssh pkgs.coreutils ];
|
||||||
script = ''
|
script = ''
|
||||||
if [ ! -f "${keyPath}" ]; then
|
if [ ! -f "${keyPath}" ]; then
|
||||||
ssh-keygen -q -N "gosovransystems" -t ed25519 -f "${keyPath}"
|
PASSPHRASE=$(cat /var/lib/secrets/ssh-passphrase)
|
||||||
|
ssh-keygen -q -N "$PASSPHRASE" -t ed25519 -f "${keyPath}"
|
||||||
chown ${userName}:users "${keyPath}" "${keyPath}.pub"
|
chown ${userName}:users "${keyPath}" "${keyPath}.pub"
|
||||||
chmod 600 "${keyPath}"
|
chmod 600 "${keyPath}"
|
||||||
chmod 644 "${keyPath}.pub"
|
chmod 644 "${keyPath}.pub"
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
fonts = pkgs.liberation_ttf;
|
|
||||||
|
|
||||||
# ── Helper: change 'free' password and save it ─────────────
|
# ── Helper: change 'free' password and save it ─────────────
|
||||||
change-free-password = pkgs.writeShellScriptBin "change-free-password" ''
|
change-free-password = pkgs.writeShellScriptBin "change-free-password" ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -50,7 +48,7 @@ in
|
|||||||
echo "║ ⚠ Use 'sudo change-free-password' instead. ║"
|
echo "║ ⚠ Use 'sudo change-free-password' instead. ║"
|
||||||
echo "║ ║"
|
echo "║ ║"
|
||||||
echo "║ 'passwd free' only updates /etc/shadow. ║"
|
echo "║ 'passwd free' only updates /etc/shadow. ║"
|
||||||
echo "║ The Hub and Magic Keys PDF will NOT be updated. ║"
|
echo "║ The Hub credentials view will NOT be updated. ║"
|
||||||
echo "╚══════════════════════════════════════════════════════╝"
|
echo "╚══════════════════════════════════════════════════════╝"
|
||||||
echo ""
|
echo ""
|
||||||
return 1
|
return 1
|
||||||
@@ -67,7 +65,7 @@ in
|
|||||||
echo "║ ⚠ Use 'sudo change-free-password' instead. ║"
|
echo "║ ⚠ Use 'sudo change-free-password' instead. ║"
|
||||||
echo "║ ║"
|
echo "║ ║"
|
||||||
echo "║ 'passwd free' only updates /etc/shadow. ║"
|
echo "║ 'passwd free' only updates /etc/shadow. ║"
|
||||||
echo "║ The Hub and Magic Keys PDF will NOT be updated. ║"
|
echo "║ The Hub credentials view will NOT be updated. ║"
|
||||||
echo "╚════════════════════════════<EFBFBD><EFBFBD>═════════════════════════╝"
|
echo "╚════════════════════════════<EFBFBD><EFBFBD>═════════════════════════╝"
|
||||||
echo ""
|
echo ""
|
||||||
return 1
|
return 1
|
||||||
@@ -116,329 +114,4 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# ── 1c. Save Zeus/lndconnect URL for hub credentials ────────
|
|
||||||
systemd.services.zeus-connect-setup = {
|
|
||||||
description = "Save Zeus lndconnect URL";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "lnd.service" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
};
|
|
||||||
path = [ pkgs.coreutils "/run/current-system/sw" ];
|
|
||||||
script = ''
|
|
||||||
SECRET_FILE="/var/lib/secrets/zeus-connect-url"
|
|
||||||
mkdir -p /var/lib/secrets
|
|
||||||
|
|
||||||
URL=""
|
|
||||||
if command -v lndconnect >/dev/null 2>&1; then
|
|
||||||
URL=$(lndconnect --url 2>/dev/null || true)
|
|
||||||
elif command -v lnconnect-clnrest >/dev/null 2>&1; then
|
|
||||||
URL=$(lnconnect-clnrest --url 2>/dev/null || true)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$URL" ]; then
|
|
||||||
echo "$URL" > "$SECRET_FILE"
|
|
||||||
chmod 600 "$SECRET_FILE"
|
|
||||||
echo "Zeus connect URL saved."
|
|
||||||
else
|
|
||||||
echo "No lndconnect URL available yet."
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# ── Refresh Zeus URL periodically (certs/macaroons may rotate)
|
|
||||||
systemd.timers.zeus-connect-setup = {
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnBootSec = "2min";
|
|
||||||
OnUnitActiveSec = "30min";
|
|
||||||
Unit = "zeus-connect-setup.service";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ── 2. Timer: Check every 5 minutes ────────────────────────
|
|
||||||
systemd.timers.generate-credentials-pdf = {
|
|
||||||
description = "Periodically check if Magic Keys PDF needs regenerating";
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnBootSec = "30s";
|
|
||||||
OnUnitActiveSec = "5min";
|
|
||||||
Unit = "generate-credentials-pdf.service";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ── 3. Generate the Magic Keys PDF ─────────────────────────
|
|
||||||
systemd.services.generate-credentials-pdf = {
|
|
||||||
description = "Generate Magic Keys PDF for Sovran_SystemsOS";
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
};
|
|
||||||
|
|
||||||
path = [
|
|
||||||
pkgs.pandoc
|
|
||||||
pkgs.typst
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.qrencode
|
|
||||||
pkgs.gnugrep
|
|
||||||
fonts
|
|
||||||
"/run/current-system/sw"
|
|
||||||
];
|
|
||||||
|
|
||||||
environment = {
|
|
||||||
TYPST_FONT_PATHS = "${fonts}/share/fonts";
|
|
||||||
};
|
|
||||||
|
|
||||||
script = ''
|
|
||||||
DOC_DIR="/home/free/Documents"
|
|
||||||
OUTPUT="$DOC_DIR/Sovran_SystemsOS_Magic_Keys.pdf"
|
|
||||||
WORK_DIR="/tmp/magic_keys_build"
|
|
||||||
FILE="$WORK_DIR/magic_keys.md"
|
|
||||||
HASH_FILE="/var/lib/secrets/.magic-keys-hash"
|
|
||||||
|
|
||||||
FENCE='```'
|
|
||||||
|
|
||||||
# ── Collect all secret sources into a single hash ──
|
|
||||||
SECRET_SOURCES=""
|
|
||||||
for f in \
|
|
||||||
/var/lib/secrets/root-password \
|
|
||||||
/var/lib/secrets/free-password \
|
|
||||||
/etc/nix-bitcoin-secrets/rtl-password \
|
|
||||||
/var/lib/tor/onion/rtl/hostname \
|
|
||||||
/var/lib/tor/onion/electrs/hostname \
|
|
||||||
/var/lib/tor/onion/bitcoind/hostname \
|
|
||||||
/var/lib/secrets/matrix-users \
|
|
||||||
/var/lib/gnome-remote-desktop/rdp-credentials \
|
|
||||||
/var/lib/secrets/nextcloud-admin \
|
|
||||||
/var/lib/secrets/wordpress-admin \
|
|
||||||
/var/lib/secrets/vaultwarden/vaultwarden.env \
|
|
||||||
/var/lib/domains/vaultwarden \
|
|
||||||
/var/lib/domains/btcpayserver \
|
|
||||||
/var/lib/secrets/zeus-connect-url; do
|
|
||||||
if [ -f "$f" ]; then
|
|
||||||
SECRET_SOURCES="$SECRET_SOURCES$(cat "$f")"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Add lndconnect URL to hash sources (changes if certs/macaroons rotate)
|
|
||||||
if command -v lndconnect >/dev/null 2>&1; then
|
|
||||||
SECRET_SOURCES="$SECRET_SOURCES$(lndconnect --url 2>/dev/null || true)"
|
|
||||||
elif command -v lnconnect-clnrest >/dev/null 2>&1; then
|
|
||||||
SECRET_SOURCES="$SECRET_SOURCES$(lnconnect-clnrest --url 2>/dev/null || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
CURRENT_HASH=$(echo -n "$SECRET_SOURCES" | sha256sum | cut -d' ' -f1)
|
|
||||||
OLD_HASH=""
|
|
||||||
if [ -f "$HASH_FILE" ]; then
|
|
||||||
OLD_HASH=$(cat "$HASH_FILE")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Skip if PDF exists and nothing changed ──
|
|
||||||
if [ -f "$OUTPUT" ] && [ "$CURRENT_HASH" = "$OLD_HASH" ]; then
|
|
||||||
echo "No changes detected, skipping PDF regeneration."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Changes detected (or PDF missing), regenerating..."
|
|
||||||
mkdir -p "$DOC_DIR" "$WORK_DIR"
|
|
||||||
|
|
||||||
# ── Read secrets (default to placeholder if missing) ──
|
|
||||||
read_secret() { if [ -f "$1" ]; then cat "$1"; else echo "$2"; fi; }
|
|
||||||
|
|
||||||
ROOT_PASS=$(read_secret /var/lib/secrets/root-password "Generating...")
|
|
||||||
FREE_PASS=$(read_secret /var/lib/secrets/free-password "free")
|
|
||||||
RTL_PASS=$(read_secret /etc/nix-bitcoin-secrets/rtl-password "Not found")
|
|
||||||
RTL_ONION=$(read_secret /var/lib/tor/onion/rtl/hostname "Not generated yet")
|
|
||||||
ELECTRS_ONION=$(read_secret /var/lib/tor/onion/electrs/hostname "Not generated yet")
|
|
||||||
BITCOIN_ONION=$(read_secret /var/lib/tor/onion/bitcoind/hostname "Not generated yet")
|
|
||||||
|
|
||||||
# ── Generate Zeus QR code PNG if lndconnect URL is available ──
|
|
||||||
ZEUS_URL=""
|
|
||||||
HAS_ZEUS_QR=""
|
|
||||||
if command -v lndconnect >/dev/null 2>&1; then
|
|
||||||
ZEUS_URL=$(lndconnect --url 2>/dev/null || true)
|
|
||||||
elif command -v lnconnect-clnrest >/dev/null 2>&1; then
|
|
||||||
ZEUS_URL=$(lnconnect-clnrest --url 2>/dev/null || true)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$ZEUS_URL" ]; then
|
|
||||||
qrencode -o "$WORK_DIR/zeus-qr.png" -s 4 -m 1 -l H "$ZEUS_URL" 2>/dev/null && HAS_ZEUS_QR="1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Build the Markdown document ──
|
|
||||||
cat > "$FILE" << ENDOFFILE
|
|
||||||
---
|
|
||||||
title: "Sovran SystemsOS Magic Keys"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Your Sovran SystemsOS Magic Keys! 🗝️
|
|
||||||
|
|
||||||
Welcome to your new computer! We have built a lot of cool secret forts (services) for you. To get into your forts, you need your magic keys (passwords).
|
|
||||||
|
|
||||||
Here are all of your keys in one place. **Keep this document safe and do not share it with strangers!**
|
|
||||||
|
|
||||||
> **How this document works:** This PDF is automatically generated by your computer. If any of your passwords, services, or connection details change, this document will automatically update itself within a few minutes. You can always find the latest version right here in your Documents folder. If you accidentally delete it, don't worry — your computer will recreate it for you!
|
|
||||||
|
|
||||||
## 🖥️ Your Computer
|
|
||||||
These are the master keys to the actual machine.
|
|
||||||
|
|
||||||
### 1. Main Screen Unlock (The 'free' account)
|
|
||||||
When you turn the computer on, it usually logs you in automatically. However, if the screen goes to sleep, or **if you enable Remote Desktop (RDP)**, you will need this to log in:
|
|
||||||
- **Username:** \`free\`
|
|
||||||
- **Password:** \`$FREE_PASS\`
|
|
||||||
|
|
||||||
🚨 **VERY IMPORTANT:** You MUST write this password down and keep it safe! If you lose it, you will be locked out of your computer!
|
|
||||||
|
|
||||||
### 2. The Big Boss (Root)
|
|
||||||
Sometimes a pop-up box might ask for an Administrator (Root) password to change a setting. We created a super-secret password just for this!
|
|
||||||
- **Root Password:** \`$ROOT_PASS\`
|
|
||||||
|
|
||||||
### 3. The Hacker Terminal (\`ssh root@localhost\`)
|
|
||||||
Because your main account is so safe, you cannot just type normal commands to become the boss. If you open a black terminal box and want to make big changes, you must use your special factory key!
|
|
||||||
|
|
||||||
Type this exact command into the terminal:
|
|
||||||
\`ssh root@localhost\`
|
|
||||||
|
|
||||||
When it asks for a passphrase, type:
|
|
||||||
- **Terminal Password:** \`gosovransystems\`
|
|
||||||
ENDOFFILE
|
|
||||||
|
|
||||||
# --- BITCOIN ECOSYSTEM ---
|
|
||||||
if [ -f "/etc/nix-bitcoin-secrets/rtl-password" ] || [ -f "/var/lib/tor/onion/rtl/hostname" ]; then
|
|
||||||
cat >> "$FILE" << BITCOIN
|
|
||||||
|
|
||||||
## ⚡ Your Bitcoin & Lightning Node
|
|
||||||
Your computer is a real Bitcoin node! It talks to the network secretly using Tor. Here is how to connect your wallet apps to it:
|
|
||||||
|
|
||||||
### 1. Ride The Lightning (RTL)
|
|
||||||
*This is the control panel for your Lightning Node.*
|
|
||||||
Open the **Tor Browser** and go to this website. Use this password to log in:
|
|
||||||
- **Website:** \`http://$RTL_ONION\`
|
|
||||||
- **Password:** \`$RTL_PASS\`
|
|
||||||
|
|
||||||
### 2. Electrs (Your Private Bank Teller)
|
|
||||||
*If you use a wallet app on your phone or computer (like Sparrow or BlueWallet), tell it to connect here so nobody can spy on your money!*
|
|
||||||
- **Tor Address:** \`$ELECTRS_ONION\`
|
|
||||||
- **Port:** \`50001\`
|
|
||||||
|
|
||||||
### 3. Bitcoin Core
|
|
||||||
*This is the heartbeat of your node. It uses this address to talk to other Bitcoiners securely.*
|
|
||||||
- **Tor Address:** \`$BITCOIN_ONION\`
|
|
||||||
BITCOIN
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- ZEUS MOBILE WALLET QR CODE ---
|
|
||||||
if [ "$HAS_ZEUS_QR" = "1" ]; then
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## 📱 Connect Zeus Mobile Wallet" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "Take your Bitcoin Lightning node anywhere in the world! Scan this QR code with the **Zeus** app on your phone to instantly connect your mobile wallet to your Lightning node." >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "1. Download **Zeus** from the App Store or Google Play" >> "$FILE"
|
|
||||||
echo "2. Open Zeus and tap **\"Scan Node Config\"**" >> "$FILE"
|
|
||||||
echo "3. Point your phone's camera at this QR code:" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "{ width=200px }" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "That's it! You're now mobile. Send and receive Bitcoin anywhere in the world, powered by your very own node! ⚡" >> "$FILE"
|
|
||||||
elif [ -n "$ZEUS_URL" ]; then
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## 📱 Connect Zeus Mobile Wallet" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "Take your Bitcoin Lightning node anywhere in the world! Paste this connection URL into the **Zeus** app on your phone:" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "1. Download **Zeus** from the App Store or Google Play" >> "$FILE"
|
|
||||||
echo "2. Open Zeus and tap **\"Scan Node Config\"** then **\"Paste Node Config\"**" >> "$FILE"
|
|
||||||
echo "3. Paste this URL:" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
echo "$ZEUS_URL" >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "That's it! You're now mobile. Send and receive Bitcoin anywhere in the world, powered by your very own node! ⚡" >> "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- MATRIX / ELEMENT ---
|
|
||||||
if [ -f "/var/lib/secrets/matrix-users" ]; then
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## 💬 Your Private Chat (Matrix / Element)" >> "$FILE"
|
|
||||||
echo "This is your very own private messaging app! Log in using an app like Element with these details:" >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
cat /var/lib/secrets/matrix-users >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- GNOME RDP ---
|
|
||||||
if [ -f "/var/lib/gnome-remote-desktop/rdp-credentials" ]; then
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## 🌎 Connect from Far Away (Remote Desktop)" >> "$FILE"
|
|
||||||
echo "This lets you control your computer screen from another device!" >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
cat /var/lib/gnome-remote-desktop/rdp-credentials >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- NEXTCLOUD ---
|
|
||||||
if [ -f "/var/lib/secrets/nextcloud-admin" ]; then
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## ☁️ Your Personal Cloud (Nextcloud)" >> "$FILE"
|
|
||||||
echo "This is like your own private Google Drive!" >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
cat /var/lib/secrets/nextcloud-admin >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- WORDPRESS ---
|
|
||||||
if [ -f "/var/lib/secrets/wordpress-admin" ]; then
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## 📝 Your Website (WordPress)" >> "$FILE"
|
|
||||||
echo "This is your very own website where you can write blogs or make pages." >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
cat /var/lib/secrets/wordpress-admin >> "$FILE"
|
|
||||||
echo "$FENCE" >> "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- VAULTWARDEN ---
|
|
||||||
if [ -f "/var/lib/domains/vaultwarden" ]; then
|
|
||||||
DOMAIN=$(cat /var/lib/domains/vaultwarden)
|
|
||||||
VW_ADMIN_TOKEN="Not found"
|
|
||||||
if [ -f "/var/lib/secrets/vaultwarden/vaultwarden.env" ]; then
|
|
||||||
VW_ADMIN_TOKEN=$(grep -oP 'ADMIN_TOKEN=\K.*' /var/lib/secrets/vaultwarden/vaultwarden.env || echo "Not found")
|
|
||||||
fi
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## 🔐 Your Password Manager (Vaultwarden)" >> "$FILE"
|
|
||||||
echo "This keeps all your other passwords safe! Go to this website to use it:" >> "$FILE"
|
|
||||||
echo "- **Website:** https://$DOMAIN" >> "$FILE"
|
|
||||||
echo "- **Admin Panel:** https://$DOMAIN/admin" >> "$FILE"
|
|
||||||
echo "- **Admin Token:** \`$VW_ADMIN_TOKEN\`" >> "$FILE"
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "*(Create your own account on the main page. Use the Admin Token to access the admin panel and manage your server.)*" >> "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- BTCPAY SERVER ---
|
|
||||||
if [ -f "/var/lib/domains/btcpayserver" ]; then
|
|
||||||
DOMAIN=$(cat /var/lib/domains/btcpayserver)
|
|
||||||
echo "" >> "$FILE"
|
|
||||||
echo "## ₿ Your Bitcoin Store (BTCPay Server)" >> "$FILE"
|
|
||||||
echo "This lets you accept Bitcoin like a real shop!" >> "$FILE"
|
|
||||||
echo "- **Website:** https://$DOMAIN" >> "$FILE"
|
|
||||||
echo "*(You make up your own Admin Password the first time you visit!)*" >> "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Generate PDF (cd into work dir so Typst finds images) ──
|
|
||||||
cd "$WORK_DIR"
|
|
||||||
pandoc magic_keys.md -o "$OUTPUT" --pdf-engine=typst \
|
|
||||||
-V mainfont="Liberation Sans" \
|
|
||||||
-V monofont="Liberation Mono"
|
|
||||||
|
|
||||||
chown free:users "$OUTPUT"
|
|
||||||
|
|
||||||
# ── Save hash so we skip next time if nothing changed ──
|
|
||||||
mkdir -p "$(dirname "$HASH_FILE")"
|
|
||||||
echo "$CURRENT_HASH" > "$HASH_FILE"
|
|
||||||
|
|
||||||
rm -rf "$WORK_DIR"
|
|
||||||
echo "PDF generated successfully."
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
./core/sovran_systemsos-desktop.nix
|
./core/sovran_systemsos-desktop.nix
|
||||||
./core/sshd-localhost.nix
|
./core/sshd-localhost.nix
|
||||||
./core/sovran-hub.nix
|
./core/sovran-hub.nix
|
||||||
|
./core/factory-seal.nix
|
||||||
|
|
||||||
# ── Always on (no flag) ───────────────────────────────────
|
# ── Always on (no flag) ───────────────────────────────────
|
||||||
./php.nix
|
./php.nix
|
||||||
|
|||||||
Reference in New Issue
Block a user