【ぷよぷよ#1】GTRができるまで

まずは鉤積み

Image from Gyazo

http://www.puyop.com/s/=41100001504241212445144225142114122554558442524512124512112451824512

土台に着目

Image from Gyazo

土台を変形 (折り返し部分を平らにする①)

Image from Gyazo

土台を変形 (折り返し部分を平らにする②)

Image from Gyazo

または

Image from Gyazo

土台の上に座布団が敷けるようにする

このままだと4列目の紫のせいで座布団が敷けない。

Image from Gyazo

座布団が敷けるように連鎖尾を鉤積みの形から変形する。

Image from Gyazo

Next step

多重折り返しを入れたり、連鎖尾を改善・追加したりする。

CORSの設定をしているはずなのにCloudFrontでCORSエラーが発生してしまう場合の対処法を調べてみた

CORSの設定をしているはずなのにエラーが発生してしまうという事象に出くわしました。

Access to XMLHttpRequest at 'https://example.com/xxx/yyy/zzz' from origin 'http://localhost:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

CloudFront および S3 には次のような設定をしていました。

  • CloudFront のキャッシュポリシーには、マネージドポリシーの ChachingOptimezed をアタッチ
  • CloudFront のオリジンリクエストポリシーには、マネージドポリシーの CORS-S3Origin をアタッチ
  • CloudFront のレスポンスヘッダーポリシーには、マネージドポリシーの CORS-and-SecurityHeadersPolicy をアタッチ
  • S3 バケットのアクセス許可で CORS の設定を記述(内容は以下)
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    }
]

追記 1

検証を進めたところ、レスポンスヘッダーポリシーの CORS 設定の挙動の認識に誤りがあることがわかりました。

オーバーライド設定を OFF にする場合には、S3 バケットのアクセス許可で CORS 設定の欄を空にする必要がありました。「S3 からのレスポンスのヘッダーに項目がなかったら」ヘッダーを追加するものと思っていましたが、実際の挙動としては「S3 に CORS 設定がなかったら」ヘッダーを追加なようでした。

CloudFront のレスポンスヘッダーポリシーで、マネージドポリシーを利用して CORS 設定を行う場合には、S3 側では CORS 設定を空にする必要がありそうです。

解決策(結論)

初めに結論を書いておきます。

CloudFront のレスポンスヘッダーポリシーには、マネージドポリシーの CORS-and-SecurityHeadersPolicy ではなく、自前で定義するカスタムポリシーを使うことにより、エラー発生を防ぐことができるようになりました。

ポイントは、Access-Control-Allow-Origin のルールでオリジンのオーバーライドを有効にすることです。マネージドポリシーの CORS-and-SecurityHeadersPolicy ではオリジンのオーバーライドが無効になっています。

CORS エラーの再現方法

では次に、どのようなケースで初めに提示した CORS 設定でもエラーが発生してしまうのかを見ていきます。

次のような手順を踏むことで CORS エラーを再現できます。

  1. 対象オブジェクトのキャッシュを削除する(CloudFront > ディストリビューション > キャッシュ削除)
  2. 対象オブジェクトに CloudFront 経由で no-cors リクエストする
  3. 対象オブジェクトに CloudFront 経由で cors リクエストする

ポイントは、no-cors リクエスト時のレスポンスを CloudFront にキャッシュさせることです。このキャッシュデータに cors でアクセスを試みるとエラーが発生します。

cors と no-cors

cors リクエストと no-cors リクエストの違いは、リクエストヘッダーに Origin ヘッダー(Origin: <scheme>://<hostname>:<port>)が含まれているか否かです。Origin ヘッダーが含まれていれば cors リクエストです。

※ 厳密には、Origin ヘッダーを含み、且つリクエスト先とリクエスト元の Origin が異なる場合に Cross Origin なリクエストとなります。

cors リクエストの送り方

ブラウザはリクエスト時に自動で Origin ヘッダーを付与する仕組みになっているので、Web アプリケーションで img タグや fetch API を通してリソースへアクセスを試みると CORS リクエストとなります。

もしくは、curl などの HTTP クライアントで Origin ヘッダーを明示的に付与することでも CORS リクエストを作成できます。

  • ブラウザ上で動作する Web アプリからリクエストする
  • HTTP クライアントから Origin ヘッダーを付与してリクエストする

no-cors リクエストの送り方

リクエストヘッダーに Origin が含まれなければいいので、次のような方法だと no-cors リクエストになります。

  • HTTP クライアントから Origin ヘッダーを付与せずにリクエストする
  • ブラウザのアドレスバーに URL を直接入力してアクセスする

CORS エラーが発生する仕組み

S3 に対して no-cors リクエストが届くと、S3 はレスポンスに CORS ヘッダー(Access-Control-Allow-Origin)を含まずにオブジェクトを返却します。CloudFront も、この CORS ヘッダーを含まないレスポンスをキャッシュします。

この状態で今度は cors リクエストが届くと、CloudFront は Access-Control-Allow-Origin が無いレスポンスを返却します。すると、ブラウザは「CORS アクセスが許可されていないリソース」と判断し、データへのアクセスを中止します。

CORS-and-SecurityHeadersPolicy が問題?

ここで疑問となるのが、CloudFront のレスポンスヘッダーポリシーにアタッチしたはずの CORS-and-SecurityHeadersPolicy です。

このポリシーは、S3 から返却されたレスポンスに Access-Control-Allow-Origin ヘッダーが存在しない場合に、Access-Control-Allow-Origin: * を付与し直してクライアントへ返却する設定になっています。

しかしながら、今回の再現手順を踏むと、どういう訳か Access-Control-Allow-Origin が付与されないままレスポンスが返されてしまっていました。

カスタムポリシーを作成して解決する

レスポンスヘッダーポリシーは、デフォルトで準備されているマネージドポリシーの他に、自前でルールを定義して適用することもできます。

カスタムルールを作成するには次のように操作します。

[CloudFront] > [ポリシー] > [レスポンスヘッダー] > [カスタムポリシー] > [レスポンスヘッダーポリシーを作成]

内容は下記のとおりです。

詳細:
  名前: My-CORS-and-SecurityHeadersPolicy
クロスオリジンリソース共有(CORS):
  CORS を設定: ON
  Access-Control-Allow-Origin: All origins
  Access-Control-Allow-Headers: All headers
  Access-Control-Allow-Methods:
    Customize:
      - GET
      - HEAD
  Access-Control-Expose-Headers: なし
  Access-Control-Max-Age: 3000
  Access-Cpntrol-Allow-Credentials: OFF
  オリジンのオーバーライド: ON
セキュリティヘッダー:
  Strict-Transport-Security:
    最大経過時間: 31536000
    オリジンのオーバーライド: ON
  X-Content-Type-Options:
    オリジンのオーバーライド: ON
  X-Frame-Options:
    オリジン: SAMEORIGIN
    オリジンのオーバーライド: ON
  X-XSS-Protection:
    X-XSS-Protection: 有効
    ブロック: ON
    オリジンのオーバーライド: ON
  Referrer-Policy:
    Referrer-Policy: strict-origin-when-cross-origin
    オリジンのオーバーライド: ON
  Content-Security-Policy: OFF

作成したら CloudFront にアタッチします。

[CloudFront] > [ディストリビューション] > [ビヘイビア] > [編集] > [レスポンスヘッダーポリシー] > [My-CORS-and-SecurityHeadersPolicy]

カスタムポリシーのアタッチが完了すると、先のエラー手順を再現しても、cors リクエスト時には Access-Control-Allow-Origin: * ヘッダーが返却され、CORS エラーが発生しなくなりました。

もう一つの解決策(キャッシュポリシーを変更する)

今回は採用を見送りましたが、キャッシュポリシーを変更することでも CORS エラーの発生を防ぐことができます。

具体的には、キャッシュポリシーを ChachingOptimezed から Managed-Elemental-MediaPackage に変更するという方法です。

Managed-Elemental-MediaPackage はキャッシュキーとして Origin ヘッダーを含む設定になっています。要するに、リクエスト元の Origin が異なればキャッシュにはヒットせず S3 へのリクエストが発生するということです。

この挙動のおかげで、Access-Control-Allow-Origin ヘッダーを含まない no-cors リクエスト(Origin: なし)の結果を CloudFront がキャッシュしたとしても、次に cors リクエストを送った場合にはこのキャッシュにはヒットせず、Access-Control-Allow-Origin ヘッダーを含む新たなレスポンスを取得することが可能になります。

ただし、この挙動だと複数ドメインからリソースを取得するようなケースでキャッシュヒット率が低くなってしまいます(localhost, www.example.com, admin.example.com など複数 Origin からアクセスする場合)。

こういった理由で、今回はこの方法は採用を見送りました。

Apache / mod_wsgi / virtualenv で Python / Flask アプリケーションを動かしてみた【EC2 / Amazon Linux 2】

Apach, mod_wsgi で Flask アプリケーションを動かしてみたのでその記録です。Python の実行環境として仮想環境を作成する方針で進めていきます。

作業手順

WIP

作業ログ

# ------------------------------------------------------------
# Apacheのデフォルトページを表示する
# ------------------------------------------------------------

# rootユーザーで作業
sudo su -

# タイムゾーンの設定
date
timedatectl status
timedatectl set-timezone Asia/Tokyo

# Apacheのインストール
yum list installed
yum list installed | grep httpd
yum -y install httpd httpd-devel
httpd -v
yum info httpd

# Apacheの起動・停止・ステータス
service httpd start
service httpd status
service httpd stop
service httpd restart

# ログの確認
tail -f -n 10 /etc/httpd/logs/access_log
tail -f -n 10 /etc/httpd/logs/error_log

# ここで http://<EC2のパブリックIP>:80/ にアクセスして確認する

# ------------------------------------------------------------
# 最小構成のWSGIアプリケーションでHello World
# ------------------------------------------------------------

# Pythonの依存モジュールのインストール
yum -y install \
 git \
 gcc \
 make \
 zlib-devel \
 bzip2 \
 bzip2-devel \
 readline \
 readline-devel \
 sqlite \
 sqlite-devel \
 openssl \
 tk-devel \
 libffi-devel \
 xz-devel \
 openssl-devel

# pyenvのインストール
git clone https://github.com/pyenv/pyenv.git /usr/local/pyenv
(cd /usr/local/pyenv/ && src/configure && make -C src)
echo 'export PYENV_ROOT="/usr/local/pyenv"' >> /etc/profile.d/pyenv.sh
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> /etc/profile.d/pyenv.sh
echo 'eval "$(pyenv init - --no-rehash)"' >> /etc/profile.d/pyenv.sh
exec $SHELL -l
which pyenv

# Pythonのインストール
pyenv install 3.9.16
pyenv global 3.9.16
python --version
which python

# poetryのインストール
curl -sSL https://install.python-poetry.org | POETRY_HOME=/usr/local/poetry python3 -
echo 'export POETRY_HOME="/usr/local/poetry"' >> /etc/profile.d/poetry.sh
echo 'export PATH="$POETRY_HOME/bin:$PATH"' >> /etc/profile.d/poetry.sh
exec $SHELL -l
which poetry

# virtualenvのインストール
pip install virtualenv

# アプリケーションのデプロイ先ディレクトリの作成
mkdir app
chmod 755 /app
chown root:root /app

# プロジェクトの作成
mkdir /app/myapp
touch /app/myapp/wsgi.py
touch /app/myapp/vhost.conf

# 仮想環境の作成とmod_wsgiのインストール
cd /app/myapp/
virtualenv venv
. venv/bin/activate
which python
python --version
python -c 'import sys; print(sys.prefix)'
pip install mod_wsgi

# wsgiファイルの編集
vi /app/myapp/wsgi.py

# apache設定ファイルの編集
mod_wsgi-express module-config
vi /app/myapp/vhost.conf

# 権限の編集
chmod -R 755 /app/myapp
find /app/myapp -type f -exec chmod 644 {} +

# apacheの設定変更・再起動
cp /app/myapp/vhost.conf /etc/httpd/conf.d/
httpd -t
service httpd restart

# ログの確認
tail -f -n 10 /etc/httpd/logs/access_log
tail -f -n 10 /etc/httpd/logs/error_log

# ここで http://<EC2のパブリックIP>:80/ にアクセスして確認する

# ここで http://<EC2のパブリックIP>:8000/ にアクセスして確認する

# ------------------------------------------------------------
# FlaskアプリケーションでHello World
# ------------------------------------------------------------

# Flaskアプリケーションのファクトリを定義する
vi /app/myapp/myapp/__init__.py

# wsgiファイルを編集してFlaskアプリケーションを読み込む
vi /app/myapp/wsgi.py

# apacheの設定変更・再起動
service httpd restart

# ログの確認
tail -f -n 10 /etc/httpd/logs/access_log
tail -f -n 10 /etc/httpd/logs/error_log

# ここで http://<EC2のパブリックIP>:8000/ にアクセスして確認する

wsgi.py

import os
import sys

project_root = os.path.dirname(__file__)
python_home = os.path.join(project_root, "venv")
activate_this = os.path.join(python_home, "bin", "activate_this.py")

print('---------------------------------------------')
print('project_root', project_root)
print('python_home', python_home)
print('activate_this', activate_this)
print('---------------------------------------------')

print('---------------------------------------------')
print('sys.prefix', sys.prefix)
print('sys.path', sys.path)
print('---------------------------------------------')

with open(activate_this) as file_:
    exec(file_.read(), dict(__file__=activate_this))

sys.path.insert(0, project_root)

print('---------------------------------------------')
print('sys.prefix', sys.prefix)
print('sys.path', sys.path)
print('---------------------------------------------')


def application(environ, start_response):
    status = "200 OK"
    output = b"Hello World!"

    response_headers = [("Content-type", "text/plain"), ("Content-Length", str(len(output)))]
    start_response(status, response_headers)

    return [output]

# from myapp import create_app

# print('---------------------------------------------')
# print('create_app', create_app)
# print('---------------------------------------------')

# application = create_app()

vhost.conf

Listen 8000

<VirtualHost *:8000>
  LoadModule wsgi_module "/app/myapp/venv/lib/python3.9/site-packages/mod_wsgi/server/mod_wsgi-py39.cpython-39-x86_64-linux-gnu.so"

  ServerName myapp.com

  LogLevel info
  
  WSGIDaemonProcess myapp user=apache group=apache
  # WSGIDaemonProcess myapp user=apache group=apache python-home=/app/myapp/venv

  WSGIProcessGroup myapp
  WSGIApplicationGroup %{GLOBAL}

  WSGIScriptAlias / /app/myapp/wsgi.py
  
  WSGIScriptReloading On
  
  <Directory /app/myapp>
    Require all granted
  </Directory>
</VirtualHost>

myapp/__init__.py

from flask import Flask


def create_app() -> Flask:
    app = Flask(__name__)

    @app.route("/hello")
    def hello() -> str:
        print('=========================================')
        print('hello')
        print('=========================================')
        return "Hello, World!"

    return app

vhost.sample.conf

<IfModule !version_module>
  LoadModule version_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_version.so'
</IfModule>

ServerName localhost
ServerRoot '/tmp/mod_wsgi-localhost:8000:1000'
PidFile '/tmp/mod_wsgi-localhost:8000:1000/httpd.pid'

<IfVersion >= 2.4>
  Defaul RuntimeDir '/tmp/mod_wsgi-localhost:8000:1000'
</IfVersion>

ServerTokens ProductOnly
ServerSignature Off

<IfDefine !MOD_WSGI_MPM_ENABLE_WINNT_MODULE>
  User ${MOD_WSGI_USER}
  Group ${MOD_WSGI_GROUP}
</IfDefine>

<IfDefine MOD_WSGI_WITH_LISTENER_HOST>
  Listen localhost:8000
</IfDefine>

<IfDefine !MOD_WSGI_WITH_LISTENER_HOST>
  Listen 8000
</IfDefine>

<IfVersion < 2.4>
  LockFile '/tmp/mod_wsgi-localhost:8000:1000/accept.lock'
</IfVersion>

<IfVersion >= 2.4>
  <IfDefine MOD_WSGI_WITH_PHP5>
    <IfModule !mpm_event_module>
      <IfModule !mpm_worker_module>
        <IfModule !mpm_prefork_module>
          <IfDefine MOD_WSGI_MPM_EXISTS_PREFORK_MODULE>
            LoadModule mpm_prefork_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_mpm_prefork.so'
          </IfDefine>
        </IfModule>
      </IfModule>
    </IfModule>
  </IfDefine>
</IfVersion>

<IfVersion >= 2.4>
  <IfModule !mpm_event_module>
    <IfModule !mpm_worker_module>
      <IfModule !mpm_prefork_module>

        <IfDefine MOD_WSGI_MPM_ENABLE_EVENT_MODULE>
          LoadModule mpm_event_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_mpm_event.so'
        </IfDefine>

        <IfDefine MOD_WSGI_MPM_ENABLE_WORKER_MODULE>
          LoadModule mpm_worker_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_mpm_worker.so'
        </IfDefine>

        <IfDefine MOD_WSGI_MPM_ENABLE_PREFORK_MODULE>
          LoadModule mpm_prefork_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_mpm_prefork.so'
        </IfDefine>

      </IfModule>
    </IfModule>
  </IfModule>
</IfVersion>

<IfDefine MOD_WSGI_WITH_HTTP2>
  LoadModule http2_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_http2.so'
</IfDefine>

<IfVersion >= 2.4>
  <IfModule !access_compat_module>
    LoadModule access_compat_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_access_compat.so'
  </IfModule>

  <IfDefine !MOD_WSGI_MPM_ENABLE_WINNT_MODULE>
    <IfModule !unixd_module>
      LoadModule unixd_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_unixd.so'
    </IfModule>
  </IfDefine>

  <IfModule !authn_core_module>
    LoadModule authn_core_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_authn_core.so'
  </IfModule>

  <IfModule !authz_core_module>
    LoadModule authz_core_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_authz_core.so'
  </IfModule>
</IfVersion>

<IfModule !authz_host_module>
  LoadModule authz_host_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_authz_host.so'
</IfModule>

<IfModule !mime_module>
  LoadModule mime_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_mime.so'
</IfModule>

<IfModule !rewrite_module>
  LoadModule rewrite_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_rewrite.so'
</IfModule>

<IfModule !alias_module>
  LoadModule alias_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_alias.so'
</IfModule>

<IfModule !dir_module>
  LoadModule dir_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_dir.so'
</IfModule>

<IfModule !env_module>
  LoadModule env_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_env.so'
</IfModule>

<IfModule !headers_module>
  LoadModule headers_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_headers.so'
</IfModule>

<IfModule !filter_module>
  LoadModule filter_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_filter.so'
</IfModule>

<IfDefine MOD_WSGI_DIRECTORY_LISTING>
  <IfModule !autoindex_module>
    LoadModule autoindex_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_autoindex.so'
  </IfModule>
</IfDefine>

<IfVersion >= 2.2.15>
  <IfModule !reqtimeout_module>
    LoadModule reqtimeout_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_reqtimeout.so'
  </IfModule>
</IfVersion>

<IfDefine MOD_WSGI_COMPRESS_RESPONSES>
  <IfModule !deflate_module>
    LoadModule deflate_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_deflate.so'
  </IfModule>
</IfDefine>

<IfDefine MOD_WSGI_AUTH_USER>
  <IfModule !auth_basic_module>
    LoadModule auth_basic_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_auth_basic.so'
  </IfModule>

  <IfModule !auth_digest_module>
    LoadModule auth_digest_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_auth_digest.so'
  </IfModule>

  <IfModule !authz_user_module>
    LoadModule authz_user_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_authz_user.so'
  </IfModule>
</IfDefine>

<IfDefine MOD_WSGI_WITH_PROXY>
  <IfModule !proxy_module>
    LoadModule proxy_module ${MOD_WSGI_MODULES_DIRECTORY}/mod_proxy.so
  </IfModule>

  <IfModule !proxy_http_module>
    LoadModule proxy_http_module ${MOD_WSGI_MODULES_DIRECTORY}/mod_proxy_http.so
  </IfModule>
</IfDefine>

<IfModule mpm_prefork_module>
  <IfDefine MOD_WSGI_WITH_PHP5>
    <IfModule !php5_module>
      Loadmodule php5_module '${MOD_WSGI_MODULES_DIRECTORY}/libphp5.so'
    </IfModule>

    AddHandler application/x-httpd-php .php
  </IfDefine>
</IfModule>

<IfDefine MOD_WSGI_LOAD_PYTHON_DYLIB>
  LoadFile ''
</IfDefine>

LoadModule wsgi_module '/app/myapp/venv/lib/python3.9/site-packages/mod_wsgi/server/mod_wsgi-py39.cpython-39-x86_64-linux-gnu.so'

<IfDefine MOD_WSGI_SERVER_METRICS>
  <IfModule !status_module>
    LoadModule status_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_status.so'
  </IfModule>
</IfDefine>

<IfDefine MOD_WSGI_CGID_SCRIPT>
  <IfModule !cgid_module>
    LoadModule cgid_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_cgid.so'
  </IfModule>
</IfDefine>

<IfDefine MOD_WSGI_CGI_SCRIPT>
  <IfModule !cgi_module>
    LoadModule cgi_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_cgi.so'
  </IfModule>
</IfDefine>

<IfVersion < 2.4>
  DefaultType text/plain
</IfVersion>

TypesConfig '/etc/mime.types'

HostnameLookups Off
MaxMemFree 64
Timeout 60
ListenBacklog 500

<IfDefine MOD_WSGI_WITH_HTTP2>
  Protocols h2 h2c http/1.1
</IfDefine>

<IfVersion >= 2.2.15>
  RequestReadTimeout header=15-30,MinRate=500 body=15,MinRate=500
</IfVersion>

LimitRequestBody 10485760

<Directory />
  AllowOverride None

  <IfVersion < 2.4>
    Order deny,allow
    Deny from all
  </IfVersion>

  <IfVersion >= 2.4>
    Require all denied
  </IfVersion>
</Directory>

WSGIPythonHome '/app/myapp/venv'

WSGIVerboseDebugging 'Off'

<IfDefine !MOD_WSGI_MPM_ENABLE_WINNT_MODULE>
  <IfDefine MOD_WSGI_WITH_SOCKET_PREFIX>
    WSGISocketPrefix None/wsgi
  </IfDefine>

  <IfDefine !MOD_WSGI_WITH_SOCKET_PREFIX>
    WSGISocketPrefix /tmp/mod_wsgi-localhost:8000:1000/wsgi
  </IfDefine>

  WSGISocketRotation Off
</IfDefine>

<IfDefine EMBEDDED_MODE>
  MaxConnectionsPerChild 0
</IfDefine>

<IfDefine ORPHAN_INTERPRETER>
  WSGIDestroyInterpreter Off
</IfDefine>

<IfDefine !ORPHAN_INTERPRETER>
  WSGIDestroyInterpreter On
</IfDefine>

<IfDefine !ONE_PROCESS>
  <IfDefine !EMBEDDED_MODE>
    WSGIRestrictEmbedded On

    <IfDefine MOD_WSGI_MULTIPROCESS>
      WSGIDaemonProcess localhost:8000 \
        display-name='(wsgi:localhost:8000:1000)' \
        home='/app/myapp' \
        processes=1 \
        threads=5 \
        maximum-requests=0 \
        python-path='' \
        python-eggs='/tmp/mod_wsgi-localhost:8000:1000/python-eggs' \
        lang='en_US.UTF-8' \
        locale='en_US.UTF-8' \
        listen-backlog=100 \
        queue-timeout=45 \
        socket-timeout=60 \
        connect-timeout=15 \
        request-timeout=60 \
        inactivity-timeout=0 \
        startup-timeout=15 \
        deadlock-timeout=60 \
        graceful-timeout=15 \
        eviction-timeout=0 \
        restart-interval=0 \
        cpu-time-limit=0 \
        shutdown-timeout=5 \
        send-buffer-size=0 \
        receive-buffer-size=0 \
        header-buffer-size=0 \
        response-buffer-size=0 \
        response-socket-timeout=0 \
        server-metrics=Off
    </IfDefine>

    <IfDefine !MOD_WSGI_MULTIPROCESS>
      WSGIDaemonProcess localhost:8000 \
        display-name='(wsgi:localhost:8000:1000)' \
        home='/app/myapp' \
        threads=5 \
        maximum-requests=0 \
        python-path='' \
        python-eggs='/tmp/mod_wsgi-localhost:8000:1000/python-eggs' \
        lang='en_US.UTF-8' \
        locale='en_US.UTF-8' \
        listen-backlog=100 \
        queue-timeout=45 \
        socket-timeout=60 \
        connect-timeout=15 \
        request-timeout=60 \
        inactivity-timeout=0 \
        startup-timeout=15 \
        deadlock-timeout=60 \
        graceful-timeout=15 \
        eviction-timeout=0 \
        restart-interval=0 \
        cpu-time-limit=0 \
        shutdown-timeout=5 \
        send-buffer-size=0 \
        receive-buffer-size=0 \
        response-buffer-size=0 \
        response-socket-timeout=0 \
        server-metrics=Off
    </IfDefine>
  </IfDefine>
</IfDefine>

WSGICallableObject 'application'
WSGIPassAuthorization On
WSGIMapHEADToGET Auto

<IfDefine MOD_WSGI_DISABLE_RELOADING>
  WSGIScriptReloading Off
</IfDefine>

<IfDefine EMBEDDED_MODE>
  <IfDefine MOD_WSGI_WITH_PYTHON_PATH>
   WSGIPythonPath ''
  </IfDefine>
</IfDefine>

<IfDefine ONE_PROCESS>
  WSGIRestrictStdin Off

  <IfDefine MOD_WSGI_WITH_PYTHON_PATH>
    WSGIPythonPath ''
  </IfDefine>
</IfDefine>

<IfDefine MOD_WSGI_SERVER_METRICS>
  ExtendedStatus On
</IfDefine>

WSGIServerMetrics Off

<IfDefine MOD_WSGI_SERVER_STATUS>
  <Location /server-status>
    SetHandler server-status

    <IfVersion < 2.4>
      Order deny,allow
      Deny from all
      Allow from localhost
    </IfVersion>

    <IfVersion >= 2.4>
      Require all denied
      Require host localhost
    </IfVersion>
  </Location>
</IfDefine>

<IfDefine MOD_WSGI_KEEP_ALIVE>
  KeepAlive On
  KeepAliveTimeout 2
</IfDefine>

<IfDefine !MOD_WSGI_KEEP_ALIVE>
  KeepAlive Off
</IfDefine>

<IfDefine MOD_WSGI_ENABLE_SENDFILE>
  EnableSendfile On
  WSGIEnableSendfile On
</IfDefine>

<IfDefine MOD_WSGI_COMPRESS_RESPONSES>
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/xml
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/json
</IfDefine>

<IfDefine MOD_WSGI_ROTATE_LOGS>
  ErrorLog "|/usr/sbin/rotatelogs \
    /tmp/mod_wsgi-localhost:8000:1000/error_log.%Y-%m-%d-%H_%M_%S 5M"
</IfDefine>

<IfDefine !MOD_WSGI_ROTATE_LOGS>
  ErrorLog "/tmp/mod_wsgi-localhost:8000:1000/error_log"
</IfDefine>

LogLevel warn

<IfDefine MOD_WSGI_ERROR_LOG_FORMAT>
  ErrorLogFormat "None"
</IfDefine>

<IfDefine MOD_WSGI_ACCESS_LOG>
  <IfModule !log_config_module>
    LoadModule log_config_module ${MOD_WSGI_MODULES_DIRECTORY}/mod_log_config.so
  </IfModule>

  LogFormat "%h %l %u %t \"%r\" %>s %b" common
  LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
  LogFormat "undefined" custom

  <IfDefine MOD_WSGI_ROTATE_LOGS>
    CustomLog "|/usr/sbin/rotatelogs \
      /tmp/mod_wsgi-localhost:8000:1000/access_log.%Y-%m-%d-%H_%M_%S 5M" common
  </IfDefine>

  <IfDefine !MOD_WSGI_ROTATE_LOGS>
    CustomLog "/tmp/mod_wsgi-localhost:8000:1000/access_log" common
  </IfDefine>
</IfDefine>

<IfDefine MOD_WSGI_CHUNKED_REQUEST>
  WSGIChunkedRequest On
</IfDefine>

<IfDefine MOD_WSGI_WITH_PROXY_HEADERS>
  WSGITrustedProxyHeaders
</IfDefine>

<IfDefine MOD_WSGI_WITH_TRUSTED_PROXIES>
  WSGITrustedProxies
</IfDefine>

<IfDefine MOD_WSGI_WITH_HTTPS>
  <IfModule !ssl_module>
    LoadModule ssl_module ${MOD_WSGI_MODULES_DIRECTORY}/mod_ssl.so
  </IfModule>
</IfDefine>

<IfModule mpm_prefork_module>
  <IfDefine !ONE_PROCESS>
    ServerLimit 20
    StartServers 1
    MaxClients 20
    MinSpareServers 1
    MaxSpareServers 2
  </IfDefine>

  <IfDefine ONE_PROCESS>
    ServerLimit 1
    StartServers 1
    MaxClients 1
    MinSpareServers 1
    MaxSpareServers 1
  </IfDefine>

  MaxRequestsPerChild 0
</IfModule>

<IfModule mpm_worker_module>
  <IfDefine !ONE_PROCESS>
    ServerLimit 2
    ThreadLimit 10
    StartServers 1
    MaxClients 20
    MinSpareThreads 10
    MaxSpareThreads 10
    ThreadsPerChild 10
  </IfDefine>

  <IfDefine ONE_PROCESS>
    ServerLimit 1
    ThreadLimit 1
    StartServers 1
    MaxClients 1
    MinSpareThreads 1
    MaxSpareThreads 1
    ThreadsPerChild 1
  </IfDefine>

  MaxRequestsPerChild 0
  ThreadStackSize 262144
</IfModule>

<IfModule mpm_event_module>
  <IfDefine !ONE_PROCESS>
    ServerLimit 2
    ThreadLimit 10
    StartServers 1
    MaxClients 20
    MinSpareThreads 10
    MaxSpareThreads 10
    ThreadsPerChild 10
  </IfDefine>

  <IfDefine ONE_PROCESS>
    ServerLimit 1
    ThreadLimit 1
    StartServers 1
    MaxClients 1
    MinSpareThreads 1
    MaxSpareThreads 1
    ThreadsPerChild 1
  </IfDefine>

  MaxRequestsPerChild 0
  ThreadStackSize 262144
</IfModule>

<IfDefine !MOD_WSGI_VIRTUAL_HOST>
  <IfVersion < 2.4>
    NameVirtualHost *:8000
  </IfVersion>

  <VirtualHost _default_:8000>
  </VirtualHost>
</IfDefine>

<IfDefine MOD_WSGI_VIRTUAL_HOST>
  <IfVersion < 2.4>
    NameVirtualHost *:8000
  </IfVersion>

  <VirtualHost _default_:8000>
    <Location />
      <IfVersion < 2.4>
        Order deny,allow
        Deny from all
      </IfVersion>

      <IfVersion >= 2.4>
        Require all denied
      </IfVersion>

      <IfDefine MOD_WSGI_ALLOW_LOCALHOST>
       Allow from localhost
      </IfDefine>
    </Location>
  </VirtualHost>

  <IfDefine !MOD_WSGI_HTTPS_ONLY>
    <VirtualHost *:8000>
      ServerName None

      <IfDefine MOD_WSGI_SERVER_ALIAS>
        ServerAlias None
      </IfDefine>
    </VirtualHost>

    <IfDefine MOD_WSGI_REDIRECT_WWW>
      <VirtualHost *:8000>
        ServerName unspecified
        Redirect permanent / http://None:8000/
      </VirtualHost>
    </IfDefine>
  </IfDefine>

  <IfDefine MOD_WSGI_HTTPS_ONLY>
    <VirtualHost *:8000>
      ServerName None

      <IfDefine MOD_WSGI_SERVER_ALIAS>
        ServerAlias None
      </IfDefine>

      RewriteEngine On
      RewriteCond %{HTTPS} off
      RewriteRule (.*) https://None:None%{REQUEST_URI}
    </VirtualHost>

    <IfDefine MOD_WSGI_REDIRECT_WWW>
      <VirtualHost *:8000>
        ServerName unspecified
        RewriteEngine On
        RewriteCond %{HTTPS} off
        RewriteRule (.*) https://None:None%{REQUEST_URI}
      </VirtualHost>
    </IfDefine>
  </IfDefine>
</IfDefine>

<IfDefine MOD_WSGI_VIRTUAL_HOST>

  <IfDefine MOD_WSGI_WITH_HTTPS>
    <IfDefine MOD_WSGI_WITH_LISTENER_HOST>
      Listen localhost:None
    </IfDefine>
    <IfDefine !MOD_WSGI_WITH_LISTENER_HOST>
      Listen None
    </IfDefine>
    <IfVersion < 2.4>
      NameVirtualHost *:None
    </IfVersion>
    <VirtualHost _default_:None>
      <Location />
        <IfVersion < 2.4>
          Order deny,allow
          Deny from all
        </IfVersion>
        <IfVersion >= 2.4>
          Require all denied
        </IfVersion>
        <IfDefine MOD_WSGI_ALLOW_LOCALHOST>
          Allow from localhost
        </IfDefine>
      </Location>
      SSLEngine On
      SSLCertificateFile None
      SSLCertificateKeyFile None
      <IfDefine MOD_WSGI_VERIFY_CLIENT>
        SSLCACertificateFile None
        SSLVerifyClient none
      </IfDefine>
      <IfDefine MOD_WSGI_CERTIFICATE_CHAIN>
        SSLCertificateChainFile None
      </IfDefine>
    </VirtualHost>
    <VirtualHost *:None>
      ServerName None
      <IfDefine MOD_WSGI_SERVER_ALIAS>
        ServerAlias None
      </IfDefine>
      SSLEngine On
      SSLCertificateFile None
      SSLCertificateKeyFile None
      <IfDefine MOD_WSGI_VERIFY_CLIENT>
        SSLCACertificateFile None
        SSLVerifyClient none
      </IfDefine>
      <IfDefine MOD_WSGI_CERTIFICATE_CHAIN>
      S SLCertificateChainFile None
      </IfDefine>
      <IfDefine MOD_WSGI_HTTPS_ONLY>
        <IfDefine MOD_WSGI_HSTS_POLICY>
          Header set Strict-Transport-Security None
        </IfDefine>
      </IfDefine>
      <IfDefine MOD_WSGI_SSL_ENVIRONMENT>
        SSLOptions +StdEnvVars
      </IfDefine>
    </VirtualHost>
  <IfDefine MOD_WSGI_REDIRECT_WWW>
    <VirtualHost *:None>
      ServerName unspecified
      Redirect permanent / https://None:None/
      SSLEngine On
      SSLCertificateFile None
      SSLCertificateKeyFile None
      <IfDefine MOD_WSGI_VERIFY_CLIENT>
        SSLCACertificateFile None
        SSLVerifyClient none
      </IfDefine>
      <IfDefine MOD_WSGI_CERTIFICATE_CHAIN>
        SSLCertificateChainFile None
      </IfDefine>
    </VirtualHost>
    </IfDefine>
  </IfDefine>

</IfDefine>

DocumentRoot '/tmp/mod_wsgi-localhost:8000:1000/htdocs'

AccessFileName .htaccess

<Directory '/tmp/mod_wsgi-localhost:8000:1000'>
  AllowOverride None
  <Files handler.wsgi>
    <IfVersion < 2.4>
      Order allow,deny
      Allow from all
    </IfVersion>
    <IfVersion >= 2.4>
      Require all granted
    </IfVersion>
  </Files>
</Directory>

<Directory '/tmp/mod_wsgi-localhost:8000:1000/htdocs'>
  AllowOverride None
  <IfDefine MOD_WSGI_DIRECTORY_INDEX>
      DirectoryIndex None
  </IfDefine>
  <IfDefine MOD_WSGI_DIRECTORY_LISTING>
      Options +Indexes
  </IfDefine>
  <IfDefine MOD_WSGI_CGI_SCRIPT>
      Options +ExecCGI
  </IfDefine>
  <IfDefine MOD_WSGI_CGID_SCRIPT>
      Options +ExecCGI
  </IfDefine>
      RewriteEngine On
      Include /tmp/mod_wsgi-localhost:8000:1000/rewrite.conf
  <IfVersion < 2.4>
      Order allow,deny
      Allow from all
  </IfVersion>
  <IfVersion >= 2.4>
      Require all granted
  </IfVersion>
</Directory>

<Directory '/tmp/mod_wsgi-localhost:8000:1000/htdocs/'>
  <IfDefine !MOD_WSGI_STATIC_ONLY>
    RewriteCond %{REQUEST_FILENAME} !-f
    <IfDefine MOD_WSGI_DIRECTORY_INDEX>
      RewriteCond %{REQUEST_FILENAME} !-d
    </IfDefine>
    <IfDefine MOD_WSGI_SERVER_STATUS>
      RewriteCond %{REQUEST_URI} !/server-status
    </IfDefine>
    RewriteRule .* - [H=wsgi-handler]
  </IfDefine>
</Directory>

<IfDefine MOD_WSGI_ERROR_OVERRIDE>
  WSGIErrorOverride On
</IfDefine>

<IfDefine MOD_WSGI_HOST_ACCESS>
  <Location />
    WSGIAccessScript 'None'
  </Location>
</IfDefine>

<IfDefine MOD_WSGI_AUTH_USER>
  <Location />
    AuthType Basic
    AuthName 'localhost:8000'
    AuthBasicProvider wsgi
    WSGIAuthUserScript 'None'
    <IfDefine MOD_WSGI_AUTH_GROUP>
      WSGIAuthGroupScript 'None'
    </IfDefine>
    <IfVersion < 2.4>
      Require valid-user
      <IfDefine MOD_WSGI_AUTH_GROUP>
          Require wsgi-group 'wsgi'
      </IfDefine>
    </IfVersion>
    <IfVersion >= 2.4>
      <RequireAll>
        Require valid-user
        <IfDefine MOD_WSGI_AUTH_GROUP>
          Require wsgi-group 'wsgi'
        </IfDefine>
      </RequireAll>
    </IfVersion>
  </Location>
</IfDefine>

<IfDefine !ONE_PROCESS>
  <IfDefine !EMBEDDED_MODE>
  WSGIHandlerScript wsgi-handler '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
    process-group='localhost:8000' application-group=%{GLOBAL}
  WSGIImportScript '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
    process-group='localhost:8000' application-group=%{GLOBAL}
  </IfDefine>
</IfDefine>

<IfDefine EMBEDDED_MODE>
  WSGIHandlerScript wsgi-handler '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
    process-group='%{GLOBAL}' application-group=%{GLOBAL}
  WSGIImportScript '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
    process-group='%{GLOBAL}' application-group=%{GLOBAL}
</IfDefine>

<IfDefine ONE_PROCESS>
  <IfDefine !MOD_WSGI_MPM_ENABLE_WINNT_MODULE>
    WSGIHandlerScript wsgi-handler '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
      process-group='%{GLOBAL}' application-group=%{GLOBAL}
    WSGIImportScript '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
      process-group='%{GLOBAL}' application-group=%{GLOBAL}
  </IfDefine>
  <IfDefine MOD_WSGI_MPM_ENABLE_WINNT_MODULE>
    WSGIHandlerScript wsgi-handler '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
      application-group=%{GLOBAL}
    WSGIImportScript '/tmp/mod_wsgi-localhost:8000:1000/handler.wsgi' \
      application-group=%{GLOBAL}
  </IfDefine>
</IfDefine>


<IfDefine MOD_WSGI_VERIFY_CLIENT>
  <Location '/'>
    SSLVerifyClient require
    SSLVerifyDepth 1
  </Location>
</IfDefine>

pip で mod_wsgi のインストールを試みたらエラーが発生した

環境

実行コマンド

pip install mod_wsgi

エラー

RuntimeError: The 'apxs' command appears not to be installed or is not executable. Please check the list of prerequisites in the documentation for this package and install any missing Apache httpd server packages.

原因

apxs コマンドがないことが原因。

解決

mod_wsgi のインストールの前に apxs コマンドを含むパッケージをインストールする。

yum info httpd-devel

The httpd-devel package contains the APXS binary (以下略)

sudo yum -y install httpd-devel

pip install mod_wsgi