Remove Cloud KMS from Nomulus Server (#1839)

* Remove Cloud KMS from Nomulus Server

Removed Cloud KMS from the Nomulus (:core) since it is no longer used.

Renamed remaining classes to reflect their use of the SecretManager.

Updated the config instructions to use a new codename for the keyring:
KMS to CSM. This PR works with both codenames. Will drop 'KMS' after
the internal repo is updated.
This commit is contained in:
Weimin Yu 2022-11-04 11:17:15 -04:00 committed by GitHub
parent 2812df303d
commit 242864d198
32 changed files with 161 additions and 911 deletions

View file

@ -186,7 +186,6 @@ dependencies {
implementation deps['com.google.apis:google-api-services-admin-directory']
implementation deps['com.google.apis:google-api-services-appengine']
implementation deps['com.google.apis:google-api-services-bigquery']
implementation deps['com.google.apis:google-api-services-cloudkms']
implementation deps['com.google.apis:google-api-services-dataflow']
implementation deps['com.google.apis:google-api-services-dns']
implementation deps['com.google.apis:google-api-services-drive']

View file

@ -77,7 +77,6 @@ com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0=c
com.google.apis:google-api-services-appengine:v1-rev20220612-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20211129-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-clouddebugger:v2-rev20210813-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudkms:v1-rev20220701-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20211017-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20210818-1.32.1=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v2beta1-rev99-1.25.0=compileClasspath,default,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath

View file

@ -1076,24 +1076,6 @@ public final class RegistryConfig {
return config.keyring.activeKeyring;
}
/**
* The name to use for the Cloud KMS KeyRing containing encryption keys for Nomulus secrets.
*
* @see <a
* href="https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings#KeyRing">projects.locations.keyRings</a>
*/
@Provides
@Config("cloudKmsKeyRing")
public static String provideCloudKmsKeyRing(RegistryConfigSettings config) {
return config.keyring.kms.keyringName;
}
@Provides
@Config("cloudKmsProjectId")
public static String provideCloudKmsProjectId(RegistryConfigSettings config) {
return config.keyring.kms.projectId;
}
@Provides
@Config("customLogicFactoryClass")
public static String provideCustomLogicFactoryClass(RegistryConfigSettings config) {

View file

@ -214,6 +214,7 @@ public class RegistryConfigSettings {
/** Configuration for keyrings (used to store secrets outside of source). */
public static class Keyring {
public String activeKeyring;
// TODO(b/257276342): Remove after config files in nomulus-internal are updated.
public Kms kms;
}

View file

@ -449,7 +449,8 @@ beam:
stagingBucketUrl: gcs-bucket-with-staged-templates
keyring:
# The name of the active keyring, either "KMS" or "Dummy".
# The name of the active keyring, either "Dummy" or "CSM". The latter stands
# for Cloud SecretManager.
activeKeyring: Dummy
# Configuration options specific to Google Cloud KMS.

View file

@ -1,41 +0,0 @@
// Copyright 2017 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.keyring.kms;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
/**
* A value type class containing a Cloud KMS encrypted and encoded ciphertext, and the name of the
* CryptoKeyVersion used to encrypt it.
*/
@AutoValue
abstract class EncryptResponse {
static EncryptResponse create(
com.google.api.services.cloudkms.v1.model.EncryptResponse cloudKmsEncryptResponse) {
return new AutoValue_EncryptResponse(
cloudKmsEncryptResponse.getCiphertext(), cloudKmsEncryptResponse.getName());
}
@VisibleForTesting
static EncryptResponse create(String ciphertext, String cryptoKeyVersionName) {
return new AutoValue_EncryptResponse(ciphertext, cryptoKeyVersionName);
}
abstract String ciphertext();
abstract String cryptoKeyVersionName();
}

View file

@ -1,48 +0,0 @@
// Copyright 2017 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.keyring.kms;
import google.registry.keyring.api.KeyringException;
/** An abstraction to simplify Cloud KMS operations. */
public interface KmsConnection {
/**
* The maximum allowable secret size, as set by Cloud KMS.
*
* @see <a
* href="https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys/encrypt#request-body">projects.locations.keyRings.cryptoKeys.encrypt</a>
*/
int MAX_SECRET_SIZE_BYTES = 64 * 1024;
/**
* Encrypts a plaintext with CryptoKey {@code cryptoKeyName} on KeyRing {@code keyRingName}.
*
* <p>The latest CryptoKeyVersion is used to encrypt the value. The value must not be larger than
* {@code MAX_SECRET_SIZE_BYTES}.
*
* <p>If no applicable CryptoKey or CryptoKeyVersion exist, they will be created.
*
* @throws KeyringException on encryption failure.
*/
EncryptResponse encrypt(String cryptoKeyName, byte[] plaintext);
/**
* Decrypts a Cloud KMS encrypted and encoded value with CryptoKey {@code cryptoKeyName}.
*
* @throws KeyringException on decryption failure.
*/
byte[] decrypt(String cryptoKeyName, String encodedCiphertext);
}

View file

@ -1,177 +0,0 @@
// Copyright 2017 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.keyring.kms;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpStatusCodes;
import com.google.api.services.cloudkms.v1.CloudKMS;
import com.google.api.services.cloudkms.v1.model.CryptoKey;
import com.google.api.services.cloudkms.v1.model.CryptoKeyVersion;
import com.google.api.services.cloudkms.v1.model.DecryptRequest;
import com.google.api.services.cloudkms.v1.model.EncryptRequest;
import com.google.api.services.cloudkms.v1.model.KeyRing;
import com.google.api.services.cloudkms.v1.model.UpdateCryptoKeyPrimaryVersionRequest;
import google.registry.keyring.api.KeyringException;
import google.registry.util.Retrier;
import java.io.IOException;
/** The {@link KmsConnection} which talks to Cloud KMS. */
class KmsConnectionImpl implements KmsConnection {
private static final String KMS_LOCATION_FORMAT = "projects/%s/locations/global";
private static final String KMS_KEYRING_NAME_FORMAT = "projects/%s/locations/global/keyRings/%s";
private static final String KMS_CRYPTO_KEY_NAME_FORMAT =
"projects/%s/locations/global/keyRings/%s/cryptoKeys/%s";
private final CloudKMS kms;
private final String kmsKeyRingName;
private final String projectId;
private final Retrier retrier;
KmsConnectionImpl(String projectId, String kmsKeyRingName, Retrier retrier, CloudKMS kms) {
this.projectId = projectId;
this.kmsKeyRingName = kmsKeyRingName;
this.retrier = retrier;
this.kms = kms;
}
@Override
public EncryptResponse encrypt(String cryptoKeyName, byte[] value) {
checkArgument(
value.length <= MAX_SECRET_SIZE_BYTES,
"Value to encrypt was larger than %s bytes",
MAX_SECRET_SIZE_BYTES);
try {
return attemptEncrypt(cryptoKeyName, value);
} catch (IOException e) {
throw new KeyringException(
String.format("CloudKMS encrypt operation failed for secret %s", cryptoKeyName), e);
}
}
private EncryptResponse attemptEncrypt(String cryptoKeyName, byte[] value) throws IOException {
String fullKeyRingName = getKeyRingName(projectId, kmsKeyRingName);
try {
kms.projects().locations().keyRings().get(fullKeyRingName).execute();
} catch (GoogleJsonResponseException jsonException) {
if (jsonException.getStatusCode() == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
// Create the KeyRing in the "global" namespace. Encryption keys will be accessible from all
// GCP regions.
kms.projects()
.locations()
.keyRings()
.create(getLocationName(projectId), new KeyRing())
.setKeyRingId(kmsKeyRingName)
.execute();
} else {
throw jsonException;
}
}
String fullKeyName = getCryptoKeyName(projectId, kmsKeyRingName, cryptoKeyName);
boolean newCryptoKey = false;
try {
kms.projects().locations().keyRings().cryptoKeys().get(fullKeyName).execute();
} catch (GoogleJsonResponseException jsonException) {
if (jsonException.getStatusCode() == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
newCryptoKey = true;
kms.projects()
.locations()
.keyRings()
.cryptoKeys()
.create(fullKeyRingName, new CryptoKey().setPurpose("ENCRYPT_DECRYPT"))
.setCryptoKeyId(cryptoKeyName)
.execute();
} else {
throw jsonException;
}
}
// New CryptoKeys start with a CryptoKeyVersion, so we only create a new CryptoKeyVersion and
// rotate to it if we're dealing with an existing CryptoKey.
if (!newCryptoKey) {
CryptoKeyVersion cryptoKeyVersion =
kms.projects()
.locations()
.keyRings()
.cryptoKeys()
.cryptoKeyVersions()
.create(fullKeyName, new CryptoKeyVersion())
.execute();
kms.projects()
.locations()
.keyRings()
.cryptoKeys()
.updatePrimaryVersion(
fullKeyName,
new UpdateCryptoKeyPrimaryVersionRequest()
.setCryptoKeyVersionId(getCryptoKeyVersionId(cryptoKeyVersion)))
.execute();
}
return EncryptResponse.create(
kms.projects()
.locations()
.keyRings()
.cryptoKeys()
.encrypt(fullKeyName, new EncryptRequest().encodePlaintext(value))
.execute());
}
@Override
public byte[] decrypt(final String cryptoKeyName, final String encodedCiphertext) {
try {
return retrier.callWithRetry(
() -> attemptDecrypt(cryptoKeyName, encodedCiphertext), IOException.class);
} catch (RuntimeException e) {
throw new KeyringException(
String.format("CloudKMS decrypt operation failed for secret %s", cryptoKeyName), e);
}
}
private byte[] attemptDecrypt(String cryptoKeyName, String encodedCiphertext) throws IOException {
return kms.projects()
.locations()
.keyRings()
.cryptoKeys()
.decrypt(
getCryptoKeyName(projectId, kmsKeyRingName, cryptoKeyName),
new DecryptRequest().setCiphertext(encodedCiphertext))
.execute()
.decodePlaintext();
}
private static String getLocationName(String projectId) {
return String.format(KMS_LOCATION_FORMAT, projectId);
}
private static String getKeyRingName(String projectId, String kmsKeyRingName) {
return String.format(KMS_KEYRING_NAME_FORMAT, projectId, kmsKeyRingName);
}
private static String getCryptoKeyName(
String projectId, String kmsKeyRingName, String cryptoKeyName) {
return String.format(KMS_CRYPTO_KEY_NAME_FORMAT, projectId, kmsKeyRingName, cryptoKeyName);
}
private static String getCryptoKeyVersionId(CryptoKeyVersion cryptoKeyVersion) {
String cryptoKeyVersionName = cryptoKeyVersion.getName();
return cryptoKeyVersionName.substring(cryptoKeyVersionName.lastIndexOf('/') + 1);
}
}

View file

@ -1,84 +0,0 @@
// Copyright 2017 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.keyring.kms;
import com.google.api.services.cloudkms.v1.CloudKMS;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.api.Keyring;
import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.Retrier;
/** Dagger module for Cloud KMS. */
@Module
public abstract class KmsModule {
public static final String NAME = "KMS";
@Provides
@Config("defaultKms")
static CloudKMS provideKms(
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
@Config("cloudKmsProjectId") String projectId) {
return createKms(credentialsBundle, projectId);
}
@Provides
@Config("beamKms")
static CloudKMS provideBeamKms(
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
@Config("beamCloudKmsProjectId") String projectId) {
return createKms(credentialsBundle, projectId);
}
private static CloudKMS createKms(GoogleCredentialsBundle credentialsBundle, String projectId) {
return new CloudKMS.Builder(
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
@Provides
@Config("defaultKmsConnection")
static KmsConnection provideKmsConnection(
@Config("cloudKmsProjectId") String projectId,
@Config("cloudKmsKeyRing") String keyringName,
Retrier retrier,
@Config("defaultKms") CloudKMS defaultKms) {
return new KmsConnectionImpl(projectId, keyringName, retrier, defaultKms);
}
@Provides
@Config("beamKmsConnection")
static KmsConnection provideBeamKmsConnection(
@Config("beamCloudKmsProjectId") String projectId,
@Config("beamCloudKmsKeyRing") String keyringName,
Retrier retrier,
@Config("beamKms") CloudKMS defaultKms) {
return new KmsConnectionImpl(projectId, keyringName, retrier, defaultKms);
}
@Binds
@IntoMap
@StringKey(NAME)
abstract Keyring provideKeyring(KmsKeyring keyring);
}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.keyring.kms;
package google.registry.keyring.secretmanager;
import static com.google.common.base.CaseFormat.LOWER_HYPHEN;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
@ -29,8 +29,7 @@ import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
/** A {@link Keyring} implementation which stores sensitive data in the Secret Manager. */
// TODO(2021-08-01): rename this class to SecretManagerKeyring and update config files.
public class KmsKeyring implements Keyring {
public class SecretManagerKeyring implements Keyring {
/** Key labels for private key secrets. */
enum PrivateKeyLabel {
@ -75,7 +74,7 @@ public class KmsKeyring implements Keyring {
private final KeyringSecretStore secretStore;
@Inject
KmsKeyring(KeyringSecretStore secretStore) {
SecretManagerKeyring(KeyringSecretStore secretStore) {
this.secretStore = secretStore;
}

View file

@ -0,0 +1,40 @@
// Copyright 2017 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.keyring.secretmanager;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
import google.registry.keyring.api.Keyring;
/** Dagger module for {@link Keyring} backed by the Cloud SecretManager. */
@Module
public abstract class SecretManagerKeyringModule {
public static final String NAME = "CSM";
// TODO(b/257276342): Remove after configs in nomulus-internal are updated.
public static final String DEPRECATED_NAME = "KMS";
@Binds
@IntoMap
@StringKey(DEPRECATED_NAME)
abstract Keyring provideDeprecatedKeyring(SecretManagerKeyring keyring);
@Binds
@IntoMap
@StringKey(NAME)
abstract Keyring provideKeyring(SecretManagerKeyring keyring);
}

View file

@ -12,33 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.keyring.kms;
package google.registry.keyring.secretmanager;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel.BRDA_SIGNING_PRIVATE;
import static google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel.RDE_SIGNING_PRIVATE;
import static google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel.RDE_STAGING_PRIVATE;
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.BRDA_RECEIVER_PUBLIC;
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.BRDA_SIGNING_PUBLIC;
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_RECEIVER_PUBLIC;
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_SIGNING_PUBLIC;
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_STAGING_PUBLIC;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.ICANN_REPORTING_PASSWORD_STRING;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.JSON_CREDENTIAL_STRING;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_DNL_LOGIN_STRING;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_LORDN_PASSWORD_STRING;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_SMDRL_LOGIN_STRING;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.RDE_SSH_CLIENT_PRIVATE_STRING;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.RDE_SSH_CLIENT_PUBLIC_STRING;
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.SAFE_BROWSING_API_KEY;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PrivateKeyLabel.BRDA_SIGNING_PRIVATE;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PrivateKeyLabel.RDE_SIGNING_PRIVATE;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PrivateKeyLabel.RDE_STAGING_PRIVATE;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.BRDA_RECEIVER_PUBLIC;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.BRDA_SIGNING_PUBLIC;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.RDE_RECEIVER_PUBLIC;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.RDE_SIGNING_PUBLIC;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.RDE_STAGING_PUBLIC;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.ICANN_REPORTING_PASSWORD_STRING;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.JSON_CREDENTIAL_STRING;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.MARKSDB_DNL_LOGIN_STRING;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.MARKSDB_LORDN_PASSWORD_STRING;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.MARKSDB_SMDRL_LOGIN_STRING;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.RDE_SSH_CLIENT_PRIVATE_STRING;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.RDE_SSH_CLIENT_PUBLIC_STRING;
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SAFE_BROWSING_API_KEY;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.flogger.FluentLogger;
import google.registry.keyring.api.KeySerializer;
import google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel;
import google.registry.keyring.kms.KmsKeyring.PublicKeyLabel;
import google.registry.keyring.kms.KmsKeyring.StringKeyLabel;
import google.registry.keyring.secretmanager.SecretManagerKeyring.PrivateKeyLabel;
import google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel;
import google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel;
import google.registry.privileges.secretmanager.KeyringSecretStore;
import java.io.IOException;
import java.util.HashMap;
@ -50,73 +50,77 @@ import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPPublicKey;
/**
* The {@link KmsUpdater} accumulates updates to a {@link KmsKeyring} and persists them to KMS and
* Datastore when closed.
* The {@link SecretManagerKeyringUpdater} accumulates updates to a {@link SecretManagerKeyring} and
* persists them to KMS and Datastore when closed.
*/
// TODO(2021-06-01): rename this class to SecretManagerKeyringUpdater
public final class KmsUpdater {
public final class SecretManagerKeyringUpdater {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final KeyringSecretStore secretStore;
private final HashMap<String, byte[]> secretValues;
@Inject
public KmsUpdater(KeyringSecretStore secretStore) {
public SecretManagerKeyringUpdater(KeyringSecretStore secretStore) {
this.secretStore = secretStore;
// Use LinkedHashMap to preserve insertion order on update() to simplify testing and debugging
this.secretValues = new LinkedHashMap<>();
}
public KmsUpdater setRdeSigningKey(PGPKeyPair keyPair) throws IOException, PGPException {
public SecretManagerKeyringUpdater setRdeSigningKey(PGPKeyPair keyPair)
throws IOException, PGPException {
return setKeyPair(keyPair, RDE_SIGNING_PRIVATE, RDE_SIGNING_PUBLIC);
}
public KmsUpdater setRdeStagingKey(PGPKeyPair keyPair) throws IOException, PGPException {
public SecretManagerKeyringUpdater setRdeStagingKey(PGPKeyPair keyPair)
throws IOException, PGPException {
return setKeyPair(keyPair, RDE_STAGING_PRIVATE, RDE_STAGING_PUBLIC);
}
public KmsUpdater setRdeReceiverPublicKey(PGPPublicKey publicKey) throws IOException {
public SecretManagerKeyringUpdater setRdeReceiverPublicKey(PGPPublicKey publicKey)
throws IOException {
return setPublicKey(publicKey, RDE_RECEIVER_PUBLIC);
}
public KmsUpdater setBrdaSigningKey(PGPKeyPair keyPair) throws IOException, PGPException {
public SecretManagerKeyringUpdater setBrdaSigningKey(PGPKeyPair keyPair)
throws IOException, PGPException {
return setKeyPair(keyPair, BRDA_SIGNING_PRIVATE, BRDA_SIGNING_PUBLIC);
}
public KmsUpdater setBrdaReceiverPublicKey(PGPPublicKey publicKey) throws IOException {
public SecretManagerKeyringUpdater setBrdaReceiverPublicKey(PGPPublicKey publicKey)
throws IOException {
return setPublicKey(publicKey, BRDA_RECEIVER_PUBLIC);
}
public KmsUpdater setRdeSshClientPublicKey(String asciiPublicKey) {
public SecretManagerKeyringUpdater setRdeSshClientPublicKey(String asciiPublicKey) {
return setString(asciiPublicKey, RDE_SSH_CLIENT_PUBLIC_STRING);
}
public KmsUpdater setRdeSshClientPrivateKey(String asciiPrivateKey) {
public SecretManagerKeyringUpdater setRdeSshClientPrivateKey(String asciiPrivateKey) {
return setString(asciiPrivateKey, RDE_SSH_CLIENT_PRIVATE_STRING);
}
public KmsUpdater setSafeBrowsingAPIKey(String apiKey) {
public SecretManagerKeyringUpdater setSafeBrowsingAPIKey(String apiKey) {
return setString(apiKey, SAFE_BROWSING_API_KEY);
}
public KmsUpdater setIcannReportingPassword(String password) {
public SecretManagerKeyringUpdater setIcannReportingPassword(String password) {
return setString(password, ICANN_REPORTING_PASSWORD_STRING);
}
public KmsUpdater setMarksdbDnlLoginAndPassword(String login) {
public SecretManagerKeyringUpdater setMarksdbDnlLoginAndPassword(String login) {
return setString(login, MARKSDB_DNL_LOGIN_STRING);
}
public KmsUpdater setMarksdbLordnPassword(String password) {
public SecretManagerKeyringUpdater setMarksdbLordnPassword(String password) {
return setString(password, MARKSDB_LORDN_PASSWORD_STRING);
}
public KmsUpdater setMarksdbSmdrlLoginAndPassword(String login) {
public SecretManagerKeyringUpdater setMarksdbSmdrlLoginAndPassword(String login) {
return setString(login, MARKSDB_SMDRL_LOGIN_STRING);
}
public KmsUpdater setJsonCredential(String credential) {
public SecretManagerKeyringUpdater setJsonCredential(String credential) {
return setString(credential, JSON_CREDENTIAL_STRING);
}
@ -144,22 +148,22 @@ public final class KmsUpdater {
}
}
private KmsUpdater setString(String key, StringKeyLabel stringKeyLabel) {
private SecretManagerKeyringUpdater setString(String key, StringKeyLabel stringKeyLabel) {
checkArgumentNotNull(key);
setSecret(stringKeyLabel.getLabel(), KeySerializer.serializeString(key));
return this;
}
private KmsUpdater setPublicKey(PGPPublicKey publicKey, PublicKeyLabel publicKeyLabel)
throws IOException {
private SecretManagerKeyringUpdater setPublicKey(
PGPPublicKey publicKey, PublicKeyLabel publicKeyLabel) throws IOException {
checkArgumentNotNull(publicKey);
setSecret(publicKeyLabel.getLabel(), KeySerializer.serializePublicKey(publicKey));
return this;
}
private KmsUpdater setKeyPair(
private SecretManagerKeyringUpdater setKeyPair(
PGPKeyPair keyPair, PrivateKeyLabel privateKeyLabel, PublicKeyLabel publicKeyLabel)
throws IOException, PGPException {
checkArgumentNotNull(keyPair);

View file

@ -33,7 +33,7 @@ import google.registry.groups.GroupssettingsModule;
import google.registry.keyring.KeyringModule;
import google.registry.keyring.api.DummyKeyringModule;
import google.registry.keyring.api.KeyModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.keyring.secretmanager.SecretManagerKeyringModule;
import google.registry.module.backend.BackendRequestComponent.BackendRequestComponentModule;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.persistence.PersistenceModule;
@ -70,9 +70,9 @@ import javax.inject.Singleton;
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
NetHttpTransportModule.class,
PersistenceModule.class,
SecretManagerKeyringModule.class,
SecretManagerModule.class,
ServerTridProviderModule.class,
SheetsServiceModule.class,

View file

@ -28,7 +28,7 @@ import google.registry.groups.GroupssettingsModule;
import google.registry.keyring.KeyringModule;
import google.registry.keyring.api.DummyKeyringModule;
import google.registry.keyring.api.KeyModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.keyring.secretmanager.SecretManagerKeyringModule;
import google.registry.module.frontend.FrontendRequestComponent.FrontendRequestComponentModule;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
@ -59,8 +59,8 @@ import javax.inject.Singleton;
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
NetHttpTransportModule.class,
SecretManagerKeyringModule.class,
SecretManagerModule.class,
ServerTridProviderModule.class,
StackdriverModule.class,

View file

@ -27,7 +27,7 @@ import google.registry.groups.GroupssettingsModule;
import google.registry.keyring.KeyringModule;
import google.registry.keyring.api.DummyKeyringModule;
import google.registry.keyring.api.KeyModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.keyring.secretmanager.SecretManagerKeyringModule;
import google.registry.module.pubapi.PubApiRequestComponent.PubApiRequestComponentModule;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.persistence.PersistenceModule;
@ -54,10 +54,10 @@ import javax.inject.Singleton;
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
NetHttpTransportModule.class,
PersistenceModule.class,
PubApiRequestComponentModule.class,
SecretManagerKeyringModule.class,
SecretManagerModule.class,
ServerTridProviderModule.class,
StackdriverModule.class,

View file

@ -29,7 +29,7 @@ import google.registry.groups.GroupssettingsModule;
import google.registry.keyring.KeyringModule;
import google.registry.keyring.api.DummyKeyringModule;
import google.registry.keyring.api.KeyModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.keyring.secretmanager.SecretManagerKeyringModule;
import google.registry.module.tools.ToolsRequestComponent.ToolsRequestComponentModule;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
@ -57,8 +57,8 @@ import javax.inject.Singleton;
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
NetHttpTransportModule.class,
SecretManagerKeyringModule.class,
SecretManagerModule.class,
ServerTridProviderModule.class,
StackdriverModule.class,

View file

@ -17,7 +17,7 @@ package google.registry.persistence;
import dagger.Component;
import google.registry.config.CredentialModule;
import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.keyring.secretmanager.SecretManagerKeyringModule;
import google.registry.persistence.PersistenceModule.AppEngineJpaTm;
import google.registry.persistence.PersistenceModule.ReadOnlyReplicaJpaTm;
import google.registry.persistence.transaction.JpaTransactionManager;
@ -32,8 +32,8 @@ import javax.persistence.EntityManagerFactory;
modules = {
ConfigModule.class,
CredentialModule.class,
KmsModule.class,
PersistenceModule.class,
SecretManagerKeyringModule.class,
SecretManagerModule.class,
UtilsModule.class
})

View file

@ -29,12 +29,11 @@ import google.registry.dns.writer.dnsupdate.DnsUpdateWriterModule;
import google.registry.keyring.KeyringModule;
import google.registry.keyring.api.DummyKeyringModule;
import google.registry.keyring.api.KeyModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.keyring.secretmanager.SecretManagerKeyringModule;
import google.registry.persistence.PersistenceModule;
import google.registry.persistence.PersistenceModule.NomulusToolJpaTm;
import google.registry.persistence.PersistenceModule.ReadOnlyReplicaJpaTm;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.rde.RdeModule;
import google.registry.request.Modules.GsonModule;
import google.registry.request.Modules.UrlConnectionServiceModule;
@ -70,13 +69,13 @@ import javax.inject.Singleton;
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
SecretManagerKeyringModule.class,
LocalCredentialModule.class,
PersistenceModule.class,
RdeModule.class,
RegistryToolDataflowModule.class,
RequestFactoryModule.class,
SecretManagerModule.class,
google.registry.privileges.secretmanager.SecretManagerModule.class,
UrlConnectionServiceModule.class,
UrlFetchServiceModule.class,
UserServiceModule.class,

View file

@ -20,7 +20,7 @@ import static google.registry.keyring.api.KeySerializer.deserializeString;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import google.registry.keyring.kms.KmsUpdater;
import google.registry.keyring.secretmanager.SecretManagerKeyringUpdater;
import google.registry.tools.params.KeyringKeyName;
import google.registry.tools.params.PathParameter;
import java.nio.file.Files;
@ -33,7 +33,7 @@ import javax.inject.Inject;
@Parameters(separators = " =", commandDescription = "Update values of secret in the keyring.")
final class UpdateKeyringSecretCommand implements CommandWithRemoteApi {
@Inject KmsUpdater kmsUpdater;
@Inject SecretManagerKeyringUpdater secretManagerKeyringUpdater;
@Inject
UpdateKeyringSecretCommand() {}
@ -55,35 +55,35 @@ final class UpdateKeyringSecretCommand implements CommandWithRemoteApi {
switch (keyringKeyName) {
case BRDA_RECEIVER_PUBLIC_KEY:
kmsUpdater.setBrdaReceiverPublicKey(deserializePublicKey(input));
secretManagerKeyringUpdater.setBrdaReceiverPublicKey(deserializePublicKey(input));
break;
case BRDA_SIGNING_KEY_PAIR:
kmsUpdater.setBrdaSigningKey(deserializeKeyPair(input));
secretManagerKeyringUpdater.setBrdaSigningKey(deserializeKeyPair(input));
break;
case BRDA_SIGNING_PUBLIC_KEY:
throw new IllegalArgumentException(
"Can't update BRDA_SIGNING_PUBLIC_KEY directly."
+ " Must update public and private keys together using BRDA_SIGNING_KEY_PAIR.");
case ICANN_REPORTING_PASSWORD:
kmsUpdater.setIcannReportingPassword(deserializeString(input));
secretManagerKeyringUpdater.setIcannReportingPassword(deserializeString(input));
break;
case JSON_CREDENTIAL:
kmsUpdater.setJsonCredential(deserializeString(input));
secretManagerKeyringUpdater.setJsonCredential(deserializeString(input));
break;
case MARKSDB_DNL_LOGIN_AND_PASSWORD:
kmsUpdater.setMarksdbDnlLoginAndPassword(deserializeString(input));
secretManagerKeyringUpdater.setMarksdbDnlLoginAndPassword(deserializeString(input));
break;
case MARKSDB_LORDN_PASSWORD:
kmsUpdater.setMarksdbLordnPassword(deserializeString(input));
secretManagerKeyringUpdater.setMarksdbLordnPassword(deserializeString(input));
break;
case MARKSDB_SMDRL_LOGIN_AND_PASSWORD:
kmsUpdater.setMarksdbSmdrlLoginAndPassword(deserializeString(input));
secretManagerKeyringUpdater.setMarksdbSmdrlLoginAndPassword(deserializeString(input));
break;
case RDE_RECEIVER_PUBLIC_KEY:
kmsUpdater.setRdeReceiverPublicKey(deserializePublicKey(input));
secretManagerKeyringUpdater.setRdeReceiverPublicKey(deserializePublicKey(input));
break;
case RDE_SIGNING_KEY_PAIR:
kmsUpdater.setRdeSigningKey(deserializeKeyPair(input));
secretManagerKeyringUpdater.setRdeSigningKey(deserializeKeyPair(input));
break;
case RDE_SIGNING_PUBLIC_KEY:
throw new IllegalArgumentException(
@ -95,16 +95,16 @@ final class UpdateKeyringSecretCommand implements CommandWithRemoteApi {
//
// Hence we can and need to update the private and public keys individually.
case RDE_SSH_CLIENT_PRIVATE_KEY:
kmsUpdater.setRdeSshClientPrivateKey(deserializeString(input));
secretManagerKeyringUpdater.setRdeSshClientPrivateKey(deserializeString(input));
break;
case RDE_SSH_CLIENT_PUBLIC_KEY:
kmsUpdater.setRdeSshClientPublicKey(deserializeString(input));
secretManagerKeyringUpdater.setRdeSshClientPublicKey(deserializeString(input));
break;
case RDE_STAGING_KEY_PAIR:
kmsUpdater.setRdeStagingKey(deserializeKeyPair(input));
secretManagerKeyringUpdater.setRdeStagingKey(deserializeKeyPair(input));
break;
case SAFE_BROWSING_API_KEY:
kmsUpdater.setSafeBrowsingAPIKey(deserializeString(input));
secretManagerKeyringUpdater.setSafeBrowsingAPIKey(deserializeString(input));
break;
case RDE_STAGING_PUBLIC_KEY:
throw new IllegalArgumentException(
@ -112,7 +112,7 @@ final class UpdateKeyringSecretCommand implements CommandWithRemoteApi {
+ " Must update public and private keys together using RDE_STAGING_KEY_PAIR.");
}
kmsUpdater.update();
secretManagerKeyringUpdater.update();
}
}

View file

@ -1,171 +0,0 @@
// Copyright 2017 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.keyring.kms;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.LowLevelHttpRequest;
import com.google.api.client.http.LowLevelHttpResponse;
import com.google.api.client.json.jackson2.JacksonFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/** A helper to create instances of {@link GoogleJsonResponseException}. */
public class GoogleJsonResponseExceptionHelper {
/**
* @param statusCode the status code that should be in the returned {@link
* GoogleJsonResponseException}
* @return a {@link GoogleJsonResponseException} with the status code {@code statusCode}
* @throws IOException shouldn't occur
*/
public static GoogleJsonResponseException create(int statusCode) throws IOException {
HttpResponse response = createHttpResponse(statusCode, null);
return GoogleJsonResponseException.from(new JacksonFactory(), response);
}
public static HttpResponse createHttpResponse(int statusCode, InputStream content)
throws IOException {
FakeHttpTransport transport = new FakeHttpTransport(statusCode, content);
HttpRequestFactory factory = transport.createRequestFactory();
HttpRequest request =
factory.buildRequest(
"foo", new GenericUrl("http://example.com/bar"), new EmptyHttpContent());
request.setThrowExceptionOnExecuteError(false);
return request.execute();
}
private static class FakeHttpTransport extends HttpTransport {
private final int statusCode;
private final InputStream content;
FakeHttpTransport(int statusCode, InputStream content) {
this.statusCode = statusCode;
this.content = content;
}
@Override
protected LowLevelHttpRequest buildRequest(String method, String url) {
return new FakeLowLevelHttpRequest(statusCode, content);
}
}
private static class FakeLowLevelHttpRequest extends LowLevelHttpRequest {
private final int statusCode;
private final InputStream content;
FakeLowLevelHttpRequest(int statusCode, InputStream content) {
this.statusCode = statusCode;
this.content = content;
}
@Override
public void addHeader(String name, String value) {
// Nothing!
}
@Override
public LowLevelHttpResponse execute() {
return new FakeLowLevelHttpResponse(statusCode, content);
}
}
private static class FakeLowLevelHttpResponse extends LowLevelHttpResponse {
private final int statusCode;
private final InputStream content;
FakeLowLevelHttpResponse(int statusCode, InputStream content) {
this.statusCode = statusCode;
this.content = content;
}
@Override
public InputStream getContent() {
return content;
}
@Override
public String getContentEncoding() {
return null;
}
@Override
public long getContentLength() {
return 0;
}
@Override
public String getContentType() {
return "text/json";
}
@Override
public String getStatusLine() {
return null;
}
@Override
public int getStatusCode() {
return statusCode;
}
@Override
public String getReasonPhrase() {
return null;
}
@Override
public int getHeaderCount() {
return 0;
}
@Override
public String getHeaderName(int index) {
return null;
}
@Override
public String getHeaderValue(int index) {
return null;
}
}
private static class EmptyHttpContent implements HttpContent {
@Override
public long getLength() {
return 0;
}
@Override
public String getType() {
return "text/json";
}
@Override
public boolean retrySupported() {
return false;
}
@Override
public void writeTo(OutputStream out) {
// Nothing!
}
}
}

View file

@ -1,226 +0,0 @@
// Copyright 2017 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.keyring.kms;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.services.cloudkms.v1.CloudKMS;
import com.google.api.services.cloudkms.v1.model.CryptoKey;
import com.google.api.services.cloudkms.v1.model.CryptoKeyVersion;
import com.google.api.services.cloudkms.v1.model.DecryptRequest;
import com.google.api.services.cloudkms.v1.model.DecryptResponse;
import com.google.api.services.cloudkms.v1.model.EncryptRequest;
import com.google.api.services.cloudkms.v1.model.EncryptResponse;
import com.google.api.services.cloudkms.v1.model.KeyRing;
import com.google.api.services.cloudkms.v1.model.UpdateCryptoKeyPrimaryVersionRequest;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import google.registry.util.Retrier;
import java.io.ByteArrayInputStream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
/** Unit tests for {@link KmsConnectionImpl}. */
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class KmsConnectionImplTest {
@Mock private CloudKMS kms;
@Mock private CloudKMS.Projects kmsProjects;
@Mock private CloudKMS.Projects.Locations kmsLocations;
@Mock private CloudKMS.Projects.Locations.KeyRings kmsKeyRings;
@Mock private CloudKMS.Projects.Locations.KeyRings.Get kmsKeyRingsGet;
@Mock private CloudKMS.Projects.Locations.KeyRings.Create kmsKeyRingsCreate;
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys kmsCryptoKeys;
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Get kmsCryptoKeysGet;
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Create kmsCryptoKeysCreate;
@Mock
private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.UpdatePrimaryVersion updatePrimaryVersion;
@Mock
private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions kmsCryptoKeyVersions;
@Mock
private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions.Create
kmsCryptoKeyVersionsCreate;
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Encrypt kmsCryptoKeysEncrypt;
@Mock private CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Decrypt kmsCryptoKeysDecrypt;
@Captor private ArgumentCaptor<KeyRing> keyRing;
@Captor private ArgumentCaptor<CryptoKey> cryptoKey;
@Captor private ArgumentCaptor<CryptoKeyVersion> cryptoKeyVersion;
@Captor private ArgumentCaptor<String> locationName;
@Captor private ArgumentCaptor<String> keyRingName;
@Captor private ArgumentCaptor<String> cryptoKeyName;
@Captor private ArgumentCaptor<EncryptRequest> encryptRequest;
@Captor private ArgumentCaptor<DecryptRequest> decryptRequest;
@Captor
private ArgumentCaptor<UpdateCryptoKeyPrimaryVersionRequest> updateCryptoKeyPrimaryVersionRequest;
private final Retrier retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
@BeforeEach
void beforeEach() throws Exception {
when(kms.projects()).thenReturn(kmsProjects);
when(kmsProjects.locations()).thenReturn(kmsLocations);
when(kmsLocations.keyRings()).thenReturn(kmsKeyRings);
when(kmsKeyRings.get(anyString())).thenReturn(kmsKeyRingsGet);
when(kmsKeyRings.create(anyString(), any(KeyRing.class))).thenReturn(kmsKeyRingsCreate);
when(kmsKeyRingsCreate.setKeyRingId(anyString())).thenReturn(kmsKeyRingsCreate);
when(kmsKeyRings.cryptoKeys()).thenReturn(kmsCryptoKeys);
when(kmsCryptoKeys.get(anyString())).thenReturn(kmsCryptoKeysGet);
when(kmsCryptoKeys.create(anyString(), any(CryptoKey.class))).thenReturn(kmsCryptoKeysCreate);
when(kmsCryptoKeysCreate.setCryptoKeyId(anyString())).thenReturn(kmsCryptoKeysCreate);
when(kmsCryptoKeys.cryptoKeyVersions()).thenReturn(kmsCryptoKeyVersions);
when(kmsCryptoKeyVersions.create(anyString(), any(CryptoKeyVersion.class)))
.thenReturn(kmsCryptoKeyVersionsCreate);
when(kmsCryptoKeyVersionsCreate.execute())
.thenReturn(new CryptoKeyVersion().setName(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION));
when(kmsCryptoKeys.encrypt(anyString(), any(EncryptRequest.class)))
.thenReturn(kmsCryptoKeysEncrypt);
when(kmsCryptoKeysEncrypt.execute())
.thenReturn(
new EncryptResponse()
.setName(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION)
.setCiphertext(KmsTestHelper.DUMMY_ENCRYPTED_VALUE));
when(kmsCryptoKeys.decrypt(anyString(), any(DecryptRequest.class)))
.thenReturn(kmsCryptoKeysDecrypt);
when(kmsCryptoKeys.updatePrimaryVersion(
anyString(), any(UpdateCryptoKeyPrimaryVersionRequest.class)))
.thenReturn(updatePrimaryVersion);
}
@Test
void test_encrypt_createsKeyRingIfNotFound() throws Exception {
when(kmsKeyRingsGet.execute()).thenThrow(createNotFoundException());
new KmsConnectionImpl("foo", "bar", retrier, kms).encrypt("key", "moo".getBytes(UTF_8));
verify(kmsKeyRings).create(locationName.capture(), keyRing.capture());
assertThat(locationName.getValue()).isEqualTo("projects/foo/locations/global");
assertThat(keyRing.getValue()).isEqualTo(new KeyRing());
verify(kmsKeyRingsCreate).setKeyRingId(keyRingName.capture());
assertThat(keyRingName.getValue()).isEqualTo("bar");
verify(kmsKeyRingsCreate).execute();
verifyEncryptKmsApiCalls(
"moo",
"projects/foo/locations/global/keyRings/bar",
"projects/foo/locations/global/keyRings/bar/cryptoKeys/key");
}
@Test
void test_encrypt_newCryptoKey() throws Exception {
when(kmsCryptoKeysGet.execute()).thenThrow(createNotFoundException());
new KmsConnectionImpl("foo", "bar", retrier, kms).encrypt("key", "moo".getBytes(UTF_8));
verify(kmsCryptoKeys).create(keyRingName.capture(), cryptoKey.capture());
assertThat(keyRingName.getValue()).isEqualTo("projects/foo/locations/global/keyRings/bar");
assertThat(cryptoKey.getValue()).isEqualTo(new CryptoKey().setPurpose("ENCRYPT_DECRYPT"));
verify(kmsCryptoKeysCreate).setCryptoKeyId(cryptoKeyName.capture());
assertThat(cryptoKeyName.getValue()).isEqualTo("key");
verify(kmsCryptoKeysCreate).execute();
verify(kmsCryptoKeyVersionsCreate, never()).execute();
verify(updatePrimaryVersion, never()).execute();
verifyEncryptKmsApiCalls(
"moo",
"projects/foo/locations/global/keyRings/bar",
"projects/foo/locations/global/keyRings/bar/cryptoKeys/key");
}
@Test
void test_encrypt() throws Exception {
new KmsConnectionImpl("foo", "bar", retrier, kms).encrypt("key", "moo".getBytes(UTF_8));
verify(kmsCryptoKeyVersions).create(cryptoKeyName.capture(), cryptoKeyVersion.capture());
assertThat(cryptoKeyName.getValue())
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/key");
verify(kmsCryptoKeys)
.updatePrimaryVersion(
cryptoKeyName.capture(), updateCryptoKeyPrimaryVersionRequest.capture());
assertThat(cryptoKeyName.getValue())
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/key");
assertThat(updateCryptoKeyPrimaryVersionRequest.getValue())
.isEqualTo(
new UpdateCryptoKeyPrimaryVersionRequest()
.setCryptoKeyVersionId(KmsTestHelper.DUMMY_CRYPTO_KEY_VERSION));
verifyEncryptKmsApiCalls(
"moo",
"projects/foo/locations/global/keyRings/bar",
"projects/foo/locations/global/keyRings/bar/cryptoKeys/key");
}
@Test
void test_decrypt() throws Exception {
when(kmsCryptoKeysDecrypt.execute())
.thenReturn(new DecryptResponse().encodePlaintext("moo".getBytes(UTF_8)));
byte[] plaintext = new KmsConnectionImpl("foo", "bar", retrier, kms).decrypt("key", "blah");
verify(kmsCryptoKeys).decrypt(cryptoKeyName.capture(), decryptRequest.capture());
assertThat(cryptoKeyName.getValue())
.isEqualTo("projects/foo/locations/global/keyRings/bar/cryptoKeys/key");
assertThat(decryptRequest.getValue()).isEqualTo(new DecryptRequest().setCiphertext("blah"));
assertThat(plaintext).isEqualTo("moo".getBytes(UTF_8));
}
private void verifyEncryptKmsApiCalls(
String goldenValue, String goldenCryptoKeyRingName, String goldenCryptoKeyName)
throws Exception {
verify(kmsKeyRings).get(keyRingName.capture());
assertThat(keyRingName.getValue()).isEqualTo(goldenCryptoKeyRingName);
verify(kmsCryptoKeys).get(cryptoKeyName.capture());
assertThat(cryptoKeyName.getValue()).isEqualTo(goldenCryptoKeyName);
verify(kmsCryptoKeys).encrypt(cryptoKeyName.capture(), encryptRequest.capture());
assertThat(cryptoKeyName.getValue()).isEqualTo(goldenCryptoKeyName);
assertThat(encryptRequest.getValue())
.isEqualTo(new EncryptRequest().encodePlaintext(goldenValue.getBytes(UTF_8)));
}
private static GoogleJsonResponseException createNotFoundException() throws Exception {
ByteArrayInputStream inputStream = new ByteArrayInputStream("".getBytes(UTF_8));
HttpResponse response = GoogleJsonResponseExceptionHelper.createHttpResponse(404, inputStream);
HttpResponseException.Builder httpResponseExceptionBuilder =
new HttpResponseException.Builder(response);
httpResponseExceptionBuilder.setStatusCode(404);
httpResponseExceptionBuilder.setStatusMessage("NOT_FOUND");
return new GoogleJsonResponseException(httpResponseExceptionBuilder, null);
}
}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.keyring.kms;
package google.registry.keyring.secretmanager;
import static com.google.common.io.Resources.getResource;
@ -26,15 +26,16 @@ import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
/** Stores dummy values for test use in {@link KmsUpdaterTest}. */
final class KmsTestHelper {
/** Stores dummy values for test use in {@link SecretManagerKeyringUpdaterTest}. */
final class SecretManagerKeyringTestHelper {
static final String DUMMY_CRYPTO_KEY_VERSION = "cheeseburger";
static final String DUMMY_ENCRYPTED_VALUE = "meow";
/** The contents of a dummy PGP private key stored in a file. */
private static final ByteSource PGP_PRIVATE_KEYRING =
Resources.asByteSource(getResource(KmsTestHelper.class, "pgp-private-keyring-registry.asc"));
Resources.asByteSource(
getResource(SecretManagerKeyringTestHelper.class, "pgp-private-keyring-registry.asc"));
private static BcPGPSecretKeyRing getPrivateKeyring() throws Exception {
return new BcPGPSecretKeyRing(PGPUtil.getDecoderStream(PGP_PRIVATE_KEYRING.openStream()));
@ -53,5 +54,5 @@ final class KmsTestHelper {
.build(new char[0])));
}
private KmsTestHelper() {}
private SecretManagerKeyringTestHelper() {}
}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.keyring.kms;
package google.registry.keyring.secretmanager;
import static com.google.common.truth.Truth.assertThat;
@ -28,22 +28,21 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link KmsKeyring} and {@link KmsUpdater} */
// TODO(2021-07-01): Rename this class along with KmsKeyring
public class KmsUpdaterTest {
/** Unit tests for {@link SecretManagerKeyring} and {@link SecretManagerKeyringUpdater} */
public class SecretManagerKeyringUpdaterTest {
@RegisterExtension
public final BouncyCastleProviderExtension bouncy = new BouncyCastleProviderExtension();
private KeyringSecretStore secretStore;
private KmsUpdater updater;
private KmsKeyring keyring;
private SecretManagerKeyringUpdater updater;
private SecretManagerKeyring keyring;
@BeforeEach
void beforeEach() {
secretStore = new KeyringSecretStore(new FakeSecretManagerClient());
updater = new KmsUpdater(secretStore);
keyring = new KmsKeyring(secretStore);
updater = new SecretManagerKeyringUpdater(secretStore);
keyring = new SecretManagerKeyring(secretStore);
}
@Test
@ -66,7 +65,7 @@ public class KmsUpdaterTest {
@Test
void brdaReceiverKey() throws Exception {
PGPPublicKey publicKey = KmsTestHelper.getPublicKey();
PGPPublicKey publicKey = SecretManagerKeyringTestHelper.getPublicKey();
updater.setBrdaReceiverPublicKey(publicKey).update();
assertThat(keyring.getBrdaReceiverKey().getFingerprint()).isEqualTo(publicKey.getFingerprint());
@ -75,12 +74,14 @@ public class KmsUpdaterTest {
@Test
void brdaSigningKey() throws Exception {
PGPKeyPair keyPair = KmsTestHelper.getKeyPair();
PGPKeyPair keyPair = SecretManagerKeyringTestHelper.getKeyPair();
updater.setBrdaSigningKey(keyPair).update();
assertThat(serializeKeyPair(keyring.getBrdaSigningKey())).isEqualTo(serializeKeyPair(keyPair));
verifyPersistedSecret("brda-signing-private", serializeKeyPair(KmsTestHelper.getKeyPair()));
verifyPersistedSecret("brda-signing-public", serializePublicKey(KmsTestHelper.getPublicKey()));
verifyPersistedSecret(
"brda-signing-private", serializeKeyPair(SecretManagerKeyringTestHelper.getKeyPair()));
verifyPersistedSecret(
"brda-signing-public", serializePublicKey(SecretManagerKeyringTestHelper.getPublicKey()));
}
@Test
@ -130,16 +131,17 @@ public class KmsUpdaterTest {
@Test
void rdeReceiverKey() throws Exception {
PGPPublicKey publicKey = KmsTestHelper.getPublicKey();
PGPPublicKey publicKey = SecretManagerKeyringTestHelper.getPublicKey();
updater.setRdeReceiverPublicKey(publicKey).update();
assertThat(keyring.getRdeReceiverKey().getFingerprint()).isEqualTo(publicKey.getFingerprint());
verifyPersistedSecret("rde-receiver-public", serializePublicKey(KmsTestHelper.getPublicKey()));
verifyPersistedSecret(
"rde-receiver-public", serializePublicKey(SecretManagerKeyringTestHelper.getPublicKey()));
}
@Test
void rdeSigningKey() throws Exception {
PGPKeyPair keyPair = KmsTestHelper.getKeyPair();
PGPKeyPair keyPair = SecretManagerKeyringTestHelper.getKeyPair();
updater.setRdeSigningKey(keyPair).update();
assertThat(serializeKeyPair(keyring.getRdeSigningKey())).isEqualTo(serializeKeyPair(keyPair));
@ -168,7 +170,7 @@ public class KmsUpdaterTest {
@Test
void rdeStagingKey() throws Exception {
PGPKeyPair keyPair = KmsTestHelper.getKeyPair();
PGPKeyPair keyPair = SecretManagerKeyringTestHelper.getKeyPair();
updater.setRdeStagingKey(keyPair).update();
assertThat(serializePublicKey(keyring.getRdeStagingEncryptionKey()))
@ -181,7 +183,8 @@ public class KmsUpdaterTest {
keyring.getRdeStagingEncryptionKey(), keyring.getRdeStagingDecryptionKey())))
.isEqualTo(serializeKeyPair(keyPair));
verifyPersistedSecret("rde-staging-private", serializeKeyPair(keyPair));
verifyPersistedSecret("rde-staging-public", serializePublicKey(KmsTestHelper.getPublicKey()));
verifyPersistedSecret(
"rde-staging-public", serializePublicKey(SecretManagerKeyringTestHelper.getPublicKey()));
}
private void verifyPersistedSecret(String secretName, String expectedPlainTextValue) {

View file

@ -21,7 +21,7 @@ import dagger.Component;
import google.registry.config.CredentialModule;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.keyring.kms.KmsModule;
import google.registry.keyring.secretmanager.SecretManagerKeyringModule;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.testing.DatastoreEntityExtension;
@ -89,8 +89,8 @@ class PersistenceModuleTest {
modules = {
ConfigModule.class,
CredentialModule.class,
KmsModule.class,
PersistenceModule.class,
SecretManagerKeyringModule.class,
SecretManagerModule.class,
UtilsModule.class
})

View file

@ -164,18 +164,16 @@ allows the codebase to compile and run, but of course any actions that attempt
to connect to external services will fail because none of the keys are real.
To configure a production registry system, you will need to either use the
KmsKeyring or write your own replacement module using `DummyKeyringModule` for
guidance. Such a module should provide either an instance of
`InMemoryKeyring` or your own custom implementation of `Keyring`.
SecretManagerKeyring or write your own replacement module using
`DummyKeyringModule` for guidance. Such a module should provide either an
instance of `InMemoryKeyring` or your own custom implementation of `Keyring`.
In either case, configure the `keyring` section of the config file with the
appropriate parameters. Use an `activeKeyring` of "KMS" with a project id for
KMS to configure accordingly, for example:
appropriate parameters. Use an `activeKeyring` of "CSM" with a project id for
SecretManager to configure accordingly, for example:
keyring:
activeKeyring: KMS
kms:
projectId: acme-registry-keys
activeKeyring: CSM
## Per-TLD configuration
@ -316,35 +314,13 @@ connect to your database with gcloud:
From this, you should have a postgres prompt and be able to enter the "GRANT"
command specified above.
### Cloud KMS
### Cloud SecretManager
You'll need to choose a project to host the KMS keyring. Best practice is to
create a separate project with a strict IAM policy. However, you can use your
current project when experimenting.
You need to create a KMS keyring in the chosen project. The default keyring
name is 'nomulus', though you can override it in the config file.
$ gcloud kms keyrings create "nomulus" --location "global" \
--project $KEYS_PROJECT_ID
Next, you need to create two keys in the keyring:
$ gcloud kms keys create "cloud-sql-password-string" \
--project $KEYS_PROJECT_ID \
--location "global" \
--keyring "nomulus" \
--purpose "encryption"
$ gcloud kms keys create "tools-cloud-sql-password-string" \
--project $KEYS_PROJECT_ID \
--location "global" \
--keyring "nomulus" \
--purpose "encryption"
You'll need to enable the SecretManager API in your project.
#### Install Cloud SQL Passwords in Nomulus Server
Use the update_kms_keyring command to upload the Cloud SQL passwords to the
Use the update_keyring_secret command to upload the Cloud SQL passwords to the
Nomulus server. We'll use the password same set of passwords we specified
above when creating database user accounts. These should currently be stored
in `/tmp/server.pass`.
@ -353,12 +329,12 @@ Paste the password for the Registry server user to a file, say
/tmp/server.pass. Make sure to avoid any trailing '\n' inserted by the editor.
$ set ENV=alpha
$ nomulus -e $ENV update_kms_keyring --keyname CLOUD_SQL_PASSWORD \
$ nomulus -e $ENV update_keyring_secret --keyname CLOUD_SQL_PASSWORD \
--input /tmp/server.pass
Repeat the steps for the tools sql password:
$ nomulus -e $ENV update_kms_keyring --keyname TOOLS_CLOUD_SQL_PASSWORD \
$ nomulus -e $ENV update_keyring_secret --keyname TOOLS_CLOUD_SQL_PASSWORD \
--input /tmp/tools.pass
Use get_keyring_secret command to verify the data you put in:
@ -376,9 +352,7 @@ Use get_keyring_secret command to verify the data you put in:
instanceConnectionName: THE_NAME_SHOWN_ON_THE_DB_INFO_PAGE
keyring:
activeKeyring: KMS
kms:
projectId: KEYS_PROJECT_ID
activeKeyring: CSM
registryTool:
clientId: TOOLS_OAUTH_CLIENT_ID

View file

@ -76,7 +76,6 @@ com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0=d
com.google.apis:google-api-services-appengine:v1-rev20220612-1.32.1=default,deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20211129-1.32.1=default,deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-clouddebugger:v2-rev20210813-1.32.1=default,deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudkms:v1-rev20220701-1.32.1=default,deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20211017-1.32.1=default,deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20210818-1.32.1=default,deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v2beta1-rev99-1.25.0=default,deploy_jar,runtimeClasspath,testRuntimeClasspath

View file

@ -73,7 +73,6 @@ com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0=c
com.google.apis:google-api-services-appengine:v1-rev20220612-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20211129-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-clouddebugger:v2-rev20210813-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudkms:v1-rev20220701-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20211017-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20210818-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v2beta1-rev99-1.25.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath

View file

@ -73,7 +73,6 @@ com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0=c
com.google.apis:google-api-services-appengine:v1-rev20220612-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20211129-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-clouddebugger:v2-rev20210813-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudkms:v1-rev20220701-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20211017-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20210818-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v2beta1-rev99-1.25.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath

View file

@ -73,7 +73,6 @@ com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0=c
com.google.apis:google-api-services-appengine:v1-rev20220612-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20211129-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-clouddebugger:v2-rev20210813-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudkms:v1-rev20220701-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20211017-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20210818-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v2beta1-rev99-1.25.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath

View file

@ -73,7 +73,6 @@ com.google.apis:google-api-services-admin-directory:directory_v1-rev118-1.25.0=c
com.google.apis:google-api-services-appengine:v1-rev20220612-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20211129-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-clouddebugger:v2-rev20210813-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudkms:v1-rev20220701-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20211017-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20210818-1.32.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v2beta1-rev99-1.25.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath