RailsをApache上で動かすためのモジュールPhusion Passenger

Rails(3.0.10)をPhusion Passenger上で動かしたときのメモ。サーバーはさくらインターネットVPS、OSはCentOS

Phusion Passengerは、RailsApache(WEBサーバー)上で動かすためのApacheモジュール。Railsを動かすためのWEBサーバーは、開発環境ではWEBrick、本番環境ではMongrelが使われることが多かったが、これらは簡単に使える反面、動作速度やメモリ使用量ではイマイチだった。また、Apacheが入っているのに、Rails専用のWEBサーバーアプリケーションを別個にインストールしないといけないのもイマイチだった?というのが、Phusion Passengerが開発された背景なんだと思う。(WEBや書籍を斜め読みした。)

他のWEBサーバーと比べて動作速度やメモリ使用量の違いは一目瞭然。

Advantages of using Passenger + Apache over Webrick - stackoverflow

公式ページでもベンチマークをガッチリやっている。

手順は以下の通り。

  1. Passengerをインストールする
  2. Apacheの設定ファイル(httpd.conf)を編集する
  3. Railsのサンプルアプリを作成する

1. Passengerをインストールする

以下のコマンドを実行する。

$ gem list passenger         # Passengerが入っていないことを確認
  
  *** LOCAL GEMS ***
  # 何も表示されない。
  
$ sudo gem install passenger # Passengerのgemをインストール
  ...
$ sudo passenger-install-apache2-module # Apacheのモジュールをインストールするコマンド
  ...  
  Installation instructions for required software
  
   * To install Curl development headers with SSL support:
     Please run yum install curl-devel as root.
  
   * To install Apache 2 development headers:
     Please run yum install httpd-devel as root.
  
   * To install Apache Portable Runtime (APR) development headers:
     Please run yum install apr-devel as root.
  
   * To install Apache Portable Runtime Utility (APU) development headers:
     Please run yum install apr-util-devel as root.
  
  If the aforementioned instructions didn't solve your problem, then please take
  a look at the Users Guide:
  
    /usr/local/lib/ruby/gems/1.8/gems/passenger-3.0.9/doc/Users guide Apache.html

インストールにはもっとソフトウェアが必要と言われてインストールが中断されるので、足りないものをインストールして再実行。僕の場合は上記のライブラリ。

# 足りないライブラリをインストール
$ sudo yum install httpd-devel
$ sudo yum install apr-devel
$ sudo yum install apr-util-devel

# 再度passenger-install-apache2-moduleを実行
$ sudo passenger-install-apache2-module
  ...
  The Apache 2 module was successfully installed.
  
  # Apacheの設定ファイルへ追記する その1
  Please edit your Apache configuration file, and add these lines:
  
     LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-3.0.9/ext/apache2/mod_passenger.so
     PassengerRoot /usr/local/lib/ruby/gems/1.8/gems/passenger-3.0.9
     PassengerRuby /usr/local/bin/ruby
  
  After you restart Apache, you are ready to deploy any number of Ruby on Rails
  applications on Apache, without any further Ruby on Rails-specific
  configuration!
  
  Press ENTER to continue.
  
  # Apacheの設定ファイルに追記する その2
  --------------------------------------------
  Deploying a Ruby on Rails application: an example
  
  Suppose you have a Rails application in /somewhere. Add a virtual host to your
  Apache configuration file and set its DocumentRoot to /somewhere/public:
  
     <VirtualHost *:80>
        ServerName www.yourhost.com
        DocumentRoot /somewhere/public    # <-- be sure to point to 'public'!
        <Directory /somewhere/public>
           AllowOverride all              # <-- relax Apache security settings
           Options -MultiViews            # <-- MultiViews must be turned off
        </Directory>
     </VirtualHost>

2. Apacheの設定ファイル(httpd.conf)を編集する

インストールが終了すると、Apacheの設定ファイル(httpd.conf)に何を書けばよいか、わかりやすく提示してくれた(上記のログ)。さっそくその通りに編集してみる。

$ sudo vi /etc/httpd/conf/httpd.conf # httpd.confの編集
  ...
  
  # Dynamic Shared Object (DSO) Support
  ...
  # Passenger
  LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-3.0.9/ext/apache2/mod_passenger.so # 追記
  PassengerRoot /usr/local/lib/ruby/gems/1.8/gems/passenger-3.0.9 # 追記
  PassengerRuby /usr/local/bin/ruby                               # 追記
  ...
  
  # VirtualHost example:
  # Almost any Apache directive may go into a VirtualHost container.
  # The first VirtualHost section is used for requests without a known
  # server name.
  
  <VirtualHost *:80>
     ServerName wwwXXXXXX.sakura.ne.jp
     DocumentRoot /var/www/demo/public            # Railsアプリケーション直下のpublicフォルダを指定
     <Directory /var/www/demo/public>
        AllowOverride all                         # ディレクトリごとの設定(.htaccessファイル)での上書きを許可する
        Options -MultiViews                       # MultiViewsを有効にする
     </Directory>
  </VirtualHost>

$ sudo /etc/init.d/httpd restart # Apacheを再起動

VirtualHostのところの設定が何を意味しているかわからなかったので調べてみた。

そもそもVirtualHostとは、ひとつのサーバーで複数のドメイン名のWEBサイトを運用するための仕組み。たとえば、www.myapp1.comとwww.myapp2.comのドメインを同じサーバーで運用することができる。詳しくはバーチャルホストの例 Apacheを参照のこと。

Directoryの「AllowOverride all」は、httpd.confの設定をユーザーによる設定で上書きできるかということ。ユーザーによる設定は、ディレクトリごとに.htaccessファイルを作って設定内容を書き込むことでできる。.htaccessファイルの詳細は、バーチャルホストの例 Apacheを参照のこと。

同じくDirectoryの「Options -MultiViews」は、HTTPのリクエストヘッダによって適切なコンテンツを返す設定を有効にするという意味。たとえば、Accept-Languageヘッダ値に「en」が優先されていれば英語版のファイル、「ja」が優先されていれば日本語版のファイルを送り出すようなことができるようだ。ITmedia エンタープライズ : Linux Tips「ApacheのMultiViews機能ってなに?」

DocumentRootには、Railsアプリのディレクトリを指定する。

3. Railsのサンプルアプリを作成する

前回、最新(3.1.1)のRailsをインストールしたが、手元の書籍のRailsのバージョンは3.0.xだったので、まずはダウングレードする。

$ rails -v
  Rails 3.1.1
$ sudo gem install rails --version 3.0.10 --no-rdoc
$ sudo gem uninstall rails --version 3.1.1
$ rails -v
  Rails 3.0.10

そして、アプリケーションを新規に作成する。

$ cd /var/www/html
$ sudo rails new demo -d mysql # DBを指定してアプリケーションを新規作成
$ cd demo
$ sudo bundle install          # 足りないライブラリをインストール
$ sudo rails generate scaffold Post name:string title:string content:text # Postという名前のモデル、ビュー、コントローラーの一式を作成
$ sudo rake db:create RAILS_ENV=production      # DBを新規作成
$ sudo rake db:migrate RAILS_ENV=production     # テーブルを新規作成

ブラウザでhttp://wwwXXXXXX.sakura.ne.jp:3000/postsを開いて、demoアプリケーションの画面が表示されたのでこれでOK。

と思いきや、上記のやり方だと、アプリケーションのディレクトリやファイルのオーナーはrootになっている。rootだとログファイルにログを書き込めないなどの問題が出る。そこで、logとtmpディレクトリのオーナーとパーミションを変更する。

$ ls -l /var/www/html/demo/log
  -rw-rw-rw- 1 root root   0 Nov 14 23:11 development.log
  -rw-rw-rw- 1 root root   0 Nov 14 23:39 production.log
  -rw-rw-rw- 1 root root   0 Nov 14 23:11 server.log
  -rw-rw-rw- 1 root root   0 Nov 14 23:11 test.log

この状態でURLにアクセスしても、production.logのファイルサイズは0のまま。以下のようにディレクトリのオーナーとパーミションを変更する。

$ cd /var/www/html/demo
$ sudo chown -R montecut:users log tmp # オーナーをmontecutに変更
$ sudo chmod 755 -R log tmp            # アクセス権を設定
$ sudo service httpd restart           # Apacheを再起動

複数人でサーバーを使うことを考えると、本当はオーナーは個人のログインアカウントでない方がいいかも。とりあえず、これでURL(http://wwwXXXXXX.sakura.ne.jp:3000/posts)を開く。その後、ログファイルを確認すると、ファイルサイズが増えていることが確認できる。

$ ls -l /var/www/html/demo/log
  -rwxr-xr-x 1 montecut users   0 Nov 14 23:11 development.log
  -rwxr-xr-x 1 montecut users 251 Nov 14 23:39 production.log
  -rwxr-xr-x 1 montecut users   0 Nov 14 23:11 server.log
  -rwxr-xr-x 1 montecut users   0 Nov 14 23:11 test.log

さくらVPSにRuby Enterprise Editionをインストールして、Railsのサンプルアプリを作成する

さくらVPS(CentOS)にRuby Enterprise Editionをインストールしたときのメモ。REE以外にも、MySQLApacheRailsをインストールして、サンプルサプリを作成する。

REEの公式ページによると、REEはPhusion Passenger(Railsを実行するためのApacheモジュール)を使ったときに、メモリ使用量が33%削減するらしい。通常のRuby1.8.7との互換も100%とのこと。

Ruby Enterprise Editionのインストールには、さくらVPSのCentOSにRuby Enterprise Editionをインストール id: kadoppeさんの記事を参考にさせていただいた。

  1. Ruby Enterprise Editionのインストール
  2. MySQLのインストール
  3. Apacheのインストール
  4. Railsのインストール
  5. サンプルアプリケーションの作成

1. Ruby Enterprise Editionのインストール

$ wget http://rubyenterpriseedition.googlecode.com/files/ruby-enterprise-1.8.7-2011.03.tar.gz # ファイルをダウンロード
$ tar zxvf ruby-enterprise-1.8.7-2011.03.tar.gz # ファイルを展開
$ cd ruby-enterprise-1.8.7-2011.03
$ sudo ./installer                              # インストーラーを実行
  Welcome to the Ruby Enterprise Edition installer
  This installer will help you install Ruby Enterprise Edition 1.8.7-2011.03.
  Don't worry, none of your system files will be touched if you don't want them
  to, so there is no risk that things will screw up.
  
  You can expect this from the installation process:
  
    1. Ruby Enterprise Edition will be compiled and optimized for speed for this
       system.
    2. Ruby on Rails will be installed for Ruby Enterprise Edition.
    3. You will learn how to tell Phusion Passenger to use Ruby Enterprise
       Edition instead of regular Ruby.
  
  Press Enter to continue, or Ctrl-C to abort.
  
  Checking for required software...
  
   * C compiler... found at /usr/bin/gcc
   * C++ compiler... found at /usr/bin/g++
   * The 'make' tool... found at /usr/bin/make
   * The 'patch' tool... found at /usr/bin/patch
   * Zlib development headers... not found
   * OpenSSL development headers... not found
   * GNU Readline development headers... not found
  
  Some required software is not installed.
  But don't worry, this installer will tell you how to install them.
  Press Enter to continue, or Ctrl-C to abort.

「必要なライブラリが入ってないよ!」と言われるので、インストールする。

$ sudo -i
# yum -y install readline-devel
# yum -y install zlib-devel
# yum -y install openssl-devel

再度インストーラーを実行。途中で、どのディレクトリにインストールするか聞いてくるので、/usr/localと入力する。

# ./installer # インストーラーを実行
  ...
  Where would you like to install Ruby Enterprise Edition to?
  (All Ruby Enterprise Edition files will be put inside that directory.)
  
  [/opt/ruby-enterprise-1.8.7-2011.03] : /usr/local # ここに入力する
  ...
  ...
  
  # mysqlのgemがインストールできなかったというエラー
  --------------------------------------------
  Warning: some libraries could not be installed
  The following gems could not be installed, probably because of an Internet
  connection error:
  
   * mysql
  
  These gems are not required, i.e. Ruby Enterprise Edition will work fine without them.
  But most people use Ruby Enterprise Edition in combination with Phusion Passenger and 
  Ruby on Rails, which do require one or more of the aforementioned gems, so you may want to
  install them later.
  
  To install the aforementioned gems, please use the following commands:
    * /usr/local/bin/ruby /usr/local/bin/gem install mysql
  
  Press ENTER to show the next screen.
  ...
  
  # Phusion Passengerについての注意事項
  --------------------------------------------
  Ruby Enterprise Edition is successfully installed!
  If want to use Phusion Passenger (http://www.modrails.com) in combination
  with Ruby Enterprise Edition, then you must reinstall Phusion Passenger against
  Ruby Enterprise Edition, as follows:
  
    /usr/local/bin/passenger-install-apache2-module
  
  Make sure you don't forget to paste the Apache configuration directives that
  the installer gives you.
  
  
  # アンインストール方法
  If you ever want to uninstall Ruby Enterprise Edition, simply remove this
  directory:
  
    /usr/local
  
  If you have any questions, feel free to visit our website:
  
    http://www.rubyenterpriseedition.com
  
  Enjoy Ruby Enterprise Edition, a product of Phusion (www.phusion.nl) :-)

mysqlのgemがインストールできなかったと出る。

2. MySQLのインストール

mysqlのgemがインストールできなかったのは、MySQLそのものがインストールされていないことが原因かもしれない。まずMySQLをインストールしてからにしてみる。

# yum install mysql-server # MySQLをインストール
# /usr/local/bin/ruby /usr/local/bin/gem install mysql # mysqlのgemをインストール
  Building native extensions.  This could take a while...
  ERROR:  Error installing mysql:
          ERROR: Failed to build gem native extension.
  
          /usr/local/bin/ruby extconf.rb
  checking for mysql_ssl_set()... no
  checking for rb_str_set_len()... no
  checking for rb_thread_start_timer()... no
  checking for mysql.h... no
  checking for mysql/mysql.h... no
  *** extconf.rb failed ***
  Could not create Makefile due to some reason, probably lack of
  necessary libraries and/or headers.  Check the mkmf.log file for more
  details.  You may need configuration options.
  
  Provided configuration options:
          --with-opt-dir
          --without-opt-dir
          --with-opt-include
          --without-opt-include=${opt-dir}/include
          --with-opt-lib
          --without-opt-lib=${opt-dir}/lib
          --with-make-prog
          --without-make-prog
          --srcdir=.
          --curdir
          --ruby=/usr/local/bin/ruby
          --with-mysql-config
          --without-mysql-config
  
  
  Gem files will remain installed in /usr/local/lib/ruby/gems/1.8/gems/mysql-2.8.1 for inspection.
  Results logged to /usr/local/lib/ruby/gems/1.8/gems/mysql-2.8.1/ext/mysql_api/gem_make.out

mysqlのgemのインストールでエラーが出た。原因はよくわからないが、 mysql-serverだけでは足りないっぽい。以下のように、mysqlmysql-develもインストールするとエラーが出なくなった。

# yum -y mysql mysql-devel
 ...
 Complete!

# /usr/local/bin/ruby /usr/local/bin/gem install mysql
  Building native extensions.  This could take a while...
  Successfully installed mysql-2.8.1                      # 今度は成功
  1 gem installed
  Installing ri documentation for mysql-2.8.1...
  Installing RDoc documentation for mysql-2.8.1...

今度はmysqlのgemを無事インストールできた。ついでに、MySQLを起動しておく。

# /etc/rc.d/init.d/mysqld start # サービスを起動
# /sbin/chkconfig mysqld on     # MySQLの自動起動をONにする

3. Apacheのインストール

Apacheをまだインストールしていなかったらインストールする。さくらVPS設定その2 Apache+MySQL+WordPress id:r7kamuraさんの記事が参考になる。

4. Railsのインストール

最新版のRailsをインストールする。

$ sudo gem install rails --no-rdoc # ドキュメント不要
  -bash: gem: command not found

おっと、gemにパスが通っていないため見つからない。これまでのように毎回フルパスを入力するのは面倒なので、以下のようにbashの設定ファイルにパスを書き込む。さくらVPS設定その1 User+SSH+Firewall id:r7kamuraさんの記事が参考になる。

$ vi ~/.bash_profile # ファイルを編集する
  PATH=$PATH:$HOME/bin                       # この行の後に...
  PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin # この行を追加

# 「:wq」で保存後、以下のコマンドで設定を再読み込みする。
$ source ~/.bash_profile

あらためてrailsをインストール。

$ sudo gem install rails --no-rdoc # ドキュメント不要

iptablesファイヤーウォール)でRailsのポートを開けていない場合は、以下のコマンドで設定しておく。

$ sudo vi /etc/sysconfig/iptables
  -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3000  -j ACCEPT # 追加
$ sudo /etc/rc.d/init.d/iptables restart # 再起動
$ sudo sudo iptables -L                  # 設定の確認

5. サンプルアプリケーションの作成

サンプルアプリケーションを作って実行してみる。

$ mkdir ~/workspace # ホームディレクトリにworkspaceフォルダを作る
$ cd ~/workspace
$ rails new demo -d mysql # DBを指定してアプリケーションを新規作成
  ...
  run  bundle install
  Enter your password to install the bundled RubyGems to your system: 

「足りないgemをインストールするからパスワード入れてよ」と言われるので、ログインユーザーのパスワードを入力する。すると、以下のようにたくさんインストールしてくれた。

  .. 
  Installing coffee-script-source (1.1.3) 
  Installing execjs (1.2.9) 
  Installing coffee-script (2.2.0) 
  ...
  Installing coffee-rails (3.1.1) 
  Installing jquery-rails (1.0.17) 
  Installing mysql2 (0.3.10) with native extensions 
  ...
  Installing sass (3.1.10) 
  Installing sass-rails (3.1.4) 
  Installing uglifier (1.0.4) 
  Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

とりあえず、デモアプリに必要な準備をする。

$ rails generate scaffold Post name:string title:string content:text # Postという名前のモデル、ビュー、コントローラーを作成
$ rake db:create   # DBを作成
$ rake db:migrate  # テーブルを作成
$ rake routes      # URLのルーティングを確認(/postsにアクセスすればよいことがわかる)
      posts GET    /posts(.:format)          {:action=>"index", :controller=>"posts"}
            POST   /posts(.:format)          {:action=>"create", :controller=>"posts"}
   new_post GET    /posts/new(.:format)      {:action=>"new", :controller=>"posts"}
  edit_post GET    /posts/:id/edit(.:format) {:action=>"edit", :controller=>"posts"}
       post GET    /posts/:id(.:format)      {:action=>"show", :controller=>"posts"}
            PUT    /posts/:id(.:format)      {:action=>"update", :controller=>"posts"}
            DELETE /posts/:id(.:format)      {:action=>"destroy", :controller=>"posts"}

作成したアプリケーションのルートディレクトリでサーバーを起動する。

$ rails server
  /usr/local/lib/ruby/gems/1.8/gems/execjs-1.2.9/lib/execjs/runtimes.rb:47:in `autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
        from /usr/local/lib/ruby/gems/1.8/gems/execjs-1.2.9/lib/execjs.rb:5
        from /usr/local/lib/ruby/gems/1.8/gems/coffee-script-2.2.0/lib/coffee_script.rb:1:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/coffee-script-2.2.0/lib/coffee_script.rb:1
        from /usr/local/lib/ruby/gems/1.8/gems/coffee-script-2.2.0/lib/coffee-script.rb:1:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/coffee-script-2.2.0/lib/coffee-script.rb:1
        from /usr/local/lib/ruby/gems/1.8/gems/coffee-rails-3.1.1/lib/coffee-rails.rb:1:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/coffee-rails-3.1.1/lib/coffee-rails.rb:1
        from /usr/local/lib/ruby/gems/1.8/gems/bundler-1.0.21/lib/bundler/runtime.rb:68:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/bundler-1.0.21/lib/bundler/runtime.rb:68:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/bundler-1.0.21/lib/bundler/runtime.rb:66:in `each'
        from /usr/local/lib/ruby/gems/1.8/gems/bundler-1.0.21/lib/bundler/runtime.rb:66:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/bundler-1.0.21/lib/bundler/runtime.rb:55:in `each'
        from /usr/local/lib/ruby/gems/1.8/gems/bundler-1.0.21/lib/bundler/runtime.rb:55:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/bundler-1.0.21/lib/bundler.rb:122:in `require'
        from /home/montecut/workspace/demo/config/application.rb:7
        from /usr/local/lib/ruby/gems/1.8/gems/railties-3.1.1/lib/rails/commands.rb:52:in `require'
        from /usr/local/lib/ruby/gems/1.8/gems/railties-3.1.1/lib/rails/commands.rb:52
        from /usr/local/lib/ruby/gems/1.8/gems/railties-3.1.1/lib/rails/commands.rb:49:in `tap'
        from /usr/local/lib/ruby/gems/1.8/gems/railties-3.1.1/lib/rails/commands.rb:49
        from script/rails:6:in `require'
        from script/rails:6

エラーが出た。Rails 3.1 execjs and Could not find a JavaScript runtimeのページによると、JavaScriptライブラリをインストールすると解決するとのこと。Gemfileにgemを追記して、bundle installコマンドで足りないgemをインストールする。

$ vi Gemfile # アプリケーションのルート直下にあるGemfileを編集
  gem 'execjs'         # 追加
  gem 'therubyracer'   # 追加
$ bundle install       # 足りないgemを入れてくれる
$ rails server         # 再度サーバーを起動

サーバーが起動したようなので、ブラウザから確認。http://wwwXXXXXX.sakura.ne.jp:3000/postsを開いて、demoアプリケーションの画面が表示されたのでこれでOK。

SSHのポート番号を変える

SSHの認証方式を、パスワード認証を禁止して公開鍵認証にすれば、SSHのポートからクラックされるリスクはかなり減る。しかし、外部からポートスキャンをされるたびにCPU負荷がかかるとのことなので、攻撃の対象になりやすいSSHはポート番号を変更しておくのが定番のようだ。

今日は以下のことを試してみる。サーバーはさくらインターネットVPSCentOS)。

  1. デフォルトのSSHポート(22番)へのアクセス状況を調べる
  2. SSHのポート番号を変更する

1. デフォルトのSSHポート(22番)へのアクセス状況を調べる

Linuxのセキュリティ関連のログは、/var/log/secureに記録される。lessコマンドでファイルを開いてみる。ちなみに、lessコマンドを終了するには「q」をタイプする。「/キーワード」をタイプすればファイル内の文字列検索もできる。

$ sudo -i # rootになる
# less /var/log/secure
...
Nov  6 00:54:12 サーバー名 sshd[1752]: Invalid user hashimoto from 220.227.121.180
Nov  6 00:54:12 サーバー名 sshd[1752]: Address 220.227.121.180 maps to report.ksrtc.in, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!
Nov  6 00:54:12 サーバー名 sshd[1753]: input_userauth_request: invalid user hashimoto
Nov  6 00:54:12 サーバー名 sshd[1752]: pam_unix(sshd:auth): check pass; user unknown
Nov  6 00:54:12 サーバー名 sshd[1752]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=220.227.121.180 
Nov  6 00:54:12 サーバー名 sshd[1752]: pam_succeed_if(sshd:auth): error retrieving information about user hashimoto
Nov  6 00:54:13 サーバー名 sshd[1752]: Failed password for invalid user hashimoto from 220.227.121.180 port 38485 ssh2
Nov  6 00:54:13 サーバー名 sshd[1753]: Received disconnect from 220.227.121.180: 11: Bye Bye
...
Nov  6 00:55:05 サーバー名 sshd[1782]: Invalid user chikafuji from 220.227.121.180
Nov  6 00:55:05 サーバー名 sshd[1782]: Address 220.227.121.180 maps to report.ksrtc.in, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!
Nov  6 00:55:05 サーバー名 sshd[1783]: input_userauth_request: invalid user chikafuji
Nov  6 00:55:05 サーバー名 sshd[1782]: pam_unix(sshd:auth): check pass; user unknown
Nov  6 00:55:05 サーバー名 sshd[1782]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=220.227.121.180 
Nov  6 00:55:05 サーバー名 sshd[1782]: pam_succeed_if(sshd:auth): error retrieving information about user chikafuji
Nov  6 00:55:07 サーバー名 sshd[1782]: Failed password for invalid user chikafuji from 220.227.121.180 port 49114 ssh2
Nov  6 00:55:08 サーバー名 sshd[1783]: Received disconnect from 220.227.121.180: 11: Bye Bye
...

適当なユーザー名とパスワードで手当たり次第に接続を試みられているっぽい。同様の接続がサーバーを立ててから6日間で12536件も。怖すぎ…。
ログの意味を調べてみた。

# 攻撃者のIPアドレスとドメイン名の正引き、逆引き結果が違う
Address 220.227.121.180 maps to report.ksrtc.in, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!

DNSIPアドレス(220.227.121.180)からドメイン名(report.ksrtc.in)を逆引きした結果と、ドメイン名からIPアドレスを正引きした結果が違うということ。これでどうして「POSSIBLE BREAK-IN ATTEMPT!(攻撃の可能性あり!)」と言えるのか? ちょっとだけ調べてみると、メールサーバーでは逆引きした結果と正引きした結果が違うとスパムメールと判定することは割と多いみたい。逆引きと正引きの結果を変えることは攻撃者にとってどんなメリットがあるのかを調べてみたが、すぐには見つけられなかった。攻撃者が本当のドメイン名を知られないようにするためかなぁ…。

# PAMで認証エラー
Nov  6 00:54:12 サーバー名 sshd[1752]: pam_unix(sshd:auth): check pass; user unknown
Nov  6 00:54:12 サーバー名 sshd[1752]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=220.227.121.180 
Nov  6 00:54:12 サーバー名 sshd[1752]: pam_succeed_if(sshd:auth): error retrieving information about user hashimoto

PAMはとは、Pluggable Authentication Moduleの略で、UNIXで共通化されている認証モジュールのようだ。このPAMから認証エラーのメッセージが出ている。

# パスワードでSSH2接続に失敗
Nov  6 00:54:13 サーバー名 sshd[1752]: Failed password for invalid user hashimoto from 220.227.121.180 port 38485 ssh2

これが重要。ログの最後にssh2とあるので、攻撃者はSSH2をパスワード認証で接続を試みていることがわかる。

2. SSHのポート番号を変更する

JFEテクノリサーチ株式会社さんのサイトがわかりやすいので参考になる。

以下の手順になる。

  1. 現在使われていないポートを調べて、新しいポート番号を決める
  2. SSHの設定ファイルにポート番号を追加する
  3. サービスの設定ファイルのポート番号を変更する
  4. iptablesファイヤーウォール)の設定ファイルを変更する
  5. 動作確認
(1) 現在使われていないポートを調べて、新しいポート番号を決める

変更するポートは、現在使われていなくて、かつ今後も使われる可能性が少ないものにする必要がある。
現在使われていないポートを調べるには、以下のコマンドを実行する。

[user@server ~]$ netstat -antu
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      # 25番ポート(SMTP)
tcp        0      0 :::80                       :::*                        LISTEN      # 80番ポート(HTTP)
tcp        0      0 :::22                       :::*                        LISTEN      # 22番ポート(SSH)
tcp        0    380 ::ffff:49.212.106.174:22    ::ffff:180.11.197.81:52561  ESTABLISHED # 22番ポートが接続中
udp        0      0 49.212.106.174:123          0.0.0.0:*                               
udp        0      0 127.0.0.1:123               0.0.0.0:*                               
udp        0      0 0.0.0.0:123                 0.0.0.0:*                               
udp        0      0 fe80::5054:ff:fe04:136:123  :::*                                    
udp        0      0 ::1:123                     :::*                                    
udp        0      0 :::123                      :::* 

まだapacheしか入れていないので、ほとんどのポートが使われていませんね。オプションは、-aはdisplay all sockets (default: connected)、-nはdon't resolve names、-tはTCPを表示、-uはUDPを表示するという意味。

規定のポートも使わないようにする。0から1023 までの範囲は Well Known Portsとして決められているので使わない。1024から49151まではRegistered Portsと呼ばれ、様々なサービスが定義されているので使わない方が望ましい。9152から65535についてはDynamic or/and Private Portsと呼ばれていて、アプリケーションで勝手に使うためのものみたい。

(2) SSHの設定ファイルにポート番号を追加する

以下のコマンドを実行する。

$ sudo vi /etc/ssh/sshd_config
  #Port 22          # コメントアウト
  Port 任意の番号   # ポート番号を追加する

$ sudo /etc/init.d/sshd restart
(3) サービスの設定ファイルのポート番号を変更する

以下のコマンドを実行する。(僕の環境では、このファイルを編集しなくても新しいポート番号で接続できた。)

$ sudo vi /etc/services
  #ssh            22/tcp          SSH Remote Login Protocol   # コメントアウトする
  #ssh            22/udp          SSH Remote Login Protocol   # コメントアウトする
  
  ssh            ポート番号/tcp          SSH Remote Login Protocol # 追加
  ssh            ポート番号/udp          SSH Remote Login Protocol # 追加
(4) iptablesファイヤーウォール)の設定ファイルを変更する

以下のコマンドを実行する。

$ sudo vi /etc/sysconfig/iptables
  # -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22    -j ACCEPT    # コメントアウト、または次の行のように書き換え
  -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport ポート番号 -j ACCEPT # 新規追加
$ sudo /etc/rc.d/init.d/iptables restart
(5) 動作確認

新しいポートでSSH2接続でログインできることを確認する。また、古いポート(22番)でログインできないことを確認する。注意事項としては、既存の接続を残したまま新規接続を試すこと。設定が間違っている状態で接続を切ってしまうと、もう接続できなくなってしまって困るから。

さくらVPSでセキュリティ関連の設定をする(ファイヤーウォール編)

SSHの設定に引き続き、ファイヤーウォールiptables)の設定をする。

1. iptablesの設定

以下のサイトの説明がわかりやすいので、その通りにした。設定ファイルの場所は、/etc/sysconfig/iptables

さくらのVPS を使いはじめる 3‐iptables を設定する

流れとしては、以下のようになる。

  1. VPSiptablesがインストールされていることを確認
  2. 現在のiptablesの設定を確認
  3. ポートの設定
  4. iptablesの再起動と動作確認

SSH, HTTP, FTP1, FTP2, MySQLのパケットを許可(ACCEPT)した。その後、iptablesがOS起動時に自動起動する設定になっているかを確認するために、以下のコマンドを実行する。

$ chkconfig --list iptables
iptables        0:off   1:off   2:on    3:on    4:on    5:on    6:off # ランレベル2〜5のonを確認

もし上記のようになっていなければ、以下のコマンドで設定する。

$ sudo chkconfig iptables on # OS起動時に自動で起動するように設定

2. iptablesが効いているか確認

(1) ポートをスキャンするためのツールnmapをインストールする

さくらVPSのOSはCentOSなので、yumコマンドでインストールする。

$ yum list installed | grep nmamp # インストール済みでないことを確認(このコマンドで何も表示されない)
# sudo -i
# yum -y install nmap # yum の -y オプションは「すべてYESで答える」
(2) ポートをスキャンしてみる

以下のコマンドを実行すると、SSHSMTPのポートだけが開いていることがわかる。「え? さっきiptablesの設定でACCEPTした他のポートはどうして開いていないの?」と思ったら、以前の記事をご覧いただきたい。ポートは、そのポートを使うアプリケーションが起動されていないと開かない。

# nmap localhost # ポートスキャンを実行
Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2011-11-07 00:22 JST
Interesting ports on localhost.localdomain (127.0.0.1):
Not shown: 1678 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
25/tcp open  smtp

Nmap finished: 1 IP address (1 host up) scanned in 0.061 seconds

ただ、この方法でポートをスキャンするだけだったら、iptablesの設定が効いているかどうかは判別できない。

(3) ブラウザでアクセスしてみる

「nmap localhost」のコマンドでは、iptablesが機能しているかどうかがわからないので、ブラウザを使ってパケットが届くかどうか試してみる。

まず、Apache*1を起動した状態で、ブラウザでVPSのURL(http://wwwXXXXXX.sakura.ne.jp)にアクセスする。

Apacheのページが表示される。

nmapコマンドを実行すると、80番ポート(HTTP)が開いていることがわかる。

# nmap localhost # ポートスキャンを実行

Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2011-11-10 01:34 JST
Interesting ports on localhost.localdomain (127.0.0.1):
Not shown: 1677 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
25/tcp open  smtp
80/tcp open  http

Nmap finished: 1 IP address (1 host up) scanned in 0.140 seconds

次に、iptablesの設定ファイルを編集して、80番ポートへのパケットを拒否するようにしてみる。

$ sudo vi /etc/sysconfig/iptables # 設定ファイルを編集する
...
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22    -j ACCEPT
# -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80    -j ACCEPT # ポート80をACCEPTしない
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3306  -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited # ACCEPTしていないパケットをすべて拒否

$ sudo /etc/rc.d/init.d/iptables restart # iptablesを再起動する

この状態では、ブラウザでVPSのURLにアクセスしても、Apacheのページが表示されない。iptablesにより、ポート80番のパケットが破棄されているからである。

ポートは開いているが、このポートに入ってくるパケットはちゃんとブロックされている。

# nmap localhost # ポートスキャンを実行

Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2011-11-10 01:34 JST
Interesting ports on localhost.localdomain (127.0.0.1):
Not shown: 1677 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
25/tcp open  smtp
80/tcp open  http

Nmap finished: 1 IP address (1 host up) scanned in 0.140 seconds

*1:Apacheのインストール方法は、「さくらVPS設定その2 Apache+MySQL+WordPress」のページを参考にさせていただいた。

さくらVPSでセキュリティ関連の設定をする(SSH編)

さくらインターネットVPSSSHの設定をしたときのメモ。サーバーのOSはCentOS、クライアントはWindows

VPSの契約が完了すると「[さくらのVPS] 仮登録完了のお知らせ」というメールが届く。そこに書かれているIPアドレス、初期パスワードを使う。

SSH関連で設定すること

  1. WEBのVPSコントロールパネルからrootのパスワードの変更と、ログインユーザーを作成する
  2. RSA公開鍵のファイルを作成してサーバーに配置する
  3. RSA公開鍵認証を有効にする設定、rootでのログインを禁止する設定をする

1. WEBのVPSコントロールパネルからrootのパスワードの変更と、ログインユーザーを作成する

(1) 「VPSコントロールパネル」にログインしVPSを起動する

仮登録完了のメールに書いてある通り、初期状態ではVPSは停止状態になっているので、WEBページのUIからVPSを起動する。以下のURLにアクセスして、メールに書いてあるIPアドレスとパスワードを入力。
https://secure.sakura.ad.jp/vpscontrol/

「仮想サーバ操作」で、[起動]ボタンを押す。ステータスが「起動中」になったことを確認。

(2) rootのパスワードを変更する

SSHクライアントにIPアドレス(メールに書いてある)、アカウント(root)、パスワード(メールに書いてある)を入力してSSH2でログインする。そして、passwdコマンドでrootのパスワードを変更する。

[root@server ~]# passwd
Changing password for user root.
New UNIX password: 
Retype new UNIX password: 
passwd: all authentication tokens updated successfully.
(3) 通常使うユーザーを作成する

adduserコマンドでユーザーを作成する。

# -D オプションで、デフォルトでどんなユーザーが作られるか確認
[root@server ~]# adduser -D
GROUP=100      # 100はusersグループを意味する。「cat /etc/group」コマンドで確認できる。
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
# スーパーユーザー(root)特権を得ることのできるグループを設定
[root@server ~]# visudo
...
# この行のコメントアウトを外して保存
%wheel        ALL=(ALL)       ALL
...
# ユーザーの追加とグループ、パスワードの設定
[root@server ~]# adduser ユーザー名
[root@server ~]# usermod -G wheel ユーザー名 # wheelグループへ追加
[root@server ~]# id ユーザー名               # グループの確認
uid=500(ユーザー名) gid=500(ユーザー名) groups=500(ユーザー名),10(wheel)
[root@server ~]# passwd ユーザー名
Changing password for user ユーザー名.
New UNIX password: 
Retype new UNIX password: 
passwd: all authentication tokens updated successfully.

これで、いったんexitコマンドでサーバーとの接続を切って、新しく作成したユーザーでログインできれば成功。

2. RSA公開鍵のファイルを作成してサーバーに配置する

パスワード認証だと外部からの攻撃でクラックされる可能性があるため、安全性の高いRSA公開鍵認証を使えるようにする。

(1) クライアント(ローカル環境)でRSA鍵を作成する

SSHクライアント(自分の場合はPoderosa)のメニューから、SSH鍵を作成する。アルゴリズムRSA、ビット数は2048、適当なパスワードを指定して実行。「秘密鍵を名前をつけて保存」でsitename.ppk、「OpenSSH形式で公開鍵を名前をつけて保存」でsitename-openssh.pubというファイル名で保存。

(2) サーバーに公開鍵のファイルを配置する

サーバーで以下のコマンドを実行する。

$ mkdir ~/.ssh                    # 鍵を配置する.sshディレクトリを作成
$ chmod 700 ~/.ssh                # 読み書き実行権限を付ける
$ cd ~/.ssh
$ touch authorized_keys           # 公開鍵のファイルを作成
$ chmod 600 ~/.ssh/authorized_keys# 読み書き権限を付ける

そして、authorized_keysファイルを開き、(1)で作成したsitename-openssh.pubの内容をコピペして保存する

$ vi authorized_keys # 
  ssh-rsa XXXXXXXX... # (1)で作成したsitename-openssh.pubの内容をコピペして保存する

3. RSA公開鍵認証を有効にする設定、rootでのログインを禁止する設定をする

(1) sshd_configを編集する
$ su -                            # rootになる
# vi /etc/ssh/sshd_config         # sshの設定を編集
  PermitRootLogin no              # rootでのログインを禁止
  
  RSAAuthentication yes           # RSA認証を許可
  PubkeyAuthentication yes        # 公開鍵認証を許可
  AuthorizedKeysFile      .ssh/authorized_keys # 認証鍵ファイルを指定
  
  PermitEmptyPasswords no         # パスワードなしのログインを禁止
  PasswordAuthentication no       # パスワード認証を禁止
  
# /etc/rc.d/init.d/sshd restart   # SSHサービスを再起動して設定を反映
(2) SSHクライアントからログインしてみる

SSHクライアントの新規接続で、認証方法を「公開鍵」に、鍵ファイルとして秘密鍵ファイルのパス(sitename.ppk)を指定、RSAの鍵を作成したときのパスワードを入力してログインできれば成功。root+パスワードでログインできないことも確かめる。

設定にミスがあると公開鍵認証もパスワード認証もできなくなる可能性があるため、動作確認をするときは、現在の接続を残したままSSHクライアントで新しい接続を作成して試すとよい。

Linuxでファイヤーウォール(iptables)が必要な理由

ネットワーク初心者の僕が、iptablesやポートの開放について勘違いしていたことをまとめる。

ネットワークやLinux関連の本やサイトでは必ず、「サーバーを立てるときはiptablesファイヤーウォール)を設定しましょう」書いている。そして、iptablesの設定で必要ないポートは全部閉じることが推奨されている。

ここで、まずひとつ目の勘違い。

Q: iptablesを設定しなければ、すべてのポートが開放されている状態になり、どのポートも攻撃の対象になってしまう?

A: いいえ。iptablesが起動していなくても、すべてのポートが開放されている状態にはならない。ポートは、アプリケーションから利用されて初めて開かれた状態になる。なので、極端な話、外部と通信するアプリケーションをひとつも起動していない状態では、ポートはすべて閉じていることになり、外部からの攻撃で問題が出ることはない。

ポートが開いているかどうかは、nmapというツール*1で確かめることができる。以下の例のサーバーは、さくらインターネットVPS。OSはCentOS。初期状態なので、WEBサーバー(Apache)すらも起動していない。このとき、開かれているポートはSSHSMTPの2つのみ。

[user@server ~]$ sudo /etc/rc.d/init.d/iptables stop # iptabelsのサービスを停止(後でstartで戻しておくこと。)
...
[user@server ~]$ nmap localhost # 自分自身(localhost)のポートの状態を調べる
Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2011-11-08 20:42 JST
Interesting ports on localhost.localdomain (127.0.0.1):
Not shown: 1678 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
25/tcp open  smtp

Nmap finished: 1 IP address (1 host up) scanned in 0.138 seconds

ここでApache*2を起動すると、80のポートも開かれている状態になる。

[user@server ~]$ sudo apachectl start
[user@server ~]$ nmap localhost
Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2011-11-08 21:31 JST
Interesting ports on localhost.localdomain (127.0.0.1):
Not shown: 1677 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
25/tcp open  smtp
80/tcp open  http

Nmap finished: 1 IP address (1 host up) scanned in 0.149 seconds

次に、ふたつ目の勘違い。

Q: アプリケーションを起動するまでポートが開かれないのなら、必要なアプリケーションだけ起動するように気をつければよく、わざわざiptablesでブロックする必要はないのでは?

A: いいえ。セキュリティは二重、三重にかけておいた方がよい。iptablesを使うメリットは以下の通り。

  1. 管理者が知らないうちにポートが使われることを防ぐ
  2. 細かくパケットをフィルタリングする
  3. ネットワークアドレスを変換する

1.は、アプリケーションはiptablesで許可されていないポートを使うことができないということ。サーバーのログインユーザーが、外部と通信するアプリケーションを勝手にインストールするなどして、管理者が知らないうちにポートが使われてしまうことを防げる。
2.は、たとえば、特定のIPアドレスから入ってきたパケットを破棄することができるということ。
3.は、特定のパケットのIPアドレスやポート番号を書き換えることができるということ。

そして、3つ目の勘違い。

Q: iptablesを使えばポートを閉じることができるよね?

A: いいえ。iptablesはポートそのものを閉じるツールではない。ポートは開いた状態のままで、ポートに入ってくるパケットを破棄することはできる。

まとめると、iptablesのメリットは、アプリケーションに到達する前のカーネルのレベル*3でパケットをフィルタリングできることである。

参考URL:
iptables - Wikipedia
パケットフィルタリングとポートの開閉

*1:nmapインストール方法は、「nmapの運用」のページを参考にさせていただいた。

*2:Apacheのインストール方法は、「さくらVPS設定その2 Apache+MySQL+WordPress」のページを参考にさせていただいた。

*3:Linuxカーネルとは - IT Pro

朝早く家を出ることができるアラームアプリ、MorningBombのバージョン1.0.3をリリースしました!

朝早く家を出ることができるアラームアプリ、MorningBomb(バージョン1.0.3)
Androidマーケット MorningBomb
MorningBombの公式ページ

改善点:

  1. 無意識にスヌーズ解除ボタン押して二度寝してしまうことを防ぐために、スヌーズ解除時には本当に解除してよいか確認するメッセージを表示するようにしました。
  2. 電池節約モードを作りました。
  3. MorningBombって何? という人のために、反省文の最後に説明ページへのURLを付けるようにしました。(Twitter投稿時のみ)

ちょっと地味ですが、実用的な機能アップだと思いますので、ぜひ使ってみてください!