読者です 読者をやめる 読者になる 読者になる

production.log

ピクスタ株式会社でエンジニアのマネージャーをやっている星直史のブログです。

Railsアプリケーションにおいて、ファイルアップロードする際のウイルスチェックをClamAVで実装する

背景

手軽にファイルアップロード機能を実装するときにCarrierwaveをよく使うんですが、
ひょんなことから、アップロードされたファイルのウイルスチェックをする必要がでてきたので、
チェックするまでの手順を、ClamAVのインストールとチェック方法を主にまとめます。

ClamAVインストール手順

  1. ClamAVインストール
  2. ウイルス更新の設定ファイル修正
  3. ウイルスのパターンファイル更新
  4. ウイルスチェックの設定ファイル修正
  5. デーモン起動

上記手順のコマンド

$ sudo su -
$ yum remove -y clam*
$ yum install -y clamav clamav-scanner-sysvinit clamav-update

# 2. ウイルス更新の設定ファイル修正
$ sed -i -e "s/Example/#Example/" /etc/freshclam.conf
$ sed -i -e "s:#DatabaseDirectory /var/lib/clamav:DatabaseDirectory /var/lib/clamav:" /etc/freshclam.conf
$ sed -i -e "s:#UpdateLogFile /var/log/freshclam.log:UpdateLogFile /var/log/freshclam.log:" /etc/freshclam.conf
$ sed -i -e "s/#DatabaseOwner clamupdate/DatabaseOwner clamupdate/" /etc/freshclam.conf

# 3. ウイルスのパターンファイル更新
$ freshclam

# 4. ウイルスチェックの設定ファイル修正
$ sed -i -e "s/Example/#Example/" /etc/clamd.d/scan.conf
$ sed -i -e "s:#LocalSocket /var/run/clamd.scan/clamd.sock:LocalSocket /var/run/clamd.scan/clamd.sock:" /etc/clamd.d/scan.conf
$ sed -i -e "s/#FixStaleSocket yes/FixStaleSocket yes/" /etc/clamd.d/scan.conf
$ sed -i -e "s/#TCPSocket 3310/TCPSocket 3310/" /etc/clamd.d/scan.conf
$ sed -i -e "s/#TCPAddr 127.0.0.1/TCPAddr 127.0.0.1/" /etc/clamd.d/scan.conf
$ chown clamscan:clamscan /var/run/clamd.scan
$ ln -s /etc/clamd.d/scan.conf /etc/clamd.conf 

# 5. デーモン起動ついでにchkconfig設定
$ /etc/rc.d/init.d/clamd.scan start
$ /sbin/chkconfig clamd.scan on

# デーモンのソケットはclamscanユーザにしかrwがないので、その他ユーザも実行できるように権限設定
$ chmod 775 /var/run/clamd.scan/
  1. ウイルスのパターンファイル更新については、定期的に実行したいので、cronで仕込む必要があると思います。

Rails プロダクションコードからの呼び出し(アップロードファイルのウイルスチェック)

  1. gem インストール
  2. 環境変数設定
  3. コード組み込み

1. gem インストール(Rails.rootにて) && 2. 環境変数設定

$ echo "gem 'clamav-client', require: 'clamav/client'" >> Gemfile
$ bundle install
$ echo 'export CLAMD_UNIX_SOCKET="/var/run/clamd.scan/clamd.sock"' >> ~/.bash_profile
$ source ~/.bash_profile
  1. 環境変数設定 についてですが、clamav-clientはCLAMD_UNIX_SOCKETが設定されていると、そこをソケットパスとして参照しにいきます。
    clamav-clientがデフォルトで参照するソケットパスが/var/run/clamav/clamd.ctlであるため、yum でinstallしたClamAVの設定ファイルのソケットパスに合わせます。

3. コード組み込み

def upload_image
  client = ClamAV::Client.new
  io = StringIO.new(params["image"].tempfile.read)
  raise "detected virus" if client.execute(ClamAV::Commands::InstreamCommand.new(io)).virus_name.present?
end

プロダクションコードでやるというよりは、ウイルスチェック専用のサーバにpostして結果だけ受け取るような仕組みにしたいですね。

参考サイト

github.com www.agilegroup.co.jp ClamAV on Amazon Linux - ぬぞうWiki