lighttpdを用いたwebサーバの構築

いちごパック > サーバ > lighttpdを用いたwebサーバの構築

webサーバについて

webサーバとは、webブラウザ(Internet Explorer、Google Chrome、Mozilla Firefox、Safariなど)に対して、 htmlや画像などをHTTP/HTTPSプロトコルで送るサーバソフトウェアです。 標準でHTTPはTCPの80番ポート、HTTPSはTCPの443番ポートを利用します。
Linuxでは、例えば次のwebサーバがよく使われます。
  • Apache
  • nginx
  • lighttpd
  • このページでは、lighttpdを用いたwebサーバの構築方法について説明します。

    webサーバが受ける攻撃

    webサーバは全世界からアクセスできます。 残念ながら、アクセスのなかにはwebサーバの乗っ取り、機密情報の取得などを目的としたものもあります。 無防備なwebサーバを構築するとすぐに乗っ取られてしまいますので、予め受ける攻撃を知っておくと良いでしょう。 webサーバを狙った攻撃の大半は、次のいずれかになります。
  • webサーバの不具合を利用する
  • 動作するスクリプトやプログラムの不具合を利用する
  • 大量のリクエストを送り付けてwebサーバをダウンさせる
  • サーバ攻撃への対策

    webサーバを構築する際は、全世界からくる攻撃に備える必要があります。 例えば、次の対策をしておくと良いでしょう。
  • アクセスログをとり、ログを時々チェックする。攻撃の兆候をつかめます。
  • 最小限の機能のみを有効にする。不具合を利用した被害を受けにくくなります。
  • webサーバのLinux上での権限に制限を加える。不具合を利用されたときの被害を軽減できます。
  • lighttpdを常に最新版へ更新する。ソフトウェア自動更新でも良い。不具合を利用した被害を受けにくくなります。
  • アクセスログに攻撃された記録が残っているなら、アクセス元のIPやサブネットをファイアウォールで阻止する。不具合を利用した被害を受けにくくなります。
  • サーバ維持に必要ない外部への接続をファイアウォール(iptablesなど)で阻止する。乗っ取られた場合の被害を軽減できます。
  • 後述のchrootをしておく。乗っ取られた場合の被害を軽減できます。
  • 時間あたりの接続数やデータ転送速度、同時接続数を制限する。大量の接続リクエストでwebサーバがダウンしにくくなります。
  • lighttpdの入手

    多くのLinuxでlighttpdはパッケージ化されており、簡単にインストールできます。 例えばDebianやubuntuでは次のようにインストールします。
    % sudo apt-get install lighttpd
    

    lighttpdの設定方法

    lighttpdは基本機能とモジュール機能で構成されています。 コンフィグで必要なモジュール機能を指定しておくと、 それを取り込んだ状態でwebサーバが起動されます。

    コンフィグの書き方

    コンフィグファイルの場所は、例えばDebianでは /etc/lighttpd/lighttpd.conf に置かれています。 コンフィグファイルの記述方法は 公式マニュアルに書かれています。 ここでは、その書き方の一部をご説明します。 なお、# 以下はコメントとして扱われます。
    コンフィグには、以下の行と条件分岐を書くことができます。 例えばDebianではinclude_shellを使って、コンフィグを複数ファイルに分割しています。
    変数名 = 値指定された変数に値をセット
    変数名 += 値指定された変数に値を追加
    include <ファイル名>ファイルをコンフィグファイルとして読み込む
    include_shell <コマンドライン>指定プログラムを実行し、その結果をコンフィグファイルとして読み込む

    コンフィグにあらわれる「値」とは、以下のいずれかを指します。
    整数1や80などの数値
    文字列ダブルクウォート("")で括ったテキスト
    bool値"enable"または"disable"
    配列(値, 値, ...)または("文字列" => 値, "文字列" => 値, ...)

    条件分岐は次のように書きます。必要であればelseも書けます。 また、条件分岐の中で条件分岐を書くこともできます。
    <フィールド> <比較の種類> <比較値> {
      条件が満たされたときの内容
    }
    else <フィールド> <比較の種類> <比較値> {
      else後の条件が満たされたときの内容
    }
    

    モジュールの選択

    以下にモジュールの一部を抜粋します。モジュールの完全なリストは公式ページにあります。 モジュールは server.modules で文字列の配列として指定します。
    server.modules = ( "mod_access", "mod_accesslog" )
    
    mod_accesslogはモジュールですが、インターネット上でwebサーバを公開するのであれば必須と考えて良いでしょう。 それ以外のモジュールについては、使うモジュールに絞って追加します。
    モジュール名内容
    mod_accesslog必須。アクセスログを accesslog.filename で指定されたファイルに記録する。ログあふれはcronで古いログを保存・削除すれば防げる。
    mod_accessファイル名が特定のパターンを持つ場合にアクセスを禁止する。
    mod_aliasディレクトリ単位でdocument-rootの位置を変える。
    mod_compressデータを圧縮して送る。
    mod_redirectアクセスURLで判断し、このURLが別のURLに移動されたことを通知する。
    mod_rewriteアクセスURLで判断し、別のURLへのアクセスに内部で書きかえて処理する。
    mod_setenvHTTPヘッダや環境変数を操作する。
    mod_cgiCGIプログラムを実行できるようにする。CGIは1回のプログラム実行で1つのリクエストを処理できる。
    mod_fastcgiFastCGIを実行できるようにする。FastCGIは1回のプログラム実行で複数のリクエストを処理できるため効率が良い。
    mod_dirlistingディレクトリ一覧表示を使えるようにする。ファイルダウンロードサイト等でないなら、使わないほうが良い。
    mod_magnetURLアクセス時にLua言語で書かれたスクリプトを実行する。スクリプトは毎回実行されるため、重いスクリプトは避けたほうが良い。

    変数

    変数には lighttpd のコアに対する指示と、モジュールに対する指示があります。

    条件分岐

    条件分岐で使えるフィールド名です。
    フィールド名内容
    $HTTP[host]接続ホスト。
    $HTTP[remoteip]接続IPv4アドレス。
    $HTTP[cookie]クッキー。
    $HTTP[useragent]ユーザエージェント(webブラウザの種類)。
    $HTTP[referer]リファラ(参照元)。
    $HTTP[url]URL。このフィールドを使った条件分岐は、内部に条件分岐を置けないという制約がある。
    $HTTP[querystring]URLの?以下の部分。
    $HTTP[scheme]httpもしくはhttps。
    $HTTP[language]受け入れ可能な言語。
    $SERVER[socket]「IP:port」形式でのwebサーバのアドレス。IPv4、IPv6の両方に対応。
    条件分岐で使える比較は次の通りです。
    比較の種類意味
    ==文字列の一致する場合に真。
    !=文字列が一致しない場合に真。
    =~正規表現(perlスタイル)で一致する場合に真。
    !~正規表現(perlスタイル)で一致しない場合に真。

    lighttpdコアの変数

    これらの変数はlighttpdへのパラメータです。モジュールなしでも効果を持ちます。 SSLの暗号化を破る方法は数多く指摘されています。 暗号化が必要な場合は、少なくともSSLv2/v3を無効化したうえで、 その時点で推奨される暗号化アルゴリズムを調べてご設定ください。
    変数名内容値の例
    server.modules読み込むモジュール("mod_access","mod_accesslog")
    connection.kbytes-per-second1接続あたりの転送速度の上限128
    server.kbytes-per-second全接続の合計転送速度の上限1024
    mimetype.assignMIMEタイプの割り当て。上から順にチェックされる。(".png" => "image/png",".jpg" => "image/jpeg")
    server.chrootchrootでルートディレクトリとするパス。この設定にはroot権限が必要。"/wwwroot"
    server.document-rootURLのルートに対応するディレクトリ。"/wwwroot/html"
    server.errorlogエラーを記録するファイルのファイル名。"/var/log/lighttpd/error.log"
    server.errorlog-use-syslogエラーをsyslogに送るかを指定。enable
    server.usernamelighttpdを動かすプロセスのユーザ。この設定にはroot権限が必要。"www-data"
    server.groupnamelighttpdを動かすプロセスのグループ。この設定にはroot権限が必要。"www-data"
    server.breakagelogCGIプログラム等の標準エラー出力を保存するファイルのファイル名。"/var/log/lighttpd/log_stderr.log"
    server.tagwebブラウザに返すサーバ名。動作するソフトウェアの種類を隠せる。"web server"
    ssl.engineSSLを有効にするか指定する。"enable"
    ssl.pemfileSSLのPEMファイルを指定する。PEMのパーミッションは通常400とする。"/etc/lighttpd/ssl_host.pem"
    ssl.ca-fileSSLのCAファイルを指定する。"/etc/lighttpd/ssl_ca.crt"
    ssl.use-sslv2セキュリティ問題がある SSLv2 を有効にするかを指定。"disable"
    ssl.use-sslv3セキュリティ問題がある SSLv3 を有効にするかを指定。"disable"
    ssl.cipher-listSSLで利用する暗号化アルゴリズム。その時点で最新の情報を調べてご設定ください。

    モジュール向けの変数

    これらの変数はモジュールのパラメータです。
    モジュール変数名内容値の例
    mod_accessurl.access-denyファイル名の最後がこのパターンと一致する場合にアクセスを拒否する。(".com",".exe",".pif",".inc")
    mod_accessaccess.deny-allすべてのファイルへのアクセスを拒否する。"enable"
    mod_accesslogaccesslog.filenameアクセスログを記録するファイル名を指定する。"/var/log/lighttpd/access.log"
    mod_accesslogaccesslog.use-syslogアクセスログをsyslogに送る。"enable"
    mod_accesslogaccesslog.syslog-levelsyslogに送るメッセージのレベルを0(緊急のみ)〜7(全部)で指定する。6
    mod_accesslogaccesslog.formatアクセスログの出力フォーマットを指定する。"%t %h %V \"%r\" \"%{Referer}i\" \"%{User-Agent}i\"
    mod_compresscompress.allowed-encodings利用する圧縮タイプを指定する。("gzip","deflate")
    mod_compresscompress.cache-dir圧縮されたファイルの保存先を指定する。"/var/cache/lighttpd/"
    mod_compresscompress.filetypeファイル圧縮の対象とするMIMEタイプを指定する。("text/css", "text/html")
    mod_compresscompress.max-filesize指定サイズ(KByte単位)以上のファイルを圧縮しない。4096
    mod_redirecturl.redirectページが移動されたものとして扱う。移動元URLと移動先URLを与える。("/pages/([0-9]+)$" => "http://www.example.com/showpage.pl?p=$1")
    mod_setenvsetenv.add-response-header指定されたHTTPヘッダを追加する。("Content-Encoding" => "gzip")
    mod_cgicgi.execute-x-onlychmod +xされていないプログラムを実行して良いか選択する。"enable"
    mod_cgicgi.assignファイル名ごとに、CGIとして実行するプログラムを指定する。(".pl" => "/usr/bin/perl", ".lua" => "/usr/bin/lua")
    mod_fastcgifastcgi.serverFastCGIのパラメータを指定する。後で詳しくします。
    変数名の完全なリストは公式ページにあります。

    chrootの使い方

    chrootとは、ルートディレクトリを指定ディレクトリに変更する機能です。 一度chrootすると指定ディレクトリより親のディレクトリには、 シンボリックリンク等を使ってもアクセスできなくなります。 chrootすることで、予期しないファイルアクセスの可能性を大幅に減らせます。
    webサーバは常に攻撃にさらされています。 乗っ取られた場合の被害を軽減する最後の砦として、chrootはしておいたほうが良いでしょう。

    ディレクトリやデバイスファイルの作成

    chrootを実行すると、本来のルートディレクトリにはアクセスできなくなります。 例えば次のようにすると、chroot後のディレクトリ(この例では/wwwroot)に必要なディレクトリを作成できます。 lighttpdがchroot後に期待通り動作しない場合は、これらのディレクトリを準備すると動くかもしれません。
    % sudo mkdir /wwwroot/bin
    % sudo mkdir /wwwroot/lib
    % sudo mkdir /wwwroot/etc
    % sudo mkdir -m 1777 /wwwroot/tmp
    
    また、/dev/null等のデバイスファイルを作りたいこともあるかとおもいます。 その場合は、まずlsでデバイスファイルのパラメータを調べます。
    % ls -al /dev/null
    crw-rw-rw- 1 root root 1, 3 *** ** **:** /dev/null
    
    この出力は、/dev/nullがc(キャラクタデバイス)、メジャー番号1、マイナー番号3のデバイスであることをあらわしています。 デバイスファイルはデバイスに接続するための接続ポイントですので、 同一のパラメータを指定すれば別の場所にも作ることができます。 例えば、/wwwrootに/dev/nullを作るには次のようにします。
    % sudo mkdir /wwwroot/dev
    % sudo mknod -m 0666 /wwwroot/dev/null c 1 3
    

    CGIを使いたい場合

    CGIを使いたい場合は、さらにchroot後のルートディレクトリに必要な環境を構築する必要があります。
  • 実行ファイルやスクリプト(bin以下にあるもの)を必要最低限のみコピーする。できればリネームして使う。
  • lddに実行ファイルを指定して依存ライブラリを表示し、それらのライブラリをコピーする。
  • コピーの過程をスクリプトとして保存しておき、ソフトウェアの更新時、あるいは定期的に、対象ファイルを上書きコピーする。
  • 具体的には、例えば、次のようにします。
    % cd /wwwroot
    % mkdir bin
    % mkdir lib
    % mkdir etc
    % mkdir -m 1777 tmp
    
    % ldd /usr/bin/perl (perlが依存するファイルの一覧;linux-gate.so.1のみ無視してかまいません) % ldd <perlが依存するファイル;複数指定してもかまいません> (perlが依存するファイルの一覧;linux-gate.so.1のみ無視してかまいません) % cat > web_chroot_setup.sh #! /bin/sh cp /usr/bin/perl /wwwroot/bin/ cp <perlが依存するファイル> /wwwroot/lib/ cd /wwwroot cp /lib/ld-linux.so.2 /wwwroot/lib/ cp /usr/lib/locale/locale-archive /wwwroot/usr/lib/locale/ (Ctrl+Dを入力) % sudo sh web_chroot_setup.sh
    chroot下でperlが正しく動いているか確認したい場合は、例えば次のコマンドを実行すると良いでしょう。
    % sudo chroot --userspec=www-data:www-data /var/www /bin/perl -e "print \"test\n\""
    

    コンフィグの1例

    以下はchrootを使い、CGIは使わないコンフィグの一例です。
    server.chroot = "/wwwroot"
    server.modules = ("mod_access","mod_accesslog")
    server.document-root = "/html"
    accesslog.filename = "/log/access.log"
    server.errorlog = "/log/error.log"
    server.pid-file = "/lighttpd.pid"
    server.username = "www-data"
    server.groupname = "www-data"
    server.port = 80
    index-file.names = ( "index.html" )
    url.access-deny = ( "~", ".inc", ".old" )
    
    include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/create-mime.assign.pl" include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
    $HTTP["referer"] !~ "^http://example\.com" { url.access-deny = ( ".jpg", ".jpeg", ".png" ) }

    コンフィグの説明

    server.chroot = "/wwwroot"
    
    chrootの実行を指示する。
    server.modules = ("mod_access","mod_accesslog")
    
    利用するモジュールを指定する。
    server.document-root = "/html"
    
    webとして公開するディレクトリのルートをchroot後のパスで指定する。
    accesslog.filename = "/log/access.log"
    server.errorlog = "/log/error.log"
    
    ログファイル名をchroot後のパスで指定する。
    server.pid-file = "/lighttpd.pid"
    
    プロセスIDを保存するファイルをchroot後のパスで指定する。
    server.username = "www-data"
    server.groupname = "www-data"
    
    lighttp動作中のLinuxプロセスのユーザ名とグループ名を指定する。
    server.port = 80
    
    webサーバのTCPポート番号を指定する。
    index-file.names = ( "index.html" )
    
    ディレクトリにアクセスされたときに返すファイルのファイル名を指定する。
    url.access-deny = ( "~", ".inc", ".old" )
    
    バックアップファイルへのアクセスを阻止する。
    include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
    include_shell "/usr/share/lighttpd/create-mime.assign.pl"
    include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
    
    Debianにデフォルトで入っているスクリプトを実行させる。
    $HTTP["referer"] !~ "^http://example\.com" {
      url.access-deny = ( ".jpg", ".jpeg", ".png" )
    }
    
    外部からの画像ファイル(JPEG,PNG)への直接アクセスを禁止する。

    virtualホストの例

    アクセスURLによってルートディレクトリを変更する(virtual host)には、次のようにします。
    $HTTP["host"] == "sun.example.com" {
      server.document-root        = "/html_sun"
    }
    $HTTP["host"] == "moon.example.com" {
      server.document-root        = "/html_moon"
    }
    

    URLリダイレクトの例

    mod_redirectを使うとリダイレクトできます。
    server.modules += ("mod_redirect")
    $HTTP["host"] == "www.example.com" {
      url.redirect = (".*" => "http://example.com$0")
    }
    

    ファイル圧縮の例

    mod_compressを使うと、ブラウザが対応していればファイルを圧縮して送るようになります。 一度圧縮されたファイルはキャッシュされますが、自動的には削除されません。 cron等で削除するプログラムを定期的に動かせば、ファイルは削除できます。
    server.modules += ("mod_compress")
    compress.cache-dir = "/wwwcache/"
    compress.filetype = ("text/html", "text/css", "text/plain")
    

    FastCGIの使い方

    mod_fastcgiを使うと、FastCGIに対応したプログラムを利用できます。 利用したい場合は、まずモジュールを追加します。
    server.modules += ("mod_fastcgi")
    
    また、fastcgi.serverには、ファイルの拡張子ごとに、 ログ用の名前とパラメータを、後述の形式で「キー=>値」の配列として与えます。 パラメータは次の通りです。
    キー必須か
    socket(注1)Unix socket通信で使うパス
    host(注1)TCP通信の接続先IPアドレス(数字のアドレス)
    port(注1)TCP通信の接続先ポート番号
    bin-pathNo起動すべきFastCGIのプログラム
    bin-environmentNoFastCGIを起動するための環境変数
    modeNoresponderかauthorizerを指定
    docroot(注2)リクエストのルートディレクトリを指定
    check-localNodisable指定されない限り、URLで指定されたファイルがない場合にFastCGIを呼ばずエラーにする
    (注1) socketと、hostとportの組のうちいずれかが必須。
    (注2) modeがauthorizerの場合は必須。
    TCP socketを使う場合は、例えば次のように与えます。 最初の".php"と".fcgi"はURLの拡張子、2番目の"php"と"fcgi"はログ等で使う名前をあらわします。 bin-pathを指定しない場合は、FastCGIプログラムをlighttpdよりも前に起動しておきます。
    fastcgi.server = (
      ".php" =>
      ("php" =>
       ( "host" => "127.0.0.1", 
         "port" => 12345,
          "bin-path" => "/usr/bin/php"
      )),
      ".fcgi" =>
      ("fcgi" =>
       ( "socket" => "/run/lighttpd/fcgi.socket", 
          "bin-path" => "/wwwroot/fastcgi/my_fcgi" 
      ))
    )
    

    コンフィグの事前チェック

    ファイルが文法として正しいかは、サーバを起動しなくても事前にチェックできます。
    % /usr/sbin/lighttpd -t -f /etc/lighttpd/lighttpd.conf
    

    起動・再起動

    lighttpdは次のコマンドで起動できます。 すでに起動されていれば、再起動されます。
    % sudo systemctl restart lighttpd