lnd/docs/macaroons.md
habibitcoin 0b1e881d18
scripted-diff: replace ⛰ emoji with $ [skip ci]
-BEGIN VERIFY SCRIPT-
sed -i 's/⛰/$/g' $(git grep -l '⛰')
-END VERIFY SCRIPT-
2022-10-28 12:06:49 -04:00

212 lines
11 KiB
Markdown

As part of [the `lnd` 0.3-alpha
release](https://github.com/lightningnetwork/lnd/releases/tag/v0.3-alpha), we
have addressed [issue 20](https://github.com/lightningnetwork/lnd/issues/20),
which is RPC authentication. Until this was implemented, all RPC calls to `lnd`
were unauthenticated. To fix this, we've utilized
[macaroons](https://research.google.com/pubs/pub41892.html), which are similar
to cookies but more capable. This brief overview explains, at a basic level,
how they work, how we use them for `lnd` authentication, and our future plans.
## What are macaroons?
You can think of a macaroon as a cookie, in a way. Cookies are small bits of
data that your browser stores and sends to a particular website when it makes a
request to that website. If you're logged into a website, that cookie can store
a session ID, which the site can look up in its own database to check who you
are and give you the appropriate content.
A macaroon is similar: it's a small bit of data that a client (like `lncli`)
can send to a service (like `lnd`) to assert that it's allowed to perform an
action. The service looks up the macaroon ID and verifies that the macaroon was
initially signed with the service's root key. However, unlike a cookie, you can
*delegate* a macaroon, or create a version of it that has more limited
capabilities, and then send it to someone else to use.
Just like a cookie, a macaroon should be sent over a secure channel (such as a
TLS-encrypted connection), which is why we've also begun enforcing TLS for RPC
requests in this release. Before SSL was enforced on websites such as Facebook
and Google, listening to HTTP sessions on wireless networks was one way to
hijack the session and log in as that user, gaining access to the user's
account. Macaroons are similar in that intercepting a macaroon in transit
allows the interceptor to use the macaroon to gain all the privileges of the
legitimate user.
## Macaroon delegation
A macaroon is delegated by adding restrictions (called caveats) and an
authentication code similar to a signature (technically an HMAC) to it. The
technical method of doing this is outside the scope of this overview
documentation, but the [README in the macaroons package](../macaroons/README.md)
or the macaroon paper linked above describe it in more detail. The
user must remember several things:
* Sharing a macaroon allows anyone in possession of that macaroon to use it to
access the service (in our case, `lnd`) to do anything permitted by the
macaroon. There is a specific type of restriction, called a "third party
caveat," that requires an external service to verify the request; however,
`lnd` doesn't currently implement those.
* If you add a caveat to a macaroon and share the resulting macaroon, the
person receiving it cannot remove the caveat.
This is used in `lnd` in an interesting way. By default, when `lnd` starts, it
creates three files which contain macaroons: a file called `admin.macaroon`,
which contains a macaroon with no caveats, a file called `readonly.macaroon`,
which is the *same* macaroon but with an additional caveat, that permits only
methods that don't change the state of `lnd`, and `invoice.macaroon`, which
only has access to invoice related methods.
## How macaroons are used by `lnd` and `lncli`.
On startup, `lnd` checks to see if the `admin.macaroon`, `readonly.macaroon`
and `invoice.macaroon` files exist. If they don't exist, `lnd` updates its
database with a new macaroon ID, generates the three files `admin.macaroon`,
`readonly.macaroon` and `invoice.macaroon`, all with the same ID. The
`readonly.macaroon` file has an additional caveat which restricts the caller
to using only read-only methods and the `invoice.macaroon` also has an
additional caveat which restricts the caller to using only invoice related
methods. This means a few important things:
* You can delete the `admin.macaroon` and be left with only the
`readonly.macaroon`, which can sometimes be useful (for example, if you want
your `lnd` instance to run in autopilot mode and don't want to accidentally
change its state).
* If you delete the data directory which contains the `macaroons.db` file, this
invalidates the `admin.macaroon`, `readonly.macaroon` and `invoice.macaroon`
files. Invalid macaroon files give you errors like `cannot get macaroon: root
key with id 0 doesn't exist` or `verification failed: signature mismatch
after caveat verification`.
You can also run `lnd` with the `--no-macaroons` option, which skips the
creation of the macaroon files and all macaroon checks within the RPC server.
This means you can still pass a macaroon to the RPC server with a client, but
it won't be checked for validity. Note that disabling authentication of a server
that's listening on a public interface is not allowed. This means the
`--no-macaroons` option is only permitted when the RPC server is in a private
network. In CIDR notation, the following IPs are considered private,
- [`169.254.0.0/16` and `fe80::/10`](https://en.wikipedia.org/wiki/Link-local_address).
- [`224.0.0.0/4` and `ff00::/8`](https://en.wikipedia.org/wiki/Multicast_address).
- [`10.0.0.0/8`, `172.16.0.0/12` and `192.168.0.0/16`](https://tools.ietf.org/html/rfc1918).
- [`fc00::/7`](https://tools.ietf.org/html/rfc4193).
Since `lnd` requires macaroons by default in order to call RPC methods, `lncli`
now reads a macaroon and provides it in the RPC call. Unless the path is
changed by the `--macaroonpath` option, `lncli` tries to read the macaroon from
the network directory of `lnd`'s currently active network (e.g. for simnet
`lnddir/data/chain/bitcoin/simnet/admin.macaroon`) by default and will error if
that file doesn't exist unless provided the `--no-macaroons` option. Keep this
in mind when running `lnd` with `--no-macaroons`, as `lncli` will error out
unless called the same way **or** `lnd` has generated a macaroon on a previous
run without this option.
`lncli` also adds a caveat which makes it valid for only 60 seconds by default
to help prevent replay in case the macaroon is somehow intercepted in
transmission. This is unlikely with TLS, but can happen e.g. when using a PKI
and network setup which allows inspection of encrypted traffic, and an attacker
gets access to the traffic logs after interception. The default 60 second
timeout can be changed with the `--macaroontimeout` option; this can be
increased for making RPC calls between systems whose clocks are more than 60s
apart.
## Stateless initialization
As mentioned above, by default `lnd` creates several macaroon files in its
directory. These are unencrypted and in case of the `admin.macaroon` provide
full access to the daemon. This can be seen as quite a big security risk if
the `lnd` daemon runs in an environment that is not fully trusted.
The macaroon files are the only files with highly sensitive information that
are not encrypted (unlike the wallet file and the macaroon database file that
contains the [root key](../macaroons/README.md), these are always encrypted,
even if no password is used).
To avoid leaking the macaroon information, `lnd` supports the so called
`stateless initialization` mode:
* The three startup commands `create`, `unlock` and `changepassword` of `lncli`
all have a flag called `--stateless_init` that instructs the daemon **not**
to create `*.macaroon` files.
* The two operations `create` and `changepassword` that actually create/update
the macaroon database will return the admin macaroon in the RPC call.
Assuming the daemon and the `lncli` are not used on the same machine, this
will leave no unencrypted information on the machine where `lnd` runs on.
* To be more precise: By default, when using the `changepassword` command, the
macaroon root key in the macaroon DB is just re-encrypted with the new
password. But the key remains the same and therefore the macaroons issued
before the `changepassword` command still remain valid. If a user wants to
invalidate all previously created macaroons, the `--new_mac_root_key` flag
of the `changepassword` command should be used!
* An user of `lncli` will see the returned admin macaroon printed to the screen
or saved to a file if the parameter `--save_to=some_file.macaroon` is used.
* **Important:** By default, `lnd` will create the macaroon files during the
`unlock` phase, if the `--stateless_init` flag is not used. So to avoid
leakage of the macaroon information, use the stateless initialization flag
for all three startup commands of the wallet unlocker service!
Examples:
* Create a new wallet stateless (first run):
```shell
$ lncli create --stateless_init --save_to=/safe/location/admin.macaroon
```
* Unlock a wallet that has previously been initialized stateless:
```shell
$ lncli unlock --stateless_init
```
* Use the created macaroon:
```shell
$ lncli --macaroonpath=/safe/location/admin.macaroon getinfo
```
## Using Macaroons with GRPC clients
When interacting with `lnd` using the GRPC interface, the macaroons are encoded
as a hex string over the wire and can be passed to `lnd` by specifying the
hex-encoded macaroon as GRPC metadata:
```text
GET https://localhost:8080/v1/getinfo
Grpc-Metadata-macaroon: <macaroon>
```
Where `<macaroon>` is the hex encoded binary data from the macaroon file itself.
A very simple example using `curl` may look something like this:
```shell
$ curl --insecure --header "Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 $HOME/.lnd/data/chain/bitcoin/simnet/admin.macaroon)" https://localhost:8080/v1/getinfo
```
Have a look at the [Java GRPC example](/docs/grpc/java.md) for programmatic usage details.
## Creating macaroons with custom permissions
The macaroon bakery is described in more detail in the
[README in the macaroons package](../macaroons/README.md).
## Future improvements to the `lnd` macaroon implementation
The existing macaroon implementation in `lnd` and `lncli` lays the groundwork
for future improvements in functionality and security. We will add features
such as:
* Improved replay protection for securing RPC calls
* Macaroon database encryption
* Root key rotation and possibly macaroon invalidation/rotation
* Additional restrictions, such as limiting payments to use (or not use)
specific routes, channels, nodes, etc.
* Accounting-based macaroons, which can make an instance of `lnd` act almost
like a bank for apps: for example, an app that pays to consume APIs whose
budget is limited to the money it receives by providing an API/service
* Support for third-party caveats, which allows external plugins for
authorization and authentication
With this new feature, we've started laying the groundwork for flexible
authentication and authorization for RPC calls to `lnd`. We look forward to
expanding its functionality to make it easy to develop secure apps.