Docker + Alpine3.5 + Apache2.4 + Python3.6で、SSIを使ってみた

前回、フォームのデータをcgiモジュールを使って受け取りました。
Docker + Alpine3.5 + Apache2.4 + Python3.6で、フォームのデータを標準モジュールcgiで受け取ってみた - メモ的な思考的な

 
今回は、ApacheのSSI(Server Side Include)を、公式チュートリアルを見ながら試してみます。
Apache チュートリアル: Server Side Includes 入門 - Apache HTTP サーバ バージョン 2.4  
 
目次

 

環境

  • Mac OS X 10.11.6
  • Docker for Mac 17.03.1-ce-mac12
  • Alpine3.5 + Apache2.4.25 + Python 3.6.1

 
なお、Dockerfileは前回のものを流用します。

また、コンソールで「Dockerのコンテナを起動する」とした場合は、以下のコマンドを入力しています。
参考:docker container / image コマンド新旧比較 - Qiita

# Docker container runしたのとは別のターミナルで実行
## Dockerのssiコンテナを停止
$ docker container stop ssi

# Docker container runしたターミナルで実行
## Dockerコンテナを削除
$ docker container rm $(docker container ls -a -q)
## もしくは起動していないDockerコンテナを全削除
$ docker container prune

## 削除したいDockerイメージのIDを知る
$ docker image ls
REPOSITORY          TAG                          IMAGE ID
alpine              python3_httpd24_ssi          658225783800

## Dockerイメージを削除
$ docker image rm 658225783800

## DockerfileからDockerイメージをビルド
$ docker image build -t alpine:python3_httpd24_ssi .


## Dockerコンテナを実行
### HTMLのみの場合、HTMLのディレクトリのみホストと共有
$ docker container run -p 8081:80 --name ssi -v `pwd`/htdocs/:/usr/local/apache2/htdocs alpine:python3_httpd24_ssi

### HTML & CGIの場合、HTML & CGIのディレクトリをホストと共有
$ docker container run -p 8081:80 --name ssi -v `pwd`/htdocs/:/usr/local/apache2/htdocs -v `pwd`/cgi/:/usr/local/apache2/cgi-bin/ alpine:python3_httpd24_ssi

 

AddOutputFilterを使って、shtmlファイルのみSSIを有効にする

まずは、SSIが動くか試してみます。

そのため、

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title></title>
</head>

<body>
    <h1>SSIサンプル(Echo)</h1>
    <!--#echo var="DATE_LOCAL" -->
</body>
</html>

  • echo.html
  • echo.shtml

として2つ用意し、両方ともドキュメントルートの中に入れます。

ホスト上のパーミッションは以下の通りです。

$ ls -al
...
-rw-r--r--  1 you  staff  182  5 14 15:28 echo.html
-rw-r--r--  1 you  staff  182  5 14 15:15 echo.shtml

 
次に、httpd.confを修正します。

  • SSIを使うため、include_moduleをLoadModule
  • Directoryディレクティブに以下を追加
    • Options Includes
    • AddType text/html .shtml
    • AddOutputFilter INCLUDES .shtml

とします。

なお、Web上ではAddOutputFilter INCLUDESAddHandler server-parsedの2つの方法がありました。

Apacheのドキュメントによると、

とのことなので、2.4系の今回はAddOutputFilterを使います。

httpd.conf

...
LoadModule include_module modules/mod_include.so
...
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted

    # shtmlファイルでSSIを使うための設定を追加
    Options Includes
    AddType text/html .shtml
    AddOutputFilter INCLUDES .shtml
</Directory>

 
Dockerコンテナを起動し、curlで確認します。

# htmlファイルではダメ
$ curl http://localhost:8081/echo.html
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    <!--#echo var="DATE_LOCAL" -->
</body>


# shtmlファイルでは、SSI動作
$ curl http://localhost:8081/echo.shtml
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    Sunday, 14-May-2017 06:36:59 
</body>

 
shtmlファイルのみSSIが動作しました。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_shtml_echo_using_outputfilter

 

AddOutputFilterを使って、shtml・htmlファイルでSSIを有効にする

htmlファイルでもSSIが動作するよう、httpd.confにてhtmlをAddOutputFilterへ追加します。

httpd.conf

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted

    Options Includes
    AddType text/html .shtml
    # AddOutputFilterに.htmlを追加
    AddOutputFilter INCLUDES .shtml .html
</Directory>

 
ホストのパーミッションには変更ありません。

$ ls -al
-rw-r--r--  1 you  staff  182  5 14 15:28 echo.html
-rw-r--r--  1 you  staff  182  5 14 15:15 echo.shtml

 
Dockerコンテナを起動し、curlで確認します。

# shtmlファイル
$ curl http://localhost:8081/echo.shtml
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    Sunday, 14-May-2017 06:45:58 
</body>

# htmlファイル
$ curl http://localhost:8081/echo.html
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    Sunday, 14-May-2017 06:46:01 
</body>

 
shtml・htmlファイルでSSIが動作しました。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_html_echo_using_outputfilter

 

XBitHack onを使って、htmlファイルのみでSSIを有効にする

AddOutputFilter以外の方法を探したところ、公式チュートリアルXBitHack onを使う方法が記載されていました。

XBitHack は、ファイルの実行ビットが立っている場合、 SSI ディレクティブにより解析することを Apache に伝えます。 従って、SSI ディレクティブを現在のページに加えるためには、 ファイル名を変更しなくてもよく、単に chmod を使用してファイルを実行可能にするだけで済みます。

SSI を許可するためのサーバの設定 | Apache チュートリアル: Server Side Includes 入門 - Apache HTTP サーバ バージョン 2.4

 
httpd.confを変更し、OptionsとXBitHackを使うようにします。

httpd.conf

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted

    Options Includes
    # XBitHackをonにする
    XBitHack on
</Directory>

 
Dockerコンテナを起動し、curlで確認します。

まずはパーミッションがそのままの場合です。

$ curl http://localhost:8081/echo.shtml
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    <!--#echo var="DATE_LOCAL" -->
</body>

$ curl http://localhost:8081/echo.html
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    <!--#echo var="DATE_LOCAL" -->
</body>

いずれもSSIは動作しませんでした。

 
続いて、ホストのパーミッションを変更します。

$ chmod +x echo.html 
$ chmod +x echo.shtml

$ ls -al
-rwxr-xr-x  1 you  staff  182  5 14 15:28 echo.html
-rwxr-xr-x  1 you  staff  182  5 14 15:15 echo.shtml

 
念のため、Dockerコンテナにあるファイルのパーミッションも確認します。

$ docker exec -it `docker ps | grep ssi | awk '{print $1}'` /bin/bash
bash-4.3# ls ./htdocs -al
...
-rwxr-xr-x    1 root     root           182 May 14 06:47 echo.html
-rwxr-xr-x    1 root     root           182 May 14 06:47 echo.shtml

ホストと同期されています。

 
パーミッションが変更されていたので、curlにて確認します。

$ curl http://localhost:8081/echo.shtml
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    <!--#echo var="DATE_LOCAL" -->
</body>

$ curl http://localhost:8081/echo.html
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    Sunday, 14-May-2017 07:06:21 
</body>

htmlファイルのみSSIが有効になりました。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_html_cgi_using_xbithack_on

 

XBitHack onを使って、html・shtmlファイルでSSIを有効にする

XBitHackディレクティブのドキュメントを見ると、

on

ユーザの実行ビットが設定されている text/html ファイルは全てサーバで解析する html ドキュメントとして扱われます。

XBitHack ディレクティブ | mod_include - Apache HTTP サーバ バージョン 2.4

との記載がありました。

そのため、AddTypeshtmlを追加すれば、shtmlでも有効になると考えられたため、試してみました。

httpd.conf

# DocumentRoot
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted

    Options Includes
    # text/htmlに、.shtmlファイルも追加する
    AddType text/html .shtml
    XBitHack on
</Directory>

 
パーミッションは両方とも実行可能です。

$ ls -al htdocs/
drwxr-xr-x  4 you  staff  136  5 14 16:08 .
drwxr-xr-x  7 you  staff  238  5 14 16:08 ..
-rwxr-xr-x  1 you  staff  182  5 14 16:08 echo.html
-rwxr-xr-x  1 you  staff  182  5 14 16:08 echo.shtml

 
Dockerコンテナを起動し、curlで確認します。

$ curl http://localhost:8081/echo.shtml
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    Sunday, 14-May-2017 07:15:58 
</body>

$ curl http://localhost:8081/echo.html
...
<body>
    <h1>SSIサンプル(Echo)</h1>
    Sunday, 14-May-2017 07:16:03 
</body>

両方ともSSIが動作しました。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_shtml_echo_using_xbithack_on

 

XBitHack onを使って、CGIを動かす

SSIの動作が確認できたため、今度はCGIを動かしてみます。

httpd.confにCGIの記述を追加します。

httpd.conf

# CGIを使うので、alias・cgidモジュールを追加
LoadModule alias_module modules/mod_alias.so
LoadModule cgid_module modules/mod_cgid.so

# CGIを使うための設定
# alias_module settings
ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
# CGI directory
<Directory "/usr/local/apache2/cgi-bin">
    AllowOverride None
    Options ExecCGI
    SetHandler cgi-script
    Require all granted
</Directory>

 
続いて、標準出力に現在時刻を出すCGIPythonスクリプトを作成します。

now.py

#!/usr/bin/python3
import datetime

# HTTPヘッダ
print('Content-Type: text/plain;charset=utf-8')
print('')

print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

if __name__ == '__main__':
    pass

 
確認用のHTMLファイルも作成します。

CGIが正しく動作しているかを確認するため、JavaScriptでも現在時刻を取得します。PythonUTCJavaScriptJSTタイムゾーンが異なりますが、今回は確認するだけなのでそのままとします*2

cgi.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title></title>
</head>

<body>
    <h1>SSIサンプル(CGI)</h1>
    <p>CGIの時間(UTC):<!--#include virtual="/cgi-bin/now.py" --></p>
    <p>JSの時間(JST) :<span id="js_time"></span></p>

    <script>
        document.getElementById("js_time").innerHTML = function(){
            var now = new Date();
            var year = now.getFullYear();
            var mon = now.getMonth() + 1;
            var day = now.getDate();
            var hour = now.getHours();
            var min = now.getMinutes();
            var sec = now.getSeconds();

            return `${year}-${mon}-${day} ${hour}:${min}:${sec}`
        }();
    </script>
</body>
</html>

 
また、htmlとPythonスクリプトを実行可能にします。

$ cd ../ssi_html_cgi_using_xbithack_on/

# 両方とも実行可能に変更
$ chmod +x cgi/now.py 
$ chmod +x htdocs/cgi.html 

# それぞれのファイルのパーミッションを確認
$ ls -Ral
...

./cgi:
-rwxr-xr-x  1 you  staff  179  5 14 16:23 now.py

./htdocs:
-rwxr-xr-x  1 you  staff  194  5 14 16:23 cgi.html

 
Dockerコンテナを起動します。CGI用のディレクトリもホストと共有します。

$ docker container run -p 8081:80 --name ssi -v `pwd`/htdocs/:/usr/local/apache2/htdocs -v `pwd`/cgi/:/usr/local/apache2/cgi-bin/ alpine:python3_httpd24_ssi

 
ブラウザでhttp://localhost:8081/cgi.htmlへアクセスして確認します。

SSIサンプル(CGI)

CGIの時間(UTC):2017-05-14 08:43:18

JSの時間(JST) :2017-5-14 17:43:18

 
再度アクセスします。

SSIサンプル(CGI)

CGIの時間(UTC):2017-05-14 08:43:58

JSの時間(JST) :2017-5-14 17:43:58

更新されているようです。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_shtml_echo_using_xbithack_on

 

XBitHack fullを使って、CGIを動かす

XBitHackディレクティブの公式ドキュメントを見ると、

注意 他の CGI を #include するかもしれないものや、各アクセスに対して違う出力を生成する (もしくは後のリクエストで変わるかもしれないもの) すべての SSI スクリプトに対してグループ実行ビットが 設定されていないことを確認できない場合は、full は使わない方が良い でしょう。

XBitHack ディレクティブ | mod_include - Apache HTTP サーバ バージョン 2.4

とあったため、試してみます。

 
httpd.confでXBitHackをfullに修正します。

httpd.conf

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted

    Options Includes
    # XBitHackを変更
    XBitHack full
</Directory>

 
Dockerコンテナを起動し、ブラウザでhttp://localhost:8081/cgi.htmlへアクセスして確認します。

SSIサンプル(CGI)

CGIの時間(UTC):2017-05-14 08:46:52

JSの時間(JST) :2017-5-14 17:46:52

 
再度アクセスします。

SSIサンプル(CGI)

CGIの時間(UTC):2017-05-14 08:46:52

JSの時間(JST) :2017-5-14 17:47:8

 
CGIの時間が初回アクセス時と同一のため、キャッシュが使われているようです。

よって、ドキュメントにもある通り、CGIを使う場合にはXBitHack onの方が良さそうです。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_html_cgi_using_xbithack_full

 

CGIを動かす際、includeやexecなどいくつかのパターンを試す

Apacheの公式ドキュメントを読むと、CGIPythonスクリプトを動かすには、

  • include virtual
  • exec cgi
  • exec cmd
  • exec cmd python3

などが使えそうでしたので、試してみます。
基本要素 | mod_include - Apache HTTP サーバ バージョン 2.4

 
HTMLにはそれぞれの動かし方と、PythonスクリプトでのContent-Type出力の有無のパターンを記載します。

cgi.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title></title>
</head>

<body>
    <h1>SSIサンプル(CGI: XBitHack on)</h1>
    <h2>CGIにcontent-typeあり</h2>
    <ul>
        <li>include virtual: <!--#include virtual="/cgi-bin/with_content_type.py" --></li>
        <li>exec cgi: <!--#exec cgi="/cgi-bin/with_content_type.py" --></li>
        <li>exec cmd: <!--#exec cmd="/usr/local/apache2/cgi-bin/with_content_type.py" --></li>
        <li>exec cmd python3: <!--#exec cmd="python3 /usr/local/apache2/cgi-bin/with_content_type.py" --></li>
    </ul>
    <h2>CGIにcontent-type無し</h2>
    <ul>
        <li>include virtual: <!--#include virtual="/cgi-bin/witout_content_type.py" --></li>
        <li>exec cgi: <!--#exec cgi="/cgi-bin/witout_content_type.py" --></li>
        <li>exec cmd: <!--#exec cmd="/usr/local/apache2/cgi-bin/witout_content_type.py" --></li>
        <li>exec cmd python3: <!--#exec cmd="python3 /usr/local/apache2/cgi-bin/witout_content_type.py" --></li>
    </ul>
</body>
</html>

 
Pythonスクリプトでは、Content-Typeを出すものと出さないものを用意します。

with_content_type.py

#!/usr/bin/python3
import datetime

# HTTPヘッダ
# SSIで「include virtual」する時はHTTPヘッダが必要
print('Content-Type: text/plain;charset=utf-8')
print('')

print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

 
witout_content_type.py

#!/usr/bin/python3
import datetime

print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

 
Dockerコンテナを起動し、ブラウザでhttp://localhost:8081/cgi.htmlへアクセスして確認します。

CGIにcontent-typeあり

・include virtual: 2017-05-14 09:49:46
・exec cgi: 2017-05-14 09:49:46
・exec cmd: Content-Type: text/plain;charset=utf-8 2017-05-14 09:49:46
・exec cmd python3: Content-Type: text/plain;charset=utf-8 2017-05-14 09:49:46

CGIにcontent-type無し

・include virtual: [an error occurred while processing this directive]
・exec cgi:
・exec cmd: 2017-05-14 09:49:46
・exec cmd python3: 2017-05-14 09:49:47

エラーが発生したり、表示されないものがありました。Dockerのログも見てみます。

[cgid:error] Premature end of script headers: witout_content_type.py
[include:error] unable to include "/cgi-bin/witout_content_type.py" in parsed file /usr/local/apache2/htdocs/cgi.html, subrequest returned 500
[cgid:error] Premature end of script headers: witout_content_type.py

 
これより、

  • Content-Typeが必要なパターン
    • include virtual, exec cgi
  • Content-Typeは不要なパターン
    • exec cmd, exec cmd python3

と分かりました。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_options_includes_using_xbithack_on

 

CGIを動かす際、OptionsディレクティブでIncludesNOEXECを使う

今までOptionsディレクティブはIncludesを指定してきました。

ただ、SSIではIncludesNOEXECという指定もできます。
Options ディレクティブ | core - Apache HTTP サーバ バージョン 2.4

ドキュメントによると、include virtualだけが使えるようですので、試してみます。

 
httpd.confを修正します。

httpd.conf

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted

    # SSIを使うための設定
    # .htmlファイルでSSIを使えるようにするが、
    # include virtualのみ有効にする
    Options IncludesNOEXEC
    XBitHack on
</Directory>

 
Dockerコンテナを起動し、ブラウザでhttp://localhost:8081/cgi.htmlへアクセスして確認します。

CGIにcontent-typeあり

・include virtual: 2017-05-14 09:51:38
・exec cgi: [an error occurred while processing this directive]
・exec cmd: [an error occurred while processing this directive]
・exec cmd python3: [an error occurred while processing this directive]

CGIにcontent-type無し

・include virtual: [an error occurred while processing this directive]
・exec cgi: [an error occurred while processing this directive]
・exec cmd: [an error occurred while processing this directive]
・exec cmd python3: [an error occurred while processing this directive]

 
SSIで動かす時にinclude virtual以外ではエラーが出ています。

Dockerのログも見ます。

[cgid:error] AH01271: exec used but not allowed in /usr/local/apache2/htdocs/cgi.html
[cgid:error] AH01271: exec used but not allowed in /usr/local/apache2/htdocs/cgi.html
[cgid:error] Premature end of script headers: witout_content_type.py
[include:error] unable to include "/cgi-bin/witout_content_type.py" in parsed file /usr/local/apache2/htdocs/cgi.html, subrequest returned 500
[cgid:error] AH01271: exec used but not allowed in /usr/local/apache2/htdocs/cgi.html
[cgid:error] AH01271: exec used but not allowed in /usr/local/apache2/htdocs/cgi.html
[cgid:error] AH01271: exec used but not allowed in /usr/local/apache2/htdocs/cgi.html

ドキュメント通り、include virtualだけが正常に動作しました。

ソースコード全体は以下です。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/tree/master/alpine_apache_python36_ssi/ssi_options_includes_noexec_using_xbithack_on

 

ソースコード

GitHubに上げました。alpine_apache_python36_ssiディレクトリの中が今回のものです。
thinkAmi-sandbox/Docker_Apache-sample

*1:Apache公式のが見当たらなかったため、Oracleのサイトにあるものをリンクしました

*2:Alpineのタイムゾーンを修正すれば良いかと思いますが、手間だったので対応せず…