This is an automated email from the ASF dual-hosted git repository. dhavalshah9131 pushed a commit to reference refs/for/master in repository https://gitbox.apache.org/repos/asf/ranger.git
commit 5221f83db91e89fcbb3df5a66021362c848fbff7 Author: ZhouTianling <[email protected]> AuthorDate: Sat Jan 22 01:51:15 2022 +0800 add TencentKMS as MasterKeyProvider --- distro/src/main/assembly/kms.xml | 4 + kms/config/kms-webapp/dbks-site.xml | 35 ++++- kms/pom.xml | 5 + kms/scripts/install.properties | 7 + kms/scripts/setup.sh | 82 ++++++++---- .../key/AzureKeyVaultClientAuthenticator.java | 33 ++--- .../hadoop/crypto/key/DBToAzureKeyVault.java | 2 +- .../apache/hadoop/crypto/key/JKS2RangerUtil.java | 4 +- ...r.java => RangerAzureKeyVaultKeyGenerator.java} | 78 +++++++++-- .../apache/hadoop/crypto/key/RangerKeyStore.java | 23 ++-- .../hadoop/crypto/key/RangerKeyStoreProvider.java | 148 ++++++++------------- .../crypto/key/RangerTencentKMSProvider.java | 135 +++++++++++++++++++ pom.xml | 6 +- 13 files changed, 402 insertions(+), 160 deletions(-) diff --git a/distro/src/main/assembly/kms.xml b/distro/src/main/assembly/kms.xml index 983a43e..f760507 100755 --- a/distro/src/main/assembly/kms.xml +++ b/distro/src/main/assembly/kms.xml @@ -195,6 +195,10 @@ <include>io.grpc:grpc-xds</include> <include>io.grpc:grpc-services</include> <include>io.opencensus:opencensus-proto</include> + <!-- TencentKMS --> + <include>com.tencentcloudapi:tencentcloud-sdk-java</include> + <include>com.squareup.okhttp:logging-interceptor</include> + <include>com.squareup.okhttp:okhttp</include> </includes> </binaries> </moduleSet> diff --git a/kms/config/kms-webapp/dbks-site.xml b/kms/config/kms-webapp/dbks-site.xml index 07de4d4..258d331 100755 --- a/kms/config/kms-webapp/dbks-site.xml +++ b/kms/config/kms-webapp/dbks-site.xml @@ -327,7 +327,40 @@ </property> <!-- Google Cloud KMS end --> - <!-- HSM Config --> + <!--Tencent KMS START--> + <property> + <name>ranger.kms.tencentkms.enabled</name> + <value>false</value> + <description>Flag for Tencent KMS</description> + </property> + <property> + <name>ranger.kms.tencent.client.id</name> + <value></value> + <description>Tencent Client Id</description> + </property> + <property> + <name>ranger.kms.tencent.client.secret</name> + <value></value> + <description>Tencent Client Secret</description> + </property> + <property> + <name>ranger.kms.tencent.client.secret.alias</name> + <value>ranger.ks.tencent.client.secret</value> + <description>Tencent Client Secret Alias</description> + </property> + <property> + <name>ranger.kms.tencent.client.region</name> + <value>ap-beijing</value> + <description>Tencent Client Id</description> + </property> + <property> + <name>ranger.kms.tencent.masterkey.id</name> + <value></value> + <description>Tencent master key name</description> + </property> + <!--Tencent KMS END--> + + <!-- HSM Config --> <property> <name>ranger.ks.hsm.type</name> <value>LunaProvider</value> diff --git a/kms/pom.xml b/kms/pom.xml index 7a4f98d..ade563f 100644 --- a/kms/pom.xml +++ b/kms/pom.xml @@ -456,6 +456,11 @@ </exclusions> </dependency> <dependency> + <groupId>com.tencentcloudapi</groupId> + <artifactId>tencentcloud-sdk-java</artifactId> + <version>${com.tencentcloudapi.sdk.version}</version> + </dependency> + <dependency> <groupId>io.reactivex</groupId> <artifactId>rxjava</artifactId> <version>${io.reactivex.rxjava.version}</version> diff --git a/kms/scripts/install.properties b/kms/scripts/install.properties index 31143d3..780509d 100755 --- a/kms/scripts/install.properties +++ b/kms/scripts/install.properties @@ -132,6 +132,13 @@ GCP_PROJECT_ID= GCP_LOCATION_ID= GCP_MASTER_KEY_NAME=MyMasterKeyNameChangeIt +#------------------------- Ranger Tencent KMS ------------------------------ +TENCENT_KMS_ENABLED=false +TENCENT_MASTERKEY_ID=b756b016-6e11-11ec-a735-525400fe0300 +TENCENT_CLIENT_ID=AKIDrXx6ybx2qNdiaBWaNs76pGQJvFJ6crpW +TENCENT_CLIENT_SECRET=<TencentSecretKey> +TENCENT_CLIENT_REGION=ap-beijing + # ------- UNIX User CONFIG ---------------- # unix_user=kms diff --git a/kms/scripts/setup.sh b/kms/scripts/setup.sh index 2051df5..448042e 100755 --- a/kms/scripts/setup.sh +++ b/kms/scripts/setup.sh @@ -121,6 +121,12 @@ GCP_PROJECT_ID=$(get_prop 'GCP_PROJECT_ID' $PROPFILE) GCP_LOCATION_ID=$(get_prop 'GCP_LOCATION_ID' $PROPFILE) GCP_MASTER_KEY_NAME=$(get_prop 'GCP_MASTER_KEY_NAME' $PROPFILE) +TENCENT_KMS_ENABLED=$(get_prop 'TENCENT_KMS_ENABLED' $PROPFILE) +TENCENT_MASTERKEY_ID=$(get_prop 'TENCENT_MASTERKEY_ID' $PROPFILE) +TENCENT_CLIENT_ID=$(get_prop 'TENCENT_CLIENT_ID' $PROPFILE) +TENCENT_CLIENT_SECRET=$(get_prop 'TENCENT_CLIENT_SECRET' $PROPFILE) +TENCENT_CLIENT_REGION=$(get_prop 'TENCENT_CLIENT_REGION' $PROPFILE) + kms_principal=$(get_prop 'kms_principal' $PROPFILE) kms_keytab=$(get_prop 'kms_keytab' $PROPFILE) hadoop_conf=$(get_prop 'hadoop_conf' $PROPFILE) @@ -235,26 +241,6 @@ password_validation(){ fi } -password_validation_safenet_keysecure(){ - if [ -z "$1" ] - then - log "[I] Blank password is not allowed for" $2". Please enter valid password." - exit 1 - else - log "[I]" $2 "password validated." - fi -} - -azure_client_secret_validation(){ - if [ -z "$1" ] - then - log "[I] Blank password is not allowed for" $2". Please enter valid password." - exit 1 - else - log "[I]" $2 "password validated." - fi -} - init_variables(){ curDt=`date '+%Y%m%d%H%M%S'` @@ -481,8 +467,10 @@ setup_kms(){ checkIfEmpty() { if [ -z "$1" ] then - echo "Error - Since GCP is enabled, Please provide valid value for '$2', Found : '$1'"; + log "[I] - Please provide valid value for '$2', Found : '$1'"; exit 1 + else + log "[I] - '$2' validated"; fi } @@ -654,11 +642,15 @@ update_properties() { AZURE_CLIENT_SEC="ranger.kms.azure.client.secret" AZURE_CLIENT_SECRET_ALIAS="ranger.ks.azure.client.secret" + TENCENT_CLIENT_SEC="ranger.kms.tencent.client.secret" + TENCENT_CLIENT_SECRET_ALIAS="ranger.ks.tencent.client.secret" + HSM_ENABLED=`echo $HSM_ENABLED | tr '[:lower:]' '[:upper:]'` KEYSECURE_ENABLED=`echo $KEYSECURE_ENABLED | tr '[:lower:]' '[:upper:]'` AZURE_KEYVAULT_ENABLED=`echo $AZURE_KEYVAULT_ENABLED | tr '[:lower:]' '[:upper:]'` IS_GCP_ENABLED=`echo $IS_GCP_ENABLED | tr '[:lower:]' '[:upper:]'` + TENCENT_KMS_ENABLED=`echo $TENCENT_KMS_ENABLED | tr '[:lower:]' '[:upper:]'` if [ "${keystore}" != "" ] then @@ -686,7 +678,7 @@ update_properties() { if [ "${KEYSECURE_ENABLED}" == "TRUE" ] then - password_validation_safenet_keysecure "$KEYSECURE_PASSWORD" "KEYSECURE User Password" + checkIfEmpty "$KEYSECURE_PASSWORD" "KEYSECURE User Password" $PYTHON_COMMAND_INVOKER ranger_credential_helper.py -l "cred/lib/*" -f "$keystore" -k "${KEYSECURE_PASSWORD_ALIAS}" -v "${KEYSECURE_PASSWORD}" -c 1 propertyName=ranger.kms.keysecure.login.password.alias @@ -700,7 +692,7 @@ update_properties() { if [ "${AZURE_KEYVAULT_ENABLED}" == "TRUE" ] then - azure_client_secret_validation "$AZURE_CLIENT_SECRET" "Azure Client Secret" + checkIfEmpty "$AZURE_CLIENT_SECRET" "Azure Client Secret" $PYTHON_COMMAND_INVOKER ranger_credential_helper.py -l "cred/lib/*" -f "$keystore" -k "${AZURE_CLIENT_SECRET_ALIAS}" -v "${AZURE_CLIENT_SECRET}" -c 1 propertyName=ranger.kms.azure.client.secret.alias @@ -712,6 +704,19 @@ update_properties() { updatePropertyToFilePy $propertyName $newPropertyValue $to_file fi + if [ "$TENCENT_KMS_ENABLED" == "TRUE" ] + then + checkIfEmpty "$TENCENT_CLIENT_SECRET" "Tencent Client Secret" + $PYTHON_COMMAND_INVOKER ranger_credential_helper.py -l "cred/lib/*" -f "$keystore" -k "${TENCENT_CLIENT_SECRET_ALIAS}" -v "${TENCENT_CLIENT_SECRET}" -c 1 + + propertyName=ranger.kms.tencent.client.secret.alias + newPropertyValue="${TENCENT_CLIENT_SECRET_ALIAS}" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + + propertyName=ranger.kms.tencent.client.secret + newPropertyValue="_" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + fi propertyName=ranger.ks.jpa.jdbc.credential.alias newPropertyValue="${DB_CREDENTIAL_ALIAS}" @@ -753,6 +758,10 @@ update_properties() { newPropertyValue="${AZURE_CLIENT_SECRET}" updatePropertyToFilePy $propertyName $newPropertyValue $to_file + propertyName="${TENCENT_CLIENT_SEC}" + newPropertyValue="${TENCENT_CLIENT_SECRET}" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + fi if test -f $keystore; then @@ -945,6 +954,33 @@ update_properties() { updatePropertyToFilePy $propertyName $newPropertyValue $to_file fi + ########### TENCENT KEY VAULT ################# + + + if [ "${TENCENT_KMS_ENABLED}" != "TRUE" ] + then + propertyName=ranger.kms.tencentkms.enabled + newPropertyValue="false" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + else + propertyName=ranger.kms.tencentkms.enabled + newPropertyValue="true" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + + propertyName=ranger.kms.tencent.client.id + newPropertyValue="${TENCENT_CLIENT_ID}" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + + propertyName=ranger.kms.client.region + newPropertyValue="${TENCENT_CLIENT_REGION}" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + + propertyName=ranger.kms.tencent.masterkey.id + newPropertyValue="${TENCENT_MASTERKEY_ID}" + updatePropertyToFilePy $propertyName $newPropertyValue $to_file + + fi + to_file_kms_site=$PWD/ews/webapp/WEB-INF/classes/conf/ranger-kms-site.xml if test -f $to_file_kms_site; then log "[I] $to_file_kms_site file found" diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/AzureKeyVaultClientAuthenticator.java b/kms/src/main/java/org/apache/hadoop/crypto/key/AzureKeyVaultClientAuthenticator.java index f96cbb7..13897ac 100644 --- a/kms/src/main/java/org/apache/hadoop/crypto/key/AzureKeyVaultClientAuthenticator.java +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/AzureKeyVaultClientAuthenticator.java @@ -236,26 +236,27 @@ public class AzureKeyVaultClientAuthenticator extends KeyVaultCredentials { return keyCert; } } -} -class KeyCert { + static class KeyCert { - X509Certificate certificate; - PrivateKey key; + X509Certificate certificate; + PrivateKey key; - public X509Certificate getCertificate() { - return certificate; - } + public X509Certificate getCertificate() { + return certificate; + } - public void setCertificate(X509Certificate certificate) { - this.certificate = certificate; - } + public void setCertificate(X509Certificate certificate) { + this.certificate = certificate; + } - public PrivateKey getKey() { - return key; - } + public PrivateKey getKey() { + return key; + } - public void setKey(PrivateKey key) { - this.key = key; + public void setKey(PrivateKey key) { + this.key = key; + } } -} \ No newline at end of file +} + diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/DBToAzureKeyVault.java b/kms/src/main/java/org/apache/hadoop/crypto/key/DBToAzureKeyVault.java index bacc928..39de0a5 100644 --- a/kms/src/main/java/org/apache/hadoop/crypto/key/DBToAzureKeyVault.java +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/DBToAzureKeyVault.java @@ -178,7 +178,7 @@ public class DBToAzureKeyVault { System.err.println("Key Vault is null. Please check the azure related configs."); System.exit(1); } - RangerKeyVaultKeyGenerator rangerKVKeyGenerator = new RangerKeyVaultKeyGenerator( + RangerKMSMKI rangerKVKeyGenerator = new RangerAzureKeyVaultKeyGenerator( conf, kvClient); boolean azureMKSuccess = rangerKVKeyGenerator.generateMasterKey(mKeyPass); if (azureMKSuccess) { diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/JKS2RangerUtil.java b/kms/src/main/java/org/apache/hadoop/crypto/key/JKS2RangerUtil.java index 4324439..a1a6f34 100644 --- a/kms/src/main/java/org/apache/hadoop/crypto/key/JKS2RangerUtil.java +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/JKS2RangerUtil.java @@ -159,7 +159,7 @@ public class JKS2RangerUtil { } String azureClientSecret = conf.get(AZURE_CLIENT_SECRET); AzureKeyVaultClientAuthenticator azureKVClientAuthenticator; - RangerKeyVaultKeyGenerator rangerKVKeyGenerator = null; + RangerKMSMKI rangerKVKeyGenerator = null; KeyVaultClient kvClient = null; if (conf != null && StringUtils.isNotEmpty(conf.get(AZURE_KEYVAULT_SSL_ENABLED)) @@ -201,7 +201,7 @@ public class JKS2RangerUtil { if (kvClient != null) { try { dbStore = new RangerKeyStore(daoManager, conf, kvClient); - rangerKVKeyGenerator = new RangerKeyVaultKeyGenerator( + rangerKVKeyGenerator = new RangerAzureKeyVaultKeyGenerator( conf, kvClient); if (rangerKVKeyGenerator != null) { success = rangerKVKeyGenerator diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyVaultKeyGenerator.java b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerAzureKeyVaultKeyGenerator.java similarity index 63% rename from kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyVaultKeyGenerator.java rename to kms/src/main/java/org/apache/hadoop/crypto/key/RangerAzureKeyVaultKeyGenerator.java index c661268..138a897 100644 --- a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyVaultKeyGenerator.java +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerAzureKeyVaultKeyGenerator.java @@ -28,19 +28,25 @@ import com.microsoft.azure.keyvault.webkey.JsonWebKeyType; import java.security.Key; +import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.joda.time.DateTime; -public class RangerKeyVaultKeyGenerator implements RangerKMSMKI { +public class RangerAzureKeyVaultKeyGenerator implements RangerKMSMKI { static final Logger logger = LoggerFactory - .getLogger(RangerKeyVaultKeyGenerator.class); - private static final String AZURE_KEYVAULT_URL = "ranger.kms.azurekeyvault.url"; - private static final String AZURE_MASTER_KEY_ALIAS = "ranger.kms.azure.masterkey.name"; - private static final String AZURE_MASTER_KEY_TYPE = "ranger.kms.azure.masterkey.type"; - private static final String ZONE_KEY_ENCRYPTION_ALGO = "ranger.kms.azure.zonekey.encryption.algorithm"; + .getLogger(RangerAzureKeyVaultKeyGenerator.class); + static final String AZURE_KEYVAULT_URL = "ranger.kms.azurekeyvault.url"; + static final String AZURE_MASTER_KEY_ALIAS = "ranger.kms.azure.masterkey.name"; + static final String AZURE_MASTER_KEY_TYPE = "ranger.kms.azure.masterkey.type"; + static final String ZONE_KEY_ENCRYPTION_ALGO = "ranger.kms.azure.zonekey.encryption.algorithm"; + static final String AZURE_KEYVAULT_SSL_ENABLED = "ranger.kms.azure.keyvault.ssl.enabled"; + static final String AZURE_CLIENT_ID = "ranger.kms.azure.client.id"; + static final String AZURE_CLIENT_SECRET = "ranger.kms.azure.client.secret"; + static final String AZURE_KEYVAULT_CERTIFICATE_PATH = "ranger.kms.azure.keyvault.certificate.path"; + static final String AZURE_KEYVAULT_CERTIFICATE_PASSWORD = "ranger.kms.azure.keyvault.certificate.password"; private String keyVaultURL; private String azureMasterKey; private String azureMasterKeyType; @@ -48,8 +54,8 @@ public class RangerKeyVaultKeyGenerator implements RangerKMSMKI { private KeyVaultClient keyVaultClient; private KeyBundle masterKeyBundle; - public RangerKeyVaultKeyGenerator(Configuration conf, - KeyVaultClient kvClient) { + public RangerAzureKeyVaultKeyGenerator(Configuration conf, + KeyVaultClient kvClient) { this.keyVaultURL = conf.get(AZURE_KEYVAULT_URL); this.azureMasterKey = conf.get(AZURE_MASTER_KEY_ALIAS); this.azureMasterKeyType = conf.get(AZURE_MASTER_KEY_TYPE); @@ -57,6 +63,62 @@ public class RangerKeyVaultKeyGenerator implements RangerKMSMKI { this.keyVaultClient = kvClient; } + public RangerAzureKeyVaultKeyGenerator(Configuration conf) throws Exception { + this(conf, createKeyVaultClient(conf)); + } + + public static KeyVaultClient createKeyVaultClient(Configuration conf) throws Exception { + AzureKeyVaultClientAuthenticator azureKVClientAuthenticator; + String azureClientId = conf.get(AZURE_CLIENT_ID); + if (StringUtils.isEmpty(azureClientId)) { + throw new Exception( + "Azure Key Vault is enabled and client id is not configured"); + } + String azureClientSecret = conf.get(AZURE_CLIENT_SECRET); + KeyVaultClient kvClient; + if (conf != null + && StringUtils.isNotEmpty(conf.get(AZURE_KEYVAULT_SSL_ENABLED)) + && conf.get(AZURE_KEYVAULT_SSL_ENABLED).equalsIgnoreCase("false")) { + try { + if (StringUtils.isEmpty(azureClientSecret)) { + throw new Exception( + "Azure Key Vault is enabled in non SSL mode and client password/secret is not configured"); + } + azureKVClientAuthenticator = new AzureKeyVaultClientAuthenticator( + azureClientId, azureClientSecret); + kvClient = new KeyVaultClient(azureKVClientAuthenticator); + } catch (Exception ex) { + throw new Exception( + "Error while getting key vault client object with client id and client secret : " + + ex); + } + } else { + try { + azureKVClientAuthenticator = new AzureKeyVaultClientAuthenticator( + azureClientId); + String keyVaultCertPath = conf + .get(AZURE_KEYVAULT_CERTIFICATE_PATH); + if (StringUtils.isEmpty(keyVaultCertPath)) { + throw new Exception( + "Azure Key Vault is enabled in SSL mode. Please provide certificate path for authentication."); + } + String keyVaultCertPassword = conf + .get(AZURE_KEYVAULT_CERTIFICATE_PASSWORD); + + kvClient = !StringUtils.isEmpty(keyVaultCertPassword) ? azureKVClientAuthenticator + .getAuthentication(keyVaultCertPath, + keyVaultCertPassword) + : azureKVClientAuthenticator.getAuthentication( + keyVaultCertPath, ""); + } catch (Exception ex) { + throw new Exception( + "Error while getting key vault client object with client id and certificate. Error : : " + + ex); + } + } + return kvClient; + } + @Override public boolean generateMasterKey(String password) throws Exception { if (keyVaultClient == null) { diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java index 5234dc7..8dc1290 100644 --- a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java @@ -91,8 +91,7 @@ public class RangerKeyStore extends KeyStoreSpi { private static final String KEY_NAME_VALIDATION = "[a-z,A-Z,0-9](?!.*--)(?!.*__)(?!.*-_)(?!.*_-)[\\w\\-\\_]*"; private static final Pattern pattern = Pattern.compile(KEY_NAME_VALIDATION); private static final String AZURE_KEYVAULT_ENABLED = "ranger.kms.azurekeyvault.enabled"; - private boolean azureKeyVaultEnabled = false; - private boolean isGCPHSMEnabled = false; + private boolean keyVaultEnabled = false; private DaoManager daoManager; private RangerKMSMKI masterKeyProvider; @@ -126,7 +125,7 @@ public class RangerKeyStore extends KeyStoreSpi { private Map<String, Object> keyEntries = new ConcurrentHashMap<>(); private Map<String, Object> deltaEntries = new ConcurrentHashMap<>(); - RangerKeyStore() { + public RangerKeyStore() { } public RangerKeyStore(DaoManager daoManager) { @@ -135,20 +134,20 @@ public class RangerKeyStore extends KeyStoreSpi { public RangerKeyStore(DaoManager daoManager, Configuration conf, KeyVaultClient kvClient) { this.daoManager = daoManager; - this.masterKeyProvider = new RangerKeyVaultKeyGenerator(conf, kvClient); + this.masterKeyProvider = new RangerAzureKeyVaultKeyGenerator(conf, kvClient); if(conf != null && StringUtils.isNotEmpty(conf .get(AZURE_KEYVAULT_ENABLED)) && conf.get(AZURE_KEYVAULT_ENABLED).equalsIgnoreCase( "true")){ - azureKeyVaultEnabled = true; + keyVaultEnabled = true; } } - public RangerKeyStore(DaoManager daoManager, boolean isGcpEnabled, RangerKMSMKI rangerGCPProvider) { + public RangerKeyStore(DaoManager daoManager, boolean keyVaultEnabled, RangerKMSMKI masterKeyProvider) { this.daoManager = daoManager; - this.masterKeyProvider = rangerGCPProvider; - this.isGCPHSMEnabled = isGcpEnabled; + this.masterKeyProvider = masterKeyProvider; + this.keyVaultEnabled = keyVaultEnabled; } String convertAlias(String alias) { @@ -404,7 +403,7 @@ public class RangerKeyStore extends KeyStoreSpi { logger.debug("==> RangerKeyStore.engineStore()"); } synchronized (deltaEntries) { - if (azureKeyVaultEnabled || isGCPHSMEnabled) { + if (keyVaultEnabled) { for (Entry<String, Object> entry : deltaEntries.entrySet()) { Long creationDate = ((SecretKeyByteEntry) entry.getValue()).date .getTime(); @@ -536,7 +535,7 @@ public class RangerKeyStore extends KeyStoreSpi { } keyEntries.clear(); - if (azureKeyVaultEnabled || isGCPHSMEnabled) { + if (keyVaultEnabled) { for (XXRangerKeyStore rangerKey : rangerKeyDetails) { String encodedStr = rangerKey.getEncoded(); byte[] encodedByte = DatatypeConverter @@ -739,7 +738,7 @@ public class RangerKeyStore extends KeyStoreSpi { } synchronized (deltaEntries) { KeyStore ks; - if (azureKeyVaultEnabled || isGCPHSMEnabled) { + if (keyVaultEnabled) { try { ks = KeyStore.getInstance(fileFormat); ks.load(stream, storePass); @@ -929,7 +928,7 @@ public class RangerKeyStore extends KeyStoreSpi { Key key; while (e.hasMoreElements()) { alias = e.nextElement(); - if(azureKeyVaultEnabled || isGCPHSMEnabled) { + if(keyVaultEnabled){ key = engineGetDecryptedZoneKey(alias); } else { key = engineGetKey(alias, masterKey); diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java index 74c54a7..18a6bee 100755 --- a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java @@ -17,7 +17,6 @@ package org.apache.hadoop.crypto.key; -import com.microsoft.azure.keyvault.KeyVaultClient; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -42,7 +41,6 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; -import org.apache.commons.lang.StringUtils; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.ranger.plugin.util.JsonUtilsV2; @@ -75,12 +73,11 @@ public class RangerKeyStoreProvider extends KeyProvider { private static final String KEYSECURE_PASSWORD = "ranger.kms.keysecure.login.password"; private static final String KEYSECURE_LOGIN = "ranger.kms.keysecure.login"; private static final String AZURE_KEYVAULT_ENABLED = "ranger.kms.azurekeyvault.enabled"; - private static final String AZURE_KEYVAULT_SSL_ENABLED = "ranger.kms.azure.keyvault.ssl.enabled"; - private static final String AZURE_CLIENT_ID = "ranger.kms.azure.client.id"; private static final String AZURE_CLIENT_SECRET_ALIAS = "ranger.kms.azure.client.secret.alias"; private static final String AZURE_CLIENT_SECRET = "ranger.kms.azure.client.secret"; - private static final String AZURE_KEYVAULT_CERTIFICATE_PATH = "ranger.kms.azure.keyvault.certificate.path"; - private static final String AZURE_KEYVAULT_CERTIFICATE_PASSWORD = "ranger.kms.azure.keyvault.certificate.password"; + private static final String TENCENT_KMS_ENABLED = "ranger.kms.tencentkms.enabled"; + private static final String TENCENT_CLIENT_SECRET = RangerTencentKMSProvider.TENCENT_CLIENT_SECRET; + private static final String TENCENT_CLIENT_SECRET_ALIAS = "ranger.kms.tencent.client.secret.alias"; private static final String IS_GCP_ENABLED = "ranger.kms.gcp.enabled"; private RangerKeyStore dbStore; private char[] masterKey; @@ -88,10 +85,7 @@ public class RangerKeyStoreProvider extends KeyProvider { private final Map<String, Metadata> cache = new HashMap<String, Metadata>(); private DaoManager daoManager; private Lock readLock; - private boolean isHSMEnabled = false; - private boolean azureKeyVaultEnabled = false; - private boolean isGCPEnabled = false; - private boolean isKeySecureEnabled = false; + private boolean keyVaultEnabled = false; public RangerKeyStoreProvider(Configuration conf) throws Throwable { super(conf); @@ -115,12 +109,14 @@ public class RangerKeyStoreProvider extends KeyProvider { "The Ranger MasterKey Password is empty or not a valid Password"); } - this.isHSMEnabled = conf.getBoolean(HSM_ENABLED, false); - this.azureKeyVaultEnabled = conf.getBoolean(AZURE_KEYVAULT_ENABLED, false); - this.isGCPEnabled = conf.getBoolean(IS_GCP_ENABLED, false); - this.isKeySecureEnabled = conf.getBoolean(KEYSECURE_ENABLED, false); + boolean isHSMEnabled = conf.getBoolean(HSM_ENABLED, false); + boolean isKeySecureEnabled = conf.getBoolean(KEYSECURE_ENABLED, false); + boolean isAzureKeyVaultEnabled = conf.getBoolean(AZURE_KEYVAULT_ENABLED, false); + boolean isGCPEnabled = conf.getBoolean(IS_GCP_ENABLED, false); + boolean isTencentKMSEnabled = conf.getBoolean(TENCENT_KMS_ENABLED, false); + this.keyVaultEnabled = isAzureKeyVaultEnabled || isGCPEnabled || isTencentKMSEnabled; - if(this.isHSMEnabled) { + if (isHSMEnabled) { logger.info("Ranger KMS HSM is enabled for storing master key."); masterKeyProvider = new RangerHSM(conf); String partitionPasswd = conf.get(HSM_PARTITION_PASSWORD); @@ -129,7 +125,18 @@ public class RangerKeyStoreProvider extends KeyProvider { || partitionPasswd.trim().equals("crypted")) { throw new IOException("Partition Password doesn't exists"); } - } else if (this.isKeySecureEnabled) { + dbStore = new RangerKeyStore(daoManager); + // generate master key on HSM + masterKeyProvider.generateMasterKey(password); + try { + masterKey = masterKeyProvider.getMasterKey(password) + .toCharArray(); + } catch (Exception ex) { + throw new Exception( + "Error while getting Safenet KeySecure master key " + + ex); + } + } else if (isKeySecureEnabled) { logger.info("KeySecure is enabled for storing the master key."); getFromJceks(conf, CREDENTIAL_PATH, KEYSECURE_PASSWORD_ALIAS, KEYSECURE_PASSWORD); @@ -151,93 +158,42 @@ public class RangerKeyStoreProvider extends KeyProvider { + ex); } - } else if (this.azureKeyVaultEnabled) { + } else if (isAzureKeyVaultEnabled) { logger.info("Azure Key Vault is enabled for storing the master key."); - azureKeyVaultEnabled = true; getFromJceks(conf, CREDENTIAL_PATH, AZURE_CLIENT_SECRET_ALIAS, AZURE_CLIENT_SECRET); - String azureClientId = conf.get(AZURE_CLIENT_ID); - if (StringUtils.isEmpty(azureClientId)) { + try { + masterKeyProvider = new RangerAzureKeyVaultKeyGenerator(conf); + masterKeyProvider.onInitialization(); + dbStore = new RangerKeyStore(daoManager, true, masterKeyProvider); + // ensure master key exist + masterKeyProvider.generateMasterKey(password); + } catch (Exception ex) { throw new Exception( - "Azure Key Vault is enabled and client id is not configured"); - } - String azureClientSecret = conf.get(AZURE_CLIENT_SECRET); - AzureKeyVaultClientAuthenticator azureKVClientAuthenticator; - KeyVaultClient kvClient = null; - if (conf != null - && StringUtils.isNotEmpty(conf.get(AZURE_KEYVAULT_SSL_ENABLED)) - && conf.get(AZURE_KEYVAULT_SSL_ENABLED).equalsIgnoreCase("false")) { - try { - if (StringUtils.isEmpty(azureClientSecret)) { - throw new Exception( - "Azure Key Vault is enabled in non SSL mode and client password/secret is not configured"); - } - azureKVClientAuthenticator = new AzureKeyVaultClientAuthenticator( - azureClientId, azureClientSecret); - kvClient = new KeyVaultClient(azureKVClientAuthenticator); - } catch (Exception ex) { - throw new Exception( - "Error while getting key vault client object with client id and client secret : " - + ex); - } - } else { - try { - azureKVClientAuthenticator = new AzureKeyVaultClientAuthenticator( - azureClientId); - String keyVaultCertPath = conf - .get(AZURE_KEYVAULT_CERTIFICATE_PATH); - if (StringUtils.isEmpty(keyVaultCertPath)) { - throw new Exception( - "Azure Key Vault is enabled in SSL mode. Please provide certificate path for authentication."); - } - String keyVaultCertPassword = conf - .get(AZURE_KEYVAULT_CERTIFICATE_PASSWORD); - - kvClient = !StringUtils.isEmpty(keyVaultCertPassword) ? azureKVClientAuthenticator - .getAuthentication(keyVaultCertPath, - keyVaultCertPassword) - : azureKVClientAuthenticator.getAuthentication( - keyVaultCertPath, ""); - } catch (Exception ex) { - throw new Exception( - "Error while getting key vault client object with client id and certificate. Error : : " - + ex); - } + "Error while generating master key and master key secret in Azure Key Vault. Error : " + + ex); } - boolean success = false; - if (kvClient != null) { - try { - dbStore = new RangerKeyStore(daoManager, conf, kvClient); - masterKeyProvider = new RangerKeyVaultKeyGenerator(conf, - kvClient); - if (masterKeyProvider != null) { - success = masterKeyProvider.generateMasterKey(password); - } - } catch (Exception ex) { - throw new Exception( - "Error while generating master key and master key secret in Azure key vault. Error : : " - + ex); - } - } else { + } else if (isTencentKMSEnabled) { + logger.info("Ranger KMS Tencent KMS is enabled for storing master key."); + getFromJceks(conf, CREDENTIAL_PATH, TENCENT_CLIENT_SECRET_ALIAS, + TENCENT_CLIENT_SECRET); + try { + masterKeyProvider = new RangerTencentKMSProvider(conf); + masterKeyProvider.onInitialization(); + dbStore = new RangerKeyStore(daoManager, true, masterKeyProvider); + // ensure master key exist + masterKeyProvider.generateMasterKey(password); + } catch (Exception ex) { throw new Exception( - "Unable to get Key Vault Client. Please check the azure credentials."); - } - if (success) { - try { - /* Master key not exportable from key vault */ - masterKey = null; - } catch (Exception ex) { - throw new Exception( - "Error while getting Azure Master key Secret. Error : " - + ex); - } + "Error while generating master key and master key secret in Tencent KMS. Error : " + + ex); } - } else if(this.isGCPEnabled) { + } else if (isGCPEnabled) { logger.info("Google Cloud HSM is enabled for storing the master key."); masterKeyProvider = new RangerGoogleCloudHSMProvider(conf); masterKeyProvider.onInitialization(); if(masterKeyProvider != null) { - this.dbStore = new RangerKeyStore(daoManager, this.isGCPEnabled, masterKeyProvider); + this.dbStore = new RangerKeyStore(daoManager, true, masterKeyProvider); masterKeyProvider.generateMasterKey(password); } } else { @@ -346,7 +302,7 @@ public class RangerKeyStoreProvider extends KeyProvider { } try { String attribute = JsonUtilsV2.mapToJson(attributes); - if (azureKeyVaultEnabled || this.isGCPEnabled) { + if (keyVaultEnabled) { dbStore.addSecureKeyByteEntry(versionName, new SecretKeySpec( material, cipher), cipher, bitLength, description, version, attribute); @@ -409,7 +365,7 @@ public class RangerKeyStoreProvider extends KeyProvider { Metadata metadata = entry.getValue(); String attributes = JsonUtilsV2.mapToJson(metadata .getAttributes()); - if (azureKeyVaultEnabled || this.isGCPEnabled) { + if (keyVaultEnabled) { Key ezkey = new KeyMetadata(metadata); if (ezkey.getEncoded().length == 0) { KeyGenerator keyGenerator = KeyGenerator @@ -457,7 +413,7 @@ public class RangerKeyStoreProvider extends KeyProvider { readLock.lock(); try { - if (azureKeyVaultEnabled || this.isGCPEnabled) { + if (keyVaultEnabled) { byte[] decryptKeyByte = null; try { if (!dbStore.engineContainsAlias(versionName)) { @@ -570,7 +526,7 @@ public class RangerKeyStoreProvider extends KeyProvider { return null; } } - if (azureKeyVaultEnabled || this.isGCPEnabled) { + if (keyVaultEnabled) { Metadata meta = dbStore.engineGetKeyMetadata(name); if (meta != null) { cache.put(name, meta); diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerTencentKMSProvider.java b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerTencentKMSProvider.java new file mode 100644 index 0000000..1f01259 --- /dev/null +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerTencentKMSProvider.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.hadoop.crypto.key; + +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.kms.v20190118.KmsClient; +import com.tencentcloudapi.kms.v20190118.models.*; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.Base64; + +public class RangerTencentKMSProvider implements RangerKMSMKI { + + static final Logger logger = LoggerFactory.getLogger(RangerTencentKMSProvider.class); + static final String TENCENT_MASTER_KEY_ID = "ranger.kms.tencent.masterkey.id"; + static final String TENCENT_CLIENT_ID = "ranger.kms.tencent.client.id"; + static final String TENCENT_CLIENT_SECRET = "ranger.kms.tencent.client.secret"; + static final String TENCENT_CLIENT_REGION = "ranger.kms.tencent.client.region"; + private String masterKeyId; + private KeyMetadata masterKeyMetadata; + private KmsClient keyVaultClient; + + protected RangerTencentKMSProvider(Configuration conf, + KmsClient client) { + this.masterKeyId = conf.get(TENCENT_MASTER_KEY_ID); + this.keyVaultClient = client; + } + + public RangerTencentKMSProvider(Configuration conf) throws Exception { + this(conf, createKMSClient(conf)); + } + + public static KmsClient createKMSClient(Configuration conf) throws Exception { + String tencentClientId = conf.get(TENCENT_CLIENT_ID); + if (StringUtils.isEmpty(tencentClientId)) { + throw new Exception( + "Tencent KMS is enabled, but client id is not configured"); + } + String tencentClientSecret = conf.get(TENCENT_CLIENT_SECRET); + String tencentClientRegion = conf.get(TENCENT_CLIENT_REGION); + return new KmsClient( + new Credential(tencentClientId, tencentClientSecret), + tencentClientRegion); + } + + @Override + public boolean generateMasterKey(String password) throws Exception { + if (keyVaultClient == null) { + throw new Exception( + "Key Vault Client is null. Please check the azure related configuration."); + } + try { + + DescribeKeyRequest desckey_req = new DescribeKeyRequest(); + desckey_req.setKeyId(masterKeyId); + DescribeKeyResponse desckey_resp = keyVaultClient.DescribeKey(desckey_req); + if (desckey_resp == null || !desckey_resp.getKeyMetadata().getKeyId().equals(masterKeyId)) { + throw new Exception("KetMetadata is invalid"); + } + masterKeyMetadata = desckey_resp.getKeyMetadata(); + } catch (TencentCloudSDKException ex) { + throw new Exception( + "Error while getting existing master key from Tencent. Master Key Id : " + + masterKeyId + " . Error : " + ex.getMessage()); + } + if (masterKeyMetadata == null) { + throw new NoSuchMethodException("generateMasterKey is not implemented for Tencent KMS"); + } else { + logger.info("Tencent Master key exist with KeyId :" + masterKeyId + + " with Alias: " + masterKeyMetadata.getAlias() + + " with Description : " + masterKeyMetadata.getDescription() + + " with ResourceId : " + masterKeyMetadata.getResourceId()); + return true; + } + } + + @Override + public byte[] encryptZoneKey(Key zoneKey) throws Exception { + try { + EncryptRequest req = new EncryptRequest(); + req.setKeyId(this.masterKeyId); + req.setPlaintext(Base64.getEncoder().encodeToString(zoneKey.getEncoded())); + EncryptResponse resp = keyVaultClient.Encrypt(req); + // resp.getCiphertextBlob() returns something looks like base64 encoded. + // It is actually a concatenation of several base64 encoded fragments. + // Maybe Tencent KMS will use the separation information of fragments. + // So we MUST NOT decode base64 here. + return resp.getCiphertextBlob().getBytes(StandardCharsets.US_ASCII); + } catch (TencentCloudSDKException e) { + throw (Exception)new Exception("Error while encrypting zone key.").initCause(e); + } + } + + @Override + public byte[] decryptZoneKey(byte[] encryptedByte) throws Exception { + try { + DecryptRequest req = new DecryptRequest(); + req.setCiphertextBlob(new String(encryptedByte, StandardCharsets.US_ASCII)); + DecryptResponse resp = keyVaultClient.Decrypt(req); + return Base64.getDecoder().decode(resp.getPlaintext()); + } catch (TencentCloudSDKException e) { + throw (Exception)new Exception("Error while decrypting zone key.").initCause(e); + } + } + + @Override + public String getMasterKey(String masterKeySecretName) { + /* + * This method is not require for Tencent KMS because we can't get + * key outside of KMS + */ + return null; + } +} diff --git a/pom.xml b/pom.xml index 8a19c2d..5e844ef 100644 --- a/pom.xml +++ b/pom.xml @@ -217,7 +217,7 @@ <presto.validation-api.version>2.0.1.Final</presto.validation-api.version> <presto.re2j.version>1.1</presto.re2j.version> - <!-- Azure Key Vault dependencies --> + <!-- Azure Key Vault dependencies --> <com.microsoft.azure.version>1.22.0</com.microsoft.azure.version> <com.microsoft.azure.azure-keyvault.version>1.2.1</com.microsoft.azure.azure-keyvault.version> <com.microsoft.azure.azure-mgmt-keyvault.version>1.22.0</com.microsoft.azure.azure-mgmt-keyvault.version> @@ -234,6 +234,7 @@ <jsonsmart.version>2.3.1</jsonsmart.version> <nimbus-jose-jwt.version>8.22.1</nimbus-jose-jwt.version> <com.amazonaws.aws-java-sdk-bom.version>1.12.125</com.amazonaws.aws-java-sdk-bom.version> + <!-- GCP HSM --> <google.cloud.kms>2.3.0</google.cloud.kms> @@ -242,6 +243,9 @@ <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin> <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis> <sonar.language>java</sonar.language> + + <!-- Tencent KMS depencies --> + <com.tencentcloudapi.sdk.version>3.1.322</com.tencentcloudapi.sdk.version> </properties> <profiles> <profile>
