5 unstable releases
new 0.3.0 | Dec 10, 2024 |
---|---|
0.2.0 | Sep 22, 2024 |
0.1.6 | Jun 5, 2024 |
0.1.5 | Mar 8, 2024 |
0.1.4 | Nov 22, 2023 |
#7 in #mtls
3MB
70K
SLoC
GRPC plugin for Core Lightning
This plugin exposes the JSON-RPC interface through grpc over the network. It listens on a configurable port, authenticates clients using mTLS certificates, and will forward any request to the JSON-RPC interface, performing translations from protobuf to JSON and back.
Getting started
The plugin only runs when lightningd
is configured with the option
--grpc-port
. Upon starting the plugin generates a number of files,
if they don't already exist:
ca.pem
andca-key.pem
: These are the certificate and private key for your own certificate authority. The plugin will only accept incoming connections using certificates that are signed by theis CA.server.pem
andserver-key.pem
: this is the identity (certificate and private key) used by the plugin to authenticate itself. It is signed by the CA, and the client will verify its identity.client.pem
andclient-key.pem
: this is an example identity that can be used by a client to connect to the plugin, and issue requests. It is also signed by the CA.
These files are generated with sane defaults, however you can generate custom certificates should you require some changes (see below for details).
Connecting
The client needs a valid mTLS identity in order to connect to the
plugin, so copy over the ca.pem
, client.pem
and client-key.pem
files from the node. The RPC interface is described in the protobuf
file, and we'll first need to generate language specific
bindings.
In this example we walk through the steps for python, however they are mostly the same for other languages.
We start by downloading the dependencies and protoc
compiler:
pip install grpcio-tools
Next we generate the bindings in the current directory:
python -m grpc_tools.protoc \
-I path/to/cln-grpc/proto \
path/to/cln-grpc/proto/node.proto \
--python_out=. \
--grpc_python_out=. \
--experimental_allow_proto3_optional
This will generate two files in the current directory:
node_pb2.py
: the description of the protobuf messages we'll be exchanging with the server.node_pb2_grpc.py
: the service and method stubs representing the server-side methods as local objects and associated methods.
And finally we can use the generated stubs and mTLS identity to connect to the node:
from pathlib import Path
from node_pb2_grpc import NodeStub
import node_pb2
p = Path(".")
cert_path = p / "client.pem"
key_path = p / "client-key.pem"
ca_cert_path = p / "ca.pem"
creds = grpc.ssl_channel_credentials(
root_certificates=ca_cert_path.open('rb').read(),
private_key=key_path.open('rb').read(),
certificate_chain=cert_path.open('rb').read()
)
channel = grpc.secure_channel(
f"localhost:{grpc_port}",
creds,
options=(('grpc.ssl_target_name_override', 'cln'),)
)
stub = NodeStub(channel)
print(stub.Getinfo(node_pb2.GetinfoRequest()))
In this example we first local the client identity, as well as the CA
certificate so we can verify the server's identity against it. We then
create a creds
instance using those details. Next we open a secure
channel, i.e., a channel over TLS with verification of identities.
Notice that we override the expected SSL name with cln
. This is
required because the plugin does not know the domain under which it
will be reachable, and will therefore use cln
as a standin. See
custom certificate generation for how this could be changed.
We then use the channel to instantiate the NodeStub
representing the
service and its methods, so we can finally call the Getinfo
method
with default arguments.
Generating custom certificates
The automatically generated mTLS certificate will not know about potential domains that it'll be served under, and will chose a number of other parameters by default. If you'd like to generate a server certificate with a custom domain you can use the following:
openssl genrsa -out server-key.pem 2048
This generates the private key. Next we create a Certificate Signature Request (CSR) that we can then process using our CA identity:
openssl req -key server-key.pem -new -out server.csr
You will be asked a number of questions, the most important of which is the Common Name, which you should set to the domain name you'll be serving the interface under. Next we can generate the actual certificate by processing the request with the CA identity:
openssl x509 -req -CA ca.pem -CAkey ca-key.pem \
-in server.csr \
-out server.pem \
-days 365 -CAcreateserial
This will finally create the server.pem
file, signed by the CA,
allowing you to access the node through its real domain name. You can
now move server.pem
and server-key.pem
into the lightning
directory, and they should be picked up during the start.
Dependencies
~27–38MB
~644K SLoC