質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

サーバー環境で階層移動ができない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 444

score 7

知りたいこと

ローカルの環境とサーバー環境で挙動が違う点があり調べています。
カレントの階層を移動させるプログラムを実行すると、サーバー環境では移動が初回実行時しか成功せず、二度目はエラーが返ってきます。ローカル環境では問題なく成功します。

環境

windows10
apache2.4
python3.8
flask

ソースコード

from flask import Flask,render_template
import os

app = Flask(__name__)

@app.route("/")
def sample():
    os.chdir("C:/Users/USERNAME/Documents/ディレクトリ名")
    return render_template("sample.html") 

if __name__ == "__main__":
    app.run(debug=True)

エラーメッセージ(apache)

[Thu Dec 31 07:34:43.863129 2020] [authz_core:error] [pid 16632:tid 1256] [client ::1:64153] AH01630: client denied by server configuration: C:/Users/USERNAME/Documents/ディレクトリ名/htdocs

httpd.conf

Define SRVROOT "c:/Apache24"
ServerRoot "${SRVROOT}"

Listen 80
Listen 1001
Listen 2001
Listen 3001
Listen 4001
Listen 5001

LoadModule actions_module modules/mod_actions.so
LoadModule alias_module modules/mod_alias.so
LoadModule allowmethods_module modules/mod_allowmethods.so
LoadModule asis_module modules/mod_asis.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule cgi_module modules/mod_cgi.so
LoadModule dir_module modules/mod_dir.so
LoadModule env_module modules/mod_env.so
LoadModule include_module modules/mod_include.so
LoadModule isapi_module modules/mod_isapi.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule mime_module modules/mod_mime.so
LoadModule negotiation_module modules/mod_negotiation.so
LoadModule setenvif_module modules/mod_setenvif.so

<IfModule unixd_module>
User daemon
Group daemon
</IfModule>

ServerAdmin admin@example.com

ServerName localhost:80

<Directory />
    AllowOverride none
    Require all denied
</Directory>

DocumentRoot "${SRVROOT}/htdocs"
<Directory "${SRVROOT}/htdocs">
    Options Indexes FollowSymLinks ExecCGI
    AllowOverride None
    Require all granted
</Directory>

<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

<Files ".ht*">
    Require all denied
</Files>

ErrorLog "logs/error.log"

LogLevel warn

<IfModule log_config_module>
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

    <IfModule logio_module>
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>

    CustomLog "logs/access.log" common

</IfModule>

<IfModule alias_module>
    ScriptAlias /cgi-bin/ "${SRVROOT}/cgi-bin/"
</IfModule>

<IfModule cgid_module>
</IfModule>

<Directory "${SRVROOT}/cgi-bin">
    AllowOverride None
    Options None
    Require all granted
</Directory>

<IfModule headers_module>
    RequestHeader unset Proxy early
</IfModule>

<IfModule mime_module>
    TypesConfig conf/mime.types
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
    AddHandler cgi-script .cgi .py
</IfModule>

<IfModule proxy_html_module>
Include conf/extra/proxy-html.conf
</IfModule>

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

LoadFile "c:/users/USERNAME/appdata/local/programs/python/python38/python38.dll"
LoadModule wsgi_module "c:/users/USERNAME/appdata/local/programs/python/python38/lib/site-packages/mod_wsgi/server/mod_wsgi.cp38-win_amd64.pyd"
WSGIPythonHome "c:/users/USERNAME/appdata/local/programs/python/python38"

<VirtualHost *:1001>
    DocumentRoot "htdocs/app/APP1"
    WSGIScriptAlias / "htdocs/app/APP1/main.wsgi"
    <Directory "htdocs/app/APP1">
      Require all granted
    </Directory>
</VirtualHost>

<VirtualHost *:2001>
    DocumentRoot "htdocs/app/APP2"
    WSGIScriptAlias / "htdocs/app/APP2/main.wsgi"
    <Directory "htdocs/app/APP2">
        <Files main.wsgi>
            Require all granted
        </Files>
    </Directory>
</VirtualHost>

<VirtualHost *:3001>
    DocumentRoot "htdocs/app/APP3"
    WSGIScriptAlias / "htdocs/app/APP3/main.wsgi"
    <Directory "htdocs/app/APP3">
      Require all granted
    </Directory>
</VirtualHost>

<VirtualHost *:4001>
    DocumentRoot "htdocs/app/APP4"
    WSGIScriptAlias / "htdocs/app/APP4/main.wsgi"
    WSGIApplicationGroup %{GLOBAL}
    <Directory "htdocs/app/APP4">
        <Files main.wsgi>
            Require all granted
        </Files>
    </Directory>
</VirtualHost>

<VirtualHost *:5001>
    DocumentRoot "htdocs/app/sample"
    WSGIScriptAlias / "htdocs/app/sample/main.wsgi"
    <Directory "htdocs/app/sample">
      Require all granted
    </Directory>
</VirtualHost>


気になるのが、エラーメッセージで返ってくるパスの末尾にhtdocsが追加されていることです。

また、その際の画面表示は
Forbidden
You don't have permission to access this resource.
です。
サーバー上ではどのような動作が行われているのでしょうか。
アドバイスいただけると助かります。よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • gogotowel

    2021/01/01 08:05

    ありがとうございます。
    追記しました。
    実際には複数のプログラムをVirtualHostで動作させてますが、こちらでは該当部分のみ記載しております。

    キャンセル

  • A_kirisaki

    2021/01/01 11:12

    複数の……その VirtualHost 部分も追記していただけますか?念の為

    キャンセル

  • gogotowel

    2021/01/01 13:51

    VirtualHost及び対応するListen部分も追記いたしました。
    これで全ての内容となります。
    どうぞよろしくお願いいたします。

    キャンセル

回答 1

checkベストアンサー

+1

すみません、「一回だけ成功する」理由はわかりましたが「403 が返ってくる」まではつかめませんでした。とりあえず前者の報告まで。

まず Flask というのは Python の WSGI という決まりの上で動かされます。この仕組みを Apache でも動かせるようにしたのが mod_wsgi というモジュールになります。上でプロセス、スレッドを共有し Python コードが走ってサーバーとして動く構造となっています。

次に Apache は ServerRoot と DocumentRoot という概念を持ちます。ServerRoot + DocumentRoot のパスを基点に Web サーバーとして動きます。例えば上記の httpd.conf から抜き出すと

Define SRVROOT "c:/Apache24"
ServerRoot "${SRVROOT}"
DocumentRoot "${SRVROOT}/htdocs"
<Directory "${SRVROOT}/htdocs">
    Options Indexes FollowSymLinks ExecCGI
    AllowOverride None
    Require all granted
</Directory>


c:/Apache24/htdocs を基点に Web サーバーとして配信します、という設定がなされています(上記設定で 80 番ポートにアクセスすると「It works!」というページが表示されるはずです)。

Apache の WSGI に関する設定部分を見ましょう。

<VirtualHost *:5001>
    DocumentRoot "htdocs/app/sample"
    WSGIScriptAlias / "htdocs/app/sample/main.wsgi"
    <Directory "htdocs/app/sample">
      Require all granted
    </Directory>
</VirtualHost>


この場合基点は c:/Apache24/htdocs/app/sample になります。そして WSGIScriptAlias の意味ですが、リクエストのパスが / の場合(http://localhost:5001/ でアクセスした場合)、サーバーの内部では DocumentRoot を基点として htdocs/app/sample/main.wsgi を呼び出すという意味になります。<Directory ... 以下はそのディレクトリへのアクセスを許可する設定です。

さて、一度目のリクエストが Apache にやってきます。すると Apache はリクエストの URL を見てどの処理を行うか決めます。http://localhost:5001/ にアクセスした場合、上記の main.wsgi が呼ばれます。すると中で os.chdir() が実行されます。そしてこの時、Apache の DocumentRoot も同時に変わってしまいます。つまり二回目にリクエストを送る時、DocumentRoot は C:/Users/USERNAME/Documents/ディレクトリ名 になっているのです。この状態で main.wsgi を呼び出そうとしても呼び出せないので失敗します。

なぜそうなるか

これは憶測なのですが、ワーカー(スレッドかプロセス)を Python と共有しているため作業ディレクトリを受け継いでいるのではないかと思われます。もし Apache の設定でワーカーを有効にすると例えば A と B というワーカーがあったとして A にアクセスした後 B にアクセスしても B はまだ成功します。その後どっちにアクセスしても失敗する、というような動きをするでしょう。

なぜ 403 になるのか

これはちょっと調べきれませんでした。ただ Windows の Documents 以下のディレクトリということで特殊な扱いを受けてて、 Apache の設定ファイルを探すと Require all denied となっているところがあるのかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/01/02 08:05

    大変分かりやすい説明ありがとうございました。
    それならos.chdir()はApache上では使用を控えるべきですね。プログラム側の基点がApacheの基点に切り替わったことで、エラーメッセージ内のパスの末尾にhtdocsが追加されていたのも納得です。
    これですっきりと他の手段を検討できます…ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る