YORKXIN×YORKXIN

本列車即將抵達終點站

[攻略] Ruby on Rails 與 SSL (https)

with 3 comments

問題:該如何在 Ruby on Rails 導入 SSL (https) 的支援呢

以下分成 Mac OS X 與 Ubuntu Linux 的說明。

另外因為 mod_rails (Passenger) 在設定 SSL 方面格外簡單,因此假設你已經安裝了 mod_rails。安裝方式請參考官方網站。

Mac OS X 請額外安裝這支小程式,讓 mod_rails 的設定更為方便。

注意事項:全世界都可以連到你的 Rails Application即使它是 development !! 建議在程式裡使用 IP 過濾

1: 製作 SSL 憑證

在這裡只考慮「用 Apache 的 OpenSSL 自己簽發 SSL 憑證」的方式,如果你透過管道 ($) 取得 SSL Key 和憑證,那應該可以跳過這步。參考的是 ihower 前輩的這篇文章,不過我把操作寫得詳細一點。

第一個指令:

openssl req -new > server.csr

下圖是產生 server.csr 及 privkey.pem 的流程概要: (按圖可以連到放大的原圖)

generate-ssl-key-1.png

需注意:

  1. 詢問加密字串 (pass phrase) 的時候,會被求輸入 4 個字元以上的字串。這個字串可以亂打,打的時候不會顯示在螢幕上,但同一個字串等等還會用到,所以,打上去之前先做小抄在別的地方,例如記事本之類的。
  2. Common Name 的地方,其實是要你輸入網域名稱。例如你的 SSL 簽證要用在 www.example.com ,則這裡就要填寫 www.example.com。本文所舉例的設定值都是捏造的。

接下來打入以下兩條指令:

openssl rsa -in privkey.pem -out server.key
openssl x509 -in server.csr -out server.crt \
-req -signkey server.key -days 365

其中當你輸入上列的第一條指令的時候,會要求你輸入剛剛產生 server.crt (及 privkey.pem) 所使用的加密字串 (pass phrase),這時候就從剛剛做小抄的地方複製貼上吧。不過記得這之後要把小抄刪掉。

下圖是以上兩條指令的流程概要: (按圖可以連到放大的原圖)

generate-ssl-key-2.png

最後我們就可以得到 server.crtserver.key 這兩個檔案。

如果是 Mac OS X Leopard ,則把它們複製到 Apache 設定檔的根目錄 /etc/apache2/

如果是 Ubuntu Linux 8.10,則分別複製到不同的目錄:

sudo cp server.crt /etc/ssl/certs/
sudo cp server.key /etc/ssl/private/

為了安全起見,把這兩個檔案的存取權限設為 400

2: 啟動 Apache 的 SSL 功能

看下去之前先注意這件事: Apache 的 mod_ssl 並不支援多重虛擬主機 (vhost),所以即使你設定了很多個虛擬主機 (多個網域名稱對應到同一個 IP),也只有 1 個 (最先被讀到的那個) 會起作用。詳情請參考這裡

a) Mac OS X Leopard

Mac OS X Leopard 的方式是在 /etc/apache2/httpd.conf 把這一行前面的註解移除:

Include /private/etc/apache2/extra/httpd-ssl.conf

接著編輯 /etc/apache2/extra/httpd-ssl.conf 修改這兩行,也就是簽證及金鑰的檔案位置,請把它指定到剛剛我們複製到 /etc/apache2 裡面的金鑰檔案:

SSLCertificateFile "/private/etc/apache2/server.crt"
# ...... 隔十幾行
SSLCertificateKeyFile "/private/etc/apache2/server.key"

這裡有個小知識:Mac OS X Leopard 的 /etc 是一個到 /private/etc 的連結,因此 /private/etc 可以視為與 /etc 相同。

接著要讓 Apache 的 mod_vhost 認得 SSL 的 Port。先假設你已經安裝了本文最上面提及的 Passenger 偏好設定面板,那你應該會在 /etc/apache2/httpd.conf最下面看到這幾行字,加入 NameVirtualHost *:443 即是:


# Added by the Passenger preference pane
# Make sure to include the Passenger configuration (the LoadModule,
# PassengerRoot, and PassengerRuby directives) before this section.
<IfModule passenger_module>
  NameVirtualHost *:80
  NameVirtualHost *:443 # 加入這一行
  Include /private/etc/apache2/passenger_pane_vhosts/*.conf
</IfModule>

然後到 Passenger 的偏好設定面板 (系統偏好設定),加入你的 Rails Application 設定:

add-passenger-setting.png

其中 Folder 是 Rails Application 的根目錄。

如果你的網域並不是 XXXXX.local 的話,例如 www.example.com ,那你還要在 /etc/hosts 加入一行:

127.0.0.1 www.example.com

接著編輯 /etc/apache2/passenger_pane_vhosts/nankang.local.vhost.conf (標紅色字的部份是你的網域名稱):

  1. 把整個 <VirtualHost> 區段複製一份,貼到它的下方
  2. 把下面那個區段的 <VirtualHost *:80> 改成 <VirtualHost *:443>
  3. 加入一行字 RequestHeader set X_FORWARDED_PROTO 'https'

改完大概像這樣:


<VirtualHost *:80>
  ServerName nankang.local
  DocumentRoot "/Users/chitsaou/src/nankang/public"
  RailsEnv development
  RailsAllowModRewrite off
  <directory "/Users/chitsaou/src/nankang/public">
    Order allow,deny
    Allow from all
  </directory>
</VirtualHost>

# 從上面複製下來,貼到下面,把 80 改成 443
<VirtualHost *:443>
  RequestHeader set X_FORWARDED_PROTO 'https' # 加入這行
  ServerName nankang.local
  DocumentRoot "/Users/chitsaou/src/nankang/public"
  RailsEnv development
  RailsAllowModRewrite off
  <directory "/Users/chitsaou/src/nankang/public">
    Order allow,deny
    Allow from all
  </directory>
</VirtualHost>

最後,透過以下指令重新啟動 Apache

sudo apachectl restart

試試看連線到 https://nankang.local 吧。(或著你自己設的網域名稱)

不過因為是自己簽憑證的,所以會出現「憑證不受信任」是很正常的事。

b) Ubuntu Linux 8.10

先啟動 mod_ssl:

sudo a2enmod ssl

接下來假設你已經用 mod_rails 設定了你的 Rails Application,沒有的話到官方網站抄一份設定檔便是。

編輯 /etc/apache2/sites-available/rails_app (標紅色字的部份是你的設定檔名稱):

  1. 把整個 <VirtualHost> 區段複製一份,貼到它的下方
  2. 把下面那個區段的 <VirtualHost *:80> 改成 <VirtualHost *:443>
  3. 加入一行字 RequestHeader set X_FORWARDED_PROTO 'https'
  4. <VirtualHost *:443></VirtualHost> 之間加入 SSL 簽證設定:
    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/server.crt # 憑證的位置
    SSLCertificateKeyFile /etc/ssl/private/server.key # 金鑰的位置
    BrowserMatch ".*MSIE.*" \
        nokeepalive ssl-unclean-shutdown \
        downgrade-1.0 force-response-1.0

改完大概像這樣:


<VirtualHost *:80>
  ServerName nankang.local
  DocumentRoot "/Users/chitsaou/src/nankang/public"
</VirtualHost>

# 從上面複製下來,貼到下面,把 80 改成 443
<VirtualHost *:443>
  RequestHeader set X_FORWARDED_PROTO 'https'
  ServerName nankang.local
  DocumentRoot "/Users/chitsaou/src/nankang/public"
  # 加入以下的 SSL 憑證資訊
  SSLEngine on
  SSLCertificateFile    /etc/ssl/certs/server.crt
  SSLCertificateKeyFile /etc/ssl/private/server.key
  BrowserMatch ".*MSIE.*" \
    nokeepalive ssl-unclean-shutdown \
    downgrade-1.0 force-response-1.0
</VirtualHost>

最後,透過以下指令重新啟動 Apache

sudo /etc/init.d/apache2 restart

如果你看到以下的錯誤:

Syntax error on line 6 of /etc/apache2/sites-enabled/rails_app:
Invalid command ‘RequestHeader’, perhaps misspelled or defined by a module not included in the server configuration

那麼只要把 mod_headers 開起來就行了

sudo a2enmod headers

試試看連線到 https://nankang.local 吧。(或著你自己設的網域名稱)

不過因為是自己簽憑證的,所以會出現「憑證不受信任」是很正常的事。

有關 Ubuntu 設定 Apache SSL 及簽發憑證的說明,請閱讀以下這兩個網頁:

3: 引入 Controller 的 SSL 支援

這裡先要安裝一個 plugin: ssl_requirement

安裝方法很簡單,在 Rails Project 的根目錄執行:

script/plugins git://github.com/rails/ssl_requirement.git

接著你可以依照 Plugin 網站上的說明,直接在 controllers/application.rb 導入,如此一來在所有的 controllers 都可以用 SSL;或是如果你跟我一樣,只考慮登入的時候需要 SSL,那麼就在相對應的 controllers 導入 SSL 。以一個使用 restful-authentication 為認證機制的網站為例:

更新:後來發現如果僅針對若干 controllers include SslRequirement 的話,會發生某些很悲慘的事。 所以還是一律加在 controllers/application.rb 吧!


## 在 application_controller.rb
class ApplicationController < ActionController::Base
  include SslRequirement # 加入這行
  # ....
end

4: 決定哪些 actions 需要 SSL

就看官方網站的範例就知道了。

還是以一個有 restful-authentication 的網站為例,至少在開帳號和登入的時候要有 SSL 支援,那便是:


## 在 users_controller.rb
class UsersController < ApplicationController

  ssl_required :new, :create # 加入這行
  # ...
end

## 在 sessions_controller.rb
class SessionsController < ApplicationController

  ssl_required :new, :create, :destroy # 加入這行
  # ...
end

這樣就可以了。

作者為Chitsaou Yorkxin

2009 年 一月 17 日 星期六 於 03:23:51

3 回應

RSS訂閱留言。

  1. [...] 在上述的第二步,你需要輸入一堆資料,我之前寫的這篇文章裡面有一張圖,參考著用。 [...]

    [筆記] SSL 憑證購買記 « YORKXIN×YORKXIN

    2009 年 二月 23 日 星期一 at 20:09:39

  2. 這個和這篇http://www.buildingwebapps.com/articles/6401-using-ssl-in-rails-applications文章好像… @@

    ENV

    2009 年 十一月 9 日 星期一 at 22:43:39

    • 啊設定方法就這樣而已啊 XD 大家都用 Apache + Passenger + SslRequirement 的組合技,會很像也很正常不是嗎

      而且我這篇是完全自己寫的 = = 寫了一個晚上吧

      Chitsaou Yorkxin

      2009 年 十一月 9 日 星期一 at 22:53:43


留言