Skip to content

Commit

Permalink
Merge pull request #29 from arenadata/feature/ADBDEV-3209
Browse files Browse the repository at this point in the history
ADBDEV-3209: Add possibility to decrypt an encrypted password
  • Loading branch information
iamlapa authored Nov 14, 2022
2 parents 8031b6f + a6e5b6a commit fe6d05a
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 19 deletions.
28 changes: 27 additions & 1 deletion server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@
buildscript {
repositories {
mavenCentral()
maven {
url "https://rt.adsw.io/artifactory/maven-arenadata-release/"
mavenContent {
releasesOnly()
}
}
maven {
url "https://rt.adsw.io/artifactory/maven-arenadata-snapshot/"
mavenContent {
snapshotsOnly()
}
}
}
}

Expand All @@ -40,6 +52,18 @@ allprojects {

repositories {
mavenCentral()
maven {
url "https://rt.adsw.io/artifactory/maven-arenadata-release/"
mavenContent {
releasesOnly()
}
}
maven {
url "https://rt.adsw.io/artifactory/maven-arenadata-snapshot/"
mavenContent {
snapshotsOnly()
}
}
}
}

Expand Down Expand Up @@ -114,6 +138,9 @@ configure(javaProjects) {
dependency("org.wildfly.openssl:wildfly-openssl:1.0.7.Final")
dependency("org.xerial.snappy:snappy-java:1.1.8.4")

// Arenadata encryption
dependency("io.arenadata.security:encryption:1.0.0")

// Hadoop dependencies
dependencySet(group:"org.apache.hadoop", version:"${hadoopVersion}") {
entry("hadoop-annotations")
Expand Down Expand Up @@ -199,7 +226,6 @@ configure(javaProjects) {
entry("aws-java-sdk-kms")
entry("aws-java-sdk-s3")
}

}
}

Expand Down
1 change: 1 addition & 0 deletions server/pxf-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
implementation("commons-configuration:commons-configuration")
implementation("commons-lang:commons-lang")
implementation("org.apache.commons:commons-lang3")
implementation("io.arenadata.security:encryption")

implementation("org.apache.hadoop:hadoop-auth") { transitive = false }
implementation("org.codehaus.woodstox:stax2-api") { transitive = false }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.greenplum.pxf.api.configuration;

import io.arenadata.security.encryption.client.configuration.JksTextEncryptorConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty({"pxf.ssl.jks-store.path", "pxf.ssl.jks-store.password", "pxf.ssl.salt.key"})
public class PxfJksTextEncryptorConfiguration extends JksTextEncryptorConfiguration {
private final String path;
private final String password;
private final String key;

public PxfJksTextEncryptorConfiguration(@Value("${pxf.ssl.jks-store.path}") String path,
@Value("${pxf.ssl.jks-store.password}") String password,
@Value("${pxf.ssl.salt.key}") String key) {
this.path = path;
this.password = password;
this.key = key;
}

@Override
protected String jksStorePath() {
return path;
}

@Override
protected char[] jksStorePassword() {
return password.toCharArray();
}

@Override
protected String secretKeyAlias() {
return key;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ public static <T extends Object> T getBean(Class<T> requiredType) {
return context.getBean(requiredType);
}

public static <T extends Object> T getNullableBean(Class<T> requiredType) {
try {
return context.getBean(requiredType);
} catch (Exception e) {
return null;
}
}

@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {

Expand Down
109 changes: 108 additions & 1 deletion server/pxf-jdbc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ User name (login) to use to connect to external database.


#### JDBC password
Password to use to connect to external database.
Password to use to connect to external database. The password might be encrypted.
How to use encrypted password is described in section [JDBC password encryption](#jdbc-password-encryption).

* **Option**: `PASS`
* **Configuration parameter**: `jdbc.password`
Expand Down Expand Up @@ -650,3 +651,109 @@ Follow these steps to enable connectivity to Hive:
If you enable impersonation, do not explicitly specify `hive.server2.proxy.user` property in the URL.

- if Hive is configured with `hive.server2.enable.doAs = FALSE`, Hive will run Hadoop operations with the identity provided by the PXF Kerberos principal (usually `gpadmin`)


## JDBC password encryption
It is possible to use an encrypted password instead of the password in a paint text in the `$PXF_BASE/servers/<server_name>/jdbc-site.xml` file.

### Prerequisites
There is a special library that is used to encrypt and decrypt password. The executable jar-file of this library has to be copied to `$PXF_BASE/lib/` directory on each segment.
It is used to encrypt password. The original jar-file of the library is used to decrypt password. It is added as a dependency to the PXF project.

### How to enable encryption
Before using an encrypted password you have to **create keystore and add encryption key** to the store.\
The keystore is a file where the encryption key will be saved. And the encryption key will be used to encrypt and decrypt password.\
The keystore and the encryption key have to be created on each segment server.

The command to create the keystore:\
```keytool -keystore <keystore_file> -storepass <keystore_password> -genkey -keypass <key_password> -alias <keystore_alias>```, where\
`keystore_file` - the file path of the keystore;\
`keystore_password` - password which will be used to access the keystore;\
`key_password` - password for the specific `keystore_alias`. It might be the same as `keystore_password`;\
`keystore_alias` - name of the keystore.

You will be asked to enter some information about your organization, first and last name, etc. after running the command.

Example of the command to create a keystore:\
`keytool -keystore /var/lib/pxf/conf/pxfkeystore.jks -storepass 12345678 -genkey -keypass 12345678 -alias pxfkeystore`

The next step is to add encryption key.\
The command to add encryption key to the keystore:\
`keytool -keystore <keystore_file> -storepass <keystore_password> -importpass -keypass <key_password> -alias <encryption_key_alias>`, where\
`keystore_file` - the file path of the keystore that was created in the previous step;\
`keystore_password` - password to access the keystore;\
`key_password` - password for the specific `encryption_key_alias`. It might be the same as `keystore_password`;\
`encryption_key_alias` - name of the encryption key. This name will be used to get encryption key from the keystore.

You will be asked to enter an encryption key you want to store after running the command.

Example of the command to create a keystore:\
`keytool -keystore /var/lib/pxf/conf/pxfkeystore.jks -storepass 12345678 -importpass -keypass 12345678 -alias PXF_PASS_KEY`\
*Enter the password to be stored:* qwerty

Next, additional properties have to be added into the `$PXF_BASE/conf/pxf-application.properties` file on each segment:\
`pxf.ssl.jks-store.path` - a Java keystore (JKS) absolute file path. It is a `keystore_file` from the command to create the keystore;\
`pxf.ssl.jks-store.password` - a Java keystore password. It is a `keystore_password` from the command to create the keystore;\
`pxf.ssl.salt.key` - an alias which is used to get encryption key from the keystore. It is an `encryption_key_alias` from the command to add encryption key to the keystore.

You have to restart PXF service after adding the properties.

Example of the properties in the `pxf-application.properties` file:
```
# Encryption
pxf.ssl.jks-store.path=/var/lib/pxf/conf/pxfkeystore.jks
pxf.ssl.jks-store.password=12345678
pxf.ssl.salt.key=PXF_PASS_KEY
```

### How to use encryption
The first step is to encrypt password that is used to connect to the database.\
There is a special command to do this action:\
`pxf encrypt <password> <encryption_type>`, where\
`<password>` - password in a plain text that is used to connect to the database. This password will be encrypted;\
`<encryption_type>` - Optional. The algorithm to encrypt password. Default value: `aes256`

The result of the command will be an encrypted password in a format `aes256:encrypted_password`

Example of the command to encrypt password:\
`pxf encrypt biuserpassword`\
*Output:* aes256:7BhhI+10ut+xM70iRlyxVDD/tokap3pbK2bmkLgPOYLH7NcfEYJSAIYkApjKM3Zu

Next, you have to copy the encrypted password including aes256 prefix and paste it into `$PXF_BASE/servers/<server_name>/jdbc-site.xml` file
instead of the password in a plain text.

The example of the `jdbc-site.xml` with encrypted password:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property>
<name>jdbc.driver</name>
<value>org.postgresql.Driver</value>
<description>Class name of the JDBC driver (e.g. org.postgresql.Driver)</description>
</property>
<property>
<name>jdbc.url</name>
<value>jdbc:postgresql://10.10.10.20/adb</value>
<description>The URL that the JDBC driver can use to connect to the database (e.g. jdbc:postgresql://localhost/postgres)</description>
</property>
<property>
<name>jdbc.user</name>
<value>bi_user</value>
<description>User name for connecting to the database (e.g. postgres)</description>
</property>
<property>
<name>jdbc.password</name>
<value>aes256:7BhhI+10ut+xM70iRlyxVDD/tokap3pbK2bmkLgPOYLH7NcfEYJSAIYkApjKM3Zu</value>
<description>Password for connecting to the database (e.g. postgres)</description>
</property>
</configuration>
```

You don't need to make any additional changes when you crate an external table. The decryption engine will recognize whether the password is encrypted or not.
If the password is encrypted the decrypter will take care about the password. If the password is in plain text format it will be passed as is to the JDBC connection manager.






1 change: 1 addition & 0 deletions server/pxf-jdbc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
implementation("org.apache.hive.shims:hive-shims-0.23") { transitive = false }
implementation("org.apache.hive.shims:hive-shims-common") { transitive = false }
implementation("org.springframework.boot:spring-boot-starter-log4j2")
implementation("io.arenadata.security:encryption")

/*******************************
* Test Dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* under the License.
*/

import io.arenadata.security.encryption.client.service.DecryptClient;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.greenplum.pxf.api.OneRow;
Expand Down Expand Up @@ -78,8 +79,8 @@ public JdbcAccessor() {
* @param connectionManager connection manager
* @param secureLogin the instance of the secure login
*/
JdbcAccessor(ConnectionManager connectionManager, SecureLogin secureLogin) {
super(connectionManager, secureLogin);
JdbcAccessor(ConnectionManager connectionManager, SecureLogin secureLogin, DecryptClient decryptClient) {
super(connectionManager, secureLogin, decryptClient);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* under the License.
*/

import io.arenadata.security.encryption.client.service.DecryptClient;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.greenplum.pxf.api.model.BasePlugin;
Expand All @@ -39,10 +40,7 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.*;
import java.util.stream.Collectors;

import static org.greenplum.pxf.api.security.SecureLogin.CONFIG_KEY_SERVICE_USER_IMPERSONATION;
Expand Down Expand Up @@ -166,6 +164,7 @@ public static TransactionIsolation typeOf(String str) {

private final ConnectionManager connectionManager;
private final SecureLogin secureLogin;
private final DecryptClient decryptClient;

static {
// Deprecated as of Oct 22, 2019 in version 5.9.2+
Expand All @@ -176,20 +175,23 @@ public static TransactionIsolation typeOf(String str) {

/**
* Creates a new instance with default (singleton) instances of
* ConnectionManager and SecureLogin.
* ConnectionManager, SecureLogin and DecryptClient.
*/
JdbcBasePlugin() {
this(SpringContext.getBean(ConnectionManager.class), SpringContext.getBean(SecureLogin.class));
this(SpringContext.getBean(ConnectionManager.class), SpringContext.getBean(SecureLogin.class),
SpringContext.getNullableBean(DecryptClient.class)
);
}

/**
* Creates a new instance with the given ConnectionManager and ConfigurationFactory
*
* @param connectionManager connection manager instance
*/
JdbcBasePlugin(ConnectionManager connectionManager, SecureLogin secureLogin) {
JdbcBasePlugin(ConnectionManager connectionManager, SecureLogin secureLogin, DecryptClient decryptClient) {
this.connectionManager = connectionManager;
this.secureLogin = secureLogin;
this.decryptClient = decryptClient;
}

@Override
Expand Down Expand Up @@ -335,11 +337,15 @@ public void afterPropertiesSet() {
);
}

// This must be the last parameter parsed, as we output connectionConfiguration earlier
// Optional parameter. By default, corresponding connectionConfiguration property is not set
if (jdbcUser != null) {
String jdbcPassword = configuration.get(JDBC_PASSWORD_PROPERTY_NAME);
if (jdbcPassword != null) {
try {
jdbcPassword = decryptClient == null ? jdbcPassword : decryptClient.decrypt(jdbcPassword);
} catch (Exception e) {
throw new RuntimeException(
"Failed to decrypt jdbc password. " + e.getMessage(), e);
}
LOG.debug("Connection password: {}", ConnectionManager.maskPassword(jdbcPassword));
connectionConfiguration.setProperty("password", jdbcPassword);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.greenplum.pxf.plugins.jdbc;

import io.arenadata.security.encryption.client.service.DecryptClient;
import org.apache.hadoop.conf.Configuration;
import org.greenplum.pxf.api.model.RequestContext;
import org.greenplum.pxf.api.security.SecureLogin;
Expand Down Expand Up @@ -49,11 +50,13 @@ public class JdbcAccessorTest {
private PreparedStatement mockPreparedStatement;
@Mock
private ResultSet mockResultSet;
@Mock
private DecryptClient mockDecryptClient;

@BeforeEach
public void setup() {

accessor = new JdbcAccessor(mockConnectionManager, mockSecureLogin);
accessor = new JdbcAccessor(mockConnectionManager, mockSecureLogin, mockDecryptClient);
configuration = new Configuration();
context = new RequestContext();
context.setConfig("default");
Expand Down
Loading

0 comments on commit fe6d05a

Please sign in to comment.