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