initial retooling
This commit is contained in:
24
modules/Sovran_SystemsOS_File_Fixes_And_New_Services.nix
Executable file
24
modules/Sovran_SystemsOS_File_Fixes_And_New_Services.nix
Executable file
@@ -0,0 +1,24 @@
|
||||
{config, pkgs, lib, ...}:
|
||||
|
||||
{
|
||||
|
||||
systemd.services.Sovran_SystemsOS_File_Fixes_And_New_Services = {
|
||||
|
||||
unitConfig = {
|
||||
After = "btcpayserver.service";
|
||||
Requires = "network-online.target";
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
ExecStartPre= "/run/current-system/sw/bin/sleep 30";
|
||||
ExecStart = "/run/current-system/sw/bin/wget https://git.sovransystems.com/Sovran_Systems/Sovran_SystemsOS/raw/branch/main/file_fixes_and_new_services/Sovran_SystemsOS_File_Fixes_And_New_Services.sh -O /home/free/Downloads/Sovran_SystemsOS_File_Fixes_And_New_Services.sh ; /run/current-system/sw/bin/bash /home/free/Downloads/Sovran_SystemsOS_File_Fixes_And_New_Services.sh";
|
||||
RemainAfterExit = "yes";
|
||||
User = "root";
|
||||
Type = "oneshot";
|
||||
};
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
23
modules/bip110.nix
Executable file
23
modules/bip110.nix
Executable file
@@ -0,0 +1,23 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sovran_systemsOS;
|
||||
in
|
||||
{
|
||||
options.sovran_systemsOS.packages.bip110 = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.package;
|
||||
default = null;
|
||||
description = "BIP110 Bitcoin package";
|
||||
};
|
||||
|
||||
config = lib.mkIf (
|
||||
cfg.features.bip110 &&
|
||||
cfg.packages.bip110 != null
|
||||
) {
|
||||
services.bitcoind.package = lib.mkForce cfg.packages.bip110;
|
||||
|
||||
environment.systemPackages = [
|
||||
cfg.packages.bip110
|
||||
];
|
||||
};
|
||||
}
|
||||
7
modules/bitcoin-core.nix
Executable file
7
modules/bitcoin-core.nix
Executable file
@@ -0,0 +1,7 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
lib.mkIf config.sovran_systemsOS.features.bitcoin-core {
|
||||
|
||||
services.bitcoind.package = lib.mkForce config.nix-bitcoin.pkgs.bitcoind;
|
||||
|
||||
}
|
||||
95
modules/bitcoinecosystem.nix
Executable file
95
modules/bitcoinecosystem.nix
Executable file
@@ -0,0 +1,95 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
lib.mkIf config.sovran_systemsOS.features.bitcoin {
|
||||
|
||||
## Bitcoind
|
||||
|
||||
services.bitcoind = {
|
||||
enable = true;
|
||||
package = config.nix-bitcoin.pkgs.bitcoind-knots;
|
||||
dataDir = "/run/media/Second_Drive/BTCEcoandBackup/Bitcoin_Node";
|
||||
txindex = true;
|
||||
tor.proxy = true;
|
||||
tor.enforce = true;
|
||||
disablewallet = true;
|
||||
extraConfig = ''
|
||||
peerbloomfilters=1
|
||||
server=1
|
||||
'';
|
||||
};
|
||||
|
||||
nix-bitcoin.onionServices.bitcoind.enable = true;
|
||||
nix-bitcoin.onionServices.electrs.enable = true;
|
||||
nix-bitcoin.onionServices.rtl.enable = true;
|
||||
|
||||
|
||||
## Electrs
|
||||
|
||||
services.electrs = {
|
||||
enable = true;
|
||||
tor.enforce = true;
|
||||
dataDir = "/run/media/Second_Drive/BTCEcoandBackup/Electrs_Data";
|
||||
};
|
||||
|
||||
|
||||
## LND
|
||||
|
||||
services.lnd = {
|
||||
enable = true;
|
||||
tor.enforce = true;
|
||||
tor.proxy = true;
|
||||
extraConfig = ''
|
||||
protocol.option-scid-alias=true
|
||||
'';
|
||||
};
|
||||
|
||||
nix-bitcoin.onionServices.lnd.public = true;
|
||||
|
||||
|
||||
## LNDconnect
|
||||
|
||||
services.lnd.lndconnect = {
|
||||
enable = true;
|
||||
onion = true;
|
||||
};
|
||||
|
||||
|
||||
## RTL
|
||||
|
||||
services.rtl = {
|
||||
enable = true;
|
||||
tor.enforce = true;
|
||||
port = 3050;
|
||||
nightTheme = true;
|
||||
nodes = {
|
||||
lnd = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
## BTCpayserver
|
||||
|
||||
services.btcpayserver = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
services.btcpayserver.lightningBackend = "lnd";
|
||||
|
||||
|
||||
## System
|
||||
|
||||
nix-bitcoin.generateSecrets = true;
|
||||
|
||||
nix-bitcoin.nodeinfo.enable = true;
|
||||
|
||||
nix-bitcoin.operator = {
|
||||
enable = true;
|
||||
name = "free";
|
||||
};
|
||||
|
||||
nix-bitcoin.useVersionLockedPkgs = false;
|
||||
|
||||
}
|
||||
108
modules/core/caddy.nix
Normal file
108
modules/core/caddy.nix
Normal file
@@ -0,0 +1,108 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
user = "caddy";
|
||||
group = "root";
|
||||
configFile = "/run/caddy/Caddyfile";
|
||||
};
|
||||
|
||||
systemd.services.caddy-generate-config = {
|
||||
description = "Generate Caddyfile from /var/lib/domains at runtime";
|
||||
before = [ "caddy.service" ];
|
||||
requiredBy = [ "caddy.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
RuntimeDirectory = "caddy";
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
MATRIX=$(cat /var/lib/domains/matrix)
|
||||
WORDPRESS=$(cat /var/lib/domains/wordpress)
|
||||
NEXTCLOUD=$(cat /var/lib/domains/nextcloud)
|
||||
BTCPAY=$(cat /var/lib/domains/btcpayserver)
|
||||
VAULTWARDEN=$(cat /var/lib/domains/vaultwarden)
|
||||
HAVEN=$(cat /var/lib/domains/haven)
|
||||
ACME_EMAIL=$(cat /var/lib/domains/sslemail)
|
||||
|
||||
# Start with global config
|
||||
cat > /run/caddy/Caddyfile <<EOF
|
||||
{
|
||||
email $ACME_EMAIL
|
||||
}
|
||||
EOF
|
||||
|
||||
# If element-calling is enabled, it wrote a snippet with
|
||||
# enhanced Matrix vhosts (.well-known, element-calling routes)
|
||||
if [ -f /run/caddy/element-calling.snippet ]; then
|
||||
cat /run/caddy/element-calling.snippet >> /run/caddy/Caddyfile
|
||||
else
|
||||
# Fallback: basic Matrix vhosts without element-calling
|
||||
cat >> /run/caddy/Caddyfile <<EOF
|
||||
|
||||
$MATRIX {
|
||||
reverse_proxy /_matrix/* http://localhost:8008
|
||||
reverse_proxy /_synapse/client/* http://localhost:8008
|
||||
}
|
||||
|
||||
$MATRIX:8448 {
|
||||
reverse_proxy http://localhost:8008
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Append remaining vhosts
|
||||
cat >> /run/caddy/Caddyfile <<EOF
|
||||
|
||||
$WORDPRESS {
|
||||
encode gzip zstd
|
||||
root * /var/lib/www/wordpress
|
||||
php_fastcgi unix//run/phpfpm/mypool.sock
|
||||
file_server browse
|
||||
}
|
||||
|
||||
$NEXTCLOUD {
|
||||
encode gzip zstd
|
||||
root * /var/lib/www/nextcloud
|
||||
php_fastcgi unix//run/phpfpm/mypool.sock {
|
||||
trusted_proxies private_ranges
|
||||
}
|
||||
file_server
|
||||
redir /.well-known/carddav /remote.php/dav/ 301
|
||||
redir /.well-known/caldav /remote.php/dav/ 301
|
||||
header {
|
||||
Strict-Transport-Security max-age=31536000;
|
||||
}
|
||||
}
|
||||
|
||||
$BTCPAY {
|
||||
reverse_proxy http://localhost:23000
|
||||
encode gzip zstd
|
||||
}
|
||||
|
||||
$VAULTWARDEN {
|
||||
reverse_proxy http://localhost:8777
|
||||
encode gzip zstd
|
||||
}
|
||||
|
||||
$HAVEN {
|
||||
reverse_proxy localhost:3355 {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
transport http {
|
||||
versions 1.1
|
||||
}
|
||||
}
|
||||
request_body {
|
||||
max_size 100MB
|
||||
}
|
||||
}
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
}
|
||||
68
modules/core/njalla-ddns.nix
Normal file
68
modules/core/njalla-ddns.nix
Normal file
@@ -0,0 +1,68 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
# The cron job scans /var/lib/njalla/hooks.d/ for DDNS URLs
|
||||
systemd.services.njalla-ddns = {
|
||||
description = "Njalla Dynamic DNS Updater";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
IP=$(${pkgs.dig}/bin/dig @resolver4.opendns.com myip.opendns.com +short -4)
|
||||
|
||||
if [ -z "$IP" ]; then
|
||||
echo "Failed to resolve external IP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Only update if IP changed
|
||||
LAST_IP_FILE="/var/lib/njalla/.last_ip"
|
||||
LAST_IP=""
|
||||
[ -f "$LAST_IP_FILE" ] && LAST_IP=$(cat "$LAST_IP_FILE")
|
||||
|
||||
if [ "$IP" = "$LAST_IP" ]; then
|
||||
echo "IP unchanged ($IP), skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -n "$IP" > "$LAST_IP_FILE"
|
||||
echo "IP changed to $IP, updating DNS records..."
|
||||
|
||||
# Update external_ip secret
|
||||
echo -n "$IP" > /var/lib/secrets/external_ip
|
||||
|
||||
# Process each DDNS hook
|
||||
HOOKS_DIR="/var/lib/njalla/hooks.d"
|
||||
mkdir -p "$HOOKS_DIR"
|
||||
|
||||
for hook in "$HOOKS_DIR"/*; do
|
||||
[ -f "$hook" ] || continue
|
||||
DDNS_URL=$(cat "$hook")
|
||||
SERVICE=$(basename "$hook")
|
||||
echo "Updating $SERVICE..."
|
||||
${pkgs.curl}/bin/curl -s "''${DDNS_URL}''${IP}" || echo "Failed: $SERVICE"
|
||||
done
|
||||
|
||||
echo "Done."
|
||||
'';
|
||||
};
|
||||
|
||||
# Run every 15 minutes
|
||||
systemd.timers.njalla-ddns = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = "*:0/15";
|
||||
Persistent = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Ensure directory exists
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/njalla 0700 root root -"
|
||||
"d /var/lib/njalla/hooks.d 0700 root root -"
|
||||
];
|
||||
}
|
||||
37
modules/core/role-logic.nix
Executable file
37
modules/core/role-logic.nix
Executable file
@@ -0,0 +1,37 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
config = lib.mkMerge [
|
||||
|
||||
# Server-Desktop Role most services enabled
|
||||
(lib.mkIf config.sovran_systemsOS.roles.server-desktop {
|
||||
sovran_systemsOS.features = {
|
||||
synapse = true;
|
||||
bitcoin = true;
|
||||
coturn = true;
|
||||
vaultwarden = true;
|
||||
haven = false;
|
||||
mempool = false;
|
||||
bip110 = false;
|
||||
element-calling = false;
|
||||
bitcoin-core = false;
|
||||
rdp = false;
|
||||
};
|
||||
})
|
||||
|
||||
# Desktop role
|
||||
(lib.mkIf config.sovran_systemsOS.roles.desktop {
|
||||
services.xserver.enable = true;
|
||||
services.desktopManager.gnome.enable = true;
|
||||
})
|
||||
|
||||
# Bitcoin node role
|
||||
(lib.mkIf config.sovran_systemsOS.roles.node {
|
||||
sovran_systemsOS.features = {
|
||||
bitcoin = true;
|
||||
bip110 = false;
|
||||
};
|
||||
})
|
||||
|
||||
];
|
||||
}
|
||||
33
modules/core/roles.nix
Executable file
33
modules/core/roles.nix
Executable file
@@ -0,0 +1,33 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
options.sovran_systemsOS = {
|
||||
roles = {
|
||||
server-desktop = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = !config.sovran_systemsOS.roles.desktop && !config.sovran_systemsOS.roles.node;
|
||||
};
|
||||
desktop = lib.mkEnableOption "Desktop Role";
|
||||
node = lib.mkEnableOption "Bitcoin Node Only Role";
|
||||
};
|
||||
|
||||
features = {
|
||||
coturn = lib.mkEnableOption "TURN server";
|
||||
synapse = lib.mkEnableOption "Matrix Synapse";
|
||||
bitcoin = lib.mkEnableOption "Bitcoin Ecosystem";
|
||||
vaultwarden = lib.mkEnableOption "Vaultwarden";
|
||||
haven = lib.mkEnableOption "Haven NOSTR relay";
|
||||
bip110 = lib.mkEnableOption "BIP-110 Bitcoin Better Money";
|
||||
mempool = lib.mkEnableOption "Bitcoin Mempool Explorer";
|
||||
element-calling = lib.mkEnableOption "Element Video and Audio Calling";
|
||||
bitcoin-core = lib.mkEnableOption "Bitcoin Core";
|
||||
rdp = lib.mkEnableOption "Gnome Remote Desktop";
|
||||
};
|
||||
|
||||
nostr_npub = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Nostr public key (npub1...) for Haven relay";
|
||||
};
|
||||
};
|
||||
}
|
||||
13
modules/core/sovran-manage.nix
Normal file
13
modules/core/sovran-manage.nix
Normal file
@@ -0,0 +1,13 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
sovran-manage = pkgs.writeShellScriptBin "sovran-manage" (builtins.readFile ../../scripts/sovran-manage.sh);
|
||||
in
|
||||
{
|
||||
environment.systemPackages = [
|
||||
sovran-manage
|
||||
pkgs.pwgen
|
||||
pkgs.dig
|
||||
pkgs.curl
|
||||
];
|
||||
}
|
||||
54
modules/coturn.nix
Executable file
54
modules/coturn.nix
Executable file
@@ -0,0 +1,54 @@
|
||||
{config, pkgs, lib, ...}:
|
||||
|
||||
let
|
||||
personalization = import ./personalization.nix;
|
||||
|
||||
in
|
||||
lib.mkIf config.sovran_systemsOS.features.coturn {
|
||||
|
||||
systemd.services.coturn-helper = {
|
||||
|
||||
script = ''
|
||||
|
||||
systemctl restart coturn
|
||||
|
||||
'';
|
||||
|
||||
unitConfig = {
|
||||
Type = "simple";
|
||||
After = "btcpayserver.service";
|
||||
Requires = "network-online.target";
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
RemainAfterExit = "yes";
|
||||
Type = "oneshot";
|
||||
};
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
};
|
||||
|
||||
|
||||
services.coturn = {
|
||||
|
||||
enable = true;
|
||||
use-auth-secret = true;
|
||||
static-auth-secret = "${personalization.coturn_static_auth_secret}";
|
||||
realm = personalization.matrix_url;
|
||||
cert = "/var/lib/coturn/${personalization.matrix_url}.crt.pem";
|
||||
pkey = "/var/lib/coturn/${personalization.matrix_url}.key.pem";
|
||||
min-port = 49152;
|
||||
max-port = 65535;
|
||||
listening-port = 5349;
|
||||
no-cli = true;
|
||||
extraConfig = ''
|
||||
verbose
|
||||
external-ip=${personalization.external_ip_secret}
|
||||
stale-nonce
|
||||
fingerprint
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
248
modules/element-calling.nix
Executable file
248
modules/element-calling.nix
Executable file
@@ -0,0 +1,248 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
livekitKeyFile = "/var/lib/livekit/livekit_keyFile";
|
||||
in
|
||||
|
||||
lib.mkIf config.sovran_systemsOS.features.element-calling {
|
||||
|
||||
####### LIVEKIT KEY GENERATION #######
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/livekit 0750 root root -"
|
||||
];
|
||||
|
||||
systemd.services.livekit-key-setup = {
|
||||
description = "Generate LiveKit key file if missing";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "livekit.service" "lk-jwt-service.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.openssl ];
|
||||
script = ''
|
||||
if [ ! -f ${livekitKeyFile} ]; then
|
||||
API_KEY="devkey_$(openssl rand -hex 16)"
|
||||
API_SECRET="$(openssl rand -base64 36 | tr -d '\n')"
|
||||
echo "$API_KEY: $API_SECRET" > ${livekitKeyFile}
|
||||
chmod 600 ${livekitKeyFile}
|
||||
echo "LiveKit key file generated at ${livekitKeyFile}"
|
||||
else
|
||||
echo "LiveKit key file already exists, skipping generation"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
####### ENSURE SERVICES START AFTER KEY EXISTS #######
|
||||
systemd.services.livekit.after = [ "livekit-key-setup.service" ];
|
||||
systemd.services.livekit.wants = [ "livekit-key-setup.service" ];
|
||||
systemd.services.lk-jwt-service.after = [ "livekit-key-setup.service" ];
|
||||
systemd.services.lk-jwt-service.wants = [ "livekit-key-setup.service" ];
|
||||
|
||||
####### CADDY SNIPPET — written to /run/caddy for caddy.nix to pick up #######
|
||||
systemd.services.element-calling-caddy-config = {
|
||||
description = "Generate Element Calling Caddy config snippet";
|
||||
before = [ "caddy-generate-config.service" ];
|
||||
requiredBy = [ "caddy-generate-config.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
MATRIX=$(cat /var/lib/domains/matrix)
|
||||
ELEMENT_CALLING=$(cat /var/lib/domains/element-calling)
|
||||
|
||||
mkdir -p /run/caddy
|
||||
|
||||
cat > /run/caddy/element-calling.snippet <<EOF
|
||||
$MATRIX {
|
||||
reverse_proxy /_matrix/* http://localhost:8008
|
||||
reverse_proxy /_synapse/client/* http://localhost:8008
|
||||
header /.well-known/matrix/* Content-Type "application/json"
|
||||
header /.well-known/matrix/* Access-Control-Allow-Origin "*"
|
||||
header /.well-known/matrix/* Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
||||
header /.well-known/matrix/* Access-Control-Allow-Headers "X-Requested-With, Content-Type, Authorization"
|
||||
respond /.well-known/matrix/client \`{ "m.homeserver": {"base_url": "https://$MATRIX" }, "org.matrix.msc4143.rtc_foci": [{ "type":"livekit", "livekit_service_url":"https://$ELEMENT_CALLING/livekit/jwt" }] }\`
|
||||
}
|
||||
|
||||
$MATRIX:8448 {
|
||||
reverse_proxy http://localhost:8008
|
||||
}
|
||||
|
||||
$ELEMENT_CALLING {
|
||||
handle /livekit/jwt/sfu/get {
|
||||
uri strip_prefix /livekit/jwt
|
||||
reverse_proxy [::1]:8073 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Server {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
handle {
|
||||
reverse_proxy localhost:7880
|
||||
}
|
||||
}
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
|
||||
####### LIVEKIT RUNTIME CONFIG #######
|
||||
systemd.services.livekit-runtime-config = {
|
||||
description = "Generate LiveKit runtime config from domain files";
|
||||
before = [ "livekit.service" ];
|
||||
after = [ "livekit-key-setup.service" ];
|
||||
requiredBy = [ "livekit.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
MATRIX=$(cat /var/lib/domains/matrix)
|
||||
|
||||
mkdir -p /run/livekit
|
||||
|
||||
cat > /run/livekit/runtime-config.yaml <<EOF
|
||||
turn:
|
||||
domain: $MATRIX
|
||||
cert_file: /var/lib/livekit/$MATRIX.crt
|
||||
key_file: /var/lib/livekit/$MATRIX.key
|
||||
EOF
|
||||
|
||||
chmod 640 /run/livekit/runtime-config.yaml
|
||||
'';
|
||||
};
|
||||
|
||||
####### LIVEKIT SERVICE #######
|
||||
services.livekit = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
keyFile = livekitKeyFile;
|
||||
settings = {
|
||||
rtc.use_external_ip = true;
|
||||
rtc.udp_port = "7882-7894";
|
||||
room.auto_create = false;
|
||||
turn = {
|
||||
enabled = true;
|
||||
tls_port = 5349;
|
||||
udp_port = 3478;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 7881 ];
|
||||
networking.firewall.allowedUDPPortRanges = [
|
||||
{ from = 7882; to = 7894; }
|
||||
];
|
||||
|
||||
####### JWT SERVICE #######
|
||||
systemd.services.lk-jwt-service-runtime-config = {
|
||||
description = "Generate lk-jwt-service runtime config from domain files";
|
||||
before = [ "lk-jwt-service.service" ];
|
||||
after = [ "livekit-key-setup.service" ];
|
||||
requiredBy = [ "lk-jwt-service.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
ELEMENT_CALLING=$(cat /var/lib/domains/element-calling)
|
||||
|
||||
mkdir -p /run/lk-jwt-service
|
||||
|
||||
cat > /run/lk-jwt-service/env <<EOF
|
||||
LIVEKIT_URL=wss://$ELEMENT_CALLING
|
||||
EOF
|
||||
|
||||
chmod 640 /run/lk-jwt-service/env
|
||||
'';
|
||||
};
|
||||
|
||||
services.lk-jwt-service = {
|
||||
enable = true;
|
||||
port = 8073;
|
||||
keyFile = livekitKeyFile;
|
||||
};
|
||||
|
||||
systemd.services.lk-jwt-service.serviceConfig.EnvironmentFile = [
|
||||
"/run/lk-jwt-service/env"
|
||||
];
|
||||
|
||||
####### SYNAPSE RUNTIME CONFIG (element-calling additions) #######
|
||||
systemd.services.element-calling-synapse-config = {
|
||||
description = "Generate Synapse runtime config for Element Calling";
|
||||
before = [ "matrix-synapse.service" ];
|
||||
requiredBy = [ "matrix-synapse.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
MATRIX=$(cat /var/lib/domains/matrix)
|
||||
|
||||
mkdir -p /run/matrix-synapse
|
||||
|
||||
cat > /run/matrix-synapse/element-calling-config.yaml <<EOF
|
||||
server_name: "$MATRIX"
|
||||
public_baseurl: "https://$MATRIX"
|
||||
serve_server_wellknown: true
|
||||
experimental_features:
|
||||
msc3266_enabled: true
|
||||
msc4222_enabled: true
|
||||
max_event_delay_duration: "24h"
|
||||
rc_message:
|
||||
per_second: 0.5
|
||||
burst_count: 30
|
||||
rc_delayed_event_mgmt:
|
||||
per_second: 1
|
||||
burst_count: 20
|
||||
EOF
|
||||
|
||||
chown matrix-synapse:matrix-synapse /run/matrix-synapse/element-calling-config.yaml
|
||||
chmod 640 /run/matrix-synapse/element-calling-config.yaml
|
||||
'';
|
||||
};
|
||||
|
||||
services.matrix-synapse = {
|
||||
extraConfigFiles = [ "/run/matrix-synapse/element-calling-config.yaml" ];
|
||||
settings = lib.mkForce {
|
||||
push.include_content = false;
|
||||
url_preview_enabled = true;
|
||||
group_unread_count_by_room = false;
|
||||
encryption_enabled_by_default_for_room_type = "invite";
|
||||
allow_profile_lookup_over_federation = false;
|
||||
allow_device_name_lookup_over_federation = false;
|
||||
url_preview_ip_range_blacklist = [
|
||||
"10.0.0.0/8" "100.64.0.0/10" "169.254.0.0/16" "172.16.0.0/12"
|
||||
"192.0.0.0/24" "192.0.2.0/24" "192.168.0.0/16" "192.88.99.0/24"
|
||||
"198.18.0.0/15" "198.51.100.0/24" "2001:db8::/32" "203.0.113.0/24"
|
||||
"224.0.0.0/4" "::1/128" "fc00::/7" "fe80::/10" "fec0::/10" "ff00::/8"
|
||||
];
|
||||
url_preview_ip_ranger_whitelist = [ "127.0.0.1" ];
|
||||
presence.enabled = true;
|
||||
enable_registration = false;
|
||||
registration_shared_secret = config.age.secrets.matrix_reg_secret.path;
|
||||
listeners = [
|
||||
{
|
||||
port = 8008;
|
||||
bind_addresses = [ "::1" ];
|
||||
type = "http";
|
||||
tls = false;
|
||||
x_forwarded = true;
|
||||
resources = [
|
||||
{ names = [ "client" ]; compress = true; }
|
||||
{ names = [ "federation" ]; compress = false; }
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
158
modules/haven.nix
Executable file
158
modules/haven.nix
Executable file
@@ -0,0 +1,158 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
npub = config.sovran_systemsOS.nostr_npub;
|
||||
in
|
||||
|
||||
lib.mkIf (config.sovran_systemsOS.features.haven && npub != "") {
|
||||
|
||||
# ── Caddy vhost is now handled centrally in caddy.nix ─────
|
||||
|
||||
# ── Generate Haven runtime config from domain files ───────
|
||||
systemd.services.haven-runtime-config = {
|
||||
description = "Generate Haven runtime config from domain files";
|
||||
before = [ "haven.service" ];
|
||||
requiredBy = [ "haven.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
HAVEN=$(cat /var/lib/domains/haven)
|
||||
|
||||
mkdir -p /run/haven
|
||||
|
||||
cat > /run/haven/runtime.env <<EOF
|
||||
RELAY_URL=$HAVEN
|
||||
PRIVATE_RELAY_NAME=$HAVEN private relay
|
||||
PRIVATE_RELAY_DESCRIPTION=The Relay From
|
||||
CHAT_RELAY_NAME=$HAVEN chat relay
|
||||
CHAT_RELAY_DESCRIPTION=a relay for private chats
|
||||
OUTBOX_RELAY_NAME=$HAVEN outbox relay
|
||||
OUTBOX_RELAY_DESCRIPTION=a relay and Blossom server for public messages and media
|
||||
INBOX_RELAY_NAME=$HAVEN inbox relay
|
||||
INBOX_RELAY_DESCRIPTION=send your interactions with my notes here
|
||||
EOF
|
||||
|
||||
chmod 640 /run/haven/runtime.env
|
||||
chown haven:haven /run/haven/runtime.env
|
||||
'';
|
||||
};
|
||||
|
||||
services.haven = {
|
||||
enable = true;
|
||||
settings = {
|
||||
OWNER_NPUB = npub;
|
||||
# RELAY_URL injected at runtime via EnvironmentFile
|
||||
|
||||
RELAY_PORT = 3355;
|
||||
RELAY_BIND_ADDRESS = "0.0.0.0";
|
||||
DB_ENGINE = "badger";
|
||||
LMDB_MAPSIZE = 3000000000;
|
||||
BLOSSOM_PATH = "blossom/";
|
||||
|
||||
# Relay names/descriptions injected at runtime via EnvironmentFile
|
||||
PRIVATE_RELAY_NPUB = npub;
|
||||
CHAT_RELAY_NPUB = npub;
|
||||
OUTBOX_RELAY_NPUB = npub;
|
||||
|
||||
INBOX_PULL_INTERVAL_SECONDS = 600;
|
||||
|
||||
PRIVATE_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = 50;
|
||||
PRIVATE_RELAY_EVENT_IP_LIMITER_INTERVAL = 1;
|
||||
PRIVATE_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = 100;
|
||||
PRIVATE_RELAY_ALLOW_EMPTY_FILTERS = true;
|
||||
PRIVATE_RELAY_ALLOW_COMPLEX_FILTERS = true;
|
||||
PRIVATE_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = 3;
|
||||
PRIVATE_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = 5;
|
||||
PRIVATE_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = 9;
|
||||
|
||||
CHAT_RELAY_WOT_DEPTH = 3;
|
||||
CHAT_RELAY_WOT_REFRESH_INTERVAL_HOURS = 24;
|
||||
CHAT_RELAY_MINIMUM_FOLLOWERS = 3;
|
||||
CHAT_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = 50;
|
||||
CHAT_RELAY_EVENT_IP_LIMITER_INTERVAL = 1;
|
||||
CHAT_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = 100;
|
||||
CHAT_RELAY_ALLOW_EMPTY_FILTERS = false;
|
||||
CHAT_RELAY_ALLOW_COMPLEX_FILTERS = false;
|
||||
CHAT_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = 3;
|
||||
CHAT_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = 3;
|
||||
CHAT_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = 9;
|
||||
|
||||
OUTBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = 100;
|
||||
OUTBOX_RELAY_EVENT_IP_LIMITER_INTERVAL = 600;
|
||||
OUTBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = 1000;
|
||||
OUTBOX_RELAY_ALLOW_EMPTY_FILTERS = true;
|
||||
OUTBOX_RELAY_ALLOW_COMPLEX_FILTERS = true;
|
||||
OUTBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = 30;
|
||||
OUTBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = 10;
|
||||
OUTBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = 90;
|
||||
|
||||
INBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL = 10;
|
||||
INBOX_RELAY_EVENT_IP_LIMITER_INTERVAL = 1;
|
||||
INBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS = 20;
|
||||
INBOX_RELAY_ALLOW_EMPTY_FILTERS = false;
|
||||
INBOX_RELAY_ALLOW_COMPLEX_FILTERS = false;
|
||||
INBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL = 3;
|
||||
INBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL = 1;
|
||||
INBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS = 9;
|
||||
|
||||
WOT_FETCH_TIMEOUT_SECONDS = 60;
|
||||
WHITELISTED_NPUBS_FILE = "/var/lib/haven/whitelisted_npubs.json";
|
||||
BLACKLISTED_NPUBS_FILE = "";
|
||||
HAVEN_LOG_LEVEL = "INFO";
|
||||
};
|
||||
|
||||
blastrRelays = [
|
||||
"nos.lol"
|
||||
"relay.nostr.band"
|
||||
"relay.snort.social"
|
||||
"nostr.mom"
|
||||
"relay.primal.net"
|
||||
"no.str.cr"
|
||||
"nostr21.com"
|
||||
"nostrue.com"
|
||||
"wot.nostr.party"
|
||||
"wot.sovbit.host"
|
||||
"wot.girino.org"
|
||||
"relay.lexingtonbitcoin.org"
|
||||
"zap.watch"
|
||||
"satsage.xyz"
|
||||
"wons.calva.dev"
|
||||
];
|
||||
};
|
||||
|
||||
systemd.services.haven.serviceConfig.EnvironmentFile = [
|
||||
"/run/haven/runtime.env"
|
||||
];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/haven 0750 haven haven -"
|
||||
];
|
||||
|
||||
systemd.services.haven-whitelist-setup = {
|
||||
description = "Ensure Haven whitelisted_npubs.json is valid";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "haven.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
FILE="/var/lib/haven/whitelisted_npubs.json"
|
||||
if [ ! -s "$FILE" ] || ! ${pkgs.jq}/bin/jq empty "$FILE" 2>/dev/null; then
|
||||
echo '[]' > "$FILE"
|
||||
chown haven:haven "$FILE"
|
||||
chmod 770 "$FILE"
|
||||
echo "Wrote valid empty JSON array to $FILE"
|
||||
else
|
||||
echo "$FILE already contains valid JSON, skipping"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.services.haven.after = [ "haven-whitelist-setup.service" "haven-runtime-config.service" ];
|
||||
systemd.services.haven.wants = [ "haven-whitelist-setup.service" "haven-runtime-config.service" ];
|
||||
}
|
||||
25
modules/mempool.nix
Executable file
25
modules/mempool.nix
Executable file
@@ -0,0 +1,25 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
lib.mkIf config.sovran_systemsOS.features.mempool {
|
||||
|
||||
services.mempool = {
|
||||
enable = true;
|
||||
frontend.enable = true;
|
||||
};
|
||||
|
||||
services.mysql.package = lib.mkForce pkgs.mariadb;
|
||||
|
||||
nix-bitcoin.onionServices.mempool-frontend.enable = true;
|
||||
|
||||
services.caddy = {
|
||||
virtualHosts = {
|
||||
":60847" = {
|
||||
extraConfig = ''
|
||||
reverse_proxy :60845
|
||||
encode gzip zstd
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
25
modules/modules.nix
Normal file
25
modules/modules.nix
Normal file
@@ -0,0 +1,25 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./core/roles.nix
|
||||
./core/role-logic.nix
|
||||
./core/caddy.nix
|
||||
./core/sovran-manage.nix
|
||||
./php.nix
|
||||
./Sovran_SystemsOS_File_Fixes_And_New_Services.nix
|
||||
./synapse.nix
|
||||
./coturn.nix
|
||||
./wordpress.nix
|
||||
./nextcloud.nix
|
||||
./btcpayserver.nix
|
||||
./vaultwarden.nix
|
||||
./haven.nix
|
||||
./bip110.nix
|
||||
./element-calling.nix
|
||||
./mempool.nix
|
||||
./bitcoin-core.nix
|
||||
./rdp.nix
|
||||
./bitcoinecosystem.nix
|
||||
];
|
||||
}
|
||||
224
modules/nextcloud.nix
Normal file
224
modules/nextcloud.nix
Normal file
@@ -0,0 +1,224 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sovran_systemsOS.services.nextcloud;
|
||||
in
|
||||
{
|
||||
options.sovran_systemsOS.services.nextcloud = {
|
||||
enable = lib.mkEnableOption "Nextcloud (raw PHP served by Caddy)";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
# ── Caddy vhost is now handled centrally in caddy.nix ─────
|
||||
|
||||
# ── PostgreSQL database ───────────────────────────────────
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
# ── Auto-generate DB password and initialize ──────────────
|
||||
systemd.services.nextcloud-db-init = {
|
||||
description = "Initialize Nextcloud PostgreSQL database with auto-generated password";
|
||||
after = [ "postgresql.service" ];
|
||||
requires = [ "postgresql.service" ];
|
||||
before = [ "nextcloud-init.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ config.services.postgresql.package pkgs.pwgen pkgs.coreutils ];
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
SECRET_FILE="/var/lib/secrets/nextclouddb"
|
||||
|
||||
# Existing machines already have this file — leave it alone
|
||||
if [ ! -f "$SECRET_FILE" ]; then
|
||||
mkdir -p /var/lib/secrets
|
||||
pwgen -s 64 1 > "$SECRET_FILE"
|
||||
chmod 600 "$SECRET_FILE"
|
||||
fi
|
||||
|
||||
DB_PASS=$(cat "$SECRET_FILE")
|
||||
|
||||
# Create role if it doesn't exist, update password either way
|
||||
psql -U postgres <<SQL
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'ncusr') THEN
|
||||
CREATE ROLE "ncusr" WITH LOGIN PASSWORD '$DB_PASS';
|
||||
ELSE
|
||||
ALTER ROLE "ncusr" WITH LOGIN PASSWORD '$DB_PASS';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
SQL
|
||||
|
||||
# Create database if it doesn't exist
|
||||
if ! psql -U postgres -lqt | cut -d \| -f 1 | grep -qw "nextclouddb"; then
|
||||
psql -U postgres -c "CREATE DATABASE nextclouddb WITH OWNER ncusr TEMPLATE template0 LC_COLLATE = 'C' LC_CTYPE = 'C';"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
# ── Fully automated Nextcloud setup ───────────────────────
|
||||
systemd.services.nextcloud-init = {
|
||||
description = "Download, extract, and fully configure Nextcloud";
|
||||
after = [ "network-online.target" "postgresql.service" "phpfpm-mypool.service" "nextcloud-db-init.service" ];
|
||||
wants = [ "network-online.target" ];
|
||||
requires = [ "postgresql.service" "nextcloud-db-init.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
unitConfig = {
|
||||
ConditionPathExists = "!/var/lib/www/nextcloud/config/config.php";
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
|
||||
path = with pkgs; [ curl unzip php pwgen coreutils ];
|
||||
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR="/var/lib/www/nextcloud"
|
||||
DATA_DIR="/var/lib/www/nextcloud-data"
|
||||
DOMAIN=$(cat /var/lib/domains/nextcloud)
|
||||
DB_NAME="nextclouddb"
|
||||
DB_USER="ncusr"
|
||||
DB_PASS=$(cat /var/lib/secrets/nextclouddb)
|
||||
DB_HOST="localhost"
|
||||
ADMIN_USER=$(pwgen -s 16 1)
|
||||
ADMIN_PASS=$(pwgen -s 24 1)
|
||||
|
||||
echo "══════════════════════════════════════════════"
|
||||
echo " Nextcloud Automated Installation"
|
||||
echo "══════════════════════════════════════════════"
|
||||
|
||||
# ── Download ────────────────────────────────────
|
||||
if [ ! -f "$INSTALL_DIR/occ" ]; then
|
||||
echo "Downloading Nextcloud..."
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
curl -L -o "$TEMP_DIR/nextcloud.zip" "https://download.nextcloud.com/server/releases/latest.zip"
|
||||
unzip -q "$TEMP_DIR/nextcloud.zip" -d "$TEMP_DIR"
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cp -a "$TEMP_DIR/nextcloud/"* "$INSTALL_DIR/"
|
||||
rm -rf "$TEMP_DIR"
|
||||
echo "Download complete."
|
||||
fi
|
||||
|
||||
# ── Create data directory ───────────────────────
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# ── Set permissions ─────────────────────────────
|
||||
chown -R caddy:root "$INSTALL_DIR"
|
||||
chown -R caddy:root "$DATA_DIR"
|
||||
find "$INSTALL_DIR" -type d -exec chmod 750 {} \;
|
||||
find "$INSTALL_DIR" -type f -exec chmod 640 {} \;
|
||||
chmod -R 770 "$INSTALL_DIR/apps"
|
||||
chmod -R 770 "$INSTALL_DIR/config"
|
||||
chmod -R 770 "$DATA_DIR"
|
||||
|
||||
# ── Wait for database ───────────────────────────
|
||||
echo "Waiting for PostgreSQL..."
|
||||
for i in $(seq 1 30); do
|
||||
if su -s /bin/sh caddy -c "php -r \"new PDO('pgsql:host=$DB_HOST;dbname=$DB_NAME', '$DB_USER', '$DB_PASS');\"" 2>/dev/null; then
|
||||
echo "Database ready."
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# ── Run Nextcloud install via occ ───────────────
|
||||
echo "Running Nextcloud installation..."
|
||||
su -s /bin/sh caddy -c "
|
||||
php $INSTALL_DIR/occ maintenance:install \
|
||||
--database 'pgsql' \
|
||||
--database-name '$DB_NAME' \
|
||||
--database-user '$DB_USER' \
|
||||
--database-pass '$DB_PASS' \
|
||||
--database-host '$DB_HOST' \
|
||||
--admin-user '$ADMIN_USER' \
|
||||
--admin-pass '$ADMIN_PASS' \
|
||||
--data-dir '$DATA_DIR'
|
||||
"
|
||||
|
||||
# ── Configure trusted domains ───────────────────
|
||||
echo "Configuring trusted domains..."
|
||||
su -s /bin/sh caddy -c "
|
||||
php $INSTALL_DIR/occ config:system:set trusted_domains 0 --value='$DOMAIN'
|
||||
php $INSTALL_DIR/occ config:system:set overwrite.cli.url --value='https://$DOMAIN'
|
||||
php $INSTALL_DIR/occ config:system:set overwriteprotocol --value='https'
|
||||
"
|
||||
|
||||
# ── Set recommended settings ─<EFBFBD><EFBFBD>──────────────────
|
||||
echo "Applying recommended settings..."
|
||||
su -s /bin/sh caddy -c "
|
||||
php $INSTALL_DIR/occ config:system:set default_phone_region --value='US'
|
||||
php $INSTALL_DIR/occ config:system:set memcache.local --value='\OC\Memcache\APCu'
|
||||
php $INSTALL_DIR/occ background:cron
|
||||
"
|
||||
|
||||
# ── Install default apps ────────────────────────
|
||||
echo "Installing default apps..."
|
||||
su -s /bin/sh caddy -c "
|
||||
php $INSTALL_DIR/occ app:install calendar || true
|
||||
php $INSTALL_DIR/occ app:install contacts || true
|
||||
php $INSTALL_DIR/occ app:install tasks || true
|
||||
php $INSTALL_DIR/occ app:install notes || true
|
||||
php $INSTALL_DIR/occ app:install deck || true
|
||||
php $INSTALL_DIR/occ app:enable calendar || true
|
||||
php $INSTALL_DIR/occ app:enable contacts || true
|
||||
php $INSTALL_DIR/occ app:enable tasks || true
|
||||
php $INSTALL_DIR/occ app:enable notes || true
|
||||
php $INSTALL_DIR/occ app:enable deck || true
|
||||
"
|
||||
|
||||
# ── Save admin credentials ──────────────────────
|
||||
CREDS_FILE="/var/lib/secrets/nextcloud-admin"
|
||||
cat > "$CREDS_FILE" << CREDS
|
||||
Nextcloud Admin Credentials
|
||||
═══════════════════════════
|
||||
URL: https://$DOMAIN/
|
||||
Username: $ADMIN_USER
|
||||
Password: $ADMIN_PASS
|
||||
CREDS
|
||||
chmod 600 "$CREDS_FILE"
|
||||
|
||||
echo ""
|
||||
echo "══════════════════════════════════════════════"
|
||||
echo " Nextcloud installation complete!"
|
||||
echo ""
|
||||
echo " URL: https://$DOMAIN/"
|
||||
echo " Username: $ADMIN_USER"
|
||||
echo " Password: $ADMIN_PASS"
|
||||
echo ""
|
||||
echo " Installed apps: Calendar, Contacts, Tasks,"
|
||||
echo " Notes, Deck"
|
||||
echo ""
|
||||
echo " Credentials saved to: $CREDS_FILE"
|
||||
echo "══════════════════════════════════════════════"
|
||||
'';
|
||||
};
|
||||
|
||||
# ── Cron ──────────────────────────────────────────────────
|
||||
services.cron.systemCronJobs = [
|
||||
"*/5 * * * * caddy /run/current-system/sw/bin/php -f /var/lib/www/nextcloud/cron.php"
|
||||
];
|
||||
|
||||
# ── Ensure directories ────────────────────────────────────
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/www 0755 caddy root -"
|
||||
"d /var/lib/www/nextcloud 0750 caddy root -"
|
||||
"d /var/lib/www/nextcloud-data 0770 caddy root -"
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
unzip
|
||||
];
|
||||
};
|
||||
}
|
||||
24
modules/personalization.nix
Executable file
24
modules/personalization.nix
Executable file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
|
||||
matrix_url = builtins.readFile /var/lib/domains/matrix;
|
||||
wordpress_url = builtins.readFile /var/lib/domains/wordpress;
|
||||
nextcloud_url = builtins.readFile /var/lib/domains/nextcloud;
|
||||
btcpayserver_url = builtins.readFile /var/lib/domains/btcpayserver;
|
||||
caddy_email_for_acme = builtins.readFile /var/lib/domains/sslemail;
|
||||
vaultwarden_url = builtins.readFile /var/lib/domains/vaultwarden;
|
||||
haven_url = builtins.readFile /var/lib/domains/haven;
|
||||
element-calling_url = builtins.readFile /var/lib/domains/element-calling;
|
||||
|
||||
##
|
||||
|
||||
external_ip_secret = builtins.readFile /var/lib/secrets/external_ip;
|
||||
coturn_static_auth_secret = builtins.readFile /var/lib/secrets/turn;
|
||||
|
||||
##
|
||||
|
||||
matrixdb = builtins.readFile /var/lib/secrets/matrixdb;
|
||||
nextclouddb = builtins.readFile /var/lib/secrets/nextclouddb;
|
||||
wordpressdb = builtins.readFile /var/lib/secrets/wordpressdb;
|
||||
|
||||
|
||||
}
|
||||
66
modules/php.nix
Executable file
66
modules/php.nix
Executable file
@@ -0,0 +1,66 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
|
||||
let
|
||||
|
||||
custom-php = pkgs.php83.buildEnv {
|
||||
extensions = { enabled, all }: enabled ++ (with all; [ bz2 apcu redis imagick memcached ]);
|
||||
extraConfig = ''
|
||||
|
||||
display_errors = On
|
||||
display_startup_errors = On
|
||||
max_execution_time = 10000
|
||||
max_input_time = 3000
|
||||
memory_limit = 1G;
|
||||
opcache.enable=1;
|
||||
opcache.memory_consumption=512;
|
||||
opcache_revalidate_freq = 240;
|
||||
opcache.max_accelerated_files=20000;
|
||||
post_max_size = 3G
|
||||
upload_max_filesize = 3G
|
||||
apc.enable_cli=1
|
||||
opcache.interned_strings_buffer = 192
|
||||
redis.session.locking_enabled=1
|
||||
redis.session.lock_retries=-1
|
||||
redis.session.lock_wait_time=10000
|
||||
|
||||
'';
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
users.users = {
|
||||
|
||||
php = {
|
||||
isSystemUser = true;
|
||||
createHome = false;
|
||||
uid = 7777;
|
||||
};
|
||||
};
|
||||
|
||||
users.users.php.group = "php";
|
||||
|
||||
users.groups.php = {};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
|
||||
custom-php
|
||||
];
|
||||
|
||||
services.phpfpm.pools = {
|
||||
mypool = {
|
||||
user = "caddy";
|
||||
group = "php";
|
||||
phpPackage = custom-php;
|
||||
settings = {
|
||||
"pm" = "dynamic";
|
||||
"pm.max_children" = 75;
|
||||
"pm.start_servers" = 10;
|
||||
"pm.min_spare_servers" = 5;
|
||||
"pm.max_spare_servers" = 20;
|
||||
"pm.max_requests" = 500;
|
||||
"clear_env" = "no";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
107
modules/rdp.nix
Executable file
107
modules/rdp.nix
Executable file
@@ -0,0 +1,107 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
lib.mkIf config.sovran_systemsOS.features.rdp {
|
||||
|
||||
services.gnome.gnome-remote-desktop.enable = true;
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 3389 ];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
freerdp
|
||||
];
|
||||
|
||||
# The NixOS module installs the unit but doesn't enable it — we just need to start it and order it
|
||||
systemd.services.gnome-remote-desktop = {
|
||||
wantedBy = [ "graphical.target" ];
|
||||
after = [ "gnome-remote-desktop-setup.service" ];
|
||||
wants = [ "gnome-remote-desktop-setup.service" ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/gnome-remote-desktop 0750 gnome-remote-desktop gnome-remote-desktop -"
|
||||
"d /var/lib/gnome-remote-desktop/.local 0750 gnome-remote-desktop gnome-remote-desktop -"
|
||||
"d /var/lib/gnome-remote-desktop/.local/share 0750 gnome-remote-desktop gnome-remote-desktop -"
|
||||
"d /var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop 0750 gnome-remote-desktop gnome-remote-desktop -"
|
||||
];
|
||||
|
||||
systemd.services.gnome-remote-desktop-setup = {
|
||||
description = "Configure GNOME Remote Desktop RDP";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "gnome-remote-desktop.service" ];
|
||||
after = [ "systemd-tmpfiles-setup.service" "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [
|
||||
pkgs.gnome-remote-desktop
|
||||
pkgs.polkit
|
||||
pkgs.openssl
|
||||
pkgs.hostname
|
||||
pkgs.gawk
|
||||
];
|
||||
script = ''
|
||||
# Ensure directory structure exists
|
||||
mkdir -p /var/lib/gnome-remote-desktop/.local/share/gnome-remote-desktop
|
||||
chown -R gnome-remote-desktop:gnome-remote-desktop /var/lib/gnome-remote-desktop
|
||||
|
||||
TLS_DIR="/var/lib/gnome-remote-desktop/tls"
|
||||
CRED_FILE="/var/lib/gnome-remote-desktop/rdp-credentials"
|
||||
|
||||
# Generate TLS certificate if it doesn't exist
|
||||
if [ ! -f "$TLS_DIR/rdp-tls.crt" ]; then
|
||||
mkdir -p "$TLS_DIR"
|
||||
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
|
||||
-sha256 -nodes -days 3650 \
|
||||
-keyout "$TLS_DIR/rdp-tls.key" \
|
||||
-out "$TLS_DIR/rdp-tls.crt" \
|
||||
-subj "/CN=gnome-remote-desktop"
|
||||
chown -R gnome-remote-desktop:gnome-remote-desktop "$TLS_DIR"
|
||||
chmod 600 "$TLS_DIR/rdp-tls.key"
|
||||
chmod 644 "$TLS_DIR/rdp-tls.crt"
|
||||
echo "Generated RDP TLS certificate"
|
||||
fi
|
||||
|
||||
# Configure TLS certificate
|
||||
grdctl --system rdp set-tls-cert "$TLS_DIR/rdp-tls.crt"
|
||||
grdctl --system rdp set-tls-key "$TLS_DIR/rdp-tls.key"
|
||||
|
||||
# Generate password on first boot only
|
||||
PASSWORD=""
|
||||
if [ ! -f /var/lib/gnome-remote-desktop/rdp-password ]; then
|
||||
PASSWORD=$(openssl rand -base64 16)
|
||||
echo "$PASSWORD" > /var/lib/gnome-remote-desktop/rdp-password
|
||||
chmod 600 /var/lib/gnome-remote-desktop/rdp-password
|
||||
else
|
||||
PASSWORD=$(cat /var/lib/gnome-remote-desktop/rdp-password)
|
||||
fi
|
||||
|
||||
# Get current IP address
|
||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
||||
|
||||
# Always rewrite the credentials file with the current IP
|
||||
cat > "$CRED_FILE" <<EOF
|
||||
========================================
|
||||
GNOME Remote Desktop (RDP) Credentials
|
||||
========================================
|
||||
|
||||
Username: sovran
|
||||
Password: $PASSWORD
|
||||
|
||||
Connect from any RDP client to:
|
||||
$LOCAL_IP:3389
|
||||
|
||||
========================================
|
||||
EOF
|
||||
|
||||
chmod 600 "$CRED_FILE"
|
||||
|
||||
# Enable RDP backend and set credentials
|
||||
grdctl --system rdp enable
|
||||
grdctl --system rdp set-credentials sovran "$PASSWORD"
|
||||
|
||||
echo "GNOME Remote Desktop RDP configured successfully"
|
||||
'';
|
||||
};
|
||||
}
|
||||
136
modules/synapse.nix
Normal file
136
modules/synapse.nix
Normal file
@@ -0,0 +1,136 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
# ── PostgreSQL database for Matrix ──────────────────────────
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [ "matrix-synapse" ];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "matrix-synapse";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# ── Auto-generate DB password and initialize ────────────────
|
||||
systemd.services.matrix-synapse-db-init = {
|
||||
description = "Initialize Matrix Synapse PostgreSQL database with auto-generated password";
|
||||
after = [ "postgresql.service" ];
|
||||
requires = [ "postgresql.service" ];
|
||||
before = [ "matrix-synapse.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ config.services.postgresql.package pkgs.pwgen pkgs.coreutils ];
|
||||
script = ''
|
||||
SECRET_DIR="/var/lib/secrets"
|
||||
SECRET_FILE="$SECRET_DIR/matrix_db_secret"
|
||||
|
||||
mkdir -p "$SECRET_DIR"
|
||||
|
||||
if [ ! -f "$SECRET_FILE" ]; then
|
||||
pwgen -s 64 1 > "$SECRET_FILE"
|
||||
chmod 600 "$SECRET_FILE"
|
||||
chown matrix-synapse:matrix-synapse "$SECRET_FILE"
|
||||
fi
|
||||
|
||||
DB_PASS=$(cat "$SECRET_FILE")
|
||||
|
||||
psql -U postgres -c "ALTER ROLE \"matrix-synapse\" WITH LOGIN PASSWORD '$DB_PASS';"
|
||||
|
||||
if ! psql -U postgres -lqt | cut -d \| -f 1 | grep -qw "matrix-synapse"; then
|
||||
psql -U postgres -c "CREATE DATABASE \"matrix-synapse\" WITH OWNER \"matrix-synapse\" TEMPLATE template0 LC_COLLATE = 'C' LC_CTYPE = 'C';"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
# ── Generate Synapse runtime config from /var/lib/domains ───
|
||||
systemd.services.matrix-synapse-runtime-config = {
|
||||
description = "Generate Matrix Synapse runtime config from domain files";
|
||||
before = [ "matrix-synapse.service" ];
|
||||
after = [ "matrix-synapse-db-init.service" ];
|
||||
requiredBy = [ "matrix-synapse.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
MATRIX=$(cat /var/lib/domains/matrix)
|
||||
RUNTIME_DIR="/run/matrix-synapse"
|
||||
mkdir -p "$RUNTIME_DIR"
|
||||
|
||||
cat > "$RUNTIME_DIR/runtime-config.yaml" <<EOF
|
||||
server_name: "$MATRIX"
|
||||
EOF
|
||||
|
||||
chown matrix-synapse:matrix-synapse "$RUNTIME_DIR/runtime-config.yaml"
|
||||
chmod 640 "$RUNTIME_DIR/runtime-config.yaml"
|
||||
'';
|
||||
};
|
||||
|
||||
# ── Synapse service ─────────────────────────────────────────
|
||||
lib.mkIf config.sovran_systemsOS.features.synapse {
|
||||
services.matrix-synapse = {
|
||||
enable = true;
|
||||
extraConfigFiles = [ "/run/matrix-synapse/runtime-config.yaml" ];
|
||||
settings = {
|
||||
push.include_content = false;
|
||||
group_unread_count_by_room = false;
|
||||
encryption_enabled_by_default_for_room_type = "invite";
|
||||
allow_profile_lookup_over_federation = false;
|
||||
allow_device_name_lookup_over_federation = false;
|
||||
# server_name is injected at runtime via extraConfigFiles
|
||||
url_preview_enabled = true;
|
||||
max_upload_size = "1024M";
|
||||
url_preview_ip_range_blacklist = [
|
||||
"10.0.0.0/8"
|
||||
"100.64.0.0/10"
|
||||
"169.254.0.0/16"
|
||||
"172.16.0.0/12"
|
||||
"192.0.0.0/24"
|
||||
"192.0.2.0/24"
|
||||
"192.168.0.0/16"
|
||||
"192.88.99.0/24"
|
||||
"198.18.0.0/15"
|
||||
"198.51.100.0/24"
|
||||
"2001:db8::/32"
|
||||
"203.0.113.0/24"
|
||||
"224.0.0.0/4"
|
||||
"::1/128"
|
||||
"fc00::/7"
|
||||
"fe80::/10"
|
||||
"fec0::/10"
|
||||
"ff00::/8"
|
||||
];
|
||||
url_preview_ip_ranger_whitelist = [ "127.0.0.1" ];
|
||||
presence.enabled = true;
|
||||
enable_registration = false;
|
||||
registration_shared_secret = config.age.secrets.matrix_reg_secret.path;
|
||||
listeners = [
|
||||
{
|
||||
port = 8008;
|
||||
bind_addresses = [ "::1" ];
|
||||
type = "http";
|
||||
tls = false;
|
||||
x_forwarded = true;
|
||||
resources = [
|
||||
{
|
||||
names = [ "client" ];
|
||||
compress = true;
|
||||
}
|
||||
{
|
||||
names = [ "federation" ];
|
||||
compress = false;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
47
modules/vaultwarden.nix
Executable file
47
modules/vaultwarden.nix
Executable file
@@ -0,0 +1,47 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
lib.mkIf config.sovran_systemsOS.features.vaultwarden {
|
||||
|
||||
# ── Caddy vhost is now handled centrally in caddy.nix ─────
|
||||
|
||||
# ── Generate Vaultwarden runtime config from domain files ──
|
||||
systemd.services.vaultwarden-runtime-config = {
|
||||
description = "Generate Vaultwarden runtime config from domain files";
|
||||
before = [ "vaultwarden.service" ];
|
||||
requiredBy = [ "vaultwarden.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
script = ''
|
||||
VAULTWARDEN=$(cat /var/lib/domains/vaultwarden)
|
||||
|
||||
mkdir -p /run/vaultwarden
|
||||
|
||||
cat > /run/vaultwarden/runtime.env <<EOF
|
||||
DOMAIN=https://$VAULTWARDEN
|
||||
EOF
|
||||
|
||||
chmod 640 /run/vaultwarden/runtime.env
|
||||
'';
|
||||
};
|
||||
|
||||
services.vaultwarden = {
|
||||
enable = true;
|
||||
config = {
|
||||
# DOMAIN injected at runtime via EnvironmentFile
|
||||
SIGNUPS_ALLOWED = false;
|
||||
ROCKET_ADDRESS = "127.0.0.1";
|
||||
ROCKET_PORT = 8777;
|
||||
ROCKET_LOG = "critical";
|
||||
};
|
||||
dbBackend = "sqlite";
|
||||
environmentFile = "/var/lib/secrets/vaultwarden/vaultwarden.env";
|
||||
};
|
||||
|
||||
systemd.services.vaultwarden.serviceConfig.EnvironmentFile = lib.mkAfter [
|
||||
"/run/vaultwarden/runtime.env"
|
||||
];
|
||||
}
|
||||
198
modules/wordpress.nix
Normal file
198
modules/wordpress.nix
Normal file
@@ -0,0 +1,198 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sovran_systemsOS.services.wordpress;
|
||||
in
|
||||
{
|
||||
options.sovran_systemsOS.services.wordpress = {
|
||||
enable = lib.mkEnableOption "WordPress (raw PHP served by Caddy)";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
# ── Caddy vhost is now handled centrally in caddy.nix ─────
|
||||
|
||||
# ── MariaDB database ──────────────────────────────────────
|
||||
services.mysql = {
|
||||
enable = true;
|
||||
package = pkgs.mariadb;
|
||||
};
|
||||
|
||||
# ── Auto-generate DB password and initialize ──────────────
|
||||
systemd.services.wordpress-db-init = {
|
||||
description = "Initialize WordPress MariaDB database with auto-generated password";
|
||||
after = [ "mysql.service" ];
|
||||
requires = [ "mysql.service" ];
|
||||
before = [ "wordpress-init.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
path = [ config.services.mysql.package pkgs.pwgen pkgs.coreutils ];
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
SECRET_FILE="/var/lib/secrets/wordpressdb"
|
||||
|
||||
# Existing machines already have this file — leave it alone
|
||||
if [ ! -f "$SECRET_FILE" ]; then
|
||||
mkdir -p /var/lib/secrets
|
||||
pwgen -s 64 1 > "$SECRET_FILE"
|
||||
chmod 600 "$SECRET_FILE"
|
||||
fi
|
||||
|
||||
DB_PASS=$(cat "$SECRET_FILE")
|
||||
|
||||
mysql -u root <<SQL
|
||||
CREATE DATABASE IF NOT EXISTS wordpressdb;
|
||||
CREATE USER IF NOT EXISTS 'wpusr'@'localhost' IDENTIFIED BY '$DB_PASS';
|
||||
ALTER USER 'wpusr'@'localhost' IDENTIFIED BY '$DB_PASS';
|
||||
GRANT ALL ON wordpressdb.* TO 'wpusr'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
SQL
|
||||
'';
|
||||
};
|
||||
|
||||
# ── Fully automated WordPress setup ───────────────────────
|
||||
systemd.services.wordpress-init = {
|
||||
description = "Download, extract, and fully configure WordPress";
|
||||
after = [ "network-online.target" "mysql.service" "phpfpm-mypool.service" "wordpress-db-init.service" ];
|
||||
wants = [ "network-online.target" ];
|
||||
requires = [ "mysql.service" "wordpress-db-init.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
unitConfig = {
|
||||
ConditionPathExists = "!/var/lib/www/wordpress/wp-config.php";
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
|
||||
path = with pkgs; [ curl unzip wp-cli pwgen php coreutils ];
|
||||
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR="/var/lib/www/wordpress"
|
||||
DOMAIN=$(cat /var/lib/domains/wordpress)
|
||||
DB_NAME="wordpressdb"
|
||||
DB_USER="wpusr"
|
||||
DB_PASS=$(cat /var/lib/secrets/wordpressdb)
|
||||
DB_HOST="localhost"
|
||||
ADMIN_USER=$(pwgen -s 16 1)
|
||||
ADMIN_PASS=$(pwgen -s 24 1)
|
||||
ADMIN_EMAIL="$ADMIN_USER@''${DOMAIN#*.}"
|
||||
|
||||
echo "══════════════════════════════════════════════"
|
||||
echo " WordPress Automated Installation"
|
||||
echo "══════════════════════════════════════════════"
|
||||
|
||||
# ── Download ────────────────────────────────────
|
||||
if [ ! -f "$INSTALL_DIR/wp-includes/version.php" ]; then
|
||||
echo "Downloading WordPress..."
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
curl -L -o "$TEMP_DIR/wordpress.zip" "https://wordpress.org/latest.zip"
|
||||
unzip -q "$TEMP_DIR/wordpress.zip" -d "$TEMP_DIR"
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cp -a "$TEMP_DIR/wordpress/"* "$INSTALL_DIR/"
|
||||
rm -rf "$TEMP_DIR"
|
||||
echo "Download complete."
|
||||
fi
|
||||
|
||||
# ── Set permissions ─────────────────────────────
|
||||
chown -R caddy:root "$INSTALL_DIR"
|
||||
find "$INSTALL_DIR" -type d -exec chmod 755 {} \;
|
||||
find "$INSTALL_DIR" -type f -exec chmod 644 {} \;
|
||||
chmod -R 775 "$INSTALL_DIR/wp-content"
|
||||
|
||||
# ── Generate wp-config.php ──────────────────────
|
||||
echo "Generating wp-config.php..."
|
||||
cd "$INSTALL_DIR"
|
||||
su -s /bin/sh caddy -c "
|
||||
wp config create \
|
||||
--dbname='$DB_NAME' \
|
||||
--dbuser='$DB_USER' \
|
||||
--dbpass='$DB_PASS' \
|
||||
--dbhost='$DB_HOST' \
|
||||
--skip-check
|
||||
"
|
||||
|
||||
# ── Wait for database to be ready ───────────────
|
||||
echo "Waiting for database..."
|
||||
for i in $(seq 1 30); do
|
||||
if su -s /bin/sh caddy -c "wp db check" 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# ── Run WordPress install ───────────────────────
|
||||
echo "Running WordPress core install..."
|
||||
su -s /bin/sh caddy -c "
|
||||
wp core install \
|
||||
--url='https://$DOMAIN' \
|
||||
--title='Sovran_SystemsOS' \
|
||||
--admin_user='$ADMIN_USER' \
|
||||
--admin_password='$ADMIN_PASS' \
|
||||
--admin_email='$ADMIN_EMAIL' \
|
||||
--skip-email
|
||||
"
|
||||
|
||||
# ── Configure WordPress settings ────────────────
|
||||
echo "Configuring WordPress..."
|
||||
su -s /bin/sh caddy -c "
|
||||
wp option update blogdescription 'Powered by Sovran_SystemsOS'
|
||||
wp option update permalink_structure '/%postname%/'
|
||||
wp option update default_ping_status 'closed'
|
||||
wp option update default_comment_status 'closed'
|
||||
wp rewrite flush
|
||||
"
|
||||
|
||||
# ── Security hardening ──────────────────────────
|
||||
echo "Applying security settings..."
|
||||
su -s /bin/sh caddy -c "
|
||||
wp config set DISALLOW_FILE_EDIT true --raw
|
||||
wp config set WP_AUTO_UPDATE_CORE true --raw
|
||||
wp config set FORCE_SSL_ADMIN true --raw
|
||||
"
|
||||
|
||||
# ── Save admin credentials ──────────────────────
|
||||
CREDS_FILE="/var/lib/secrets/wordpress-admin"
|
||||
cat > "$CREDS_FILE" << CREDS
|
||||
WordPress Admin Credentials
|
||||
═══════════════════════════
|
||||
URL: https://$DOMAIN/wp-admin/
|
||||
Username: $ADMIN_USER
|
||||
Password: $ADMIN_PASS
|
||||
Email: $ADMIN_EMAIL
|
||||
CREDS
|
||||
chmod 600 "$CREDS_FILE"
|
||||
|
||||
echo ""
|
||||
echo "══════════════════════════════════════════════"
|
||||
echo " WordPress installation complete!"
|
||||
echo ""
|
||||
echo " URL: https://$DOMAIN/wp-admin/"
|
||||
echo " Username: $ADMIN_USER"
|
||||
echo " Password: $ADMIN_PASS"
|
||||
echo ""
|
||||
echo " Credentials saved to: $CREDS_FILE"
|
||||
echo "══════════════════════════════════════════════"
|
||||
'';
|
||||
};
|
||||
|
||||
# ── Ensure directories ────────────────────────────────────
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/www 0755 caddy root -"
|
||||
"d /var/lib/www/wordpress 0755 caddy root -"
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
wp-cli
|
||||
unzip
|
||||
];
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user