Skip to content
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

Replace BouncyCastle's OpenBSDBCrypt use with password4j for password hashing and verification #4381

Merged

Conversation

dancristiancecoi
Copy link
Contributor

@dancristiancecoi dancristiancecoi commented May 30, 2024

Description

This change removes the usages of Bouncy Castle's OpenBSDBCrypt from the code and replaces it with a FIPS compliant library that supports additional hashing algorithms like PBKDF2, Argon2 and SCrypt.

Furthermore it consolidates the password hashing and verification logic in one place.

This change will require a security review.

Issues Resolved

Related Issues

Testing

Various authentication attempts against a local 3.x deploy

  • Can create users when password is supplied
  • Can create users when a hashed password is supplied
  • Can authenticate an user with a correct password
  • Authentication fails when the password is wrong
  • hash.sh script still works
(base) mtldce@SAS-1B16WV3:~$ curl -k https://localhost:9200 -u admin:$PASSWORD
{
  "name" : "SAS-1B16WV3",
  "cluster_name" : "opensearch",
  "cluster_uuid" : "XE_PodgCSZCaQQjHhWzlJA",
  "version" : {
    "distribution" : "opensearch",
    "number" : "3.0.0-SNAPSHOT",
    "build_type" : "tar",
    "build_hash" : "db5240e06dc08e7d7d03432595bb4d93b0e2e32d",
    "build_date" : "2024-05-30T10:46:41.778575385Z",
    "build_snapshot" : true,
    "lucene_version" : "9.11.0",
    "minimum_wire_compatibility_version" : "2.15.0",
    "minimum_index_compatibility_version" : "2.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

(base) mtldce@SAS-1B16WV3:~$ curl -k https://localhost:9200 -u admin:$WRONG_PASSWORD
Unauthorized

(base) mtldce@SAS-1B16WV3:~$ curl -X PUT -k -s -H "Content-Type: application/json" "https://localhost:9200/_opendistro/_security/api/internalusers/dancecoi" -u admin:$PASSWORD -d $'{"password": "strongpassword1996!","opendistro_security_roles": ["all_access"],"backend_roles": ["all_access"],"attributes": {"attribute1": "value1","attribute2": "value2"}}'
{"status":"CREATED","message":"'dancecoi' created."}


(base) mtldce@SAS-1B16WV3:~$  curl -k -s "https://localhost:9200/_cat/indices?v" -u dancecoi:strongpassword1996!
health status index                        uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   security-auditlog-2024.05.30 ihLqFsJQToS1Jih90b_2Dg   1   1          4            0     57.5kb         57.5kb
green  open   .opendistro_security         w9zyt-LAT3CcwEqsL1p2TQ   1   0         10            0     88.6kb         88.6kb

(base) mtldce@SAS-1B16WV3:~$ curl -k -s "https://localhost:9200/_cat/indices?v" -u dancecoi:wrong_password
Unauthorized(base)


(base) mtldce@SAS-1B16WV3:~$ curl -X PUT -k -s -H "Content-Type: application/json" "https://localhost:9200/_opendistro/_security/api/internalusers/dancecoihash" -u admin:$PASSWORD -d $'{"hash": "$2y$12$gdh2ecVBQmwpmcAeyReicuNtXyR6GMWSfXHxtcBBqFeFz2VQ8kDZe","opendistro_security_roles": ["all_access"],"backend_roles": ["all_access"],"attributes": {"attribute1": "value1","attribute2": "value2"}}'
{"status":"CREATED","message":"'dancecoihash' created."}


(base) mtldce@SAS-1B16WV3:~$ curl -k -s "https://localhost:9200/_cat/indices?v" -u dancecoihash:testPassword
health status index                        uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   security-auditlog-2024.05.30 ihLqFsJQToS1Jih90b_2Dg   1   1          6            0     87.5kb         87.5kb
green  open   .opendistro_security         w9zyt-LAT3CcwEqsL1p2TQ   1   0         10            1    114.6kb        114.6kb

(base) mtldce@SAS-1B16WV3:~$ curl -k -s "https://localhost:9200/_cat/indices?v" -u dancecoihash:wrongTestPassword
Unauthorized(base)

(base) mtldce@SAS-1B16WV3:~/Projects/forks/OpenSearch/build/distribution/local/opensearch-3.0.0-SNAPSHOT/plugins/opensearch-security/tools$ ./hash.sh 
**************************************************************************
** This tool will be deprecated in the next major release of OpenSearch **
** https://github.com/opensearch-project/security/issues/1755           **
**************************************************************************
WARNING: nor OPENSEARCH_JAVA_HOME nor JAVA_HOME is set, will use /usr/bin/java
[Password:] 
$2y$12$2/lx4CpmaotLUtfa.vyiCOw3.1T6pgiPaql6l49pFF8m43ZAlJcRO


curl -X PUT -k -s -H "Content-Type: application/json" "https://localhost:9200/_opendistro/_security/api/internalusers/dancecoihash2" -u admin:$PASSWORD -d $'{"hash": "$2y$12$2/lx4CpmaotLUtfa.vyiCOw3.1T6pgiPaql6l49pFF8m43ZAlJcRO","opendistro_security_roles": ["all_access"],"backend_roles": ["all_access"],"attributes": {"attribute1": "value1","attribute2": "value2"}}' "value2"}}'
{"status":"CREATED","message":"'dancecoihash2' created."}

mtldce@SAS-1B16WV3:~$ curl -k -s "https://localhost:9200/_cat/indices?v" -u dancecoihash2:geforce_rtx_3070!
health status index                        uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   security-auditlog-2024.05.30 ihLqFsJQToS1Jih90b_2Dg   1   1          8            0    117.6kb        117.6kb
green  open   .opendistro_security         w9zyt-LAT3CcwEqsL1p2TQ   1   0         10            2    127.8kb        127.8kb

(base) mtldce@SAS-1B16WV3:~$ curl -k -s "https://localhost:9200/_cat/indices?v" -u dancecoihash2:wrong_password
Unauthorized

Upgrade 2.11 -> 2.13 (with these changes back-ported)

Pre-upgrade:
  • Created an user
curl -X PUT -k -s -H "Content-Type: application/json" "https://localhost:9200/_opendistro/_security/api/internalusers/dancecoisas" -u $ADMINUSER:$PASSWORD -d $'
{"password": "strongpassword1996!","opendistro_security_roles": ["all_access"],"backend_roles": ["all_access"],"attributes": {"attribute1": "value1","attribute2": "value2"}}'
{"status":"CREATED","message":"'dancecoisas' created."}

curl -k -s "https://localhost:9200/" -u $ADMINUSER:$PASSWORD
{
  "name" : "sas-opendistro-default-0",
  "cluster_name" : "sas-opendistro",
  "cluster_uuid" : "arjz9067TKSANhk48Xg6JQ",
  "version" : {
    "distribution" : "opensearch",
    "number" : "2.11.0",
    "build_type" : "tar",
    "build_hash" : "4dcad6dd1fd45b6bd91f041a041829c8687278fa",
    "build_date" : "2023-10-13T02:55:55.511945994Z",
    "build_snapshot" : false,
    "lucene_version" : "9.7.0",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

Post-upgrade:
  • Passwords previously hashed with OpenBSDBCrypt still work as expected
  • Persisted users can still authenticate as expected
curl -k -s "https://localhost:9200/" -u $ADMINUSER:$PASSWORD
{
  "name" : "sas-opendistro-default-0",
  "cluster_name" : "sas-opendistro",
  "cluster_uuid" : "arjz9067TKSANhk48Xg6JQ",
  "version" : {
    "distribution" : "opensearch",
    "number" : "2.13.0",
    "build_type" : "tar",
    "build_hash" : "7ec678d1b7c87d6e779fdef94e33623e1f1e2647",
    "build_date" : "2024-03-26T00:02:39.659767978Z",
    "build_snapshot" : false,
    "lucene_version" : "9.10.0",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

curl -k -s "https://localhost:9200/_cat/indices" -u dancecoisas:strongpassword1996!
Defaulted container "sas-opendistro" out of: sas-opendistro, sysctl (init), sas-certframe (init)
green  open .plugins-ml-config               A3U0wNFnS5WOrC2yUrGtKg 1 0   1 0   3.9kb   3.9kb
green  open .opensearch-observability        SPlW4oIsQ5yDO7qT5xuFrA 1 0   0 0    208b    208b
yellow open security-auditlog-2024.05.30     VLtGQpMrS1en6CyEMj-VMA 1 1   3 0    47kb    47kb
green  open .opensearch-sap-log-types-config 7rNwKfsWRImllcPVTkbgbA 1 0 452 0 101.6kb 101.6kb
green  open .opendistro_security             3q1xOXU_SZGg1ud34p_K_Q 1 0   9 0  24.4kb  24.4kb

curl -k -s "https://localhost:9200/_cat/indices" -u dancecoisas:wrongpassword

(base) curl -k -s "https://localhost:9200/_cat/indices" -u dancecoisas:wrongpassword

Extra:
  • Added unit tests
  • The initial GitHub Checks are passing

Check List

  • New functionality includes testing
  • New functionality has been documented
  • Commits are signed per the DCO using --signoff

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.

@dancristiancecoi
Copy link
Contributor Author

There are some failing integration tests that I will look into ASAP.

@dancristiancecoi dancristiancecoi marked this pull request as draft May 30, 2024 16:48
Copy link

codecov bot commented May 31, 2024

Codecov Report

Attention: Patch coverage is 88.67925% with 6 lines in your changes missing coverage. Please review.

Project coverage is 65.40%. Comparing base (a1e5db3) to head (8e3b9cc).
Report is 11 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4381      +/-   ##
==========================================
- Coverage   65.44%   65.40%   -0.04%     
==========================================
  Files         310      311       +1     
  Lines       21992    22020      +28     
  Branches     3554     3557       +3     
==========================================
+ Hits        14392    14402      +10     
- Misses       5830     5843      +13     
- Partials     1770     1775       +5     
Files Coverage Δ
.../opensearch/security/OpenSearchSecurityPlugin.java 84.35% <100.00%> (+0.04%) ⬆️
...y/auth/internal/InternalAuthenticationBackend.java 72.30% <100.00%> (+0.87%) ⬆️
...security/dlic/rest/api/InternalUsersApiAction.java 81.57% <100.00%> (+0.16%) ⬆️
...security/dlic/rest/api/SecurityRestApiActions.java 80.00% <ø> (ø)
...g/opensearch/security/dlic/rest/support/Utils.java 60.52% <ø> (-2.89%) ⬇️
...ch/security/securityconf/DynamicConfigFactory.java 55.62% <100.00%> (ø)
...ain/java/org/opensearch/security/tools/Hasher.java 6.06% <100.00%> (-10.16%) ⬇️
...y/tools/democonfig/SecuritySettingsConfigurer.java 76.71% <100.00%> (+0.32%) ⬆️
...earch/security/dlic/rest/api/AccountApiAction.java 60.56% <66.66%> (+0.56%) ⬆️
...java/org/opensearch/security/user/UserService.java 57.03% <80.00%> (+0.32%) ⬆️
... and 1 more

... and 2 files with indirect coverage changes

@dancristiancecoi
Copy link
Contributor Author

dancristiancecoi commented May 31, 2024

Integration tests were failing due to pre-set hashes in various internal_users.yml files used for integration tests having a different configuration to the default one (BCrypt.A with 4 log rounds vs BCrypt.Y with 12 log rounds). I've adapted the code so it verifies the passwords with a BCrypt configuration based on the hash.

@dancristiancecoi dancristiancecoi marked this pull request as ready for review May 31, 2024 10:22
Dan Cecoi added 3 commits June 3, 2024 15:21
* PasswordHasher now gets instantiated only once in createComponents() and then gets passed down to classes that rely on it
* JUnit assertions changed to Hamcrest assertions
* Small changes based on review comments

Signed-off-by: Dan Cecoi <[email protected]>
Signed-off-by: Dan Cecoi <[email protected]>
@DarshitChanpura DarshitChanpura changed the title Replace BouncyCastle's OpenBSDBCrypt use in password hashing and verification Replace BouncyCastle's OpenBSDBCrypt use with password4j for password hashing and verification Jun 4, 2024
Copy link
Contributor

@shikharj05 shikharj05 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. Quick questions -

  1. How does this look like for end-users? Doesn't it break existing logins?
  2. Any performance numbers we can get to understand the difference?

build.gradle Show resolved Hide resolved
@dancristiancecoi
Copy link
Contributor Author

dancristiancecoi commented Jun 5, 2024

@shikharj05

Thanks for the PR. Quick questions -

  1. How does this look like for end-users? Doesn't it break existing logins?
  1. No difference for end-users as the existing hashes still get verified!
  1. Any performance numbers we can get to understand the difference?

I do not have any performance numbers but I can run some if required. What will be the best way to do it? Should I use the opensearch-benchmark tool locally or is there a github action we can run?

@dancristiancecoi
Copy link
Contributor Author

dancristiancecoi commented Jun 6, 2024

@shikharj05 I've ran the benchmarking tool with the percolator workload. It was against a one node local cluster running on my laptop so I am not sure how representative the results are :D.

For comparison I also attached a benchmark run in similar conditions but against the latest code in main.

testRunAgainstChanges.txt
testRunAgainstMain.txt

Run 2:

testRunAgainstMain-run2.txt
testRunAgainstMyChanges-run2.txt

To me it doesn't look like there is a significant variation either way in these bench-marking results.

@cwperks
Copy link
Member

cwperks commented Jun 6, 2024

This change LGTM! I am also looking at the benchmarking tool to look at ways to measure the impact on performance for the change in library.

There is a performance test suite that's in Draft and not unavailable yet unfortunately.

@dancristiancecoi
Copy link
Contributor Author

This change LGTM! I am also looking at the benchmarking tool to look at ways to measure the impact on performance for the change in library.

There is a performance test suite that's in Draft and not unavailable yet unfortunately.

Awesome, thanks @cwperks!

Copy link
Member

@DarshitChanpura DarshitChanpura left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dancristiancecoi Changes look good to me! I don't have any reservations against merging this as long as there is some sort of benchmarking done once the #4282 is merged. Once everything is implemented we can ship it in 2.x line.

Copy link
Contributor

@derek-ho derek-ho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@shikharj05
Copy link
Contributor

@shikharj05 I've ran the benchmarking tool with the percolator workload. It was against a one node local cluster running on my laptop so I am not sure how representative the results are :D.

For comparison I also attached a benchmark run in similar conditions but against the latest code in main.

testRunAgainstChanges.txt testRunAgainstMain.txt

Run 2:

testRunAgainstMain-run2.txt testRunAgainstMyChanges-run2.txt

To me it doesn't look like there is a significant variation either way in these bench-marking results.

Thanks, looked at the results, Run-1 shows some drop in throughput for index, however in run-2 it matches up. LGTM for now!

@cwperks cwperks added the backport 2.x backport to 2.x branch label Jun 10, 2024
@willyborankin willyborankin merged commit 20c524a into opensearch-project:main Jun 10, 2024
82 of 83 checks passed
@opensearch-trigger-bot
Copy link
Contributor

The backport to 2.x failed:

The process '/usr/bin/git' failed with exit code 128

To backport manually, run these commands in your terminal:

# Navigate to the root of your repository
cd $(git rev-parse --show-toplevel)
# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add ../.worktrees/security/backport-2.x 2.x
# Navigate to the new working tree
pushd ../.worktrees/security/backport-2.x
# Create a new branch
git switch --create backport/backport-4381-to-2.x
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 20c524ad994a9cc7d8757999f92f6d2fec6cb8ca
# Push it to GitHub
git push --set-upstream origin backport/backport-4381-to-2.x
# Go back to the original working tree
popd
# Delete the working tree
git worktree remove ../.worktrees/security/backport-2.x

Then, create a pull request where the base branch is 2.x and the compare/head branch is backport/backport-4381-to-2.x.

@willyborankin
Copy link
Collaborator

willyborankin commented Jun 10, 2024

@dancristiancecoi could you please prepare a manual backport?

dancristiancecoi added a commit to dancristiancecoi/security that referenced this pull request Jun 10, 2024
…d hashing and verification (opensearch-project#4381)

Signed-off-by: Dan Cecoi <[email protected]>
Co-authored-by: Dan Cecoi <[email protected]>
(cherry picked from commit 20c524a)
@dancristiancecoi
Copy link
Contributor Author

@dancristiancecoi could you please prepare a manual backport?

Sure! #4428

@dancristiancecoi
Copy link
Contributor Author

Thanks everyone for the reviews!

@dancristiancecoi dancristiancecoi deleted the passwordHashingStage1-v2 branch June 12, 2024 09:34
@ruanyl
Copy link
Member

ruanyl commented Jun 19, 2024

Integration tests were failing due to pre-set hashes in various internal_users.yml files used for integration tests having a different configuration to the default one (BCrypt.A with 4 log rounds vs BCrypt.Y with 12 log rounds). I've adapted the code so it verifies the passwords with a BCrypt configuration based on the hash.

Hi @dancristiancecoi I'm not able to start a new cluster with old internal_users.yml, is there documentations that I can take a look on fix this?

I got error:

Not yet initialized (you may need to run securityadmin)

@dancristiancecoi
Copy link
Contributor Author

Hi @ruanyl.

Are there any other errors in the logs to pinpoint to why the Security plugin was not initialised?

When you mention "old internal-users.yml file" is it similar to the ones used in the integration tests / demo config ? Anything that's unique about them?

There isn't documentation for this change as it SHOULD have worked seamlessly but it's very possible we've missed something

@ruanyl
Copy link
Member

ruanyl commented Jun 20, 2024

Hi @dancristiancecoi Thanks, I think I figured it out, just need to regenerate the password hash, then it works :)
I was using a internal_users.yml that's generated before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 2.x backport to 2.x branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants