-
Notifications
You must be signed in to change notification settings - Fork 324
Allow for secure LDAP password settings #5323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Allow for secure LDAP password settings #5323
Conversation
Signed-off-by: Chris White <chriswhite199@gmail.com>
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5323 +/- ##
==========================================
+ Coverage 71.68% 71.75% +0.07%
==========================================
Files 384 386 +2
Lines 23869 23877 +8
Branches 3647 3647
==========================================
+ Hits 17111 17134 +23
+ Misses 4928 4912 -16
- Partials 1830 1831 +1
🚀 New features to boost your workflow:
|
See associated PR for implementation: opensearch-project/security#5323 Signed-off-by: Chris White <chriswhite199@gmail.com>
Thank you for this PR @chriswhite199 ! Its been on my agenda to support the keystore here for some time now :) |
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
import static org.opensearch.security.ssl.SecureSSLSettings.SSLSetting.SECURITY_SSL_HTTP_PEMKEY_PASSWORD; | ||
|
||
public class SecureSSLSettingsTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this class be renamed. It seems to only have 1 test now. It doesn't make sense to have this test class file.
} | ||
|
||
@Test | ||
public void testShouldFavorSecureOverInsecureSetting() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
try { | ||
return (SecurableLegacySetting) field.get(null); | ||
} catch (IllegalAccessException e) { | ||
throw new RuntimeException("Unable to access field: " + field.getName(), e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be SettingsException
|
||
/** | ||
* Alternative to InsecureStringSetting, which doesn't raise an exception if allow_insecure_settings is false, but | ||
* instead log.WARNs the violation. This is to appease a potential cyclic dependency between commons-utils |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. For my curiosity, how did you discover this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't remember now, but setting allow_insecure_settings
and defining the property as secure would raise an exception - hence the original implementation crafted two properties - password
and password_secure
to not break existing installations.
If you look at the original review for #2296, @peternied and I were discussing the integration test issues related to this - hopefully that comment can help you here
@chriswhite199 have you tested this outside of the unit tests? I think there may be larger structural changes required for settings in security yaml files to be able to fetch values from the keystore. For settings that reside in |
I have not yet (for authn / authz plugins, I know it works for tls certs for https / transport as this was tested in the original PR for that scope). I didn't think it would be any different for authNZ plugins but I will test and get back to you. On a side not the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this. Secure settings for the security plugin are certainly an important topic.
Still, it feels to me that we have kind of a concept mismatch between the normal security settings (stored in an index) and the secure settings (stored in the opensearch-keystore file on each node).
- It is unclear how the config names in security settings config.yml map to to the setting in the keystore, especially if we have lists of auth domains.
- The processes of config hot reloading (without starting the node) seem to unaligned for security settings and secure settings (I think it is possible, but it will involve quite a few steps).
See below for more elaboration on that.
@@ -184,7 +184,7 @@ private ConnectionInitializer getConnectionInitializer() { | |||
BindConnectionInitializer result = new BindConnectionInitializer(); | |||
|
|||
String bindDn = settings.get(ConfigConstants.LDAP_BIND_DN, null); | |||
String password = settings.get(ConfigConstants.LDAP_PASSWORD, null); | |||
String password = ConfigConstants.LDAP_PASSWORD.getSetting(settings); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the complete name of the secure setting for LDAP passwords?
In the security plugin, you can configure more than one LDAP authentication domain. This can then also have different passwords. If I get this right, we won't be able to configure more than one LDAP domain with different passwords, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The AuthenticationBackend settings are created from a subset of the properties here:
So it should allow for each individual backend to have it's own secure value. But examining the above only the settings from the index are used - there are no secure settings subset created and used when configuring the settings object for the backend.
Also a misunderstanding on my part renders this whole PR void, as the settings are centralized in an index, not pulled from a yaml on each node. We could allow for pulling the secure settings from the keystore by amending the above to subset and inject secure settings when creating the Settings for each backend, but then you'd almost certainly run into issues where the admin would have to remember to update the keystore on every node when secure values are set, and when changed (also leading to confusion if those keystores are not in sync - where auth make work against one node, but fail against another).
I'm leaning towards withdrawing this PR in light of the above, unless anyone has any ideas on a way forward?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chriswhite199 I remember taking a look a few months ago when looking at #4004 and believe it will take some changes in OpenSearch core.
I'll take another look into what's required for the security config to support reading from the keystore and reply back ASAP. (It may take a few days).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is the direction the PR should take (in relation to #4004):
- Amend the securityadmin script to allow for the passwords / secret settings to be sourced from the opensearch keystore (or config.yaml for legacy reasons)
- securityadmin still stores the secrets in the .opendistro-security index
My original intent of this PR is to avoid having passwords in cleartext config files on the filesystem (currently an admin has to remember to remove the config.yaml or redact the passwords after syncing).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is a good idea to file an RFC about this.
We also have the trouble that there is not only securityadmin, but - optionally - the REST API can be also used to change/retrieve the config.yml configuration.
.put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") | ||
.put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") | ||
.put(ConfigConstants.LDAP_BIND_DN, "cn=Captain Spock,ou=people,o=TEST") | ||
.setSecureSettings(mockSecureSettings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need one integration test for secure settings. You can find integration tests for LDAP here:
/** | ||
* Wrapper for legacy settings that support a secure variant located in the Keystore. | ||
* <p> | ||
* Secure name is the insecure name with "_secure" appended to it. | ||
*/ | ||
public class SecurableLegacySetting { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit confused about the word "legacy" here. Could you please elaborate what "legacy settings" are?
return Arrays.stream(SecureSSLSettings.class.getDeclaredFields()) | ||
.filter(field -> SecurableLegacySetting.class.isAssignableFrom(field.getType())) | ||
.map(field -> { | ||
try { | ||
return (SecurableLegacySetting) field.get(null); | ||
} catch (IllegalAccessException e) { | ||
throw new RuntimeException("Unable to access field: " + field.getName(), e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It took me a while to understand this until I figured out that you are using reflection to retrieve static members (as a replacement for enum.values()).
As this is solution is maybe a bit non-obvious, I would recommend to add a comment somewhere to explain the approach.
Another question: With the SecurityManager, this would needed to be wrapped in a doPrivileged() block in order to be able to do reflection. With the new security manager replacement, did this need go away?
@chriswhite199 Are you still working on this PR? |
Description
password
andpem_password
were only configurable in an insecure manner (plaintext in yml or via cli / env variables)If this PR is approved, i plan to submit similar PRs for the other secure settings using the
SecurableLegacySetting
class:pemkey_password, client_secret, passphrase
(https://docs.opensearch.org/docs/latest/security/authentication-backends/openid-connect/)signing_key
(https://docs.opensearch.org/docs/latest/security/authentication-backends/jwt/)signature_private_key_password, exchange_key, pemkey_password
(https://docs.opensearch.org/docs/latest/security/authentication-backends/saml/)Issues Resolved
Is this a backport: no
Do these changes introduce new permission(s) to be displayed in the static dropdown on the front-end: no
Testing
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.