Feature request: support running MISP in Kubernetes (single Docker image for Docker + K8s)
@tobmes42 is already working on this.
Since Sep 11, 2026.
- Dominant language
- Shell
- Stars
- 394
- Forks
- 194
- Avg merge
- 3d 9h
- Merged PRs (30d)
- 9
Description
Summary
We are running MISP in a Kubernetes environment (non-root, read-only root filesystem) and
would like to contribute back our adaptations to the upstream misp-docker project. The
goal was to keep a single Docker image that works unchanged for plain Docker (root, full
permissions) and for Kubernetes (non-root www-data, readOnlyRootFilesystem).
This issue describes the adaptation so the maintainers can decide what to adopt. It is split
into (A) the core K8s mechanism, and (B) a full inventory of the remaining differences from
upstream — most are intentional infrastructure adaptations, a few are candidates for adoption.
A. Core mechanism: one image for Docker + K8s
1. Runtime user bridge — misp_run()
Upstream calls MISP commands with sudo -u www-data. In K8s the container already runs as
www-data and sudo is unavailable. We introduced a wrapper that is used instead of the
hard-coded sudo -u www-data:
misp_run() {
if [ "$(id -u)" -eq 0 ]; then
sudo -u www-data "$@"
else
"$@"
fi
}
- If started as
root(plain Docker): behaves exactly like upstream (sudo -u www-data). - If started as
www-data(K8s non-root): runs directly, no sudo needed.
All sudo -u www-data calls in configure_misp.sh were replaced with misp_run.
Note: some
cakecalls inutilities.sh(enforce_env_settings,set_safe_default) were
refactored to callcakedirectly without the wrapper; in the root (Docker) mode these run
asrootinstead ofwww-data. This is a known, low-severity divergence (the K8s/runtime
bridge) and a candidate for cleanup.
2. Read-only root filesystem tolerance
update-alternatives --install ... writes to /var/lib/dpkg/alternatives, which fails on a
read-only root fs. We made it non-fatal:
sudo update-alternatives --install /usr/bin/php php /usr/bin/php8.3 150 2>/dev/null || true
(and the same pattern for the other update-alternatives calls in entrypoint.sh).
3. K8s build flag — ARG ENABLE_K8S_TWEAKS
Infrastructure steps that are K8s-only are gated behind a build argument
(ARG ENABLE_K8S_TWEAKS=false, default = upstream behavior), e.g.:
chown -R www-data:www-data /etc/supervisor- sudoers entry for cron (
www-data ALL=(ALL) NOPASSWD: /usr/sbin/cron) chmod -R 777 /etc/nginx/conf.d- execute-permission cleanup via
disable_exec_for_others.sh
4. .kubernetes-specific file variants
Separate files that are only copied in the K8s build:
etc/supervisor/supervisord.conf.kubernetes(setsuser=www-data)etc/supervisor/conf.d/10-supervisor.conf.kubernetesetc/supervisor/conf.d/50-workers.conf.kubernetes
The regular (non-K8s) supervisord.conf keeps user=root, identical to upstream.
5. Runtime nginx port parameterization
nginx site files use placeholders that default to 80/443 (Docker behavior), replaced at
runtime via env vars for K8s:
sed -i "s/@NGINX_HTTP_PORT@/${NGINX_HTTP_PORT:-80}/" /etc/nginx/sites-enabled/misp80
sed -i "s/@NGINX_HTTPS_PORT@/${NGINX_HTTPS_PORT:-443}/" /etc/nginx/sites-enabled/misp443
6. Readiness marker for K8s probes
At the end of a successful configuration the container writes a marker file used by K8s
readiness probes. The write is tolerantly guarded so it does not abort the init in plain
Docker, where the /misp/readiness mount does not exist:
echo "MISP is ready" >>/misp/readiness/ready.log 2>/dev/null || true
Without the
2>/dev/null || trueguard, a Docker (non-K8s) run fails because
/misp/readinessis not mounted there; with the guard the same image works in both modes.
7. config.php readability after init (www-data/php-fpm)
The final command of configure_misp.sh ensures config.php is readable by www-data
(php-fpm), regardless of the user the init ran as. This matters because several cake Admin setSetting / modify_config.php calls rewrite config.php; when the init runs as root
(plain Docker) the file is created root:root 600/640, which php-fpm (www-data) cannot
read → Permission denied on every request (HTTP 500 / redirect loop). Setting the group to
www-data with 640 lets php-fpm read the file in both modes:
chown :www-data /var/www/MISP/app/Config/config.php
chmod 640 /var/www/MISP/app/Config/config.php
Why the group and not the owner: in the root (Docker) init the file owner stays
rootand
only the group is fixed; in the K8s (www-data) init the write already produces a readable
file, so this is a no-op safe-guard.640grantsr--to thewww-datagroup without
exposing the file to "other".
B. Full inventory of remaining differences from upstream
Intentional infrastructure / K8s adaptations (low adoption value upstream)
- Dockerfile build-hygiene refactor: larger
apt-get clean && rm -rf /var/lib/apt/lists/*in
thephp-basebuild with matchingapt-get updatein later stages;SHELL ["/bin/bash","-o","pipefail","-c"]after the pecl step. - Roughly more
<<-EOFheredocs split into smaller RUN layers (mostly structural, for layer
attribution); behaviorally equivalent. mkdir /wheelssplit into its own RUN; mod detection usingif ! grep -qinstead of
$?; extracurlentries in the apt package lists (benign).
Candidates considered upstream-worthy (may already match latest upstream)
config.phpreadability guarantee after init (Section A.7) — upstreamconfigure_misp.sh
also runscakeaswww-data, but the fork's finalchown :www-data; chmod 640explicitly
protects against a root-run init leaving an unreadable file. High value: it hardens the
single-image promise for both Docker and K8s.ready.logtolerant write (Section A.6) — small, but prevents a Docker boot regression when
/misp/readinessis absent.entrypoint_nginx.sh: loop over*glob (withbasename) instead of parsinglsoutput —
handles paths with spaces; otherwise equivalent.- The 503 maintenance page in
var/www/html/index.php(see discussion below). MISP.uuidinitialization inminimum_config.defaults.json(see below).
Discussion points for maintainers
- 503 maintenance page — upstream
index.phpsets503 Service Unavailable+
no-store/no-cache+retry-after/refresh: 30while MISP loads, which lets load
balancers drain traffic during boot. We restored it for parity. In K8s the readiness
probe (ready.log) is the primary signal; both can coexist. MISP.uuidbootstrap — upstream'sminimum_config.defaults.jsonincludes a
MISP.uuidblock withdefault_value: ""andcommand_args: "-f"to force-initialize the
setting when unset. We restored it for parity. Note the fork had removed it.
What we'd like from maintainers
- Feedback on whether the K8s mechanism (A) is a welcome upstream addition, and
- guidance on the preferred shape: a
K8S/ENABLE_K8S_TWEAKSbuild arg +.kubernetesfile
variants, versus any alternative approach you'd prefer.
Happy to open a PR and adjust the adaptation to your conventions if there is interest.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.