Merge pull request #785 from brndnmtthws/master

Add more details on using macaroons with GRPC.
This commit is contained in:
Olaoluwa Osuntokun 2018-02-28 19:27:13 -08:00 committed by GitHub
commit 9479b085e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 21 deletions

View File

@ -62,6 +62,11 @@ The following dependencies are required.
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>2.0.7.Final</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
</dependencies>
```
In the build section, we'll need to configure the following things :
@ -98,32 +103,83 @@ In the build section, we'll need to configure the following things :
```
#### Main.java
```java
import io.grpc.Attributes;
import io.grpc.CallCredentials;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.Status;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NettyChannelBuilder;
import io.netty.handler.ssl.SslContext;
import lnrpc.LightningGrpc;
import lnrpc.LightningGrpc.LightningBlockingStub;
import lnrpc.Rpc.*;
import lnrpc.Rpc.GetInfoRequest;
import lnrpc.Rpc.GetInfoResponse;
import org.apache.commons.codec.binary.Hex;
import javax.net.ssl.SSLException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.Executor;
public class Main {
static class MacaroonCallCredential implements CallCredentials {
private final String macaroon;
private static final String CERT_PATH = "/Users/user/Library/Application Support/Lnd/tls.cert";
private static final String HOST = "localhost";
private static final int PORT = 10009;
public static void main(String... args) throws SSLException {
SslContext sslContext = GrpcSslContexts.forClient().trustManager(new File(CERT_PATH)).build();
NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress(HOST, PORT);
ManagedChannel channel = channelBuilder.sslContext(sslContext).build();
LightningBlockingStub stub = LightningGrpc.newBlockingStub(channel);
GetInfoResponse response = stub.getInfo(GetInfoRequest.getDefaultInstance());
System.out.println(response.getIdentityPubkey());
MacaroonCallCredential(String macaroon) {
this.macaroon = macaroon;
}
public void thisUsesUnstableApi() {}
public void applyRequestMetadata(
MethodDescriptor < ? , ? > methodDescriptor,
Attributes attributes,
Executor executor,
final MetadataApplier metadataApplier
) {
String authority = attributes.get(ATTR_AUTHORITY);
System.out.println(authority);
executor.execute(new Runnable() {
public void run() {
try {
Metadata headers = new Metadata();
Metadata.Key < String > macaroonKey = Metadata.Key.of("macaroon", Metadata.ASCII_STRING_MARSHALLER);
headers.put(macaroonKey, macaroon);
metadataApplier.apply(headers);
} catch (Throwable e) {
metadataApplier.fail(Status.UNAUTHENTICATED.withCause(e));
}
}
});
}
}
private static final String CERT_PATH = "/Users/user/Library/Application Support/Lnd/tls.cert";
private static final String MACAROON_PATH = "/Users/user/Library/Application Support/Lnd/admin.macaroon";
private static final String HOST = "localhost";
private static final int PORT = 10009;
public static void main(String...args) throws IOException {
SslContext sslContext = GrpcSslContexts.forClient().trustManager(new File(CERT_PATH)).build();
NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress(HOST, PORT);
ManagedChannel channel = channelBuilder.sslContext(sslContext).build();
String macaroon =
Hex.encodeHexString(
Files.readAllBytes(Paths.get(MACAROON_PATH))
);
LightningBlockingStub stub = LightningGrpc
.newBlockingStub(channel)
.withCallCredentials(new MacaroonCallCredential(macaroon));
GetInfoResponse response = stub.getInfo(GetInfoRequest.getDefaultInstance());
System.out.println(response.getIdentityPubkey());
}
}
```
#### Running the example
@ -143,27 +199,27 @@ mvn compile exec:java -Dexec.mainClass="Main" -Dexec.cleanupDaemonThreads=false
[INFO] os.detected.version.major: 10
[INFO] os.detected.version.minor: 13
[INFO] os.detected.classifier: osx-x86_64
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building lightning-client 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO]
[INFO] --- protobuf-maven-plugin:0.5.0:compile (default) @ lightning-client ---
[INFO] Compiling 3 proto file(s) to /Users/user/Documents/Projects/lightningclient/target/generated-sources/protobuf/java
[INFO]
[INFO]
[INFO] --- protobuf-maven-plugin:0.5.0:compile-custom (default) @ lightning-client ---
[INFO] Compiling 3 proto file(s) to /Users/user/Documents/Projects/lightningclient/target/generated-sources/protobuf/grpc-java
[INFO]
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ lightning-client ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO] Copying 3 resources
[INFO] Copying 3 resources
[INFO]
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ lightning-client ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 12 source files to /Users/user/Documents/Projects/lightningclient/target/classes
[INFO]
[INFO]
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ lightning-client ---
032562215c38dede6f1f2f262ff4c8db58a38ecf889e8e907eee8e4c320e0b5e81
[INFO] ------------------------------------------------------------------------
@ -181,4 +237,4 @@ There are 2 options available that can be used in the *rpc.proto* file :
* option java_multiple_files = true;
* option java_package = "network.lightning.rpc";
>The package you want to use for your generated Java classes. If no explicit java_package option is given in the .proto file, then by default the proto package (specified using the "package" keyword in the .proto file) will be used. However, proto packages generally do not make good Java packages since proto packages are not expected to start with reverse domain names. If not generating Java code, this option has no effect.
>The package you want to use for your generated Java classes. If no explicit java_package option is given in the .proto file, then by default the proto package (specified using the "package" keyword in the .proto file) will be used. However, proto packages generally do not make good Java packages since proto packages are not expected to start with reverse domain names. If not generating Java code, this option has no effect.

View File

@ -97,6 +97,23 @@ 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.
## 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:
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:
curl --insecure --header "Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 $HOME/.lnd/admin.macaroon)" https://localhost:8080/v1/getinfo
Have a look at the [Java GRPC example](/docs/grpc/java.md) for programmatic usage details.
## Future improvements to the `lnd` macaroon implementation
The existing macaroon implementation in `lnd` and `lncli` lays the groundwork