Twitter Facebook github

Category “インフラ”

nginxでHTTP/2ことはじめ

このエントリを書いているうちにクリスマスイブになりましたね。いよいよ年末ムードですが、今年は年の瀬ギリギリまでお仕事です。クリスマスなんて無かった

前回のエントリの最後でHTTP/2, SPDYやりたいって書いたので有言実行ということで。この記事は既にstable(1.8)なnginxでSSLサイトを構築している環境が前提になります。

 

一昔前にGoogleが開発した次世代WebプロトコルのSPDYが流行っていましたが、SPDYを受け継いだHTTP/2の出現から、新たにSPDYを導入することはほとんどないようです。

Google自身もHTTP/2へ移行したようですので、今回はSPDYではなくHTTP/2を使ってみようと思います。cssやjs、画像などを多くロードしているページでは、表示速度がかなり向上するようですよ。

nginxでhttp/2を導入する場合は、–with-http_v2_module というパラメータをつけてビルドしないといけません。ただ、nginx公式のリポジトリから落とせるバイナリはこのパラメータ付きでビルドされているようですので、今回はこれを使う方針で進めます。(ちなみにSPDYは古いnginxで利用できていましたが、HTTP/2に載せ替えられる形で使えなくなりました。)

 

古いnginxの削除

まずは今インストールされているバージョンを確認します。

執筆時点のAmazon Linuxで標準のリポジトリからyumでインストールすると、上の通りstableな1.8がインストールされます。nginxは今年の9月末にリリースされたv1.9.5でHTTP/2をサポートするようになったので、1.8.0のnginxは削除します。

 

メインラインnginxをインストール

前述の通り標準のリポジトリからインストールすると1.8が落ちてきてしまいます。メインラインのnginxをインストールするには、公式のドキュメントを参考にnginxのリポジトリを参照するように変更します。

正しくメインラインのnginxが取得できるか確認し、問題なければインストールします。今回はnginx.repoにpriority=1を追記したので、Amazon標準リポジトリよりnginxリポジトリが優先して参照されます。このほかyumのコマンドに –disablerepo=amzn-main を記述することでもインストール可能ですが、yum updateなどで問題になることがあるので特に理由がなければ優先度を変更しましょう。

すんなりインストールできました。念のために自動起動の設定を確認しておきましょう。

 

http2の設定

次は1.9.9のnginxでhttp2を使えるようにします。

今回は1.8系のnginxからの乗り換えなので、古いnginxをremoveした時に残っている設定ファイルをベースに書き換えていきます。

SSLは前回設定していますので、書き換えるのはここだけですね。本当に簡単です。

なお、http2はSSLなしでも利用できる仕様ですが、事実上SSL対応しないと使えませんので、SSLに対応していない場合は過去のエントリ「Let’s Encryptをnginxで使ってみる」などをご参考ください。

 

動作確認する

ちゃんとHTTP/2で通信してくれているかどうかは、Chromeの場合デバッグ機能のnet-internalsを開くことで確認できます。新しいタブで、

chrome://net-internals/#http2

を開いてみてください。

HTTP/2 sessionsのテーブルに、今回対応したドメインが表示されていればうまく動いています。

スクリーンショット 2015-12-24 2.55.28

 

また、Chrome Extensionの「HTTP/2 and SPDY indicator」もオススメです。これをインストールしておくと、ブラウズしているページがHTTP/2またはSPDYに対応しているかどうかをURLバー右側の雷アイコンで視覚的に表示してくれます。

表示されている雷アイコンが青色であればHTTP/2、緑色であればSPDY、灰色であればこれらのプロトコルに対応していないことを表します。正常に動いているようですね。

 

雑感

数年前にSPDYで賑わったことは比較的記憶に新しいのですが、暫くその動向を追っていない間にHTTP/2が策定され移行が進んでいるようです。Web技術の変革スピードの早さを再認識しました。時代に取り残されないよう、新しい技術も積極的にキャッチアップしていきたいですね。

WordPressをSSLに対応させる手順

非SSL環境で動かしていたWordPressをSSL化するときの手順です。ちょうどLet’s Encryptを使って認証された証明書が手に入ったので、これを使ってWordPress全体をSSL化してみました。手順を備忘録として残しておきます。

 

wp-config.phpの修正

まずは基本ですね。WP_HOMEとWP_SITEURLを書き換えます。また、FORCE_SSL_ADMINを設定することで管理画面はHTTPSを強制できます。

 

データベースの修正

WordPressのデータベースに接続し、(WPプレフィックス)_options テーブルを開きます。

option_nameカラムがsiteurlになっているレコードを探し、値のhttp://をhttps://に書き換えます。続いてoption_nameカラムがhomeになっているレコードを探し、同じように書き換えます。

 

HTTP接続をHTTPSにリダイレクト

上の2ステップでWordPressのシステム的な部分の移行が終わりました。HTTPSで適当なページを開いて正しくリンクが遷移できるか、リンク遷移後もページもHTTPSになっているか、画像や画像のサムネイルが正しく読み込めているか、管理画面へHTTPSのままログインできるか、などをチェックします。

問題がないようであれば、httpでの接続はすべてhttpsにリダイレクトするようにします。(任意です。httpsにすると若干サーバーの負荷も増えますので、ハイトラフィックなブログを運営されている方はhttpの方が有利という考え方もできます。ブログであれば、http/httpsどちらでもアクセス出来るようにしておき、通信を暗号化するかどうか判断をユーザーに委ねるのも良い方法です。)

今回nginxを使っているので、nginx.confを書き換えることにしましょう。リダイレクトにはrewiteを使っているブログも多く見られますが、nginx.orgではreturnが使われていたのでreturnを使うことにします。計った訳ではないですが、シンプルな分処理も早そうです。

保存したらnginxを再起動します。

再起動が終わったら、適当なURLを叩いてみてHTTPSに正しくリダイレクトされることを確認します。

 

安全でないリソースを排除する(Mixed Content問題)

これで9割方終わっていて、SSLにするという目的自体は達成なのですが、次のように「このページに安全でない他のリソースが含まれています。」と表示され、URLのアイコンや文字色も緑色にならないことがあります。

これは、アクセスしているページはSSL接続にも関わらず、そのページ内から非SSLの静的ファイル等をロードしているためです。例えば、サードパーティのCSSやJavaScript、フォントファイル、画像ファイル等を絶対パスでhttp:// に続く形でロードしていることに起因するものが多いです。

これらを見つけて、httpsが提供されているコンテンツはhttpsでロードするように、提供されていない場合はライセンスを確認して自分のサーバーでホスティングできるか確認して移行するなどの対策を行います。量が多いと結構大変ですね…。

どのファイルがMixed Contentなのかは、Google Chromeのデベロッパーツールにあるコンソールが助けになります。

例えば上の画像のように、過去にアップロードした画像ファイルに起因するものであれば、次の項を読んでみてください。テーマやプラグインに起因するものは、手動で直さなければいけません。

ちなみに、Protocol Relative URLという仕様により、CSSやJavaScriptをロードする際のhttpやhttpsを省いて記述すると、読み込み元のスキーム(SSLか非SSLか)に従って読み込むスキームも変更されポータビリティが高まります。(例えば //example.com/main.js をSSLのページからロードさせた場合はhttps://〜と解釈され、非SSLのページからロードさせた場合はhttp://〜と解釈されます。)

 

アップロード画像に起因するMixed Contents問題を直す

この場合、基本的には記事を編集して、画像へのURLを書き直すことで修正できますが、過去の記事・画像が多いと直すのに骨が折れます。

一気に前述したProtocol RelativeなURLに画像リンクを書き換えるSQL文を使うと一瞬で解消できるでしょう。テストしていますが、万一のために必ず実行前にテーブルのバックアップを取ってください。

 

無事にMixed ContentsによるSSLの警告も消えました。

 

雑感

サクっとこなすつもりが意外と色々気にすることがあって大変でした。一度やっておくとノウハウ的に使い回せるので、知識として蓄えておきたいですね。

次はHTTP/2, SPDY対応や、証明書の透明性確保あたりを触れたらいいなと思っています。

Let’s Encryptをnginxで使ってみる

Let’s Encryptとは

https://letsencrypt.org/

無料で認証されたSSL証明書を発行できるサービスです。今月パブリックベータになり、誰でも試すことができるようになりました。Web Tech界隈では今アツい技術の一つです。

今回使うnginxは1.8系のため、HTTP/2、SPDYは使えません。メインラインの1.9系を使うことで対応できるので、興味がある方は別に調べてみてください。

ビジネスなどエンタープライズで使用する際はこのSSLではなく信頼された発行元から

 

Let’s Encryptのインストール

Let’s Encryptを使うためには、nginxやapacheなどのウェブサーバーのほかGitが必要です。OpenSSLなどはインストールされていない場合、Let’s Encryptが自動的にインストールしてくれます。Gitがインストールされていなければ、事前にyumで入れておきます。

まず、Let’s Encryptのクライアント本体を適当な場所にgitでクローンしておきましょう。

gitが必要な箇所はここだけです。結構頻繁に更新されているようなので、都度ローカルリポジトリも追従させてあげるのが良いでしょう。

クローンしたら、内包されているメインのコマンド letencrypt-auto を叩いてみましょう。このコマンドを実行した場合、letsencryptが使用する必要なライブラリがインストールされていなければ自動でインストールしようとします。rootで実行していない場合はパスワードを入力してください。

今回、AWS EC2で実行している関係で上のような警告が表示されました。EC2上で利用するのであれば一般的なCentOS等と使い方は何ら変わりませんので、言われるままに継続します。

一度debugをつけて実行した後はdebugなしで利用できるようです。

 

証明書の取得

発行にはドメインの所有権認証が必要です。letsencrypt-autoでは、次の方法でこの認証を簡単なコマンドだけで実行できます。

現時点でlets encryptでは apache, standalone, webroot の3種類の証明書取得方法をサポートしています。apacheを使っている場合はapacheを使うと自動でインストールしてくれます。standaloneはletsencrypt自身がウェブサーバーを起動して所有権の認証を行います。webrootは既にウェブサーバーが起動している場合、ドキュメントルートを指定して既存のサーバーを介して認証を行います。 詳しい違いについては ./letsencrypt –help で確認してください。

今回はnginxですし、既に起動しているサーバーに対してインストールしょうとしているので、3番目のwebrootオプションを使って認証を行います。

上記を実行するとメールアドレスの入力と利用規約への同意が求められますので、指示通り進みます。認証情報を紛失した際などは、ここで登録したメールアドレスを使ってリカバリするので間違えないように気をつけてください。

次のようなメッセージが出力されれば、正しく証明書を取得することができました。

 

上の例では、/etc/letsencrypt/live/<ドメイン>/ の中に証明書類が保存されています。

 

DH鍵交換のパラメーター生成

証明書をウェブサーバーにインストールする前に、セキュリティ向上のために実行することをおすすめします。

opensslコマンドで生成します。AWSのsmallインスタンスで3分程度かかりました。環境によってはもっと時間がかかるかもしれません。

 

証明書をnginxに設定

最後に取得したSSL証明書をnginxから参照して利用できるようにします。

上の通り変更して、nginxを再起動します。設定ファイルに問題がなければSSLで通信できるようになっていますので、テストしてみましょう。

ssl-test

ということで、Let’s Encryptで簡単にSSL対応できました!

 

SSLのセキュリティテスト

SSLサーバーのセキュリティテストは Qualys SSL Report を使って簡単に実施できます。

ssl-check

最低限の設定を施しただけだとCやDランクになるかと思います。例えばPOODLEや弱いDHパラメーター、ダウングレード攻撃などの脆弱性を防いでいない場合は評価が下がります。

前述のnginx.confの設定を施せばAまたはA+の評価になると思います。ときどきこのサイトでSSLの安全性をチェックして、常にこの評価をキープしていきましょう。

 

雑感

以前から無料で発行できるSSL証明書もありましたが、手続きが煩雑だったりと色々厄介なことが多かったです。Let’s Encryptもまだ改善の余地はあり、速い速度で開発も進んでいますので、まだまだ今後に期待できるでしょう。Let’s EncryptでSSL対応が当たり前な時代になれば良いですね。

 

Vagrant+Chef-Zero入門

今更感があってごめんなさい。確かにAnsibleとかFabricとか流行ってますもんね。でも老舗感と重厚感あるChef触っておくのもいいと思うんです。にしてもChefって色々複雑そうで入りにくいですよね…。

私はChef初級者です。一応裏付けは取るようにしていますが、もし間違いがあれば指摘いただけると嬉しいです。

Chef-Zeroっていうのは、消えゆく(らしい)Chef-Soloに代わる形で推奨されている単体で動く環境構築の自動化ツールです。元々Chefはサーバーとクライアントが分かれているアプリケーションですが、Chef-Soloは独立して動くChefで、Chef-Zeroは1つのマシンにサーバーとクライアントの両方を入れたようなイメージらしいです。

 

導入編

昔はRubyのgemでChef入れて、関連ツール入れて・・・のような形でRubyの環境がない人はそこから、という結構導入が重めでした。

最近はChefDKというのがあり、Chef本体とその関連ツールを一気にインストールできるようになっています(実際は内部でgemが使われているんですが…)。

https://downloads.chef.io/chef-dk

今回はこのMac用のChefDK、執筆時点の最新 0.10.11を使います。バージョン番号から察するに、まだ正式リリースではないようですがインストールされるChefは最新のものなので心配ありません。

インストール直後のバージョン情報は次の通りです。

 

Vagrantを準備

とりあえず適当にVagrantのゲストOSを用意します。今回は Vagrantことはじめ でダウンロードしてきた bento/centos-6.7 を使います。読むのに慣れてVagrantfileのコメントが邪魔になってきたら init時に –minimal (-m)オプションを付けましょう。コメントが消えてくれます。

Chefを使ってプロビジョニングを行うためには、ゲストOS内にもChefが必要です。色々な解説サイトを読むと、Vagrantのプラグインを入れてゲストOSにChefをインストールするような記事が多く見られますが、そうするとVagrantfileを配布する際にそのプラグインのインストール手順を共同開発者に伝え、手動で入れてもらう必要があります。せっかくの自動化なのに、vagrant upだけで完結させたいですよね。なので今回はChefのインストール部分だけインラインシェルを使うことにします。

 

Chefリポジトリを作る

まずはベースとなるリポジトリを作ります。今後はこの中で作業するので、移動しておいてください。

 

Cookbookを作る

オリジナルなcookbookはデフォルトで作られているcookbooksではなくsite-cookbooksに入れるのが慣習のようなので、それに従おうと思います。ここでは例として空のCookbookを置いてみます。

これで空のクックブックができました。

 

Berksfileを作る

実際にCookbookの中身を作り込む前に、Berkshelfへの登録を済ませておきましょう。
BerkshelfというのはRubyのBundler、PHPのComposerのように、Cookbookに依存するCookbookを自動で入れてくれるCookbookマネージャーのような役割を担います。

Berkshelfで自分の使いたいCookbookを記述しておくのがBerksfileになります。これをChefリポジトリのルートに作ります。

1行目のsourceというのは、指定された名前のCookbookをデフォルトでどこから落としてくるか指定します。今回は有名な Chef Supermarket を使いました。

その後で使いたいcookbookの名前とバージョンを指定し、

コマンドを実行することで、 Berksfileを元にcookbooksディレクトリに依存性が解決されたCookbookが保存されるようになります。

実際に上のコマンドを実行してみてください。./site-cookbooks/do-something が ./cookbooks/do-something にコピーされたと思います。今回のようにpathパラメーターにローカルのCookbookを与えた場合は、sourceに記述したリモートロケーションではなく指定されたローカルのCookbookを見に行くようになります。

 

Vagrantのプロビジョンで実行する

Chef-Soloでプロビジョンを行ったことがある人は同じように使えます。

chef_zero provisionerを使って、cookbooksのディレクトリと実行するレシピをadd_recipeで列挙していきます。

add_recipe の代わりに add_role を使ってRolesという機能も使えます。これは複数のレシピをロールという単位で管理することで、役割という意味単位でグルーピングして扱うことができます。

 

Marketで公開されているCookbookを使ってみる

既に作った do-something のCookbookを自分で書いてもいいのですが、とても長くなってしまうので今は公開されているCookbookを使ってみます。例えばApacheやMySQL Serverなどの有名どころは大抵公開されているので、そもそも自分で一から書くことは少ないかもしれません。

上でBerksfileのsourceに指定したURLが Chef Supermarket です。

https://supermarket.chef.io/

今回はApacheのCookbook、 apache2 を使ってみようと思います。

berks vendorでCookbookを取得してきます。

cookbooksディレクトリの中にapache2のcookbookがインストールされればOKです。

Vagrantfileにも追加して、実行してみましょう。

プロビジョンが完了したら、正しくインストールされているか確認してみましょう。

 

Vagrantfileを公開するとき

チーム内で開発用仮想マシンを共有したいときは、Vagrantfileの他に最低限 ./provision/cookbooksを共有するといいでしょう。Berkshelfがインストールされているユーザーであれば、 Vagrantfile+Berkshelfでなんとかなりますが、一般的にはVagrantfile+cookbooksの共有が多いような気がします。

 

まとめ

Chef勉強しながら作業内容の備忘録と共有のために書きました。実際これだけではChefの2割にも満たないくらいの知識量だと思います。学習コストが比較的高いですが頑張っていきましょう。

EC2起動後に行うべき設定

標準のAmazon Linux AMIを前提に進めていきます。

AWS EC2はデフォルトでrootログイン禁止、オプトイン方式のインバウンドセキュリティポリシー、公開鍵認証のキーペア生成など、適当に使っていてもある程度セキュアになりますが、少しの手間でより堅牢なサーバーにできます。是非取り組んで欲しいです。

 

とりあえずアップデート

AmazonLinuxの場合大抵最新のパッケージが入っていますが、念のために更新しておきます。

以下の指定にすることでセキュリティパッケージの更新のみを適用できるため、すべてのパッケージを更新したくない場合、最低限こちらだけでも実行しましょう。

 

 

タイムゾーンの変更

EC2は起動したリージョンにかかわらずUTCの設定になっています。最近は世界を目指すプロダクトではアプリケーションやDBクロックを日本時間に変えずUTCのまま使用し、表示時にアプリケーションが変換するなど、UTCスタンダードなモデルもよく聞きますね。とはいえ日本に住む以上日本時間で統一したくなるのも事実。

設定が終わったら再起動して全てのプロセスに反映させます。

再起動後はdateコマンドで日本時間になっているのを確認します。

 

rootパスワードの設定

適切にインスタンスを起動した場合、接続に必要な鍵が適切に管理されていれば通常安心ですが、万が一の漏洩に備えてrootにパスワードを設定しておきましょう。

 

管理用ユーザーの変更

デフォルトではec2-userというユーザーが管理用に作られていますが、セキュリティを高めるために別の名前のユーザーを使うことをお勧めします。その際、AmazonLinuxでは他Linuxによくあるwheelグループなどの管理グループ全体にsudoを付与しているわけではなくユーザー単位に設定されているため、新規で追加したユーザーにも個別にsudo権限を付与するようにしましょう。

まずは新しいユーザーを追加して、管理(sudo)権限を付与します。

cloud-initファイルを次のように変更します。

ec2-userのsudo権限は /etc/sudoers.d/cloud-init に書かれており、/etc/sudoersからロードされています。引数なしのvisudoでは編集できません。また、sudoersファイルを直接viなどで編集するのは危険なのでやめましょう。ここではec2-userの権限を剥奪し、またsudo実行時にパスワードを求めるように(NOPASSWD非設定)しました。

すべて完了したら、一度ログアウトして外部からnewuserでログインしてみましょう。鍵もコピーされていますので、ssh接続時はユーザー名だけを変更すればOKです。正しく接続できて、sudoがパスワード入力後に実行できれば完了です。

最後にec2-userを削除して終わりです。ec2-userでログインできないことを確認しましょう。

 

ポート番号の変更

定番のセキュリティ対策ですね。SSHのポート番号をデフォルトの22番から別のものに変更します。簡単で単純ですが、効果は高いです。

ここでは仮に22922に変更します。また、待ち受けポートは1つという縛りはないため、下記例のように変更前のポート22でも待ち受けるようにしたまま進めると万が一の特に困らないでしょう。

終わったらreloadして設定を反映させます。また、iptablesが稼働中であれば不正なルーティングによって弾かれる可能性があるので停止されているのを確認してください。

SSHのポートを変更しますので、AWSのEC2に紐付いているセキュリティポリシーも変更する必要があります。新しく設定したポート番号のTCPを、自分のIPアドレスから通すように設定を変更しましょう。

これで新しいポートを用いて接続できるようになりました。一度ログアウトして、新しいポートへのSSH接続が成功すれば完了です。sshd_configを開いて Port 22 の記述を削除したあとでreloadしましょう。AWSのセキュリティポリシーからもTCP:22のルールを削除して完了です。

万が一新しいポートで接続できない場合は、落ち着いて22ポートで接続して、設定を再確認してください。ポートが正しく待ち受けられているかチェックするにはnetstatコマンドが有効です。

 

SSHをもっとセキュアに

  • iptablesでアクセスできる回数を制限
  • 認証試行回数の設定
  • アクセス試行ログの通知

もろもろ考えましたが次の観点からこれらを講じる意味はないと思っているので割愛します。

  • 鍵認証が強制になっていること(デフォルト)
  • rootログインができないこと(デフォルト)
  • ユーザー名を変更したこと
  • SSHポートを変更したこと
  • EC2のセキュリティポリシーにより、SSHポートは信頼されたIPにしか公開されていないこと

これだけの対策を講じていれば、上述の更なる対策はあまり意味がありません。

鍵の管理だったり、権限を持つ内部からの不正アクセスリスクの方が高いはずなので、その辺の対策(運用体制などの整備)に時間を割く方が良いでしょう。

 

そのほか

nginxやその他サーバー類の個別なセキュリティ対策についてはまた別に書こうと思います。

特にエンタープライズでのウェブサイト改竄や情報流出などの危機管理が取り沙汰されている昨今、エンジニアである以上最前線で食い止めていきたいですね。

 

  • 2015-11-18 公開
  • 2015-11-25 加筆