Imgur proxy¶
kubernetes/apps/downloads/imgur-proxy is an in-cluster SNI relay that geo-unblocks imgur.com
and its subdomains, reached through a gluetun WireGuard sidecar to the VPN provider (see
split-dns for how OPNsense redirects the zone to it). This page
keeps the incident detail behind the sidecar's hardening, which does not fit as manifest comments.
The gluetun sidecar's capability workaround¶
imgur-proxy was originally the only gluetun sidecar in downloads setting capabilities.drop:
[ALL]; the other three kept root's default capability set and never hit two upstream behaviours
this one's hardening exposed:
cmd/gluetun/main.gocreates its status directory withos.MkdirAll("/tmp/gluetun", 0644), a directory with no execute bit, so traversing into it needsCAP_DAC_OVERRIDE. The container pre- creates the path itself as anemptyDir(mode1777), which makes theMkdirAlla no-op.internal/publicip/fs.gochowns the written public-IP file toPUID:PGID(default1000), which needsCAP_CHOWN. BothPUIDandPGIDare set to0so the chown targets the file's existing owner, a permitted no-op.PUIDotherwise only feeds the unused OpenVPN process user, since this tunnel is WireGuard.
A follow-up (#3950) levelled qbittorrent, sabnzbd and prowlarr up to the identical pattern rather
than relaxing this one back down, so all four gluetun sidecars in downloads now drop every
capability except NET_ADMIN and carry the same PUID/PGID/gluetun-tmp workaround.
The blocklist and the memory limit¶
BLOCK_MALICIOUS defaults to true and is a live filter, not dormant config. It is off only here,
because nginx's SNI map already drops every lookup that isn't .imgur.com before a connection
opens, so the blocklist download has nothing left to filter. qbittorrent, sabnzbd and prowlarr leave
it at the default; all three also set DNS_KEEP_NAMESERVER: "on", which keeps CoreDNS as their
resolver rather than gluetun's own, so whether the filter sees their traffic at all is a separate
question this page does not answer.
Turning it off was also the main response to an OOM crashloop: the sidecar OOMKilled 240+ times at
a 256Mi limit. cAdvisor never sampled this container above ~86Mi in 7 days (steady state ~26Mi),
because the spike lands about 2 seconds into startup and dies inside one scrape interval, invisible
to sampling. The 512Mi limit in the manifest is insurance rather than a proven fix: the unmeasured
7MB servers.json parse is a second candidate the blocklist change does not rule out. The other
three gluetun sidecars set no memory limit at all.
The probe restart loop¶
imgur-proxy was, for a period, the only one of the four gluetun sidecars in downloads with
kubelet probes enabled on gluetun's health server. That probe restarts the container, but gluetun
keeps the pod's network namespace across container restarts and re-appends its firewall rules on
every start, so one transient tunnel failure became self-sustaining: probe failure, restart, more
accumulated netns state, a slower or failed start, probe failure again. This container sat in that
loop for over 4 days and 1352 restarts, ending in OOMKills, and only a pod recreation broke it.
The liveness probe is now disabled, matching every other gluetun sidecar. gluetun's own health
loop (HEALTH_RESTART_VPN, on by default) restarts the tunnel in-process on failure, recovering
the same faults without touching the netns. A startup probe stays enabled: it only runs until the
tunnel first comes up, and gates the app container on it (hardening backlog H-20).