【问题标题】:Ruby: S3 access with AWS instance profileRuby:使用 AWS 实例配置文件进行 S3 访问
【发布时间】:2021-02-23 13:23:14
【问题描述】:

我有一个附有配置文件的 ec2 实例。我可以使用 awscli,它可以很好地上传到存储桶。

root@ocr-sa-test:/# aws s3 ls s3://company-ocr-east/
                           PRE 7_day_expiry/

root@ocr-sa-test:/# touch foo
root@ocr-sa-test:/# aws s3 cp foo s3://company-ocr-east/foo
upload: ./foo to s3://company-ocr-east/foo
root@ocr-sa-test:/# aws s3 rm s3://company-ocr-east/foo
delete: s3://company-ocr-east/foo

我无法让它与 ruby​​ 中的 aws-sdk 一起使用。我被拒绝访问。

irb(main):001:0> require "aws-sdk"
=> true
irb(main):002:0>
irb(main):003:0> credentials = Aws::InstanceProfileCredentials.new
irb(main):004:1* client = Aws::S3::Client.new(
irb(main):005:1*   region: "us-east-1",
irb(main):006:1*   credentials: credentials,
irb(main):007:0> )
irb(main):008:0>
irb(main):009:0>
irb(main):010:0>
irb(main):011:1* begin
irb(main):012:2*   client.put_object(
irb(main):013:2*     key: 'hello.txt',
irb(main):014:2*     body: 'Hello World!',
irb(main):015:2*     bucket: 'company-ocr-east',
irb(main):016:2*     content_type: 'text/plain'
irb(main):017:1*   )
irb(main):018:1* rescue Exception => e
irb(main):019:1*   puts "S3 Upload Error: #{e.class} : Message: #{e.message}"
irb(main):020:0> end
S3 Upload Error: Aws::S3::Errors::AccessDenied : Message: Access Denied

【问题讨论】:

  • 它是通过 ec2 实例配置文件,并且由于尝试在 kubernetes 中运行它并且不希望容器中的 api 密钥,因此不能使用本地 .aws creds 文件
  • 如果我运行 credentials = Aws::InstanceProfileCredentials.new 并打印出凭证对象,我会看到带有过期时间的临时密钥/秘密
  • 嗨,迈克,您是否尝试在创建客户端之前配置凭据? Aws.config.update( credentials: credentials ) 在做之前:client = Aws::S3::Client.new( region: "us-east-1" ) 似乎他们建议在 AWS 文档中这样做:docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/…docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/…
  • @Mike,您的代码适用于我配置类似的 EC2 实例。 AWS 库可以在运行时自动生成凭证,而无需显式传递它。您可以尝试在构建客户端后从第 11 行运行您的代码,例如:client = Aws::S3::Client.new(region: "us-east-1")
  • @Mike 你能解决这个问题吗?解决方案是什么?

标签: ruby aws-sdk-ruby


【解决方案1】:

这些命令并不完全等效,因此确定线路上的确切差异将是有益的。特别是,SDK 被指示使用特定区域并从 IMDS 获取 STS 令牌,而 CLI 则根据其自己的默认值或配置文件配置来解决问题。除此之外,它们的行为并不完全相同。

要找出实际发生的情况,意味着使用适用的调试标志重新运行两者,即:

aws --debug s3 cp hello.txt s3://bucketname/hello.txt

credentials = Aws::InstanceProfileCredentials.new(http_debug_output: $stdout)
client = Aws::S3::Client.new(region: 'us-east-1', credentials: credentials, http_wire_trace: true)
client.put_object(key: 'hello.txt', body: 'Hello World!', bucket: 'bucketname', content_type: 'text/plain')

这些会产生大量的输出,但它们都是相关的,而且至关重要的是,一旦你忽略了噪音,它们就具有可比性。首先要验证的是 CLI 确实在与 IMDS 对话(它会向http://169.254.169.254 发出请求,最终会出现“从 IAM 角色找到凭据”之类的内容。如果没有,则说明该实例未按照您的想法进行配置,并且日志中会提供线索来解释它如何获取凭据,例如意外的配置文件或环境变量。您还需要检查它们是否获得了相同的角色。 p>

要比较的第二件事是他们都尝试的后续 PUT 序列。在调试的这一点上,几乎所有其他内容都是相同的,因此您很可能可以调整 Ruby SDK 客户端的设置以匹配 CLI 成功的任何内容。

第三种可能性是系统防火墙,或某种进程级别的强制访问控制、用户权限、cgroups/容器等。然而,调试你的操作系统内核和配置将是一个很深的、黑暗的兔子洞,而且无论如何,您已经说过这是“一个 EC2 实例”,所以它大概是一个普通的旧 EC2 实例。如果实际上上面的 Ruby 命令在不同的用户 ID 下或在容器内运行,那么也许您已经有了答案,由于用户/容器/安全控制或类似的操作系统级配置需要,这很可能是网络问题修复。

强制性警告:如果您选择发布任何日志数据,请小心覆盖任何凭据!我不相信这些调试跟踪特别可重播,但如果我错了,你不想找出困难的方法。

【讨论】:

    【解决方案2】:

    访问被拒绝错误可能是由Aws::InstanceProfileCredentials 中“非常激进”的默认超时引起的。

    尝试使用更长的超时时间或额外的重试来初始化它:

    credentials = Aws::InstanceProfileCredentials.new({
    retries: 2,                 # Integer, default: 1
    http_open_timeout: 2.5,     # Float, default: 1
    http_read_timeout: 2.5      # Float, default: 1
    }) 
    

    文档没有说明超时选项是以秒还是其他持续时间给出的。在默认情况下,2.5 似乎很保守。可能需要进一步调整。


    v3 Ruby API 的 AWS 文档在 Aws::S3::Client docs 中讨论了主动超时,您可以查看配置 Aws::InstanceProfileCredentials 的选项。

    【讨论】:

      猜你喜欢
      • 2016-09-25
      • 2017-04-13
      • 2018-11-01
      • 2015-08-19
      • 2016-10-01
      • 2016-01-16
      • 2017-10-03
      • 1970-01-01
      • 2019-07-10
      相关资源
      最近更新 更多