Password Protection for Non-Java Applications

This section describes an alternative mechanism that non-Java applications can use to access the key and trust store passwords.

As described in the previous section, the Hadoop Credential Provider API protects key and trust store passwords by storing them in an encrypted Java credential store. Non-Java applications cannot access the Hadoop credential store. An alternative mechanism is needed for non-Java applications to retrieve:
  • Passwords needed to access the PKCS#12 key and trust stores
  • Private keys that are encrypted with a key store password
To protect passwords for non-Java applications, store key store passwords in maprkeycreds.conf and trust store passwords in maprtrustcreds.conf in ${MAPR_HOME}/conf. ${MAPR_HOME}/conf is created by the ${MAPR_HOME}/conf/configure.sh -genkeys script.
The format of each line of the maprkeycreds.conf and maprtrustcreds.conf file is is shown below.
<password property>=ENC:<code>:<checksum>:<Base64 encrypted password>
The table below list keys and provide and key escriptions.
Key Description
password property The password property in ssl-client.xml or ssl-server.xml. For example: ssl.server.keystore.password
ENC Indication that the password is encrypted.
code Encryption code to denote the type of algorithm used for encryption. For release 7.0.0, the code is always 1 to denote AES-256-CTR using PBKDF2 with 20000 iterations. The password used to derive the encryption key is obtained in an identical way from the Hadoop Credential Provider API (it is obtained from the value of the environment variable HADOOP_CREDSTORE_PASSWORD and defaults to none if HADOOP_CREDSTORE_PASSWORD is not set).
checksum The SHA-256 checksum used to verify that the password is correctly decrypted. Upon decryption, the application should compute the SHA-256 checksum on the decrypted password and verify that it matches this checksum.
Base-64 encrypted password The encrypted password in Base-64 encoding.
For example:
pwd 
/opt/mapr/conf 
cat maprkeycreds.conf  
ssl.server.keystore.password=ENC:1:b8f9933aa5af6d9d2c0706fec5156fba5233546ac3bce8213524353b5c70c42f:U2FsdGVkX1+OYGv5p/2c3nYXw3u2EYax2N9Y7GpfQKeifFkskdDYA17XEqUkinAf7Q== 
ssl.server.keystore.keypassword=ENC:1:b8f9933aa5af6d9d2c0706fec5156fba5233546ac3bce8213524353b5c70c42f:U2FsdGVkX18LDAUdN66mdVxmt8k8xQo2vAnQJ5xw7V/enAOq3fQ1NVXOPpi1J027Bg== 
ssl.server.truststore.password=ENC:1:e7a15c233a1252a17e6a8a07c2cb397017ecd939224593d2327d381cbb56ab54:U2FsdGVkX1+sPmXLhP26sPWC3mi2MD6yRYeVnFOauBEnPVd69+rGuPE2qoxFcXoJ9A== 
Applications can use the openssl command to decrypt the password. In the following example, replace the default decryption key of none with the actual decryption key, which is either the value of the environment variable HADOOP_CREDSTORE_PASSWORD or the default value none. For example:
echo 
"U2FsdGVkX18Qbi4OXoFrPDjQhVtJAzzP+fsyHmAgXKcz5OanmpaQZIOfpNENlZPwIw==" | openssl enc -aes-256-ctr -iter 20000 -pass pass:none -base64 -md sha256 -A -d 
8M2HdpkZxjb1QLVHG2lx_Dtg_bg870gS 
To verify that the password is correctly decrypted, use the openssl dgst command to obtain the SHA-256 signature of the decrypted password. Then verify that it matches the value configured in the checksum field in maprkeycreds.conf or maprtrustcreds.conf. For example:
echo "8M2HdpkZxjb1QLVHG2lx_Dtg_bg870gS" | \ 
  openssl dgst -sha256 | awk '{print $2}' 
b8f9933aa5af6d9d2c0706fec5156fba5233546ac3bce8213524353b5c70c42f 
To reduce the duplication of code to retrieve these passwords for non-Java applications, common-ecosystem.sh, which is already used by most MEP/EEP components, now includes a routine that implements the above steps. Its interface looks like the following:
getStorePw() { 
    # routine expects 2 or 3 inputs 
    # key to lookup - like ssl.server.keystore.password 
    # file to look in - like /opt/mapr/conf/maprkeycreds.conf 
    # optional password, if not provided, $HADOOP_CREDSTORE_PASSWORD is used if set, 
    # otherwise none 
    # 
    # returns pw on success otherwise error messages 
    # rc=0 on success - 1 otherwise 
    # 
    # called like: 
    #  pw=$(getStorePw ssl.server.keystore.password /opt/mapr/conf/maprkeycreds.conf) 
    #  if [ $? -ne 0 ]; then 
    #      echo "got an error: $pw" 
    #      ... 
    #  fi 
Since many ecosystem components need to work in both core 6.2.0- and core 7.0.0 environments, an ecosystem configure.sh (or supporting script) must deal with both environments. Example usage would look like the following (assume maprKeyCredsConf is set to $MAPR_HOME/conf/maprkeycreds.conf):
keystorePass="$(grep -F -A 1 ssl.server.keystore.password $sslServerConf | tail -1 | sed 's/ *<value>//;s/<\/value>//')" 

if [ "$keystorePass" = "__##CREDENTIALS_STORE##__" ] || [ -z "$keystorePass" ] && [ -e "$maprKeyCredsConf" ]; then 

    keystorePass=$(getStorePw ssl.server.keystore.password $maprKeyCredsConf 
    rc=$? 
    if [ $rc -ne 0 ]; then 
        echo "Failed to extract keystore password: $keystorePass" 
    fi 
fi 

Similar code is expected to be done for the trust store password.

In addition, if a non-Java EEP component requires a private key in PEM format, additional work is required to protect the unencrypted private key or the password for the encrypted private key.

For a component that requires an unencrypted private key pem file, the requirement is that its init/start script must generate the pem key, start the process, and then remove the key after the process is up and running. This assumes that the service does not negatively react to the change in the key file (from containing the key to empty).

Using Grafana, which needs an unencrypted private key, as an example, you can generate an empty key PEM file during configure.sh stage. After Warden starts Grafana, the Grafana init/start script extracts the keystore password, and uses that to decrypt the encrypted pem key. Then it puts the unencrypted private key into the key.pem file that Grafana requires right before it is started. Additional code is added to the init/start script to detect when the service is fully up and has read the pem key, before zeroing out the file. On an unsuccessful start, the pem key file is also zeroed out.

Similarly, for a service that can use an encrypted private key, but reasonably requires the password for the key in a config file, the password in the config file should only be there during startup. The init/start script must then aquire the required password, edit the config file to fill it in, start the process, and then remove it from the config file. (This only works for processes that do not monitor changes in the config file and restart.)