Skip to content

Commit dc27a4a

Browse files
committed
administrate(varnish): add auth/restrict examples
1 parent 2f88118 commit dc27a4a

File tree

1 file changed

+223
-9
lines changed

1 file changed

+223
-9
lines changed

content/doc/administrate/cache.md

Lines changed: 223 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ type: docs
1515

1616
## Overview
1717

18-
[Varnish](https://www.varnish-cache.org/) is a HTTP proxy-cache, which works as a reverse proxy between your application
19-
and the client. Following rules defined by the user, Varnish will cache the data of an application to reduce the load on its server. We use **Varnish 7.7.1 and varnish-modules 0.26.0**.
18+
[Varnish](https://www.varnish-cache.org/) is a HTTP proxy-cache, which works as a reverse proxy between your application and the client. Following rules defined by the user, Varnish will cache the data of an application to reduce the load on its server. We use **Varnish 7.7.1 and varnish-modules 0.26.0**.
2019

2120
## Limitations
2221

@@ -34,7 +33,11 @@ The `vcl 4.1;` and backend section of the `varnish.vcl` configuration file are n
3433
If you have a PHP FTP application or if your `varnish.vcl` file is on an FS Bucket, make sure you redeploy the application for the changes to take effect.
3534
{{< /callout >}}
3635

37-
To know how to write your `varnish.vcl` file, have a look at the [Varnish 6 book](https://info.varnish-software.com/resources/varnish-6-by-example-book).
36+
To learn how to write your `varnish.vcl` file, read:
37+
- [Varnish documentation](https://varnish-cache.org/docs/)
38+
- [Varnish 6 book](https://info.varnish-software.com/resources/varnish-6-by-example-book)
39+
40+
If you already have a configuration for an older version of Varnish, read [the upgrading to 7.0 guide](https://varnish-cache.org/docs/7.0/whats-new/upgrading-7.0.html).
3841

3942
## Listen on the right port
4043

@@ -48,15 +51,226 @@ You can change the storage size specified in the varnish.params file with the `C
4851
CC_VARNISH_STORAGE_SIZE=2G
4952
```
5053

51-
## Varnish 6 migration
54+
## Varnish to cache your application's content
55+
56+
We provide some [examples of Varnish configuration files](https://GitHub.com/CleverCloud/varnish-examples) that you can use for your application.
57+
58+
## Varnish to restrict access to your application
59+
60+
As Varnish acts as a reverse proxy, you can also use it to chose whether requests should be forwarded to your application, based on the Authorization header or the IP address of the client for example.
61+
62+
>[!NOTE] Varnish on Clever Cloud and client IP
63+
>Clever Cloud's applications are behind a load balancer, which means that the `client.ip` variable in Varnish will always be the IP address of the load balancer. To get the real client IP address, you should use the `X-Forwarded-For` or `Forwarded` headers.
64+
65+
### Block IP addresses
66+
67+
```bash {filename="clevercloud/varnish.vcl"}
68+
sub vcl_recv {
69+
# Local health check
70+
if (client.ip == "127.0.0.1" && !req.http.X-Forwarded-For) {
71+
return (synth(200, "OK"));
72+
}
73+
74+
# We don't rely on client.ip which send the load balancer IP address
75+
# We check if the IP to block is included in the Forwarded header instead
76+
# Replace X.X.X.X and Y.Y.Y.Y with the IP addresses you want to block
77+
if (req.http.Forwarded ~ "X.X.X.X" ||
78+
req.http.Forwarded ~ "Y.Y.Y.Y" ) {
79+
return (synth(403, "Blocked"));
80+
}
81+
82+
# Use return (hash); to use the cache
83+
return (pass);
84+
}
85+
86+
sub vcl_synth {
87+
if (resp.status == 403) {
88+
set resp.http.Content-Type = "text/plain";
89+
synthetic("Access denied");
90+
return (deliver);
91+
}
92+
}
93+
```
94+
95+
To be able to configure multiple IPs to block, CIDR, exceptions, with an environment variable, use the following example:
96+
97+
- [Varnish multiple IP blocking with environment variable](https://github.com/CleverCloud/varnish-examples/blob/main/varnish-ip-blocking/varnish.vcl)
98+
99+
### Basic authentication
100+
101+
To ask for a login/password, you can use [Basic authentication](https://www.rfc-editor.org/rfc/rfc7617.txt) and the `Authorization` header:
102+
103+
```bash {filename="clevercloud/varnish.vcl"}
104+
sub vcl_recv {
105+
# Local health check
106+
if (client.ip == "127.0.0.1" && !req.http.X-Forwarded-For) {
107+
return (synth(200, "OK"));
108+
}
109+
110+
if (!req.http.Authorization) {
111+
return (synth(401, "Authentication Required"));
112+
}
113+
114+
if (req.http.Authorization !~ "^Basic ") {
115+
return (synth(401, "Basic Authentication Required"));
116+
}
117+
118+
set req.http.X-Auth-Credentials = regsub(req.http.Authorization, "^Basic ", "");
119+
120+
# Replace CREDENTIALS with your base64 encoded login:password
121+
# You can generate it with this command: echo -n "username:password" | base64
122+
if (req.http.X-Auth-Credentials != "CREDENTIALS" &&
123+
req.http.X-Auth-Credentials != "ANOTHER_CREDENTIALS") {
124+
return (synth(401, "Valid Basic Authentication Required"));
125+
}
126+
127+
# Remove the Authorization header to avoid sending it to the backend
128+
unset req.http.X-Auth-Credentials;
129+
unset req.http.Authorization;
130+
131+
# Use return (hash); to use the cache
132+
return (pass);
133+
}
134+
135+
sub vcl_synth {
136+
if (resp.status == 200) {
137+
set resp.http.Content-Type = "text/plain";
138+
synthetic("OK");
139+
return (deliver);
140+
}
141+
142+
if (resp.status == 401) {
143+
set resp.http.Content-Type = "text/html; charset=utf-8";
144+
set resp.http.WWW-Authenticate = "Basic realm='Restricted Area'";
145+
synthetic("<html><body><h1>Authentication Required</h1></body></html>");
146+
147+
return (deliver);
148+
}
149+
}
150+
```
151+
152+
To avoid writing your credentials in the `varnish.vcl` file, you can use an environment variable to store them:
153+
154+
- [Varnish Basic authentication with environment variable](https://github.com/CleverCloud/varnish-examples/blob/main/varnish-ip-blocking/varnish.vcl)
52155
53-
If you already have a configuration for an older version of varnish, you can read this [guide](https://varnish-cache.org/docs/6.0/whats-new/upgrading-6.0.html) to upgrade to version 6.
156+
### Bearer token authentication
54157
55-
## Example files
158+
To protect access to an API, using a Bearer token is a common practice. You can use Varnish to check the `Authorization` header and validate the Bearer token before forwarding the request to your application:
56159
57-
We provide some [examples of Varnish configuration files](https://GitHub.com/CleverCloud/varnish-examples) that you can
58-
use for your application. Create a `/clevercloud` folder at the root of your application if it does not exist,
59-
rename the file to `varnish.vcl` and move it in the `/clevercloud` folder.
160+
```bash {filename="clevercloud/varnish.vcl"}
161+
sub vcl_recv {
162+
# Local health check
163+
if (client.ip == "127.0.0.1" && !req.http.X-Forwarded-For) {
164+
return (synth(200, "OK"));
165+
}
166+
167+
if (!req.http.Authorization) {
168+
return (synth(401, "Authentication Required"));
169+
}
170+
171+
if (req.http.Authorization !~ "^Bearer ") {
172+
return (synth(401, "Bearer token Required"));
173+
}
174+
175+
set req.http.X-Token = regsub(req.http.Authorization, "^Bearer ", "");
176+
177+
# Replace YOUR_BEARER_TOKEN and ANOTHER_BEARER_TOKEN with your actual tokens
178+
if (req.http.X-Token != "YOUR_BEARER_TOKEN" &&
179+
req.http.X-Token != "ANOTHER_BEARER_TOKEN") {
180+
return (synth(401, "Valid Bearer token Required"));
181+
}
182+
183+
# Remove the Authorization header to avoid sending it to the backend
184+
unset req.http.X-Token;
185+
unset req.http.Authorization;
186+
187+
# Use return (hash); to use the cache
188+
return (pass);
189+
}
190+
191+
sub vcl_synth {
192+
if (resp.status == 200) {
193+
set resp.http.Content-Type = "text/plain";
194+
synthetic("OK");
195+
return (deliver);
196+
}
197+
198+
if (resp.status == 401) {
199+
set resp.http.Content-Type = "text/html; charset=utf-8";
200+
synthetic("<html><body><h1>Authentication Required</h1></body></html>");
201+
202+
return (deliver);
203+
}
204+
}
205+
```
206+
207+
To avoid writing your credentials in the `varnish.vcl` file, you can use an environment variable to store them:
208+
209+
- [Varnish Bearer token with environment variable](https://github.com/CleverCloud/varnish-examples/blob/main/varnish-ip-blocking/varnish.vcl)
210+
211+
## Varnish to throttle your application's traffic
212+
213+
You can use Varnish to limit the number of requests per second to your application, which is useful to prevent abuse or to protect your application from DDoS attacks. This can be combined with other Varnish rules and features.
214+
215+
```bash {filename="clevercloud/varnish.vcl"}
216+
import vsthrottle;
217+
218+
sub vcl_recv {
219+
220+
# Local health check
221+
if (client.ip == "127.0.0.1" && !req.http.X-Forwarded-For) {
222+
return (synth(200, "OK"));
223+
}
224+
225+
# For other requests, we check the Forwarded header to extract the client IP and refuse access if it's malformed or missing
226+
if (!req.http.Forwarded) {
227+
return (synth(403, "Access Denied"));
228+
}
229+
230+
# Expected format is proto=https;for=IP:PORT;by=IP
231+
if (req.http.Forwarded !~ "^proto=https;for=[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+;by=[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") {
232+
return (synth(403, "Access Denied"));
233+
}
234+
235+
# We get and check the client IP format
236+
set req.http.X-Client-IP = regsub(req.http.Forwarded, "^proto=https;for=([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}):[0-9]+;by=.*$", "\1");
237+
if (req.http.X-Client-IP !~ "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") {
238+
return (synth(403, "Access Denied"));
239+
}
240+
241+
# Limit to 10 requests per 1 minute
242+
# It's good values for tests, use higher values in production
243+
if (vsthrottle.is_denied(req.http.X-Client-IP, 10, 1m)) {
244+
return (synth(429, "Rate limit exceeded"));
245+
}
246+
247+
# Use return (hash); to use the cache
248+
return (pass);
249+
}
250+
251+
sub vcl_synth {
252+
if (resp.status == 403) {
253+
set resp.http.Content-Type = "text/plain";
254+
synthetic("Access Denied");
255+
return (deliver);
256+
}
257+
258+
if (resp.status == 429) {
259+
set resp.http.Retry-After = "60";
260+
set resp.http.Content-Type = "text/plain";
261+
synthetic("Rate limit exceeded. Please try again later.");
262+
return (deliver);
263+
}
264+
}
265+
266+
# Send informations about rate limit and remaining requests in response headers
267+
sub vcl_deliver {
268+
set resp.http.X-RateLimit-Limit = "10";
269+
set resp.http.X-RateLimit-Remaining = "" + vsthrottle.remaining(req.http.X-Client-IP, 10, 1m);
270+
271+
unset resp.http.X-Client-IP;
272+
}
273+
```
60274
61275
## Varnish with a monorepo
62276

0 commit comments

Comments
 (0)