phpdockersymfonydocker-composemercure

Trying to use mercure with symfony (docker config)


I'm integrating Mercure into my Symfony app and running it with Docker Compose. When I start the container, I get the following warning:

WARN[0002] The "MERCURE_JWT_SECRET" variable is not set. Defaulting to a blank string. WARN[0002] The "MERCURE_JWT_SECRET" variable is not set. Defaulting to a blank string.

my image set up

    mercure:
        image: dunglas/mercure
        restart: unless-stopped
        container_name: mercure
        environment:
            # Uncomment the following line to disable HTTPS,
            SERVER_NAME: ':3000'
            MERCURE_PUBLISHER_JWT_KEY: ${MERCURE_JWT_SECRET}
            MERCURE_SUBSCRIBER_JWT_KEY: ${MERCURE_JWT_SECRET}
            # Set the URL of your Symfony project (without trailing slash!) as value of the cors_origins directive
            MERCURE_EXTRA_DIRECTIVES: |
                cors_origins http://127.0.0.1:8081
        command: /usr/bin/caddy run --config /etc/caddy/dev.Caddyfile
        healthcheck:
            test: ["CMD", "curl", "-f", "http://localhost/healthz"]
            timeout: 5s
            retries: 5
            start_period: 60s
        ports:
            -   "3000:3000"
        env_file:
            - ./symfony/.env.local
        volumes:
            - mercure_data:/data
            - mercure_config:/config

my env.local

MERCURE_URL=https://localhost:3000/.well-known/mercure
MERCURE_PUBLIC_URL=https://localhost:3000/.well-known/mercure
MERCURE_JWT_SECRET=!MyMercureHubJWTSecretKey!

Solution

  • The values in an env_file: are directly injected into the container where they're used. For this setup, I'd set the JWT signing and verification keys directly in the external environment file and not mention them in the per-container environment:.

    # .env.local
    MERCURE_URL=https://localhost:3000/.well-known/mercure
    MERCURE_PUBLIC_URL=https://localhost:3000/.well-known/mercure
    MERCURE_PUBLISHER_JWT_KEY=!MyMercureHubJWTSecretKey!
    MERCURE_SUBSCRIBER_JWT_KEY=!MyMercureHubJWTSecretKey!
    
    environment:
      SERVER_NAME: ':3000'
      MERCURE_EXTRA_DIRECTIVES: |
        cors_origins http://127.0.0.1:8081
      # But not the JWT-related variables, these are in `env_file:`
    env_file:
      - ./symfony/.env.local
    

    (Note that JWT HMAC signing has a significant intrinsic security problem: anyone who can verify a token can also forge their own token for any identity they'd like. For anything larger than a toy application I'd recommend using RSA signing, which will have different keys for signing and verification.)

    What's happening here is that Compose has two separate stages of environment-variable handling. First Compose uses the host environment, a .env file, and any docker compose --env-file files, and uses these to replace any environment-variable references in the Compose file. Second it reads in any env_file: values and directly uses that to set environment variables for the containers where they're used. But env_file: isn't read until after variables are substituted in the Compose file, so you can't use env_file: values for replacement in environment:.

    Correspondingly, your initial setup would also work if you moved the definition of MERCURE_JWT_SECRET to a new separate top-level .env file, in the same directory as the docker-compose.yml file. This would allow the variable substitution in the Compose file to work, but that variable would not be directly visible in container(s) unless you forwarded it with its own environment: definition.