Use self signed certificate when running the proxy locally

This allows us to not obtain a certificate and encrypt it with KMS when running the proxy locally during development.

Also updated FOSS build dagger version.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=191746309
This commit is contained in:
jianglai 2018-04-05 08:37:27 -07:00 committed by Ben McIlwain
parent ea891001d9
commit 18a145eef1
10 changed files with 157 additions and 117 deletions

View file

@ -19,10 +19,12 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
import google.registry.proxy.ProxyModule.PemBytes;
import google.registry.proxy.ProxyConfig.Environment;
import google.registry.util.FormattingLogger;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@ -32,6 +34,7 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.function.Function;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Singleton;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
@ -44,18 +47,32 @@ import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
/**
* Dagger module that provides bindings needed to inject EPP SSL certificate chain and private key.
*
* <p>The certificates and private key are stored in a .pem file that is encrypted by Cloud KMS. The
* .pem file can be generated by concatenating the .crt certificate files on the chain and the .key
* private file.
* <p>The production certificates and private key are stored in a .pem file that is encrypted by
* Cloud KMS. The .pem file can be generated by concatenating the .crt certificate files on the
* chain and the .key private file.
*
* <p>The certificates in the .pem file must be stored in order, where the next certificate's
* subject is the previous certificate's issuer.
* <p>The production certificates in the .pem file must be stored in order, where the next
* certificate's subject is the previous certificate's issuer.
*
* <p>When running the proxy locally or in test, a self signed certificate is used.
*
* @see <a href="https://cloud.google.com/kms/">Cloud Key Management Service</a>
*/
@Module
public class CertificateModule {
/** Dagger qualifier to provide bindings related to EPP certificates */
@Qualifier
public @interface EppCertificates {}
/** Dagger qualifier to provide bindings when running locally. */
@Qualifier
public @interface Local {}
/** Dagger qualifier to provide bindings when running in production. */
@Qualifier
public @interface Prod {}
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
static {
@ -79,12 +96,56 @@ public class CertificateModule {
.collect(toImmutableList());
}
@Singleton
@Provides
@EppCertificates
static X509Certificate[] provideCertificates(
Environment env,
@Local Lazy<X509Certificate[]> localCertificates,
@Prod Lazy<X509Certificate[]> prodCertificates) {
return (env == Environment.LOCAL) ? localCertificates.get() : prodCertificates.get();
}
@Singleton
@Provides
@EppCertificates
static PrivateKey providePrivateKey(
Environment env,
@Local Lazy<PrivateKey> localPrivateKey,
@Prod Lazy<PrivateKey> prodPrivateKey) {
return (env == Environment.LOCAL) ? localPrivateKey.get() : prodPrivateKey.get();
}
@Singleton
@Provides
static SelfSignedCertificate provideSelfSignedCertificate() {
try {
return new SelfSignedCertificate();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Singleton
@Provides
@Local
static PrivateKey provideLocalPrivateKey(SelfSignedCertificate ssc) {
return ssc.key();
}
@Singleton
@Provides
@Local
static X509Certificate[] provideLocalCertificates(SelfSignedCertificate ssc) {
return new X509Certificate[] {ssc.cert()};
}
@Singleton
@Provides
@Named("pemObjects")
static ImmutableList<Object> providePemObjects(PemBytes pemBytes) {
static ImmutableList<Object> providePemObjects(@Named("pemBytes") byte[] pemBytes) {
PEMParser pemParser =
new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemBytes.getBytes()), UTF_8));
new PEMParser(new InputStreamReader(new ByteArrayInputStream(pemBytes), UTF_8));
ImmutableList.Builder<Object> listBuilder = new ImmutableList.Builder<>();
Object obj;
// PEMParser returns an object (private key, certificate, etc) each time readObject() is called,
@ -107,7 +168,8 @@ public class CertificateModule {
@Singleton
@Provides
static PrivateKey providePrivateKey(@Named("pemObjects") ImmutableList<Object> pemObjects) {
@Prod
static PrivateKey provideProdPrivateKey(@Named("pemObjects") ImmutableList<Object> pemObjects) {
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Function<PEMKeyPair, PrivateKey> privateKeyConverter =
pemKeyPair -> {
@ -129,8 +191,8 @@ public class CertificateModule {
@Singleton
@Provides
@Named("eppServerCertificates")
static X509Certificate[] provideCertificates(
@Prod
static X509Certificate[] provideProdCertificates(
@Named("pemObjects") ImmutableList<Object> pemObject) {
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider("BC");
Function<X509CertificateHolder, X509Certificate> certificateConverter =

View file

@ -28,7 +28,6 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.logging.LoggingHandler;
import java.security.cert.X509Certificate;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Qualifier;
@ -44,7 +43,7 @@ public class HttpsRelayProtocolModule {
/** Dagger qualifier to provide https relay protocol related handlers and other bindings. */
@Qualifier
@interface HttpsRelayProtocol {}
public @interface HttpsRelayProtocol {}
private static final String PROTOCOL_NAME = "https_relay";
@ -89,7 +88,7 @@ public class HttpsRelayProtocolModule {
@Nullable
@Provides
@Named("relayTrustedCertificates")
@HttpsRelayProtocol
public static X509Certificate[] provideTrustedCertificates() {
// null uses the system default trust store.
return null;

View file

@ -232,7 +232,8 @@ public class ProxyModule {
@Singleton
@Provides
static PemBytes providePemBytes(
@Named("pemBytes")
static byte[] providePemBytes(
CloudKMS cloudKms, @Named("encryptedPemBytes") byte[] encryptedPemBytes, ProxyConfig config) {
String cryptoKeyUrl =
String.format(
@ -240,15 +241,14 @@ public class ProxyModule {
config.projectId, config.kms.location, config.kms.keyRing, config.kms.cryptoKey);
try {
DecryptRequest decryptRequest = new DecryptRequest().encodeCiphertext(encryptedPemBytes);
return PemBytes.create(
cloudKms
.projects()
.locations()
.keyRings()
.cryptoKeys()
.decrypt(cryptoKeyUrl, decryptRequest)
.execute()
.decodePlaintext());
return cloudKms
.projects()
.locations()
.keyRings()
.cryptoKeys()
.decrypt(cryptoKeyUrl, decryptRequest)
.execute()
.decodePlaintext();
} catch (IOException e) {
logger.severefmt(e, "PEM file decryption failed using CryptoKey: %s", cryptoKeyUrl);
throw new RuntimeException(e);
@ -283,31 +283,6 @@ public class ProxyModule {
return getProxyConfig(env);
}
/**
* A wrapper class for decrypted bytes of the PEM file.
*
* <p>Note that this should not be an @AutoValue class because we need a clone of the bytes to be
* returned, otherwise the wrapper class becomes mutable.
*/
// TODO: remove this class once FOSS build can use @BindsInstance to bind a byte[]
// (https://github.com/bazelbuild/bazel/issues/4138)
static class PemBytes {
private final byte[] bytes;
static PemBytes create(byte[] bytes) {
return new PemBytes(bytes);
}
private PemBytes(byte[] bytes) {
this.bytes = bytes;
}
byte[] getBytes() {
return bytes.clone();
}
}
/** Root level component that exposes the port-to-protocol map. */
@Singleton
@Component(

View file

@ -1 +0,0 @@
# This file is for test only. Leave it blank.

View file

@ -17,6 +17,7 @@ package google.registry.proxy.handler;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.proxy.Protocol.PROTOCOL_KEY;
import google.registry.proxy.HttpsRelayProtocolModule.HttpsRelayProtocol;
import google.registry.proxy.Protocol.BackendProtocol;
import google.registry.util.FormattingLogger;
import io.netty.channel.Channel;
@ -29,7 +30,6 @@ import io.netty.handler.ssl.SslProvider;
import java.security.cert.X509Certificate;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
@ -51,8 +51,7 @@ public class SslClientInitializer<C extends Channel> extends ChannelInitializer<
@Inject
SslClientInitializer(
SslProvider sslProvider,
@Nullable @Named("relayTrustedCertificates") X509Certificate... trustCertificates) {
SslProvider sslProvider, @Nullable @HttpsRelayProtocol X509Certificate... trustCertificates) {
logger.infofmt("Client SSL Provider: %s", sslProvider);
this.sslProvider = sslProvider;
this.trustedCertificates = trustCertificates;

View file

@ -14,6 +14,7 @@
package google.registry.proxy.handler;
import google.registry.proxy.CertificateModule.EppCertificates;
import google.registry.util.FormattingLogger;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
@ -30,7 +31,6 @@ import io.netty.util.concurrent.Promise;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
/**
@ -66,8 +66,8 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
@Inject
SslServerInitializer(
SslProvider sslProvider,
PrivateKey privateKey,
@Named("eppServerCertificates") X509Certificate... certificates) {
@EppCertificates PrivateKey privateKey,
@EppCertificates X509Certificate... certificates) {
logger.infofmt("Server SSL Provider: %s", sslProvider);
this.sslProvider = sslProvider;
this.privateKey = privateKey;