File-integrity monitoring
An optional watcher mounts the site's volume read-only, polls it every fim.interval seconds and reports findings as Prometheus metrics (wp_fim_violation{path,rule}, wp_fim_violations, wp_fim_last_scan_timestamp_seconds, wp_fim_scan_duration_seconds). It polls rather than using inotify, which does not see writes made by another NFS client. It runs PHP from the chart's own image, so it needs no further image.
fim:
enabled: true
mode: deployment # own pod (recommended); or: sidecar
nameMarkers: [requestentry] # indicators of a known toolkit
baseline: # quote the digests
salts.php: ["ee9409c0..."]
serviceMonitor:
enabled: true
prometheusRule:
enabled: true # leave off if a shared rule set covers the watchersWhat it reports, structurally rather than by known file names: a mu-plugins directory, executable files directly in wp-content/, loose files directly in plugins/ or themes/, executable files in uploads/, .user.ini files, encoded payload carriers (.b64, .bak, ...), unreadable directories, drop-ins that differ from their plugin's copy, directives in nginx.conf that run code or map other files, and changes to baselined files. Each finding names its rule in the rule label; values.yaml lists them all.
- Plugin updates need no baseline maintenance. Plugins update themselves, and some rewrite files the watcher checks. Those files are judged by content instead of a digest: W3 Total Cache's drop-ins must equal the copies the installed plugin ships (
fim.dropins),nginx.conf- where W3 Total Cache writes its rules - must not hold directives such asfastcgi_pass,aliasorinclude, and anindex.phpis accepted wherever it is a placeholder ("Silence is golden"). Baseline only files nothing rewrites on its own. - wp-config.php is baselined automatically. The chart generates it, so the digest is computed at render time and follows every chart upgrade without manual re-baselining.
- Web-root mounts are watched as static paths. Every
extraVolumeMountsentry that mounts a subPath of the chart's volume below/var/www/htmlis included; anything executable there is a finding. - Re-baselining after a legitimate change: a
baseline-modifiedfinding carries the digest it found in itssha256label. The watcher re-reads its configuration on every scan, so a values change takes effect without a restart. To scan by hand, runkubectl exec deploy/<release>-wordpress-nginx-fim -- /usr/local/bin/php /fim/fim.php --scan-once. - Own pod or sidecar: the own pod shares neither the network namespace nor the lifecycle of WordPress. It keeps reporting while WordPress is scaled down for an investigation, and a compromised WordPress container cannot take over its port. It needs the volume on its node. With a volume that is not ReadWriteMany, it is scheduled next to WordPress; ReadWriteOncePod and setups without persistence need
mode: sidecar.