Add support for server-side SSL
There's no integration test per se, but the GrpcServerApplicationTests main method runs the server securely with an anonymous certificate, so you can test it manually with grpcurl -insecure. See #10
This commit is contained in:
@@ -24,7 +24,7 @@ public class GrpcServerApplicationTests {
|
||||
private static Log log = LogFactory.getLog(GrpcServerApplicationTests.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(GrpcServerApplication.class, ExtraConfiguration.class).run(args);
|
||||
new SpringApplicationBuilder(GrpcServerApplication.class, ExtraConfiguration.class).profiles("ssl").run(args);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
spring.grpc.server.ssl.bundle=ssltest
|
||||
spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks
|
||||
spring.ssl.bundle.jks.ssltest.keystore.password=secret
|
||||
spring.ssl.bundle.jks.ssltest.keystore.type=JKS
|
||||
spring.ssl.bundle.jks.ssltest.key.password=password
|
||||
BIN
samples/grpc-server/src/test/resources/test.jks
Normal file
BIN
samples/grpc-server/src/test/resources/test.jks
Normal file
Binary file not shown.
@@ -18,14 +18,14 @@ package org.springframework.grpc.autoconfigure.server;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.grpc.BindableService;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.netty.NettyServerBuilder;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.grpc.server.DefaultGrpcServerFactory;
|
||||
@@ -34,6 +34,15 @@ import org.springframework.grpc.server.NettyGrpcServerFactory;
|
||||
import org.springframework.grpc.server.ServerBuilderCustomizer;
|
||||
import org.springframework.grpc.server.ShadedNettyGrpcServerFactory;
|
||||
|
||||
import io.grpc.BindableService;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.NettyServerBuilder;
|
||||
import io.netty.handler.ssl.ClientAuth;
|
||||
import io.netty.handler.ssl.JdkSslContext;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
|
||||
/**
|
||||
* Configurations for {@link GrpcServerFactory gRPC server factories}.
|
||||
*
|
||||
@@ -60,6 +69,29 @@ class GrpcServerFactoryConfigurations {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerBuilderCustomizer<io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder> sslServerCustomizer(
|
||||
GrpcServerProperties properties, SslBundles bundles) {
|
||||
if (properties.getSsl().isEnabled()) {
|
||||
SslBundle bundle = bundles.getBundle(properties.getSsl().getBundle());
|
||||
return builder -> {
|
||||
try {
|
||||
builder.sslContext(io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts
|
||||
.configure(io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder
|
||||
.forServer(bundle.getManagers().getKeyManagerFactory()))
|
||||
.build());
|
||||
}
|
||||
catch (SSLException e) {
|
||||
throw new IllegalStateException("Failed to create SSL context", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return builder -> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -81,6 +113,28 @@ class GrpcServerFactoryConfigurations {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerBuilderCustomizer<NettyServerBuilder> sslServerCustomizer(GrpcServerProperties properties,
|
||||
SslBundles bundles) {
|
||||
if (properties.getSsl().isEnabled()) {
|
||||
SslBundle bundle = bundles.getBundle(properties.getSsl().getBundle());
|
||||
return builder -> {
|
||||
try {
|
||||
builder.sslContext(GrpcSslContexts
|
||||
.configure(SslContextBuilder.forServer(bundle.getManagers().getKeyManagerFactory()))
|
||||
.build());
|
||||
}
|
||||
catch (SSLException e) {
|
||||
throw new IllegalStateException("Failed to create SSL context", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return builder -> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
@@ -215,4 +215,51 @@ public class GrpcServerProperties {
|
||||
|
||||
}
|
||||
|
||||
private final Ssl ssl = new Ssl();
|
||||
|
||||
public Ssl getSsl() {
|
||||
return this.ssl;
|
||||
}
|
||||
|
||||
public static class Ssl {
|
||||
|
||||
/**
|
||||
* Whether to enable SSL support. Enabled automatically if "bundle" is provided
|
||||
* unless specified otherwise.
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* SSL bundle name.
|
||||
*/
|
||||
private String bundle;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return (this.enabled != null) ? this.enabled : this.bundle != null;
|
||||
}
|
||||
|
||||
public void copyDefaultsFrom(Ssl config) {
|
||||
if (this.enabled == null) {
|
||||
this.enabled = config.enabled;
|
||||
}
|
||||
if (this.bundle == null) {
|
||||
this.bundle = config.bundle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getBundle() {
|
||||
return this.bundle;
|
||||
}
|
||||
|
||||
public void setBundle(String bundle) {
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -64,7 +65,7 @@ class GrpcServerAutoConfigurationTests {
|
||||
when(service.bindService()).thenReturn(serviceDefinition);
|
||||
// NOTE: we use noop server lifecycle to avoid startup
|
||||
return new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(GrpcServerAutoConfiguration.class))
|
||||
.withConfiguration(AutoConfigurations.of(GrpcServerAutoConfiguration.class, SslAutoConfiguration.class))
|
||||
.withBean("noopServerLifecycle", GrpcServerLifecycle.class, Mockito::mock)
|
||||
.withBean(BindableService.class, () -> service);
|
||||
}
|
||||
@@ -75,7 +76,7 @@ class GrpcServerAutoConfigurationTests {
|
||||
when(service.bindService()).thenReturn(serviceDefinition);
|
||||
// NOTE: we use noop server lifecycle to avoid startup
|
||||
return new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(GrpcServerAutoConfiguration.class))
|
||||
.withConfiguration(AutoConfigurations.of(GrpcServerAutoConfiguration.class, SslAutoConfiguration.class))
|
||||
.withBean(BindableService.class, () -> service);
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ class GrpcServerAutoConfigurationTests {
|
||||
.withUserConfiguration(ServerBuilderCustomizersConfig.class)
|
||||
.run((context) -> assertThat(context).getBean(ServerBuilderCustomizers.class)
|
||||
.extracting("customizers", InstanceOfAssertFactories.list(ServerBuilderCustomizer.class))
|
||||
.containsExactly(ServerBuilderCustomizersConfig.CUSTOMIZER_BAR,
|
||||
.contains(ServerBuilderCustomizersConfig.CUSTOMIZER_BAR,
|
||||
ServerBuilderCustomizersConfig.CUSTOMIZER_FOO));
|
||||
}
|
||||
|
||||
@@ -253,6 +254,17 @@ class GrpcServerAutoConfigurationTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyServerFactoryAutoConfiguredWithSsl() {
|
||||
serverFactoryAutoConfiguredAsExpected(
|
||||
this.contextRunner()
|
||||
.withPropertyValues("spring.grpc.server.ssl.bundle=ssltest",
|
||||
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks")
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder.class)),
|
||||
NettyGrpcServerFactory.class);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ServerBuilderCustomizersConfig {
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user