Search code examples
phplaravellaravel-8mitmproxy

Laravel keeps returning "419 Page Expired" on POST requests in reverse proxy setup


In order to create a production alike development setup we set the local dev environment with a ssl-terminating reverse proxy up using mitmproxy and docker-compose. So the schematics of our setup looks like this:

                       :443             :8000
 ┌───────────┐         ┌───────┐        ┌─────┐
 │ localhost ├─────────┤ proxy ├────────┤ app │
 └───────────┘         └───▲───┘        └─────┘
  dns mimicry via          │
  /etc/hosts:           upstream: http://app:8000

  localhost.dev 127.0.0.1

So i access the app surfing to https://localhost.dev which is successful being upstreamed by mitmproxy (in reverse operating mode) to http://app:8000.

The i guess most important env variables are set as follows:

APP_URL: https://localhost.dev
SANCTUM_STATEFUL_DOMAINS: localhost.dev,app,localhost
SESSION_DRIVER: cookie
SESSION_COOKIE: dev
SESSION_DOMAIN: .localhost.dev

Every kind of request works fine, but all POST requests fail. We did not mix up the web- and api-middleware, so the issue is we actually can't login in the dev environment because the Blade based login-request:

POST http://localhost.dev:8000/signin HTTP/2.0
user-agent: Mozilla/5.0 (X11; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
accept-language:    de-DE,en-US;q=0.7,en;q=0.3
accept-encoding:    gzip, deflate, br
content-type:   application/x-www-form-urlencoded
content-length: 103
origin: https://localhost.dev
referer:    https://localhost.dev/signin
cookie: XSRF-TOKEN=eyJpdiI6IjczeWNlSkpQQz<...>
cookie: dev=eyJpdiI6Im9nTFNDZ3IvVWxtVlNQW<...>
cookie: 6z6cjWPCXukcTJxmZr9QvJyFEraMhksxP<...>
cookie: _pk_id.39.560d=8d63f5a6d47b0b9a.1633971639.
cookie: _pk_ses.39.560d=1
cookie: mtm_consent=1633971639118
cookie: hasConsent=true
cookie: FCGnj1jUBGwy2ndzVUK45gGCWyB1rqgWZ<...>
upgrade-insecure-requests:  1
sec-fetch-dest: document
sec-fetch-mode: navigate
sec-fetch-site: same-origin
sec-fetch-user: ?1
sec-gpc:    1
te: trailers
X-Forwarded-Proto:  https
_token:   dEmnQkCa6bq7E30M30BBKbqm815WoG9V17<...>
email:    me@this.com
password: 123456

fails:

HTTP/1.1 419 unknown status
Host:   localhost.dev
Date:   Mon, 11 Oct 2021 17:01:07 GMT
Connection: close
X-Powered-By:   PHP/7.4.24
Cache-Control:  no-cache, private
date:   Mon, 11 Oct 2021 17:01:07 GMT
Content-Type:   text/html; charset=UTF-8
Set-Cookie: dev=eyJpdiI6IlVpUzNid0xIbjRPT<...>; expires=Mon, 11-Oct-2021 19:01:07 GMT; Max-Age=7200; path=/; domain=localhost.dev; httponly; samesite=lax
Set-Cookie: x1UxyIEmxnJDgdt7e5hVCvjSwD0J5<...>; expires=Mon, 11-Oct-2021 19:01:07 GMT; Max-Age=7200; path=/; domain=localhost.dev; httponly; samesite=lax

I snipped (<...>) hopefully useless gibberish.

I tried multiple different combinations of domains and config settings - all keeps failing. I would be very thankful for any hint why this keeps failing.

Feel free to request any further information.

TrustedProxies.php
protected $proxies = "*";
protected $headers =
  RequestAlias::HEADER_X_FORWARDED_FOR |
  RequestAlias::HEADER_X_FORWARDED_HOST |
  RequestAlias::HEADER_X_FORWARDED_PORT |
  RequestAlias::HEADER_X_FORWARDED_PROTO;

Solution

  • After investing lots of time i was able to pin this down to the following symptom:

    The request did only contain one of the sent cookies. Which one was somehow random, but it was never the session cookie.

    Therefore the session could not be reused and was "started" every request.

    mitmproxy in reverse mode has likely a bug

    I removed mitmproxy from the stack and added Caddy instead:

    docker-compose.yml
    # ...
      proxy:
        image: caddy:latest
        cap_add:
          - NET_BIND_SERVICE
        volumes:
          - .reverse/Caddyfile:/etc/caddy/Caddyfile
          - .reverse/data:/data
        ports:
          - "443:443"
    # ...
    
    .reverse/Caddyfile
    https://localhost.dev {
      tls internal
      reverse_proxy http://app:8000
    }
    

    After installing the issued cert (.reverse/data/caddy/cerificates/pki/authorities/local/root.crt) in the browser everything worked like a charm.

    I would state that mitmproxy does not pass all cookies from the request.