Expose dynamic audit config via cluster settings in SSL-only mode - #6392
Expose dynamic audit config via cluster settings in SSL-only mode#6392Taiwo435 wants to merge 1 commit into
Conversation
PR Reviewer Guide 🔍(Review updated until commit 18e5c32)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 18e5c32 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 55910ed
Suggestions up to commit a5eef32
Suggestions up to commit d32b363
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6392 +/- ##
==========================================
+ Coverage 75.33% 75.47% +0.13%
==========================================
Files 456 456
Lines 30075 30143 +68
Branches 4564 4571 +7
==========================================
+ Hits 22657 22749 +92
+ Misses 5297 5270 -27
- Partials 2121 2124 +3
🚀 New features to boost your workflow:
|
DarshitChanpura
left a comment
There was a problem hiding this comment.
Nice PR — the write-up made this easy to follow, and the diagnosis is right. I checked the pieces I was worried about: isSslOnlyMode() is the instance field so that's fine, and Property.Sensitive only gates writes, it doesn't filter reads, so narrowing the filter is genuinely the right fix here. Pulling the three Filtered flags onto http_endpoints/ciphers/protocols in the same PR is correct too — without them the narrowing would leak those, so they belong here.
I'm good with the design tradeoff. In ssl-only mode the audit config only lives in cluster settings and there's no authz layer anyway, so exposing the non-secret dynamic config to settings readers is consistent with what that mode already is. Not going to bikeshed keeping compliance.* filtered.
One thing I do want before merging: the safety story is now "every secret under config.* must be individually Property.Filtered," since the wildcard no longer covers them in ssl-only. That's fine today, but it's a trap for whoever adds the next sink credential. Can you add a short comment at the config.* registration block spelling that out, plus a test that fails if a config.* key with a secret-ish name isn't Filtered? Something like:
@Test
public void allSensitiveConfigSettingsAreFiltered() {
Settings disabled = Settings.builder().put(ConfigConstants.SECURITY_DISABLED, true).build();
Pattern secretish = Pattern.compile("password|username|token|webhook|pem|salt|jks|http_endpoints|ssl_");
for (Setting<?> s : new OpenSearchSecurityPlugin(disabled, null).getSettings()) {
String key = s.getKey();
if (key.startsWith(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX) && secretish.matcher(key).find()) {
assertThat(key + " must be Property.Filtered", s.getProperties().contains(Setting.Property.Filtered), equalTo(true));
}
}
}(Building the plugin with disabled=true is the easy way to reach getSettings() without dragging in configPath/TLS setup — a plain new OpenSearchSecurityPlugin(Settings.EMPTY, null) throws. If it fights the unit-test module for any reason, just fold the same secret-not-present assertions into StandaloneAuditSettingsFilterTest.)
Couple of small things, non-blocking: the tests hardcode the plugins.security.audit.compliance. prefix — worth exposing the constant from SecuritySettings so it can't drift. And add the skip-changelog label. Also worth a line in the description that http_endpoints/ciphers/protocols will disappear from settings output in FGAC/default too, so we don't get surprised on backport.
d32b363 to
a5eef32
Compare
PR Code Analyzer ❗AI-powered 'Code-Diff-Analyzer' found issues on commit a5eef32. ⛔ Hard block: Issues at High severity or above will block this PR from merging.
The table above displays the top 10 most important findings. Pull Requests Author(s): Please update your Pull Request according to the report above. Repository Maintainer(s): You can Thanks. |
|
Persistent review updated to latest commit a5eef32 |
a5eef32 to
55910ed
Compare
|
Persistent review updated to latest commit 55910ed |
In SSL-only (standalone audit) mode there is no security index, so the audit configuration is stored in cluster settings and read back via GET _cluster/settings. getSettingsFilter() stripped the entire plugins.security.audit.* subtree, which also hid the non-secret dynamic config the dashboards audit panel needs. Narrow the filter in SSL-only mode to strip only the credential-bearing group settings (plugins.security.audit.endpoints.* / .routes.*), exposing the dynamic config (config.* and compliance.*). FGAC and other modes keep the original broad filter. Filtered secrets (username/password/webhook.url, pem*, salt) remain stripped by core regardless. Also mark three static external-sink settings (http_endpoints, enabled_ssl_ciphers, enabled_ssl_protocols) as Property.Filtered. These were previously hidden only by the broad wildcard; narrowing it would otherwise disclose backend audit host/TLS config to unauthenticated clients in SSL-only mode. They are static sink infrastructure, not panel-managed dynamic config, so filtering them has no functional impact. Add integration tests covering the SSL-only read path, credential and infrastructure hiding, and the unchanged FGAC behavior. Signed-off-by: Muzzamil Jolaade <muzzajol@amazon.com>
55910ed to
18e5c32
Compare
|
Persistent review updated to latest commit 18e5c32 |
Description
Narrow the audit settings filter in SSL-only (standalone audit) mode so the non-secret dynamic audit configuration is readable via
GET _cluster/settings, while continuing to hide the credential-bearing sink settings.Why these changes are required?
In SSL-only mode there is no
.opendistro_securityindex, so the audit configuration is stored in cluster settings and is managed throughPUT/GET _cluster/settings. The standalone audit configuration panel in the security dashboards plugin reads its state back throughGET _cluster/settings.OpenSearchSecurityPlugin.getSettingsFilter()stripped the entireplugins.security.audit.*subtree from settings responses, which also hid the ~17 non-secret dynamic config keys the panel needs. Writes worked (the filter only affects reads), but reads came back empty — the panel could not display the current configuration.What is the old behavior before changes and new behavior after changes?
getSettingsFilter()always addedplugins.security.audit.*, so the whole subtree — including the non-secret dynamic config — was stripped from every settings response in every mode.plugins.security.audit.endpoints.*andplugins.security.audit.routes.*); the dynamic config (plugins.security.audit.config.*andplugins.security.audit.compliance.*) is now returned. FGAC and all other modes keep the original broadplugins.security.audit.*filter, because their real audit config lives in the security index, not cluster settings.Credential safety is preserved:
username,password,webhook.url,pem*,salt) are registered withProperty.Filtered, so OpenSearch core strips them from settings responses regardless of this filter.Property.Filtered, so they are kept out of responses by the retainedendpoints.*/routes.*filters.Additionally, three static external-sink settings under
config.*—http_endpoints(the backend audit host list),enabled_ssl_ciphers, andenabled_ssl_protocols— were previously hidden only by the broadplugins.security.audit.*wildcard and carry noProperty.Filteredannotation. Narrowing the wildcard would have exposed this infrastructure topology to unauthenticated clients in SSL-only mode viaGET _nodes/settings. These are static (non-dynamic) sink settings the audit panel does not manage, so they are now registeredProperty.Filteredalongside their credential siblings — closing the disclosure in all modes with no functional impact (the sink still reads them directly from settings).Note for backport: Adding Property.Filtered to http_endpoints, enabled_ssl_ciphers, and enabled_ssl_protocols means these settings will also disappear from _nodes/settings output in FGAC/default mode (not just SSL-only). This is intentional — they are static external-sink infrastructure config that should not have been exposed to settings readers in any mode.
Issues Resolved
Read-path gap for the standalone audit configuration panel in SSL-only mode.
Testing
Added two integration tests under
src/integrationTest:StandaloneAuditSettingsFilterTest(SSL-only mode):dynamicAuditConfigIsReadableViaClusterSettings— writes a representative slice of the dynamic settings across both prefixes (config.*andcompliance.*) and both value shapes (list + boolean) viaPUT _cluster/settings, then asserts they come back throughGET _cluster/settings. The filter is prefix-based, so covering both subtrees and both value shapes proves all ~17 dynamic keys flow through.auditSecretsAreNotExposedInNodeSettings— statically configures theProperty.Filtereddefault-endpoint secrets plus a secondary endpoint credential and a route credential, then asserts viaGET _nodes/settingsthat the non-secret config surfaces while none of the secret values or theendpoints.*/routes.*keys appear. Also configureshttp_endpoints,enabled_ssl_ciphers, andenabled_ssl_protocolsunderconfig.*and asserts they stay hidden (regression guard for theProperty.Filteredadditions).StandaloneAuditFgacFilterUnchangedTest(FGAC mode):fgacModeStillFiltersAuditSettings— statically configures a dynamic audit setting and a nestedendpoints.*credential, then asserts the broadplugins.security.audit.*filter still strips both fromGET _nodes/settings, confirming the narrowing is gated to SSL-only mode and does not change FGAC behavior. (The values are set statically rather than viaPUTbecause in FGACSecurityFilterblocks a runtime cluster-settings update to a sensitive key unless the caller holds arestapi.roles_enabledrole — a write guard orthogonal to the read filter under test.)Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.