Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Enable rpc TLS and macaroon authentication #4129

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ configure(subprojects) {
logbackVersion = '1.1.11'
loggingVersion = '1.2'
lombokVersion = '1.18.2'
macaroonsVersion = '0.4.1'
mockitoVersion = '3.0.0'
netlayerVersion = '0.6.7'
protobufVersion = '3.10.0'
Expand Down Expand Up @@ -137,7 +138,7 @@ configure([project(':cli'),
unixScriptFile.text = unixScriptFile.text.replace(
'cd "`dirname \\"$PRG\\"`/.." >/dev/null', 'cd "`dirname \\"$PRG\\"`" >/dev/null')

if (applicationName == 'desktop') {
if (applicationName == 'desktop' || applicationName == 'daemon' || applicationName == 'cli') {
def script = file("${rootProject.projectDir}/bisq-$applicationName")
script.text = script.text.replace(
'DEFAULT_JVM_OPTS=""', 'DEFAULT_JVM_OPTS="-XX:MaxRAM=4g"')
Expand Down Expand Up @@ -305,6 +306,7 @@ configure(project(':core')) {
compile("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") {
exclude(module: 'jackson-annotations')
}
implementation "com.github.nitram509:jmacaroons:$macaroonsVersion"
implementation "com.google.protobuf:protobuf-java:$protobufVersion"
implementation("io.grpc:grpc-protobuf:$grpcVersion") {
exclude(module: 'guava')
Expand Down Expand Up @@ -340,6 +342,8 @@ configure(project(':cli')) {
dependencies {
compile project(':proto')
implementation "net.sf.jopt-simple:jopt-simple:$joptVersion"
implementation "commons-codec:commons-codec:$codecVersion"
implementation "com.github.nitram509:jmacaroons:$macaroonsVersion"
implementation "com.google.guava:guava:$guavaVersion"
implementation "com.google.protobuf:protobuf-java:$protobufVersion"
implementation("io.grpc:grpc-core:$grpcVersion") {
Expand All @@ -350,7 +354,7 @@ configure(project(':cli')) {
exclude(module: 'guava')
exclude(module: 'animal-sniffer-annotations')
}
runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") {
implementation("io.grpc:grpc-netty-shaded:$grpcVersion") {
exclude(module: 'guava')
exclude(module: 'animal-sniffer-annotations')
}
Expand Down
58 changes: 58 additions & 0 deletions cert/aes256/generate-aes256.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#! /bin/bash

# https://stackoverflow.com/questions/53047940/grpc-java-set-up-sslcontext-on-server
#
# https://grpc.io/docs/guides/auth
#
# To enable TLS on a server, a certificate chain and private key need to be specified in PEM format.
# Such private key should not be using a password. The order of certificates in the chain matters:
# more specifically, the certificate at the top has to be the host CA, while the one at the very
# bottom has to be the root CA. The standard TLS port is 443, but we use 8443 below to avoid
# needing extra permissions from the OS.
#
# If the issuing certificate authority is not known to the client then a properly configured
# SslContext or SSLSocketFactory should be provided to the NettyChannelBuilder or OkHttpChannelBuilder,
# respectively.

# Set-up SSL on my GRPC server: a certificate chain and a pkcs8 private key

# Generate CA key:

openssl genrsa -aes256 -out ca.key 4096


# Generate CA certificate:

# Common Name (e.g. server FQDN or YOUR name) []:localhost
openssl req -new -x509 -days 365 -key ca.key -out ca.crt

Check cert
openssl x509 -in ca.crt -text -noout


# Generate server key:

openssl genrsa -aes256 -out server.key 4096


# Generate server signing request:

openssl req -new -key server.key -out server.csr
# Common Name (e.g. server FQDN or YOUR name) []:localhost


# Self-sign server certificate:

openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt


# Remove passphrase from the server key:

openssl rsa -in server.key -out server.key

# Convert to pkcs8

openssl pkcs8 -topk8 -nocrypt -in server.key -out pkcs8_key.pem

# -dname "cn=bisq.network, ou=None, L=YYY, ST=TTTT, o=ExampleOrg, c=AT
# Common Name (e.g. server FQDN or YOUR name) []:localhost
52 changes: 52 additions & 0 deletions cert/aes256/pkcs8_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
sV/NBNgqI8wB48EZ98I0rJY57allWQ==
-----END PRIVATE KEY-----
30 changes: 30 additions & 0 deletions cert/aes256/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFJjCCAw4CAQEwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDQwMjE0NDMyNVoXDTIxMDQwMjE0
NDMyNVowWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNV
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1kiwRvRn4CZ8v+vEUSmA
65+Oo+0UaGlfv1YrlOESxZ9P8SOFjeUBhujPkRhY8zsqlYzzX3n0BkwhNHowDY0F
pGLhcLLNZjl7rSs+OOPgVw9MckP/T7y5BYdgdi89RzYkUKt6WPHqVknGbAT0Bvgi
s/TkcwtjbmtqOgMTXx4Mefjyyw/QMT5dsqjAHwU55GqhjIrLHRb3auNWG4lDbe3k
IMeJno4eRiXbuwr9s+iEOm2gnx9+W4E1qMb4fKmTkvWaYihwddN3+s6nxiS3Lu96
dp/9SDd+JynjhWUbzlVntUSkHgZrVmYe8/35ZLrNcHIcb2nYNVb1bt6W11dE3WIP
k14Zee/bEwwswNeBmkMMlmRxyt+M2TPQSZuoQVIom33vxnK+KVeH3M7E1o0Czdgd
JfEwbooJvkW052JDDxLZaLkbECsUZWAYlADXrfzi/sbhlflyHGqAADUJdbQSz6f2
WLrRvGftsJAH8reacSvzlUP86GzKf7RHs/eiTAxEsv6BDO06TjSBDe8w8lvbFmpd
LFCBL+BKdmzlAFY9XZLDA3lJAr/SHB2w6y9ACoZMBcgkQqfd14BvqlzPcVNOwqSH
L/H6srYJxRu35BP//aXB3VKRoJ7WkEsznFyCjsFjM6XoQlBHm1HU1qDq89gPE9h4
/cUx5f791ya989f7RBtwM5UCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAgOif9nlf
MCM5xGiNLZoK1IKeCOXD2CNqGzpBhXKv7DnZPqhpy0+qTmGrYjX1lEO0sl7NrSoK
RWUf6iILHYoXYABXI+WjPq6btpXKuVFkG7Bd7PGEYdgXcptbwt13Jtsm+p7wg2u7
m26tic+6E/yscWgxNE/n0ivBeKdGilBuaAtBwMKBVAwZnWArJ/Lpb3ZV2fDHpkeC
AVhF1q0bQXbPeTYNbzDrJSXinxmQOKcoR4iaVrGLrsrMF0buG1BafH9EVVysHqh/
4pfdG1a2fknG7DV5jM14zvXSSXodlltWc6LZEekCYMQc4iJqh9Yo59zeSOzWIG0X
iPu39b+mTZOz65XXZsbbfq3wLREikXg4kV9vyKXfpY5/ydUOR7N6VLXyqoxlLH9G
V1iHW1usflXBXsN2O+ul/Gpbeo9Xt05M0hA6p/r8An3u4gR4ns/m0jmmZ6pV9TaH
lpcFKBYXPKj4C/w0Gm8msXSKPXxvifpRkIrh8Gy3ZdeDDna7FIxNJWyt7qscg+5u
k6H1i9n3tsLeI98MwwUruOfM8E+UPAQr9KUdcfeqx+SmrDOx+5vWzWDvRdWWJjg+
1m4YxcgMKqT/sxIrgikJ5fwLOsy7t0lcxUv7rQZ8KAh7NUA6LXxcM7QqWyekIdbn
lMXFZHhZv+0N/HVxDi7AH7Ni3SBeRPBlvmw=
-----END CERTIFICATE-----
57 changes: 57 additions & 0 deletions cert/des3/generate-des3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#! /bin/bash

# https://stackoverflow.com/questions/53047940/grpc-java-set-up-sslcontext-on-server
#
# https://grpc.io/docs/guides/auth
#
# To enable TLS on a server, a certificate chain and private key need to be specified in PEM format.
# Such private key should not be using a password. The order of certificates in the chain matters:
# more specifically, the certificate at the top has to be the host CA, while the one at the very
# bottom has to be the root CA. The standard TLS port is 443, but we use 8443 below to avoid
# needing extra permissions from the OS.
#
# If the issuing certificate authority is not known to the client then a properly configured
# SslContext or SSLSocketFactory should be provided to the NettyChannelBuilder or OkHttpChannelBuilder,
# respectively.

# Set-up SSL on my GRPC server: a certificate chain and a pkcs8 private key

# Generate CA key:

openssl genrsa -des3 -out ca.key 4096

# Generate CA certificate:

# Common Name (e.g. server FQDN or YOUR name) []:localhost
openssl req -new -x509 -days 365 -key ca.key -out ca.crt

Check cert
openssl x509 -in ca.crt -text -noout


# Generate server key:

openssl genrsa -des3 -out server.key 4096


# Generate server signing request:

openssl req -new -key server.key -out server.csr
# Common Name (e.g. server FQDN or YOUR name) []:localhost


# Self-sign server certificate:

openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt


# Remove passphrase from the server key:

openssl rsa -in server.key -out server.key

# Convert to pkcs8

openssl pkcs8 -topk8 -nocrypt -in server.key -out pkcs8_key.pem

# -dname "cn=bisq.network, ou=None, L=YYY, ST=TTTT, o=ExampleOrg, c=AT
# Common Name (e.g. server FQDN or YOUR name) []:localhost
71 changes: 57 additions & 14 deletions cli/src/main/java/bisq/cli/app/BisqCliMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,27 @@
package bisq.cli.app;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;

import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;

import org.apache.commons.codec.binary.Hex;

import javax.net.ssl.SSLException;

import java.nio.file.Files;
import java.nio.file.Paths;

import java.io.File;
import java.io.IOException;

import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import lombok.extern.slf4j.Slf4j;

Expand All @@ -34,6 +47,7 @@
import static bisq.cli.app.CommandParser.HELP;
import static bisq.cli.app.CommandParser.STOPSERVER;
import static java.lang.String.format;
import static java.lang.System.err;
import static java.lang.System.exit;
import static java.lang.System.out;

Expand All @@ -51,28 +65,31 @@ public class BisqCliMain {
private final OptionParser parser;

public static void main(String[] args) {
new BisqCliMain("localhost", 9998, args);
}

private BisqCliMain(String host, int port, String[] args) {
// Channels are secure by default (via SSL/TLS); for the example disable TLS to avoid needing certificates.
this(ManagedChannelBuilder.forAddress(host, port).usePlaintext().build());
String command = parseCommand(args);
String result = runCommand(command);
out.println(result);
try {
shutdown(); // Orderly channel shutdown
} catch (InterruptedException ignored) {
String certPath = "cert/aes256/server.crt";
NettyChannelBuilder channelBuilder = NettyChannelBuilder.forAddress("localhost", 9998)
.sslContext(GrpcSslContexts.forClient().trustManager(new File(certPath)).build());
new BisqCliMain(channelBuilder.build(), args);
} catch (SSLException e) {
e.printStackTrace();
exit(EXIT_FAILURE);
}
}

/**
* Construct client for accessing server using the existing channel.
*/
private BisqCliMain(ManagedChannel channel) {
private BisqCliMain(ManagedChannel channel, String[] args) {
this.channel = channel;
this.cmd = new CliCommand(channel);
this.cmd = new CliCommand(channel, loadMacaroon());
this.parser = new CommandParser().configure();
String command = parseCommand(args);
String result = runCommand(command);
out.println(result);
try {
shutdown(); // Orderly channel shutdown
} catch (InterruptedException ignored) {
}
}

private String runCommand(String command) {
Expand Down Expand Up @@ -109,8 +126,34 @@ private String parseCommand(String[] params) {
return detectedOptions.get(0);
}

private String loadMacaroon() {
String macaroonPath = appDataDirHack.get() + File.separatorChar + "bisqd.macaroon";
try {
return Hex.encodeHexString(Files.readAllBytes(Paths.get(macaroonPath)));
} catch (IOException e) {
err.println("Error encoding authentication token " + macaroonPath);
exit(EXIT_FAILURE);
return null;
}
}

private void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
exit(EXIT_SUCCESS);
}

// TODO Avoid duplicating these methods from bisq.common.util.Utilities, which are not visible to :cli.
private final Supplier<String> os = () -> System.getProperty("os.name").toLowerCase(Locale.US);
private final Supplier<Boolean> isLinux = () -> os.get().contains("linux");
private final Supplier<Boolean> isOSX = () -> os.get().contains("mac") || os.get().contains("osx");
private final Supplier<String> appDataDirHack = () -> {
String userHome = System.getProperty("user.home");
if (isLinux.get()) {
return userHome + File.separatorChar + ".local/share/Bisq";
} else if (isOSX.get()) {
return userHome + File.separatorChar + "Library/Application Support/Bisq";
} else {
throw new RuntimeException("OS " + os.get() + " not supported");
}
};
}
10 changes: 6 additions & 4 deletions cli/src/main/java/bisq/cli/app/CliCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
@Slf4j
final class CliCommand {

private final MacaroonCallCredential macaroonCallCredential;
private final GetBalanceGrpc.GetBalanceBlockingStub getBalanceStub;
private final GetVersionGrpc.GetVersionBlockingStub getVersionStub;
private final StopServerGrpc.StopServerBlockingStub stopServerStub;
Expand All @@ -30,10 +31,11 @@ final class CliCommand {
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
final Function<Long, String> prettyBalance = (sats) -> btcFormat.format(BigDecimal.valueOf(sats).divide(satoshiDivisor));

CliCommand(ManagedChannel channel) {
getBalanceStub = GetBalanceGrpc.newBlockingStub(channel);
getVersionStub = GetVersionGrpc.newBlockingStub(channel);
stopServerStub = StopServerGrpc.newBlockingStub(channel);
CliCommand(ManagedChannel channel, String macaroon) {
this.macaroonCallCredential = new MacaroonCallCredential(macaroon);
this.getBalanceStub = GetBalanceGrpc.newBlockingStub(channel).withCallCredentials(macaroonCallCredential);
this.getVersionStub = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(macaroonCallCredential);
this.stopServerStub = StopServerGrpc.newBlockingStub(channel).withCallCredentials(macaroonCallCredential);
}

String getVersion() {
Expand Down
Loading