オレオレ証明書でsslをaction単位で、かつnginx + unicorn + rails
ステージング環境とかで需要があるかもしれない、ぶっちゃけ証明書がオレオレかどうかはあまり関係のない話。
アプリの対応
全体的にSSLにするならconfig/enviroments/あたりにconfig.force_ssl = trueしておけばいいのだけど、action単位でforce_sslしたい場合はこんな感じで対応する。
controllers/application_controller.rb
def force_ssl(options = {}) host = options.delete(:host) unless request.ssl? or Rails.env.development? redirect_options = {protocol: 'https://', status: :moved_permanently} redirect_options.merge!(host: host) if host flash.keep redirect_to redirect_options and return else true end end
設定例
class UsersController < ApplicationController before_filter :force_ssl, only: [:new, :create]
こんな感じ。
証明書準備
オレオレでやる場合は、適当なディレクトリで
openssl genrsa -des3 2048 > server.key openssl rsa -in server.key -out server.key openssl req -new -key server.key > server.csr openssl x509 -in server.csr -days 3650 -req -signkey server.key > server.crt
パスフレーズはテキトウで、質問コーナーも全部enterで。
nginxに設定
もともとhttpで動いていた設定にsslの設定を追加するという前提。
upstream your_app { server unix:/var/www/your-web/shared/unicorn.sock; } server { listen 80; listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; root /var/www/your-web/public; access_log /var/www/your-web/current/log/access.log; error_log /var/www/your-web/current/log/error.log; location ~ ^(/assets/|/audios/|/system/) { gzip_static on; root /var/www/your-web/current/public; expires 1y; add_header Cache-Control public; add_header ETag ""; break; } location / { proxy_set_header X-Forwarded-Proto $scheme; if (-f $request_filename) { break; } # ファイルが存在しなければunicornにproxyする proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass http://your_app; } }
足したのは
~snip~ listen 443 ssl; ~snip~ ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ~snip~ location / { proxy_set_header X-Forwarded-Proto $scheme; ~snip~
httpとsslの設定を混ぜて設定する場合は最後のproxy_set_headerが必要。これがないとrailsはリクエストがssl-terminatedなやつかどうか判断つかなくて無限リダイレクトに突入するから。
http://stackoverflow.com/questions/9448168/why-am-i-getting-infinite-redirect-loop-with-force-ssl-in-my-rails-app
追記)
これだと一回SSLのページに行ってあと、どこにいってもSSLの状態がキープされてしまい、だったら全部SSLでもいいんじゃね?という状態になる。なのでトップページなど、SSLでアクセスしてほしくないページを逆にHTTPに矯正するようにするforce_httpも作った。うーん。でもなんかアレな感じがするなぁ。
def force_http(options = {}) host = options.delete(:host) if request.ssl? redirect_options = {protocol: 'http://'} redirect_options.merge!(host: host) if host flash.keep redirect_to redirect_options and return else true end end