HappyGoLucky

Web系サーバーサイド寄りの自動化大好きエンジニアの徒然なるブログ

ElasticBeanstalkのPlatform Versionを更新したらPaperclipでエラーが出た

TL;DR

  • Ruby on Rails 4.10, Ruby 2.4 な環境で
  • AWS ElasticBeanstalk Platform の version を Amazon Linux 2016.03 v2.1.0 から2017.09 v2.6.5 に更新した
  • PaperClip 4.7 でエラーが出るようになったので調べたら
  • ImageMagickpolicy.xmlHTTPS から直接画像を指定できないようになっていたためだった
  • なので、S3からダウンロードしてファイルパスを Paperclip に渡すようにした

経緯

もともと、Paperclip を使って S3 上の画像ファイルを使ってゴニョゴニョしていた。S3上のファイルは https の Endpoint を指定していた。
で、Ruby バージョンを更新することにしたので、それならついでに AWS ElasticBeanstalk Platform も更新するかー、としてみた。
すると、Paperclip を使った処理で Paperclip::Errors::NotIdentifiedByImageMagickError が発生するようになった。

原因特定と対策

Paperclip::Errors::NotIdentifiedByImageMagickError だけだと原因が分からないのだが、すでに以前、 ImageMagick の policy.xml が更新されたことで、rmagick が動かなくなる現象に出会ったことがあった。こちらも rmagickconvert に S3 の Endpoint を渡すようにしていたためで、この時は画像をダウンロードしてから使うことにした。
同じ原因だろう、とログを差し込んで、 Paperclip で https なEndpointを指定していることを確認して原因の特定は完了。

ImageMagick の policy.xml

2016年に公開された ImageMagick脆弱性対策のため policy.xml で制御する、という対策が取られた。
脆弱性の内容自体は次の記事が分かりやすい。
ImageMagickの脆弱性(CVE-2016-3714他)についてまとめてみた - piyolog ImageMagickの脆弱性(ImageTragick) - てきとうなメモ

Amazon Linux もこの対策を採用している。
https://alas.aws.amazon.com/ALAS-2016-699.html

Note: This update contains an updated /etc/ImageMagick/policy.xml file that disables the EPHEMERAL, HTTPS, HTTP, URL, FTP, MVG, MSL, TEXT, and LABEL coders. If you experience any problems after the update, it may be necessary to manually adjust the policy.xml file to match your requirements. Please take additional precautions to ensure that your applications using the ImageMagick library do not process malicious or untrusted files before doing so.

実際に EC2 上の policy.xml を見てみると HTTPS は制限されている。

<policymap>
  <policy domain="coder" rights="none" pattern="EPHEMERAL" />
  <policy domain="coder" rights="none" pattern="HTTPS" />
  <policy domain="coder" rights="none" pattern="HTTP" />
  <policy domain="coder" rights="none" pattern="URL" />
  <policy domain="coder" rights="none" pattern="FTP" />
  <policy domain="coder" rights="none" pattern="MVG" />
  <policy domain="coder" rights="none" pattern="MSL" />
  <policy domain="coder" rights="none" pattern="TEXT" />
  <policy domain="coder" rights="none" pattern="LABEL" />
  <policy domain="path" rights="none" pattern="@*" />
</policymap>

該当行をコメントアウトすると、今までどおりの処理がされた。

<policymap>
  <policy domain="coder" rights="none" pattern="EPHEMERAL" />
  <!--<policy domain="coder" rights="none" pattern="HTTPS" />-->
  <policy domain="coder" rights="none" pattern="HTTP" />
  <policy domain="coder" rights="none" pattern="URL" />
  <policy domain="coder" rights="none" pattern="FTP" />
  <policy domain="coder" rights="none" pattern="MVG" />
  <policy domain="coder" rights="none" pattern="MSL" />
  <policy domain="coder" rights="none" pattern="TEXT" />
  <policy domain="coder" rights="none" pattern="LABEL" />
  <policy domain="path" rights="none" pattern="@*" />
</policymap>

HTTPSの制御をコメントアウトして良いのか?

HTTPS で指定したファイルを使えるようにする、というのは果たして良いのだろうか?
せっかく塞いである穴を自ら開けることになるので、せめてホワイトリスト指定ができれば良い。

ImageMagick 6.9.7-7 では、そのような機能が入ったらしい。
Behavior of policy changed from 6.9.7-7 · Issue #377 · ImageMagick/ImageMagick · GitHub ImageMagick 6.9.7-7 から policy の挙動が変わりました - awm-Tech

ただし、URLをホワイトリスト指定する方法は調べた感じなさそう。ソースコードを追いかけたわけではないので、実装上できないのかは分からない。
また、私が使っている AmazonLinux 上の ImageMagick6.7.8-9 なので、そもそもホワイトリスト指定は使えない。yum update すれば良い話だけど。

やはりセキュリティの穴を自ら開けるのは嫌なので別の方法を検討する。

Paperclip コミュニティではどのように対策を?

Paperclipコミュニティでは、どのような解決方法を検討しているのか調べてみた。

結局ダウンロードしてからPaperclipに渡すことにした

他の手もあるとは思うが、仕方がないので PaperClip の Wrapper を作ることにした。 モンキーパッチは避ける主義なので、Wrapperクラスをべた書き。

Dir.mktmpdir do |tmpdir|
    image_file = File.basename(URI.parse(image_url).path)
    download_path = Pathname(dir).join(image_file)
    File.open(download_path, "wb") do |output|
      OpenURI.open_uri(image_url) do |data|
        output.write(data.read)
      end
    end
    Paperclip::Geometry.from_file(download_path)
end