lamassu: integrate with nix-bitcoin secrets system for TLS certificates
- Replace runtime SSL generation with nix-bitcoin secrets integration - Add certificate.extraIPs and certificate.extraDomains options (same pattern as LND) - Certificates auto-regenerate when SAN configuration changes - Add certPath and keyPath read-only options - Update nginx and services to use secrets from secretsDir - Add nix-bitcoin-secrets.target dependency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e3db3e58b9
commit
471a4d982f
1 changed files with 62 additions and 57 deletions
|
|
@ -4,24 +4,12 @@ with lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.services.lamassu-server;
|
cfg = config.services.lamassu-server;
|
||||||
secretsDir = "/var/src/secrets"; # krops deploys secrets here
|
nbLib = config.nix-bitcoin.lib;
|
||||||
|
secretsDir = config.nix-bitcoin.secretsDir;
|
||||||
|
|
||||||
# Path to the deployed lamassu-server (krops puts it in /var/src)
|
# Path to the deployed lamassu-server (krops puts it in /var/src)
|
||||||
lamassuServerPath = "/var/src/lamassu-server-built";
|
lamassuServerPath = "/var/src/lamassu-server-built";
|
||||||
|
|
||||||
# Detect if a string is an IP address (simple regex check)
|
|
||||||
isIpAddress = str: builtins.match "([0-9]{1,3}\\.){3}[0-9]{1,3}" str != null;
|
|
||||||
|
|
||||||
# Build Subject Alternative Name (SAN) extension for certificate
|
|
||||||
# Uses IP: prefix for IP addresses, DNS: prefix for hostnames
|
|
||||||
sanEntries =
|
|
||||||
[ (if isIpAddress cfg.hostname then "IP:${cfg.hostname}" else "DNS:${cfg.hostname}") ] ++
|
|
||||||
(optional (cfg.ipAddress != null) "IP:${cfg.ipAddress}") ++
|
|
||||||
(optional (cfg.nginx.enable && cfg.nginx.hostname != null && cfg.nginx.hostname != cfg.hostname)
|
|
||||||
(if isIpAddress cfg.nginx.hostname then "IP:${cfg.nginx.hostname}" else "DNS:${cfg.nginx.hostname}"));
|
|
||||||
|
|
||||||
sanExtension = concatStringsSep "," sanEntries;
|
|
||||||
|
|
||||||
# Basic hardening settings (simplified from nix-bitcoin)
|
# Basic hardening settings (simplified from nix-bitcoin)
|
||||||
defaultHardening = {
|
defaultHardening = {
|
||||||
# Sandboxing
|
# Sandboxing
|
||||||
|
|
@ -123,14 +111,40 @@ in
|
||||||
hostname = mkOption {
|
hostname = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "localhost";
|
default = "localhost";
|
||||||
description = "Hostname for the server (used by application and included in certificate)";
|
description = "Hostname for the server (used by application)";
|
||||||
};
|
};
|
||||||
|
|
||||||
ipAddress = mkOption {
|
# Certificate options (same pattern as LND)
|
||||||
type = types.nullOr types.str;
|
certificate = {
|
||||||
default = null;
|
extraIPs = mkOption {
|
||||||
description = "IP address to include in certificate SAN (optional)";
|
type = with types; listOf str;
|
||||||
example = "192.168.1.100";
|
default = [];
|
||||||
|
example = [ "192.168.1.100" ];
|
||||||
|
description = ''
|
||||||
|
Extra IP addresses to include in the certificate SAN.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
extraDomains = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
default = [];
|
||||||
|
example = [ "lamassu.example.com" ];
|
||||||
|
description = ''
|
||||||
|
Extra domain names to include in the certificate SAN.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Read-only options for certificate paths
|
||||||
|
certPath = mkOption {
|
||||||
|
readOnly = true;
|
||||||
|
default = "${secretsDir}/lamassu-cert";
|
||||||
|
description = "Path to the TLS certificate.";
|
||||||
|
};
|
||||||
|
|
||||||
|
keyPath = mkOption {
|
||||||
|
readOnly = true;
|
||||||
|
default = "${secretsDir}/lamassu-key";
|
||||||
|
description = "Path to the TLS private key.";
|
||||||
};
|
};
|
||||||
|
|
||||||
nginx = {
|
nginx = {
|
||||||
|
|
@ -152,6 +166,24 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
# nix-bitcoin secrets integration
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
nix-bitcoin.secrets = {
|
||||||
|
lamassu-key.user = cfg.user;
|
||||||
|
lamassu-cert = {
|
||||||
|
user = cfg.user;
|
||||||
|
permissions = "444"; # World readable (it's a public cert)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nix-bitcoin.generateSecretsCmds.lamassu = ''
|
||||||
|
makeCert lamassu '${nbLib.mkCertExtraAltNames cfg.certificate}'
|
||||||
|
'';
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
# Nginx reverse proxy (optional, disabled by default)
|
# Nginx reverse proxy (optional, disabled by default)
|
||||||
services.nginx = mkIf cfg.nginx.enable {
|
services.nginx = mkIf cfg.nginx.enable {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
@ -160,8 +192,8 @@ in
|
||||||
|
|
||||||
virtualHosts.${if cfg.nginx.hostname != null then cfg.nginx.hostname else cfg.hostname} = {
|
virtualHosts.${if cfg.nginx.hostname != null then cfg.nginx.hostname else cfg.hostname} = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
sslCertificate = "${cfg.dataDir}/ssl/cert.pem";
|
sslCertificate = cfg.certPath;
|
||||||
sslCertificateKey = "${cfg.dataDir}/ssl/key.pem";
|
sslCertificateKey = cfg.keyPath;
|
||||||
|
|
||||||
# Route API endpoints to main server (port 3000)
|
# Route API endpoints to main server (port 3000)
|
||||||
locations."/ca" = {
|
locations."/ca" = {
|
||||||
|
|
@ -253,7 +285,6 @@ in
|
||||||
"d '${cfg.dataDir}/photos/idcards' 0770 ${cfg.user} ${cfg.group} - -"
|
"d '${cfg.dataDir}/photos/idcards' 0770 ${cfg.user} ${cfg.group} - -"
|
||||||
"d '${cfg.dataDir}/photos/frontcamera' 0770 ${cfg.user} ${cfg.group} - -"
|
"d '${cfg.dataDir}/photos/frontcamera' 0770 ${cfg.user} ${cfg.group} - -"
|
||||||
"d '${cfg.dataDir}/operator' 0770 ${cfg.user} ${cfg.group} - -"
|
"d '${cfg.dataDir}/operator' 0770 ${cfg.user} ${cfg.group} - -"
|
||||||
"d '${cfg.dataDir}/ssl' 0770 ${cfg.user} ${cfg.group} - -"
|
|
||||||
# Ensure lamassu-server user can read/write to the source directory
|
# Ensure lamassu-server user can read/write to the source directory
|
||||||
"Z '${cfg.package}' 0755 ${cfg.user} ${cfg.group} - -"
|
"Z '${cfg.package}' 0755 ${cfg.user} ${cfg.group} - -"
|
||||||
];
|
];
|
||||||
|
|
@ -289,7 +320,7 @@ in
|
||||||
systemd.services.lamassu-server = {
|
systemd.services.lamassu-server = {
|
||||||
description = "Lamassu Bitcoin ATM Server";
|
description = "Lamassu Bitcoin ATM Server";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network.target" "postgresql.service" "lamassu-postgres-setup.service" ];
|
after = [ "network.target" "postgresql.service" "lamassu-postgres-setup.service" "nix-bitcoin-secrets.target" ];
|
||||||
wants = [ "postgresql.service" "lamassu-postgres-setup.service" ];
|
wants = [ "postgresql.service" "lamassu-postgres-setup.service" ];
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
|
|
@ -307,10 +338,10 @@ in
|
||||||
LOG_LEVEL = cfg.logLevel;
|
LOG_LEVEL = cfg.logLevel;
|
||||||
HOSTNAME = cfg.hostname;
|
HOSTNAME = cfg.hostname;
|
||||||
|
|
||||||
# SSL/TLS certificates
|
# SSL/TLS certificates (from nix-bitcoin secrets)
|
||||||
CA_PATH = "${cfg.dataDir}/ssl/cert.pem";
|
CA_PATH = cfg.certPath;
|
||||||
CERT_PATH = "${cfg.dataDir}/ssl/cert.pem";
|
CERT_PATH = cfg.certPath;
|
||||||
KEY_PATH = "${cfg.dataDir}/ssl/key.pem";
|
KEY_PATH = cfg.keyPath;
|
||||||
|
|
||||||
# Wallet and mnemonic
|
# Wallet and mnemonic
|
||||||
MNEMONIC_PATH = "${cfg.dataDir}/lamassu-mnemonic";
|
MNEMONIC_PATH = "${cfg.dataDir}/lamassu-mnemonic";
|
||||||
|
|
@ -370,7 +401,6 @@ in
|
||||||
|
|
||||||
preStart = ''
|
preStart = ''
|
||||||
mkdir -p ${cfg.dataDir}/logs
|
mkdir -p ${cfg.dataDir}/logs
|
||||||
mkdir -p ${cfg.dataDir}/ssl
|
|
||||||
|
|
||||||
# Wait for PostgreSQL using peer authentication
|
# Wait for PostgreSQL using peer authentication
|
||||||
timeout=30
|
timeout=30
|
||||||
|
|
@ -384,31 +414,6 @@ in
|
||||||
((timeout--))
|
((timeout--))
|
||||||
done
|
done
|
||||||
echo "PostgreSQL is ready"
|
echo "PostgreSQL is ready"
|
||||||
|
|
||||||
# Generate self-signed SSL certificates if they don't exist
|
|
||||||
if [ ! -f ${cfg.dataDir}/ssl/key.pem ]; then
|
|
||||||
echo "Generating self-signed SSL certificates..."
|
|
||||||
echo " Hostname: ${cfg.hostname}"
|
|
||||||
${optionalString (cfg.ipAddress != null) ''
|
|
||||||
echo " IP Address: ${cfg.ipAddress}"
|
|
||||||
''}
|
|
||||||
${optionalString (cfg.nginx.enable && cfg.nginx.hostname != null) ''
|
|
||||||
echo " Nginx hostname: ${cfg.nginx.hostname}"
|
|
||||||
''}
|
|
||||||
|
|
||||||
# Generate certificate with SAN extension
|
|
||||||
${pkgs.openssl}/bin/openssl req -x509 -newkey rsa:4096 \
|
|
||||||
-keyout ${cfg.dataDir}/ssl/key.pem \
|
|
||||||
-out ${cfg.dataDir}/ssl/cert.pem \
|
|
||||||
-days 365 -nodes \
|
|
||||||
-subj "/CN=${cfg.hostname}" \
|
|
||||||
-addext "subjectAltName=${sanExtension}"
|
|
||||||
|
|
||||||
chmod 640 ${cfg.dataDir}/ssl/key.pem # Allow group read for nginx
|
|
||||||
chmod 644 ${cfg.dataDir}/ssl/cert.pem
|
|
||||||
|
|
||||||
echo "Certificate generated with SAN: ${sanExtension}"
|
|
||||||
fi
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -424,9 +429,9 @@ in
|
||||||
ADMIN_SERVER_PORT = toString cfg.adminPort;
|
ADMIN_SERVER_PORT = toString cfg.adminPort;
|
||||||
LOG_LEVEL = cfg.logLevel;
|
LOG_LEVEL = cfg.logLevel;
|
||||||
HOSTNAME = cfg.hostname;
|
HOSTNAME = cfg.hostname;
|
||||||
CA_PATH = "${cfg.dataDir}/ssl/cert.pem";
|
CA_PATH = cfg.certPath;
|
||||||
CERT_PATH = "${cfg.dataDir}/ssl/cert.pem";
|
CERT_PATH = cfg.certPath;
|
||||||
KEY_PATH = "${cfg.dataDir}/ssl/key.pem";
|
KEY_PATH = cfg.keyPath;
|
||||||
# Database configuration (using TCP with password auth)
|
# Database configuration (using TCP with password auth)
|
||||||
POSTGRES_HOST = "127.0.0.1";
|
POSTGRES_HOST = "127.0.0.1";
|
||||||
POSTGRES_PORT = "5432";
|
POSTGRES_PORT = "5432";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue