Symfony on Kubernetes - New Session is started on each request

Viewed 781

Having an app running perfectly on my local with docker.

When I deploy it, I can't get why a new session is started on each request. It seems it can't come from the code since it's exactly the same.

The session is stored on a redis DB (I have the same bug if I use the filesystem session). It's there I can see all the new session created. (see last code block).

From the log I can clearly identify that the $request->getSession()->getId() changes on each request but not the PHPSESSID cookie.

For example:

First request

[2020-11-02 15:03:59] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\Controller\\SecurityController::login"},"request_uri":"https://foo.bar.dev/login","method":"POST"} []
[2020-11-02 15:03:59] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
[2020-11-02 15:03:59] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2020-11-02 15:03:59] app.DEBUG: [LoginFormAuthenticator::supports] $request session id => 6491ddf4e8f3e2eaa22b44b3a98c094a [] []
[2020-11-02 15:03:59] app.DEBUG: [LoginFormAuthenticator::supports] $_COOKIE =>  {"PHPSESSID":"87cf6185b652f8d713c45031ebe6d8a4"} []

Second one

[2020-11-02 15:04:33] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\Controller\\SecurityController::login"},"request_uri":"https://foo.bar.dev/login","method":"POST"} []
[2020-11-02 15:04:33] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
[2020-11-02 15:04:33] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2020-11-02 15:04:33] app.DEBUG: [LoginFormAuthenticator::supports] $request session id => 41b08dac8a803337a48dca7d5b33b840 [] []
[2020-11-02 15:04:33] app.DEBUG: [LoginFormAuthenticator::supports] $_COOKIE =>  {"PHPSESSID":"87cf6185b652f8d713c45031ebe6d8a4"} []

KUBERNETES

ingress-nginx.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: secured-front
  namespace: foo-apis-dev
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/affinity-mode: "persistent"
    nginx.ingress.kubernetes.io/session-cookie-name: "PHPSESSID"
    nginx.ingress.kubernetes.io/session-cookie-path: "/"
    nginx.ingress.kubernetes.io/session-cookie-samesite: "Lax"
    nginx.ingress.kubernetes.io/session-cookie-expires: "172800000"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "172800000"
spec:
  tls:
    - hosts:
        - bar.foo.dev
      secretName: tls-secret
  rules:
    - host: bar.foo.dev
      http:
        paths:
          - backend:
              serviceName: bar-nginx
              servicePort: 80
            path: /(.*)

Symfony APP

security.yaml

[...]
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: lazy
            pattern: ^/.*
            logout:
                path: app_logout
                target: login

            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
[...]

services.yaml

[...]
    Redis:
        class: Redis
        calls:
            - connect:
                  - '%env(REDIS_HOST)%'
                  - '%env(int:REDIS_PORT)%'

    Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
        arguments:
            - '@Redis'
            - { prefix: 'admin_phpsess_' }
[...]

packages.framework.yaml

[...]
    session:
        handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
        cookie_secure: auto
        cookie_samesite: lax
[...]

REDIS

127.0.0.1:6379> KEYS *admin*
1) "admin_phpsess_245e4a79fe35e2320943770061884c24"
2) "admin_phpsess_0ff29464322b3c2cfc5d8f5fd323ef75"
3) "admin_phpsess_26812c17f93a5d28a71853b77ac85386"
4) "admin_phpsess_7fbae6f0b1fdbe9576e41c9eee2cd60f"

VERSIONS:

  • PHP 7.4.12
  • Symfony 4.4
  • Kubernetes 1.17.9
  • redis (pecl) 5.3.2

IMPORTANT NOTE

The issue was about the redis configuration.

I took the step of using PdoSessionHandler and it works. The problem comes from Redis and / or Kubernetes, I have been trying for 2 hours to point out the configuration which generates this bug but impossible for now.

1 Answers

Set nginx.ingress.kubernetes.io/session-cookie-path: "/" to instead of the /(.*) regex.

Symfony's default parameter for the framework.session.cookie_path is /, too. (documentation)

Further change nginx.ingress.kubernetes.io/session-cookie-name to "INGRESSCOOKIE" or omit the annotation.

The session cookie of the nginx ingress is used to determine which pod the initial request was routed to and route any further requests to the exact same pod.

It's a different session cookie than the one of the PHP session and therefore should have a different cookie name. The ingress session cookie get's created and does likely overwrite the php session cookie with your configuration.

If you're using a redis session there should be no need to route continuous requests to the same pod in the cluster.

Related