From 27f133efd85814f4f9602f212d0a018b3c0bd24b Mon Sep 17 00:00:00 2001 From: padreug Date: Tue, 23 Dec 2025 13:14:14 +0100 Subject: [PATCH 01/10] lamassu: use nix-bitcoin secrets for database password MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded database password with nix-bitcoin secrets pattern: - Add lamassu-db-password secret (readable by lamassu user and postgres) - Generate random 20-char password using makePasswordSecret - Read password at runtime in service wrapper scripts - Update lamassu-postgres-setup to read password from secrets - Update helper scripts to read password at runtime The password is now automatically generated on first deploy and stored in ${secretsDir}/lamassu-db-password. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- modules/lamassu-lnbits.nix | 49 ++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/modules/lamassu-lnbits.nix b/modules/lamassu-lnbits.nix index 82fbbd5..7bb6b4e 100644 --- a/modules/lamassu-lnbits.nix +++ b/modules/lamassu-lnbits.nix @@ -104,11 +104,8 @@ in description = "PostgreSQL username"; }; - password = mkOption { - type = types.str; - default = "lamassu123"; - description = "PostgreSQL password for lamassu-server user"; - }; + # Password is managed by nix-bitcoin secrets system. + # See: ${secretsDir}/lamassu-db-password }; hostname = mkOption { @@ -187,10 +184,15 @@ in user = cfg.user; permissions = "444"; # World readable (it's a public cert) }; + lamassu-db-password = { + user = cfg.user; + group = "postgres"; # PostgreSQL needs to read this too + }; }; nix-bitcoin.generateSecretsCmds.lamassu = '' makeCert lamassu '${nbLib.mkCertExtraAltNames cfg.certificate}' + makePasswordSecret lamassu-db-password ''; # ═══════════════════════════════════════════════════════════════════════════ @@ -216,10 +218,6 @@ in host all all 127.0.0.1/32 md5 host all all ::1/128 md5 ''; - # Set initial password for lamassu-server user - initialScript = pkgs.writeText "postgres-init.sql" '' - ALTER USER "${cfg.database.user}" WITH PASSWORD '${cfg.database.password}'; - ''; }; # Create system users and groups @@ -249,11 +247,11 @@ in "Z '${cfg.package}' 0755 ${cfg.user} ${cfg.group} - -" ]; - # Service to set PostgreSQL password + # Service to set PostgreSQL password from nix-bitcoin secrets systemd.services.lamassu-postgres-setup = { description = "Setup PostgreSQL password for lamassu-server"; wantedBy = [ "multi-user.target" ]; - after = [ "postgresql.service" ]; + after = [ "postgresql.service" "nix-bitcoin-secrets.target" ]; wants = [ "postgresql.service" ]; serviceConfig = { Type = "oneshot"; @@ -261,11 +259,12 @@ in User = "postgres"; }; script = '' - # Wait for user to exist, then set password + # Wait for user to exist, then set password from secrets for i in {1..30}; do if ${pkgs.postgresql}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${cfg.database.user}'" | grep -q 1; then echo "Setting password for ${cfg.database.user}..." - ${pkgs.postgresql}/bin/psql -c "ALTER USER \"${cfg.database.user}\" WITH PASSWORD '${cfg.database.password}';" + password=$(cat ${secretsDir}/lamassu-db-password) + ${pkgs.postgresql}/bin/psql -c "ALTER USER \"${cfg.database.user}\" WITH PASSWORD '$password';" exit 0 fi echo "Waiting for user ${cfg.database.user} to be created (attempt $i/30)..." @@ -286,12 +285,11 @@ in environment = { NODE_ENV = "production"; - # Database configuration (using TCP with password auth) + # Database configuration (password read at runtime from secrets) POSTGRES_HOST = "127.0.0.1"; POSTGRES_PORT = "5432"; POSTGRES_DB = cfg.database.name; POSTGRES_USER = cfg.database.user; - POSTGRES_PASSWORD = cfg.database.password; # Server configuration SERVER_PORT = toString cfg.serverPort; @@ -329,8 +327,10 @@ in #!/bin/bash set -euo pipefail export PATH=${pkgs.nodejs_22}/bin:$PATH - # Use TCP connection to localhost with password - export DATABASE_URL="postgresql://${cfg.database.user}:${cfg.database.password}@127.0.0.1:5432/${cfg.database.name}" + # Read database password from nix-bitcoin secrets + DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) + export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" + export POSTGRES_PASSWORD="$DB_PASSWORD" export NODE_PATH=${cfg.package}/node_modules:${cfg.package}/packages/server/node_modules cd ${cfg.package} exec "$@" @@ -391,12 +391,11 @@ in CA_PATH = cfg.certPath; CERT_PATH = cfg.certPath; KEY_PATH = cfg.keyPath; - # Database configuration (using TCP with password auth) + # Database configuration (password read at runtime from secrets) POSTGRES_HOST = "127.0.0.1"; POSTGRES_PORT = "5432"; POSTGRES_DB = cfg.database.name; POSTGRES_USER = cfg.database.user; - POSTGRES_PASSWORD = cfg.database.password; MNEMONIC_PATH = "${cfg.dataDir}/lamassu-mnemonic"; SKIP_2FA = if cfg.skip2FA then "true" else "false"; # Data directories @@ -411,8 +410,10 @@ in #!/bin/bash set -euo pipefail export PATH=${pkgs.nodejs_22}/bin:$PATH - # Use TCP connection to localhost with password - export DATABASE_URL="postgresql://${cfg.database.user}:${cfg.database.password}@127.0.0.1:5432/${cfg.database.name}" + # Read database password from nix-bitcoin secrets + DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) + export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" + export POSTGRES_PASSWORD="$DB_PASSWORD" export NODE_PATH=${cfg.package}/node_modules:${cfg.package}/packages/admin-server/node_modules cd ${cfg.package} exec "$@" @@ -444,14 +445,16 @@ in nodePackages.pnpm postgresql (writeShellScriptBin "lamassu-register-user" '' + # Read database password from nix-bitcoin secrets + DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) export NODE_PATH="${cfg.package}/node_modules:${cfg.package}/packages/server/node_modules" - export DATABASE_URL="postgresql://${cfg.database.user}:${cfg.database.password}@127.0.0.1:5432/${cfg.database.name}" + export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" export HOSTNAME="${cfg.hostname}" export POSTGRES_HOST="127.0.0.1" export POSTGRES_PORT="5432" export POSTGRES_DB="${cfg.database.name}" export POSTGRES_USER="${cfg.database.user}" - export POSTGRES_PASSWORD="${cfg.database.password}" + export POSTGRES_PASSWORD="$DB_PASSWORD" export SKIP_2FA="${if cfg.skip2FA then "true" else "false"}" sudo -E -u ${cfg.user} bash -c "cd ${cfg.package}/packages/server && ${pkgs.nodejs_22}/bin/node bin/lamassu-register \"\$@\"" -- "$@" From 1bbd7d6bc50d5841e75d57d7b845b94c97a00169 Mon Sep 17 00:00:00 2001 From: padreug Date: Tue, 23 Dec 2025 13:20:01 +0100 Subject: [PATCH 02/10] lamassu: switch to peer authentication for PostgreSQL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace password-based TCP auth with Unix socket peer authentication: - Remove lamassu-db-password secret (no password needed) - Remove lamassu-postgres-setup service entirely - Use DATABASE_URL with Unix socket: postgresql://user@/db?host=/run/postgresql - Remove POSTGRES_HOST, POSTGRES_PORT, POSTGRES_PASSWORD env vars This follows the same pattern as btcpayserver and simplifies the module significantly. Peer auth uses OS-level user authentication via Unix socket. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- modules/lamassu-lnbits.nix | 79 ++++++-------------------------------- 1 file changed, 12 insertions(+), 67 deletions(-) diff --git a/modules/lamassu-lnbits.nix b/modules/lamassu-lnbits.nix index 7bb6b4e..ec25f2b 100644 --- a/modules/lamassu-lnbits.nix +++ b/modules/lamassu-lnbits.nix @@ -101,11 +101,8 @@ in user = mkOption { type = types.str; default = cfg.user; - description = "PostgreSQL username"; + description = "PostgreSQL username (uses peer authentication via Unix socket)"; }; - - # Password is managed by nix-bitcoin secrets system. - # See: ${secretsDir}/lamassu-db-password }; hostname = mkOption { @@ -184,15 +181,10 @@ in user = cfg.user; permissions = "444"; # World readable (it's a public cert) }; - lamassu-db-password = { - user = cfg.user; - group = "postgres"; # PostgreSQL needs to read this too - }; }; nix-bitcoin.generateSecretsCmds.lamassu = '' makeCert lamassu '${nbLib.mkCertExtraAltNames cfg.certificate}' - makePasswordSecret lamassu-db-password ''; # ═══════════════════════════════════════════════════════════════════════════ @@ -200,7 +192,7 @@ in # NOTE: Nginx reverse proxy is disabled. See docs/lamassu-future-nginx.md # for future implementation when --ui-port is added to upstream. - # Enable PostgreSQL + # Enable PostgreSQL with peer authentication (no password needed) services.postgresql = { enable = true; package = pkgs.postgresql_15; @@ -211,13 +203,6 @@ in ensureDBOwnership = true; } ]; - # Enable password authentication for localhost connections - authentication = pkgs.lib.mkOverride 10 '' - # TYPE DATABASE USER ADDRESS METHOD - local all all peer - host all all 127.0.0.1/32 md5 - host all all ::1/128 md5 - ''; }; # Create system users and groups @@ -247,47 +232,17 @@ in "Z '${cfg.package}' 0755 ${cfg.user} ${cfg.group} - -" ]; - # Service to set PostgreSQL password from nix-bitcoin secrets - systemd.services.lamassu-postgres-setup = { - description = "Setup PostgreSQL password for lamassu-server"; - wantedBy = [ "multi-user.target" ]; - after = [ "postgresql.service" "nix-bitcoin-secrets.target" ]; - wants = [ "postgresql.service" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - User = "postgres"; - }; - script = '' - # Wait for user to exist, then set password from secrets - for i in {1..30}; do - if ${pkgs.postgresql}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${cfg.database.user}'" | grep -q 1; then - echo "Setting password for ${cfg.database.user}..." - password=$(cat ${secretsDir}/lamassu-db-password) - ${pkgs.postgresql}/bin/psql -c "ALTER USER \"${cfg.database.user}\" WITH PASSWORD '$password';" - exit 0 - fi - echo "Waiting for user ${cfg.database.user} to be created (attempt $i/30)..." - sleep 1 - done - echo "ERROR: User ${cfg.database.user} was not created after 30 seconds" - exit 1 - ''; - }; - # Main lamassu server service systemd.services.lamassu-server = { description = "Lamassu Bitcoin ATM Server"; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" "postgresql.service" "lamassu-postgres-setup.service" "nix-bitcoin-secrets.target" ]; - wants = [ "postgresql.service" "lamassu-postgres-setup.service" ]; + after = [ "network.target" "postgresql.service" "nix-bitcoin-secrets.target" ]; + wants = [ "postgresql.service" ]; environment = { NODE_ENV = "production"; - # Database configuration (password read at runtime from secrets) - POSTGRES_HOST = "127.0.0.1"; - POSTGRES_PORT = "5432"; + # Database configuration (peer auth via Unix socket) POSTGRES_DB = cfg.database.name; POSTGRES_USER = cfg.database.user; @@ -327,10 +282,8 @@ in #!/bin/bash set -euo pipefail export PATH=${pkgs.nodejs_22}/bin:$PATH - # Read database password from nix-bitcoin secrets - DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) - export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" - export POSTGRES_PASSWORD="$DB_PASSWORD" + # Use Unix socket for peer authentication (no password needed) + export DATABASE_URL="postgresql://${cfg.database.user}@/${cfg.database.name}?host=/run/postgresql" export NODE_PATH=${cfg.package}/node_modules:${cfg.package}/packages/server/node_modules cd ${cfg.package} exec "$@" @@ -391,9 +344,7 @@ in CA_PATH = cfg.certPath; CERT_PATH = cfg.certPath; KEY_PATH = cfg.keyPath; - # Database configuration (password read at runtime from secrets) - POSTGRES_HOST = "127.0.0.1"; - POSTGRES_PORT = "5432"; + # Database configuration (peer auth via Unix socket) POSTGRES_DB = cfg.database.name; POSTGRES_USER = cfg.database.user; MNEMONIC_PATH = "${cfg.dataDir}/lamassu-mnemonic"; @@ -410,10 +361,8 @@ in #!/bin/bash set -euo pipefail export PATH=${pkgs.nodejs_22}/bin:$PATH - # Read database password from nix-bitcoin secrets - DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) - export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" - export POSTGRES_PASSWORD="$DB_PASSWORD" + # Use Unix socket for peer authentication (no password needed) + export DATABASE_URL="postgresql://${cfg.database.user}@/${cfg.database.name}?host=/run/postgresql" export NODE_PATH=${cfg.package}/node_modules:${cfg.package}/packages/admin-server/node_modules cd ${cfg.package} exec "$@" @@ -445,16 +394,12 @@ in nodePackages.pnpm postgresql (writeShellScriptBin "lamassu-register-user" '' - # Read database password from nix-bitcoin secrets - DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) + # Use Unix socket for peer authentication (no password needed) export NODE_PATH="${cfg.package}/node_modules:${cfg.package}/packages/server/node_modules" - export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" + export DATABASE_URL="postgresql://${cfg.database.user}@/${cfg.database.name}?host=/run/postgresql" export HOSTNAME="${cfg.hostname}" - export POSTGRES_HOST="127.0.0.1" - export POSTGRES_PORT="5432" export POSTGRES_DB="${cfg.database.name}" export POSTGRES_USER="${cfg.database.user}" - export POSTGRES_PASSWORD="$DB_PASSWORD" export SKIP_2FA="${if cfg.skip2FA then "true" else "false"}" sudo -E -u ${cfg.user} bash -c "cd ${cfg.package}/packages/server && ${pkgs.nodejs_22}/bin/node bin/lamassu-register \"\$@\"" -- "$@" From 2b46736e69689fd2b08508b5f81b8a21e3b1c786 Mon Sep 17 00:00:00 2001 From: padreug Date: Tue, 23 Dec 2025 13:26:11 +0100 Subject: [PATCH 03/10] Revert "lamassu: switch to peer authentication for PostgreSQL" This reverts commit a819ec5ad2fef050fd878afde8243576ebaa7f88. --- modules/lamassu-lnbits.nix | 79 ++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/modules/lamassu-lnbits.nix b/modules/lamassu-lnbits.nix index ec25f2b..7bb6b4e 100644 --- a/modules/lamassu-lnbits.nix +++ b/modules/lamassu-lnbits.nix @@ -101,8 +101,11 @@ in user = mkOption { type = types.str; default = cfg.user; - description = "PostgreSQL username (uses peer authentication via Unix socket)"; + description = "PostgreSQL username"; }; + + # Password is managed by nix-bitcoin secrets system. + # See: ${secretsDir}/lamassu-db-password }; hostname = mkOption { @@ -181,10 +184,15 @@ in user = cfg.user; permissions = "444"; # World readable (it's a public cert) }; + lamassu-db-password = { + user = cfg.user; + group = "postgres"; # PostgreSQL needs to read this too + }; }; nix-bitcoin.generateSecretsCmds.lamassu = '' makeCert lamassu '${nbLib.mkCertExtraAltNames cfg.certificate}' + makePasswordSecret lamassu-db-password ''; # ═══════════════════════════════════════════════════════════════════════════ @@ -192,7 +200,7 @@ in # NOTE: Nginx reverse proxy is disabled. See docs/lamassu-future-nginx.md # for future implementation when --ui-port is added to upstream. - # Enable PostgreSQL with peer authentication (no password needed) + # Enable PostgreSQL services.postgresql = { enable = true; package = pkgs.postgresql_15; @@ -203,6 +211,13 @@ in ensureDBOwnership = true; } ]; + # Enable password authentication for localhost connections + authentication = pkgs.lib.mkOverride 10 '' + # TYPE DATABASE USER ADDRESS METHOD + local all all peer + host all all 127.0.0.1/32 md5 + host all all ::1/128 md5 + ''; }; # Create system users and groups @@ -232,17 +247,47 @@ in "Z '${cfg.package}' 0755 ${cfg.user} ${cfg.group} - -" ]; + # Service to set PostgreSQL password from nix-bitcoin secrets + systemd.services.lamassu-postgres-setup = { + description = "Setup PostgreSQL password for lamassu-server"; + wantedBy = [ "multi-user.target" ]; + after = [ "postgresql.service" "nix-bitcoin-secrets.target" ]; + wants = [ "postgresql.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "postgres"; + }; + script = '' + # Wait for user to exist, then set password from secrets + for i in {1..30}; do + if ${pkgs.postgresql}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${cfg.database.user}'" | grep -q 1; then + echo "Setting password for ${cfg.database.user}..." + password=$(cat ${secretsDir}/lamassu-db-password) + ${pkgs.postgresql}/bin/psql -c "ALTER USER \"${cfg.database.user}\" WITH PASSWORD '$password';" + exit 0 + fi + echo "Waiting for user ${cfg.database.user} to be created (attempt $i/30)..." + sleep 1 + done + echo "ERROR: User ${cfg.database.user} was not created after 30 seconds" + exit 1 + ''; + }; + # Main lamassu server service systemd.services.lamassu-server = { description = "Lamassu Bitcoin ATM Server"; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" "postgresql.service" "nix-bitcoin-secrets.target" ]; - wants = [ "postgresql.service" ]; + after = [ "network.target" "postgresql.service" "lamassu-postgres-setup.service" "nix-bitcoin-secrets.target" ]; + wants = [ "postgresql.service" "lamassu-postgres-setup.service" ]; environment = { NODE_ENV = "production"; - # Database configuration (peer auth via Unix socket) + # Database configuration (password read at runtime from secrets) + POSTGRES_HOST = "127.0.0.1"; + POSTGRES_PORT = "5432"; POSTGRES_DB = cfg.database.name; POSTGRES_USER = cfg.database.user; @@ -282,8 +327,10 @@ in #!/bin/bash set -euo pipefail export PATH=${pkgs.nodejs_22}/bin:$PATH - # Use Unix socket for peer authentication (no password needed) - export DATABASE_URL="postgresql://${cfg.database.user}@/${cfg.database.name}?host=/run/postgresql" + # Read database password from nix-bitcoin secrets + DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) + export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" + export POSTGRES_PASSWORD="$DB_PASSWORD" export NODE_PATH=${cfg.package}/node_modules:${cfg.package}/packages/server/node_modules cd ${cfg.package} exec "$@" @@ -344,7 +391,9 @@ in CA_PATH = cfg.certPath; CERT_PATH = cfg.certPath; KEY_PATH = cfg.keyPath; - # Database configuration (peer auth via Unix socket) + # Database configuration (password read at runtime from secrets) + POSTGRES_HOST = "127.0.0.1"; + POSTGRES_PORT = "5432"; POSTGRES_DB = cfg.database.name; POSTGRES_USER = cfg.database.user; MNEMONIC_PATH = "${cfg.dataDir}/lamassu-mnemonic"; @@ -361,8 +410,10 @@ in #!/bin/bash set -euo pipefail export PATH=${pkgs.nodejs_22}/bin:$PATH - # Use Unix socket for peer authentication (no password needed) - export DATABASE_URL="postgresql://${cfg.database.user}@/${cfg.database.name}?host=/run/postgresql" + # Read database password from nix-bitcoin secrets + DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) + export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" + export POSTGRES_PASSWORD="$DB_PASSWORD" export NODE_PATH=${cfg.package}/node_modules:${cfg.package}/packages/admin-server/node_modules cd ${cfg.package} exec "$@" @@ -394,12 +445,16 @@ in nodePackages.pnpm postgresql (writeShellScriptBin "lamassu-register-user" '' - # Use Unix socket for peer authentication (no password needed) + # Read database password from nix-bitcoin secrets + DB_PASSWORD=$(cat ${secretsDir}/lamassu-db-password) export NODE_PATH="${cfg.package}/node_modules:${cfg.package}/packages/server/node_modules" - export DATABASE_URL="postgresql://${cfg.database.user}@/${cfg.database.name}?host=/run/postgresql" + export DATABASE_URL="postgresql://${cfg.database.user}:$DB_PASSWORD@127.0.0.1:5432/${cfg.database.name}" export HOSTNAME="${cfg.hostname}" + export POSTGRES_HOST="127.0.0.1" + export POSTGRES_PORT="5432" export POSTGRES_DB="${cfg.database.name}" export POSTGRES_USER="${cfg.database.user}" + export POSTGRES_PASSWORD="$DB_PASSWORD" export SKIP_2FA="${if cfg.skip2FA then "true" else "false"}" sudo -E -u ${cfg.user} bash -c "cd ${cfg.package}/packages/server && ${pkgs.nodejs_22}/bin/node bin/lamassu-register \"\$@\"" -- "$@" From 4db5e80ac827e861de9da4480d12e59e4a229e3a Mon Sep 17 00:00:00 2001 From: padreug Date: Tue, 23 Dec 2025 13:27:54 +0100 Subject: [PATCH 04/10] lamassu: document future peer authentication implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream lamassu-server doesn't support DATABASE_URL, so peer auth via Unix socket isn't currently possible. Document the required upstream changes and module updates for future implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/lamassu-future-peer-auth.md | 167 +++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 docs/lamassu-future-peer-auth.md diff --git a/docs/lamassu-future-peer-auth.md b/docs/lamassu-future-peer-auth.md new file mode 100644 index 0000000..88cc9fc --- /dev/null +++ b/docs/lamassu-future-peer-auth.md @@ -0,0 +1,167 @@ +# Lamassu Server: Future Peer Authentication Implementation + +This document describes how to implement PostgreSQL peer authentication (Unix socket) for the lamassu-server module in the future. + +## Current Limitation + +The lamassu-server's `constants.js` builds the database URL from individual environment variables: + +```javascript +const POSTGRES_USER = process.env.POSTGRES_USER +const POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD +const POSTGRES_HOST = process.env.POSTGRES_HOST +const POSTGRES_PORT = process.env.POSTGRES_PORT +const POSTGRES_DB = process.env.POSTGRES_DB + +const PSQL_URL = `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}` +``` + +This means: +- `DATABASE_URL` environment variable is **not supported** +- Unix socket connections are **not possible** (setting `POSTGRES_HOST=/run/postgresql` produces invalid URL syntax) +- Password authentication via TCP is **required** + +## Required Upstream Changes + +### 1. Modify `packages/server/lib/constants.js` + +Add support for `DATABASE_URL` environment variable: + +```javascript +const POSTGRES_USER = process.env.POSTGRES_USER +const POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD +const POSTGRES_HOST = process.env.POSTGRES_HOST +const POSTGRES_PORT = process.env.POSTGRES_PORT +const POSTGRES_DB = process.env.POSTGRES_DB + +// Support DATABASE_URL for Unix socket connections (peer auth) +const PSQL_URL = process.env.DATABASE_URL || + `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}` +``` + +### 2. Update nix-bitcoin module + +Once upstream supports `DATABASE_URL`, update `modules/lamassu-lnbits.nix`: + +#### Remove password secret + +```nix +nix-bitcoin.secrets = { + lamassu-key.user = cfg.user; + lamassu-cert = { + user = cfg.user; + permissions = "444"; + }; + # Remove: lamassu-db-password +}; + +nix-bitcoin.generateSecretsCmds.lamassu = '' + makeCert lamassu '${nbLib.mkCertExtraAltNames cfg.certificate}' + # Remove: makePasswordSecret lamassu-db-password +''; +``` + +#### Remove lamassu-postgres-setup service + +Delete the entire `systemd.services.lamassu-postgres-setup` block. + +#### Simplify PostgreSQL config + +```nix +services.postgresql = { + enable = true; + package = pkgs.postgresql_15; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { + name = cfg.database.user; + ensureDBOwnership = true; + } + ]; + # No custom authentication needed - default peer auth works +}; +``` + +#### Update service wrapper scripts + +```nix +lamassuEnv = pkgs.writeShellScript "lamassu-env" '' + #!/bin/bash + set -euo pipefail + export PATH=${pkgs.nodejs_22}/bin:$PATH + # Use Unix socket for peer authentication (no password needed) + export DATABASE_URL="postgresql://${cfg.database.user}@/${cfg.database.name}?host=/run/postgresql" + export NODE_PATH=${cfg.package}/node_modules:${cfg.package}/packages/server/node_modules + cd ${cfg.package} + exec "$@" +''; +``` + +#### Remove password-related env vars + +Remove from service `environment` blocks: +- `POSTGRES_HOST` +- `POSTGRES_PORT` +- `POSTGRES_PASSWORD` + +## Benefits of Peer Authentication + +1. **No password to manage** - No secrets generation, storage, or rotation +2. **More secure** - Authentication handled by OS kernel, not application +3. **Simpler module** - ~55 fewer lines of code +4. **Consistent with btcpayserver** - Same pattern used by other nix-bitcoin modules + +## Architecture Comparison + +### Current (password auth via TCP) + +``` +┌─────────────────────────────────────────────────────────────┐ +│ lamassu-server │ +│ └── connects to 127.0.0.1:5432 with password │ +│ │ │ +│ ▼ │ +│ PostgreSQL (TCP) │ +│ └── validates password via md5 auth │ +└─────────────────────────────────────────────────────────────┘ + +Requires: + - lamassu-db-password secret + - lamassu-postgres-setup service + - POSTGRES_HOST, POSTGRES_PORT, POSTGRES_PASSWORD env vars +``` + +### Future (peer auth via Unix socket) + +``` +┌─────────────────────────────────────────────────────────────┐ +│ lamassu-server (runs as user: lamassu-server) │ +│ └── connects to /run/postgresql socket │ +│ │ │ +│ ▼ │ +│ PostgreSQL (Unix socket) │ +│ └── validates OS user matches DB user (peer auth) │ +└─────────────────────────────────────────────────────────────┘ + +Requires: + - Nothing extra! Default NixOS PostgreSQL config works. +``` + +## DATABASE_URL Format for Unix Socket + +The node-postgres library supports Unix sockets via the `host` query parameter: + +``` +postgresql://username@/database?host=/run/postgresql +``` + +Components: +- `username` - PostgreSQL user (must match OS user for peer auth) +- `database` - Database name +- `host=/run/postgresql` - Path to Unix socket directory + +## Reference + +- btcpayserver module uses peer auth: `postgres=User ID=btcpayserver;Host=/run/postgresql;Database=btcpaydb` +- node-postgres Unix socket docs: https://node-postgres.com/features/connecting +- pg-promise (used by lamassu): https://github.com/vitaly-t/pg-promise From 9d2884b6f78fdbcbf2523c0cc7bcffbfb8b2cc97 Mon Sep 17 00:00:00 2001 From: padreug Date: Tue, 23 Dec 2025 13:49:43 +0100 Subject: [PATCH 05/10] lamassu: register module in modules.nix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this, the services.lamassu-server option doesn't exist. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- modules/modules.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/modules.nix b/modules/modules.nix index 1b3c204..1759a1b 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -28,6 +28,7 @@ ./joinmarket.nix ./joinmarket-ob-watcher.nix ./hardware-wallets.nix + ./lamassu-lnbits.nix # Support features ./versioning.nix From ebd13a2c877c9accce178ad90b3e9134c307e948 Mon Sep 17 00:00:00 2001 From: padreug Date: Tue, 23 Dec 2025 20:00:21 +0100 Subject: [PATCH 06/10] rtl: change default port from 3000 to 3001 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid conflict with lamassu-server which also defaults to port 3000. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- modules/rtl.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rtl.nix b/modules/rtl.nix index 60a3bca..9d7df66 100644 --- a/modules/rtl.nix +++ b/modules/rtl.nix @@ -11,7 +11,7 @@ let }; port = mkOption { type = types.port; - default = 3000; + default = 3001; description = "HTTP server port."; }; dataDir = mkOption { From ab188f03f8c07a75ab6ff106a61e95b407feeb49 Mon Sep 17 00:00:00 2001 From: padreug Date: Wed, 24 Dec 2025 17:13:26 +0100 Subject: [PATCH 07/10] lnd: use optionals instead of optional for list additions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use optionals with explicit list syntax for requires, after, and extraGroups. This makes it clearer that we're conditionally adding elements to a list. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- modules/lnd.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lnd.nix b/modules/lnd.nix index 3482cce..163d547 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -261,8 +261,8 @@ in { systemd.services.lnd = { wantedBy = [ "multi-user.target" ]; - requires = optional (cfg.backend == "bitcoind") "bitcoind.service"; - after = optional (cfg.backend == "bitcoind") "bitcoind.service" ++ [ "nix-bitcoin-secrets.target" ]; + requires = optionals (cfg.backend == "bitcoind") [ "bitcoind.service" ]; + after = optionals (cfg.backend == "bitcoind") [ "bitcoind.service" ] ++ [ "nix-bitcoin-secrets.target" ]; preStart = '' install -m600 ${configFile} '${cfg.dataDir}/lnd.conf' ${optionalString (cfg.backend == "bitcoind") '' @@ -324,7 +324,7 @@ in { users.users.${cfg.user} = { isSystemUser = true; group = cfg.group; - extraGroups = optional (cfg.backend == "bitcoind") "bitcoinrpc-public"; + extraGroups = optionals (cfg.backend == "bitcoind") [ "bitcoinrpc-public" ]; home = cfg.dataDir; # lnd creates .lnd dir in HOME }; users.groups.${cfg.group} = {}; From 7d06fed28cdf60bbbd6ed218f39f9dc0a689198e Mon Sep 17 00:00:00 2001 From: padreug Date: Wed, 24 Dec 2025 17:19:56 +0100 Subject: [PATCH 08/10] lnd: rename neutrino.addpeers to neutrino.peers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use simpler naming for the NixOS option. The option defines which peers to connect to, not the action of adding them. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- modules/lnd.nix | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/lnd.nix b/modules/lnd.nix index 163d547..d548e11 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -150,12 +150,12 @@ let description = "The backend to use for fetching blockchain data."; }; neutrino = { - addpeers = mkOption { + peers = mkOption { type = types.listOf types.str; default = []; example = [ "192.168.1.1:8333" "btcd.example.com:8333" ]; description = '' - List of Bitcoin full node peers to connect to via neutrino.addpeer. + List of Bitcoin full node peers to connect to for neutrino. Multiple peers provide redundancy for maximum uptime. ''; }; @@ -206,7 +206,7 @@ let bitcoind.zmqpubrawtx=${zmqHandleSpecialAddress bitcoind.zmqpubrawtx} '' else '' bitcoin.node=neutrino - ${lib.concatMapStringsSep "\n" (peer: "neutrino.addpeer=${peer}") cfg.neutrino.addpeers} + ${lib.concatMapStringsSep "\n" (peer: "neutrino.addpeer=${peer}") cfg.neutrino.peers} fee.url=${cfg.neutrino.feeUrl} ''} @@ -232,10 +232,10 @@ in { services.lnd.port to a port other than 9735. ''; } - { assertion = cfg.backend != "neutrino" || cfg.neutrino.addpeers != []; + { assertion = cfg.backend != "neutrino" || cfg.neutrino.peers != []; message = '' When using neutrino backend, you must configure at least one peer - in services.lnd.neutrino.addpeers. + in services.lnd.neutrino.peers. ''; } ]; From a019f79283cb791f2387c4a79c6c5bb03866877c Mon Sep 17 00:00:00 2001 From: padreug Date: Wed, 24 Dec 2025 17:38:02 +0100 Subject: [PATCH 09/10] lnd: add neutrino.maxPeers option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow configuring the maximum number of inbound and outbound peers for neutrino. Default is 8, matching lnd's default. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- modules/lnd.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/lnd.nix b/modules/lnd.nix index d548e11..38f00bc 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -167,6 +167,13 @@ let Required because neutrino doesn't have access to mempool data. ''; }; + maxPeers = mkOption { + type = types.int; + default = 8; + description = '' + Maximum number of inbound and outbound peers for neutrino. + ''; + }; }; }; @@ -207,6 +214,7 @@ let '' else '' bitcoin.node=neutrino ${lib.concatMapStringsSep "\n" (peer: "neutrino.addpeer=${peer}") cfg.neutrino.peers} + neutrino.maxpeers=${toString cfg.neutrino.maxPeers} fee.url=${cfg.neutrino.feeUrl} ''} From 800afe647fd34ce5216864f7db14341cd5734dd7 Mon Sep 17 00:00:00 2001 From: padreug Date: Wed, 24 Dec 2025 17:38:21 +0100 Subject: [PATCH 10/10] examples: add neutrino backend example for lnd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show users how to configure lnd with the neutrino backend instead of bitcoind. This is useful for resource-constrained systems that can't run a full Bitcoin node, but provides less privacy and security than a local bitcoind. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- examples/configuration.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/configuration.nix b/examples/configuration.nix index 125beaa..b242392 100644 --- a/examples/configuration.nix +++ b/examples/configuration.nix @@ -80,6 +80,15 @@ # Set this to enable lnd, a lightning implementation written in Go. # services.lnd.enable = true; # + # By default, lnd uses bitcoind as its backend. You can use neutrino instead + # to run lnd without a full Bitcoin node. This is useful for resource-constrained + # systems, but provides less privacy and security than a local bitcoind. + # services.lnd = { + # enable = true; + # backend = "neutrino"; + # neutrino.peers = [ "btcd.example.com:8333" ]; + # }; + # # NOTE: In order to avoid collisions with clightning you must disable clightning or # change the services.clightning.port or services.lnd.port to a port other than # 9735.