diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 280f165a775..4c987dbfa30 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -181,6 +181,8 @@ New Features * SOLR-9045: Make RecoveryStrategy settings configurable. (Christine Poerschke) +* SOLR-10076: Hide keystore and truststore passwords from /admin/info/* outputs. (Mano Kovacs via Mark Miller) + Bug Fixes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java index c16cded6e4c..57a7492ae75 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java @@ -17,12 +17,14 @@ package org.apache.solr.handler.admin; import java.io.IOException; +import java.util.Enumeration; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.RedactionUtils; import static org.apache.solr.common.params.CommonParams.NAME; @@ -32,23 +34,36 @@ import static org.apache.solr.common.params.CommonParams.NAME; */ public class PropertiesRequestHandler extends RequestHandlerBase { + + public static final String REDACT_STRING = RedactionUtils.getRedactString(); + @Override public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { - Object props = null; + NamedList props = new SimpleOrderedMap<>(); String name = req.getParams().get(NAME); if( name != null ) { - NamedList p = new SimpleOrderedMap<>(); - p.add( name, System.getProperty(name) ); - props = p; + String property = getSecuredPropertyValue(name); + props.add( name, property); } else { - props = System.getProperties(); + Enumeration enumeration = System.getProperties().propertyNames(); + while(enumeration.hasMoreElements()){ + name = (String) enumeration.nextElement(); + props.add(name, getSecuredPropertyValue(name)); + } } rsp.add( "system.properties", props ); rsp.setHttpCaching(false); } - + + private String getSecuredPropertyValue(String name) { + if(RedactionUtils.isSystemPropertySensitive(name)){ + return REDACT_STRING; + } + return System.getProperty(name); + } + //////////////////////// SolrInfoMBeans methods ////////////////////// @Override diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java index d031d69f685..94fb0553768 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java @@ -36,6 +36,8 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Arrays; import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.Locale; import org.apache.commons.io.IOUtils; @@ -50,7 +52,7 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.schema.IndexSchema; import org.apache.solr.util.RTimer; - +import org.apache.solr.util.RedactionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +68,8 @@ public class SystemInfoHandler extends RequestHandlerBase { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public static String REDACT_STRING = RedactionUtils.getRedactString(); + /** *

* Undocumented expert level system property to prevent doing a reverse lookup of our hostname. @@ -373,7 +377,7 @@ public class SystemInfoHandler extends RequestHandlerBase // the input arguments passed to the Java virtual machine // which does not include the arguments to the main method. - jmx.add( "commandLineArgs", mx.getInputArguments()); + jmx.add( "commandLineArgs", getInputArgumentsRedacted(mx)); jmx.add( "startTime", new Date(mx.getStartTime())); jmx.add( "upTimeMS", mx.getUptime() ); @@ -436,6 +440,18 @@ public class SystemInfoHandler extends RequestHandlerBase return newSizeAndUnits; } + + private static List getInputArgumentsRedacted(RuntimeMXBean mx) { + List list = new LinkedList<>(); + for (String arg : mx.getInputArguments()) { + if (arg.startsWith("-D") && arg.contains("=") && RedactionUtils.isSystemPropertySensitive(arg.substring(2, arg.indexOf("=")))) { + list.add(String.format("%s=%s", arg.substring(0, arg.indexOf("=")), REDACT_STRING)); + } else { + list.add(arg); + } + } + return list; + } } diff --git a/solr/core/src/java/org/apache/solr/util/RedactionUtils.java b/solr/core/src/java/org/apache/solr/util/RedactionUtils.java new file mode 100644 index 00000000000..afa2abf952a --- /dev/null +++ b/solr/core/src/java/org/apache/solr/util/RedactionUtils.java @@ -0,0 +1,51 @@ +/* + * 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.solr.util; + +import java.util.regex.Pattern; + +public class RedactionUtils { + public static final String SOLR_REDACTION_SYSTEM_PATTERN_PROP = "solr.redaction.system.pattern"; + private static Pattern pattern = Pattern.compile(System.getProperty(SOLR_REDACTION_SYSTEM_PATTERN_PROP, ".*password.*"), Pattern.CASE_INSENSITIVE); + private static final String REDACT_STRING = "--REDACTED--"; + + private static boolean redactSystemProperty = Boolean.parseBoolean(System.getProperty("solr.redaction.system.enabled", "true")); + + /** + * Returns if the given system property should be redacted. + * + * @param name The system property that is being checked. + * @return true if property should be redacted. + */ + static public boolean isSystemPropertySensitive(String name) { + return redactSystemProperty && pattern.matcher(name).matches(); + } + + /** + * @return redaction string to be used instead of the value. + */ + static public String getRedactString() { + return REDACT_STRING; + } + + public static void setRedactSystemProperty(boolean redactSystemProperty) { + RedactionUtils.redactSystemProperty = redactSystemProperty; + } + + +} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java new file mode 100644 index 00000000000..1a959a45116 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java @@ -0,0 +1,73 @@ +/* + * 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.solr.handler.admin; + +import java.io.StringReader; + +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.solrj.impl.XMLResponseParser; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.util.RedactionUtils; +import org.junit.BeforeClass; +import org.junit.Test; + + +public class PropertiesRequestHandlerTest extends SolrTestCaseJ4 { + + public static final String PASSWORD = "secret123"; + public static final String REDACT_STRING = RedactionUtils.getRedactString(); + + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig.xml", "schema.xml"); + } + + @Test + public void testRedaction() throws Exception { + RedactionUtils.setRedactSystemProperty(true); + for(String propName: new String[]{"some.password", "javax.net.ssl.trustStorePassword"}){ + System.setProperty(propName, PASSWORD); + NamedList>> properties = readProperties(); + + assertEquals("Failed to redact "+propName, REDACT_STRING, properties.get(propName)); + } + } + + @Test + public void testDisabledRedaction() throws Exception { + RedactionUtils.setRedactSystemProperty(false); + for(String propName: new String[]{"some.password", "javax.net.ssl.trustStorePassword"}){ + System.setProperty(propName, PASSWORD); + NamedList>> properties = readProperties(); + + assertEquals("Failed to *not* redact "+propName, PASSWORD, properties.get(propName)); + } + } + + private NamedList>> readProperties() throws Exception { + String xml = h.query(req( + CommonParams.QT, "/admin/properties", + CommonParams.WT, "xml" + )); + + XMLResponseParser parser = new XMLResponseParser(); + return (NamedList>>) + parser.processResponse(new StringReader(xml)).get("system.properties"); + } +} \ No newline at end of file