AWS RDS - 使用Rails进行IAM数据库身份验证

时间:2017-11-26 23:48:28

标签: ruby-on-rails amazon-web-services amazon-rds amazon-iam

我希望将AWS RDS IAM database authentication与Ruby on Rails一起使用,因为它为AWS用户提供了一种方便的方式来管理数据库权限并避免在其代码库中存储数据库密码。

在较高的层次上,它的工作原理是根据您的AWS凭据生成密码,以连接到仅有效15分钟的数据库。如果您想在15分钟后再次连接,则需要生成新密码。

此密码可以轻松地使用AWS Ruby SDK生成,因此理论上可以像config/database.yml一样嵌入到production: adapter: mysql2 host: db_host database: db_name username: db_user password: <%= Aws::RDS::AuthTokenGenerator .new(credentials: Aws::InstanceProfileCredentials.new) .auth_token( region: 'us-east-1', endpoint: 'db_host:3306', user_name: 'db_user' ) %> 中;

config/database.yml

但是,据我所知,--------- |letter| |a | |b | |c | -------- 在启动时只评估一次,并且在Rails的生命周期内仍然在该状态下缓存。

因此,通过使用这种方法,Rails服务器最初会成功连接到数据库,但如果在前15分钟窗口之后的任何时候Rails尝试打开新的数据库连接或重新连接断开的连接,则现在已过期凭证将被拒绝。

使用Rails进行IAM数据库身份验证的最佳方法是什么?我是否需要以某种方式使用在每个连接建立时重新评估的密码进行数据库配置?

1 个答案:

答案 0 :(得分:2)

我曾考虑过解决此问题的方法,而我想到的最佳方法是猴子修补Mysql2::Client#initialize,以便您可以启用IAM数据库身份验证,并将密码属性透明地更改为RDS密码。在mysql 0.5.2。的Rails 5.2.2.1中似乎可以使用。

一个重要的警告是您无法启用客户端的重新连接功能,因为我们需要确保无论何时发生连接错误(在上面的Rails版本中默认都会发生这种情况),Rails都会回收客户端。

# config/database.rb
require 'aws-sdk-rds'
require 'mysql2'

Aws.config.update(
  region: 'your_region',
)

class RdsIamPasswordGenerator
  def self.generate(region, host, user, port)
    Aws::RDS::AuthTokenGenerator
      .new(credentials: Aws::InstanceProfileCredentials.new)
      .auth_token(
        region: region,
        endpoint: host.to_s + ':' + port.to_s,
        user_name: user
      )
  end
end

module MysqlClientIamMonkeyPatch
  def initialize(opts = {})
    opts         = opts.dup
    aws_iam_auth = opts.delete(:aws_iam_auth)

    if aws_iam_auth
      raise ArgumentError, 'reconnect must be false if aws_iam_auth is true' if opts[:reconnect]

      user = opts[:username] || opts[:user]
      host = opts[:host] || opts[:hostname]
      port = opts[:port] || 3306

      raise ArgumentError, 'username/user and host/hostname must be present' if user.nil? || host.nil?

      opts.delete(:pass)
      opts.delete(:password)

      opts[:password] = RdsIamPasswordGenerator.generate(Aws.config[:region], host, user, port)
      opts[:enable_cleartext_plugin] = true # Necessary for IAM auth
    end

    super(opts)
  end
end

Mysql2::Client.prepend(MysqlClientIamMonkeyPatch)
# config/boot.rb
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)

require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.

require_relative './database' # Handles patching in IAM auth
# config/database.yml
production:
  adapter: mysql2
  database: production
  ssl_mode: verify_identity
  sslverify: true
  sslca: /opt/aws/rds-combined-ca-bundle.pem
  aws_iam_auth: true
  host: db_host
  username: db_user
  password: null