有効期限切れになる前に!RakeタスクでSSL証明書を更新する方法

数週間前に、他の主要なサイトの証明書と一緒にStripeのSSL証明書が無効になりましたそれらのサイトの証明書が有効期限切れになったわけではなく、認証局のルート証明書の有効期限が切れてしまったのです。これは起こってはならないことですが、他の恐ろしいことがそうであるように、起こってほしくないときにむしろ発生してしまうものです。

サービスプロバイダの証明書が有効期限切れになることに対しては、それを防ぐための対策として自分でできることはあまりありません。しかし、自分の証明書が有効期限切れになることに対しては前もって対策を立てることができます。大きなポイントとしては、スケジュールとプロセスを確立することです。

スケジュール

これは簡単にできます。”SSL証明書をチェックする”というカレンダエントリが毎月自動更新されるように設定するだけです。そしてカレンダエントリが上がってきたら、自分のWebサイトに行き、鍵のアイコンをクリックして証明書をチェックします。チェック項目を以下に挙げておきましょう。

  1. 鍵のアイコンの見え方はいつもと同じですか? EV証明書の場合は、タイトルバーに社名が出ているはずです。通常の証明書の場合、FirefoxやChromeでは緑色の鍵、Safariではグレーの”https”が表示されています。

  2. 有効期限が近づいていませんか? 少なくとも有効期限が切れる前日までに、前もって証明書を更新しましょう。

これはSSL証明書を1つ持っているとしたらやるべき最小限のことです。もし2つ以上の証明書を持っていたり、もっと何か対策を行いたいと思っていたりする場合は、世の中にはいくらでも無料のそして有料のSSLモニタリングサービスがありますので、そのようなサービスを使えば、証明書に問題がないかどうかを四六時中チェックすることができます。

プロセス

証明書を更新するためのプロセスは、多少なりともインフラストラクチャに依存します。しかし、一般的な考え方は変わりません。

  • 新たな秘密鍵を生成する方法を知っていること
  • その鍵から新たなCSRを生成する方法を知っていること
  • そのCSRを使ってプロバイダで証明書を更新する方法を知っていること
  • 新たな証明書をインストールする方法を知っていること

証明書を更新する際は、あなたの知らないところで秘密鍵が漏洩しているかもしれないので、毎回新たな秘密鍵を使うべきだということに気をつけましょう。さらに詳しく知りたい場合は、「SSL LabによるSSL/TLSデプロイメントのベストプラクティス」(pdf)を読んでみてください。

Herokuを使っていて、このようなことが非常に面倒に思う場合は、ExpeditedSSLのアドオンをチェックしてみることをお勧めします。私が挙げたような手順をすべて自動化し、SSLの状態を保つことができますよ。

私は自分のサイト全てをVPS上で運営していますので、あらゆることを自分自身で管理できるという特権があります。そこで、覚えるのが難しい手順を私の代わりに管理してくれるRakefileをまとめました。鍵や証明書と同様にプライベートなソース管理の領域に置いてありますが、今のところ以下のような感じになっています。

desc "Generate a new key"
task :gen_key do
 domain = get_env(:domain)
 filename = "#{domain}.key"

 `openssl genrsa -out #{filename} 2048`
end

desc "Generate a new CSR"
task :gen_csr => :gen_key do
 domain = get_env(:domain)
 csr_filename = "#{domain}.csr"
 key_filename = "#{domain}.key"

 `openssl req -new -utf8 -sha256 -key #{key_filename} -out #{csr_filename}`
 `cat #{csr_filename} | pbcopy`
end

desc "Generate a proper nginx cert file from Namecheap Comodo certificate download"
task :assemble_cert do
 cert_dir = get_env(:cert_dir)
 domain = get_env(:domain)
 pem_path = "#{domain}.crt"

 File.open(pem_path, 'w+') do |pem_file|
   add_file_to_cert pem_file, domain.gsub(/\./, '_') + '.crt'
   add_file_to_cert pem_file, 'COMODORSADomainValidationSecureServerCA.crt'
   add_file_to_cert pem_file, 'COMODORSAAddTrustCA.crt'
   add_file_to_cert pem_file, 'AddTrustExternalCARoot.crt'
 end

 puts "Wrote pem to #{pem_path}"
end

def add_file_to_cert(pem_file, filename)
 cert_dir = get_env(:cert_dir)
 full_path = File.join(cert_dir, filename)
 puts "Adding #{full_path} to pem"
 pem_file.write(File.read(full_path))
end

def get_env(name)
 val = ENV[name.to_s]
 raise "Required env variable missing: #{name}" unless val && val != ''
 val
end

とてもシンプルな仕組みです。ここでやっていることは完全に適格なドメイン名を入力し鍵とCSRを出力するということだけです。使えるものがあれば、すでに存在している鍵を再利用します。クリップボードにCSRをコピーする機能があるので、プロバイダのWebサイトにペーストするだけでいいのは便利です。

証明書のインストールは、インフラストラクチャに依存します。Herokuには、Amazonと同様に、いい指針があります。Nginxを使っているのであれば、自分の証明書や中間証明書からPEMファイルを生成する必要があります。上記のRakefileにはassemble_certという名のタスクがあり、Nginxに適したPEMファイルを構築することができるのです。

非常に重要な注意:証明書をまとめる時に順序性が必要になりますが、その順序性を反映させるため、assemble_certのタスクの変更は確実に行ってください。このスクリプトはNamecheapが発行したComodoの証明書に対しては正しいファイルを生成しますが、他の認証プロバイダでも使えるかどうかの保証はありません。