Read GCP proxy EPP SSL secret from GCS

This allows us to not ship the proxy with certificates/private keys. The secret is still encrypted by KMS. Reading the secret only happens once when the first EPP request comes in, which should not incur any tangible performance penalty.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=191771680
This commit is contained in:
jianglai 2018-04-05 11:28:58 -07:00 committed by Ben McIlwain
parent 18a145eef1
commit 983bd27ee0
7 changed files with 55 additions and 12 deletions

View file

@ -27,6 +27,7 @@ java_library(
"@com_google_api_client", "@com_google_api_client",
"@com_google_apis_google_api_services_cloudkms", "@com_google_apis_google_api_services_cloudkms",
"@com_google_apis_google_api_services_monitoring", "@com_google_apis_google_api_services_monitoring",
"@com_google_apis_google_api_services_storage",
"@com_google_auto_value", "@com_google_auto_value",
"@com_google_code_findbugs_jsr305", "@com_google_code_findbugs_jsr305",
"@com_google_dagger", "@com_google_dagger",

View file

@ -37,7 +37,7 @@ public class ProxyConfig {
public List<String> gcpScopes; public List<String> gcpScopes;
public int accessTokenValidPeriodSeconds; public int accessTokenValidPeriodSeconds;
public int accessTokenRefreshBeforeExpirySeconds; public int accessTokenRefreshBeforeExpirySeconds;
public String sslPemFilename; public Gcs gcs;
public Kms kms; public Kms kms;
public Epp epp; public Epp epp;
public Whois whois; public Whois whois;
@ -45,6 +45,12 @@ public class ProxyConfig {
public HttpsRelay httpsRelay; public HttpsRelay httpsRelay;
public Metrics metrics; public Metrics metrics;
/** Configuration options that apply to GCS. */
public static class Gcs {
public String bucket;
public String sslPemFilename;
}
/** Configuration options that apply to Cloud KMS. */ /** Configuration options that apply to Cloud KMS. */
public static class Kms { public static class Kms {
public String location; public String location;

View file

@ -16,7 +16,6 @@ package google.registry.proxy;
import static com.google.common.base.Suppliers.memoizeWithExpiration; import static com.google.common.base.Suppliers.memoizeWithExpiration;
import static google.registry.proxy.ProxyConfig.getProxyConfig; import static google.registry.proxy.ProxyConfig.getProxyConfig;
import static google.registry.util.ResourceUtils.readResourceBytes;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import com.beust.jcommander.JCommander; import com.beust.jcommander.JCommander;
@ -26,6 +25,7 @@ import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.util.Utils; import com.google.api.client.googleapis.util.Utils;
import com.google.api.services.cloudkms.v1.CloudKMS; import com.google.api.services.cloudkms.v1.CloudKMS;
import com.google.api.services.cloudkms.v1.model.DecryptRequest; import com.google.api.services.cloudkms.v1.model.DecryptRequest;
import com.google.api.services.storage.Storage;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.monitoring.metrics.MetricReporter; import com.google.monitoring.metrics.MetricReporter;
@ -44,6 +44,7 @@ import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.OpenSsl; import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
@ -218,14 +219,32 @@ public class ProxyModule {
.build(); .build();
} }
@Singleton
@Provides
static Storage provideStorage(GoogleCredential credential, ProxyConfig config) {
return new Storage.Builder(
Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), credential)
.setApplicationName(config.projectId)
.build();
}
@Singleton @Singleton
@Provides @Provides
@Named("encryptedPemBytes") @Named("encryptedPemBytes")
static byte[] provideEncryptedPemBytes(ProxyConfig config) { static byte[] provideEncryptedPemBytes(Storage storage, ProxyConfig config) {
try { try {
return readResourceBytes(ProxyModule.class, "resources/" + config.sslPemFilename).read(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
storage
.objects()
.get(config.gcs.bucket, config.gcs.sslPemFilename)
.executeMediaAndDownloadTo(outputStream);
return outputStream.toByteArray();
} catch (IOException e) { } catch (IOException e) {
logger.severefmt(e, "Error reading encrypted PEM file: %s", config.sslPemFilename); logger.severefmt(
e,
"Error reading encrypted PEM file %s from GCS bucket %s",
config.gcs.sslPemFilename,
config.gcs.bucket);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

View file

@ -36,8 +36,11 @@ accessTokenValidPeriodSeconds: 1800
# com.google.api.client.auth.oauth2.Credential#intercept. # com.google.api.client.auth.oauth2.Credential#intercept.
accessTokenRefreshBeforeExpirySeconds: 60 accessTokenRefreshBeforeExpirySeconds: 60
# Name of the encrypted PEM file. gcs:
sslPemFilename: your-ssl.pem # GCS bucket that stores the encrypted PEM file.
bucket: your-gcs-bucket
# Name of the encrypted PEM file.
sslPemFilename: your-pem-filename
# Strings used to construct the KMS crypto key URL. # Strings used to construct the KMS crypto key URL.
# See: https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys # See: https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys

View file

@ -7,11 +7,12 @@ terraform {
} }
module "proxy" { module "proxy" {
source = "../../modules" source = "../../modules"
proxy_project_name = "YOUR_PROXY_PROJECT" proxy_project_name = "YOUR_PROXY_PROJECT"
nomulus_project_name = "YOUR_NOMULUS_GPROJECT" nomulus_project_name = "YOUR_NOMULUS_GPROJECT"
gcr_project_name = "YOUR_GCR_PROJECT" gcr_project_name = "YOUR_GCR_PROJECT"
proxy_domain_name = "YOUR_PROXY_DOMAIN" proxy_domain_name = "YOUR_PROXY_DOMAIN"
proxy_certificate_bucket = "YOU_CERTIFICATE_BUCKET"
} }
output "proxy_service_account_client_id" { output "proxy_service_account_client_id" {

View file

@ -0,0 +1,10 @@
resource "google_storage_bucket" "proxy_certificate" {
name = "${var.proxy_certificate_bucket}"
storage_class = "MULTI_REGIONAL"
}
resource "google_storage_bucket_iam_member" "member" {
bucket = "${google_storage_bucket.proxy_certificate.name}"
role = "roles/storage.objectViewer"
member = "serviceAccount:${google_service_account.proxy_service_account.email}"
}

View file

@ -10,6 +10,9 @@ variable "gcr_project_name" {}
# The base domain name of the proxy, without the whois. or epp. part. # The base domain name of the proxy, without the whois. or epp. part.
variable "proxy_domain_name" {} variable "proxy_domain_name" {}
# The GCS bucket that stores the encrypted SSL certificate.
variable "proxy_certificate_bucket" {}
# Cloud KMS keyring name # Cloud KMS keyring name
variable "proxy_key_ring" { variable "proxy_key_ring" {
default = "proxy-key-ring" default = "proxy-key-ring"