active_adminのassets precompileが失敗する
active_adminのprecompileが失敗する。
プリコンパイルしてみると..
$ bundle exec rake assets:precompile /Users/hakutoitoi/.rbenv/versions/1.9.3-p194/bin/ruby /Users/hakutoitoi/.rbenv/versions/1.9.3-p194/bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets rake aborted! Undefined mixin 'global-reset'. (in /Users/hakutoitoi/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activeadmin-0.5.1/app/assets/stylesheets/active_admin/_base.css.scss) Tasks: TOP => assets:precompile:all(See full trace by running task with --trace) rake aborted! Command failed with status (1): [/Users/hakutoitoi/.rbenv/versions/1.9.3-p1...] Tasks: TOP => assets:precompile (See full trace by running task with --trace)
sassのmixinが未定義と言われて失敗する。
github issueでは色々議論されていて色んな解決法が書いてあるが、自分の環境ではconfig.assets.precompileの指定を変えたら成功するようになった。
https://github.com/gregbell/active_admin/issues/810
もともとはprecompileするファイルの指定はproduction.rbで下記のように指定していた。
config.assets.precompile += ['*.js', '*.css']
アスタリスクでの指定をやめて正規表現で指定したら成功した。
config.assets.precompile += [/^[a-z0-9]\w+.(css|js)$/]
protect_from_forgeryが何をやっているのか調べた
Rails3でCSRF対策としてApplicationControllerにデフォルト指定されるprotect_from_forgeryですが、実際のところ何をやっているのかわからなかったのでコードリーディングしてみたメモ。
処理の流れ
0. ApplicationControllerに下記の指定がされているところから始まる
class ApplicationController < ActionController::Base protect_from_forgery end
1. まずprepend_before_filterでverify_authenticity_tokenをbefore_filter郡に突っ込む
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 67 def protect_from_forgery(options = {}) self.request_forgery_protection_token ||= :authenticity_token prepend_before_filter :verify_authenticity_token, options end
2. verify_authenticity_tokenではverified_request?を呼ぶ
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 75 def verify_authenticity_token unless verified_request? logger.warn "WARNING: Can't verify CSRF token authenticity" if logger handle_unverified_request end end
3. verified_request?でリクエストデータを検証する
- protect_against_forgery?は設定ファイルでallow_forgery_protection = falseが指定されていないか検証
- GETリクエストか検証
- sessionに含まれるtokenとリクエストパラメータにrequest_forgery_protection_tokenが指定されていてマッチするか検証
- sessionに含まれるtokenとX-CSRF-Tokenヘッダのトークンが指定されていてマッチするか検証
の4項目を検証している。
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 93 def verified_request? !protect_against_forgery? || request.get? || form_authenticity_token == params[request_forgery_protection_token] || form_authenticity_token == request.headers['X-CSRF-Token'] end
4. 処理の分岐
verified_request?の検証で成功(どれか1つでもtrue)すれば何もしないでprotect_from_forgeryの処理は完了、
失敗(すべてfalse)だった場合はhandle_unverified_requestが呼ばれてさらにreset_sessionが呼ばれて
最終的にリクエストからsession情報がリセットされる。
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 84 def handle_unverified_request reset_session end
# File actionpack/lib/action_controller/metal/rack_delegation.rb, line 22 def reset_session @_request.reset_session end
# File actionpack/lib/action_dispatch/http/request.rb, line 209 def reset_session session.destroy if session && session.respond_to?(:destroy) self.session = {} @env['action_dispatch.request.flash_hash'] = nil end
まとめ
protect_from_forgeryはリクエストを検証して正しければ何もしない、正しくない場合はsessionをクリアする。
capistrano-unicornでハマった
capistrano-unicornはその名の通りcapistranoでunicornを扱う便利なgemです。
早速Railsのサービスに導入してみた。
試した環境は
- ruby-1.9.3p194
- rails-3.2.9
- capistrano-2.13.5
- capistrano-unicorn-0.1.6
githubの使い方を参考にGemfileに下記を追加してbundle叩く
group :development do gem 'capistrano-unicorn', :require => false end
config/deploy.rbでrequireするのとdeploy:restart時のcollbackを登録
# set :application宣言より下に書かないとエラーになる require 'capistrano-unicorn' ... # hot deployを利用するため unicorn:restartを指定 after 'deploy:restart', 'unicorn:restart'
unicorn設定ファイル(config/unicorn.rb)を作成
# Set your full path to application. app_path = "/var/www/vhosts/blog/current" # Set unicorn options worker_processes 3 preload_app true timeout 30 listen "/tmp/unicorn.blog.sock" # Spawn unicorn master worker for user apps (group: apps) user 'rubys', 'rubys' # Fill path to your app working_directory app_path # Should be 'production' by default, otherwise use other env rails_env = ENV['RAILS_ENV'] || 'production' # Log everything to one file stderr_path "log/unicorn.log" stdout_path "log/unicorn.log" # Set master PID location pid "#{app_path}/tmp/pids/unicorn.pid" before_fork do |server, worker| ActiveRecord::Base.connection.disconnect! old_pid = "#{server.config[:pid]}.oldbin" if File.exists?(old_pid) && server.pid != old_pid begin Process.kill("QUIT", File.read(old_pid).to_i) rescue Errno::ENOENT, Errno::ESRCH # someone else did our job for us end end end after_fork do |server, worker| ActiveRecord::Base.establish_connection end
ここまでで準備はOKのようです、追加、変更したファイルはこんな感じで配置されます。
Gemfile config |-- deploy.rb |-- unicorn.rb
準備も整ったのでcap deployしてみると..
* 2012-12-22 02:19:17 executing `deploy:restart' triggering after callbacks for `deploy:restart' * 2012-12-22 02:19:17 executing `unicorn:reload' * executing "if [ -e /var/www/vhosts/realplay/current/tmp/pids/unicorn.pid ]; then echo 'true'; fi" servers: ["192.168.6.29"] [192.168.6.29] executing command command finished in 3739ms *** [Unicorn] Stopping... * executing "kill -s USR2 `cat /var/www/vhosts/realplay/current/tmp/pids/unicorn.pid`" servers: ["192.168.6.29"] [192.168.6.29] executing command command finished in 3646ms the task `unicorn:restart' does not exist
あれ、after 'deploy:restart', 'unicorn:restart'を呼んでるのにunicorn:reloadが呼ばれてる。
しかもunicorn:restartがないとも言ってるし。
github上のソースコード見る限りちゃんとrestart定義されてるし..
task :restart, :roles => :app, :except => {:no_release => true} do run <<-END if #{unicorn_is_running?}; then echo "Restarting Unicorn..."; #{unicorn_send_signal('USR2')}; else #{start_unicorn} fi; sleep 2; # in order to wait for the (old) pidfile to show up if #{old_unicorn_is_running?}; then #{unicorn_send_signal('QUIT', get_old_unicorn_pid)}; fi; END end
githubのmasterのversion.rbも0.1.6になってるし古いバージョンを入れているわけではなさそう。
しかたなくインストールされているソースを見るとなんとunicorn:restartが定義されていない。
どうもrubygems.orgに上がっているソースは最新ではないようなのでgithubからインストールするように変更する。
group :development do gem 'capistrano-unicorn', :git => 'git@github.com:sosedoff/capistrano-unicorn.git' end
再度deployしてみる。
* 2012-12-22 04:25:11 executing `deploy:restart' triggering after callbacks for `deploy:restart' * 2012-12-22 04:25:11 executing `unicorn:restart' triggering before callbacks for `unicorn:restart' ... [192.168.6.29] executing command ** [out :: 192.168.6.29] Restarting Unicorn...
想定どおり、unicorn:restartが動いてくれた。