4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SymfonyAdvent Calendar 2018

Day 12

TrustedProxy, getClientIps などロードバランサ配下での挙動について

Last updated at Posted at 2018-12-12

※この投稿はSymfony3.4.20で調べた内容を元に書いています。

本番リリースしたとたん絶対URLがhttpsにならない(焦

ロードバランサー(SSLオフロード機能有効)配下にSymfonyのアプリケーションを配置してアプリケーション内でURLを生成したところ、URLがhttpで生成されてしまいました。

IndexController.php
class IndexController extends Controller
{
    public function indexAction(Request $request)
    {
        $absoluteUrl = $this->generateUrl('ROUTE_NAME', [], UrlGeneratorInterface::ABSOLUTE_URL);
        // http://... になってしまった
    }
}

対応策は、web/app.phpで

app.php
$request = Request::createFromGlobals();
Request::setTrustedProxies(
    ['CIDR表記'],
    Request::HEADER_X_FORWARDED_ALL
);
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

をセットすることです。
ロードバランサー配下では、クライアントがロードバランサーへの接続に使用したプロトコルは$_SERVER['HTTPS']ではなく、X-Forwarded-Protoヘッダに記録されます。

そう、SymfonyはRequest::setTrustedProxies()を設定しさえすれば、HTTPS接続かを見る箇所を切り替えてくれるのです。なんと親切なんでしょう。

Symfonyのソースコードを眺めてみます。
Symfony\Component\HttpFoundation\Requestクラスに

Request.php
    /**
     * Gets the request's scheme.
     *
     * @return string
     */
    public function getScheme()
    {
        return $this->isSecure() ? 'https' : 'http';
    }

isSecureの中身は

Request.php
    public function isSecure()
    {
        if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
            return in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true);
        }

        $https = $this->server->get('HTTPS');

        return !empty($https) && 'off' !== strtolower($https);
    }

Request::setTrustedProxies()の設定にひっかかれば、前半のif文の中の処理で動作しています。

IPアドレスでも同じことが…

ロードバランサー配下では、クライアントがロードバランサーへの接続に使用したIPアドレスは$_SERVER['REMOTE_ADDR']ではなく、X-Forwarded-Forヘッダに記録されます。

Request::geClientIp()もきちんと対応してくれています。

Request.php
    public function getClientIp()
    {
        $ipAddresses = $this->getClientIps();

        return $ipAddresses[0];
    }
    public function getClientIps()
    {
        $ip = $this->server->get('REMOTE_ADDR');

        if (!$this->isFromTrustedProxy()) {
            return array($ip);
        }

        return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip);
    }

getClientIp()は、getClientIps()で配列で返ってきたIPの先頭を返しています。
ということは、getClientIps()がIPアドレスを複数返すことがあるんです。

これはどういうケースかというと WAF → LB → アプリケーション のようにプロキシを2段かましたときに、X-Forwarded-ForにIP1, IP2のように記録されるからです。
SymfonyはこんなIPにもきっちり対応して、カンマでIPを分解して処理してくれています。

4
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?