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

Initial Implementation #1

Merged
merged 22 commits into from Sep 6, 2022
Merged

Conversation

maxcoulombe
Copy link
Contributor

@maxcoulombe maxcoulombe commented Aug 16, 2022

Overview

  • added initial elasticache redis implementation

Contributor Checklist

  • Add relevant docs to upstream Vault repository, or sufficient reasoning why docs won’t be added yet
    • Doc will be added when the project is ready for release
  • Add output for any tests not ran in CI to the PR description (eg, acceptance tests)
/usr/local/go/bin/go tool test2json -t /tmp/GoLand/___redisElastiCacheClient_test_go.test -test.v -test.paniconexit0 -test.run ^\QTest_generateUserId\E$
=== RUN   Test_generateUserId
=== RUN   Test_generateUserId/compliant_username
=== RUN   Test_generateUserId/short_username
=== RUN   Test_generateUserId/username_too_long
=== RUN   Test_generateUserId/username_with_non-alphanumeric_characters
=== RUN   Test_generateUserId/username_starting_with_a_number
--- PASS: Test_generateUserId (0.00s)
    --- PASS: Test_generateUserId/compliant_username (0.00s)
    --- PASS: Test_generateUserId/short_username (0.00s)
    --- PASS: Test_generateUserId/username_too_long (0.00s)
    --- PASS: Test_generateUserId/username_with_non-alphanumeric_characters (0.00s)
    --- PASS: Test_generateUserId/username_starting_with_a_number (0.00s)
PASS
  • Backwards compatible

Copy link
Member

@austingebauer austingebauer left a comment

Choose a reason for hiding this comment

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

Looking good! Nice job on including the Terraform config for setting things up. I left a few suggestions/questions. It'd also be good to get the rest of the team added for review on this.

@@ -0,0 +1,78 @@
provider "aws" {
Copy link
Member

Choose a reason for hiding this comment

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

Nice! 👍

internal/plugin/redisElastiCacheClient.go Outdated Show resolved Hide resolved
internal/plugin/plugin.go Outdated Show resolved Hide resolved
internal/plugin/redisElastiCacheClient.go Outdated Show resolved Hide resolved
internal/plugin/redisElastiCacheClient.go Outdated Show resolved Hide resolved
internal/plugin/redisElastiCacheClient_test.go Outdated Show resolved Hide resolved
// The ID can have up to 40 characters, and must begin with a letter.
// It should not end with a hyphen or contain two consecutive hyphens.
// Valid characters: A-Z, a-z, 0-9, and -(hyphen).
func generateUserId(username string) string {
Copy link
Member

Choose a reason for hiding this comment

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

If we end up allowing our users to configure a username template, we should consider the potential for collision when using this function to derive the ID from the username. Perhaps there is a favorable alternative that we can come up with.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was not completely sure about this strategy of generating a userid from the username tbh, I'm happy to discuss the pros and cons.

We could:

  • Add constraints so userId == username at all times
  • Generate random userId and use Elasticache's API to fetch and filter by username to get the userId over the network
  • Do what we do here and have a deterministic way to generate a userId from the information given by Vault

Copy link
Member

Choose a reason for hiding this comment

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

I'm good with option 1 or 3. Option 1 seems workable, but we'd need to validate that constraints are met (esp. when username templates are introduced). For Option 3, it seems unlikely that a collision would occur. I suspect it could if the username template didn't provide sufficient entropy.

I'd say let's go with 3 for now. We can re-evaluate when username templates come in.

internal/plugin/redisElastiCacheClient.go Outdated Show resolved Hide resolved
internal/plugin/redisElastiCacheClient.go Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
return dbplugin.NewUserResponse{Username: *output.UserName}, nil
}

func (r *redisElastiCacheDB) UpdateUser(_ context.Context, req dbplugin.UpdateUserRequest) (dbplugin.UpdateUserResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is just the initial implementation but have you considered any locking strategies?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question, I'm looking at it right now as part of the user group support.

AWS usually handle locking on their side. Whenever you add, modify or delete a resource on most of their APIs the resource changes status and you can only execute a subsequent request once the status goes back to "Active".

So most likely the locking strategy will be to rely on the AWS status but I still have to test and read more about this.

* changed creation_statements format
accessString = strings.TrimSpace(accessString)

return accessString, nil
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For people who already reviewed, this is a behaviour change that was introduced in between the updates.

We talked and decided that the format for the creation statements should be a list of strings instead of a string.
We also decided to handle the "ON" keyword for the users so they can give it or omit it at their preference. With this decision we also forbid the creation of disabled or "OFF" users as using vault to manage disabled users seems pointless.
Finally we decided that if no creation commands is given we'd create a general purpose read-only user as the default behavior.

Copy link
Member

Choose a reason for hiding this comment

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

SGTM. The tests helped me understand the behavior here. Nice job on those!

@austingebauer austingebauer self-requested a review August 25, 2022 16:44
return dbplugin.DeleteUserResponse{}, nil
}

func parseCreationCommands(commands []string) (string, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: A small comment on what this function does would help future readers understand quickly. Details you shared in files#r954459565 would be perfect.

}
}

if strings.HasPrefix(accessString, "off ") || strings.Contains(accessString, " off ") || strings.HasSuffix(accessString, " off") {
Copy link
Contributor

Choose a reason for hiding this comment

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

would strings.Contains(accessString, "off") be sufficient? Or are the spaces required here because off could occur in another context?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could occur in another context. For example, if an eshop business caches special offers in Redis using the key pattern:
offer-summer-sales
And they try to create a user that has access to only the cache entries for these special offers they'd try to configure the role with:
on ~offer-* +@read
which would block them and return an erroneous error message even if the rules are legitimate.

It really needs to be the keyword "off" all by itself.

Would it be more readable with a word-boundary regex? \boff\b

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for verifying! That makes sense -- assuming key patterns cannot contain spaces.

Yeah my initial thought was to use a regex. But that is just a nitpick. I am good with either one.

maxcoulombe and others added 2 commits September 6, 2022 12:05
* supporting user groups
* switched to static-role support only
Copy link
Member

@austingebauer austingebauer left a comment

Choose a reason for hiding this comment

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

LGTM!

README.md Outdated Show resolved Hide resolved
redis_elasticache_client.go Outdated Show resolved Hide resolved
@maxcoulombe maxcoulombe merged commit 548b4d8 into main Sep 6, 2022
@maxcoulombe maxcoulombe deleted the vault-7720-ElastiCacheRedisBootstrap branch September 6, 2022 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants