How to manage your own Fastly configuration (custom VCL)

Goal

Review and take control of the Fastly CDN cache rules that are in place for your project.

Context

If you are using a Platform.sh supplied Fastly service, under a standard Enterprise support plan, it will be set up with the basic optimisation and protection settings that should be appropriate to your type of application.
But if you want to add app-specific custom rules or behaviours, blocklists or bot restrictions, this is not done by the Platform.sh infrastructure team, as:

  • It can get complex and requires a deep understanding of the desired result.
  • This is regarded as your application logic, not infrastructure.

Your project will have access to the Fastly service ID key ($FASTLY_API_SERVICE) and a Fastly API token ($FASTLY_API_TOKEN) that can be used to interact with the Fastly API for rule management directly.

These environment variables are also available for application-level plugins such as the Drupal fastly.module Or the Magento one - which may be worth investigating for basic Fastly service manipulation before getting in to custom VCLs. In most circumstances, just enabling the module is enough, it will auto-configure itself from the environment variables.

All Platform.sh projects that have Fastly services attached to the production instance, also provide a separate test Fastly service as well, attached to your staging branch for testing.

Requirements

  • The following instructions are commands to be run assuming you are on a local development environment, in your project directory, with a working *nix-like command shell.

  • You must have the platform CLI tool installed and authenticated.

  • Common CLI utilities like curl and jq are expected to be available.

Important

Exactly what you choose to do via VCL rules and the API may be complex, and cannot be set, debugged, or supported by the Platform.sh Infrastructure support team for you.

It’s quite possible to break things, or impact your traffic in unwanted ways if you get this wrong, so always test on your staging branch first when attempting cache changes.

Steps

Set up some context variables

To be explicit, these instructions will use the following variables.
If you set them for your own project, then the following platform commands will not need to be given the --project & --environment flags explicitly.

export PLATFORM_PROJECT=2rfibv66j424w
export PLATFORM_BRANCH=master
 # or `main` or `production`

Alternatively, you should be able to just cd inside your local copy of the Platform.sh project root and the project and branch may be auto-detected.

Run:

platform environment:info

to ensure your project connection is correct and authenticated.

Retrieve your Fastly Authentication keys

FASTLY_SERVICE_ID=$(platform ssh "echo \$FASTLY_SERVICE_ID" | sed -e 's/[[:space:]]//g' )
FASTLY_API_TOKEN=$(platform ssh "echo \$FASTLY_API_TOKEN" | sed -e 's/[[:space:]]//g' )
FASTLY_API_URL="https://api.fastly.com"

You now have access to the Fastly API, with commands like:

echo curl -s -H \"Fastly-Key: $FASTLY_API_TOKEN\"  "${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/details"

curl -s -H "Fastly-Key:p8Y21lH KMAbBxAQubjH-JuJ7dyqjJAfI" https://api.fastly.com/service/r53ogV1Om7TiVDPQiF7zbD/details

That query returns a response that includes lots of info like:

{
  ...
  "name": "ni4byfwnx7wq6",
  "id": "4zYfQk6gWNKY4MSqieyK0A",
  "type": "vcl",
  "version": {
    ...
    "service_id": "4zYfQk6gWNKY4MSqieyK0A",
    "active": true,
    "testing": false,
    "acls": [
      {
        "name": "Generated_by_IP_block_list",
        "id": "4zYfQk6gWNKY4MSqieyK0A"
      }
    ],
    "backends": [
      {
        "address": "gw.au.platformsh.site",
        "port": 443,
        "min_tls_version": null,
        "ssl_hostname": null,
        "connect_timeout": 60000,
    ....

Fetch current VCL

A VCL is a text file used to define cache behaviour. Retrieving the VCL will tell you all the rules currently in place on your service. On Fastly, each VCL state is versioned.
For more VCL manipulation info, check the API docs

Fetch the vcl version number, as this is often needed.

FASTLY_SERVICE_VERSION=$(
curl -s -H "Fastly-Key: $FASTLY_API_TOKEN" \
    "${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/details" \
    | jq -r ".active_version.number"
)

Review the full vcl

This makes a call to the Fastly API, using your service credentials, and returns a copy of the raw VCL as a file for you to inspect locally. Fastly docs on this API call.

curl -s -H "Fastly-Key: $FASTLY_API_TOKEN" \
"${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/version/${FASTLY_SERVICE_VERSION}/generated_vcl" \
| jq -r ".content" > generated.vcl

Conclusion

You now have access to the full, technical dump of the contents of all the cache rules in place.

Actually understanding the VCL is out of scope for Platform.sh support docs or staff, you should start with the Technical VCL Reference docs.

Advanced

Adding your own VCL snippets

Use the above credentials with the documented API calls for VCL management
to push your own rules into the service.

Only try this on your staging branch first.

Reviewing IP blocklists

The one Fastly service update that Platform.sh support staff may do if it’s included in your support plan is to block IPs in response to bot flood attacks. A list of currently blocked IP numbers may be an ACL (Access Control List) attached to your production Fastly service

  • Only the production instance is maintained like this, your staging instance will probably not contain the same settings.

By convention, this is IP block list managed as a dictionary and can be retrieved like this:

# For all autogenerated ip block rules, the name is this:
ACL_RULE_NAME="Generated_by_IP_block_list"
# Need to fetch its ID before we can find its content.
ACL_ID=$( curl -s -H "Fastly-Key: $FASTLY_API_TOKEN" \
"${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/version/${FASTLY_SERVICE_VERSION}/acl/${ACL_RULE_NAME}"  | jq -r ".id")

Request the individual IPs to see what’s being blocked

curl -s -H "Fastly-Key: $FASTLY_API_TOKEN" \
    "${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/acl/${ACL_ID}/entries" | jq -r ".[].ip"

181.214.61.65
184.75.209.26
185.107.95.210
185.189.114.51
185.242.6.3
...

^ This command parses out the IPs for you directly.

Why was this IP blocked?

You can skip the -r ".[].ip" section at the end to see the whole data objects. There you may find a comment referencing the date and reason for the block.

  {
    "created_at": "2021-10-02T03:24:54Z",
    "service_id": "WNKSqieyK0fQY44zYk6gMA",
    "ip": "104.254.92.60",
    "subnet": null,
    "id": "3RtHNHPJpKnvamtIE9XTAL",
    "negated": "0",
    "comment": "Bot zd152719",
    "acl_id": "VMlv2Rasj80nBNBIeba65h",
    "updated_at": "2021-10-02T03:29:13Z"
  },
  {
    "acl_id": "VMlv2Rasj80nBNBIeba65h",
    "negated": "0",
    "comment": "bot/dos 2021-11-12",
    "updated_at": "2021-11-12T15:30:32Z",
    "id": "32uamxFlNds1fvtzQzah3p",
    "subnet": 20,
    "created_at": "2021-11-12T15:30:32Z",
    "ip": "107.152.96.0",
    "service_id": "WNKSqieyK0fQY44zYk6gMA"
  },
2 Likes

You can avoid the manual steps!

Fastly now provides a CLI tool that you should be able to use to get access to all Fastly functionality directly!
https://github.com/fastly/cli

1 Like

To use the Fastly CLI, quickstart:

# Declare your project ID (or just move into your working directory)
export PLATFORM_PROJECT=2rfibv66j424w
export PLATFORM_BRANCH=main
# Then extract and remember the Fastly service ID info. 
# Fastly API will use these environment vars from now on to make invocation easier. 
export FASTLY_SERVICE_ID=$(platform ssh "echo \$FASTLY_SERVICE_ID" | sed -e 's/[[:space:]]//g' )
export FASTLY_API_TOKEN=$(platform ssh "echo \$FASTLY_API_TOKEN" | sed -e 's/[[:space:]]//g' )

fastly service describe

And you should see the information about your Fastly service that is wrapping this environment.

See the CLI docs, it mirrors the API functions, but is a lot quicker to use.

fastly stats historical --from="2 days ago"

Remember, if working with an active Fastly service, you have to first

  • clone the active version
  • edit the new latest version (eg add a snippet)
  • activate the latest version

It may look something like:

fastly service-version clone --version=active
fastly vcl snippet create --version=latest --name="Re-Enable shielding"  --content="./re_enable_shielding.vcl_snippet"
fastly service-version activate --version=latest
1 Like

Adding a couple more commands to show how to add IPs to the list and how to remove them via their IDs

View Blocked IPs and the ID

curl -s -H "Fastly-Key: $FASTLY_API_TOKEN" \
  "${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/acl/${ACL_ID}/entries" | jq -r ".[] | {\"id\": .id, \"ip\": .ip } "

Add an IP to the Block list

curl -X POST -s -H "Fastly-Key: $FASTLY_API_TOKEN" -H "Content-Type: application/json"  -H "Accept: application/json"  \
    "${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/acl/${ACL_ID}/entry" -d "{\"ip\":\"192.168.0.1\"}"

Delete an IP address from the Block list via it’s ID

curl -s -H "Fastly-Key: $FASTLY_API_TOKEN" -H "Accept: application/json" \
   -X DELETE "${FASTLY_API_URL}/service/${FASTLY_SERVICE_ID}/acl/${ACL_ID}/entry/lExhSsWk3QVyrAt5hykZ34"

Based on the above information I’ve written a tool to auto add IPs to the fastly block list based on whatever is in the table ban_ip in your application.

This allows you to use a tool like autoban for drupal
Or write your own code to detect and block ips right in your app. They will automatically be synced to fastly.

Source: GitHub - matthiaz/platformsh-tools