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 pass adding AWS IAM Authentication #1263 #1381

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

matt-domsch-sp
Copy link

@matt-domsch-sp matt-domsch-sp commented Nov 10, 2024

This adds AWS IAM authentication as a replacement for defining a password in the configuration.

When the configuration option :use_iam_authentication = true, an authentication token (password) will be fetched from IAM and cached for the next 14 minutes (tokens expire in 15 minutes). These can then be reused by all new connections until it expires, at which point a new token will be fetched when next needed.

To allow for multiple Mysql2::Client configurations to multiple servers, the cache is keyed by database username, host name, port, and region.

Two new configuration options are necessary:

  • :use_iam_authentication = true
  • :host_region is a string region name, e.g. 'us-east-1'. If not set, ENV['AWS_REGION'] will be used. If this is not present, authenticaiton will fail.

As prerequisites, you must enable IAM authentication on the RDS instance, create an IAM policy, attach the policy to the target IAM user or role, create the database user set to use the AWS Authentication Plugin, and then run your ruby code using that user or role. See
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.html for details on these steps.

mysql2.gemspec Outdated Show resolved Hide resolved
@matt-domsch-sp matt-domsch-sp force-pushed the aws-iam-authentication branch 4 times, most recently from 897d4f5 to e6534de Compare November 11, 2024 01:29
This adds AWS IAM authentication as a replacement for defining a
password in the configuration.

When the configuration option :use_iam_authentication = true, an
authentication token (password) will be fetched from IAM and cached
for the next 14 minutes (tokens expire in 15 minutes).  These can then
be reused by all new connections until it expires, at which point a
new token will be fetched when next needed.

To allow for multiple Mysql2::Client configurations to multiple
servers, the cache is keyed by database username, host name, port, and
region.

Two new configuration options are necessary:
- :use_iam_credentials = true
- :host_region is a string region name, e.g. 'us-east-1'.  If not set,
  ENV['AWS_REGION'] will be used.  If this is not present,
  authenticaiton will fail.

As prerequisites, you must enable IAM authentication on the RDS
instance, create an IAM policy, attach the policy to the target IAM
user or role, create the database user set to use the AWS
Authentication Plugin, and then run your ruby code using that user or
role.  See
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.html
for details on these steps.

You must include the aws-sdk-rds gem in your bundle to use this feature.
@matt-domsch-sp matt-domsch-sp marked this pull request as ready for review November 12, 2024 03:01
@matt-domsch-sp
Copy link
Author

This behaves as expected with a simple test program below, and lints without any additional messages. Failure to have the aws-sdk-rds gem already present results in a LoadError only when option :use_iam_authentication = true. No change in behavior when :use_iam_authentication is not provided or is false.

#!ruby

require 'mysql2'

opts={}
opts[:use_iam_authentication] = true
opts[:host] = 'dbserver'
opts[:port] = '3306'
opts[:host_region] = 'us-east-1'
opts[:username] = 'dbuser'
opts[:ssl_mode] = :verify_identity

aws = Mysql2::AwsIamAuth.instance
puts "passwords = #{aws.passwords.count}" # => 0 as expected

clients = []
(1..10).to_a.each do

 client = Mysql2::Client.new(opts)
 clients.append(client)
 puts "passwords = #{aws.passwords.count}" # => yields 1 as expected because it's reused for each connection
 results = client.query("SHOW DATABASES")
 results.each do |row|
   puts row # => yields the databases visible to the user as expected
 end
end

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.

2 participants