Note that the API of this VMOD is considered experimental at this time and may change in subsequent releases. Such changes will be highlighted in the release notes.
DESCRIPTION
Varnish Configuration Language module (VMOD) for Data Dome integration for Varnish Enterprise. Even though some of the examples show how you can use the VMOD without including a bundled VCL, Varnish Enterprise customers are encouraged to learn about the bundled VCL file “datadome_api.vcl” which reduces the amount of boilerplate code when using the VMOD.
This VMOD uses an external service for bot detection. This means that some data gets forwarded to this external service for decision making. It is therefore important that you check with the vendor for what type of data is used and collected, as you might be obligated to inform your users about this process.
EXAMPLES
These examples check all non-static requests against the DataDome service. For most use cases, there are many URLs that do not need to be checked, typically because serving a small static resource is cheaper than getting an approval from DataDome. The bundled VCL skips validation with DataDome for file extensions that are known “static” data types.
.. vcl-start
Simple
vcl 4.1;
import datadome;
import urlplus;
sub vcl_init {
new dd_api = datadome.init();
dd_api.set_key("KEY");
}
sub vcl_recv {
if (urlplus.get_extension() !~ "^(avi|avif|bmp|css|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|json|less|map|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|otf|png|svg|svgz|swf|ttf|wav|webm|webp|woff|woff2|xml|zip)$") {
dd_api.check_request();
if (req.http.X-DataDomeResponse == "200") {
# Request passed
} else if (req.http.X-DataDomeResponse == "301") {
# Request redirected
return (synth(301, "DataDome"));
} else if (req.http.X-DataDomeResponse == "302") {
# Request redirected
return (synth(302, "DataDome"));
} else if (req.http.X-DataDomeResponse == "400") {
# It would be wrong to block a request just
# because the DataDome API server does not like
# our key.
} else if (req.http.X-DataDomeResponse == "401") {
# Request blocked
return (synth(401, "DataDome"));
} else if (req.http.X-DataDomeResponse == "403") {
# Request blocked
return (synth(403, "DataDome"));
} else if (req.http.X-DataDomeResponse == "429") {
# Request rate limited
return (synth(429, "DataDome"));
} else {
# Unknown response - Fail-open
}
}
}
sub vcl_synth {
if (!req.is_reset && req.http.X-DataDomeResponse) {
if (dd_api.synth()) {
return (deliver);
}
}
}
sub vcl_backend_fetch {
if (bereq.http.X-DataDomeResponse) {
dd_api.fetch();
}
}
sub vcl_deliver {
if (req.http.X-DataDomeResponse) {
dd_api.deliver();
}
}
.. vcl-end
.. vcl-start
Using bundled VCL
vcl 4.1;
include "datadome_api.vcl";
sub vcl_init {
dd_api.set_key("KEY");
}
sub vcl_recv {
call datadome_api_check_request;
}
.. vcl-end
API
OBJECT init()
Arguments: None
Type: Object
Returns: Object.
VOID .set_key(STRING key)
Insert your DataDome Protection API key here. Make sure this is the Server-side Module key.
DataDome will respond with HTTP status code 400 (Bad Request) if the key is
either left blank or is invalid. The VMOD will not fail the VCL from loading or
block request when this status code is returned. Whenever this happens the
counter VMOD_DATADOME.req_bad is increased.
Arguments:
key accepts type STRINGType: Method
Returns: None
Restricted to: vcl_init
VOID .set_host(STRING host)
Change the endpoint DataDome Protection API is served from.
The default host is https://api.datadome.co.
It is possible to use a static IP endpoints from this list:
https://docs.datadome.co/docs/static-ip-endpoints
However, it is not necessary to change the host unless you have a special need.
Arguments:
host accepts type STRINGType: Method
Returns: None
Restricted to: vcl_init
VOID .set_path(STRING path)
It is possible to overwrite the URL path for validating requests.
The default URL path is /validate-request/.
Arguments:
path accepts type STRINGType: Method
Returns: None
Restricted to: vcl_init
VOID .set_timeout(ENUM {RESPONSE} type, DURATION timeout)
It is possible to timeout validation requests to DataDome.
A zero timeout will return immediately, where as a negative timeout will block
forever. The default timeout is 300ms (1500ms under varnishtest)
Arguments:
timeout accepts type DURATION
type is an ENUM that accepts values of RESPONSE
Type: Method
Returns: None
Restricted to: vcl_init
VOID .filter(ENUM {Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, AuthorizationLen, CacheControl, ClientID, Connection, ContentType, CookiesLen, CookiesList, From, HeadersList, Host, IP, JA3, McpProtocolVersion, McpSessionId, Method, ModuleVersion, Origin, Port, PostParamLen, Pragma, Protocol, Referer, Request, RequestModuleName, SecCHDeviceMemory, SecCHUA, SecCHUAArch, SecCHUAFullVersionList, SecCHUAMobile, SecCHUAModel, SecCHUAPlatform, SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser, ServerHostname, ServerName, Signature, SignatureAgent, SignatureInput, SkyfirePayId, TimeRequest, TlsCipher, TlsProtocol, TrueClientIP, UserAgent, Via, XForwardedForIP, XRequestedWith, XRealIP} field, ENUM { FORWARD, RETAIN } choice)
For each of the possible values of field, this can be used to choose if
the corresponding information will be forwarded in the validation request to
DataDome. Note that this method is only available in vcl_init, so the
filter is fixed when vcl_init has finished. If you use more than one filter
because you might need to change it on a per request basis, you need to create
more than one VMOD object and use the object whose filter matches the filter
you want on a particular request. If a parameter is never set, a default
based on DataDome’s recommendation will be used. The recommendation, and
therefore the default, is subject to change.
The DataDome API server might consider some field to be mandatory and
filtering them might result in the VMOD not working anymore as intended.
Please consider informing your users about what type of data is sent to an external service for bot detection.
Please refer to the official DataDome documentation for what kind of data is
sent with each field: https://docs.datadome.co/reference/validate-request
The “Host” and “ServerHostname” field represent the same value which is the
value of the “Host” header.
Arguments:
field is an ENUM that accepts values of Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, AuthorizationLen, CacheControl, ClientID, Connection, ContentType, CookiesLen, CookiesList, From, HeadersList, Host, IP, JA3, McpProtocolVersion, McpSessionId, Method, ModuleVersion, Origin, Port, PostParamLen, Pragma, Protocol, Referer, Request, RequestModuleName, SecCHDeviceMemory, SecCHUA, SecCHUAArch, SecCHUAFullVersionList, SecCHUAMobile, SecCHUAModel, SecCHUAPlatform, SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser, ServerHostname, ServerName, Signature, SignatureAgent, SignatureInput, SkyfirePayId, TimeRequest, TlsCipher, TlsProtocol, TrueClientIP, UserAgent, Via, XForwardedForIP, XRequestedWith, and XRealIP
choice is an ENUM that accepts values of FORWARD, and RETAIN
Type: Method
Returns: None
Restricted to: vcl_init
VOID .check_request()
Validate the request against the DataDome Protection API.
A POST request is sent to the host (settable through .set_host() in
vcl_init) with a given path (settable through .set_path() in
vcl_init).
The data is encoded using application/x-www-form-urlencoded
(i.e., key-value tuples separated by an ampersand (&)). The first key-value pair
added is the Key (settable through .set_key() in vcl_init). The
remaining set of key-value pairs depends on the policy employed by .filter()
in vcl_init and non-empty values. Each field that is inserted into the FORM
is logged using the DataDome VSL tag.
The DataDome Protection API enforces field limits for some fields and a combined
total size of 24 kB for all fields. When a field limit is reached, the
corresponding value is truncated. However, the request is not sent to DataDome
when the total size is consumed. It is treated the same as if 200 was returned
from DataDome and the counter VMOD_DATADOME.req_body_overflow is increased.
The client workspace is used as a scratch buffer and the reservation can be up
to 24 kB, but a minimum of 512 B of space is required. When this happens, the
counter VMOD_DATADOME.client_workspace_overflow is increased.
The result is written to the X-DataDomeResponse header. A call to
.check_request() will always populate this header with a status code.
The status code 503 is also produced by Varnish when encountering
unrecoverable error condition (e.g., out of workspace). Other non-critical error
paths get a 200 status code, indicating that the client request should not be
blocked. One example is when Varnish fails to get a valid and timely response
from DataDome.
DataDome recommend to not check static content and that is why the bundled VCL will ignore such content.
This method can only be called once.
Arguments: None
Type: Method
Returns: None
Restricted to: vcl_recv
INT .synth()
Forward response returned by the DataDome API to the client. Response headers received from the DataDome API are also added to the client response.
This method will return 1 on success.
Arguments: None
Type: Method
Returns: Int
Restricted to: vcl_synth
VOID .deliver()
Add response headers received from the DataDome API to the client response.
Arguments: None
Type: Method
Returns: None
Restricted to: vcl_deliver
VOID .fetch()
Remove the X-DataDomeResponse header from the request.
Arguments: None
Type: Method
Returns: None
Restricted to: vcl_backend_fetch
AVAILABILITY
The datadome VMOD is available in Varnish Enterprise version 6.0.16r13
and later.