diff --git a/contrib/docker/entrypoint.sh b/contrib/docker/entrypoint.sh index dae6950cc0..e506eba5af 100755 --- a/contrib/docker/entrypoint.sh +++ b/contrib/docker/entrypoint.sh @@ -1,8 +1,34 @@ #!/bin/bash set -e -# Configure /etc/frepple/djangosettings -sed -i "s/SECRET_KEY.*mzit.*i8b.*6oev96=.*/SECRET_KEY = \"$(mktemp -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX)\"/g" /etc/frepple/djangosettings.py +# Configure /etc/frepple/djangosettings — stamp the Django SECRET_KEY. +# +# Honor an externally supplied key (DJANGO_SECRET_KEY environment variable) so +# that deployments which do not persist /etc/frepple keep a stable key across +# restarts. This matters on Kubernetes (see contrib/kubernetes): unlike a Docker +# named volume — which is seeded from the image — a pod's /etc/frepple lives in +# the ephemeral container layer, so it resets to the shipped placeholder on every +# (re)start and the key would otherwise be regenerated each time, invalidating all +# sessions and rotating SECRET_WEBTOKEN_KEY (the Odoo SSO bridge). +# +# When DJANGO_SECRET_KEY is unset, fall back to a random one-time key, as before. +# The substitution only matches the placeholder shipped in the image, so a key +# that has already been stamped (eg on a persisted /etc/frepple) is never touched. +python3 - <<'PYEOF' +import os, re, secrets + +path = "/etc/frepple/djangosettings.py" +key = os.environ.get("DJANGO_SECRET_KEY") or secrets.token_urlsafe(50) +with open(path) as f: + content = f.read() +content = re.sub( + r"SECRET_KEY.*mzit.*i8b.*6oev96=.*", + "SECRET_KEY = " + repr(key), + content, +) +with open(path, "w") as f: + f.write(content) +PYEOF # Djangosettings must be writeable by the web server to support installing&uninstalling apps chmod g+w /etc/frepple/djangosettings.py diff --git a/contrib/kubernetes/frepple-deployment.yaml b/contrib/kubernetes/frepple-deployment.yaml index f8122a21f6..9b25df5d72 100644 --- a/contrib/kubernetes/frepple-deployment.yaml +++ b/contrib/kubernetes/frepple-deployment.yaml @@ -54,6 +54,14 @@ spec: value: frepple - name: POSTGRES_DBNAME value: freppledb + # A pod's /etc/frepple is ephemeral, so without a fixed key the + # entrypoint would mint a new SECRET_KEY on every restart — logging + # everyone out and rotating the Odoo SSO token. Supply a stable key. + - name: DJANGO_SECRET_KEY + valueFrom: + secretKeyRef: + name: frepple-secret + key: DJANGO_SECRET_KEY image: ghcr.io/frepple/frepple-community:latest name: frepple-webserver ports: @@ -93,3 +101,16 @@ spec: app: frepple status: loadBalancer: {} +--- +apiVersion: v1 +kind: Secret +metadata: + name: frepple-secret +type: Opaque +stringData: + # Django secret key. Replace this example value before any real deployment — + # it signs sessions and CSRF tokens (and SECRET_WEBTOKEN_KEY, the Odoo SSO + # bridge). Generate a unique one, eg: + # kubectl create secret generic frepple-secret \ + # --from-literal=DJANGO_SECRET_KEY="$(openssl rand -base64 48)" + DJANGO_SECRET_KEY: "change-me-to-a-unique-random-50-or-more-character-string" diff --git a/doc/installation-guide/advanced/kubernetes.rst b/doc/installation-guide/advanced/kubernetes.rst index b9c9f9b7b4..68d16bfcf3 100644 --- a/doc/installation-guide/advanced/kubernetes.rst +++ b/doc/installation-guide/advanced/kubernetes.rst @@ -23,3 +23,22 @@ The following resources are then defined in your cluster: and the postgresql data (1GB). - A network policy to keep the connection between frepple and its postgres database private. + +- A secret ``frepple-secret`` holding the Django ``DJANGO_SECRET_KEY``. + +Secret key +---------- + +A pod's ``/etc/frepple`` is not persisted, so the Django secret key cannot be +stored there — it would be regenerated on every restart, invalidating all +sessions and rotating the Odoo single-sign-on token. ``frepple-deployment.yaml`` +therefore reads ``DJANGO_SECRET_KEY`` from the ``frepple-secret`` secret, which +keeps it stable across restarts. + +The sample file ships a placeholder value. Replace it with a unique key before +deploying, for example: + +.. code-block:: bash + + kubectl create secret generic frepple-secret \ + --from-literal=DJANGO_SECRET_KEY="$(openssl rand -base64 48)" diff --git a/doc/installation-guide/docker-container.rst b/doc/installation-guide/docker-container.rst index 07dae42710..dc222ce260 100644 --- a/doc/installation-guide/docker-container.rst +++ b/doc/installation-guide/docker-container.rst @@ -105,6 +105,14 @@ The image can be extended and customized using the following: | The default database names are "frepple", "scenario1", "scenario2", "scenario3". | If this argument is passed as "X", the database names will be "X0", "X1", "X2" and "X3". +* The **DJANGO_SECRET_KEY** environment variable sets the Django secret key, used to + sign sessions and CSRF tokens (and SECRET_WEBTOKEN_KEY, the Odoo single-sign-on + token). When left unset a random key is generated each time the container starts. + Persist /etc/frepple (see the volumes below) or set DJANGO_SECRET_KEY explicitly to + keep the key stable across restarts. This matters on Kubernetes, where the container + filesystem is ephemeral: without a fixed key it is regenerated on every pod restart, + logging all users out. + * The following **volumes** let you access all logs, configuration and license files: * | /etc/frepple: @@ -148,6 +156,7 @@ The following environment variables can be set to configure your container: POSTGRES_PORT: 5432 POSTGRES_USER: "frepple" POSTGRES_PASSWORD: "frepple" + DJANGO_SECRET_KEY: "" # random per start when empty; set it (or persist /etc/frepple) to keep sessions across restarts FREPPLE_DATE_STYLE: "year-month-day" FREPPLE_DATE_STYLE_WITH_HOURS: "false" FREPPLE_TIME_ZONE: "UTC"