Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f3d2a24
Split geonode PVC into individual PVCs, to allow more granular handli…
jwkaltz May 29, 2026
4e5152e
Hardening: add security context; split geonode PVC into individual PV…
jwkaltz May 29, 2026
16d5254
Hardening nginx: use non-root port; do not mount favicon here as that…
jwkaltz May 29, 2026
40e224d
Remove cron command, cron should not be used within a K8S container
jwkaltz May 29, 2026
78028e4
Hardening: add security context; split geonode PVC into individual PV…
jwkaltz May 29, 2026
08396f0
Hardening: add security context; split geonode PVC into individual PV…
jwkaltz May 29, 2026
e3011b2
no more need to mount pvc-data into build statics job (review comment…
jwkaltz Jun 2, 2026
d4ffcd2
Conditional StorageClassName (as per gemini-code-assist suggestion)
jwkaltz Jun 2, 2026
07efce2
allow configuring individual storage sizes for each PVC (as per gemin…
jwkaltz Jun 2, 2026
367cf0a
make nginx fsGroup configurable (as per gemini-code-assist suggestion)
jwkaltz Jun 2, 2026
7c579b9
Reenable custom favicon loading, but without extra mount. Change favi…
jwkaltz Jun 2, 2026
d9c3e11
Security improvements as suggested by gemini-code-review
jwkaltz Jun 2, 2026
8b5d6c5
Add securitycontext to cleanup-job
jwkaltz Jun 2, 2026
eeeb54b
more robust values handling, as per gemini-code-assist suggestion
jwkaltz Jun 2, 2026
8cf1094
Make geonode pod (geonode, celery) runnable as non-root
jwkaltz Jun 4, 2026
fa901aa
Make chart more robust, as per gemini-code-assist review
jwkaltz Jun 5, 2026
27c119a
Make chart more robust, as per gemini-code-assist review
jwkaltz Jun 5, 2026
97a6930
make code clearer
jwkaltz Jun 5, 2026
4bb5a80
Make chart more robust, as per gemini-code-assist suggestions
jwkaltz Jun 5, 2026
10b1583
Document how to run geonode/geoserver as non-root
jwkaltz Jun 5, 2026
0cf6453
Add migration notice
jwkaltz Jun 5, 2026
af92aea
Apply upstream change to PR, and remove superfluous parameter, we sho…
jwkaltz Jun 8, 2026
24a945d
By default, allow root usage for geoserver pod (to be able to use cur…
jwkaltz Jun 8, 2026
ca32829
Make value handling resistent to boolean and integer values
jwkaltz Jun 8, 2026
5f59315
Integrate upstream PR https://github.com/GeoNode/geonode/pull/14307
jwkaltz Jun 8, 2026
7d76aaf
Mount the statics PVC as readOnly: true in the geoserver deployment (…
jwkaltz Jun 19, 2026
b17b311
Strip setuid bits (from Review comment)
jwkaltz Jun 19, 2026
380db64
start implementing readOnlyRootFilesystem (Review comment)
jwkaltz Jun 19, 2026
707d8a0
continue implementing readOnlyRootFilesystem (Review comment)
jwkaltz Jun 22, 2026
c2a30e2
implement readOnlyRootFilesystem (Review comment) for geoserver, memc…
jwkaltz Jun 22, 2026
1277752
implement readOnlyRootFilesystem (Review comment) for cleanup job
jwkaltz Jun 22, 2026
b809473
implement readOnlyRootFilesystem (Review comment) for remaining job c…
jwkaltz Jun 22, 2026
3d298fd
Fix bug about hard-coded uid/gid (from review comment)
jwkaltz Jun 23, 2026
fdb9044
reduce access rights (from review comment)
jwkaltz Jun 23, 2026
299c167
consistently use fsGroupChangePolicy (from review comment)
jwkaltz Jun 23, 2026
9988ff5
More robust Dockerfile
jwkaltz Jul 7, 2026
baa230a
Improve security compliance of postgres pod by using all available op…
jwkaltz Jul 7, 2026
861993d
Allow postgres to start with full securitycontext despite postgres-op…
jwkaltz Jul 7, 2026
dd46de7
Add documentation for postgres securitycontext issue
jwkaltz Jul 7, 2026
9b3067c
Merge branch 'main' into hardening
mwallschlaeger Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

- [GeoWhat?](#Geonode)
- [Geonode-k8s](#geonode-k8s)
- [Install Guilde](#install)
- [Install Guide](#install)

**Homepage:** <https://github.com/GeoNodeUserGroup-DE/geonode-k8s>

Expand Down Expand Up @@ -69,6 +69,31 @@ Define your own values.yaml to configure your geonode installation. Use the [doc
vi my-values.yaml
```

## Hardened K8S environments

By default, this Helm Chart is intended to run in hardened K8S environments, notably with non-root permissions. To use it, ensure that your storage will be writeable by the geonode technical user; UID and GID can be configured in the values file.

There are two exceptions to this rule:
- the `geonode-postgres` Pod does not have full securityContext out-of-the-box, because the subchart `postgres-operator` does not support that yet. A workaround is available if you want the full securityContext, for that, set `postgres.kyvernoSecurityContext`to `enabled` in your values YAML.
- the `geonode/geoserver` Pod, because the current `geonode/geoserver` Docker Image does not support running as non-root out-of-the-box, therefore, default chart settings for this image are set to run as root. If you wish to run this Pod as non-root, proceed as follows:

Create a custom image with the provided script:
```bash
cd geoserver-nonroot
./build.sh
```
And then override the settings in your values file, for example:
```bash
geoserver:
image:
name: geonode/geoserver-nonroot
securityContext:
runAsNonRoot: true
fsGroup: 1000
runAsUser: 1000
runAsGroup: 1000
```

## Install chart

```bash
Expand All @@ -81,6 +106,11 @@ helm upgrade --cleanup-on-fail --install --namespace geonode --create-namespace
helm delete --namespace geonode geonode geonode
```

## Migrating from earlier versions of this Helm Chart

The hardened version of this helm chart introduced a split from one common PVC for geonode to four distinct PVC.
When upgrading from earlier versions of the helm chart, you may need to copy user manually to the new PVC.

## Contribution

### Create an Issue
Expand Down
1 change: 1 addition & 0 deletions charts/geonode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ If `geonode.gatewayApi.hostnames` is left empty, the route defaults to
| postgres.external.ssl | string | `"prefer"` | |
| postgres.geodata_databasename_and_username | string | `"geodata"` | geoserver database name and username |
| postgres.geonode_databasename_and_username | string | `"geonode"` | geonode database name and username |
| postgres.kyvernoSecurityContext.enabled | bool | `false`| Provide full securityContext to postgres Pod via Kyverno, as postgres-operator does not yet fully support securityContext |
| postgres.operator.allowedSourceRanges | list | `[]` | when one or more load balancers are enabled for the cluster, this parameter defines the comma-separated range of IP networks (in CIDR-notation). The corresponding load balancer is accessible only to the networks defined by this parameter. Optional, when empty the load balancer service becomes inaccessible from outside of the Kubernetes cluster. |
| postgres.operator.annotations | object | `{}` | additional annotation for postgresql object |
| postgres.operator.clone | object | `{}` | |
Expand Down
72 changes: 72 additions & 0 deletions charts/geonode/files/celery-cmd-temporary-fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# A configurable celery command.
# Luca Pasquali <luca.pasquali@geosolutionsgroup.com>
CELERY_BIN=${CELERY_BIN:-"$(which celery||echo celery)"}
CELERY_APP=${CELERY_APP:-"geonode.celery_app:app"}
CELERY__STATE_DB=${CELERY__STATE_DB:-"/mnt/volumes/statics/worker@%h.state"}
# expressed in KB
CELERY__MAX_MEMORY_PER_CHILD=${CELERY__MAX_MEMORY_PER_CHILD:-"200000"}
CELERY__AUTOSCALE_VALUES=${CELERY__AUTOSCALE_VALUES:-"10,5"}
CELERY__MAX_TASKS_PER_CHILD=${CELERY__MAX_TASKS_PER_CHILD:-"10"}
CELERY__OPTS=${CELERY__OPTS:-"--without-gossip --without-mingle -Ofair -E"}
CELERY__LOG_LEVEL=${CELERY__LOG_LEVEL:-"ERROR"}
CELERY__LOG_FILE=${CELERY__LOG_FILE:-"/var/log/celery.log"}
CELERY__WORKER_NAME=${CELERY__WORKER_NAME:-"worker1@%h"}
CELERY__WORKER_CONCURRENCY=${CELERY__WORKER_CONCURRENCY:-"4"}

# Celery beat settings
CELERY__BEAT_SCHEDULE=${CELERY__BEAT_SCHEDULE:-"celery.beat:PersistentScheduler"}
CELERY__BEAT_DB=${CELERY__BEAT_DB:-"/mnt/volumes/statics/celerybeat-schedule"}
CELERY__BEAT_LOG=${CELERY__BEAT_LOG:-"/var/log/celery_beat.log"}

# Harvester settings
CELERY__HARVESTER_WORKER_NAME=${CELERY__HARVESTER_WORKER_NAME:-"harvesting_worker@%h"}
CELERY__HARVESTER_CONCURRENCY=${CELERY__HARVESTER_CONCURRENCY:-"10"}
CELERY__HARVESTER_AUTOSCALE_VALUES=${CELERY__HARVESTER_AUTOSCALE_VALUES:-"15,10"}
CELERY__HARVESTER_MAX_MEMORY_PER_CHILD=${CELERY__HARVESTER_MAX_MEMORY_PER_CHILD:-"500000"}

# --- FIX: Remove stale Beat pidfile before starting beat ---
BEAT_PIDFILE="/tmp/celerybeat.pid"

if [ -f "$BEAT_PIDFILE" ]; then
PID=$(cat "$BEAT_PIDFILE" 2>/dev/null)

# If PID exists and is running → warn but continue (avoid killing)
if kill -0 "$PID" 2>/dev/null; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the BEAT_PIDFILE is empty, PID will be empty, and kill -0 "$PID" will evaluate to kill -0 "", which fails. It is safer to check if PID is non-empty before running kill.

    if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then

Comment thread
jwkaltz marked this conversation as resolved.
Comment thread
jwkaltz marked this conversation as resolved.
echo "WARNING: Celery Beat seems to be running already (PID $PID). Removing stale pidfile anyway."
else
echo "Removing stale Celery Beat pidfile: $BEAT_PIDFILE"
fi

rm -f "$BEAT_PIDFILE"
fi
# --- END FIX ---

echo "Starting Celery Beat..."
$CELERY_BIN -A $CELERY_APP beat --scheduler=$CELERY__BEAT_SCHEDULE \
--schedule=$CELERY__BEAT_DB \
--loglevel=$CELERY__LOG_LEVEL -f $CELERY__BEAT_LOG --pidfile=/tmp/celerybeat.pid &

echo "Starting Default Celery Worker..."
$CELERY_BIN -A $CELERY_APP worker --autoscale=$CELERY__AUTOSCALE_VALUES \
--max-memory-per-child=$CELERY__MAX_MEMORY_PER_CHILD $CELERY__OPTS \
--statedb=$CELERY__STATE_DB \
--loglevel=$CELERY__LOG_LEVEL -n $CELERY__WORKER_NAME -f $CELERY__LOG_FILE \
--concurrency=$CELERY__WORKER_CONCURRENCY --max-tasks-per-child=$CELERY__MAX_TASKS_PER_CHILD \
-X harvesting &

echo "Starting Harvester Celery Worker..."
$CELERY_BIN -A $CELERY_APP worker -Q harvesting \
--autoscale=$CELERY__HARVESTER_AUTOSCALE_VALUES \
--max-memory-per-child=$CELERY__HARVESTER_MAX_MEMORY_PER_CHILD \
--loglevel=$CELERY__LOG_LEVEL \
-n $CELERY__HARVESTER_WORKER_NAME \
--concurrency=$CELERY__HARVESTER_CONCURRENCY \
-f $CELERY__LOG_FILE &
Comment thread
jwkaltz marked this conversation as resolved.

# Wait for any process to exit
wait -n

# Exit with the status of the process that exited first
# Docker will restart the container if this is non-zero (i.e., a failure)
exit $?
9 changes: 9 additions & 0 deletions charts/geonode/files/settings_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Overlay for existing GeoNode settings

from pathlib import Path
from geonode.settings import *

settings_additions = Path("/opt/geonode-custom/settings_additions.py")

if settings_additions.exists():
exec(settings_additions.read_text(encoding="utf-8"))
16 changes: 14 additions & 2 deletions charts/geonode/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,20 @@ postgres-password
{{- end -}}

# Volume names
{{- define "persistant_volume_name" -}}
persistence
{{- define "pvc_statics_name" -}}
pvc-{{ .Release.Name }}-geonode-statics
{{- end -}}

{{- define "pvc_geoserver_data_name" -}}
pvc-{{ .Release.Name }}-geonode-geoserver-data
{{- end -}}

{{- define "pvc_backup_restore_name" -}}
pvc-{{ .Release.Name }}-geonode-backup-restore
{{- end -}}

{{- define "pvc_data_name" -}}
pvc-{{ .Release.Name }}-geonode-data
{{- end -}}

# ports and endpoints
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Temporary fix pending https://github.com/GeoNode/geonode/pull/14292
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-celery-cmd-temporary-fix
data:
celery-cmd: |
{{ .Files.Get "files/celery-cmd-temporary-fix" | indent 4 }}
Loading
Loading