mkucej / i-librarian-free

I, Librarian - open-source version of a PDF managing SaaS.
https://i-librarian.net
GNU General Public License v3.0
237 stars 28 forks source link

Setup behind reverse proxy with https redirect #54

Closed arunoruto closed 3 years ago

arunoruto commented 3 years ago

I wanted to host a docker container of i-librarian using Traefik, a reverse proxy. The proxy uses let's encrypt certificates to encrypt the traffic of web requests to the server. When trying to host librarian, I get a lot of Mixed content errors in the developer console in chrome. This is due to making http requests to an endpoint, which now should be https. Is there a way to fix this? I suppose somewhere in PHP one can change it. I will look it up myself but wanted to let you know about this.

mkucej commented 3 years ago

Can your reverse proxy send X-Forwarded-Proto header?

mkucej commented 3 years ago

If you have the header set up, try this Librarian\Http\Url class: https://github.com/mkucej/i-librarian-free/blob/master/classes/http/url.php

<?php

namespace Librarian\Http;

use Librarian\Http\Client\Psr7\ServerRequest as Request;

/**
 * URL related methods.
 */
final class Url {

    /**
     * @var Request
     */
    private $request;

    /**
     * @var array Server super globals.
     */
    private  $server;

    public function __construct(Request $request) {

        $this->request = $request;
        $this->server  = $this->request->getServerParams();
    }

    /**
     * Get base URL (where the index.php bootstrap file is).
     *
     * @return string
     */
    public function base(): string {

        // Construct whole URL from globals using Guzzle.
        $url = Request::getUriFromGlobals();

        // Get base path (ends with trailing slash).
        $path = $url->getPath();
        $base_path = strpos($path, 'index.php') === false ? $path : strstr($path, 'index.php', true);

        // Add port.
        $port = $url->getPort();
        $port = $port === '80' || $port === '443' || empty($port) ? '' : ":{$port}";

        // Add scheme. Check for the scheme that client sent to a reverse proxy.
        $scheme = !empty($this->server['HTTP_X_FORWARDED_PROTO']) ? strtolower($this->server['HTTP_X_FORWARDED_PROTO']) : $url->getScheme();

        return "{$scheme}://{$url->getHost()}{$port}{$base_path}";
    }

    /**
     * Get the URL path (after index.php). No leading slash.
     *
     * @return string
     */
    public function path(): string {

        return isset($this->server['PATH_INFO']) ? substr($this->server['PATH_INFO'], 1) : '';
    }
}
arunoruto commented 3 years ago

Can your reverse proxy send X-Forwarded-Proto header?

Using this documentation of traefik it seems to be possible.

If you have the header set up, try this Librarian\Http\Url class: https://github.com/mkucej/i-librarian-free/blob/master/classes/http/url.php

<?php

namespace Librarian\Http;

use Librarian\Http\Client\Psr7\ServerRequest as Request;

/**
 * URL related methods.
 */
final class Url {

    /**
     * @var Request
     */
    private $request;

    /**
     * @var array Server super globals.
     */
    private  $server;

    public function __construct(Request $request) {

        $this->request = $request;
        $this->server  = $this->request->getServerParams();
    }

    /**
     * Get base URL (where the index.php bootstrap file is).
     *
     * @return string
     */
    public function base(): string {

        // Construct whole URL from globals using Guzzle.
        $url = Request::getUriFromGlobals();

        // Get base path (ends with trailing slash).
        $path = $url->getPath();
        $base_path = strpos($path, 'index.php') === false ? $path : strstr($path, 'index.php', true);

        // Add port.
        $port = $url->getPort();
        $port = $port === '80' || $port === '443' || empty($port) ? '' : ":{$port}";

        // Add scheme. Check for the scheme that client sent to a reverse proxy.
        $scheme = !empty($this->server['HTTP_X_FORWARDED_PROTO']) ? strtolower($this->server['HTTP_X_FORWARDED_PROTO']) : $url->getScheme();

        return "{$scheme}://{$url->getHost()}{$port}{$base_path}";
    }

    /**
     * Get the URL path (after index.php). No leading slash.
     *
     * @return string
     */
    public function path(): string {

        return isset($this->server['PATH_INFO']) ? substr($this->server['PATH_INFO'], 1) : '';
    }
}

But using this fix solved the problem without modifying the headers myself. I checked in the network section of chrome and traefik doesnt send automatically an X-Forwarded-Proto header. I just copied the provided code in a new file and mounted it instead of the actual file. It works for both https (behind the reverse proxy) and http with a direct access. Thanks for the fix!