saitoxu.io

AboutTwitterGitHub

CarrierWave+FogでリソースをS3に保存・CloudFrontで配信するときの小ネタ

September 13, 2017

Rails で CarrierWave+Fog を使って画像などのリソースを S3 に保存し、 それを CloudFront 経由で配信するとき、ちょっと困ったことがあったのでメモしておきます。

環境は次のとおりです。

  • Rails 5.1.1
  • CarrierWave 1.1.0
  • Fog 1.40.0

問題

セキュリティのため S3 に保存したリソースへの直接アクセスは禁止し、 CloudFront からのみのアクセスを許可したいという要求があります。

これを実現するには、CarrierWave の設定を次のようにすればできそうな気がします。

# config/initializers/carrierwave.rb
CarrierWave.configure do |config|
  config.storage = :fog
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: 'ABCDEFGHIJKLMNOPQRST',
    aws_secret_access_key: 'abcdefghijklmnopqrstuvwxyz1234567890ABCD',
    region: 'ap-northeast-1',
    path_style: true
  }
  config.fog_public = false
  config.fog_attributes = { 'Cache-Control' => 'public, max-age=86400' }
  config.remove_previously_stored_files_after_update = false
  config.fog_directory = 'bucket-example'
  config.asset_host = 'https://abcdefghijklmn.cloudfront.net'
end

しかし、実際にmodel.resource.urlのようにアクセスすると、 以下のような S3 の Pre-Signed URL が返ってきます。

https://bucket-example.s3.amazonaws.com/uploads/user/image/1/image.jpeg?X-Amz-Expires=60&X-Amz-Date=20160914T044238Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJTIEVPQZEXU26EJA/20160914/us-east-1/s3/aws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=53daea895d9b40d5821011ee0e4c776c0ab96bdce5f14d078716f40a2e723244

古いですが以下の Issue などを見たところ、 AWS Specific な課題なので CarrierWave の方で対応はされていないようです。

Using CloudFront CDN for private files · Issue #1158 · carrierwaveuploader/carrierwave

解決方法

安直ですが各 Uploader のurlメソッドをオーバーライドして CloudFront の URL を返すようにします。

CloudFront のドメインはrailsconfig/configを使って定義していて、 加えて以下の工夫を加えています。

  • default_urlが定義されているときはそれを返す
  • 開発・テスト環境と本番環境で場合分け
  • リソースを更新したあとはキャッシュクリアしたいので更新日時をクエリパラメータに追加
class ResourceUploader < CarrierWave::Uploader::Base

  # rest of uploader

  def url
    if path.present?
      # 保存先がローカルの場合
      return "#{super}?updatedAt=#{model.updated_at.to_i}" if Rails.env.development? || Rails.env.test?
      # 保存先がS3の場合
      return "#{Settings.asset_host}/#{path}?updatedAt=#{model.updated_at.to_i}"
    end
    super
  end
end

あと次のような解決方法もあるみたいです。

amazon s3 - Use CDN with carrierwave + fog in s3 + cloudfront with rails 3.1 - Stack Overflow

以上、CarrierWave+Fog でリソースを S3 に保存・CloudFront で配信するときの小ネタでした。


© 2021, Yosuke Saito