Skip to content

Commit

Permalink
Load certificates from filesystem and classpath. (#198)
Browse files Browse the repository at this point in the history
Fixes #165
  • Loading branch information
ilopmar authored Aug 21, 2020
1 parent 7aed02f commit fb77d2f
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 42 deletions.
5 changes: 4 additions & 1 deletion grpc-server-runtime/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
id 'com.google.protobuf' version '0.8.13'
}
dependencies {
annotationProcessor "io.micronaut:micronaut-inject-java:$micronautVersion"
annotationProcessor "io.micronaut:micronaut-inject-java:$micronautVersion"
annotationProcessor "io.micronaut.docs:micronaut-docs-asciidoc-config-props:$micronautDocsVersion"
api project(":grpc-annotation")
api "io.micronaut:micronaut-inject:$micronautVersion"
Expand All @@ -25,6 +25,9 @@ dependencies {
testImplementation "io.micronaut:micronaut-inject-java:$micronautVersion"
testImplementation 'io.micronaut.test:micronaut-test-spock:1.2.2'
testImplementation project(":grpc-client-runtime")

testRuntimeOnly "io.netty:netty-tcnative:2.0.29.Final"
testRuntimeOnly "io.netty:netty-tcnative-boringssl-static:2.0.29.Final"
}

// compileJava.options.fork=true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import io.micronaut.context.annotation.Property;
import io.micronaut.context.env.Environment;
import io.micronaut.context.exceptions.ConfigurationException;
import io.micronaut.core.annotation.Creator;
import io.micronaut.core.convert.format.ReadableBytes;
import io.micronaut.core.io.ResourceResolver;
import io.micronaut.core.io.socket.SocketUtils;
import io.micronaut.scheduling.TaskExecutors;

Expand All @@ -41,6 +43,7 @@
* Configuration for the GRPC server.
*
* @author graemerocher
* @author Iván López
* @since 1.0
*/
@ConfigurationProperties(GrpcServerConfiguration.PREFIX)
Expand All @@ -56,24 +59,44 @@ public class GrpcServerConfiguration {
protected final NettyServerBuilder serverBuilder;
private final int serverPort;
private final String serverHost;
private final Environment environment;
private final ResourceResolver resourceResolver;
private GrpcSslConfiguration serverConfiguration = new GrpcSslConfiguration();
private boolean secure = false;
private String instanceId = "";

/**
* Default constructor.
* @param environment The environment
* @param serverHost The server host
* @param serverPort The server port
* Constructor.
*
* @param environment The environment
* @param serverHost The server host
* @param serverPort The server port
* @param executorService The IO executor service
*/
@Deprecated
public GrpcServerConfiguration(
Environment environment,
@Property(name = HOST) @Nullable String serverHost,
@Property(name = PORT) @Nullable Integer serverPort,
@Named(TaskExecutors.IO) ExecutorService executorService) {
this.environment = environment;
this(environment, serverHost, serverPort, executorService, null);
}

/**
* Default constructor.
*
* @param environment The environment
* @param serverHost The server host
* @param serverPort The server port
* @param executorService The IO executor service
* @param resourceResolver The resource resolver
*/
@Creator
public GrpcServerConfiguration(
Environment environment,
@Property(name = HOST) @Nullable String serverHost,
@Property(name = PORT) @Nullable Integer serverPort,
@Named(TaskExecutors.IO) ExecutorService executorService,
ResourceResolver resourceResolver) {
this.serverPort = serverPort != null ? serverPort :
environment.getActiveNames().contains(Environment.TEST) ? SocketUtils.findAvailableTcpPort() : DEFAULT_PORT;
this.serverHost = serverHost;
Expand All @@ -85,6 +108,7 @@ public GrpcServerConfiguration(
this.serverBuilder = NettyServerBuilder.forPort(this.serverPort);
}
this.serverBuilder.executor(executorService);
this.resourceResolver = resourceResolver;
}

/**
Expand Down Expand Up @@ -192,45 +216,45 @@ public void setMaxInboundMetadataSize(@ReadableBytes int bytes) {
public void setServerConfiguration(GrpcSslConfiguration sslConfiguration) {
if (sslConfiguration != null) {
this.serverConfiguration = sslConfiguration;
final Optional<InputStream> certChain = sslConfiguration.getCertChain()
.flatMap(environment::getResourceAsStream);
final Optional<InputStream> privateKey = sslConfiguration.getPrivateKey()
.flatMap(environment::getResourceAsStream);

final boolean hasCert = certChain.isPresent();
final boolean hasPrivateKey = privateKey.isPresent();
if (hasCert && hasPrivateKey) {
try {
try (InputStream certStream = certChain.get()) {
try (InputStream keyStream = privateKey.get()) {
serverBuilder.useTransportSecurity(
certStream,
keyStream
);
}
}
this.secure = true;
} catch (IOException e) {
throw new ConfigurationException("Unable to configure SSL certificate: " + e.getMessage(), e);
}
} else {
if (hasCert) {

if (resourceResolver != null) {
final Optional<InputStream> certChain = sslConfiguration.getCertChain().flatMap(resourceResolver::getResourceAsStream);
final Optional<InputStream> privateKey = sslConfiguration.getPrivateKey().flatMap(resourceResolver::getResourceAsStream);

final boolean hasCert = certChain.isPresent();
final boolean hasPrivateKey = privateKey.isPresent();
if (hasCert && hasPrivateKey) {
try {
certChain.get().close();
try (InputStream certStream = certChain.get()) {
try (InputStream keyStream = privateKey.get()) {
serverBuilder.useTransportSecurity(
certStream,
keyStream
);
}
}
this.secure = true;
} catch (IOException e) {
// ignore
throw new ConfigurationException("Unable to configure SSL certificate: " + e.getMessage(), e);
}
throw new ConfigurationException("Both 'cert-chain' and 'private-key' properties should be configured");
} else if (hasPrivateKey) {
try {
privateKey.get().close();
} catch (IOException e) {
// ignore
} else {
if (hasCert) {
try {
certChain.get().close();
} catch (IOException e) {
// ignore
}
throw new ConfigurationException("Both 'cert-chain' and 'private-key' properties should be configured");
} else if (hasPrivateKey) {
try {
privateKey.get().close();
} catch (IOException e) {
// ignore
}
throw new ConfigurationException("Both 'cert-chain' and 'private-key' properties should be configured");
}
throw new ConfigurationException("Both 'cert-chain' and 'private-key' properties should be configured");
}
}

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class GrpcServerInstance implements EmbeddedServerInstance {
* @param uri The URI
* @param metadata The metadata
* @param metadataContributors The metadata contributors
* @param grpcConfiguration The GRPC Configuration
*/
GrpcServerInstance(
EmbeddedServer embeddedServer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,27 @@ class GrpcServerConfigurationSpec extends Specification {
server.shutdown()
ctx.close()
}

void "test GRPC SSL configuration"() {
given:
def port = SocketUtils.findAvailableTcpPort()
def ctx = ApplicationContext.run([
'grpc.server.port' : port,
'grpc.server.ssl.cert-chain' : 'classpath:example.crt',
'grpc.server.ssl.private-key' : 'classpath:example.key',
])

when:
GrpcServerConfiguration configuration = ctx.getBean(GrpcServerConfiguration)
ServerBuilder serverBuilder = configuration.getServerBuilder()
def server = serverBuilder.build()
server.start()

then:
noExceptionThrown()

cleanup:
server.shutdown()
ctx.close()
}
}
31 changes: 31 additions & 0 deletions grpc-server-runtime/src/test/resources/example.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIUeNH4sv/Qq1xJ9AhhsjAfN8x2E6EwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA4MjEwODUzMjdaFw0zMDA4
MTkwODUzMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCuCTdpW29qrE2VR+HYClgMGWzMSi10aAbMBVnk03Wh
sxWKamAD7JKTi+GTZnUaSCQDd2z/BanK0B2dz70b9vIpdIOivOEZdkeoZ0/Frhma
elAnrFUU8u1af+V6jD3m5PIMwwClcPRY1hbs2ojjuOSQ0HikksLjbqrorm9eFFu9
LdfgpZi3A40ud+75zACOkS3yyBMVjI1LXsklbPBPVVU1PVQhENCM1a36OBqiC/3Q
Y5jqG6K2equiqDw6rHSANWc4GnE6vQHXU2HYzC+YDC3VgJ4xArZbKcihqTWHHrqx
amaYGgCybWaimj+aUDH8Uhr5iieZx2TGRh6rwuekTzxNF6KEeJcmQd1nFFG4er5x
Zltdyf50galo+qYePkFS6RSfIBeDXU8X1e1HxPSd+TyKsHtuHzxOIAkUrQu9ABAS
7CsOP+34rWmafnHsx/IDS+O6KSPP9c2So6esMyR+yaHLjDUM3dL3u+yKq/48g4LL
Lx0yOtaqtJfpeeaWjp88mFlA33XQURJnTC2xp2pr6m9ey94MXPEIw+CqpZ9ficbz
KAbeBqW/iUTSnyLtxc2qlLKV6kszJxQ10p9gnd40mz+iloosIScehZOVuGmGeNdw
C/xiiCxrYMl+7gDLPIADnTkAMtyhKsnmfalf1ic35l5wl06ZuU4SOKPiZPBlDlXc
yQIDAQABo1MwUTAdBgNVHQ4EFgQU5/rVJIPrMRDEayzRPQ3A24Q7hikwHwYDVR0j
BBgwFoAU5/rVJIPrMRDEayzRPQ3A24Q7hikwDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAQcYjKCNf6mnMZ4CZ6lWv00B2FcJj2p57tQ6DY+nEZU8P
fTiCFdC2Kjl/st2y5T6JapWd//L+RNaFPGw4AHL4193tTAX5klgGjOhL28Wd7LVF
50mELYzbXGXAcGPyMqxIyc1ByRP4/rPoa0gjLz+/vnxuiNHzHPX0JxmrBK1P6yCG
jOGvt30BOMzljrsLKGAK9NRZ/OweWHim8p8osTH00FbAbY85YqA0I/oKO/ta1Hiw
hK+RCN/1xixoW4JZ5D1bzMdpW0BluaivLrHV5OuBlzYjHq5ACMXoRyqI5Lqhbptp
OxxMnxE8ESzFEqGZ4gdvoGsMu2ibpmfJZKMGGIKoE/EgglltPRcmDwUe48Xmn0SB
BV4AN49+BaDi0rczC1kt4XUKfBkPOZuVyfds6dUJuKdziy0yrnPGR87IhL269KTS
JDKgmtYpvVZ7aptShWCQ3Wvq6B45FBCQmCdBkcnlbYDA7aRtnCawRWgqNmj3sDWF
DM+3qEXZxKVQhWw+ccKBJLnA9gs+pfCgYxyH7IAPEN28NVY5MOOBW8jsIT0xPE84
Operh1jv9wXV3Bgmgm0KJe9C3CKYvqrELMXkSFDnnIyAnE22zOys3vUINSHAth71
TZQPAVxZ9Evf0ogZ1yMauXaafKWcLxvkAUVBCNmwlmVJH28BCvcTJEkVRWz3f8w=
-----END CERTIFICATE-----
52 changes: 52 additions & 0 deletions grpc-server-runtime/src/test/resources/example.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
7aK5AkoUiB014BYf67L01eOs0o0Xmg==
-----END PRIVATE KEY-----
6 changes: 4 additions & 2 deletions src/main/docs/guide/server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ grpc:
keep-alive-time: 3h
max-inbound-message-size: 1024
ssl:
cert-chain: '/path/to/my.cert'
private-key: '/path/to/my.key'
cert-chain: 'file://path/to/my.cert' # <1>
private-key: 'classpath:my.key' # <2>
----
<1> Load the certificate from `/path/to/my.cert` file.
<2> Load the private key from the classpath. The file should be in `src/main/resources/my.key`.

By default the GRPC server will be enabled. To disable the GRPC server, set `grpc.server.enabled` to false.

Expand Down

0 comments on commit fb77d2f

Please sign in to comment.