-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Allow registering custom CredentialProviders for per-conn passwords #1042
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
Allow registering custom CredentialProviders for per-conn passwords #1042
Conversation
README.md
Outdated
Default: "" | ||
``` | ||
|
||
If set, this must refer to a credential provider name registerd with `RegisterCredentialProvider`. When this is set, the username and password in the DSN will be ignored; instead, each time a conneciton is to be opened, the named credential provider function will be called to obtain a username/password to connect with. This is useful when using, for example, IAM database auth in Amazon AWS, where "passwords" are actually temporary tokens that expire. |
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.
small typo: "registerd" -> "registered"
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.
oops. Pushed a fix now.
auth.go
Outdated
// CredentialProviderFunc is a function which can be used to fetch a username/password | ||
// pair for use when opening a new MySQL connection. The first return value is the username | ||
// and the second the password. | ||
type CredentialProviderFunc func() (string, string, error) |
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.
For the purposes of documentation could it be useful to name the return values?
type CredentialProviderFunc func() (string, string, error) | |
type CredentialProviderFunc func() (user string, password string, err error) |
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.
Agreed, this makes the purpose of the return values clearer, I have updated it.
When using a temporary credential system for MySQL, for example IAM database authenticaiton on AWS or the Database secret backend for Hashicorp Vault, it may not be the case that the same username and password be used for opening every connection in a *sql.DB. This PR adds funcionality whereby the caller can, instead of specifying cfg.User and cfg.Passwd (in the DSN as user:pass@...), specify a CredentialProvider= arguemnt which refers to a callback registered with RegisterCredentialProvider. When a new connection is to be opened, if the CredentialProvider callback is specified, that is called to obtain a username/password pair rather than using the values from the DSN.
33d6e68
to
5042da9
Compare
I don't like adding DSN option + registry. |
Oh I totally didn't realise that the Connector interface existed! That's definitely way cleaner. Is the best approach to just put the Second question - If I added a commit to this PR (or open a new PR?) to do add a |
I don't consider about the best API design carefully yet.
It should be separated PR. And it might be |
@methane I pushed a commit to get rid of the registry and put the I had a look at doing what you suggested with a setter instead. However, since all of the other config options are on the config struct as plain fields, and since the config struct is just passed into Thinking about this a bit more, a good API for making a
What do you think? I'm happy to put together a builder like this for all the fields if that's something you're interested in. |
8cfceb7
to
e00b62e
Compare
|
e00b62e
to
8dad233
Compare
Instead, it can be used by passing the config object directly to a Connector.
8dad233
to
fc21a98
Compare
@KJTsanaktsidis I still feel this proposal is ugly. If we accept this, why not dynamic Hostname and Port? or SSL Cert? Please look #1045 for my counter proposal. You can modify any config value dynamically. |
+1 to #1045 If anything, I want to overwrite TLS configure of existing |
Oh, that makes... a ton of sense. I didn't realise it would be OK to use I'm on holiday at the moment, but when I get back I'll swap our app over to use an approach like #1045 (unless @catkins beats me to the punch :) ). Thanks a lot for your help! I'll go ahead and close this PR. |
@KJTsanaktsidis You also have the option of writing an alternate See mine as a reference: it wraps the MySQL driver to load creadentials from the |
Sorry - this is the second copy of this PR, because go modules is apparently very unhappy about the use of branches with slashes in them...
Description
When using a temporary credential system for MySQL, for example IAM database authenticaiton on AWS or the Database secret backend for Hashicorp Vault, it may not be the case that the same username and password be used for opening every connection in a
sql.DB
.This PR adds funcionality whereby the caller can, instead of specifying
cfg.User
andcfg.Passwd
(or in the DSN as user:pass@...), specify acredentialProvider=
argument which refers to a callback registered withRegisterCredentialProvider
.When a new connection is to be opened, if the
CredentialProvider
callback is specified, that is called to obtain a username/password pair rather than using the values from the DSN.We need this in order to use it with IAM database authentication for Aurora. With IAM database authentication, the "password" you use to connect is in fact a signed token generated with https://docs.aws.amazon.com/sdk-for-go/api/service/rds/rdsutils/#BuildAuthToken. However, the token is signed with an expiry of 15 minutes; so, if a
sql.DB
is constructed with such a token as the password, the database driver will fail to open any new connections after 15 minutes. By allowing a callback to be used to generate the password, we can re-sign it each time, so that it stays valid.Although the AWS Aurora usecase only requires changing the password, I suspect some users of the database secrets engine in Hashicorp vault (https://www.vaultproject.io/docs/secrets/databases/mysql-maria.html) will find it useful to be able to change the username as well; Vault works by creating temporary users in the database, so when they expire, vault deletes the actual users out of MySQL and replaces it with one named differently. So this usecase would require being able to change the username in the callback as well as the password.
Anyway, keen to get some 👀 on this and see if people think this would be a useful addition to the library!
Checklist