From daa3bfbae337679d0e15f024499307bd2fb764a5 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Thu, 10 Oct 2024 12:13:17 +0200 Subject: [PATCH] lndconnect: add clnrest --- dev/dev-scenarios.nix | 10 +++ dev/topics/lndconnect-and-wireguard.sh | 12 +++- docs/services.md | 20 +++--- examples/configuration.nix | 6 +- modules/lndconnect.nix | 87 ++++++++++++++++++++++++-- modules/presets/wireguard.nix | 18 +++++- test/tests.nix | 5 ++ test/tests.py | 5 ++ test/wireguard-lndconnect.nix | 37 ++++++++--- 9 files changed, 171 insertions(+), 29 deletions(-) diff --git a/dev/dev-scenarios.nix b/dev/dev-scenarios.nix index 80e2ea2..e6716ca 100644 --- a/dev/dev-scenarios.nix +++ b/dev/dev-scenarios.nix @@ -66,6 +66,16 @@ with lib; onion = true; }; }; + services.clightning = { + enable = true; + plugins.clnrest = { + enable = true; + lnconnect = { + enable = true; + onion = true; + }; + }; + }; services.clightning-rest = { enable = true; lndconnect = { diff --git a/dev/topics/lndconnect-and-wireguard.sh b/dev/topics/lndconnect-and-wireguard.sh index 7f40ff0..76dc641 100644 --- a/dev/topics/lndconnect-and-wireguard.sh +++ b/dev/topics/lndconnect-and-wireguard.sh @@ -7,13 +7,15 @@ run-tests.sh -s wireguard-lndconnect-online container # 2. Test connecting via Tor # Print QR codes for lnd, clightning-rest connections via Tor c lndconnect +c lnconnect-clnrest c lndconnect-clightning -# Add these to Zeus >= 0.7.1. +# Add these to Zeus >= 0.9.0. # To explicitly check if the connection is successful, press the node logo in the top # left corner, and then "Node Info". # Debug c lndconnect --url +c lnconnect-clnrest --url c lndconnect-clightning --url # 3. Test connecting via WireGuard @@ -33,13 +35,15 @@ c nix-bitcoin-wg-connect --text # Print QR codes for lnd, clightning-rest connections via WireGuard c lndconnect-wg +c lnconnect-clnrest-wg c lndconnect-clightning-wg -# Add these to Zeus >= 0.7.1. -# To explicitly check if the connection is successful, press the node logo in the top +# Add these to Zeus >= 0.9.0. +# To explicitly check if the connection is successful, press the menu button in the top # left corner, and then "Node Info". # Debug c lndconnect-wg --url +c lnconnect-clnrest-wg --url c lndconnect-clightning-wg --url # 3.3.remove external firewall port forward, remove local port forward: @@ -55,6 +59,8 @@ c nodeinfo c lndconnect --url c lndconnect-wg --url +c lndconnect-clnrest --url +c lndconnect-clnrest-wg --url c lndconnect-clightning --url c lndconnect-clightning-wg --url diff --git a/docs/services.md b/docs/services.md index f4a408d..ad719cd 100644 --- a/docs/services.md +++ b/docs/services.md @@ -143,7 +143,7 @@ The default password location is `$secretsDir/rtl-password`. See: [Secrets dir](./configuration.md#secrets-dir) # Use Zeus (mobile lightning wallet) via Tor -1. Install [Zeus](https://zeusln.app) (version ≥ 0.7.1) +1. Install [Zeus](https://zeusln.app) (version ≥ 0.9.0) 2. Edit your `configuration.nix` @@ -161,9 +161,9 @@ See: [Secrets dir](./configuration.md#secrets-dir) Add the following config: ```nix - services.clightning-rest = { + services.clightning.plugins.clnrest = { enable = true; - lndconnect = { + lnconnect = { enable = true; onion = true; }; @@ -182,7 +182,7 @@ See: [Secrets dir](./configuration.md#secrets-dir) ##### For clightning ``` - lndconnect-clightning + lnconnect-clnrest ``` 5. Configure Zeus @@ -212,7 +212,7 @@ There are two ways to establish a secure, direct connection: - Connecting via WireGuard. This approach is simpler and more versatile, and is described in this guide. -1. Install [Zeus](https://zeusln.app) (version ≥ 0.7.1) and +1. Install [Zeus](https://zeusln.app) (version ≥ 0.9.0) and [WireGuard](https://www.wireguard.com/install/) on your mobile device. 2. Add the following to your `configuration.nix`: @@ -229,9 +229,11 @@ There are two ways to establish a secure, direct connection: services.lnd.lndconnect.enable = true; # For clightning - services.clightning-rest = { - enable = true; - lndconnect.enable = true; + services.clightning = { + plugins.clnrest = { + enable = true; + lnconnect.enable = true; + }; }; ``` 3. Deploy your configuration. @@ -275,7 +277,7 @@ There are two ways to establish a secure, direct connection: ##### For clightning ``` - lndconnect-clightning-wg + lnconnect-clnrest-wg ``` Configure Zeus: diff --git a/examples/configuration.nix b/examples/configuration.nix index 2e45f3a..f4871a9 100644 --- a/examples/configuration.nix +++ b/examples/configuration.nix @@ -56,15 +56,15 @@ # # == REST server # Set this to create a clightning REST onion service. - # This also adds binary `lndconnect-clightning` to the system environment. + # This also adds binary `lnconnect-clnrest` to the system environment. # This binary creates QR codes or URLs for connecting applications to clightning # via the REST onion service. # You can also connect via WireGuard instead of Tor. # See ../docs/services.md for details. # - # services.clightning-rest = { + # services.clightning.plugins.clnrest = { # enable = true; - # lndconnect = { + # lnconnect = { # enable = true; # onion = true; # }; diff --git a/modules/lndconnect.nix b/modules/lndconnect.nix index 4a54782..0bd370a 100644 --- a/modules/lndconnect.nix +++ b/modules/lndconnect.nix @@ -32,6 +32,34 @@ let }; }; + services.clightning.plugins.clnrest.lnconnect = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Add a `lnconnect-clnrest` binary to the system environment which prints + connection info for clightning clients. + See: https://github.com/LN-Zap/lndconnect + + Usage: + ```bash + # Print QR code + lnconnect-clnrest + + # Print URL + lnconnect-clnrest --url + ``` + ''; + }; + onion = mkOption { + type = types.bool; + default = false; + description = '' + Create an onion service for the clnrest server, + which is used by lnconnect. + ''; + }; + }; services.clightning-rest.lndconnect = { enable = mkOption { @@ -77,14 +105,18 @@ let inherit (config.services) lnd + clightning clightning-rest; + inherit (clightning.plugins) clnrest; + mkLndconnect = { name, shebang ? "#!${pkgs.stdenv.shell} -e", isClightning ? false, + isClnrest ? false, port, - macaroonPath, + authSecretPath, enableOnion, onionService ? null, certPath ? null @@ -99,7 +131,7 @@ let ${optionalString enableOnion "--host=$(cat ${config.nix-bitcoin.onionAddresses.dataDir}/${onionService})"} \ --port=${toString port} \ ${if enableOnion || certPath == null then "--nocert" else "--tlscertpath='${certPath}'"} \ - --adminmacaroonpath='${macaroonPath}' \ + --adminmacaroonpath='${authSecretPath}' \ --configfile=/dev/null "$@" ) @@ -109,7 +141,7 @@ let # Because `macaroon` is always the last URL fragment, the # sed replacement below works correctly. '' - macaroonHex=$(${getExe pkgs.xxd} -p -u -c 99999 '${macaroonPath}') + macaroonHex=$(${getExe pkgs.xxd} -p -u -c 99999 '${authSecretPath}') url=$( echo "$url" | ${getExe pkgs.gnused} " s|^lndconnect|c-lightning-rest| @@ -119,6 +151,18 @@ let '' } + ${optionalString isClnrest + # Change URL procotcol to clnrest + '' + url=$( + echo "$url" | ${getExe pkgs.gnused} " + s|^lndconnect|clnrest| + s|macaroon=.*|rune=$(cat '${authSecretPath}')| + "; + ) + '' + } + # If --url is in args if [[ " $* " =~ " --url " ]]; then echo "$url" @@ -146,7 +190,7 @@ in { onionService = "${lnd.user}/lnd-rest"; port = lnd.restPort; certPath = lnd.certPath; - macaroonPath = "${lnd.networkDir}/admin.macaroon"; + authSecretPath = "${lnd.networkDir}/admin.macaroon"; } )]; @@ -169,6 +213,39 @@ in { }) ])) + (mkIf (clnrest.enable && clnrest.lnconnect.enable) + (mkMerge [ + { + environment.systemPackages = [( + mkLndconnect { + name = "lnconnect-clnrest"; + isClnrest = true; + enableOnion = clnrest.lnconnect.onion; + onionService = "${operatorName}/clnrest"; + port = clnrest.port; + certPath = "${clightning.networkDir}/client.pem"; + authSecretPath = "${clightning.networkDir}/admin-rune"; + } + )]; + + services.clightning.plugins.clnrest.address = mkIf (!clnrest.lnconnect.onion) "0.0.0.0"; + } + + (mkIf clnrest.lnconnect.onion { + services.tor = { + enable = true; + relay.onionServices.clnrest = nbLib.mkOnionService { + target.addr = nbLib.address clnrest.address; + target.port = clnrest.port; + port = clnrest.port; + }; + }; + # This also allows nodeinfo to show the clnrest onion address + nix-bitcoin.onionAddresses.access.${operatorName} = [ "clnrest" ]; + }) + ]) + ) + (mkIf (clightning-rest.enable && clightning-rest.lndconnect.enable) (mkMerge [ { @@ -180,7 +257,7 @@ in { onionService = "${operatorName}/clightning-rest"; port = clightning-rest.port; certPath = "${clightning-rest.dataDir}/certs/certificate.pem"; - macaroonPath = "${clightning-rest.dataDir}/certs/access.macaroon"; + authSecretPath = "${clightning-rest.dataDir}/certs/access.macaroon"; } )]; diff --git a/modules/presets/wireguard.nix b/modules/presets/wireguard.nix index 4a04b28..47b7133 100644 --- a/modules/presets/wireguard.nix +++ b/modules/presets/wireguard.nix @@ -2,7 +2,8 @@ # Create a WireGuard server with a single peer. # Private/public keys are created via the secrets system. -# Add helper binaries `nix-bitcoin-wg-connect` and optionally `lndconnect-wg`, `lndconnect-clightning-wg`. +# Add helper binaries `nix-bitcoin-wg-connect` and optionally `lndconnect-wg`, +# `lnconnect-clnrest-wg`, `lndconnect-clightning-wg`. # See ../../docs/services.md ("Use Zeus (mobile lightning wallet) via WireGuard") # for usage instructions. @@ -33,9 +34,12 @@ let inherit (config.networking.wireguard.interfaces) wg-nb; inherit (config.services) lnd + clightning clightning-rest; + inherit (clightning.plugins) clnrest; lndconnect = lnd.enable && lnd.lndconnect.enable; + lnconnect-clnrest = clnrest.enable && clnrest.lnconnect.enable; lndconnect-clightning = clightning-rest.enable && clightning-rest.lndconnect.enable; serverAddress = "${wgSubnet}.1"; @@ -150,6 +154,10 @@ in { (pkgs.writers.writeBashBin "lndconnect-wg" '' exec lndconnect --host "${serverAddress}" --nocert "$@" '') + ) ++ (optional lnconnect-clnrest + (pkgs.writers.writeBashBin "lnconnect-clnrest-wg" '' + exec lnconnect-clnrest --host "${serverAddress}" --nocert "$@" + '') ) ++ (optional lndconnect-clightning (pkgs.writers.writeBashBin "lndconnect-clightning-wg" '' exec lndconnect-clightning --host "${serverAddress}" --nocert "$@" @@ -165,6 +173,9 @@ in { optionalString lndconnect '' iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString lnd.restPort} -j nixos-fw-accept '' + + optionalString lnconnect-clnrest '' + iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString clnrest.port} -j nixos-fw-accept + '' + optionalString lndconnect-clightning '' iptables -w -A nixos-fw -p tcp -s ${wgSubnet}.0/24 --dport ${toString clightning-rest.port} -j nixos-fw-accept '' @@ -187,6 +198,11 @@ in { restAddress = "0.0.0.0"; tor.enforce = false; }; + + services.clightning.plugins.clnrest.address = mkIf lnconnect-clnrest "0.0.0.0"; + # clnrest runs inside `clightning.service` + services.clightning.tor.enforce = mkIf lnconnect-clnrest false; + services.clightning-rest = mkIf lndconnect-clightning { # clightning-rest always listens on "0.0.0.0" tor.enforce = false; diff --git a/test/tests.nix b/test/tests.nix index 66aaf22..f950b62 100644 --- a/test/tests.nix +++ b/test/tests.nix @@ -103,6 +103,7 @@ let nix-bitcoin.onionServices.lnd.public = true; tests.lndconnect-onion-lnd = with cfg.lnd.lndconnect; enable && onion; + tests.lnconnect-onion-clnrest = with cfg.clightning.plugins.clnrest.lnconnect; enable && onion; tests.lndconnect-onion-clightning = with cfg.clightning-rest.lndconnect; enable && onion; tests.lightning-loop = cfg.lightning-loop.enable; @@ -195,6 +196,10 @@ let encrypt = true; local.directory = "/var/backup/clightning"; }; + services.clightning.plugins.clnrest = { + enable = true; + lnconnect = { enable = true; onion = true; }; + }; test.features.clightningPlugins = true; services.rtl.enable = true; services.clightning-rest.enable = true; diff --git a/test/tests.py b/test/tests.py index 3dfb11d..4dee84c 100644 --- a/test/tests.py +++ b/test/tests.py @@ -179,6 +179,11 @@ def _(): assert_running("lnd") assert_matches("runuser -u operator -- lndconnect --url", ".onion") +@test("lnconnect-onion-clnrest") +def _(): + assert_running("clightning") + assert_matches("runuser -u operator -- lnconnect-clnrest --url", ".onion") + @test("lndconnect-onion-clightning") def _(): assert_running("clightning-rest") diff --git a/test/wireguard-lndconnect.nix b/test/wireguard-lndconnect.nix index ebfe29d..e8d8fdc 100644 --- a/test/wireguard-lndconnect.nix +++ b/test/wireguard-lndconnect.nix @@ -18,6 +18,14 @@ makeTestVM { nix-bitcoin.generateSecrets = true; nix-bitcoin.operator.enable = true; + services.clightning = { + enable = true; + plugins.clnrest = { + enable = true; + lnconnect.enable = true; + }; + }; + services.clightning-rest = { enable = true; lndconnect.enable = true; @@ -51,16 +59,19 @@ makeTestVM { def parse_lndconnect_url(url): u = Url.urlparse(url) + data = {'host': u.hostname, 'port': u.port} queries = Url.parse_qs(u.query) - macaroon = queries['macaroon'][0] - is_clightning = url.startswith("c-lightning-rest") + if url.startswith("clnrest"): + data['rune'] = queries['rune'][0] + else: + macaroon = queries['macaroon'][0] + if url.startswith("c-lightning-rest"): + data['macaroon_hex'] = macaroon + else: + # lnd + data['macaroon_hex'] = base64.urlsafe_b64decode(macaroon + '===').hex().upper() - return SimpleNamespace( - host = u.hostname, - port = u.port, - macaroon_hex = - macaroon if is_clightning else base64.urlsafe_b64decode(macaroon + '===').hex().upper() - ) + return SimpleNamespace(**data) client.start() server.connect() @@ -92,6 +103,16 @@ makeTestVM { f"-X GET https://{api.host}:{api.port}/v1/getinfo" ) + with subtest("lnconnect-clnrest-wg"): + server.wait_for_unit("clightning.service") + lndconnect_url = server.succeed("runuser -u operator -- lnconnect-clnrest-wg --url") + api = parse_lndconnect_url(lndconnect_url) + # Make clnrest API call + client.succeed( + f"curl -fsS --max-time 3 --insecure --header 'rune: {api.rune}' " + f"-X POST https://{api.host}:{api.port}/v1/getinfo" + ) + with subtest("lndconnect-clightning-wg"): server.wait_for_unit("clightning-rest.service") lndconnect_url = server.succeed("runuser -u operator -- lndconnect-clightning-wg --url")