/** * 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.ambari.server.utils; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.StaticallyInject; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Config; import org.apache.ambari.server.state.PropertyInfo; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.inject.Inject; @StaticallyInject public class SecretReference { private static final String secretPrefix = "SECRET"; private String configType; private Long version; private String value; private final static String PASSWORD_TEXT = "password"; private final static String PASSWD_TEXT = "passwd"; @Inject private static Gson gson; public SecretReference(String reference, Cluster cluster) throws AmbariException{ String[] values = reference.split(":"); configType = values[1]; version = Long.valueOf(values[2]); String propertyName = values[3]; String clusterName = cluster.getClusterName(); Config refConfig = cluster.getConfigByVersion(configType, version); if(refConfig == null) throw new AmbariException(String.format("Error when parsing secret reference. Cluster: %s does not contain ConfigType: %s ConfigVersion: %s", clusterName, configType, version)); Map<String, String> refProperties = refConfig.getProperties(); if(!refProperties.containsKey(propertyName)) throw new AmbariException(String.format("Error when parsing secret reference. Cluster: %s ConfigType: %s ConfigVersion: %s does not contain property '%s'", clusterName, configType, version, propertyName)); this.value = refProperties.get(propertyName); } public void setConfigType(String configType) { this.configType = configType; } public Long getVersion() { return version; } public String getValue() { return value; } public static boolean isSecret(String value) { String[] values = value.split(":"); return values.length == 4 && values[0].equals(secretPrefix); } public static String generateStub(String configType, Long configVersion, String propertyName) { return secretPrefix + ":" + configType + ":" + configVersion.toString() + ":" + propertyName; } /** * Helper function to mask a string of property bags that may contain a property with a password. * @param propertyMap Property map to mask by replacing any passwords with the text "SECRET" * @return New string with the passwords masked, or null if the property map is null. */ public static String maskPasswordInPropertyMap(String propertyMap) { if (null == propertyMap) return null; Map<String, String> maskedMap = new HashMap<>(); Map<String, String> map = gson.fromJson(propertyMap, new TypeToken<Map<String, String>>() {}.getType()); for (Map.Entry<String, String> e : map.entrySet()) { String value = e.getValue(); if (e.getKey().toLowerCase().contains(PASSWORD_TEXT) || e.getKey().toLowerCase().contains(PASSWD_TEXT)) { value = secretPrefix; } maskedMap.put(e.getKey(), value); } return gson.toJson(maskedMap); } /** * Replace secret references with appropriate real passwords. * @param targetMap map in which replacement will be performed * @param cluster current cluster * @throws AmbariException */ public static void replaceReferencesWithPasswords(Map<String, String> targetMap, Cluster cluster) throws AmbariException { if(cluster != null) { for (Map.Entry<String, String> propertyValueEntry : targetMap.entrySet()) { String key = propertyValueEntry.getKey(); String value = propertyValueEntry.getValue(); if (value != null && SecretReference.isSecret(value)) { SecretReference ref = new SecretReference(value, cluster); targetMap.put(key, ref.getValue()); } } } } /** * Replace real passwords with secret references * @param propertiesTypes map with properties types * @param propertiesMap map with properties in which replacement will be performed * @param configType configuration type * @param configVersion configuration version */ public static void replacePasswordsWithReferences(Map<PropertyInfo.PropertyType, Set<String>> propertiesTypes, Map<String, String> propertiesMap, String configType, Long configVersion){ if(propertiesTypes != null && propertiesTypes.containsKey(PropertyInfo.PropertyType.PASSWORD)) { for(String pwdPropertyName: propertiesTypes.get(PropertyInfo.PropertyType.PASSWORD)) { if(propertiesMap.containsKey(pwdPropertyName)){ if(!propertiesMap.get(pwdPropertyName).equals("")) { String stub = SecretReference.generateStub(configType, configVersion, pwdPropertyName); propertiesMap.put(pwdPropertyName, stub); } } } } } /** * Replace real passwords with secret references * @param configAttributes map with config attributes containing properties types as part of their content * @param propertiesMap map with properties in which replacement will be performed * @param configType configuration type * @param configVersion configuration version */ public static void replacePasswordsWithReferencesForCustomProperties(Map<String, Map<String, String>> configAttributes, Map<String, String> propertiesMap, String configType, Long configVersion){ if(configAttributes != null && configAttributes.containsKey("password")) { for(String pwdPropertyName: configAttributes.get("password").keySet()) { if(propertiesMap.containsKey(pwdPropertyName)){ if(!propertiesMap.get(pwdPropertyName).equals("")) { String stub = SecretReference.generateStub(configType, configVersion, pwdPropertyName); propertiesMap.put(pwdPropertyName, stub); } } } } } }