Install-test / Install-test apache-tika (pull_request) Successful in 1m21s
Install-test / Install-test bentopdf (pull_request) Successful in 1m16s
Install-test / Install-test cyberchef (pull_request) Successful in 1m6s
Install-test / Install-test gotenberg (pull_request) Successful in 1m32s
Install-test / Install-test outline (pull_request) Failing after 1m16s
Install-test / Install-test rss-bridge (pull_request) Successful in 1m1s
Install-test / Install-test searxng (pull_request) Successful in 1m13s
Validate Helm Charts / Install-test (pull_request) Failing after 9m57s
Install-test / Install-test ytdlp-web-player (pull_request) Successful in 1m11s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Failing after 18s
Validate Helm Charts / HTTPRoute match (pull_request) Failing after 13s
Validate Helm Charts / Selector match (pull_request) Failing after 17s
Validate Helm Charts / Bump level (pull_request) Successful in 8s
Validate Helm Charts / YAML scalars (pull_request) Failing after 16s
Validate Helm Charts / NetworkPolicy (pull_request) Failing after 9s
Validate Helm Charts / Build apache-tika (pull_request) Successful in 5s
Validate Helm Charts / Trivy config apache-tika (pull_request) Successful in 13s
Validate Helm Charts / Build bentopdf (pull_request) Successful in 5s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 6s
Validate Helm Charts / Build outline (pull_request) Successful in 5s
Validate Helm Charts / Build template-chart (pull_request) Successful in 5s
Validate Helm Charts / Trivy config outline (pull_request) Failing after 12s
Validate Helm Charts / Trivy config searxng (pull_request) Successful in 15s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 14s
Validate Helm Charts / Build gotenberg (pull_request) Successful in 5s
Validate Helm Charts / Build rss-bridge (pull_request) Successful in 5s
Validate Helm Charts / Build searxng (pull_request) Successful in 5s
Validate Helm Charts / Build ytdlp-web-player (pull_request) Successful in 5s
Validate Helm Charts / Trivy config bentopdf (pull_request) Successful in 14s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 13s
Validate Helm Charts / Trivy config gotenberg (pull_request) Successful in 13s
Validate Helm Charts / Trivy config rss-bridge (pull_request) Successful in 14s
Validate Helm Charts / Trivy config ytdlp-web-player (pull_request) Successful in 15s
bentopdf, apache-tika, gotenberg, rss-bridge, searxng, ytdlp-web-player. One major each, networkPolicy.enabled defaults to true. Three of them differ from the simple case in ways worth naming: GOTENBERG gets DNS-only egress as a deliberate, documented trade. It drives a headless Chromium, and 3000 is its probe port - so it is reachable from the whole cluster by default, and an egress rule for 80/443 would turn a service anyone can POST to into a way of fetching arbitrary URLs. The price is the remote-asset half of HTML conversion: inline content converts, images the page would have fetched silently do not. That degrades a PDF instead of failing a pod, so nothing alerts on it, which is why it is in the template header, in values.yaml and in NOTES.txt, with extraPorts: [80, 443] named as the knowing opt-in. RSS-BRIDGE, SEARXNG and YTDLP-WEB-PLAYER get the open internet on their web ports, because reaching other people's servers IS the application and this object cannot name a host. Said plainly in each, so none of them reads stricter than it is. What the cap still buys is real: no other outbound port, so a compromised pod reaches no database, no mail server and no SSH anywhere. APACHE-TIKA and GOTENBERG render no Ingress - their consumers are other pods, in other namespaces here - so neither gets an ingressController value and both say in NOTES.txt that narrowing probeSources means listing the consumers' namespaces in allowedNamespaces in the same change, or extraction and conversion stop. YTDLP-WEB-PLAYER names the gap this convention has: its oauth2-proxy subchart renders its own Deployment with its own labels and ships no policy, so with the proxy enabled the pod that actually faces the Ingress is unrestricted in both directions. Nothing in this chart can change that - an upstream chart is consumed as it comes - so it is written down instead. ci/outline/test-values.yaml gains egress.extraPorts: [5556]. The CI dex runs there rather than on 443, and Outline fetches the discovery document at boot - so this is the chart's documented escape hatch used for exactly the case it was written for, and the one place in the repo where the cap's upgrade risk is actually exercised. Part of #5
203 lines
11 KiB
Plaintext
203 lines
11 KiB
Plaintext
{{- $replicas := include "searxng.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.searxng.replicas) -}}
|
|
{{- $settings := fromYaml (include "searxng.effectiveSettings" .Values.settings) -}}
|
|
{{- $httpRoute := .Values.httpRoute | default dict -}}
|
|
{{- $hostnames := $httpRoute.hostnames | default list -}}
|
|
{{- if not $hostnames -}}
|
|
{{- $hostnames = list .Values.ingress.domain -}}
|
|
{{- end -}}
|
|
SearXNG {{ .Chart.AppVersion }} — release {{ .Release.Name }} in namespace {{ .Release.Namespace }}.
|
|
|
|
{{- if .Values.ingress.enabled }}
|
|
|
|
ACCESS — INGRESS MODE (ingress.enabled: true, httpRoute.enabled: false)
|
|
https://{{ .Values.ingress.domain }}
|
|
|
|
The certificate is requested by cert-manager through the "{{ .Values.ingress.clusterIssuer }}"
|
|
cluster issuer. Until it has been issued the browser shows a warning; follow it with
|
|
|
|
kubectl -n {{ .Release.Namespace }} get certificate tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress
|
|
{{- else if $httpRoute.enabled }}
|
|
|
|
ACCESS — GATEWAY MODE (httpRoute.enabled: true, ingress.enabled: false)
|
|
{{- range $hostnames }}
|
|
https://{{ . }}
|
|
{{- end }}
|
|
|
|
This release renders an HTTPRoute ({{ .Release.Name }}-{{ .Chart.Name }}-httproute) and NOTHING else
|
|
of the routing path — no Gateway, no class, no Certificate. Three things
|
|
therefore belong to the cluster and not to this chart:
|
|
|
|
1. The Gateway-API CRDs. Without them this install would have failed.
|
|
2. The Gateway named in httpRoute.parentRefs
|
|
{{- range $httpRoute.parentRefs }}
|
|
{{ .name }}{{ if .namespace }} in namespace {{ .namespace }}{{ end }}{{ if .sectionName }}, listener {{ .sectionName }}{{ end }}
|
|
{{- end }}
|
|
must exist AND its listener must allow routes from this namespace
|
|
({{ .Release.Namespace }}) in allowedRoutes. A rejected attachment shows
|
|
up only in the route's status:
|
|
|
|
kubectl -n {{ .Release.Namespace }} get httproute {{ .Release.Name }}-{{ .Chart.Name }}-httproute -o yaml
|
|
|
|
Read status.parents[].conditions — "Accepted: True" and
|
|
"ResolvedRefs: True" is a working attachment. An empty status means no
|
|
controller has looked at the route at all.
|
|
3. The TLS certificate for the hostname, on the Gateway's listener.
|
|
ingress.clusterIssuer is NOT used in this mode and is not required: the
|
|
chart renders no Certificate, so there is no
|
|
tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress secret and nothing for cert-manager to do.
|
|
|
|
SEARXNG_BASE_URL still follows ingress.domain ("https://{{ .Values.ingress.domain }}/"), not
|
|
httpRoute.hostnames: signed image-proxy URLs and the OpenSearch description
|
|
keep naming that host whatever the route publishes.
|
|
|
|
AND THE THING THIS MODE TAKES AWAY: the forward-auth escape hatch. The
|
|
Gateway API has no annotation channel, so the Authentik-style auth
|
|
annotations documented under ingress.annotations in values.yaml have NO
|
|
counterpart here, and the chart renders no implementation-specific policy
|
|
object on purpose. SearXNG has no login of its own, so "who may use this
|
|
instance" is entirely the Gateway's question in this mode — a listener that
|
|
is not publicly reachable, or that implementation's own auth filter,
|
|
configured next to the Gateway and not here.
|
|
{{- else }}
|
|
|
|
ACCESS — NEITHER INGRESS NOR HTTPROUTE
|
|
ingress.enabled and httpRoute.enabled are both false, so this release has no
|
|
Ingress, no HTTPRoute and no certificate.
|
|
ingress.domain is still required: it builds SEARXNG_BASE_URL, which SearXNG
|
|
puts into its OpenSearch description, into the signed image-proxy URLs and
|
|
into every absolute link. Reach the instance inside the cluster with
|
|
|
|
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }}-{{ .Chart.Name }}-service 8080:80
|
|
|
|
and open http://localhost:8080. Note that links on the page will still point
|
|
at https://{{ .Values.ingress.domain }}/.
|
|
{{- end }}
|
|
{{ if eq $replicas "0" }}
|
|
SCALED TO ZERO
|
|
{{ if .Values.scaleDown }}scaleDown: true{{ else }}searxng.replicas: 0{{ end }} — the deployment runs with replicas: 0, so the endpoint
|
|
above does not answer until you set it back and upgrade again. Service{{ if .Values.ingress.enabled }} and
|
|
Ingress stay in place, so the certificate is not re-issued.{{ else if $httpRoute.enabled }} and
|
|
HTTPRoute stay in place, so the route keeps pointing at a Service with no
|
|
endpoints — the Gateway answers with an error of its own rather than with
|
|
nothing.{{ else }} stays in
|
|
place.{{ end }}
|
|
{{ end }}
|
|
CONFIGURATION
|
|
Instance name: {{ $settings.instanceName }}
|
|
settings.yml is rendered from the "settings" values into
|
|
{{ .Release.Name }}-{{ .Chart.Name }}-configmap and mounted at /etc/searxng/settings.yml.
|
|
It starts with use_default_settings: true and names only the deviations, so
|
|
every engine the image ships stays available and upstream engine changes
|
|
arrive with the app version instead of with a chart release.
|
|
|
|
Anything the chart does not model first-class goes into settings.extra in
|
|
SearXNG's own key names; it is merged over the generated block. Changing any
|
|
of it rolls the pods — the deployment hashes the ConfigMap, because a subPath
|
|
mount would otherwise never reach the running container.
|
|
|
|
WHAT THE GREEN CHECKMARK DOES AND DOES NOT MEAN
|
|
All three probes hit /healthz, the only path SearXNG's bot detection lets
|
|
through unconditionally. Its handler returns a fixed "OK". So a Ready pod
|
|
means the worker answers — not that a search works, not that any engine is
|
|
reachable, and not that a login placed in front of it is up (probes go
|
|
straight to the pod and never pass the {{ if $httpRoute.enabled }}Gateway{{ else }}Ingress{{ end }}).
|
|
{{- if not $settings.limiter }}
|
|
|
|
BOT DETECTION IS OFF
|
|
{{- if $httpRoute.enabled }}
|
|
settings.limiter is false and no Valkey is configured. The instance relies on
|
|
whatever sits in front of it — and in GATEWAY MODE that cannot be configured
|
|
from this chart: ingress.annotations has no counterpart, so a login has to be
|
|
put on the Gateway itself (a listener that is not publicly reachable, or that
|
|
implementation's own auth filter). The limiter itself is unaffected by the
|
|
mode; it runs inside SearXNG, not at the proxy.
|
|
{{- else }}
|
|
settings.limiter is false and no Valkey is configured. The instance relies on
|
|
whatever sits in front of it — put authentication there via
|
|
ingress.annotations if it must not be open to everyone. Mind that this
|
|
escape hatch exists in Ingress mode only: httpRoute.enabled has no
|
|
annotation channel at all.
|
|
{{- end }}
|
|
{{- else if not .Values.secrets.valkeySecretRef }}
|
|
|
|
BOT DETECTION IS ON WITHOUT A VALKEY
|
|
settings.limiter is true but secrets.valkeySecretRef is unset. The limiter
|
|
needs a Valkey to keep its counters, so it does nothing at all: SearXNG logs
|
|
|
|
ERROR:searx.limiter: The limiter requires Valkey, please consult the
|
|
documentation: https://docs.searxng.org/admin/searx.limiter.html
|
|
|
|
once at worker start, the pod becomes Ready, and every request is served
|
|
unlimited. This release is therefore NOT rate limited, and the only trace of
|
|
that is one line at the top of the log. Set secrets.valkeySecretRef, or set
|
|
settings.limiter back to false.
|
|
{{- end }}
|
|
|
|
ROTATING THE SIGNING KEY
|
|
secrets.searxngSecretRef carries the key that signs the settings cookie and
|
|
the image-proxy URLs. Pointing it at a DIFFERENT 1Password item rolls the
|
|
pods; changing the value INSIDE the same item does not — Helm never sees that
|
|
content. After such a rotation:
|
|
|
|
kubectl -n {{ .Release.Namespace }} rollout restart deployment/{{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
|
|
|
A new key invalidates every user's saved preferences cookie, which is a
|
|
logout-shaped event for the people using it.
|
|
|
|
NOTHING TO BACK UP
|
|
The pod holds no state worth keeping: settings.yml comes from the ConfigMap,
|
|
the signing key from the Secret, and the SQLite caches under /tmp plus
|
|
/var/cache/searxng are emptyDirs that refill on their own. App-version
|
|
upgrades and rollbacks therefore carry no data risk.
|
|
{{- if .Values.networkPolicy.enabled }}
|
|
|
|
NETWORKPOLICY IS ACTIVE — WHAT IT CAPS, AND WHAT IT DOES NOT
|
|
{{ .Release.Name }}-{{ .Chart.Name }}-networkpolicy selects this workload's pods.
|
|
Inbound: TCP 8080 only. Outbound: DNS, TCP 443 (the search engines) and 6379
|
|
(external Valkey){{ with .Values.networkPolicy.egress }}{{ with .extraPorts }}, plus TCP {{ join ", " . }}{{ end }}{{ end }}.
|
|
|
|
443 GOES TO ANY DESTINATION, because querying search engines is the
|
|
application and this object cannot name a host. 6379 is the STANDARD Valkey
|
|
port: the URL sits in a 1Password item the chart never parses, so a Valkey on
|
|
another port is blocked — put it in networkPolicy.egress.extraPorts.
|
|
|
|
Three limits belong in the same breath, because none of them is visible in
|
|
the object itself:
|
|
|
|
- EGRESS IS BY PORT, NEVER BY DESTINATION. A NetworkPolicy matches CIDRs and
|
|
selectors and cannot express a hostname, so every egress rule permits its
|
|
port to ANY address. That is a limit of the object, not a shortcut — this
|
|
cluster's CNI (Canal: Flannel plus Calico) has no FQDN rule either. An
|
|
outbound port the chart does not know about is BLOCKED: add it to
|
|
networkPolicy.egress.extraPorts, and expect to find out through an
|
|
application-level connection error rather than through this object.
|
|
{{- if .Values.networkPolicy.probeSources }}
|
|
- THE PROBE PATH IS RESTRICTED in this release, because
|
|
networkPolicy.probeSources is set to
|
|
{{ join ", " .Values.networkPolicy.probeSources }}
|
|
Probes come from the IP of the WORKER NODE running the pod. If none of those
|
|
CIDRs covers the node this pod lands on, the kubelet's probes are dropped
|
|
and THE POD NEVER BECOMES READY — the rollout then times out and the symptom
|
|
looks like a broken application. Compare
|
|
|
|
kubectl -n {{ .Release.Namespace }} describe pod -l app={{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
|
|
|
against `kubectl get node -o wide`.
|
|
{{- else }}
|
|
- THE PROBED PORT IS OPEN INSIDE THE CLUSTER. networkPolicy.probeSources is
|
|
empty, so that rule carries no source selector at all and any pod the CNI
|
|
routes to can reach it. Not from the internet — but this is NOT pod-level
|
|
isolation. Narrowing it means listing the worker node IPs in
|
|
networkPolicy.probeSources, because the kubelet probes from the node's own
|
|
address and the chart cannot know it; a wrong guess there leaves the pod
|
|
permanently un-Ready.
|
|
{{- end }}
|
|
- IT IS ONLY A BOUNDARY WHERE THE CNI ENFORCES POLICIES. Where it does not,
|
|
this object is accepted and filters nothing, so everything reads as
|
|
configured while nothing is filtered. `kubectl -n {{ .Release.Namespace }} get networkpolicy`
|
|
shows the object exists; only a connection attempt from an unrelated
|
|
namespace shows that it bites.
|
|
|
|
Switch it off with networkPolicy.enabled: false.
|
|
{{- end }}
|