diff --git a/networking/src/main/java/google/registry/networking/handler/SslClientInitializer.java b/networking/src/main/java/google/registry/networking/handler/SslClientInitializer.java index 6376453a7..15c5781ab 100644 --- a/networking/src/main/java/google/registry/networking/handler/SslClientInitializer.java +++ b/networking/src/main/java/google/registry/networking/handler/SslClientInitializer.java @@ -17,16 +17,20 @@ package google.registry.networking.handler; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelInitializer; import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslProvider; +import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.function.Function; +import java.util.function.Supplier; import javax.inject.Singleton; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; @@ -47,14 +51,33 @@ public class SslClientInitializer extends ChannelInitializer< private final Function hostProvider; private final Function portProvider; private final SslProvider sslProvider; - private final X509Certificate[] trustedCertificates; + private final ImmutableList trustedCertificates; + // The following two suppliers only need be none-null when client authentication is required. + private final Supplier privateKeySupplier; + private final Supplier> certificateChainSupplier; - public SslClientInitializer( - SslProvider sslProvider, - Function hostProvider, - Function portProvider) { - // null uses the system default trust store. - this(sslProvider, hostProvider, portProvider, null); + public static SslClientInitializer + createSslClientInitializerWithSystemTrustStore( + SslProvider sslProvider, + Function hostProvider, + Function portProvider) { + return new SslClientInitializer<>(sslProvider, hostProvider, portProvider, null, null, null); + } + + public static SslClientInitializer + createSslClientInitializerWithSystemTrustStoreAndClientAuthentication( + SslProvider sslProvider, + Function hostProvider, + Function portProvider, + Supplier privateKeySupplier, + Supplier> certificateChainSupplier) { + return new SslClientInitializer<>( + sslProvider, + hostProvider, + portProvider, + ImmutableList.of(), + privateKeySupplier, + certificateChainSupplier); } @VisibleForTesting @@ -62,22 +85,38 @@ public class SslClientInitializer extends ChannelInitializer< SslProvider sslProvider, Function hostProvider, Function portProvider, - X509Certificate[] trustCertificates) { + ImmutableList trustedCertificates, + Supplier privateKeySupplier, + Supplier> certificateChainSupplier) { logger.atInfo().log("Client SSL Provider: %s", sslProvider); this.sslProvider = sslProvider; this.hostProvider = hostProvider; this.portProvider = portProvider; - this.trustedCertificates = trustCertificates; + this.trustedCertificates = trustedCertificates; + this.privateKeySupplier = privateKeySupplier; + this.certificateChainSupplier = certificateChainSupplier; } @Override protected void initChannel(C channel) throws Exception { checkNotNull(hostProvider.apply(channel), "Cannot obtain SSL host for channel: %s", channel); checkNotNull(portProvider.apply(channel), "Cannot obtain SSL port for channel: %s", channel); - SslHandler sslHandler = + + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient() .sslProvider(sslProvider) - .trustManager(trustedCertificates) + .trustManager( + trustedCertificates.isEmpty() + ? null + : trustedCertificates.toArray(new X509Certificate[0])); + + if (privateKeySupplier != null && certificateChainSupplier != null) { + sslContextBuilder.keyManager( + privateKeySupplier.get(), certificateChainSupplier.get().toArray(new X509Certificate[0])); + } + + SslHandler sslHandler = + sslContextBuilder .build() .newHandler(channel.alloc(), hostProvider.apply(channel), portProvider.apply(channel)); diff --git a/networking/src/main/java/google/registry/networking/handler/SslServerInitializer.java b/networking/src/main/java/google/registry/networking/handler/SslServerInitializer.java index b4a3f8dca..9bbf357cc 100644 --- a/networking/src/main/java/google/registry/networking/handler/SslServerInitializer.java +++ b/networking/src/main/java/google/registry/networking/handler/SslServerInitializer.java @@ -14,6 +14,7 @@ package google.registry.networking.handler; +import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler.Sharable; @@ -39,7 +40,7 @@ import java.util.function.Supplier; * come before this handler. The type parameter {@code C} is needed so that unit tests can construct * this handler that works with {@link EmbeddedChannel}; * - *

The ssl handler added requires client authentication, but it uses an {@link + *

The ssl handler added can require client authentication, but it uses an {@link * InsecureTrustManagerFactory}, which accepts any ssl certificate presented by the client, as long * as the client uses the corresponding private key to establish SSL handshake. The client * certificate hash will be passed along to GAE as an HTTP header for verification (not handled by @@ -58,14 +59,16 @@ public class SslServerInitializer extends ChannelInitializer< private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private final boolean requireClientCert; private final SslProvider sslProvider; + // We use suppliers for the key/cert pair because they are fetched and cached from GCS, and can + // change when the artifacts on GCS changes. private final Supplier privateKeySupplier; - private final Supplier certificatesSupplier; + private final Supplier> certificatesSupplier; public SslServerInitializer( boolean requireClientCert, SslProvider sslProvider, Supplier privateKeySupplier, - Supplier certificatesSupplier) { + Supplier> certificatesSupplier) { logger.atInfo().log("Server SSL Provider: %s", sslProvider); this.requireClientCert = requireClientCert; this.sslProvider = sslProvider; @@ -76,7 +79,9 @@ public class SslServerInitializer extends ChannelInitializer< @Override protected void initChannel(C channel) throws Exception { SslHandler sslHandler = - SslContextBuilder.forServer(privateKeySupplier.get(), certificatesSupplier.get()) + SslContextBuilder.forServer( + privateKeySupplier.get(), + certificatesSupplier.get().toArray(new X509Certificate[0])) .sslProvider(sslProvider) .trustManager(InsecureTrustManagerFactory.INSTANCE) .clientAuth(requireClientCert ? ClientAuth.REQUIRE : ClientAuth.NONE) diff --git a/networking/src/test/java/google/registry/networking/handler/NettyRule.java b/networking/src/test/java/google/registry/networking/handler/NettyRule.java index 4a5646103..f88f8961f 100644 --- a/networking/src/test/java/google/registry/networking/handler/NettyRule.java +++ b/networking/src/test/java/google/registry/networking/handler/NettyRule.java @@ -48,7 +48,7 @@ import org.junit.rules.ExternalResource; * *

Used in {@link SslClientInitializerTest} and {@link SslServerInitializerTest}. */ -final class NettyRule extends ExternalResource { +public final class NettyRule extends ExternalResource { // All I/O operations are done inside the single thread within this event loop group, which is // different from the main test thread. Therefore synchronizations are required to make sure that @@ -63,8 +63,12 @@ final class NettyRule extends ExternalResource { private Channel channel; + public EventLoopGroup getEventLoopGroup() { + return eventLoopGroup; + } + /** Sets up a server channel bound to the given local address. */ - void setUpServer(LocalAddress localAddress, ChannelHandler handler) { + public void setUpServer(LocalAddress localAddress, ChannelHandler... handlers) { checkState(echoHandler == null, "Can't call setUpServer twice"); echoHandler = new EchoHandler(); ChannelInitializer serverInitializer = @@ -72,7 +76,7 @@ final class NettyRule extends ExternalResource { @Override protected void initChannel(LocalChannel ch) { // Add the given handler - ch.pipeline().addLast(handler); + ch.pipeline().addLast(handlers); // Add the "echoHandler" last to log the incoming message and send it back ch.pipeline().addLast(echoHandler); } @@ -147,6 +151,11 @@ final class NettyRule extends ExternalResource { assertThrows(ExecutionException.class, () -> dumpHandler.getResponseFuture().get()))); } + // TODO(jianglai): find a way to remove this helper method. + public void assertReceivedMessage(String message) throws Exception { + assertThat(echoHandler.getRequestFuture().get()).isEqualTo(message); + } + /** * A handler that echoes back its inbound message. The message is also saved in a promise for * inspection later. diff --git a/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java b/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java index 43779811b..3d15c6119 100644 --- a/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java +++ b/networking/src/test/java/google/registry/networking/handler/SslClientInitializerTest.java @@ -19,18 +19,21 @@ import static google.registry.networking.handler.SslInitializerTestUtils.getKeyP import static google.registry.networking.handler.SslInitializerTestUtils.setUpSslChannel; import static google.registry.networking.handler.SslInitializerTestUtils.signKeyPair; +import com.google.common.collect.ImmutableList; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelPipeline; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; +import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.OpenSsl; import io.netty.handler.ssl.SniHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslProvider; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; import java.security.KeyPair; import java.security.PrivateKey; @@ -39,6 +42,7 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.function.Function; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -86,9 +90,14 @@ public class SslClientInitializerTest { /** Saves the SNI hostname received by the server, if sent by the client. */ private String sniHostReceived; - private ChannelHandler getServerHandler(PrivateKey privateKey, X509Certificate certificate) + private ChannelHandler getServerHandler( + boolean requireClientCert, PrivateKey privateKey, X509Certificate certificate) throws Exception { - SslContext sslContext = SslContextBuilder.forServer(privateKey, certificate).build(); + SslContext sslContext = + SslContextBuilder.forServer(privateKey, certificate) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .clientAuth(requireClientCert ? ClientAuth.REQUIRE : ClientAuth.NONE) + .build(); return new SniHandler( hostname -> { sniHostReceived = hostname; @@ -99,7 +108,8 @@ public class SslClientInitializerTest { @Test public void testSuccess_swappedInitializerWithSslHandler() throws Exception { SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider, hostProvider, portProvider); + new SslClientInitializer<>( + sslProvider, hostProvider, portProvider, ImmutableList.of(), null, null); EmbeddedChannel channel = new EmbeddedChannel(); ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(sslClientInitializer); @@ -114,7 +124,8 @@ public class SslClientInitializerTest { @Test public void testSuccess_nullHost() { SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider, channel -> null, portProvider); + new SslClientInitializer<>( + sslProvider, channel -> null, portProvider, ImmutableList.of(), null, null); EmbeddedChannel channel = new EmbeddedChannel(); ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(sslClientInitializer); @@ -125,7 +136,8 @@ public class SslClientInitializerTest { @Test public void testSuccess_nullPort() { SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider, hostProvider, channel -> null); + new SslClientInitializer<>( + sslProvider, hostProvider, channel -> null, ImmutableList.of(), null, null); EmbeddedChannel channel = new EmbeddedChannel(); ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(sslClientInitializer); @@ -138,9 +150,10 @@ public class SslClientInitializerTest { SelfSignedCertificate ssc = new SelfSignedCertificate(SSL_HOST); LocalAddress localAddress = new LocalAddress("DEFAULT_TRUST_MANAGER_REJECT_SELF_SIGNED_CERT_" + sslProvider); - nettyRule.setUpServer(localAddress, getServerHandler(ssc.key(), ssc.cert())); + nettyRule.setUpServer(localAddress, getServerHandler(false, ssc.key(), ssc.cert())); SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider, hostProvider, portProvider); + new SslClientInitializer<>( + sslProvider, hostProvider, portProvider, ImmutableList.of(), null, null); nettyRule.setUpClient(localAddress, sslClientInitializer); // The connection is now terminated, both the client side and the server side should get // exceptions. @@ -163,12 +176,12 @@ public class SslClientInitializerTest { // Set up the server to use the signed cert and private key to perform handshake; PrivateKey privateKey = keyPair.getPrivate(); - nettyRule.setUpServer(localAddress, getServerHandler(privateKey, cert)); + nettyRule.setUpServer(localAddress, getServerHandler(false, privateKey, cert)); // Set up the client to trust the self signed cert used to sign the cert that server provides. SslClientInitializer sslClientInitializer = new SslClientInitializer<>( - sslProvider, hostProvider, portProvider, new X509Certificate[] {ssc.cert()}); + sslProvider, hostProvider, portProvider, ImmutableList.of(ssc.cert()), null, null); nettyRule.setUpClient(localAddress, sslClientInitializer); setUpSslChannel(nettyRule.getChannel(), cert); @@ -178,6 +191,43 @@ public class SslClientInitializerTest { assertThat(sniHostReceived).isEqualTo(SSL_HOST); } + @Test + public void testSuccess_customTrustManager_acceptSelfSignedCert_clientCertRequired() + throws Exception { + LocalAddress localAddress = + new LocalAddress( + "CUSTOM_TRUST_MANAGER_ACCEPT_SELF_SIGNED_CERT_CLIENT_CERT_REQUIRED_" + sslProvider); + + SelfSignedCertificate serverSsc = new SelfSignedCertificate(SSL_HOST); + SelfSignedCertificate clientSsc = new SelfSignedCertificate(); + + // Set up the server to require client certificate. + nettyRule.setUpServer(localAddress, getServerHandler(true, serverSsc.key(), serverSsc.cert())); + + // Set up the client to trust the server certificate and use the client certificate. + SslClientInitializer sslClientInitializer = + new SslClientInitializer<>( + sslProvider, + hostProvider, + portProvider, + ImmutableList.of(serverSsc.cert()), + () -> clientSsc.key(), + () -> ImmutableList.of(clientSsc.cert())); + nettyRule.setUpClient(localAddress, sslClientInitializer); + + SSLSession sslSession = setUpSslChannel(nettyRule.getChannel(), serverSsc.cert()); + nettyRule.assertThatMessagesWork(); + + // Verify that the SNI extension is sent during handshake. + assertThat(sniHostReceived).isEqualTo(SSL_HOST); + + // Verify that the SSL session gets the client cert. Note that this SslSession is for the client + // channel, therefore its local certificates are the remote certificates of the SslSession for + // the server channel, and vice versa. + assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert()); + assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert()); + } + @Test public void testFailure_customTrustManager_wrongHostnameInCertificate() throws Exception { LocalAddress localAddress = @@ -192,12 +242,12 @@ public class SslClientInitializerTest { // Set up the server to use the signed cert and private key to perform handshake; PrivateKey privateKey = keyPair.getPrivate(); - nettyRule.setUpServer(localAddress, getServerHandler(privateKey, cert)); + nettyRule.setUpServer(localAddress, getServerHandler(false, privateKey, cert)); // Set up the client to trust the self signed cert used to sign the cert that server provides. SslClientInitializer sslClientInitializer = new SslClientInitializer<>( - sslProvider, hostProvider, portProvider, new X509Certificate[] {ssc.cert()}); + sslProvider, hostProvider, portProvider, ImmutableList.of(ssc.cert()), null, null); nettyRule.setUpClient(localAddress, sslClientInitializer); // When the client rejects the server cert due to wrong hostname, both the client and server diff --git a/networking/src/test/java/google/registry/networking/handler/SslInitializerTestUtils.java b/networking/src/test/java/google/registry/networking/handler/SslInitializerTestUtils.java index 317579088..a690572e7 100644 --- a/networking/src/test/java/google/registry/networking/handler/SslInitializerTestUtils.java +++ b/networking/src/test/java/google/registry/networking/handler/SslInitializerTestUtils.java @@ -29,15 +29,23 @@ import java.time.Duration; import java.time.Instant; import java.util.Date; import javax.net.ssl.SSLSession; -import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.x509.X509V3CertificateGenerator; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; +import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; +import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; /** * Utility class that provides methods used by {@link SslClientInitializerTest} and {@link * SslServerInitializerTest}. */ -@SuppressWarnings("deprecation") public final class SslInitializerTestUtils { static { @@ -59,16 +67,26 @@ public final class SslInitializerTestUtils { */ public static X509Certificate signKeyPair( SelfSignedCertificate ssc, KeyPair keyPair, String hostname) throws Exception { - X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); - X500Principal dnName = new X500Principal("CN=" + hostname); - certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); - certGen.setSubjectDN(dnName); - certGen.setIssuerDN(ssc.cert().getSubjectX500Principal()); - certGen.setNotBefore(Date.from(Instant.now().minus(Duration.ofDays(1)))); - certGen.setNotAfter(Date.from(Instant.now().plus(Duration.ofDays(1)))); - certGen.setPublicKey(keyPair.getPublic()); - certGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); - return certGen.generate(ssc.key(), "BC"); + X500Name subjectDnName = new X500Name("CN=" + hostname); + BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); + X500Name issuerDnName = new X500Name(ssc.cert().getIssuerDN().getName()); + Date from = Date.from(Instant.now().minus(Duration.ofDays(1))); + Date to = Date.from(Instant.now().plus(Duration.ofDays(1))); + SubjectPublicKeyInfo subPubKeyInfo = + SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); + AlgorithmIdentifier sigAlgId = + new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSAEncryption"); + AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); + + ContentSigner sigGen = + new BcRSAContentSignerBuilder(sigAlgId, digAlgId) + .build(PrivateKeyFactory.createKey(ssc.key().getEncoded())); + X509v3CertificateBuilder v3CertGen = + new X509v3CertificateBuilder( + issuerDnName, serialNumber, from, to, subjectDnName, subPubKeyInfo); + + X509CertificateHolder certificateHolder = v3CertGen.build(sigGen); + return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder); } /** diff --git a/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java b/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java index 5a6c08df1..928dd2089 100644 --- a/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java +++ b/networking/src/test/java/google/registry/networking/handler/SslServerInitializerTest.java @@ -20,6 +20,7 @@ import static google.registry.networking.handler.SslInitializerTestUtils.setUpSs import static google.registry.networking.handler.SslInitializerTestUtils.signKeyPair; import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; @@ -86,7 +87,7 @@ public class SslServerInitializerTest { requireClientCert, sslProvider, Suppliers.ofInstance(privateKey), - Suppliers.ofInstance(certificates)); + Suppliers.ofInstance(ImmutableList.copyOf(certificates))); } private ChannelHandler getServerHandler(PrivateKey privateKey, X509Certificate... certificates) { @@ -125,7 +126,7 @@ public class SslServerInitializerTest { true, sslProvider, Suppliers.ofInstance(ssc.key()), - Suppliers.ofInstance(new X509Certificate[] {ssc.cert()})); + Suppliers.ofInstance(ImmutableList.of(ssc.cert()))); EmbeddedChannel channel = new EmbeddedChannel(); ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(sslServerInitializer); diff --git a/prober/build.gradle b/prober/build.gradle index 5720a6e6c..cefd3b376 100644 --- a/prober/build.gradle +++ b/prober/build.gradle @@ -40,6 +40,7 @@ dependencies { compile deps['xpp3:xpp3'] compile project(':common') compile project(':util') + compile project(':networking') runtime deps['com.google.flogger:flogger-system-backend'] runtime deps['com.google.auto.value:auto-value'] @@ -51,6 +52,7 @@ dependencies { testCompile deps['org.mockito:mockito-core'] testCompile project(':third_party') testCompile project(path: ':common', configuration: 'testing') + testCompile project(path: ':networking', configuration: 'testRuntime') // Include auto-value in compile until nebula-lint understands // annotationProcessor diff --git a/prober/gradle/dependency-locks/compile.lockfile b/prober/gradle/dependency-locks/compile.lockfile index 302965bb4..02a5f81ec 100644 --- a/prober/gradle/dependency-locks/compile.lockfile +++ b/prober/gradle/dependency-locks/compile.lockfile @@ -13,6 +13,7 @@ com.google.code.findbugs:jsr305:3.0.2 com.google.code.gson:gson:2.8.5 com.google.dagger:dagger:2.21 com.google.errorprone:error_prone_annotations:2.3.2 +com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger:0.1 com.google.guava:failureaccess:1.0.1 com.google.guava:guava:28.1-jre @@ -33,6 +34,7 @@ io.netty:netty-codec:4.1.31.Final io.netty:netty-common:4.1.31.Final io.netty:netty-handler:4.1.31.Final io.netty:netty-resolver:4.1.31.Final +io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-transport:4.1.31.Final io.opencensus:opencensus-api:0.21.0 io.opencensus:opencensus-contrib-http-util:0.21.0 diff --git a/prober/gradle/dependency-locks/compileClasspath.lockfile b/prober/gradle/dependency-locks/compileClasspath.lockfile index 302965bb4..02a5f81ec 100644 --- a/prober/gradle/dependency-locks/compileClasspath.lockfile +++ b/prober/gradle/dependency-locks/compileClasspath.lockfile @@ -13,6 +13,7 @@ com.google.code.findbugs:jsr305:3.0.2 com.google.code.gson:gson:2.8.5 com.google.dagger:dagger:2.21 com.google.errorprone:error_prone_annotations:2.3.2 +com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger:0.1 com.google.guava:failureaccess:1.0.1 com.google.guava:guava:28.1-jre @@ -33,6 +34,7 @@ io.netty:netty-codec:4.1.31.Final io.netty:netty-common:4.1.31.Final io.netty:netty-handler:4.1.31.Final io.netty:netty-resolver:4.1.31.Final +io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-transport:4.1.31.Final io.opencensus:opencensus-api:0.21.0 io.opencensus:opencensus-contrib-http-util:0.21.0 diff --git a/prober/gradle/dependency-locks/testCompile.lockfile b/prober/gradle/dependency-locks/testCompile.lockfile index 9ca963a0c..43dc48071 100644 --- a/prober/gradle/dependency-locks/testCompile.lockfile +++ b/prober/gradle/dependency-locks/testCompile.lockfile @@ -13,6 +13,7 @@ com.google.code.findbugs:jsr305:3.0.2 com.google.code.gson:gson:2.8.5 com.google.dagger:dagger:2.21 com.google.errorprone:error_prone_annotations:2.3.2 +com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger:0.1 com.google.guava:failureaccess:1.0.1 com.google.guava:guava:28.1-jre @@ -36,6 +37,7 @@ io.netty:netty-codec:4.1.31.Final io.netty:netty-common:4.1.31.Final io.netty:netty-handler:4.1.31.Final io.netty:netty-resolver:4.1.31.Final +io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-transport:4.1.31.Final io.opencensus:opencensus-api:0.21.0 io.opencensus:opencensus-contrib-http-util:0.21.0 diff --git a/prober/gradle/dependency-locks/testCompileClasspath.lockfile b/prober/gradle/dependency-locks/testCompileClasspath.lockfile index 9ca963a0c..43dc48071 100644 --- a/prober/gradle/dependency-locks/testCompileClasspath.lockfile +++ b/prober/gradle/dependency-locks/testCompileClasspath.lockfile @@ -13,6 +13,7 @@ com.google.code.findbugs:jsr305:3.0.2 com.google.code.gson:gson:2.8.5 com.google.dagger:dagger:2.21 com.google.errorprone:error_prone_annotations:2.3.2 +com.google.flogger:flogger-system-backend:0.1 com.google.flogger:flogger:0.1 com.google.guava:failureaccess:1.0.1 com.google.guava:guava:28.1-jre @@ -36,6 +37,7 @@ io.netty:netty-codec:4.1.31.Final io.netty:netty-common:4.1.31.Final io.netty:netty-handler:4.1.31.Final io.netty:netty-resolver:4.1.31.Final +io.netty:netty-tcnative-boringssl-static:2.0.22.Final io.netty:netty-transport:4.1.31.Final io.opencensus:opencensus-api:0.21.0 io.opencensus:opencensus-contrib-http-util:0.21.0 diff --git a/prober/src/main/java/google/registry/monitoring/blackbox/ProberModule.java b/prober/src/main/java/google/registry/monitoring/blackbox/ProberModule.java index 13d43f468..e284030b0 100644 --- a/prober/src/main/java/google/registry/monitoring/blackbox/ProberModule.java +++ b/prober/src/main/java/google/registry/monitoring/blackbox/ProberModule.java @@ -21,6 +21,7 @@ import google.registry.monitoring.blackbox.connection.ProbingAction; import google.registry.monitoring.blackbox.module.CertificateModule; import google.registry.monitoring.blackbox.module.EppModule; import google.registry.monitoring.blackbox.module.WebWhoisModule; +import google.registry.networking.handler.SslClientInitializer; import google.registry.util.Clock; import google.registry.util.SystemClock; import io.netty.bootstrap.Bootstrap; @@ -45,10 +46,7 @@ public class ProberModule { /** Default {@link Duration} chosen to be time between each {@link ProbingAction} call. */ private static final Duration DEFAULT_PROBER_INTERVAL = Duration.standardSeconds(4); - /** - * {@link Provides} the {@link SslProvider} used by instances of {@link - * google.registry.monitoring.blackbox.handler.SslClientInitializer} - */ + /** {@link Provides} the {@link SslProvider} used by instances of {@link SslClientInitializer} */ @Provides @Singleton static SslProvider provideSslProvider() { diff --git a/prober/src/main/java/google/registry/monitoring/blackbox/handler/SslClientInitializer.java b/prober/src/main/java/google/registry/monitoring/blackbox/handler/SslClientInitializer.java deleted file mode 100644 index a9f2bf03b..000000000 --- a/prober/src/main/java/google/registry/monitoring/blackbox/handler/SslClientInitializer.java +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.monitoring.blackbox.handler; - -import static com.google.common.base.Preconditions.checkNotNull; -import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY; -import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.flogger.FluentLogger; -import google.registry.monitoring.blackbox.connection.Protocol; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.SslProvider; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.function.Supplier; -import javax.inject.Singleton; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; - -/** - * Adds a client side SSL handler to the channel pipeline. - * - *

Code is close to unchanged from {@link SslClientInitializer} in proxy, but is modified for - * revised overall structure of connections, and to accomdate EPP connections - * - *

This must be the first handler provided for any handler provider list, if it is - * provided. The type parameter {@code C} is needed so that unit tests can construct this handler - * that works with {@link EmbeddedChannel}; - */ -@Singleton -@Sharable -public class SslClientInitializer extends ChannelInitializer { - - private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - - private final SslProvider sslProvider; - private final X509Certificate[] trustedCertificates; - private final Supplier privateKeySupplier; - private final Supplier certificateSupplier; - - public SslClientInitializer(SslProvider sslProvider) { - // null uses the system default trust store. - // Used for WebWhois, so we don't care about privateKey and certificates, setting them to null - this(sslProvider, null, null, null); - } - - public SslClientInitializer( - SslProvider sslProvider, - Supplier privateKeySupplier, - Supplier certificateSupplier) { - // We use the default trust store here as well, setting trustCertificates to null - this(sslProvider, null, privateKeySupplier, certificateSupplier); - } - - @VisibleForTesting - SslClientInitializer(SslProvider sslProvider, X509Certificate[] trustCertificates) { - this(sslProvider, trustCertificates, null, null); - } - - private SslClientInitializer( - SslProvider sslProvider, - X509Certificate[] trustCertificates, - Supplier privateKeySupplier, - Supplier certificateSupplier) { - logger.atInfo().log("Client SSL Provider: %s", sslProvider); - - this.sslProvider = sslProvider; - this.trustedCertificates = trustCertificates; - this.privateKeySupplier = privateKeySupplier; - this.certificateSupplier = certificateSupplier; - } - - @Override - protected void initChannel(C channel) throws Exception { - Protocol protocol = channel.attr(PROTOCOL_KEY).get(); - String host = channel.attr(REMOTE_ADDRESS_KEY).get(); - - // Builds SslHandler from Protocol, and based on if we require a privateKey and certificate - checkNotNull(protocol, "Protocol is not set for channel: %s", channel); - SslContextBuilder sslContextBuilder = - SslContextBuilder.forClient().sslProvider(sslProvider).trustManager(trustedCertificates); - if (privateKeySupplier != null && certificateSupplier != null) { - sslContextBuilder = - sslContextBuilder.keyManager(privateKeySupplier.get(), certificateSupplier.get()); - } - - SslHandler sslHandler = - sslContextBuilder.build().newHandler(channel.alloc(), host, protocol.port()); - - // Enable hostname verification. - SSLEngine sslEngine = sslHandler.engine(); - SSLParameters sslParameters = sslEngine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - sslEngine.setSSLParameters(sslParameters); - - channel.pipeline().addLast(sslHandler); - } -} diff --git a/prober/src/main/java/google/registry/monitoring/blackbox/module/CertificateModule.java b/prober/src/main/java/google/registry/monitoring/blackbox/module/CertificateModule.java index 6af9a5cbf..05b6597b5 100644 --- a/prober/src/main/java/google/registry/monitoring/blackbox/module/CertificateModule.java +++ b/prober/src/main/java/google/registry/monitoring/blackbox/module/CertificateModule.java @@ -18,6 +18,7 @@ import static com.google.common.base.Suppliers.memoizeWithExpiration; import static google.registry.util.ResourceUtils.readResourceBytes; import static google.registry.util.ResourceUtils.readResourceUtf8; +import com.google.common.collect.ImmutableList; import dagger.Module; import dagger.Provides; import java.io.IOException; @@ -90,7 +91,8 @@ public class CertificateModule { @Provides @LocalSecrets - static X509Certificate[] provideCertificates(@LocalSecrets Provider passwordProvider) { + static ImmutableList provideCertificates( + @LocalSecrets Provider passwordProvider) { try { InputStream inStream = readResource("secrets/prober-client-tls-sandbox.p12"); @@ -98,7 +100,7 @@ public class CertificateModule { ks.load(inStream, passwordProvider.get().toCharArray()); String alias = ks.aliases().nextElement(); - return new X509Certificate[] {(X509Certificate) ks.getCertificate(alias)}; + return ImmutableList.of((X509Certificate) ks.getCertificate(alias)); } catch (Exception e) { throw new RuntimeException(e); } @@ -116,8 +118,8 @@ public class CertificateModule { @Singleton @Provides @LocalSecrets - static Supplier provideCertificatesSupplier( - @LocalSecrets Provider certificatesProvider, + static Supplier> provideCertificatesSupplier( + @LocalSecrets Provider> certificatesProvider, @LocalSecrets Duration duration) { return memoizeWithExpiration( certificatesProvider::get, duration.getStandardSeconds(), TimeUnit.SECONDS); diff --git a/prober/src/main/java/google/registry/monitoring/blackbox/module/EppModule.java b/prober/src/main/java/google/registry/monitoring/blackbox/module/EppModule.java index 966634bd0..db2ca6dde 100644 --- a/prober/src/main/java/google/registry/monitoring/blackbox/module/EppModule.java +++ b/prober/src/main/java/google/registry/monitoring/blackbox/module/EppModule.java @@ -14,6 +14,8 @@ package google.registry.monitoring.blackbox.module; +import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY; +import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY; import static google.registry.monitoring.blackbox.message.EppRequestMessage.CLIENT_ID_KEY; import static google.registry.monitoring.blackbox.message.EppRequestMessage.CLIENT_PASSWORD_KEY; import static google.registry.monitoring.blackbox.message.EppRequestMessage.CLIENT_TRID_KEY; @@ -30,13 +32,13 @@ import google.registry.monitoring.blackbox.ProbingStep; import google.registry.monitoring.blackbox.connection.Protocol; import google.registry.monitoring.blackbox.handler.EppActionHandler; import google.registry.monitoring.blackbox.handler.EppMessageHandler; -import google.registry.monitoring.blackbox.handler.SslClientInitializer; import google.registry.monitoring.blackbox.message.EppMessage; import google.registry.monitoring.blackbox.message.EppRequestMessage; import google.registry.monitoring.blackbox.message.EppResponseMessage; import google.registry.monitoring.blackbox.metric.MetricsCollector; import google.registry.monitoring.blackbox.module.CertificateModule.LocalSecrets; import google.registry.monitoring.blackbox.token.EppToken; +import google.registry.networking.handler.SslClientInitializer; import google.registry.util.Clock; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelHandler; @@ -556,9 +558,15 @@ public class EppModule { static SslClientInitializer provideSslClientInitializer( SslProvider sslProvider, @LocalSecrets Supplier privateKeySupplier, - @LocalSecrets Supplier certificatesSupplier) { + @LocalSecrets Supplier> certificatesSupplier) { - return new SslClientInitializer<>(sslProvider, privateKeySupplier, certificatesSupplier); + return SslClientInitializer + .createSslClientInitializerWithSystemTrustStoreAndClientAuthentication( + sslProvider, + channel -> channel.attr(REMOTE_ADDRESS_KEY).get(), + channel -> channel.attr(PROTOCOL_KEY).get().port(), + privateKeySupplier, + certificatesSupplier); } @Provides diff --git a/prober/src/main/java/google/registry/monitoring/blackbox/module/WebWhoisModule.java b/prober/src/main/java/google/registry/monitoring/blackbox/module/WebWhoisModule.java index b3c2f6c7b..11f963abf 100644 --- a/prober/src/main/java/google/registry/monitoring/blackbox/module/WebWhoisModule.java +++ b/prober/src/main/java/google/registry/monitoring/blackbox/module/WebWhoisModule.java @@ -14,6 +14,10 @@ package google.registry.monitoring.blackbox.module; +import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY; +import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY; +import static google.registry.networking.handler.SslClientInitializer.createSslClientInitializerWithSystemTrustStore; + import com.google.common.collect.ImmutableList; import dagger.Module; import dagger.Provides; @@ -21,12 +25,12 @@ import dagger.multibindings.IntoSet; import google.registry.monitoring.blackbox.ProbingSequence; import google.registry.monitoring.blackbox.ProbingStep; import google.registry.monitoring.blackbox.connection.Protocol; -import google.registry.monitoring.blackbox.handler.SslClientInitializer; import google.registry.monitoring.blackbox.handler.WebWhoisActionHandler; import google.registry.monitoring.blackbox.handler.WebWhoisMessageHandler; import google.registry.monitoring.blackbox.message.HttpRequestMessage; import google.registry.monitoring.blackbox.metric.MetricsCollector; import google.registry.monitoring.blackbox.token.WebWhoisToken; +import google.registry.networking.handler.SslClientInitializer; import google.registry.util.Clock; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; @@ -156,7 +160,10 @@ public class WebWhoisModule { @HttpsWhoisProtocol static SslClientInitializer provideSslClientInitializer( SslProvider sslProvider) { - return new SslClientInitializer<>(sslProvider); + return createSslClientInitializerWithSystemTrustStore( + sslProvider, + channel -> channel.attr(REMOTE_ADDRESS_KEY).get(), + channel -> channel.attr(PROTOCOL_KEY).get().port()); } /** {@link Provides} the {@link Bootstrap} used by the WebWhois sequence. */ diff --git a/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java b/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java index 0b1173fcf..a14514fc5 100644 --- a/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java +++ b/prober/src/test/java/google/registry/monitoring/blackbox/ProbingStepTest.java @@ -26,11 +26,11 @@ import google.registry.monitoring.blackbox.connection.Protocol; import google.registry.monitoring.blackbox.exception.UndeterminedStateException; import google.registry.monitoring.blackbox.handler.ActionHandler; import google.registry.monitoring.blackbox.handler.ConversionHandler; -import google.registry.monitoring.blackbox.handler.NettyRule; import google.registry.monitoring.blackbox.handler.TestActionHandler; import google.registry.monitoring.blackbox.message.OutboundMessageType; import google.registry.monitoring.blackbox.message.TestMessage; import google.registry.monitoring.blackbox.token.Token; +import google.registry.networking.handler.NettyRule; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; @@ -62,7 +62,7 @@ public class ProbingStepTest { private final Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup).channel(LocalChannel.class); /** Used for testing how well probing step can create connection to blackbox server */ - @Rule public NettyRule nettyRule = new NettyRule(eventLoopGroup); + @Rule public NettyRule nettyRule = new NettyRule(); /** * The two main handlers we need in any test pipeline used that connects to {@link NettyRule's diff --git a/prober/src/test/java/google/registry/monitoring/blackbox/connection/ProbingActionTest.java b/prober/src/test/java/google/registry/monitoring/blackbox/connection/ProbingActionTest.java index ec6c07c32..68d1254d1 100644 --- a/prober/src/test/java/google/registry/monitoring/blackbox/connection/ProbingActionTest.java +++ b/prober/src/test/java/google/registry/monitoring/blackbox/connection/ProbingActionTest.java @@ -22,19 +22,17 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableList; import google.registry.monitoring.blackbox.handler.ActionHandler; import google.registry.monitoring.blackbox.handler.ConversionHandler; -import google.registry.monitoring.blackbox.handler.NettyRule; import google.registry.monitoring.blackbox.handler.TestActionHandler; import google.registry.monitoring.blackbox.message.TestMessage; +import google.registry.networking.handler.NettyRule; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; -import io.netty.channel.EventLoopGroup; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; -import io.netty.channel.nio.NioEventLoopGroup; import org.joda.time.Duration; import org.junit.Ignore; import org.junit.Rule; @@ -57,10 +55,7 @@ public class ProbingActionTest { private static final String ADDRESS_NAME = "TEST_ADDRESS"; private static final int TEST_PORT = 0; - private static final EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1); - - /** Used for testing how well probing step can create connection to blackbox server */ - @Rule public NettyRule nettyRule = new NettyRule(eventLoopGroup); + @Rule public NettyRule nettyRule = new NettyRule(); /** * We use custom Test {@link ActionHandler} and {@link ConversionHandler} so test depends only on @@ -127,7 +122,8 @@ public class ProbingActionTest { // setup LocalAddress address = new LocalAddress(ADDRESS_NAME); - Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup).channel(LocalChannel.class); + Bootstrap bootstrap = + new Bootstrap().group(nettyRule.getEventLoopGroup()).channel(LocalChannel.class); // Sets up a Protocol corresponding to when a new connection is created. Protocol protocol = diff --git a/prober/src/test/java/google/registry/monitoring/blackbox/handler/NettyRule.java b/prober/src/test/java/google/registry/monitoring/blackbox/handler/NettyRule.java deleted file mode 100644 index ae520dffa..000000000 --- a/prober/src/test/java/google/registry/monitoring/blackbox/handler/NettyRule.java +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2019 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.monitoring.blackbox.handler; - -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.truth.Truth.assertThat; -import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY; -import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY; -import static google.registry.testing.JUnitBackports.assertThrows; -import static java.nio.charset.StandardCharsets.US_ASCII; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; -import com.google.common.truth.ThrowableSubject; -import google.registry.monitoring.blackbox.ProbingStepTest; -import google.registry.monitoring.blackbox.connection.ProbingActionTest; -import google.registry.monitoring.blackbox.connection.Protocol; -import google.registry.monitoring.blackbox.testserver.TestServer; -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.ReferenceCountUtil; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import org.junit.rules.ExternalResource; - -/** - * Helper for setting up and testing client / server connection with netty. - * - *

Code based on and almost identical to {@code NettyRule} in the proxy. Used in {@link - * SslClientInitializerTest}, {@link ProbingActionTest}, and {@link ProbingStepTest} - */ -public final class NettyRule extends ExternalResource { - - private final EventLoopGroup eventLoopGroup; - - // Handler attached to server's channel to record the request received. - private EchoHandler echoHandler; - // Handler attached to client's channel to record the response received. - private DumpHandler dumpHandler; - - private Channel channel; - - // All I/O operations are done inside the single thread within this event loop group, which is - // different from the main test thread. Therefore synchronizations are required to make sure that - // certain I/O activities are finished when assertions are performed. - public NettyRule() { - eventLoopGroup = new NioEventLoopGroup(1); - } - - public NettyRule(EventLoopGroup e) { - eventLoopGroup = e; - } - - private static void writeToChannelAndFlush(Channel channel, String data) { - ChannelFuture unusedFuture = - channel.writeAndFlush(Unpooled.wrappedBuffer(data.getBytes(US_ASCII))); - } - - /** Sets up a server channel bound to the given local address. */ - public void setUpServer(LocalAddress localAddress, ChannelHandler... handlers) { - checkState(echoHandler == null, "Can't call setUpServer twice"); - echoHandler = new EchoHandler(); - - new TestServer( - eventLoopGroup, - localAddress, - ImmutableList.builder().add(handlers).add(echoHandler).build()); - } - - /** Sets up a client channel connecting to the give local address. */ - void setUpClient( - LocalAddress localAddress, Protocol protocol, String host, ChannelHandler handler) { - checkState(echoHandler != null, "Must call setUpServer before setUpClient"); - checkState(dumpHandler == null, "Can't call setUpClient twice"); - dumpHandler = new DumpHandler(); - ChannelInitializer clientInitializer = - new ChannelInitializer() { - @Override - protected void initChannel(LocalChannel ch) throws Exception { - // Add the given handler - ch.pipeline().addLast(handler); - // Add the "dumpHandler" last to log the incoming message - ch.pipeline().addLast(dumpHandler); - } - }; - Bootstrap b = - new Bootstrap() - .group(eventLoopGroup) - .channel(LocalChannel.class) - .handler(clientInitializer) - .attr(PROTOCOL_KEY, protocol) - .attr(REMOTE_ADDRESS_KEY, host); - - channel = b.connect(localAddress).syncUninterruptibly().channel(); - } - - private void checkReady() { - checkState(channel != null, "Must call setUpClient to finish NettyRule setup"); - } - - /** Test that custom setup to send message to current server sends right message */ - public void assertReceivedMessage(String message) throws Exception { - assertThat(echoHandler.getRequestFuture().get()).isEqualTo(message); - } - - /** - * Test that a message can go through, both inbound and outbound. - * - *

The client writes the message to the server, which echos it back and saves the string in its - * promise. The client receives the echo and saves it in its promise. All these activities happens - * in the I/O thread, and this call itself returns immediately. - */ - void assertThatMessagesWork() throws Exception { - checkReady(); - assertThat(channel.isActive()).isTrue(); - - writeToChannelAndFlush(channel, "Hello, world!"); - assertThat(echoHandler.getRequestFuture().get()).isEqualTo("Hello, world!"); - assertThat(dumpHandler.getResponseFuture().get()).isEqualTo("Hello, world!"); - } - - Channel getChannel() { - checkReady(); - return channel; - } - - ThrowableSubject assertThatServerRootCause() { - checkReady(); - return assertThat( - Throwables.getRootCause( - assertThrows(ExecutionException.class, () -> echoHandler.getRequestFuture().get()))); - } - - ThrowableSubject assertThatClientRootCause() { - checkReady(); - return assertThat( - Throwables.getRootCause( - assertThrows(ExecutionException.class, () -> dumpHandler.getResponseFuture().get()))); - } - - @Override - protected void after() { - Future unusedFuture = eventLoopGroup.shutdownGracefully(); - } - - /** - * A handler that echoes back its inbound message. The message is also saved in a promise for - * inspection later. - */ - public static class EchoHandler extends ChannelInboundHandlerAdapter { - - private final CompletableFuture requestFuture = new CompletableFuture<>(); - - public Future getRequestFuture() { - return requestFuture; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - // In the test we only send messages of type ByteBuf. - - assertThat(msg).isInstanceOf(ByteBuf.class); - String request = ((ByteBuf) msg).toString(UTF_8); - // After the message is written back to the client, fulfill the promise. - ChannelFuture unusedFuture = - ctx.writeAndFlush(msg).addListener(f -> requestFuture.complete(request)); - } - - /** Saves any inbound error as the cause of the promise failure. */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ChannelFuture unusedFuture = - ctx.channel().closeFuture().addListener(f -> requestFuture.completeExceptionally(cause)); - } - } - - /** A handler that dumps its inbound message to a promise that can be inspected later. */ - private static class DumpHandler extends ChannelInboundHandlerAdapter { - - private final CompletableFuture responseFuture = new CompletableFuture<>(); - - Future getResponseFuture() { - return responseFuture; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - // In the test we only send messages of type ByteBuf. - assertThat(msg).isInstanceOf(ByteBuf.class); - String response = ((ByteBuf) msg).toString(UTF_8); - // There is no more use of this message, we should release its reference count so that it - // can be more effectively garbage collected by Netty. - ReferenceCountUtil.release(msg); - // Save the string in the promise and make it as complete. - responseFuture.complete(response); - } - - /** Saves any inbound error into the failure cause of the promise. */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ctx.channel().closeFuture().addListener(f -> responseFuture.completeExceptionally(cause)); - } - } -} diff --git a/prober/src/test/java/google/registry/monitoring/blackbox/handler/SslClientInitializerTest.java b/prober/src/test/java/google/registry/monitoring/blackbox/handler/SslClientInitializerTest.java deleted file mode 100644 index 16751680c..000000000 --- a/prober/src/test/java/google/registry/monitoring/blackbox/handler/SslClientInitializerTest.java +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2019 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.monitoring.blackbox.handler; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY; -import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY; -import static google.registry.monitoring.blackbox.handler.SslInitializerTestUtils.getKeyPair; -import static google.registry.monitoring.blackbox.handler.SslInitializerTestUtils.setUpSslChannel; -import static google.registry.monitoring.blackbox.handler.SslInitializerTestUtils.signKeyPair; - -import com.google.common.collect.ImmutableList; -import google.registry.monitoring.blackbox.connection.Protocol; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.SniHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.cert.CertPathBuilderException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import javax.net.ssl.SSLException; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; - -/** - * Unit tests for {@link SslClientInitializer}. - * - *

To validate that the handler accepts & rejects connections as expected, a test server and a - * test client are spun up, and both connect to the {@link LocalAddress} within the JVM. This avoids - * the overhead of routing traffic through the network layer, even if it were to go through - * loopback. It also alleviates the need to pick a free port to use. - * - *

The local addresses used in each test method must to be different, otherwise tests run in - * parallel may interfere with each other. - */ -@RunWith(Parameterized.class) -public class SslClientInitializerTest { - - /** Fake host to test if the SSL engine gets the correct peer host. */ - private static final String SSL_HOST = "www.example.tld"; - - /** Fake port to test if the SSL engine gets the correct peer port. */ - private static final int SSL_PORT = 12345; - /** Fake protocol saved in channel attribute. */ - private static final Protocol PROTOCOL = - Protocol.builder() - .setName("ssl") - .setPort(SSL_PORT) - .setHandlerProviders(ImmutableList.of()) - .setPersistentConnection(false) - .build(); - - @Rule public NettyRule nettyRule = new NettyRule(); - - @Parameter(0) - public SslProvider sslProvider; - /** Saves the SNI hostname received by the server, if sent by the client. */ - private String sniHostReceived; - - // We do our best effort to test all available SSL providers. - @Parameters(name = "{0}") - public static SslProvider[] data() { - return OpenSsl.isAvailable() - ? new SslProvider[] {SslProvider.JDK, SslProvider.OPENSSL} - : new SslProvider[] {SslProvider.JDK}; - } - - private ChannelHandler getServerHandler(PrivateKey privateKey, X509Certificate certificate) - throws Exception { - SslContext sslContext = SslContextBuilder.forServer(privateKey, certificate).build(); - return new SniHandler( - hostname -> { - sniHostReceived = hostname; - return sslContext; - }); - } - - @Test - public void testSuccess_swappedInitializerWithSslHandler() throws Exception { - SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider); - EmbeddedChannel channel = new EmbeddedChannel(); - channel.attr(PROTOCOL_KEY).set(PROTOCOL); - channel.attr(REMOTE_ADDRESS_KEY).set(SSL_HOST); - ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast(sslClientInitializer); - ChannelHandler firstHandler = pipeline.first(); - assertThat(firstHandler.getClass()).isEqualTo(SslHandler.class); - SslHandler sslHandler = (SslHandler) firstHandler; - assertThat(sslHandler.engine().getPeerHost()).isEqualTo(SSL_HOST); - assertThat(sslHandler.engine().getPeerPort()).isEqualTo(SSL_PORT); - assertThat(channel.isActive()).isTrue(); - } - - @Test - public void testSuccess_protocolAttributeNotSet() { - SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider); - EmbeddedChannel channel = new EmbeddedChannel(); - ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast(sslClientInitializer); - // Channel initializer swallows error thrown, and closes the connection. - assertThat(channel.isActive()).isFalse(); - } - - @Test - public void testFailure_defaultTrustManager_rejectSelfSignedCert() throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(SSL_HOST); - LocalAddress localAddress = - new LocalAddress("DEFAULT_TRUST_MANAGER_REJECT_SELF_SIGNED_CERT_" + sslProvider); - nettyRule.setUpServer(localAddress, getServerHandler(ssc.key(), ssc.cert())); - SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider); - - nettyRule.setUpClient(localAddress, PROTOCOL, SSL_HOST, sslClientInitializer); - // The connection is now terminated, both the client side and the server side should get - // exceptions. - nettyRule.assertThatClientRootCause().isInstanceOf(CertPathBuilderException.class); - nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class); - assertThat(nettyRule.getChannel().isActive()).isFalse(); - } - - @Test - public void testSuccess_customTrustManager_acceptCertSignedByTrustedCa() throws Exception { - LocalAddress localAddress = - new LocalAddress("CUSTOM_TRUST_MANAGER_ACCEPT_CERT_SIGNED_BY_TRUSTED_CA_" + sslProvider); - - // Generate a new key pair. - KeyPair keyPair = getKeyPair(); - - // Generate a self signed certificate, and use it to sign the key pair. - SelfSignedCertificate ssc = new SelfSignedCertificate(); - X509Certificate cert = signKeyPair(ssc, keyPair, SSL_HOST); - - // Set up the server to use the signed cert and private key to perform handshake; - PrivateKey privateKey = keyPair.getPrivate(); - nettyRule.setUpServer(localAddress, getServerHandler(privateKey, cert)); - - // Set up the client to trust the self signed cert used to sign the cert that server provides. - SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider, new X509Certificate[] {ssc.cert()}); - - nettyRule.setUpClient(localAddress, PROTOCOL, SSL_HOST, sslClientInitializer); - - setUpSslChannel(nettyRule.getChannel(), cert); - nettyRule.assertThatMessagesWork(); - - // Verify that the SNI extension is sent during handshake. - assertThat(sniHostReceived).isEqualTo(SSL_HOST); - } - - @Test - public void testFailure_customTrustManager_wrongHostnameInCertificate() throws Exception { - LocalAddress localAddress = - new LocalAddress("CUSTOM_TRUST_MANAGER_WRONG_HOSTNAME_" + sslProvider); - - // Generate a new key pair. - KeyPair keyPair = getKeyPair(); - - // Generate a self signed certificate, and use it to sign the key pair. - SelfSignedCertificate ssc = new SelfSignedCertificate(); - X509Certificate cert = signKeyPair(ssc, keyPair, "wrong.com"); - - // Set up the server to use the signed cert and private key to perform handshake; - PrivateKey privateKey = keyPair.getPrivate(); - nettyRule.setUpServer(localAddress, getServerHandler(privateKey, cert)); - - // Set up the client to trust the self signed cert used to sign the cert that server provides. - SslClientInitializer sslClientInitializer = - new SslClientInitializer<>(sslProvider, new X509Certificate[] {ssc.cert()}); - - nettyRule.setUpClient(localAddress, PROTOCOL, SSL_HOST, sslClientInitializer); - - // When the client rejects the server cert due to wrong hostname, both the client and server - // should throw exceptions. - nettyRule.assertThatClientRootCause().isInstanceOf(CertificateException.class); - nettyRule.assertThatClientRootCause().hasMessageThat().contains(SSL_HOST); - nettyRule.assertThatServerRootCause().isInstanceOf(SSLException.class); - assertThat(nettyRule.getChannel().isActive()).isFalse(); - } -} diff --git a/prober/src/test/java/google/registry/monitoring/blackbox/handler/SslInitializerTestUtils.java b/prober/src/test/java/google/registry/monitoring/blackbox/handler/SslInitializerTestUtils.java deleted file mode 100644 index cb3bcb157..000000000 --- a/prober/src/test/java/google/registry/monitoring/blackbox/handler/SslInitializerTestUtils.java +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.monitoring.blackbox.handler; - -import static com.google.common.truth.Truth.assertThat; - -import io.netty.channel.Channel; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.SecureRandom; -import java.security.Security; -import java.security.cert.X509Certificate; -import java.time.Duration; -import java.time.Instant; -import java.util.Date; -import javax.net.ssl.SSLSession; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.crypto.util.PrivateKeyFactory; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; -import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; -import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; - -/** Utility class that provides methods used by {@link SslClientInitializerTest} */ -public class SslInitializerTestUtils { - - static { - Security.addProvider(new BouncyCastleProvider()); - } - - public static KeyPair getKeyPair() throws Exception { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC"); - keyPairGenerator.initialize(2048, new SecureRandom()); - return keyPairGenerator.generateKeyPair(); - } - - /** - * Signs the given key pair with the given self signed certificate. - * - * @return signed public key (of the key pair) certificate - */ - public static X509Certificate signKeyPair( - SelfSignedCertificate ssc, KeyPair keyPair, String hostname) throws Exception { - X500Name subjectDnName = new X500Name("CN=" + hostname); - BigInteger serialNumber = (BigInteger.valueOf(System.currentTimeMillis())); - X500Name issuerDnName = new X500Name(ssc.cert().getIssuerDN().getName()); - Date from = Date.from(Instant.now().minus(Duration.ofDays(1))); - Date to = Date.from(Instant.now().plus(Duration.ofDays(1))); - SubjectPublicKeyInfo subPubKeyInfo = - SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); - AlgorithmIdentifier sigAlgId = - new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSAEncryption"); - AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); - - ContentSigner sigGen = - new BcRSAContentSignerBuilder(sigAlgId, digAlgId) - .build(PrivateKeyFactory.createKey(ssc.key().getEncoded())); - X509v3CertificateBuilder v3CertGen = - new X509v3CertificateBuilder( - issuerDnName, serialNumber, from, to, subjectDnName, subPubKeyInfo); - - X509CertificateHolder certificateHolder = v3CertGen.build(sigGen); - return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder); - } - - /** - * Verifies tha the SSL channel is established as expected, and also sends a message to the server - * and verifies if it is echoed back correctly. - * - * @param certs The certificate that the server should provide. - * @return The SSL session in current channel, can be used for further validation. - */ - static SSLSession setUpSslChannel(Channel channel, X509Certificate... certs) throws Exception { - SslHandler sslHandler = channel.pipeline().get(SslHandler.class); - // Wait till the handshake is complete. - sslHandler.handshakeFuture().get(); - - assertThat(channel.isActive()).isTrue(); - assertThat(sslHandler.handshakeFuture().isSuccess()).isTrue(); - assertThat(sslHandler.engine().getSession().isValid()).isTrue(); - assertThat(sslHandler.engine().getSession().getPeerCertificates()) - .asList() - .containsExactlyElementsIn(certs); - // Returns the SSL session for further assertion. - return sslHandler.engine().getSession(); - } -} diff --git a/proxy/src/main/java/google/registry/proxy/CertificateModule.java b/proxy/src/main/java/google/registry/proxy/CertificateModule.java index 342a65e50..67c408bf2 100644 --- a/proxy/src/main/java/google/registry/proxy/CertificateModule.java +++ b/proxy/src/main/java/google/registry/proxy/CertificateModule.java @@ -62,7 +62,9 @@ import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; * @see Cloud Key Management Service */ @Module -public class CertificateModule { +public final class CertificateModule { + + private CertificateModule() {} /** Dagger qualifier to provide bindings related to the certificates that the server provides. */ @Qualifier @@ -94,8 +96,7 @@ public class CertificateModule { */ private static ImmutableList filterAndConvert( ImmutableList objects, Class clazz, Function converter) { - return objects - .stream() + return objects.stream() .filter(clazz::isInstance) .map(clazz::cast) .map(converter) @@ -112,19 +113,20 @@ public class CertificateModule { @Singleton @Provides - static Supplier provideCertificatesSupplier( - @ServerCertificates Provider certificatesProvider, ProxyConfig config) { + static Supplier> provideCertificatesSupplier( + @ServerCertificates Provider> certificatesProvider, + ProxyConfig config) { return memoizeWithExpiration( certificatesProvider::get, config.serverCertificateCacheSeconds, SECONDS); } @Provides @ServerCertificates - static X509Certificate[] provideCertificates( + static ImmutableList provideCertificates( Environment env, - @Local Lazy localCertificates, - @Prod Lazy prodCertificates) { - return (env == Environment.LOCAL) ? localCertificates.get() : prodCertificates.get(); + @Local Lazy> localCertificates, + @Prod Lazy> prodCertificates) { + return env == Environment.LOCAL ? localCertificates.get() : prodCertificates.get(); } @Provides @@ -133,7 +135,7 @@ public class CertificateModule { Environment env, @Local Lazy localPrivateKey, @Prod Lazy prodPrivateKey) { - return (env == Environment.LOCAL) ? localPrivateKey.get() : prodPrivateKey.get(); + return env == Environment.LOCAL ? localPrivateKey.get() : prodPrivateKey.get(); } @Singleton @@ -156,8 +158,8 @@ public class CertificateModule { @Singleton @Provides @Local - static X509Certificate[] provideLocalCertificates(SelfSignedCertificate ssc) { - return new X509Certificate[] {ssc.cert()}; + static ImmutableList provideLocalCertificates(SelfSignedCertificate ssc) { + return ImmutableList.of(ssc.cert()); } @Provides @@ -210,7 +212,7 @@ public class CertificateModule { // This binding should not be used directly. Use the supplier binding instead. @Provides @Prod - static X509Certificate[] provideProdCertificates( + static ImmutableList provideProdCertificates( @Named("pemObjects") ImmutableList pemObject) { JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider("BC"); Function certificateConverter = @@ -224,7 +226,7 @@ public class CertificateModule { }; ImmutableList certificates = filterAndConvert(pemObject, X509CertificateHolder.class, certificateConverter); - checkState(certificates.size() != 0, "No certificates found in the pem file"); + checkState(!certificates.isEmpty(), "No certificates found in the pem file"); X509Certificate lastCert = null; for (X509Certificate cert : certificates) { if (lastCert != null) { @@ -236,8 +238,6 @@ public class CertificateModule { } lastCert = cert; } - X509Certificate[] certificateArray = new X509Certificate[certificates.size()]; - certificates.toArray(certificateArray); - return certificateArray; + return certificates; } } diff --git a/proxy/src/main/java/google/registry/proxy/EppProtocolModule.java b/proxy/src/main/java/google/registry/proxy/EppProtocolModule.java index 32314225c..b9b00cba8 100644 --- a/proxy/src/main/java/google/registry/proxy/EppProtocolModule.java +++ b/proxy/src/main/java/google/registry/proxy/EppProtocolModule.java @@ -53,7 +53,9 @@ import javax.inject.Singleton; /** A module that provides the {@link FrontendProtocol} used for epp protocol. */ @Module -public class EppProtocolModule { +public final class EppProtocolModule { + + private EppProtocolModule() {} /** Dagger qualifier to provide epp protocol related handlers and other bindings. */ @Qualifier @@ -159,7 +161,7 @@ public class EppProtocolModule { static SslServerInitializer provideSslServerInitializer( SslProvider sslProvider, Supplier privateKeySupplier, - Supplier certificatesSupplier) { + Supplier> certificatesSupplier) { return new SslServerInitializer<>(true, sslProvider, privateKeySupplier, certificatesSupplier); } diff --git a/proxy/src/main/java/google/registry/proxy/HttpsRelayProtocolModule.java b/proxy/src/main/java/google/registry/proxy/HttpsRelayProtocolModule.java index 389db1bb8..f625e2f89 100644 --- a/proxy/src/main/java/google/registry/proxy/HttpsRelayProtocolModule.java +++ b/proxy/src/main/java/google/registry/proxy/HttpsRelayProtocolModule.java @@ -14,6 +14,8 @@ package google.registry.proxy; +import static google.registry.networking.handler.SslClientInitializer.createSslClientInitializerWithSystemTrustStore; + import com.google.common.collect.ImmutableList; import dagger.Module; import dagger.Provides; @@ -63,7 +65,7 @@ public class HttpsRelayProtocolModule { @HttpsRelayProtocol static SslClientInitializer provideSslClientInitializer( SslProvider sslProvider) { - return new SslClientInitializer<>( + return createSslClientInitializerWithSystemTrustStore( sslProvider, channel -> ((BackendProtocol) channel.attr(Protocol.PROTOCOL_KEY).get()).host(), channel -> channel.attr(Protocol.PROTOCOL_KEY).get().port()); diff --git a/proxy/src/main/java/google/registry/proxy/ProxyModule.java b/proxy/src/main/java/google/registry/proxy/ProxyModule.java index 694548d37..e64ae64b3 100644 --- a/proxy/src/main/java/google/registry/proxy/ProxyModule.java +++ b/proxy/src/main/java/google/registry/proxy/ProxyModule.java @@ -217,8 +217,7 @@ public class ProxyModule { @Singleton @Provides @Named("accessToken") - static Supplier provideAccessTokenSupplier( - GoogleCredentialsBundle credentialsBundle, ProxyConfig config) { + static Supplier provideAccessTokenSupplier(GoogleCredentialsBundle credentialsBundle) { return () -> { GoogleCredentials credentials = credentialsBundle.getGoogleCredentials(); try { @@ -329,14 +328,14 @@ public class ProxyModule { @Singleton @Component( modules = { - ProxyModule.class, - CertificateModule.class, - HttpsRelayProtocolModule.class, - WhoisProtocolModule.class, - WebWhoisProtocolsModule.class, - EppProtocolModule.class, - HealthCheckProtocolModule.class, - MetricsModule.class + ProxyModule.class, + CertificateModule.class, + HttpsRelayProtocolModule.class, + WhoisProtocolModule.class, + WebWhoisProtocolsModule.class, + EppProtocolModule.class, + HealthCheckProtocolModule.class, + MetricsModule.class }) interface ProxyComponent { diff --git a/proxy/src/main/java/google/registry/proxy/WebWhoisProtocolsModule.java b/proxy/src/main/java/google/registry/proxy/WebWhoisProtocolsModule.java index 04c00e44a..0242c3a33 100644 --- a/proxy/src/main/java/google/registry/proxy/WebWhoisProtocolsModule.java +++ b/proxy/src/main/java/google/registry/proxy/WebWhoisProtocolsModule.java @@ -35,7 +35,9 @@ import javax.inject.Singleton; /** A module that provides the {@link FrontendProtocol}s to redirect HTTP(S) web WHOIS requests. */ @Module -public class WebWhoisProtocolsModule { +public final class WebWhoisProtocolsModule { + + private WebWhoisProtocolsModule() {} /** Dagger qualifier to provide HTTP whois protocol related handlers and other bindings. */ @Qualifier @@ -54,7 +56,7 @@ public class WebWhoisProtocolsModule { static FrontendProtocol provideHttpWhoisProtocol( @HttpWhoisProtocol int httpWhoisPort, @HttpWhoisProtocol ImmutableList> handlerProviders) { - return google.registry.proxy.Protocol.frontendBuilder() + return Protocol.frontendBuilder() .name(HTTP_PROTOCOL_NAME) .port(httpWhoisPort) .hasBackend(false) @@ -68,7 +70,7 @@ public class WebWhoisProtocolsModule { static FrontendProtocol provideHttpsWhoisProtocol( @HttpsWhoisProtocol int httpsWhoisPort, @HttpsWhoisProtocol ImmutableList> handlerProviders) { - return google.registry.proxy.Protocol.frontendBuilder() + return Protocol.frontendBuilder() .name(HTTPS_PROTOCOL_NAME) .port(httpsWhoisPort) .hasBackend(false) @@ -110,15 +112,13 @@ public class WebWhoisProtocolsModule { @Provides @HttpWhoisProtocol - static WebWhoisRedirectHandler provideHttpRedirectHandler( - google.registry.proxy.ProxyConfig config) { + static WebWhoisRedirectHandler provideHttpRedirectHandler(ProxyConfig config) { return new WebWhoisRedirectHandler(false, config.webWhois.redirectHost); } @Provides @HttpsWhoisProtocol - static WebWhoisRedirectHandler provideHttpsRedirectHandler( - google.registry.proxy.ProxyConfig config) { + static WebWhoisRedirectHandler provideHttpsRedirectHandler(ProxyConfig config) { return new WebWhoisRedirectHandler(true, config.webWhois.redirectHost); } @@ -133,7 +133,7 @@ public class WebWhoisProtocolsModule { static SslServerInitializer provideSslServerInitializer( SslProvider sslProvider, Supplier privateKeySupplier, - Supplier certificatesSupplier) { + Supplier> certificatesSupplier) { return new SslServerInitializer<>(false, sslProvider, privateKeySupplier, certificatesSupplier); } } diff --git a/proxy/src/test/java/google/registry/proxy/CertificateModuleTest.java b/proxy/src/test/java/google/registry/proxy/CertificateModuleTest.java index 191418a29..91f0a3141 100644 --- a/proxy/src/test/java/google/registry/proxy/CertificateModuleTest.java +++ b/proxy/src/test/java/google/registry/proxy/CertificateModuleTest.java @@ -20,6 +20,7 @@ import static google.registry.networking.handler.SslInitializerTestUtils.signKey import static google.registry.testing.JUnitBackports.assertThrows; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.common.collect.ImmutableList; import dagger.Component; import dagger.Module; import dagger.Provides; @@ -79,7 +80,7 @@ public class CertificateModuleTest { byte[] pemBytes = getPemBytes(cert, ssc.cert(), key); component = createComponent(pemBytes); assertThat(component.privateKey()).isEqualTo(key); - assertThat(component.certificates()).asList().containsExactly(cert, ssc.cert()).inOrder(); + assertThat(component.certificates()).containsExactly(cert, ssc.cert()).inOrder(); } @Test @@ -87,7 +88,7 @@ public class CertificateModuleTest { byte[] pemBytes = getPemBytes(cert, key, ssc.cert()); component = createComponent(pemBytes); assertThat(component.privateKey()).isEqualTo(key); - assertThat(component.certificates()).asList().containsExactly(cert, ssc.cert()).inOrder(); + assertThat(component.certificates()).containsExactly(cert, ssc.cert()).inOrder(); } @Test @@ -131,13 +132,13 @@ public class CertificateModuleTest { private final byte[] pemBytes; PemBytesModule(byte[] pemBytes) { - this.pemBytes = pemBytes; + this.pemBytes = pemBytes.clone(); } @Provides @Named("pemBytes") byte[] providePemBytes() { - return pemBytes; + return pemBytes.clone(); } } @@ -156,6 +157,6 @@ public class CertificateModuleTest { PrivateKey privateKey(); @Prod - X509Certificate[] certificates(); + ImmutableList certificates(); } }