diff --git a/docs/circuit-breaker.md b/docs/circuit-breaker.md new file mode 100644 index 000000000..d6124c07d --- /dev/null +++ b/docs/circuit-breaker.md @@ -0,0 +1,180 @@ +# Circuit Breaker for Warnet + +## Overview + +Circuit Breaker is a Lightning Network firewall that protects LND nodes from being flooded with HTLCs. When integrated with Warnet, Circuit Breaker runs as a sidecar container alongside your LND nodes. + +Circuit Breaker is to Lightning what firewalls are to the internet - it allows nodes to protect themselves by setting maximum limits on in-flight HTLCs on a per-peer basis and applying rate limits to forwarded HTLCs. + +* **Repository**: https://github.com/lightningequipment/circuitbreaker +* **Full Documentation**: See the main repository for detailed information about Circuit Breaker's features, operating modes, and configuration options + +## Usage in Warnet + +### Basic Configuration + +To enable Circuit Breaker for an LND node in your `network.yaml` file, add the `circuitbreaker` section under the `lnd` configuration. When enabled, Circuit Breaker will automatically start as a sidecar container and connect to your LND node: + +```yaml +nodes: + - name: tank-0003 + addnode: + - tank-0000 + ln: + lnd: true + lnd: + config: | + bitcoin.timelockdelta=33 + channels: + - id: + block: 300 + index: 1 + target: tank-0004-ln + capacity: 100000 + push_amt: 50000 + circuitbreaker: + enabled: true # This enables Circuit Breaker for this node + httpPort: 9235 # Can override default port per-node (optional) +``` + +### Configuration Options + +- `enabled`: Set to `true` to enable Circuit Breaker for the node +- `httpPort`: Override the default HTTP port (9235) for the web UI (optional) + +### Complete Example + +Here's a complete `network.yaml` example with Circuit Breaker enabled on one node: + +```yaml +nodes: + - name: tank-0000 + addnode: + - tank-0001 + ln: + lnd: true + + - name: tank-0001 + addnode: + - tank-0002 + ln: + lnd: true + + - name: tank-0002 + addnode: + - tank-0000 + ln: + lnd: true + + - name: tank-0003 + addnode: + - tank-0000 + ln: + lnd: true + lnd: + config: | + bitcoin.timelockdelta=33 + channels: + - id: + block: 300 + index: 1 + target: tank-0004-ln + capacity: 100000 + push_amt: 50000 + circuitbreaker: + enabled: true + httpPort: 9235 + + - name: tank-0004 + addnode: + - tank-0000 + ln: + lnd: true + lnd: + channels: + - id: + block: 300 + index: 2 + target: tank-0005-ln + capacity: 50000 + push_amt: 25000 + + - name: tank-0005 + addnode: + - tank-0000 + ln: + lnd: true +``` + +## Accessing Circuit Breaker + +Circuit Breaker provides both a web-based interface and REST API endpoints for configuration and monitoring. + +### Web UI Access + +To access the web interface: + +1. **Port Forward to the Circuit Breaker service**: + ```bash + kubectl port-forward pod/-ln : + ``` + + For example, if your node is named `tank-0003` and using the default port: + ```bash + kubectl port-forward pod/tank-0003-ln 9235:9235 + ``` + +2. **Open your browser** and navigate to: + ``` + http://localhost:9235 + ``` + +3. **Configure your firewall rules** through the web interface: + - Set per-peer HTLC limits + - Configure rate limiting parameters + - Choose operating modes + - Monitor HTLC statistics + +### API Access + +You can also interact with Circuit Breaker programmatically using kubectl commands to access the REST API: + +**Get node information:** +```bash +kubectl exec -ln -c circuitbreaker -- wget -qO - 127.0.0.1:/api/info +``` + +**Get current limits:** +```bash +kubectl exec -ln -c circuitbreaker -- wget -qO - 127.0.0.1:/api/limits +``` + +For example, with node `tank-0003-ln`: +```bash +kubectl exec tank-0003-ln -c circuitbreaker -- wget -qO - 127.0.0.1:9235/api/info +kubectl exec tank-0003-ln -c circuitbreaker -- wget -qO - 127.0.0.1:9235/api/limits +``` + +## Architecture + +Circuit Breaker runs as a sidecar container alongside your LND node in Warnet: +- **LND Container**: Runs your Lightning node +- **Circuit Breaker Container**: Connects to LND via RPC and provides firewall functionality +- **Shared Volume**: Allows Circuit Breaker to access LND's TLS certificates and macaroons +- **Web Interface**: Accessible via port forwarding for configuration + +## Requirements + +- **LND Version**: 0.15.4-beta or above +- **Warnet**: Compatible with standard Warnet LND deployments + +## Support + +For issues and questions: +- Circuit Breaker Repository: https://github.com/lightningequipment/circuitbreaker +- Warnet Documentation: Refer to the Warnet installation guides [install.md](install.md) +- LND Documentation: https://docs.lightning.engineering/ + +--- + +*Circuit Breaker integration for Warnet enables sophisticated HTLC management and protection for Lightning Network nodes in test environments.* \ No newline at end of file diff --git a/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml b/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml index c5d66851a..2199f556b 100644 --- a/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml +++ b/resources/charts/bitcoincore/charts/lnd/templates/pod.yaml @@ -58,9 +58,28 @@ spec: - mountPath: /root/.lnd/tls.cert name: config subPath: tls.cert + - name: shared-volume + mountPath: /root/.lnd/ {{- with .Values.extraContainers }} {{- toYaml . | nindent 4 }} {{- end }} + {{- if .Values.circuitbreaker.enabled }} + - name: circuitbreaker + image: {{ .Values.circuitbreaker.image | quote }} + imagePullPolicy: IfNotPresent + args: + - "--network={{ .Values.global.chain }}" + - "--rpcserver=localhost:{{ .Values.RPCPort }}" + - "--tlscertpath=/tls.cert" + - "--macaroonpath=/root/.lnd/data/chain/bitcoin/{{ .Values.global.chain }}/admin.macaroon" + - "--httplisten=0.0.0.0:{{ .Values.circuitbreaker.httpPort }}" + volumeMounts: + - name: shared-volume + mountPath: /root/.lnd/ + - name: config + mountPath: /tls.cert + subPath: tls.cert + {{- end }} volumes: {{- with .Values.volumes }} {{- toYaml . | nindent 4 }} @@ -68,6 +87,8 @@ spec: - configMap: name: {{ include "lnd.fullname" . }} name: config + - name: shared-volume + emptyDir: {} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 4 }} diff --git a/resources/charts/bitcoincore/charts/lnd/values.yaml b/resources/charts/bitcoincore/charts/lnd/values.yaml index d56e65bf4..971a8e72b 100644 --- a/resources/charts/bitcoincore/charts/lnd/values.yaml +++ b/resources/charts/bitcoincore/charts/lnd/values.yaml @@ -132,3 +132,8 @@ config: "" defaultConfig: "" channels: [] + +circuitbreaker: + enabled: false # Default to disabled + image: carlakirkcohen/circuitbreaker:attackathon-test + httpPort: 9235 \ No newline at end of file diff --git a/test/data/ln/network.yaml b/test/data/ln/network.yaml index 5d30686d4..8006b568f 100644 --- a/test/data/ln/network.yaml +++ b/test/data/ln/network.yaml @@ -21,6 +21,10 @@ nodes: target: tank-0004-ln capacity: 100000 push_amt: 50000 + circuitbreaker: + enabled: true + httpPort: 9235 + - name: tank-0004 addnode: - tank-0000 diff --git a/test/data/network_with_plugins/network.yaml b/test/data/network_with_plugins/network.yaml index 07a334de2..6e4d64a30 100644 --- a/test/data/network_with_plugins/network.yaml +++ b/test/data/network_with_plugins/network.yaml @@ -32,6 +32,9 @@ nodes: target: tank-0004-ln capacity: 100000 push_amt: 50000 + circuitbreaker: + enabled: true # This enables circuitbreaker for this node + httpPort: 9235 # Can override defaults per-node - name: tank-0004 addnode: @@ -85,3 +88,4 @@ plugins: # Each plugin section has a number of hooks available (preDeploy, post entrypoint: "../plugins/hello" helloTo: "postNetwork!" podName: "hello-post-network" + \ No newline at end of file diff --git a/test/ln_basic_test.py b/test/ln_basic_test.py index dbbbb767a..4788697b8 100755 --- a/test/ln_basic_test.py +++ b/test/ln_basic_test.py @@ -2,6 +2,7 @@ import json import os +import subprocess from pathlib import Path from time import sleep @@ -24,11 +25,18 @@ def __init__(self): "tank-0005-ln", ] + self.cb_port = 9235 + self.cb_node = "tank-0003-ln" + self.port_forward = None + def run_test(self): try: # Wait for all nodes to wake up. ln_init will start automatically self.setup_network() + # Test circuit breaker API + self.test_circuit_breaker_api() + # Send a payment across channels opened automatically by ln_init self.pay_invoice(sender="tank-0005-ln", recipient="tank-0003-ln") @@ -120,6 +128,23 @@ def scenario_open_channels(self): self.log.info(f"Running scenario from: {scenario_file}") self.warnet(f"run {scenario_file} --source_dir={self.scen_dir} --debug") + def test_circuit_breaker_api(self): + self.log.info("Testing Circuit Breaker API with direct kubectl commands") + + # Test /info endpoint + info_cmd = f"kubectl exec {self.cb_node} -c circuitbreaker -- wget -qO - 127.0.0.1:{self.cb_port}/api/info" + info = json.loads(subprocess.check_output(info_cmd, shell=True).decode()) + assert "nodeKey" in info, "Circuit breaker info missing nodeKey" + self.log.info(f"Got node info: {info}") + + # Test /limits endpoint + limits_cmd = f"kubectl exec {self.cb_node} -c circuitbreaker -- wget -qO - 127.0.0.1:{self.cb_port}/api/limits" + limits = json.loads(subprocess.check_output(limits_cmd, shell=True).decode()) + assert "limits" in limits, "Circuit breaker limits missing" + self.log.info(f"Got limits: {limits}") + + self.log.info("✅ Circuit Breaker API tests passed") + if __name__ == "__main__": test = LNBasicTest()