diff --git a/resources/charts/bitcoincore/charts/lnd/templates/_helpers.tpl b/resources/charts/bitcoincore/charts/lnd/templates/_helpers.tpl index c7f5ae792..b32edb388 100644 --- a/resources/charts/bitcoincore/charts/lnd/templates/_helpers.tpl +++ b/resources/charts/bitcoincore/charts/lnd/templates/_helpers.tpl @@ -85,3 +85,15 @@ Create a hex-encoded RGB color derived from the namespace {{- printf "#%s" (substr 0 6 $hash) -}} {{- end -}} +{{/* +Data to init wallet with root key if lnd >= v0.16.0-beta +*/}} +{{- define "lnd.initwalletData" -}} +{{- $tag := .Values.image.tag -}} +{{- $supportsRootKey := semverCompare ">=0.16.0-beta" $tag -}} +{{- if $supportsRootKey -}} +"{\"macaroon_root_key\":\"{{ .Values.macaroonRootKey }}\", \"wallet_password\":\"AAAAAAAAAAA=\", \"cipher_seed_mnemonic\": $PHRASE}" +{{- else -}} +"{\"wallet_password\":\"AAAAAAAAAAA=\", \"cipher_seed_mnemonic\": $PHRASE}" +{{- end -}} +{{- end -}} diff --git a/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml b/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml index c8cbb2441..ae48728a4 100644 --- a/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml +++ b/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml @@ -69,7 +69,7 @@ spec: PHRASE=$(cat /tmp/genseed.json | grep -o '\[[^]]*\]') - until curl --fail --insecure https://localhost:8080/v1/initwallet --data "{\"macaroon_root_key\":\"{{ .Values.macaroonRootKey }}\", \"wallet_password\":\"AAAAAAAAAAA=\", \"cipher_seed_mnemonic\": $PHRASE}"; do + until curl --fail --insecure https://localhost:8080/v1/initwallet --data {{ include "lnd.initwalletData" . }}; do sleep 5 done resources: diff --git a/resources/charts/commander/templates/rbac.yaml b/resources/charts/commander/templates/rbac.yaml index da4d653b8..a19283d9d 100644 --- a/resources/charts/commander/templates/rbac.yaml +++ b/resources/charts/commander/templates/rbac.yaml @@ -17,6 +17,9 @@ rules: - apiGroups: [""] resources: ["pods", "configmaps"] verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["pods/exec"] + verbs: ["get", "create"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/resources/scenarios/commander.py b/resources/scenarios/commander.py index 0ed8da9b1..edc9b132c 100644 --- a/resources/scenarios/commander.py +++ b/resources/scenarios/commander.py @@ -16,7 +16,7 @@ from kubernetes import client, config from kubernetes.stream import stream -from ln_framework.ln import CLN, LND, LNNode +from ln_framework.ln import CLN, LND, LNNode, get_admin_macaroon from test_framework.authproxy import AuthServiceProxy from test_framework.blocktools import get_witness_script, script_BIP34_coinbase_height from test_framework.messages import ( @@ -117,7 +117,7 @@ pod.metadata.name, pod.metadata.namespace, pod_ip, - pod.metadata.annotations["adminMacaroon"], + get_admin_macaroon(sclient, pod), ) if "cln" in pod.metadata.labels["app.kubernetes.io/name"]: lnnode = CLN(pod.metadata.name, pod.metadata.namespace, pod.status.pod_ip) diff --git a/resources/scenarios/ln_framework/ln.py b/resources/scenarios/ln_framework/ln.py index 4128733a8..75a8aeba9 100644 --- a/resources/scenarios/ln_framework/ln.py +++ b/resources/scenarios/ln_framework/ln.py @@ -2,11 +2,13 @@ import http.client import json import logging +import re import ssl from abc import ABC, abstractmethod from time import sleep import requests +from kubernetes.stream import stream # Don't worry about lnd's self-signed certificates INSECURE_CONTEXT = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) @@ -420,3 +422,50 @@ def payinvoice(self, payment_request) -> str: def graph(self): res = self.get("/v1/graph") return json.loads(res) + + +def _get_lnd_semver(pod): + for c in pod.spec.containers: + if c.name == "lnd": + return c.image.removeprefix("lightninglabs/lnd:") + raise RuntimeError("no lnd container found") + + +def _supports_macaroon_root_key(pod): + semver = _get_lnd_semver(pod) + minor = int(semver.split(".")[1]) + return minor >= 16 + + +def _read_admin_macaroon(sclient, pod): + chain = pod.metadata.labels["chain"] + path = f"/root/.lnd/data/chain/bitcoin/{chain}/admin.macaroon" + cmd = [ + "bash", + "-c", + f"""for i in {{1..10}}; do xxd -p '{path}' 2>/dev/null && exit 0 || sleep 1; done; exit 1""", + ] + out = stream( + sclient.connect_get_namespaced_pod_exec, + name=pod.metadata.name, + namespace=pod.metadata.namespace, + container="lnd", + command=cmd, + stderr=True, + stdin=False, + stdout=True, + tty=False, + ) + admin_macaroon_hex = "".join(out.split()) + if not re.fullmatch(r"[0-9a-fA-F]+", admin_macaroon_hex): + raise RuntimeError(f"could not read admin.macaroon for {pod.metadata.name}") + return admin_macaroon_hex + + +def get_admin_macaroon(sclient, pod): + # we have to read admin.macaroon for lnd versions below v0.16.0-beta + # because they cannot use adminMacaroon from the pod config + if _supports_macaroon_root_key(pod): + return pod.metadata.annotations["adminMacaroon"] + else: + return _read_admin_macaroon(sclient, pod) diff --git a/test/data/ln/network.yaml b/test/data/ln/network.yaml index 42d7213ef..a1d06dffa 100644 --- a/test/data/ln/network.yaml +++ b/test/data/ln/network.yaml @@ -72,4 +72,11 @@ nodes: - name: tank-0005 addnode: - - tank-0000 \ No newline at end of file + - tank-0000 + + - name: tank-0006 + addnode: + - tank-0000 + lnd: + image: + tag: v0.15.5-beta \ No newline at end of file diff --git a/test/ln_basic_test.py b/test/ln_basic_test.py index b5976d8f6..1869705a2 100755 --- a/test/ln_basic_test.py +++ b/test/ln_basic_test.py @@ -27,6 +27,7 @@ def __init__(self): "tank-0003-ln", "tank-0004-ln", "tank-0005-ln", + "tank-0006-ln", ] self.cb_port = 9235 @@ -100,11 +101,26 @@ def test_admin_macaroons(self): raise AssertionError("That should not have worked!") except Exception as e: assert "verification failed: signature mismatch after caveat verification" in str(e) + try: + self.warnet("ln rpc tank-0001-ln --rpcserver=tank-0006-ln.default:10009 getinfo") + raise AssertionError("That should not have worked!") + except Exception as e: + assert "verification failed: signature mismatch after caveat verification" in str(e) try: self.warnet("ln rpc tank-0003-ln --rpcserver=tank-0004-ln.default:10009 getinfo") raise AssertionError("That should not have worked!") except Exception as e: assert "verification failed: signature mismatch after caveat verification" in str(e) + try: + self.warnet("ln rpc tank-0003-ln --rpcserver=tank-0006-ln.default:10009 getinfo") + raise AssertionError("That should not have worked!") + except Exception as e: + assert "verification failed: signature mismatch after caveat verification" in str(e) + try: + self.warnet("ln rpc tank-0006-ln --rpcserver=tank-0004-ln.default:10009 getinfo") + raise AssertionError("That should not have worked!") + except Exception as e: + assert "verification failed: signature mismatch after caveat verification" in str(e) def fund_wallets(self): for ln in self.lns: