mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-20 13:54:36 +01:00
clnrest: prefixing all rest config options with cln
This will allow users to use clnrest with c-lightning-REST without conflicts. It was required for applications to have enough time for migrating from c-lightning-REST to clnrest. Changelog-Changed: config option `rest-certs` changed to `clnrest-certs` config option `rest-protocol` changed to `clnrest-protocol` config option `rest-host` changed to `clnrest-host` config option `rest-port` changed to `clnrest-port` config option `rest-cors-origins` changed to `clnrest-cors-origins` config option `rest-csp` changed to `clnrest-csp`
This commit is contained in:
parent
017581cd79
commit
cc2665da7f
6 changed files with 69 additions and 68 deletions
|
@ -28,7 +28,7 @@ An online demo for the REST interface is available at [REST API REFERENCE](ref:g
|
|||
>
|
||||
> - The `ip` should be configured with your system's public IP address.
|
||||
>
|
||||
> - Default `rest-host` is `127.0.0.1` but this testing will require it to be `0.0.0.0`.
|
||||
> - Default `clnrest-host` is `127.0.0.1` but this testing will require it to be `0.0.0.0`.
|
||||
>
|
||||
> Note: This setup is for **testing only**. It is **highly recommended** to test with _non-mainnet_ (regtest/testnet) setup only.
|
||||
|
||||
|
@ -38,36 +38,37 @@ An online demo for the REST interface is available at [REST API REFERENCE](ref:g
|
|||
The plugin is built-in with Core Lightning but its python dependencies are not, and must be installed separately.
|
||||
Install required packages with `pip install -r plugins/clnrest/requirements.txt`.
|
||||
|
||||
Note: if you have the older c-lightning-rest plugin, you can configure Core Lightning with `disable-plugin=clnrest.py` option
|
||||
to avoid any conflict with this one. Of course, you could use this one instead!
|
||||
Note: if you have the older c-lightning-REST plugin, you can configure Core Lightning with `disable-plugin=clnrest.py`
|
||||
option to avoid confusion with this one. You can also run both plugins simultaneously till all your applications
|
||||
are not migrated to `clnrest`.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
If `rest-port` is not specified, the plugin will disable itself.
|
||||
If `clnrest-port` is not specified, the plugin will disable itself.
|
||||
|
||||
- --rest-port: Sets the REST server port to listen to (3010 is common)
|
||||
- --clnrest-port: Sets the REST server port to listen to (3010 is common)
|
||||
|
||||
- --rest-protocol: Specifies the REST server protocol. Default is HTTPS.
|
||||
- --clnrest-protocol: Specifies the REST server protocol. Default is HTTPS.
|
||||
|
||||
- --rest-host: Defines the REST server host. Default is 127.0.0.1.
|
||||
- --clnrest-host: Defines the REST server host. Default is 127.0.0.1.
|
||||
|
||||
- --rest-certs: Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate.
|
||||
- --clnrest-certs: Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate.
|
||||
If it is missing at the configured location, new identity will be generated.
|
||||
|
||||
- --rest-csp: Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks.
|
||||
- --clnrest-csp: Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks.
|
||||
Default CSP:
|
||||
`default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';`
|
||||
Example CSP:
|
||||
`rest-csp=default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';`.
|
||||
`clnrest-csp=default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';`.
|
||||
|
||||
- --rest-cors-origins: Define multiple origins which are allowed to share resources on web pages to a domain different from the
|
||||
- --clnrest-cors-origins: Define multiple origins which are allowed to share resources on web pages to a domain different from the
|
||||
one that served the web page. Default is `*` which allows all origins. Example to define multiple origins:
|
||||
|
||||
```
|
||||
rest-cors-origins=https://localhost:5500
|
||||
rest-cors-origins=http://192.168.1.50:3030
|
||||
rest-cors-origins=https?://127.0.0.1:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])
|
||||
clnrest-cors-origins=https://localhost:5500
|
||||
clnrest-cors-origins=http://192.168.1.50:3030
|
||||
clnrest-cors-origins=https?://127.0.0.1:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -632,27 +632,27 @@ all DNS lookups, to avoid leaking information.
|
|||
Set a Tor control password, which may be needed for *autotor:* to
|
||||
authenticate to the Tor control port.
|
||||
|
||||
* **rest-port**=*PORT* [plugin `clnrest.py`]
|
||||
* **clnrest-port**=*PORT* [plugin `clnrest.py`]
|
||||
|
||||
Sets the REST server port to listen to (3010 is common). If this is not specified, the clnrest.py plugin will be disabled.
|
||||
|
||||
* **rest-protocol**=*PROTOCOL* [plugin `clnrest.py`]
|
||||
* **clnrest-protocol**=*PROTOCOL* [plugin `clnrest.py`]
|
||||
|
||||
Specifies the REST server protocol. Default is HTTPS.
|
||||
|
||||
* **rest-host**=*HOST* [plugin `clnrest.py`]
|
||||
* **clnrest-host**=*HOST* [plugin `clnrest.py`]
|
||||
|
||||
Defines the REST server host. Default is 127.0.0.1.
|
||||
|
||||
* **rest-certs**=*PATH* [plugin `clnrest.py`]
|
||||
* **clnrest-certs**=*PATH* [plugin `clnrest.py`]
|
||||
|
||||
Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate. If it is missing at the configured location, new identity (`client.pem` and `client-key.pem`) will be generated.
|
||||
|
||||
* **rest-cors-origins**=*CORSORIGINS* [plugin `clnrest.py`]
|
||||
* **clnrest-cors-origins**=*CORSORIGINS* [plugin `clnrest.py`]
|
||||
|
||||
Define multiple origins which are allowed to share resources on web pages to a domain different from the one that served the web page. Default is `*` which allows all origins.
|
||||
|
||||
* **rest-csp**=*CSPOLICY* [plugin `clnrest.py`]
|
||||
* **clnrest-csp**=*CSPOLICY* [plugin `clnrest.py`]
|
||||
|
||||
Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks. Default CSP is `default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';`.
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ def add_csp_headers(response):
|
|||
response.headers['Content-Security-Policy'] = REST_CSP.replace('\\', '').replace("[\"", '').replace("\"]", '')
|
||||
return response
|
||||
except Exception as err:
|
||||
plugin.log(f"Error from rest-csp config: {err}", "info")
|
||||
plugin.log(f"Error from clnrest-csp config: {err}", "info")
|
||||
|
||||
|
||||
def set_application_options(plugin):
|
||||
|
|
|
@ -3,9 +3,9 @@ from pyln.client import Plugin
|
|||
|
||||
plugin = Plugin(autopatch=False)
|
||||
|
||||
plugin.add_option(name="rest-certs", default=os.getcwd(), description="Path for certificates (for https)", opt_type="string", deprecated=False)
|
||||
plugin.add_option(name="rest-protocol", default="https", description="REST server protocol", opt_type="string", deprecated=False)
|
||||
plugin.add_option(name="rest-host", default="127.0.0.1", description="REST server host", opt_type="string", deprecated=False)
|
||||
plugin.add_option(name="rest-port", default=None, description="REST server port to listen", opt_type="int", deprecated=False)
|
||||
plugin.add_option(name="rest-cors-origins", default="*", description="Cross origin resource sharing origins", opt_type="string", deprecated=False, multi=True)
|
||||
plugin.add_option(name="rest-csp", default="default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';", description="Content security policy (CSP) for the server", opt_type="string", deprecated=False, multi=False)
|
||||
plugin.add_option(name="clnrest-certs", default=os.getcwd(), description="Path for certificates (for https)", opt_type="string", deprecated=False)
|
||||
plugin.add_option(name="clnrest-protocol", default="https", description="REST server protocol", opt_type="string", deprecated=False)
|
||||
plugin.add_option(name="clnrest-host", default="127.0.0.1", description="REST server host", opt_type="string", deprecated=False)
|
||||
plugin.add_option(name="clnrest-port", default=None, description="REST server port to listen", opt_type="int", deprecated=False)
|
||||
plugin.add_option(name="clnrest-cors-origins", default="*", description="Cross origin resource sharing origins", opt_type="string", deprecated=False, multi=True)
|
||||
plugin.add_option(name="clnrest-csp", default="default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';", description="Content security policy (CSP) for the server", opt_type="string", deprecated=False, multi=False)
|
||||
|
|
|
@ -39,25 +39,25 @@ def validate_port(port):
|
|||
|
||||
|
||||
def set_config(options):
|
||||
if 'rest-port' not in options:
|
||||
return "`rest-port` option is not configured"
|
||||
if 'clnrest-port' not in options:
|
||||
return "`clnrest-port` option is not configured"
|
||||
global CERTS_PATH, REST_PROTOCOL, REST_HOST, REST_PORT, REST_CSP, REST_CORS_ORIGINS
|
||||
|
||||
REST_PORT = int(options["rest-port"])
|
||||
REST_PORT = int(options["clnrest-port"])
|
||||
if validate_port(REST_PORT) is False:
|
||||
return f"`rest-port` {REST_PORT}, should be a valid available port between 1024 and 65535."
|
||||
return f"`clnrest-port` {REST_PORT}, should be a valid available port between 1024 and 65535."
|
||||
|
||||
REST_HOST = str(options["rest-host"])
|
||||
REST_HOST = str(options["clnrest-host"])
|
||||
if REST_HOST != "localhost" and validate_ip4(REST_HOST) is False and validate_ip6(REST_HOST) is False:
|
||||
return f"`rest-host` should be a valid IP."
|
||||
return f"`clnrest-host` should be a valid IP."
|
||||
|
||||
REST_PROTOCOL = str(options["rest-protocol"])
|
||||
REST_PROTOCOL = str(options["clnrest-protocol"])
|
||||
if REST_PROTOCOL != "http" and REST_PROTOCOL != "https":
|
||||
return f"`rest-protocol` can either be http or https."
|
||||
return f"`clnrest-protocol` can either be http or https."
|
||||
|
||||
CERTS_PATH = str(options["rest-certs"])
|
||||
REST_CSP = str(options["rest-csp"])
|
||||
cors_origins = options["rest-cors-origins"]
|
||||
CERTS_PATH = str(options["clnrest-certs"])
|
||||
REST_CSP = str(options["clnrest-csp"])
|
||||
cors_origins = options["clnrest-cors-origins"]
|
||||
REST_CORS_ORIGINS.clear()
|
||||
for origin in cors_origins:
|
||||
REST_CORS_ORIGINS.append(str(origin))
|
||||
|
|
|
@ -24,22 +24,22 @@ def http_session_with_retry():
|
|||
|
||||
|
||||
def test_clnrest_no_auto_start(node_factory):
|
||||
"""Ensure that we do not start clnrest unless a `rest-port` is configured."""
|
||||
"""Ensure that we do not start clnrest unless a `clnrest-port` is configured."""
|
||||
l1 = node_factory.get_node()
|
||||
# This might happen really early!
|
||||
l1.daemon.logsearch_start = 0
|
||||
assert [p for p in l1.rpc.plugin('list')['plugins'] if 'clnrest.py' in p['name']] == []
|
||||
assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `rest-port` option is not configured')
|
||||
assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-port` option is not configured')
|
||||
|
||||
|
||||
def test_clnrest_self_signed_certificates(node_factory):
|
||||
"""Test that self-signed certificates have `rest-host` IP in Subject Alternative Name."""
|
||||
"""Test that self-signed certificates have `clnrest-host` IP in Subject Alternative Name."""
|
||||
rest_port = str(reserve())
|
||||
rest_host = '127.0.0.1'
|
||||
base_url = f'https://{rest_host}:{rest_port}'
|
||||
l1 = node_factory.get_node(options={'disable-plugin': 'cln-grpc',
|
||||
'rest-port': rest_port,
|
||||
'rest-host': rest_host})
|
||||
'clnrest-port': rest_port,
|
||||
'clnrest-host': rest_host})
|
||||
# This might happen really early!
|
||||
l1.daemon.logsearch_start = 0
|
||||
l1.daemon.wait_for_log(r'plugin-clnrest.py: REST server running at ' + base_url)
|
||||
|
@ -54,12 +54,12 @@ def test_clnrest_self_signed_certificates(node_factory):
|
|||
def test_clnrest_uses_grpc_plugin_certificates(node_factory):
|
||||
"""Test that clnrest reuses `cln-grpc` plugin certificates if available.
|
||||
Defaults:
|
||||
- rest-protocol: https
|
||||
- clnrest-protocol: https
|
||||
"""
|
||||
rest_host = 'localhost'
|
||||
grpc_port = str(reserve())
|
||||
rest_port = str(reserve())
|
||||
l1 = node_factory.get_node(options={'grpc-port': grpc_port, 'rest-host': rest_host, 'rest-port': rest_port})
|
||||
l1 = node_factory.get_node(options={'grpc-port': grpc_port, 'clnrest-host': rest_host, 'clnrest-port': rest_port})
|
||||
base_url = f'https://{rest_host}:{rest_port}'
|
||||
# This might happen really early!
|
||||
l1.daemon.logsearch_start = 0
|
||||
|
@ -73,21 +73,21 @@ def test_clnrest_uses_grpc_plugin_certificates(node_factory):
|
|||
|
||||
def test_clnrest_generate_certificate(node_factory):
|
||||
"""Test whether we correctly generate the certificates."""
|
||||
# when `rest-protocol` is `http`, certs are not generated at `rest-certs` path
|
||||
# when `clnrest-protocol` is `http`, certs are not generated at `clnrest-certs` path
|
||||
rest_port = str(reserve())
|
||||
rest_protocol = 'http'
|
||||
rest_certs = node_factory.directory + '/clnrest-certs'
|
||||
l1 = node_factory.get_node(options={'rest-port': rest_port,
|
||||
'rest-protocol': rest_protocol,
|
||||
'rest-certs': rest_certs})
|
||||
l1 = node_factory.get_node(options={'clnrest-port': rest_port,
|
||||
'clnrest-protocol': rest_protocol,
|
||||
'clnrest-certs': rest_certs})
|
||||
|
||||
assert not Path(rest_certs).exists()
|
||||
|
||||
# node l1 not started
|
||||
rest_port = str(reserve())
|
||||
rest_certs = node_factory.directory + '/clnrest-certs'
|
||||
l1 = node_factory.get_node(options={'rest-port': rest_port,
|
||||
'rest-certs': rest_certs}, start=False)
|
||||
l1 = node_factory.get_node(options={'clnrest-port': rest_port,
|
||||
'clnrest-certs': rest_certs}, start=False)
|
||||
rest_certs_path = Path(rest_certs)
|
||||
files = [rest_certs_path / f for f in [
|
||||
'ca.pem',
|
||||
|
@ -131,7 +131,7 @@ def start_node_with_clnrest(node_factory):
|
|||
- the certificate authority path used for the self-signed certificates."""
|
||||
rest_port = str(reserve())
|
||||
rest_certs = node_factory.directory + '/clnrest-certs'
|
||||
l1 = node_factory.get_node(options={'rest-port': rest_port, 'rest-certs': rest_certs})
|
||||
l1 = node_factory.get_node(options={'clnrest-port': rest_port, 'clnrest-certs': rest_certs})
|
||||
base_url = 'https://127.0.0.1:' + rest_port
|
||||
# This might happen really early!
|
||||
l1.daemon.logsearch_start = 0
|
||||
|
@ -374,34 +374,34 @@ def test_clnrest_websocket_rune_no_listnotifications(node_factory):
|
|||
|
||||
|
||||
def test_clnrest_options(node_factory):
|
||||
"""Test startup options `rest-host`, `rest-protocol` and `rest-certs`."""
|
||||
"""Test startup options `clnrest-host`, `clnrest-protocol` and `clnrest-certs`."""
|
||||
# with invalid port
|
||||
rest_port = 1000
|
||||
l1 = node_factory.get_node(options={'rest-port': rest_port})
|
||||
assert l1.daemon.is_in_log(f'plugin-clnrest.py: Killing plugin: disabled itself at init: `rest-port` {rest_port}, should be a valid available port between 1024 and 65535.')
|
||||
l1 = node_factory.get_node(options={'clnrest-port': rest_port})
|
||||
assert l1.daemon.is_in_log(f'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-port` {rest_port}, should be a valid available port between 1024 and 65535.')
|
||||
|
||||
# with invalid protocol
|
||||
rest_port = str(reserve())
|
||||
rest_protocol = 'htttps'
|
||||
l1 = node_factory.get_node(options={'rest-port': rest_port,
|
||||
'rest-protocol': rest_protocol})
|
||||
assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `rest-protocol` can either be http or https.')
|
||||
l1 = node_factory.get_node(options={'clnrest-port': rest_port,
|
||||
'clnrest-protocol': rest_protocol})
|
||||
assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-protocol` can either be http or https.')
|
||||
|
||||
# with invalid host
|
||||
rest_port = str(reserve())
|
||||
rest_host = '127.0.0.12.15'
|
||||
l1 = node_factory.get_node(options={'rest-port': rest_port,
|
||||
'rest-host': rest_host})
|
||||
assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `rest-host` should be a valid IP.')
|
||||
l1 = node_factory.get_node(options={'clnrest-port': rest_port,
|
||||
'clnrest-host': rest_host})
|
||||
assert l1.daemon.is_in_log(r'plugin-clnrest.py: Killing plugin: disabled itself at init: `clnrest-host` should be a valid IP.')
|
||||
|
||||
|
||||
def test_clnrest_http_headers(node_factory):
|
||||
"""Test HTTP headers set with `rest-csp` and `rest-cors-origins` options."""
|
||||
"""Test HTTP headers set with `clnrest-csp` and `clnrest-cors-origins` options."""
|
||||
# start a node with clnrest
|
||||
l1, base_url, ca_cert = start_node_with_clnrest(node_factory)
|
||||
http_session = http_session_with_retry()
|
||||
|
||||
# Default values for `rest-csp` and `rest-cors-origins` options
|
||||
# Default values for `clnrest-csp` and `clnrest-cors-origins` options
|
||||
response = http_session.get(base_url + '/v1/list-methods', verify=ca_cert)
|
||||
assert response.headers['Content-Security-Policy'] == "default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';"
|
||||
assert response.headers['Access-Control-Allow-Origin'] == '*'
|
||||
|
@ -409,14 +409,14 @@ def test_clnrest_http_headers(node_factory):
|
|||
l1.daemon.logsearch_start = 0
|
||||
l1.daemon.wait_for_log(f'plugin-clnrest.py: REST server running at {base_url}')
|
||||
|
||||
# Custom values for `rest-csp` and `rest-cors-origins` options
|
||||
# Custom values for `clnrest-csp` and `clnrest-cors-origins` options
|
||||
rest_port = str(reserve())
|
||||
rest_certs = node_factory.directory + '/clnrest-certs'
|
||||
l2 = node_factory.get_node(options={
|
||||
'rest-port': rest_port,
|
||||
'rest-certs': rest_certs,
|
||||
'rest-csp': "default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';",
|
||||
'rest-cors-origins': ['https://localhost:5500', 'http://192.168.1.30:3030', 'http://192.168.1.10:1010']
|
||||
'clnrest-port': rest_port,
|
||||
'clnrest-certs': rest_certs,
|
||||
'clnrest-csp': "default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';",
|
||||
'clnrest-cors-origins': ['https://localhost:5500', 'http://192.168.1.30:3030', 'http://192.168.1.10:1010']
|
||||
})
|
||||
base_url = 'https://127.0.0.1:' + rest_port
|
||||
# This might happen really early!
|
||||
|
|
Loading…
Add table
Reference in a new issue