Unable to secure site with SSL with pound reverse proxy server

Hi everyone!

After spending almost the entirety of yesterday searching and experimenting with different methods, I am hoping you experts might be able to help me out on this. As some background, I am a tech literate guy, but not to a great extent.

I have Invoice Ninja running in a Bitnami VM behind a Pound reverse proxy server. When I have this set up and go to any page, my browser (Chrome) states that the website is attempting to run scripts from unauthenticated sources. I have the pound server redirecting all HTTP traffic to the HTTPS address. When I enable the option in the .env file to require HTTPS, I get an error about a redirect loop. When I set this to false, I get the page with almost no styling stating that I am trying to run scripts from unauthenticated sources.

Some of the things that I tried doing was setting the .env file’s address to use HTTPS, I’ve tried to add the SESSION_ENCRYPT=true and SESSION_SECURE=true lines to the .env file without success.

If there are any suggestions you can provide, I would be immensely appreciative. This is an amazing project and I hope that I can get this working for production.

Thank you very much!

Chris

Try setting a value for TRUSTED_PROXIES in your .env file.

Here’s the code from the top of app/Http/Middleware/StartupCheck.php

    // Set up trusted X-Forwarded-Proto proxies
    // TRUSTED_PROXIES accepts a comma delimited list of subnets
    // ie, TRUSTED_PROXIES='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
    if (isset($_ENV['TRUSTED_PROXIES'])) {
        Request::setTrustedProxies(array_map('trim', explode(',', env('TRUSTED_PROXIES'))));
    }

Hi Hillel! Thank you for your response! I added TRUSTED_PROXIES=‘192.168.1.196/16’ to the .env file and restarted the server and have not had any change. For reference, here’s what my .env file looks like.

APP_ENV=production
APP_DEBUG=false
APP_URL=https://billing.XXX.com
APP_CIPHER=rijndael-256
APP_KEY=eKSBhF9xgPMPPR431yIitaYCghIEsI9H

DB_TYPE=mysql
DB_STRICT=false
DB_HOST=localhost
DB_DATABASE=bitnami_invoiceninja
DB_USERNAME=bn_invoiceninja
DB_PASSWORD=XXX

MAIL_DRIVER=smtp
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAIL_HOST=smtp.gmail.com
MAIL_USERNAME=XXX
MAIL_FROM_ADDRESS=XXX
MAIL_FROM_NAME=XXX
MAIL_PASSWORD=XXX

PHANTOMJS_CLOUD_KEY=‘a-demo-eKSBhF9xgPMPPR431yIitaYCghIEsI9H-with-low-quota-per-ip-address’
LOG=single
REQUIRE_HTTPS=true

GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET
GOOGLE_OAUTH_REDIRECT=http://ninja.dev/auth/google

TRUSTED_PROXIES=‘192.168.1.196/16’

SESSION_ENCRYPT=true
SESSION_SECURE=true

The login page still just consists of the text boxes without the styling. I checked the source of the page and its still trying to point to the HTTP address of everything. Any other suggestions or am I not understanding this option?

I also meant to mention that 192.168.1.196 is the reverse proxy server.

If there’s no styling that makes me think this is a problem with the app itself, not the reverse proxy.

Can you check either your browser’s console or storage/logs/laravel.log for details on the error.

The last error block in the laravel.log file is below. It There is one identical error like this earlier but given how often the page is being accessed, it does not look like any relevant errors are popping up in this log. What I notice as interesting is that when I inspect the page source of the login page, I see that all references are being made with HTTP as opposed to HTTPS. Is there a way to hardcode that HTTP so that it is not being dynamically generated?

[2016-01-19 00:35:03] production.ERROR: exception ‘Illuminate\Session\TokenMismatchException’ in /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php:2550
Stack trace:
#0 /opt/bitnami/apps/invoiceninja/htdocs/app/Http/Middleware/VerifyCsrfToken.php(39): Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#1 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(9236): App\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#2 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(12416): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#3 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(9236): Illuminate\View\Middleware\ShareErrorsFromSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#4 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(11106): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#5 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(9236): Illuminate\Session\Middleware\StartSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#6 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(12118): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#7 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(9236): Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle(Object(Illuminate\Http\Request), Object(Closure))
#8 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(12066): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#9 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(9236): Illuminate\Cookie\Middleware\EncryptCookies->handle(Object(Illuminate\Http\Request), Object(Closure))
#10 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(2589): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#11 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(9236): Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle(Object(Illuminate\Http\Request), Object(Closure))
#12 [internal function]: Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#13 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(9227): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#14 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(1996): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#15 /opt/bitnami/apps/invoiceninja/htdocs/vendor/compiled.php(1983): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#16 /opt/bitnami/apps/invoiceninja/htdocs/public/index.php(53): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#17 {main}

When I tell the browser to allow the unsafe scripts to run, the page renders normally. Also, when I direct port 443 to the VM directly, everything works great. It’s only when funneled through the reverse proxy that these issues arise. As I am planning to host other sites, forwarding standard port 443 directly to the VM is not an option.

To force HTTP you could set REQUIRE_HTTPS=true in your .env file.

You could try clearing you browser cookies to resolve TokenMismatchException errors.

Setting REQUIRE_HTTPS=true generates a redirect loop. The HTTPS request comes in through the reverse proxy server, decrypted, and sent via HTTP to the Bitnami VM hosting Invoice Ninja. My guess is that Invoice Ninja sees the funneled decrypted traffic as HTTP and requires HTTPS. This new request loops through the reverse proxy server where it is decrypted into HTTP and hits Invoice Ninja again generating this same loop.

I’m not sure, you may be find more info online if you search for ‘laravel reverse proxy’.

Got it! I’m so happy I can barely contain myself! I want to post my configuration here so anyone in my shoes can fix this and maybe help the community out!

Instead of searching for reverse proxy information specifically with Invoice Ninja, I followed your advise and searched ‘laravel reverse proxy’ like you suggested. That led me to this page: https://kura.io/2011/09/29/load-balancing-httphttps-with-pound-on-debian-6ubuntu/

This web page told me to add the
AddHeader "X-Forwarded-Proto: https"
option to my Pound server configuration. This means that my configuration for pound looks like this:

ListenHTTPS
        Address 0.0.0.0
        Port    443
        AddHeader "X-Forwarded-Proto: https"
        Cert    "/path/to/cert"
        CAList  "/path/to/cabundle"

        Service
                HeadRequire "Host:.billing.xxx.com.*"
                BackEnd
                        Address 192.168.1.197
                        Port 80
                End
        End
End

As soon as I implemented this and restarted the server, everything worked beautifully. Thank you so much for your help Hillel and I hope this helps anyone who is running into this issue like me! I look forward to pushing Invoice Ninja into production!

Oh also, here’s what my .env file ended up looking like:

APP_ENV=production
APP_DEBUG=false
APP_URL=https://billing.xxx.com
APP_CIPHER=rijndael-256
APP_KEY=****************************

DB_TYPE=mysql
DB_STRICT=false
DB_HOST=localhost
DB_DATABASE=bitnami_invoiceninja
DB_USERNAME=*********************
DB_PASSWORD=*********************

MAIL_DRIVER=smtp
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAIL_HOST=smtp.gmail.com
MAIL_USERNAME=xxx
MAIL_FROM_ADDRESS=xxx
MAIL_FROM_NAME=xxx
MAIL_PASSWORD=xxx

PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
LOG=single
REQUIRE_HTTPS=true

GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET
GOOGLE_OAUTH_REDIRECT=http://ninja.dev/auth/google

TRUSTED_PROXIES='192.168.1.196/24'

SESSION_ENCRYPT=true
SESSION_SECURE=true

Thanks for sharing your working configuration, happy to hear your time hasn’t been wasted.

Let us know if you run into any other issues.