diff --git a/.kubernetes/config-varnish.yml b/.kubernetes/config-varnish.yml new file mode 100644 index 0000000..cb3a38d --- /dev/null +++ b/.kubernetes/config-varnish.yml @@ -0,0 +1,97 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: varnish-vcl + namespace: ${NAMESPACE} +data: + default.vcl: | + vcl 4.0; + + import std; + import digest; + + # Define backend pointing to Home Assistant IP + backend hass_backend { + .host = "${HOMEASSISTANT_IP}"; + .port = "8123"; + } + + sub vcl_recv { + # Handle CORS preflight + if (req.method == "OPTIONS") { + return (synth(204, "Preflight")); + } + + # Rewrite image URL + if (req.url ~ "^/image/") { + # Extract everything after /image/ and store it + set req.http.X-Image-URL = regsub(req.url, "^/image/(.*)", "\1"); + # Rewrite req.url to match backend expectations + set req.url = regsub(req.http.X-Image-URL, "^http://[^/]+", ""); + } + + # Remove cookies so content is cacheable + unset req.http.Cookie; + } + + sub vcl_synth { + if (resp.status == 204) { + set resp.http.Access-Control-Allow-Origin = "*"; + set resp.http.Access-Control-Allow-Methods = "GET, OPTIONS"; + set resp.http.Access-Control-Allow-Headers = "Content-Type, X-Cache-ID"; + set resp.http.Content-Length = "0"; + return (deliver); + } + + if (resp.status == 304) { + set resp.http.ETag = req.http.If-None-Match; + set resp.http.Content-Length = "0"; + return (deliver); + } + } + + sub vcl_backend_fetch { + # Always use the HASS backend + set bereq.backend = hass_backend; + + # Set proper Host header from original URL + # if (bereq.http.X-Image-URL) { + # set bereq.http.Host = regsub(bereq.http.X-Image-URL, "^http://([^/]+).*", "\1"); + # set bereq.http.Host = regsub(bereq.http.Host, ":[0-9]+$", ""); + # } + } + + sub vcl_backend_response { + set beresp.ttl = 1s; + set beresp.grace = 60s; + set beresp.keep = 60s; + + # Ensure ETag is passed to client + if (beresp.http.ETag) { + set beresp.http.X-Cache-ETag = beresp.http.ETag; + } else { + # Optional: generate one if not provided + # set beresp.http.ETag = digest.hash_md5(beresp.body); + set beresp.http.ETag = beresp.http.Content-Length; + set beresp.http.X-Cache-ETag = beresp.http.ETag; + } + } + + sub vcl_hit { + if (obj.ttl < 0s && std.healthy(req.backend_hint)) { + return (deliver); + } + } + + sub vcl_deliver { + unset resp.http.X-Image-URL; + set resp.http.Access-Control-Allow-Origin = "*"; + + # Handle conditional request with ETag + if ( + req.http.If-None-Match && + req.http.If-None-Match == resp.http.ETag + ) { + return (synth(304)); + } + } diff --git a/.kubernetes/deployment.yml b/.kubernetes/deployment.yml index 67230f5..8a77890 100644 --- a/.kubernetes/deployment.yml +++ b/.kubernetes/deployment.yml @@ -31,6 +31,26 @@ spec: requests: cpu: 250m memory: 64Mi + - image: varnish:7.4 + imagePullPolicy: IfNotPresent + name: varnish + command: ['varnishd'] + args: ['-F', '-f', '/etc/varnish/default.vcl', '-a', ':6081', '-s', 'malloc,512m'] + volumeMounts: + - name: varnish-vcl + mountPath: /etc/varnish/default.vcl + subPath: default.vcl + resources: + limits: + cpu: 900m + memory: 828Mi + requests: + cpu: 250m + memory: 64Mi restartPolicy: Always imagePullSecrets: - name: ghcr-login-secret + volumes: + - name: varnish-vcl + configMap: + name: varnish-vcl