diff --git a/.gitignore b/.gitignore index 7379d00..bfe5ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ data/ instance/ venv/ .env -version.txt \ No newline at end of file +version.txt +deploy/varnish/default.vcl \ No newline at end of file diff --git a/deploy/varnish/default.vcl b/deploy/varnish/default.vcl deleted file mode 100644 index d7448a0..0000000 --- a/deploy/varnish/default.vcl +++ /dev/null @@ -1,56 +0,0 @@ -vcl 4.1; - -backend app { - .host = "app"; - .port = "8080"; -} - -acl purge { "127.0.0.1"; } - -sub vcl_recv { - # PURGE tylko lokalnie - if (req.method == "PURGE") { - if (!client.ip ~ purge) { - return (synth(405, "Not allowed")); - } - return (purge); - } - - # omijamy cache dla healthchecków / wewn. nagłówka - if (req.url == "/healthcheck" || req.http.X-Internal-Check) { - return (pass); - } - - # metody inne niż GET/HEAD bez cache - if (req.method != "GET" && req.method != "HEAD") { - return (pass); - } - - # static – agresywnie cache’ujemy - if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { - return (hash); - } - - return (hash); -} - -sub vcl_backend_response { - if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { - set beresp.ttl = 24h; - } else { - if (beresp.http.Cache-Control ~ "no-cache|no-store|private") { - set beresp.uncacheable = true; - set beresp.ttl = 0s; - } else { - set beresp.ttl = 60s; # domyślny TTL dla HTML/API - } - } -} - -sub vcl_deliver { - if (obj.hits > 0) { - set resp.http.X-Cache = "HIT"; - } else { - set resp.http.X-Cache = "MISS"; - } -} diff --git a/deploy/varnish/default.vcl.template b/deploy/varnish/default.vcl.template index 8fe201d..e8f0032 100644 --- a/deploy/varnish/default.vcl.template +++ b/deploy/varnish/default.vcl.template @@ -1,34 +1,35 @@ vcl 4.1; +import vsthrottle; + backend app { .host = "app"; .port = "${APP_PORT}"; } -acl purge { "127.0.0.1"; } +acl purge { "localhost"; "127.0.0.1"; } sub vcl_recv { + # RATE LIMIT: 10 żądań / 10s, po przekroczeniu blokada na 30s + if (vsthrottle.is_denied(client.identity, 10, 10s, 30s)) { + return (synth(429, "Too Many Requests")); + } + # PURGE tylko lokalnie if (req.method == "PURGE") { - if (!client.ip ~ purge) { - return (synth(405, "Not allowed")); - } + if (!client.ip ~ purge) { return (synth(405, "Not allowed")); } return (purge); } # omijamy cache dla healthchecków / wewn. nagłówka - if (req.url == "/healthcheck" || req.http.X-Internal-Check) { - return (pass); - } + if (req.url == "/healthcheck" || req.http.X-Internal-Check) { return (pass); } # metody inne niż GET/HEAD bez cache - if (req.method != "GET" && req.method != "HEAD") { - return (pass); - } + if (req.method != "GET" && req.method != "HEAD") { return (pass); } - # static – agresywnie cache’ujemy - if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { - return (hash); + # statyczne – agresywny cache + if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { + return (hash); } return (hash); @@ -42,15 +43,27 @@ sub vcl_backend_response { set beresp.uncacheable = true; set beresp.ttl = 0s; } else { - set beresp.ttl = 60s; # domyślny TTL dla HTML/API + set beresp.ttl = 60s; # domyślny TTL } } } sub vcl_deliver { + if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } + + # Nagłówki rate limit – MUSZĄ używać tej samej czwórki parametrów co is_denied() + set resp.http.X-RateLimit-Limit = "10"; + set resp.http.X-RateLimit-Window = "10s"; + set resp.http.X-RateLimit-Remaining = vsthrottle.remaining(client.identity, 10, 10s, 30s); + set resp.http.Retry-After = vsthrottle.blocked(client.identity, 10, 10s, 30s); + + unset resp.http.Via; + unset resp.http.X-Varnish; + #unset resp.http.Age; + unset resp.http.Server; } diff --git a/deploy/varnish/default_throttle.vcl b/deploy/varnish/default_throttle.vcl deleted file mode 100644 index 9e5dbcc..0000000 --- a/deploy/varnish/default_throttle.vcl +++ /dev/null @@ -1,58 +0,0 @@ -vcl 4.1; - -import vsthrottle; - -backend app { - .host = "app"; - .port = "8080"; -} - -acl purge { "localhost"; "127.0.0.1"; } - -sub vcl_recv { - # RATE LIMIT - if (!vsthrottle.is_allowed(client.ip, 10, 10s)) { - return (synth(429, "Too Many Requests")); - } - - # PURGE tylko lokalnie - if (req.method == "PURGE") { - if (!client.ip ~ purge) { return (synth(405, "Not allowed")); } - return (purge); - } - - # omijamy cache dla healthchecków / wewn. nagłówka - if (req.url == "/healthcheck" || req.http.X-Internal-Check) { return (pass); } - - # metody inne niż GET/HEAD bez cache - if (req.method != "GET" && req.method != "HEAD") { return (pass); } - - # static – agresywnie cache’ujemy - if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { return (hash); } - - return (hash); -} - -sub vcl_backend_response { - if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { - set beresp.ttl = 24h; - } else { - if (beresp.http.Cache-Control ~ "no-cache|no-store|private") { - set beresp.uncacheable = true; - set beresp.ttl = 0s; - } else { - set beresp.ttl = 60s; # domyślny TTL dla HTML/API - } - } -} - -sub vcl_deliver { - if (obj.hits > 0) { - set resp.http.X-Cache = "HIT"; - } else { - set resp.http.X-Cache = "MISS"; - } - - set resp.http.X-RateLimit-Limit = "10"; - set resp.http.X-RateLimit-Window = "10s"; -} \ No newline at end of file