web-dev-qa-db-ja.com

フォワード/リバースプロキシとしてのPHP + Apache:PHPでクライアント要求とサーバー応答を処理する方法は?

Apachemod_proxy.soが希望どおりに機能するように適切に構成するのに多くの問題があります...

主なアイデアは、ネットワーク内のローカルマシン上にプロキシを作成することです。このプロキシは、PHPでクライアント要求(このApacheで準備されたプロキシを介して接続されたクライアント)を処理する機能を備えています。また、PHPでもサーバーの応答を処理する能力があります。

これらは2つの機能であり、互いに独立しています。

私が達成する必要があることの小さなスキーマを提示しましょう:

alt text

ここでわかるように、青と赤の2つの方法があります。

青いものについては、基本的にローカルネットワーク(自宅)にクライアント(マシンB-携帯電話)を接続し、まったく同じネットワーク上のマシンA(パーソナルコンピューター)であるプロキシを経由するように構成しました。

それで、(DHCPではなく)言いましょう:

マシンA: 192.168.1.40-> Apacheはこのマシンで実行されており、ポート80をリッスンするように構成されています。

マシンB(携帯電話): 192.168.1.75->プロキシを経由するように構成されています。プロキシはIP192.168.1.75およびポート80(基本的にはマシンA)です。

Apacheを適切に構成した後、これは基本的に、mod_proxy.so(メインワーカー)、mod_proxy_connect.so(SSL、allowCONNECTなど)およびmod_proxy_http.so(必要な)の行のhttpd.confから「#」を削除することです。 HTTPリクエスト/レスポンスを処理します)そして私の場合、次のような行があります:

# Implements a proxy/gateway for Apache.
Include "conf/extra/httpd-proxy.conf"

# Various default settings
Include "conf/extra/httpd-default.conf"

# Secure (SSL/TLS) connections
Include "conf/extra/httpd-ssl.conf"

これにより、ファイルhttpd-proxy.confを構成して、フォワードプロキシまたはリバースプロキシを準備することができます。

したがって、必要なのがフォワードプロキシなのかリバースプロキシなのかはわかりません。

フォワードプロキシの場合、私はこれを行いました:

<IfModule proxy_module>
<IfModule proxy_http_module>

#
# FORWARD Proxy
#

#ProxyRequests Off
ProxyRequests On
ProxyVia On

<Proxy *>
    Order deny,allow
#   Allow from all
    Deny from all
    Allow from 192.168.1
</Proxy>

</IfModule>
</IfModule>

これは基本的にすべてのパケットを通常どおりサーバーに渡し、クライアントに戻します。 Apacheの「access.log」を見ると、完全に追跡できます(そしてそれが機能するテストもできます)。携帯電話で行ったリクエストはすべて、Apacheログに表示されます。だからそれは動作します。

しかし、ここに問題があります。

  • これらのクライアントリクエストを処理する必要があります。そして私はPHPでそれをする必要があります。

私はこれについてたくさん読んだ。 mod_proxyに関するApacheの公式サイトを詳しく読みました。そして、私はフォーラムでたくさん検索しましたが、運がありませんでした。

だから私は最初の近似について考えました:

1)Apacheのフォワードプロキシは、すべてのパケットを渡し、それらを処理することはできません。これは本当のようですが、リバースプロキシはどうですか?

だから私は次のようなものを想像しました:

ProxyRequests Off

<Proxy *>
Order deny,allow
Allow from all
</Proxy>

ProxyPass http://www.google.com http://www.yahoo.com
ProxyPassReverse http://www.google.com http://www.yahoo.com 

これは単なるテストですが、これにより私の携帯電話でGoogleに移動しようとすると、Yahooに行く必要がありますね。だがしかし。動作しません。

つまり、Apacheリバースプロキシのすべての例は次のようになります。

ProxyPass /foo http://foo.example.com/bar
ProxyPassReverse /foo http://foo.example.com/bar

つまり、ローカルコンテキストでのあらゆる種類のリクエストは、リモートの場所で解決されます。

しかし、私が必要としたのはその逆です!それは私の電話でリモートサイトを要求するとき、私はローカルサーバー(Apacheサーバー)でこの要求を解決してPHPモジュールで処理するということです。

したがって、フォワードプロキシの場合は、最初にPHPをパススルーする必要があります。リバースプロキシの場合は、ローカルサーバーへの「移動」方向を次のように変更する必要があります。最初にPHPで処理します。

次に、2番目のオプションが思い浮かびます。

2)私は次のようなものを見ました:

<Proxy http://example.com/foo/*>
SetOutputFilter INCLUDES
</Proxy>

そして、私はSetOutputFilter、SetInputFilter、AddOutputFilter、AddInputFilterを検索し始めました。

しかし、どうすれば使えるのかよくわかりません。

このようなものを使用すると、入力フィルターを追加してPHPクライアントの要求を処理し、クライアントに私が送信したものを送り返すことができるはずなので、良い、または私にとっての解決策のようですスキーマ上のBLUEパスであるprogrammed/want(リモートサーバー応答ではない)、および以前にリモートサーバー応答を処理する機能を提供するように見える出力フィルターを追加する機能が必要ですそれをクライアントに送信します。これは、スキーマのREDパスである必要があります。

レッドパス、それはサーバーの応答を読んでそれらで遊ぶことだけです。しかし、それ以上はありません。ブルーパスは重要なものです。リクエストを処理した後、クライアントに必要なものを送信するためです。

この驚くほど大きな投稿で申し訳ありませんが、私はできる限りそれを説明する必要がありました。

誰かが私の問題を理解し、私がそれを解決するのを手伝ってくれることを願っています!

3
Lightworker

さて、まず第一に:Apacheは間違ったツールです!

ApacheはWebサーバーであり、プロキシサーバーではありません。はい、プロキシモジュールが付属していますが、何よりもまずWebサーバーです。

むしろ、squidのような実際のプロキシサーバーを調べる必要があります。そして、イカの中であなたは「コンテンツ適応」と呼ばれる機能を探しています:

http://wiki.squid-cache.org/SquidFaq/ContentAdaptation

2
Sarek

@Sarekは正しいですが、問題は、Apacheがそのための適切なツールであるかどうかではなく、PHP(Apacheのmod_proxyから)でプロキシ要求を処理する方法についてです。

ApacheをプロキシとしてPHP)を使用するには、httpd.confで(mod_proxyとmod_rewriteが必要です)を使用します。

# Forward proxy server
<VirtualHost *:8080>
    ProxyRequests On
    ProxyVia On

    <Proxy *>
        Order deny,allow
        Deny from all
        Allow from 192.168

        RewriteEngine On
        RewriteCond %{REQUEST_URI} !/pac.php
        RewriteRule ^ /endpoint.php [L]
    </Proxy>
</VirtualHost>

次に、/pac.php[〜#〜] pac [〜#〜] ファイルの内容を定義します):

<?php header('Content-Type: application/x-javascript-config') ?>
function FindProxyForURL(url, Host)
{ 
     return "PROXY <?php echo $_SERVER['SERVER_ADDR'] ?>:<?php echo $_SERVER['SERVER_PORT'] ?>; DIRECT";
}

マシンB(プロキシクライアント)でのプロキシ構成に使用されます。 http://192.168.1.40:8080/pac.phpを使用します。これにより、クライアントは常に任意のドメイン/ IP(127.0.0.1、localhost、*。localなど)のプロキシを使用します。 注:IEおよび.NetAppsは、FQDNの末尾のlocalhostおよび127.0.0.1に対してのみ書き込む必要があります。ドット:http://localhost.およびhttp://127.0.0.1.

最後に/endpoint.phpで:

<?php
// Don't handle domain existance
$url = $_SERVER['REQUEST_URI'];
$url_parts = parse_url($url);
// Some security checks (no local file...)
if(false === $url_parts || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], array('http', 'https'))){
    die();
}

$headers_raw = '';
foreach ($_SERVER as $name => $value)
{
    if (substr($name, 0, 5) == 'HTTP_')
    {
        $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
        $headers_raw .= $name . ': ' . $value . "\r\n";
    } else if ($name == "CONTENT_TYPE") {
        $headers_raw .= 'Content-Type: ' . $value . "\r\n";
    } else if ($name == "CONTENT_LENGTH") {
        $headers_raw .= 'Content-Length: ' . $value . "\r\n";
    }
}

// http://php.net/manual/en/context.http.php
$context = stream_context_create(array(
    'http' => array(
        'method' => $_SERVER['REQUEST_METHOD'],
        'header' => $headers_raw,
        'ignore_errors' => true,
        'content' => file_get_contents('php://input')
    )
));
$content = file_get_contents($url, false, $context);

$content_type = 'application/octet-stream';// "text/html"
$content_type_raw = $content_type;// "text/html; charset=UTF-8"
foreach($http_response_header as $response_header){
    if('Content-Type:' == substr($response_header, 0, 13)){
        $content_type_raw = substr($response_header, 14);
        $content_type = strstr($content_type_raw, ';', true);
        header($response_header);
    }
    elseif('Content-Encoding:' == substr($response_header, 0, 17) && 'gzip' == substr($response_header, 18, 4))
    {
        //Now lets uncompress the compressed data
        $content = gzinflate(substr($content, 10, -8));
    }
    elseif('Content-Length:' == substr($response_header, 0, 15))
    {
        //Skip it
    }
    else{
        header($response_header);
    }
}

// Content transforms
//var_dump($url_parts);exit();
if('text/html' == $content_type){
    echo str_replace('cat', 'dog', $content);
}else{
    echo $content;
}

これは一例です。本番環境では使用しないでください。無効/タイムアウトドメイン/ IPを処理しません。

これにより、要求されたURLを書き換え(プロキシはURL http://google.com/search?q=doghttp://google.com/search?q=catのコンテンツを提供できます)、コンテンツを更新できます(広告の削除、JS/CSSの挿入など)。

2
mems