You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ho...@apache.org on 2023/07/05 15:59:13 UTC

[solr] branch branch_9x updated: SOLR-16809: Converge logic for hidden sysProps

This is an automated email from the ASF dual-hosted git repository.

houston pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 659021c7d50 SOLR-16809: Converge logic for hidden sysProps
659021c7d50 is described below

commit 659021c7d50164a3166887f24875228431b02102
Author: Houston Putman <ho...@apache.org>
AuthorDate: Mon Jun 26 15:12:43 2023 -0400

    SOLR-16809: Converge logic for hidden sysProps
    
    (cherry picked from commit 98c198810f2cd934d23d0d80aadb570a2bbb3b8e)
---
 solr/CHANGES.txt                                   |  2 +
 .../java/org/apache/solr/core/MetricsConfig.java   | 23 ------
 .../src/java/org/apache/solr/core/NodeConfig.java  | 96 +++++++++++++++++++---
 .../java/org/apache/solr/core/SolrXmlConfig.java   | 40 ++++++---
 .../org/apache/solr/handler/admin/InfoHandler.java |  2 +-
 .../handler/admin/PropertiesRequestHandler.java    | 31 ++++---
 .../solr/handler/admin/SystemInfoHandler.java      | 21 +++--
 .../apache/solr/servlet/CoreContainerProvider.java |  6 +-
 .../java/org/apache/solr/util/RedactionUtils.java  | 77 -----------------
 .../src/test-files/solr/solr-hiddensysprops.xml    |  6 +-
 .../src/test-files/solr/solr-metricsconfig.xml     |  6 +-
 .../admin/PropertiesRequestHandlerTest.java        | 25 ++----
 .../org/apache/solr/metrics/JvmMetricsTest.java    |  5 +-
 .../pages/configuring-solr-xml.adoc                | 16 ++++
 .../pages/major-changes-in-solr-9.adoc             | 11 +++
 15 files changed, 187 insertions(+), 180 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 9c823b016f2..842ab5738c5 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -239,6 +239,8 @@ Bug Fixes
 
 * SOLR-16619: Fix solr scripts running on IBM i (Jesse Gorzinski via Eric Pugh)
 
+* SOLR-16809: The configuration for hiding sensitive sysProp information has been joined under `-Dsolr.hiddenSysProps` and `SOLR_HIDDEN_SYS_PROPS`. (Houston Putman, David Smiley)
+
 Dependency Upgrades
 ---------------------
 * PR#1494: Upgrade forbiddenapis to 3.5 (Uwe Schindler)
diff --git a/solr/core/src/java/org/apache/solr/core/MetricsConfig.java b/solr/core/src/java/org/apache/solr/core/MetricsConfig.java
index 08f039927d2..75c98707f77 100644
--- a/solr/core/src/java/org/apache/solr/core/MetricsConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/MetricsConfig.java
@@ -17,14 +17,11 @@
 package org.apache.solr.core;
 
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 
 /** */
 public class MetricsConfig {
 
   private final PluginInfo[] metricReporters;
-  private final Set<String> hiddenSysProps;
   private final PluginInfo counterSupplier;
   private final PluginInfo meterSupplier;
   private final PluginInfo timerSupplier;
@@ -39,7 +36,6 @@ public class MetricsConfig {
   private MetricsConfig(
       boolean enabled,
       PluginInfo[] metricReporters,
-      Set<String> hiddenSysProps,
       PluginInfo counterSupplier,
       PluginInfo meterSupplier,
       PluginInfo timerSupplier,
@@ -51,7 +47,6 @@ public class MetricsConfig {
       CacheConfig cacheConfig) {
     this.enabled = enabled;
     this.metricReporters = metricReporters;
-    this.hiddenSysProps = hiddenSysProps;
     this.counterSupplier = counterSupplier;
     this.meterSupplier = meterSupplier;
     this.timerSupplier = timerSupplier;
@@ -97,14 +92,6 @@ public class MetricsConfig {
     return nullObject;
   }
 
-  public Set<String> getHiddenSysProps() {
-    if (enabled) {
-      return hiddenSysProps;
-    } else {
-      return Collections.emptySet();
-    }
-  }
-
   /** Symbolic name to use as plugin class name for no-op implementations. */
   public static final String NOOP_IMPL_CLASS = "__noop__";
 
@@ -145,7 +132,6 @@ public class MetricsConfig {
 
   public static class MetricsConfigBuilder {
     private PluginInfo[] metricReporterPlugins = new PluginInfo[0];
-    private Set<String> hiddenSysProps = new HashSet<>();
     private PluginInfo counterSupplier;
     private PluginInfo meterSupplier;
     private PluginInfo timerSupplier;
@@ -170,14 +156,6 @@ public class MetricsConfig {
       return this;
     }
 
-    public MetricsConfigBuilder setHiddenSysProps(Set<String> hiddenSysProps) {
-      if (hiddenSysProps != null && !hiddenSysProps.isEmpty()) {
-        this.hiddenSysProps.clear();
-        this.hiddenSysProps.addAll(hiddenSysProps);
-      }
-      return this;
-    }
-
     public MetricsConfigBuilder setMetricReporterPlugins(PluginInfo[] metricReporterPlugins) {
       this.metricReporterPlugins =
           metricReporterPlugins != null ? metricReporterPlugins : new PluginInfo[0];
@@ -228,7 +206,6 @@ public class MetricsConfig {
       return new MetricsConfig(
           enabled,
           metricReporterPlugins,
-          hiddenSysProps,
           counterSupplier,
           meterSupplier,
           timerSupplier,
diff --git a/solr/core/src/java/org/apache/solr/core/NodeConfig.java b/solr/core/src/java/org/apache/solr/core/NodeConfig.java
index fbf5cc4fe65..9006951e3dc 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeConfig.java
@@ -25,13 +25,15 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
@@ -68,6 +70,9 @@ public class NodeConfig {
 
   private final String modules;
 
+  private final Set<String> hiddenSysProps;
+  private final Predicate<String> hiddenSysPropPattern;
+
   private final PluginInfo shardHandlerFactoryConfig;
 
   private final UpdateShardHandlerConfig updateShardHandlerConfig;
@@ -145,7 +150,8 @@ public class NodeConfig {
       Set<Path> allowPaths,
       List<String> allowUrls,
       String configSetServiceClass,
-      String modules) {
+      String modules,
+      Set<String> hiddenSysProps) {
     // all Path params here are absolute and normalized.
     this.nodeName = nodeName;
     this.coreRootDirectory = coreRootDirectory;
@@ -180,6 +186,10 @@ public class NodeConfig {
     this.allowUrls = allowUrls;
     this.configSetServiceClass = configSetServiceClass;
     this.modules = modules;
+    this.hiddenSysProps = hiddenSysProps;
+    this.hiddenSysPropPattern =
+        Pattern.compile("^(" + String.join("|", hiddenSysProps) + ")$", Pattern.CASE_INSENSITIVE)
+            .asMatchPredicate();
 
     if (this.cloudConfig != null && this.getCoreLoadThreadCount(false) < 2) {
       throw new SolrException(
@@ -464,6 +474,25 @@ public class NodeConfig {
     return modules;
   }
 
+  /** Returns the list of hidden system properties. The list values are regex expressions */
+  public Set<String> getHiddenSysProps() {
+    return hiddenSysProps;
+  }
+
+  /** Returns whether a given system property is hidden */
+  public boolean isSysPropHidden(String sysPropName) {
+    return hiddenSysPropPattern.test(sysPropName);
+  }
+
+  public static final String REDACTED_SYS_PROP_VALUE = "--REDACTED--";
+
+  /** Returns the a system property value, or "--REDACTED--" if the system property is hidden */
+  public String getRedactedSysPropValue(String sysPropName) {
+    return hiddenSysPropPattern.test(sysPropName)
+        ? REDACTED_SYS_PROP_VALUE
+        : System.getProperty(sysPropName);
+  }
+
   // Finds every jar in each folder and adds it to shardLib, then reloads Lucene SPI
   private void addFoldersToSharedLib(Set<String> libDirs) {
     boolean modified = false;
@@ -551,6 +580,7 @@ public class NodeConfig {
     private Path configSetBaseDirectory;
     private String sharedLibDirectory;
     private String modules;
+    private String hiddenSysProps;
     private PluginInfo shardHandlerFactoryConfig;
     private UpdateShardHandlerConfig updateShardHandlerConfig = UpdateShardHandlerConfig.DEFAULT;
     private String configSetServiceClass;
@@ -595,16 +625,17 @@ public class NodeConfig {
         "org.apache.solr.handler.admin.ConfigSetsHandler";
 
     public static final Set<String> DEFAULT_HIDDEN_SYS_PROPS =
-        new HashSet<>(
-            Arrays.asList(
-                "javax.net.ssl.keyStorePassword",
-                "javax.net.ssl.trustStorePassword",
-                "basicauth",
-                "zkDigestPassword",
-                "zkDigestReadonlyPassword",
-                "aws.secretKey", // AWS SDK v1
-                "aws.secretAccessKey", // AWS SDK v2
-                "http.proxyPassword"));
+        Set.of(
+            "javax\\.net\\.ssl\\.keyStorePassword",
+            "javax\\.net\\.ssl\\.trustStorePassword",
+            "basicauth",
+            "zkDigestPassword",
+            "zkDigestReadonlyPassword",
+            "aws\\.secretKey", // AWS SDK v1
+            "aws\\.secretAccessKey", // AWS SDK v2
+            "http\\.proxyPassword",
+            ".*password.*",
+            ".*secret.*");
 
     public NodeConfigBuilder(String nodeName, Path solrHome) {
       this.nodeName = nodeName;
@@ -779,6 +810,44 @@ public class NodeConfig {
       return this;
     }
 
+    public NodeConfigBuilder setHiddenSysProps(String hiddenSysProps) {
+      this.hiddenSysProps = hiddenSysProps;
+      return this;
+    }
+
+    /**
+     * Finds list of hiddenSysProps requested by system property or environment variable or the
+     * default
+     *
+     * @return set of raw hidden sysProps, may be regex
+     */
+    private Set<String> resolveHiddenSysPropsFromSysPropOrEnvOrDefault(String hiddenSysProps) {
+      // Fall back to sysprop and env.var if nothing configured through solr.xml
+      if (!StrUtils.isNotNullOrEmpty(hiddenSysProps)) {
+        String fromProps = System.getProperty("solr.hiddenSysProps");
+        // Back-compat for solr 9x
+        // DEPRECATED: Remove in 10.0
+        if (StrUtils.isNotNullOrEmpty(fromProps)) {
+          fromProps = System.getProperty("solr.redaction.system.pattern");
+        }
+        String fromEnv = System.getenv("SOLR_HIDDEN_SYS_PROPS");
+        if (StrUtils.isNotNullOrEmpty(fromProps)) {
+          hiddenSysProps = fromProps;
+        } else if (StrUtils.isNotNullOrEmpty(fromEnv)) {
+          hiddenSysProps = fromEnv;
+        }
+      }
+      Set<String> hiddenSysPropSet = Collections.emptySet();
+      if (hiddenSysProps != null) {
+        hiddenSysPropSet =
+            StrUtils.splitSmart(hiddenSysProps, ',').stream()
+                .map(String::trim)
+                .filter(s -> !s.isEmpty())
+                .collect(Collectors.toSet());
+      }
+      return hiddenSysPropSet.isEmpty() ? DEFAULT_HIDDEN_SYS_PROPS : hiddenSysPropSet;
+    }
+
     public NodeConfig build() {
       // if some things weren't set then set them now.  Simple primitives are set on the field
       // declaration
@@ -818,7 +887,8 @@ public class NodeConfig {
           allowPaths,
           allowUrls,
           configSetServiceClass,
-          modules);
+          modules,
+          resolveHiddenSysPropsFromSysPropOrEnvOrDefault(hiddenSysProps));
     }
 
     public NodeConfigBuilder setSolrResourceLoader(SolrResourceLoader resourceLoader) {
diff --git a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
index 723acab4709..f6d1eb78d93 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
@@ -172,6 +172,10 @@ public class SolrXmlConfig {
     if (cloudConfig != null) configBuilder.setCloudConfig(cloudConfig);
     configBuilder.setBackupRepositoryPlugins(
         getBackupRepositoryPluginInfos(root.get("backup").getAll("repository")));
+    // <metrics><hiddenSysProps></metrics> will be removed in Solr 10, but until then, use it if a
+    // <hiddenSysProps> is not provided under <solr>.
+    // Remove this line in 10.0
+    configBuilder.setHiddenSysProps(getHiddenSysProps(root.get("metrics")));
     configBuilder.setMetricsConfig(getMetricsConfig(root.get("metrics")));
     configBuilder.setFromZookeeper(fromZookeeper);
     configBuilder.setDefaultZkHost(defaultZkHost);
@@ -360,6 +364,9 @@ public class SolrXmlConfig {
               case "modules":
                 builder.setModules(it.txt());
                 break;
+              case "hiddenSysProps":
+                builder.setHiddenSysProps(it.txt());
+                break;
               case "allowPaths":
                 builder.setAllowPaths(separatePaths(it.txt()));
                 break;
@@ -404,6 +411,13 @@ public class SolrXmlConfig {
     return Arrays.asList(COMMA_SEPARATED_PATTERN.split(commaSeparatedString));
   }
 
+  private static Set<String> separateStringsToSet(String commaSeparatedString) {
+    if (StrUtils.isNullOrEmpty(commaSeparatedString)) {
+      return Collections.emptySet();
+    }
+    return Set.of(COMMA_SEPARATED_PATTERN.split(commaSeparatedString));
+  }
+
   private static Set<Path> separatePaths(String commaSeparatedString) {
     if (StrUtils.isNullOrEmpty(commaSeparatedString)) {
       return Collections.emptySet();
@@ -673,11 +687,7 @@ public class SolrXmlConfig {
     }
 
     PluginInfo[] reporterPlugins = getMetricReporterPluginInfos(metrics);
-    Set<String> hiddenSysProps = getHiddenSysProps(metrics);
-    return builder
-        .setMetricReporterPlugins(reporterPlugins)
-        .setHiddenSysProps(hiddenSysProps)
-        .build();
+    return builder.setMetricReporterPlugins(reporterPlugins).build();
   }
 
   private static Object decodeNullValue(Object o) {
@@ -721,20 +731,24 @@ public class SolrXmlConfig {
     return configs.toArray(new PluginInfo[configs.size()]);
   }
 
-  private static Set<String> getHiddenSysProps(ConfigNode metrics) {
+  /**
+   * Deprecated as of 9.3, will be removed in 10.0
+   *
+   * @param metrics configNode for the metrics
+   * @return a comma-separated list of hidden Sys Props
+   */
+  @Deprecated(forRemoval = true, since = "9.3")
+  private static String getHiddenSysProps(ConfigNode metrics) {
     ConfigNode p = metrics.get("hiddenSysProps");
-    if (!p.exists()) return NodeConfig.NodeConfigBuilder.DEFAULT_HIDDEN_SYS_PROPS;
+    if (!p.exists()) return null;
     Set<String> props = new HashSet<>();
     p.forEachChild(
         it -> {
-          if (it.name().equals("str") && StrUtils.isNotNullOrEmpty(it.txt())) props.add(it.txt());
+          if (it.name().equals("str") && StrUtils.isNotNullOrEmpty(it.txt()))
+            props.add(Pattern.quote(it.txt()));
           return Boolean.TRUE;
         });
-    if (props.isEmpty()) {
-      return NodeConfig.NodeConfigBuilder.DEFAULT_HIDDEN_SYS_PROPS;
-    } else {
-      return props;
-    }
+    return String.join(",", props);
   }
 
   private static PluginInfo getPluginInfo(ConfigNode cfg) {
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
index b982b468935..455bd361247 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
@@ -48,7 +48,7 @@ public class InfoHandler extends RequestHandlerBase {
   public InfoHandler(final CoreContainer coreContainer) {
     this.coreContainer = coreContainer;
     handlers.put("threads", new ThreadDumpHandler());
-    handlers.put("properties", new PropertiesRequestHandler());
+    handlers.put("properties", new PropertiesRequestHandler(coreContainer));
     handlers.put("logging", new LoggingHandler(coreContainer));
     handlers.put("system", new SystemInfoHandler(coreContainer));
     if (coreContainer.getHealthCheckHandler() == null) {
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 2a65e6a5c62..8658adf3c52 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
@@ -25,45 +25,49 @@ import org.apache.solr.api.AnnotatedApi;
 import org.apache.solr.api.Api;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.NodeConfig;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.admin.api.NodePropertiesAPI;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.security.AuthorizationContext;
-import org.apache.solr.util.RedactionUtils;
 
 /**
  * @since solr 1.2
  */
 public class PropertiesRequestHandler extends RequestHandlerBase {
 
-  public static final String REDACT_STRING = RedactionUtils.getRedactString();
+  private CoreContainer cc;
+
+  public PropertiesRequestHandler() {
+    this(null);
+  }
+
+  public PropertiesRequestHandler(CoreContainer cc) {
+    super();
+    this.cc = cc;
+  }
 
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
     NamedList<String> props = new SimpleOrderedMap<>();
     String name = req.getParams().get(NAME);
+    NodeConfig nodeConfig = getCoreContainer(req).getNodeConfig();
     if (name != null) {
-      String property = getSecuredPropertyValue(name);
+      String property = nodeConfig.getRedactedSysPropValue(name);
       props.add(name, property);
     } else {
       Enumeration<?> enumeration = System.getProperties().propertyNames();
       while (enumeration.hasMoreElements()) {
         name = (String) enumeration.nextElement();
-        props.add(name, getSecuredPropertyValue(name));
+        props.add(name, nodeConfig.getRedactedSysPropValue(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
@@ -90,4 +94,9 @@ public class PropertiesRequestHandler extends RequestHandlerBase {
   public Name getPermissionName(AuthorizationContext request) {
     return Name.CONFIG_READ_PERM;
   }
+
+  private CoreContainer getCoreContainer(SolrQueryRequest req) {
+    CoreContainer coreContainer = req.getCoreContainer();
+    return coreContainer == null ? cc : coreContainer;
+  }
 }
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 e9c5061c7e1..6f30e06b340 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
@@ -42,6 +42,7 @@ import org.apache.solr.api.Api;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.NodeConfig;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.admin.api.NodeSystemInfoAPI;
@@ -53,7 +54,6 @@ import org.apache.solr.security.AuthorizationPlugin;
 import org.apache.solr.security.PKIAuthenticationPlugin;
 import org.apache.solr.security.RuleBasedAuthorizationPluginBase;
 import org.apache.solr.util.RTimer;
-import org.apache.solr.util.RedactionUtils;
 import org.apache.solr.util.stats.MetricUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -66,8 +66,6 @@ import org.slf4j.LoggerFactory;
 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.
    * This property will be logged as a suggested workaround if any problems are noticed when doing
@@ -152,7 +150,8 @@ public class SystemInfoHandler extends RequestHandlerBase {
     }
 
     rsp.add("lucene", getLuceneInfo());
-    rsp.add("jvm", getJvmInfo());
+    NodeConfig nodeConfig = getCoreContainer(req).getNodeConfig();
+    rsp.add("jvm", getJvmInfo(nodeConfig));
     rsp.add("security", getSecurityInfo(req));
     rsp.add("system", getSystemInfo());
     if (solrCloudMode) {
@@ -235,7 +234,7 @@ public class SystemInfoHandler extends RequestHandlerBase {
   }
 
   /** Get JVM Info - including memory info */
-  public static SimpleOrderedMap<Object> getJvmInfo() {
+  public static SimpleOrderedMap<Object> getJvmInfo(NodeConfig nodeConfig) {
     SimpleOrderedMap<Object> jvm = new SimpleOrderedMap<>();
 
     final String javaVersion = System.getProperty("java.specification.version", "unknown");
@@ -304,7 +303,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", getInputArgumentsRedacted(mx));
+      jmx.add("commandLineArgs", getInputArgumentsRedacted(nodeConfig, mx));
 
       jmx.add("startTime", new Date(mx.getStartTime()));
       jmx.add("upTimeMS", mx.getUptime());
@@ -408,14 +407,18 @@ public class SystemInfoHandler extends RequestHandlerBase {
     return newSizeAndUnits;
   }
 
-  private static List<String> getInputArgumentsRedacted(RuntimeMXBean mx) {
+  private static List<String> getInputArgumentsRedacted(NodeConfig nodeConfig, RuntimeMXBean mx) {
     List<String> list = new ArrayList<>();
     for (String arg : mx.getInputArguments()) {
       if (arg.startsWith("-D")
           && arg.contains("=")
-          && RedactionUtils.isSystemPropertySensitive(arg.substring(2, arg.indexOf('=')))) {
+          && nodeConfig.isSysPropHidden(arg.substring(2, arg.indexOf('=')))) {
         list.add(
-            String.format(Locale.ROOT, "%s=%s", arg.substring(0, arg.indexOf('=')), REDACT_STRING));
+            String.format(
+                Locale.ROOT,
+                "%s=%s",
+                arg.substring(0, arg.indexOf('=')),
+                NodeConfig.REDACTED_SYS_PROP_VALUE));
       } else {
         list.add(arg);
       }
diff --git a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
index 47eb8198e82..d5988f01990 100644
--- a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
+++ b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
@@ -42,7 +42,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Properties;
-import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -415,7 +414,7 @@ public class CoreContainerProvider implements ServletContextListener {
   private void setupJvmMetrics(CoreContainer coresInit, MetricsConfig config) {
     metricManager = coresInit.getMetricManager();
     registryName = SolrMetricManager.getRegistryName(Group.jvm);
-    final Set<String> hiddenSysProps = coresInit.getConfig().getMetricsConfig().getHiddenSysProps();
+    final NodeConfig nodeConfig = coresInit.getConfig();
     try {
       metricManager.registerAll(
           registryName, new AltBufferPoolMetricSet(), ResolutionStrategy.IGNORE, "buffers");
@@ -455,8 +454,7 @@ public class CoreContainerProvider implements ServletContextListener {
                   System.getProperties()
                       .forEach(
                           (k, v) -> {
-                            //noinspection SuspiciousMethodCalls
-                            if (!hiddenSysProps.contains(k)) {
+                            if (!nodeConfig.isSysPropHidden(String.valueOf(k))) {
                               map.putNoEx(String.valueOf(k), v);
                             }
                           }));
diff --git a/solr/core/src/java/org/apache/solr/util/RedactionUtils.java b/solr/core/src/java/org/apache/solr/util/RedactionUtils.java
deleted file mode 100644
index d6e866d53f5..00000000000
--- a/solr/core/src/java/org/apache/solr/util/RedactionUtils.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.Comparator;
-import java.util.Map;
-import java.util.TreeMap;
-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--";
-  public static final String NODE_REDACTION_PREFIX = "N_";
-  public static final String COLL_REDACTION_PREFIX = "COLL_";
-
-  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.
-   */
-  public static boolean isSystemPropertySensitive(String name) {
-    return redactSystemProperty && pattern.matcher(name).matches();
-  }
-
-  /**
-   * @return redaction string to be used instead of the value.
-   */
-  public static String getRedactString() {
-    return REDACT_STRING;
-  }
-
-  public static void setRedactSystemProperty(boolean redactSystemProperty) {
-    RedactionUtils.redactSystemProperty = redactSystemProperty;
-  }
-
-  /**
-   * Replace actual names found in a string with redacted names.
-   *
-   * @param redactions a map of original to redacted names
-   * @param data string to redact
-   * @return redacted string where all actual names have been replaced.
-   */
-  public static String redactNames(Map<String, String> redactions, String data) {
-    // replace the longest first to avoid partial replacements
-    Map<String, String> sorted =
-        new TreeMap<>(
-            Comparator.comparing(String::length).reversed().thenComparing(String::compareTo));
-    sorted.putAll(redactions);
-    for (Map.Entry<String, String> entry : sorted.entrySet()) {
-      data = data.replaceAll("\\Q" + entry.getKey() + "\\E", entry.getValue());
-    }
-    return data;
-  }
-}
diff --git a/solr/core/src/test-files/solr/solr-hiddensysprops.xml b/solr/core/src/test-files/solr/solr-hiddensysprops.xml
index 20e5aec2175..eb638daf819 100644
--- a/solr/core/src/test-files/solr/solr-hiddensysprops.xml
+++ b/solr/core/src/test-files/solr/solr-hiddensysprops.xml
@@ -17,12 +17,8 @@
 -->
 
 <solr>
+ <str name="hiddenSysProps">foo,bar,baz</str>
  <metrics>
-  <hiddenSysProps>
-    <str>foo</str>
-    <str>bar</str>
-    <str>baz</str>
-  </hiddenSysProps>
   <!-- this reporter doesn't specify 'group' or 'registry', it will be instantiated for any group. -->
   <reporter name="universal" class="org.apache.solr.metrics.reporters.MockMetricReporter">
     <str name="configurable">configured</str>
diff --git a/solr/core/src/test-files/solr/solr-metricsconfig.xml b/solr/core/src/test-files/solr/solr-metricsconfig.xml
index 3e656826406..acab241f3f1 100644
--- a/solr/core/src/test-files/solr/solr-metricsconfig.xml
+++ b/solr/core/src/test-files/solr/solr-metricsconfig.xml
@@ -17,6 +17,7 @@
 -->
 
 <solr>
+ <str name="hiddenSysProps">foo,bar,baz</str>
  <metrics enabled="${metricsEnabled:true}">
   <suppliers>
     <counter class="${counter.class:}">
@@ -48,11 +49,6 @@
       <long name="window">${histogram.window:-1}</long>
     </histogram>
   </suppliers>
-  <hiddenSysProps>
-    <str>foo</str>
-    <str>bar</str>
-    <str>baz</str>
-  </hiddenSysProps>
   <!-- this reporter doesn't specify 'group' or 'registry', it will be instantiated for any group. -->
   <reporter name="universal" class="org.apache.solr.metrics.reporters.MockMetricReporter">
     <str name="configurable">configured</str>
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
index 7332d8525a5..c9120121586 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/PropertiesRequestHandlerTest.java
@@ -22,14 +22,13 @@ import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.util.NamedList;
-import org.apache.solr.util.RedactionUtils;
+import org.apache.solr.core.NodeConfig;
 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 {
@@ -38,23 +37,17 @@ public class PropertiesRequestHandlerTest extends SolrTestCaseJ4 {
 
   @Test
   public void testRedaction() throws Exception {
-    RedactionUtils.setRedactSystemProperty(true);
-    for (String propName : new String[] {"some.password", "javax.net.ssl.trustStorePassword"}) {
+    for (String propName :
+        new String[] {
+          "some.password", "javax.net.ssl.trustStorePassword", "basicauth", "some.Secret"
+        }) {
       System.setProperty(propName, PASSWORD);
       NamedList<Object> 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<Object> properties = readProperties();
-
-      assertEquals("Failed to *not* redact " + propName, PASSWORD, properties.get(propName));
+      assertEquals(
+          "Failed to redact " + propName,
+          NodeConfig.REDACTED_SYS_PROP_VALUE,
+          properties.get(propName));
     }
   }
 
diff --git a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java
index ec1207e63f5..970818a27d4 100644
--- a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java
@@ -115,13 +115,12 @@ public class JvmMetricsTest extends SolrJettyTestBase {
     String solrXml = Files.readString(home.resolve("solr.xml"), StandardCharsets.UTF_8);
     NodeConfig config = SolrXmlConfig.fromString(home, solrXml);
     NodeConfig.NodeConfigBuilder.DEFAULT_HIDDEN_SYS_PROPS.forEach(
-        s -> assertTrue(s, config.getMetricsConfig().getHiddenSysProps().contains(s)));
+        s -> assertTrue(s, config.isSysPropHidden(s)));
 
     // custom config
     solrXml = Files.readString(home.resolve("solr-hiddensysprops.xml"), StandardCharsets.UTF_8);
     NodeConfig config2 = SolrXmlConfig.fromString(home, solrXml);
-    Arrays.asList("foo", "bar", "baz")
-        .forEach(s -> assertTrue(s, config2.getMetricsConfig().getHiddenSysProps().contains(s)));
+    Arrays.asList("foo", "bar", "baz").forEach(s -> assertTrue(s, config2.isSysPropHidden(s)));
   }
 
   @Test
diff --git a/solr/solr-ref-guide/modules/configuration-guide/pages/configuring-solr-xml.adoc b/solr/solr-ref-guide/modules/configuration-guide/pages/configuring-solr-xml.adoc
index 5de1e855031..26743e7d9af 100644
--- a/solr/solr-ref-guide/modules/configuration-guide/pages/configuring-solr-xml.adoc
+++ b/solr/solr-ref-guide/modules/configuration-guide/pages/configuring-solr-xml.adoc
@@ -305,6 +305,22 @@ This is the same system property used in the `_default` configset for the xref:c
 <maxBooleanClauses>${solr.max.booleanClauses:1024}</maxBooleanClauses>
 ----
 
+[#hiddenSysProps]
+`hiddenSysProps`::
++
+[%autowidth,frame=none]
+|===
+|Optional |Default: _see description_
+|===
++
+Comma-separated list of regex patterns to match sysProps that should be redacted to hide sensitive information.
++
+The allow-list can also be configured with the `solr.hiddenSysProps` system property,
+or via the `SOLR_HIDDEN_SYS_PROPS` environment variable.
++
+By default, Solr will hide all basicAuth, AWS, ZK or SSL secret sysProps. It will also hide any sysProp that contains
+"password" or "secret" in it.
+
 === The <solrcloud> Element
 
 This element defines several parameters that relate so SolrCloud.
diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc
index d8cc104bc7b..7df6c90c0af 100644
--- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc
+++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc
@@ -81,6 +81,17 @@ Please refer to the https://solr.apache.org/downloads.html[Solr Downloads] site
 Older configuration now does nothing.
 Instead, set an env var: SOLR_ENABLE_REMOTE_STREAMING or SOLR_ENABLE_STREAM_BODY or system property equivalents.
 
+* The method for specifying sysProps that contain sensitive information has been streamlined.
+Now the sysProp `-Dsolr.hiddenSysProps` or the envVar `SOLR_HIDDEN_SYS_PROPS` are available to provide a comma-separated
+list of patterns to match sysProps that should be hidden or redacted.
+Please see the xref:configuration-guide:configuring-solr-xml.adoc#hiddenSysProps[hiddenSysProps section] for more information.
++
+The sysProp `-Dsolr.redaction.system.pattern` has been deprecated, use the above options instead.
++
+The `<hiddenSysProps>` solr.xml element under `<metrics>` has been deprecated.
+Instead, use the xref:configuration-guide:configuring-solr-xml.adoc#hiddenSysProps[<hiddenSysProps>] tag under `<solr>`, which accepts a comma-separated string.
+
+
 === Official Docker Image
 * The customization of the Official Solr Dockerfile has been changed.
 The customization options `SOLR_DOWNLOAD_URL`, `SOLR_CLOSER_URL`, `SOLR_DIST_URL` and `SOLR_ARCHIVE_URL`, have been removed.