sure: new chart for the we-promise Sure app (1.0.0+up0.7.4) #36
No files matched your search
@@ -336,6 +336,13 @@ probe on "/" reach the database at all (see values.yaml).
|
|||||||
value: "true"
|
value: "true"
|
||||||
- name: RAILS_ENV
|
- name: RAILS_ENV
|
||||||
value: "production"
|
value: "production"
|
||||||
|
# Not cosmetic and not optional: /tmp is an emptyDir, which Kubernetes creates
|
||||||
|
# mode 0777 without a sticky bit, and Ruby skips exactly that shape when it
|
||||||
|
# looks for a temporary directory. Without this the app aborts during boot with
|
||||||
|
# "could not find a temporary directory". The directory is created by the
|
||||||
|
# init container in the StatefulSet, which explains the whole case.
|
||||||
|
- name: TMPDIR
|
||||||
|
value: "/tmp/ruby"
|
||||||
- name: PORT
|
- name: PORT
|
||||||
value: "3000"
|
value: "3000"
|
||||||
# Sure's production.rb defaults BOTH of these to true. The chart renders them
|
# Sure's production.rb defaults BOTH of these to true. The chart renders them
|
||||||
|
|||||||
@@ -34,6 +34,71 @@ spec:
|
|||||||
automountServiceAccountToken: false
|
automountServiceAccountToken: false
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- include "sure.podSecurityContext" .Values.sure.securityContext | nindent 8 }}
|
{{- include "sure.podSecurityContext" .Values.sure.securityContext | nindent 8 }}
|
||||||
|
initContainers:
|
||||||
|
# Ruby refuses a world-writable directory that has no sticky bit as its
|
||||||
|
# temporary directory (ruby 3.4, tmpdir.rb: a candidate is skipped when
|
||||||
|
# "world_writable? && !sticky?"), and a Kubernetes emptyDir is created
|
||||||
|
# mode 0777 WITHOUT the sticky bit. With /tmp mounted as an emptyDir and
|
||||||
|
# the image's root filesystem read-only, Ruby then finds no candidate at
|
||||||
|
# all and both containers die during boot - measured in this chart's
|
||||||
|
# first install-test, which logged
|
||||||
|
#
|
||||||
|
# system temporary path is world-writable: /tmp
|
||||||
|
# /tmp is world-writable: /tmp
|
||||||
|
# . is not writable: /rails
|
||||||
|
# ArgumentError: could not find a temporary directory
|
||||||
|
#
|
||||||
|
# raised from MiniMagick, which Active Storage loads while Rails boots.
|
||||||
|
# Note where it does NOT come from: nothing in the app asked for a temp
|
||||||
|
# file yet.
|
||||||
|
#
|
||||||
|
# The fix is a directory that is writable without being world-writable.
|
||||||
|
# This init container creates one per app container inside the same
|
||||||
|
# emptyDirs (mode 0700, owned by the pod's uid) and both containers get
|
||||||
|
# TMPDIR pointed at it. It runs as the same non-root uid as the app and
|
||||||
|
# needs no capability at all: the emptyDir root is 0777, so creating an
|
||||||
|
# entry in it is allowed, and chmod on a directory one has just created
|
||||||
|
# needs no privilege. That is deliberately weaker than the chown-as-root
|
||||||
|
# init container paperless-ngx needs.
|
||||||
|
#
|
||||||
|
# The image is the app's own, so there is no second image to pin, no
|
||||||
|
# Renovate block to maintain for it and nothing extra to pull.
|
||||||
|
- name: {{ .Release.Name }}-{{ .Chart.Name }}-init-tmp
|
||||||
|
image: "ghcr.io/we-promise/sure:{{ .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
# command, not args: the image entrypoint would start the application.
|
||||||
|
command:
|
||||||
|
- /bin/bash
|
||||||
|
- -ec
|
||||||
|
- |
|
||||||
|
for dir in /web-tmp/ruby /worker-tmp/ruby; do
|
||||||
|
mkdir -p "$dir"
|
||||||
|
chmod 700 "$dir"
|
||||||
|
done
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
volumeMounts:
|
||||||
|
- name: web-tmp
|
||||||
|
mountPath: /web-tmp
|
||||||
|
- name: worker-tmp
|
||||||
|
mountPath: /worker-tmp
|
||||||
|
# Fixed, deliberately small: this container runs two mkdirs and two
|
||||||
|
# chmods. Not values-configurable, for the same reason as the init
|
||||||
|
# container in paperless-ngx - there is nothing here to tune.
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 32Mi
|
||||||
|
ephemeral-storage: 1Mi
|
||||||
|
limits:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 64Mi
|
||||||
|
ephemeral-storage: 10Mi
|
||||||
containers:
|
containers:
|
||||||
# The web process. Its command is the image's default
|
# The web process. Its command is the image's default
|
||||||
# (CMD ["./bin/rails", "server"]), which is exactly what makes the
|
# (CMD ["./bin/rails", "server"]), which is exactly what makes the
|
||||||
@@ -74,8 +139,11 @@ spec:
|
|||||||
# between the two processes, and a shared scratch directory would
|
# between the two processes, and a shared scratch directory would
|
||||||
# only create coupling nobody asked for.
|
# only create coupling nobody asked for.
|
||||||
#
|
#
|
||||||
# /tmp: Rack writes multipart uploads (CSV imports, attachments) to
|
# /tmp: Rack writes multipart uploads (CSV imports, attachments)
|
||||||
# Dir.tmpdir before Active Storage ever sees them.
|
# here before Active Storage ever sees them. The app does not use
|
||||||
|
# this path directly, though - TMPDIR points at the private
|
||||||
|
# subdirectory the init container creates inside it, because Ruby
|
||||||
|
# rejects a world-writable emptyDir as a temporary directory.
|
||||||
- name: web-tmp
|
- name: web-tmp
|
||||||
mountPath: /tmp
|
mountPath: /tmp
|
||||||
# /rails/tmp: Rails' own scratch directory - the Bootsnap cache,
|
# /rails/tmp: Rails' own scratch directory - the Bootsnap cache,
|
||||||
|
|||||||
+12
-4
@@ -36,10 +36,18 @@ sure:
|
|||||||
# need a ReadWriteMany volume. Above 1 the extra pods normally stay Pending on
|
# need a ReadWriteMany volume. Above 1 the extra pods normally stay Pending on
|
||||||
# "Multi-Attach error for volume" - see NOTES.txt.
|
# "Multi-Attach error for volume" - see NOTES.txt.
|
||||||
replicas: 1
|
replicas: 1
|
||||||
# Pod-level hardening, shared by both containers. The upstream image declares
|
# Pod-level hardening, shared by both containers and by the init container.
|
||||||
# USER 1000:1000 (group and user "rails", created in the Dockerfile), so the
|
# The upstream image declares USER 1000:1000 (group and user "rails", created
|
||||||
# chart needs no deviation from the repo hardening standard; fsGroup 1000
|
# in the Dockerfile), so the chart needs no deviation from the repo hardening
|
||||||
# makes the storage PVC writable for that uid.
|
# standard; fsGroup 1000 makes the storage PVC writable for that uid.
|
||||||
|
#
|
||||||
|
# readOnlyRootFilesystem: true holds, and the writable paths are emptyDirs:
|
||||||
|
# /rails/tmp, /rails/log and /tmp, one set per container. /tmp needs one extra
|
||||||
|
# step that is easy to mistake for decoration - a tiny init container creates
|
||||||
|
# a private subdirectory in it and the containers run with TMPDIR pointing
|
||||||
|
# there, because Ruby refuses a world-writable directory without a sticky bit
|
||||||
|
# as its temporary directory and an emptyDir is exactly that. Without it the
|
||||||
|
# app aborts during boot; the StatefulSet carries the measured evidence.
|
||||||
securityContext:
|
securityContext:
|
||||||
pod:
|
pod:
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
|
|||||||
Reference in new issue
Block a user