address Logo

Apache

目次

2009/01/11

はじめに

僕はこれまでに Mac OSX の Apache を何度もインストールし直している。もっと正確に言えば、僕が愛用しているサーバは Plan 9 で動いており、僕はそのサーバの上で、僕が設計した Web サーバである Pegasus (http://plan9.aichi-u.ac.jp/pegasus/) を動かしている。僕は Pegasus の開発のために時々 Apache を動かして実験する必要に迫られる。従って実験が終われば Apache は用済みになって、そしてまた忘れた頃になって Apache を動かしに行く。今回もそうだ。

今回は Pegasus の WebDAV スクリプトを Perl から Lua に書き換えた。書き換えるだけではなく、ついでに全体のロジックを見直そうと思った。そこで僕は Apache の WebDAV はどのように行っているかを確認しようとしたのだ。これまでは、また初めから僕の記憶を頼りに Apache の再設定を行って来たが、そろそろ僕も記憶力が悪くなっており、この際、次の事を考えて記録を採りましょうと、このメモを書き出した。

Apache の基礎

基本的な事

Apache は Mac Mini (以下 mmac と言う)にインストールされている。OSX のバージョンは Leopard(10.5) である。過去 G4 Power Mac (以下 pmac) に Apache をインストールした記憶はあるが、 mmac にインストールした事があるか否かの記憶はない。

OSX には標準的に Apache がインストールされており、最低限の機能であれば「システム環境設定」の「共有」を開いて「Web 共有」にチェックマークを付けるだけで Web のサービスが開始される。動作の確認は mmac からブラウザで http://localhost/ にアクセスし「あなたの予想に反して、このページが見えているでしょうか?」のページが見えていれば OK。

当然の事であるが、他のコンピュータからも mmac の名前でアクセスできるようにするためには、それらのコンピュータに mmac を登録する必要がある。

さて「Web 共有」にチェックマークを付けると httpd が動き出す。この事はターミナルから

-bash$ ps -ax|grep httpd
 2974 ??         0:00.42 /usr/sbin/httpd -D FOREGROUND
 2978 ??         0:00.01 /usr/sbin/httpd -D FOREGROUND
 3002 ??         0:00.00 /usr/sbin/httpd -D FOREGROUND
 3032 ttys000    0:00.00 grep httpd
-bash$ 

で確認できる。

使用中の httpd のバージョンは

-bash$ /usr/sbin/httpd -v
Server version: Apache/2.2.8 (Unix)
Server built:   Mar  4 2008 21:37:02

つまりこの場合「Apache/2.2.8」である。

ところでどうして httpd が3つも動いているの? (この問題は後回し* )

注*: 「プロセスのオーナー」を見よ

Manual

さてアクセスに成功すると「あなたの予想に反して、このページが見えているでしょうか?」のページが現れるが、このページには Apache のマニュアルへのリンクがある。ホスト名が設定されていないなら

    http://localhost/manual/

であるが、僕の場合には

    http://mmac/manual/

である。

設定 httpd.conf

僕の mmac を見ると Apache の基本設定ファイルである httpd.conf が

    /private/etc/httpd/httpd.conf

    /private/etc/apatch2/httpd.conf

の2カ所にある。いやこの事に気付いたのは後のことで、僕は httpd/httpd.conf の方を変更していて、「なんで変更が反映されないの?」と悩んでいたのである。どうやら

    /private/etc/apatch2/httpd.conf

の方が使われているのだ。

この内容の主なものは

ServerRoot "/usr"
Listen 80
ServerAdmin you@example.com
#ServerName www.example.com:80
DocumentRoot "/Library/WebServer/Documents"
ErrorLog /private/var/log/apache2/error_log
CustomLog /private/var/log/apache2/access_log common

ここで "ServerRoot" の値 "/usr" は httpd.conf の中に現れるファイルの相対パスで指定されているパス名に作用し、"ServerRoot" で指定された値(このケースでは "/usr" )を前に付けて解釈する。実際にはこれが活用されているのは LoadModule であり、例えば

LoadModule authn_file_module libexec/apache2/mod_authn_file.so

とあるのは、このモジュールの場所が

    /usr/libexec/apache2/mod_authn_file.so

であることを意味する。実際にこの場所にモジュールが存在するのが確認できる。

先に

    http://localhost/

で動作確認できると言ったが、その時に現れたメッセージ「あなたの予想に反して、このページが見えているでしょうか?」は確かに "DocumentRoot" で指定された "/Library/WebServer/Documents" の中の "index.html.ja.iso2022-jp" の内容そのものである。

実験レベルでは httpd.conf の標準設定を変える必要はあまりない。実際の運用では

    ServerAdmin you@example.com
    #ServerName www.example.com:80

は現実に即して変更する必要がある。

設定変更後のリスタート

Apache のリスタートは apachectl で行う。

    /usr/sbin/apachectl restart		

apachectl に関する詳しい解説は

に載っている。

OSX の過剰な保護

OSX を使っていると、Mac の平均的なユーザを意識した、システムファイルに対する過剰なまでの保護が鼻につく。
例えば /private/etc/apatch2/httpd.conf を OSX 付属のテキストエディタで編集できない。UNIX のパーミッションの設定で可能なはずなのにである。

この問題はもっと根深いらしい。次の結果がそれをよく表している。

-bash$ cp httpd.conf /tmp
-bash$ emacs /tmp/httpd.conf
-bash$ cp  /tmp/httpd.conf .
cp: ./httpd.conf: Permission denied
-bash$ pwd
/private/etc/apache2
-bash$ ls -l httpd.conf
-rw-rw-r--  1 root  arisawa  17614 Sep 24  2007 httpd.conf
-bash$ ls -ld .
drwxrwxr-x  9 root  arisawa  306 Nov 20  2007 .
-bash$ 

つまり TextEdit.app だけではなく UNIX の標準的なコマンドである cp でもダメなのだ。

僕の嫌なのは OSX ではセキュリティモデルがはっきりしていないことだ。UNIX のようで UNIX ではないのが嫌だ。ただ実際問題として httpd.conf を tmp にコピーして、それをエディタで編集し、その結果を cp で元に戻すのは、悪い変え方ではない。ただその場合には cp は root 権限で行わなくてはならないが...

WebDAV

WebDAV の設定

さて WebDAV に向かってまっしぐらに進む事にする。

    /private/etc/apatch2/httpd.conf

の中に dav に関する若干の設定がある。

    #Include /private/etc/apache2/extra/httpd-dav.conf

と書いてあるが、これのコメントを外す必要がある。("#" の削除)
これで

    /private/etc/apache2/extra/httpd-dav.conf

が dav に関する設定ファイルとして取り込まれる。さてこの中に

DavLockDB "/usr/var/DavLock"
Alias /uploads "/usr/uploads"
<Directory "/usr/uploads">

などの記述があるが、"/usr/...." とあるのは、サーバのこの場所に LockDB ファイルや、アップロードするファイルを置く事を意味する。つまりこの場所がサーバ上のファイルの置き場所になる。また

    Alias /upload "/usr/uploads"

とあるのは、クライアントからは

    http://mmac/uploads

のようにして URL でアップロードの場所を指定することを意味する。("mmac" は WebDAV のサーバマシン)

今回僕は、まあ実験に過ぎないので、

    /private/etc/apache2/uploads

を作り、そこをアップロードファイルの置き場所にした。またロックデータペースは

    /private/etc/apache2/davlock

に置く事にした。だから

DavLockDB "/private/etc/apache2/davlock"
Alias /uploads "/private/etc/apache2/uploads"
<Directory "/private/etc/apache2/uploads">

としたのであるが、"DavLockDB" に関しては間違っていた。(後に解説する)

実験の下準備

認証などに関する五月蝿いものは当面外す。

<Directory "/private/etc/apache2/uploads">
    Dav On

    Order Allow,Deny
    Allow from all

    #AuthType Digest
    #AuthName DAV-upload
    # You can use the htdigest program to create the password database:
    #   htdigest -c "/usr/user.passwd" DAV-upload admin
    #AuthUserFile "/usr/user.passwd"

    # Allow universal read-access, but writes are restricted
    # to the admin user.
    #<LimitExcept GET OPTIONS>
    #    require user admin
    #</LimitExcept>
</Directory>

動作の確認

OPTIONS

さて次のデータをサーバに送ってやる。WebDAV のクライアントは、最初に OPTIONS から入るので、ここでも OPTIONS から入る。

term% connect tcp!mmac!80
OPTIONS /uploads/ HTTP/1.1
translate: f
User-Agent: Microsoft-WebDAV-MiniRedir/5.1.2600
Host: mmac
Content-Length: 0
Connection: close

ここに

    connect tcp!mmac!80

は UNIX ではほぼ

    telnet mmac 80

だと思ってよい。(connect は僕が Plan9 の為に作った簡単なツールで telnet よりもシンプルである。)

応答は

HTTP/1.1 200 OK
Date: Sun, 11 Jan 2009 03:10:08 GMT
Server: Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.7l DAV/2
DAV: 1,2
DAV: <http://apache.org/dav/propset/fs/1>
MS-Author-Via: DAV
Allow: OPTIONS,GET,HEAD,POST,DELETE,TRACE,PROPFIND,PROPPATCH,COPY,MOVE,LOCK,UNLOCK
Content-Length: 0
Connection: close
Content-Type: httpd/unix-directory

サーバは "/uploads" が WebDAV 用である事をちゃんと認識している。

Apache は、クライアントが

    OPTIONS /uploads HTTP/1.1

とやるとリダイレクションをクライアントに指示する。これはマイクロソフト系のクライアントに対しては問題で、"httpd-dav.conf" の中を見ると、ブラウザ毎にリダイレクションの指示をしないようにできるらしい。このような複雑さが発生するのは、実は Apache の作り込みに若干の問題を抱えているからである*

注*: Pegasus はこの問題を解決している. 詳しくは http://plan9.aichi-u.ac.jp/webdav/win.html を見よ。

PROPFIND

term% connect tcp!mmac!80
PROPFIND /uploads/ HTTP/1.1
User-Agent: WebDAVFS/1.4.1 (01418000) Darwin/8.8.1 (i386)
Accept: */*
Content-Type: text/xml
Depth: 0
Content-Length: 161
Connection: close
Host: mmac

<?xml version="1.0" encoding="utf-8"?>
<D:propfind xmlns:D="DAV:">
<D:prop>
<D:getlastmodified/>
<D:getcontentlength/>
<D:resourcetype/>
</D:prop>
</D:propfind>

応答は

HTTP/1.1 207 Multi-Status
Date: Sun, 11 Jan 2009 03:27:29 GMT
Server: Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.7l DAV/2
Content-Length: 554
Connection: close
Content-Type: text/xml; charset="utf-8"

<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:" xmlns:ns0="DAV:">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/" xmlns:g0="DAV:">
<D:href>/uploads/</D:href>
<D:propstat>
<D:prop>
<lp1:getlastmodified>Sat, 10 Jan 2009 05:15:25 GMT</lp1:getlastmodified>
<lp1:resourcetype><D:collection/></lp1:resourcetype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
<D:propstat>
<D:prop>
<g0:getcontentlength/>
</D:prop>
<D:status>HTTP/1.1 404 Not Found</D:status>
</D:propstat>
</D:response>
</D:multistatus>

ここまで来れば僕の目標はほぼ達成しているし、また実用的に使いたい場合にはサーバ保護に必要なセキュリティ上の設定をすればよい。(実はそうでもなかった)

PUT に失敗

どうやらファイルのアップロードに失敗しているようなので、小さなファイル(たった6文字のファイル)をアップロードしてみた。

term% connect tcp!mmac!80
PUT /uploads/b.txt HTTP/1.1
User-Agent: WebDAVFS/1.4.1 (01418000) Darwin/8.8.1 (i386)
Accept: */*
Content-Length: 6
Connection: close
Host: mmac

Hello

応答

HTTP/1.1 500 Internal Server Error
Date: Sun, 11 Jan 2009 06:39:50 GMT
Server: Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.7l DAV/2
Content-Length: 535
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>500 Internal Server Error</title>
</head><body>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error or
misconfiguration and was unable to complete
your request.</p>
<p>Please contact the server administrator,
 you@example.com and inform them of the time the error occurred,
and anything you might have done that may have
caused the error.</p>
<p>More information about this error may be available
in the server error log.</p>
</body></html>

エラーログ

The locks could not be queried for verification against a possible "If:" header.  [500, #0]
Could not open the lock database.  [500, #400]
(13)Permission denied: Could not open property database.  [500, #1]

いったいこれは何だ。ネットでこのメッセージをクグってみると、あった。

どうやら僕は "DavLockDB" で指定されたパスの意味を取り間違えていたらしい。ネットの記事によると、このパスはファイル名でもディレクトリ名でもないらしい。そこで次のように書き直してみた。

DavLockDB "/private/etc/apache2/davlock/LockDB"

大切な事は "davlock" が、("/tmp" のように)どのプロセスからも書き込み可能になっていることらしい。
そこで

    chmod 777 /private/etc/apache2/davlock

としておいたら、"Internal Server Error" がクリアされた。そして "davlock" の中に次の2つのファイルが生成されていた。

-rw-r--r--  1 _www  nobody  0 Jan 11 16:32 LockDB.dir
-rw-r--r--  1 _www  nobody  0 Jan 11 16:32 LockDB.pag

である。設定ファイル "httpd-dav.conf" の中に "DavLockDB" の件が少し説明されていたが、具体的に何が生成されるのかは書かれていなかった。この2つが生成される事が分かるように書いてあれば、もっと対応は楽だったと思われる。

プロセスのオーナー

そして、僕はもっと早くからモヤモヤした気分を整理しておくべきであった。それは httpd が誰のプロセスかと言うプログラムを理解する上での基本問題である。

# ps -lax
...
    0 31856     1    4000   0  31  0    77948   3416 -      Ss   2e9b250 ??         0:01.07 /usr/sbin/httpd -D FOREGROUND
   70 31857 31856     100   0  31  0    77948   1736 -      S    4eba2b0 ??         0:00.01 /usr/sbin/httpd -D FOREGROUND
   70 31858 31856     100   0  31  0    77948   1768 -      S    4eb9720 ??         0:00.01 /usr/sbin/httpd -D FOREGROUND
...

3つの内の最初の httpd は root 権限(uid 0)で、そして残りの2つは uid 70 である。第3フィールドから分かるように uid 70 のプロセスは uid 0 のプロセスから生成されている。そして uid 70 とは "/etc/passwd" を見るに

    _www:*:70:70:World Wide Web Server:/Library/WebServer:/usr/bin/false

となっている。つまりプロセスのオーナーは "_www" だ。だから "LockDB.dir" も "LockDB.pag" もオーナーは "_www" となっている。

以上の事から次の極めて大切な事が判明する: "davlock" は "_www" による書き込みを可能にしておく必要があり、ログファイルもアップロード用のディレクトリ "uploads" もまた然りである。

僕は httpd が nobody の権限で動いていると誤解していたので、"davlock" も "uploads" も "log" も正しくオーナーの設定をしていない。正しくは

    chown _www davlock

あるいは

    chown _www:_www davlock

である。セキュリティを上げたければ後者を採用すれば良いが、窮屈なのでグループに関しては便利さを重視した方が良いだろう。

OSX の WebDAV

僕は何年か前に OSX の WebDAV の動作を調べ、酷評してきた。何しろ「非効率で重くて実用にならないではないか」と。当時は Tiger の時代だったと思う*。しかし今回の Apache との組み合わせで使ってみた限り(今は認証部分を外しているが)気持ちよく使えるではないか。(少なくとも LAN 内で接続され、強力なサーバーだと) 速いし、安定しているし十分に実用になる。どこが改善されたのか、じっくり調べてみたい。

注*: WebDAV と OSX クライアント (http://plan9.aichi-u.ac.jp/webdav/osx.html)

ユーザのページ

404 Forbidden

さて $HOME/Sitesindex.html には「ここはあなた専用の Web サイトです」と書かれている。この内容はブラウザからは

    http://mmac/~arisawa/

で見えるはずである。しかし

    404 Forbidden

でアクセスが拒否された。error_log には

    (13)Permission denied: access to /~arisawa/ denied

となっている。念のために $HOME/Sites までのパスのパーミッションを調べた。

drwxr-xr-x+  5 arisawa  arisawa   170 Mar  1  2007 Sites
-bash$ cd Sites
-bash$ ls -l
total 16
drwxr-xr-x  5 arisawa  arisawa   170 Mar  1  2007 images
-rw-r--r--  1 arisawa  arisawa  6161 Mar  1  2007 index.html

うん、ここまでは異常はない。しかし

-bash$ pwd
/Users
drwxrwx---+ 33 arisawa       arisawa  1122 Dec  1 08:26 arisawa
-bash$ ls -ld
drwxr-xr-x  6 root  admin  204 Nov 20  2007 .

えー、/Users/arisawa へは httpd が侵入できない! アクセスが拒否されるのは当然である! そこで

    chmod 775 $HOME

しかしやっぱり

    404 Forbidden

である。但し今度はエラーログが異なる。

    client denied by server configuration: /Users/arisawa/Sites/

どうやら Apache の設定ファイルの内容に問題があるらしい。

Leopard の特殊問題?

次のキーワードでクグってみる。

    osx apache Sites forbidden

とうやら僕がここで出会ったのと同じ問題が多くの Mac ユーザを悩ましているらしい。そしてどうやら結論は

    ???

らしい。ソースコードが必要だなあ...

許可ビットの "+"

さて、ls -l の出力でしばしば

    drwxrwx---+ 33 arisawa       arisawa  1122 Dec  1 08:26 arisawa

のように、許可ビットの最後に "+" が付いているのがある。これは何だ? Mac を使っていると、僕の知識の外にあるようなものがしばしば現れる。これもその一つであり、僕はこれまでいい加減にしてきた。良く似たものとして "@" がある。

マニュアルで調べると

-bash$ man ls
...
If the file or directory has extended security information,
the permissions field printed by the -l option is followed by a '+' character.

"+" に関する話題はネット上には

で取り上げられている。このサイトでは

    man lis

に次のように書かれていると書いてある。

If the file or directory has extended attributes,
the permissions field printed by the -l option is followed by a '@' character.
Otherwise, if the file or directory has extended security information,
the permissions field printed by the -l option is followed by a '+' character.

つまり古いマニュアルには "@" の説明が含まれていたのだ。
なお "+" に関しては chmod の "ACL MANIPULATION OPTIONS" に詳しい解説がある。この解説によると
拡張パーミッションの内容は

    ls -le

で分かる。

-bash$ pwd
/Users
-bash$ ls -le
drwxrwxr-x+ 33 arisawa       arisawa  1122 Dec  1 08:26 arisawa
 0: group:everyone deny delete
-bash$ pwd
/Users/arisawa
-bash$ ls -le
drwxr-xr-x+  5 arisawa  arisawa   170 Mar  1  2007 Sites
 0: group:everyone deny delete

どうやら Win 的な ACL を "+" によってサポートしようとしているらしい。