Skip to content

Commit 397a51b

Browse files
ericsmallingclaude
andcommitted
jenkins: envsubst allowlists, chmod 600 .env, tighten Django defaults
Three Copilot findings from the thirteenth pass on PR #330: - harbor/deploy.sh: pass an explicit allowlist to every `envsubst` call. Without it, envsubst expands every `$VAR` sequence in the template, so any future secret containing a `$`-prefixed substring (HARBOR_ADMIN_PASSWORD, PULL_PASS) could be silently mangled. Now each call lists just the variables it actually templates. - setup.sh: `chmod 600 .env` after persisting config. Once HARBOR_ADMIN_PASSWORD lands here it's secret material; a default umask of 022 would leave it group/world-readable. - python312-pip-django/app.py: read SECRET_KEY from DJANGO_SECRET_KEY with a demo-only fallback, and narrow ALLOWED_HOSTS from `["*"]` to `["localhost", "127.0.0.1", "testserver"]`. The Test stage's Django Client uses Host: testserver, so the smoke test still passes (verified locally against an in-memory venv). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8977a1b commit 397a51b

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

jenkins/apps/python312-pip-django/app.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,18 @@
1313

1414
settings.configure(
1515
DEBUG=False,
16-
SECRET_KEY="demo-not-secret",
16+
# Demo-only fallback. In any non-demo deployment, set DJANGO_SECRET_KEY
17+
# in the environment — Django's signed cookies, password reset tokens,
18+
# and CSRF tokens all derive from this key, so a static value defeats
19+
# those protections.
20+
SECRET_KEY=os.environ.get("DJANGO_SECRET_KEY", "demo-not-secret"),
1721
ROOT_URLCONF=__name__,
18-
ALLOWED_HOSTS=["*"],
22+
# Localhost + the test client's default host ("testserver") only.
23+
# ALLOWED_HOSTS=["*"] would let the container respond to any Host
24+
# header — a Host-header injection risk if the image ever escaped its
25+
# localhost demo context. The pipeline's smoke test uses Client().get(),
26+
# which sends Host: testserver, so we include that explicitly.
27+
ALLOWED_HOSTS=["localhost", "127.0.0.1", "testserver"],
1928
INSTALLED_APPS=["django.contrib.contenttypes", "django.contrib.auth"],
2029
MIDDLEWARE=[],
2130
# In-memory SQLite — contrib.contenttypes and contrib.auth declare models

jenkins/harbor/deploy.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,15 @@ else
4848
fi
4949

5050
echo "==> Rendering manifests with REGISTRY_URL=${REGISTRY_URL}..."
51-
envsubst < cg/manifests/deploy-ingress-nginx.template > cg/manifests/deploy-ingress-nginx.yaml
52-
envsubst < cg/helm/values.template > cg/helm/values.yaml
51+
# Pass an explicit allowlist to envsubst so it only substitutes the variables
52+
# we intend to template. Without one it expands every `$VAR` in the file,
53+
# including anything `$FOO`-shaped that happens to appear inside a secret
54+
# (e.g. a future HARBOR_ADMIN_PASSWORD containing `$`) — corrupting the
55+
# rendered output. The allowlist syntax is `'$NAME1 $NAME2 ...'`.
56+
envsubst '$REGISTRY_URL' \
57+
< cg/manifests/deploy-ingress-nginx.template > cg/manifests/deploy-ingress-nginx.yaml
58+
envsubst '$REGISTRY_URL $HARBOR_ADMIN_PASSWORD' \
59+
< cg/helm/values.template > cg/helm/values.yaml
5360

5461
# Namespaces (idempotent).
5562
kubectl get ns ingress-nginx >/dev/null 2>&1 || kubectl create ns ingress-nginx
@@ -92,7 +99,11 @@ done
9299

93100
echo "==> Configuring Harbor with Terraform (cgr.dev proxy registry + cgr-proxy project)..."
94101
export PULL_USER PULL_PASS
95-
envsubst < terraform/terraform.templatevars > terraform/terraform.tfvars
102+
# Same allowlist treatment as the manifests above — keeps `$`-containing
103+
# secrets (e.g. HARBOR_ADMIN_PASSWORD, PULL_PASS) from being interpreted as
104+
# `$VAR` references and silently corrupting the tfvars output.
105+
envsubst '$ORG_NAME $PULL_USER $PULL_PASS $HARBOR_ADMIN_PASSWORD' \
106+
< terraform/terraform.templatevars > terraform/terraform.tfvars
96107
( cd terraform
97108
terraform init -input=false -upgrade
98109
terraform apply -input=false -auto-approve

jenkins/setup.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,11 @@ if [[ "$HARBOR_ENABLED" == "true" ]]; then
358358
validate_env_value HARBOR_ADMIN_PASSWORD "$HARBOR_ADMIN_PASSWORD" || exit 1
359359
update_env HARBOR_ADMIN_PASSWORD "$HARBOR_ADMIN_PASSWORD"
360360
fi
361+
# Tighten .env perms unconditionally — once HARBOR_ADMIN_PASSWORD lands
362+
# here it's a secret, and any future variables we persist could be too.
363+
# 600 = host-user-only; both `bash source` and `docker compose --env-file`
364+
# only need the invoking user to read it.
365+
chmod 600 .env
361366

362367
# ---- Phase 2: (re)create Jenkins with the new env so its OIDC signing key
363368
# is the one we'll upload to Chainguard in Phase 3. ----

0 commit comments

Comments
 (0)