diff --git a/src/system/machines/server/system.nix b/src/system/machines/server/system.nix index 5dad7cf..20feaed 100644 --- a/src/system/machines/server/system.nix +++ b/src/system/machines/server/system.nix @@ -9,6 +9,11 @@ forgejo.enable = true; frigate.enable = false; immich.enable = true; + bitcoin = { + enable = true; + electrum.enable = true; + clightning.enable = true; + }; backup = { enable = true; diff --git a/src/system/modules/bitcoin/config/bitcoin.conf b/src/system/modules/bitcoin/config/bitcoin.conf index 641827a..756bfc1 100644 --- a/src/system/modules/bitcoin/config/bitcoin.conf +++ b/src/system/modules/bitcoin/config/bitcoin.conf @@ -1,14 +1,12 @@ server=1 -mempoolfullrbf=1 -v2transport=1 - -rpcauth= - +rpccookiefile=/var/lib/bitcoin/.cookie +rpccookieperms=group rpcbind=127.0.0.1 rpcallowip=127.0.0.1 dnsseed=0 +onlynet=onion bind=127.0.0.1 proxy=127.0.0.1:9050 @@ -16,4 +14,5 @@ proxy=127.0.0.1:9050 listen=1 listenonion=1 torcontrol=127.0.0.1:9051 -torenablecircuit=1 + +txindex=1 diff --git a/src/system/modules/bitcoin/default.nix b/src/system/modules/bitcoin/default.nix index 6a4b89e..e7e12a0 100644 --- a/src/system/modules/bitcoin/default.nix +++ b/src/system/modules/bitcoin/default.nix @@ -5,7 +5,7 @@ let cfg = config.modules.system.bitcoin; nginx = config.modules.system.nginx; - home = "/var/lib/bitcoind"; + home = "/var/lib/bitcoin"; bitcoinConf = pkgs.writeTextFile { name = "bitcoin.conf"; @@ -15,16 +15,10 @@ let in { options.modules.system.bitcoin = { enable = mkEnableOption "Bitcoin Server"; }; config = mkIf cfg.enable { - nixpkgs.overlays = [ - (final: prev: { - bitcoind = prev.bitcoind.overrideAttrs (old: rec { - version = "28.0"; - src = fetchTarball { - url = "https://github.com/bitcoin/bitcoin/archive/refs/tags/v${version}.tar.gz"; - sha256 = "sha256-LLtw6pMyqIJ3IWHiK4P3XoifLojB9yMNMo+MGNFGuRY="; - }; - }); - }) + modules.system.tor.enable = true; + + environment.systemPackages = with pkgs; [ + bitcoind ]; users = { @@ -34,6 +28,7 @@ in description = "Bitcoin Core system user"; isSystemUser = true; group = "bitcoin"; + extraGroups = [ "tor" ]; createHome = true; }; "nginx" = { @@ -46,17 +41,18 @@ in "bitcoin" = { members = [ "btc" + config.user.name ]; }; }; }; programs.bash.shellAliases = { - btc = "bitcoind"; + btc = "bitcoin-cli"; }; services.bitcoind = { - "btc" = { + "mainnet" = { enable = true; user = "btc"; group = "bitcoin"; @@ -65,5 +61,20 @@ in pidFile = "${home}/bitcoind.pid"; }; }; + + # Make data dir group-accessible so electrs/clightning can read cookie + systemd.tmpfiles.rules = [ + "d ${home} 0750 btc bitcoin -" + ]; + + systemd.services.bitcoind-mainnet = { + wants = [ "tor.service" ]; + after = [ "tor.service" ]; + serviceConfig.ExecStartPre = "+${pkgs.coreutils}/bin/chmod 750 /var/lib/tor"; + }; + + modules.system.backup.paths = [ + "${home}/wallets" + ]; }; } diff --git a/src/system/modules/bitcoin/modules/clightning/config/lightning.conf b/src/system/modules/bitcoin/modules/clightning/config/lightning.conf new file mode 100644 index 0000000..def24ec --- /dev/null +++ b/src/system/modules/bitcoin/modules/clightning/config/lightning.conf @@ -0,0 +1,31 @@ +alias=OrdSux + +network=bitcoin +bitcoin-datadir=/var/lib/bitcoin +bitcoin-rpcconnect=127.0.0.1 +bitcoin-rpcport=8332 + +lightning-dir=/var/lib/clightning +plugin-dir=/var/lib/clightning/plugins + +log-file=/var/lib/clightning/lightningd.log +log-level=info +rpc-file-mode=0660 + +# Bind RPC locally only +bind-addr=127.0.0.1:9736 + +# Auto-create Tor hidden service for peer connections +addr=autotor:127.0.0.1:9051 + +# Route outbound through Tor +proxy=127.0.0.1:9050 +always-use-proxy=true + +large-channels +fee-base=1000 +fee-per-satoshi=10 +min-capacity-sat=10000 +htlc-minimum-msat=0 +funding-confirms=3 +max-concurrent-htlcs=30 diff --git a/src/system/modules/bitcoin/modules/clightning/default.nix b/src/system/modules/bitcoin/modules/clightning/default.nix new file mode 100644 index 0000000..f052e52 --- /dev/null +++ b/src/system/modules/bitcoin/modules/clightning/default.nix @@ -0,0 +1,106 @@ +{ lib, pkgs, config, ... }: + +with lib; +let + cfg = config.modules.system.bitcoin.clightning; + btc = config.modules.system.bitcoin; + nginx = config.modules.system.nginx; + home = "/var/lib/clightning"; + domain = "ramos.codes"; + + clnrest = pkgs.callPackage ./plugins/clnrest.nix { }; + + clnConfig = pkgs.writeTextFile { + name = "lightning.conf"; + text = '' + ${builtins.readFile ./config/lightning.conf} + bitcoin-cli=${pkgs.bitcoind}/bin/bitcoin-cli + + # CLNRest configuration + clnrest-port=3010 + clnrest-host=127.0.0.1 + clnrest-protocol=https + ''; + }; + +in +{ options.modules.system.bitcoin.clightning = { enable = mkEnableOption "Core Lightning Server"; }; + config = mkIf (cfg.enable && btc.enable) { + environment.systemPackages = with pkgs; [ + clightning + ]; + + users = { + users = { + "clightning" = { + inherit home; + description = "Core Lightning system user"; + isSystemUser = true; + group = "bitcoin"; + extraGroups = [ "tor" ]; + createHome = true; + }; + }; + groups = { + "bitcoin" = { + members = mkAfter [ + "clightning" + ]; + }; + }; + }; + + programs.bash.shellAliases = { + cln = "lightning-cli"; + }; + + systemd.services.lightningd = { + description = "Core Lightning Daemon"; + wantedBy = [ "multi-user.target" ]; + + wants = [ "bitcoind-mainnet.service" "tor.service" ]; + after = [ + "bitcoind-mainnet.service" + "tor.service" + "network.target" + ]; + + serviceConfig = { + ExecStartPre = "+${pkgs.coreutils}/bin/chmod 750 /var/lib/bitcoin /var/lib/tor ${home} ${home}/bitcoin"; + ExecStart = "${pkgs.clightning}/bin/lightningd --conf=${clnConfig}"; + User = "clightning"; + Group = "bitcoin"; + WorkingDirectory = home; + + Type = "simple"; + KillMode = "process"; + TimeoutSec = 60; + Restart = "always"; + RestartSec = 60; + }; + }; + + # Ensure data directory exists with correct permissions + systemd.tmpfiles.rules = mkAfter [ + "d ${home} 0750 clightning bitcoin -" + "d ${home}/plugins 0750 clightning bitcoin -" + "L+ /home/${config.user.name}/.lightning - - - - ${home}" + "L+ ${home}/plugins/clnrest - - - - ${clnrest}/libexec/c-lightning/plugins/clnrest" + ]; + + modules.system.backup.paths = [ + "${home}/bitcoin/hsm_secret" + ]; + + services.nginx.virtualHosts."ln.${domain}" = mkIf nginx.enable { + useACMEHost = domain; + forceSSL = true; + locations."/" = { + proxyPass = "https://127.0.0.1:3010"; + extraConfig = '' + proxy_ssl_verify off; + ''; + }; + }; + }; +} diff --git a/src/system/modules/bitcoin/modules/clightning/plugins/clnrest.nix b/src/system/modules/bitcoin/modules/clightning/plugins/clnrest.nix new file mode 100644 index 0000000..b4124cf --- /dev/null +++ b/src/system/modules/bitcoin/modules/clightning/plugins/clnrest.nix @@ -0,0 +1,54 @@ +{ + lib, + rustPlatform, + fetchFromGitHub, + pkg-config, + openssl, + protobuf, +}: + +rustPlatform.buildRustPackage rec { + pname = "clnrest"; + version = "25.02.2"; + + src = fetchFromGitHub { + owner = "ElementsProject"; + repo = "lightning"; + rev = "v${version}"; + hash = "sha256-SiPYB463l9279+zawsxmql1Ui/dTdah5KgJgmrWsR2A="; + }; + + cargoLock = { + lockFile = "${src}/Cargo.lock"; + }; + + cargoBuildFlags = [ + "-p" + "clnrest" + ]; + cargoTestFlags = [ + "-p" + "clnrest" + ]; + + nativeBuildInputs = [ + pkg-config + protobuf + ]; + + buildInputs = [ openssl ]; + + postInstall = '' + mkdir -p $out/libexec/c-lightning/plugins + mv $out/bin/clnrest $out/libexec/c-lightning/plugins/ + rmdir $out/bin + ''; + + meta = { + description = "Transforms RPC calls into REST APIs"; + homepage = "https://docs.corelightning.org/docs/rest"; + license = lib.licenses.mit; + platforms = lib.platforms.linux; + mainProgram = "clnrest"; + }; +} diff --git a/src/system/modules/bitcoin/modules/electrum/config/config.toml b/src/system/modules/bitcoin/modules/electrum/config/config.toml index c030e25..9f05fe2 100644 --- a/src/system/modules/bitcoin/modules/electrum/config/config.toml +++ b/src/system/modules/bitcoin/modules/electrum/config/config.toml @@ -2,13 +2,12 @@ network = "bitcoin" electrum_rpc_addr = "127.0.0.1:50001" -cookie-file = "/var/lib/bitcoind/.cookie" +cookie_file = "/var/lib/bitcoin/.cookie" db_dir = "/var/lib/electrs" log_filters = "INFO" -timestamp = true -daemon-rpc-addr = "127.0.0.1:8332" -daemon-p2p-addr = "127.0.0.1:8333" -daemon-dir = "/var/lib/bitcoind" +daemon_rpc_addr = "127.0.0.1:8332" +daemon_p2p_addr = "127.0.0.1:8333" +daemon_dir = "/var/lib/bitcoin" diff --git a/src/system/modules/bitcoin/modules/electrum/default.nix b/src/system/modules/bitcoin/modules/electrum/default.nix index 9b210ce..6673f4f 100644 --- a/src/system/modules/bitcoin/modules/electrum/default.nix +++ b/src/system/modules/bitcoin/modules/electrum/default.nix @@ -3,9 +3,11 @@ with lib; let cfg = config.modules.system.bitcoin.electrum; + nginx = config.modules.system.nginx; home = "/var/lib/electrs"; btc = config.modules.system.bitcoin; + domain = "ramos.codes"; electrsConfig = pkgs.writeTextFile { name = "config.toml"; @@ -63,18 +65,20 @@ in systemd.services.electrs = { description = "Electrs Bitcoin Indexer"; + wantedBy = [ "multi-user.target" ]; - script = "${pkgs.electrs}/bin/electrs"; - scriptArgs = "--conf=${electrsConfig}"; - + wants = [ "bitcoind-mainnet.service" ]; after = [ - "bitcoind-btc.service" + "bitcoind-mainnet.service" + "network.target" ]; serviceConfig = { - + ExecStartPre = "+${pkgs.coreutils}/bin/chmod 750 /var/lib/bitcoin"; + ExecStart = "${pkgs.electrs}/bin/electrs --conf=${electrsConfig}"; User = "electrs"; Group = "bitcoin"; + WorkingDirectory = home; Type = "simple"; KillMode = "process"; @@ -82,10 +86,29 @@ in Restart = "always"; RestartSec = 60; }; - requisite = [ - "bitcoind-btc.service" - "network.target" - ]; }; + + # Ensure db directory exists with correct permissions + systemd.tmpfiles.rules = [ + "d ${home} 0750 electrs bitcoin -" + ]; + + # Nginx SSL proxy for Electrum protocol (TCP) + networking.firewall.allowedTCPPorts = mkIf nginx.enable [ 50002 ]; + + services.nginx.streamConfig = mkIf nginx.enable '' + map $ssl_server_name $electrs_backend { + electrum.${domain} 127.0.0.1:50001; + default ""; + } + + server { + listen 50002 ssl; + proxy_pass $electrs_backend; + + ssl_certificate /var/lib/acme/${domain}/fullchain.pem; + ssl_certificate_key /var/lib/acme/${domain}/key.pem; + } + ''; }; } diff --git a/src/system/modules/default.nix b/src/system/modules/default.nix index 13da930..c8ecd1a 100644 --- a/src/system/modules/default.nix +++ b/src/system/modules/default.nix @@ -4,10 +4,11 @@ let entries = builtins.readDir dir; names = builtins.attrNames entries; - isModuleDir = path: + isModuleDir = path: builtins.pathExists path && builtins.readFileType path == "directory" && - builtins.baseNameOf path != "config"; + builtins.baseNameOf path != "config" && + builtins.baseNameOf path != "plugins"; isModule = file: file == "default.nix"; isNix = file: builtins.match ".*\\.nix" file != null && file != "default.nix"; diff --git a/src/system/modules/tor/default.nix b/src/system/modules/tor/default.nix new file mode 100644 index 0000000..37c2e95 --- /dev/null +++ b/src/system/modules/tor/default.nix @@ -0,0 +1,30 @@ +{ pkgs, lib, config, ... }: + +with lib; +let + cfg = config.modules.system.tor; + +in +{ + options.modules.system.tor = { + enable = mkEnableOption "Tor"; + }; + + config = mkIf cfg.enable { + services.tor = { + enable = true; + + client = { + enable = true; + # SOCKS proxy on 127.0.0.1:9050 + }; + + settings = { + ControlPort = 9051; + CookieAuthentication = true; + CookieAuthFileGroupReadable = true; + DataDirectoryGroupReadable = true; + }; + }; + }; +}