Skip to content

enable readOnlyRootFilesystem in helm chart#14

Merged
akarnani merged 3 commits into
mainfrom
helm-readonly-rootfs
Apr 29, 2026
Merged

enable readOnlyRootFilesystem in helm chart#14
akarnani merged 3 commits into
mainfrom
helm-readonly-rootfs

Conversation

@akarnani
Copy link
Copy Markdown
Owner

Summary

  • Flip securityContext.readOnlyRootFilesystem from false to true in the chart's values.yaml.
  • App audit confirms no filesystem writes anywhere in the codebase (no os.Create/OpenFile/WriteFile/temp-file usage). Config is baked into the image at /config.yaml and read once at startup; the METAR cache is in-memory; logs go to stdout/stderr.

Test plan

  • helm lint deploy/charts/avweather-cache — clean.
  • helm template confirms rendered pod spec has readOnlyRootFilesystem: true.
  • Smoke-test in a real cluster: install the chart, hit /api/metar and /metrics, confirm the pod stays Ready and METAR pulls from aviationweather.gov continue to succeed.

🤖 Generated with Claude Code

akarnani and others added 3 commits April 29, 2026 10:15
The app holds the cache in memory, reads config once at startup from a
baked-in /config.yaml, and writes only to stdout/stderr — no
filesystem writes anywhere in the code. Setting readOnlyRootFilesystem
true is compatible and gives us a kernel-enforced barrier against
write-based exploits compromising the container.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The cache's poll loop used http.Get with no timeout, so a slow or hung
upstream could leave a request blocked indefinitely and stall the
update cycle. Give the cache a dedicated *http.Client with a 30s
timeout (well within the 5m default update interval).

The HTTP server lacked ReadHeaderTimeout, leaving it slightly more
exposed to slowloris-style header-dribble. Add a 5s ReadHeaderTimeout
on top of the existing ReadTimeout/WriteTimeout/IdleTimeout.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…y headers

Cache fetch:
  Wrap the upstream body in io.LimitReader (100 MiB compressed) and
  the gzip stream in another LimitReader (1 GiB decompressed) so a
  hostile or misbehaving upstream can't blow the container's memory.

HTTP server:
  Set MaxHeaderBytes to 16 KiB on both servers, complementing the
  ReadHeaderTimeout. Add a securityHeaders middleware that emits
  X-Content-Type-Options, X-Frame-Options, and Referrer-Policy.

/metrics on its own listener:
  Run a second http.Server bound to a separate port (default 9090).
  This lets the operator scope /metrics network exposure independently
  of the public API. Helm chart now renders a dedicated ClusterIP
  service for metrics so the LoadBalancer never exposes /metrics
  externally; ServiceMonitor and probes target the new port.

Webapp:
  Cap ?search= at 32 chars (real station IDs are 3-4) so a multi-MB
  query can't amplify CPU through the per-station Contains scan.

Error responses:
  Replace internal-error leaks ("failed to encode JSON: %v", etc.) in
  /api/metar, /search, and / with generic 500s. Full detail still goes
  to the server log.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@akarnani akarnani force-pushed the helm-readonly-rootfs branch from 365f0a1 to c86e358 Compare April 29, 2026 17:18
@akarnani akarnani merged commit d6128eb into main Apr 29, 2026
13 checks passed
@akarnani akarnani deleted the helm-readonly-rootfs branch April 29, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant