Using Next-Gen WAF in Compute

The guidance on this page was tested with an older version (0.10.5) of the Rust SDK. It may still work with the latest version (0.11.0), but the change log may help if you encounter any issues.

Pass requests to Fastly's Next-Gen Web Application Firewall (Next-Gen WAF) from Compute code and make decisions based on the analysis response.

Prerequisites

The Next-Gen WAF is disabled by default. To purchase the product and then enable Compute for your Next-Gen WAF corp (also known as account), contact sales@fastly.com. Once enabled, users assigned the role of superuser or engineer can enable the Next-Gen WAF for your Compute services.

Instructions

IMPORTANT: This tutorial assumes that you already have the Fastly CLI installed. If you are new to the platform, check out our guidance on getting started, which includes CLI installation instructions.

Initialize a project

If you haven't already created a Rust-based Compute project, run fastly compute init in a new directory in your terminal and follow the prompts to provision a new service using the empty Rust starter kit:

$ mkdir my-ngwaf-app && cd my-ngwaf-app
$ fastly compute init
Creating a new Compute project.
Press ^C at any time to quit.
Name: [my-ngwaf-app]
Description: A low quality image placeholder (LQIP) generator, at the edge.
Author: My Name
Language:
[1] Rust
[2] JavaScript
[4] Other ('bring your own' Wasm binary)
Choose option: [1]
Starter kit:
[1] Default starter for Rust
A basic starter kit that demonstrates routing, simple synthetic responses and overriding caching rules.
https://github.com/fastly/compute-starter-kit-rust-default
[2] Empty starter for Rust
An empty starter kit project template.
https://github.com/fastly/compute-starter-kit-rust-empty
Choose option or paste git URL: [2]

After the command has completed, you'll have some new files and folders in the working directory.

Configure a backend

Your Compute service needs a backend from which it can load your website. For this tutorial, any backend will do, so we'll use http-me.glitch.me, a backend that returns predictable HTTP responses for development and testing purposes. Add the following lines to the fastly.toml file to inform the CLI that you want to create this backend when you deploy the application later:

fastly.toml
TOML
[setup]
[setup.backends]
[setup.backends.content_backend]
address = "http-me.glitch.me"
port = 443

HINT: The fastly.toml file specifies configuration related to a variety of resources, including config stores and logging endpoints. Check out our reference documentation about this package manifest format to learn more.

Configure the Next-Gen WAF inspector

To inspect requests, the Next-Gen WAF client must be configured with your corp (also known as account) and site (also known as workspace).

The best way to store configuration data for Compute services is using config stores, which allow you to share that data across services and update it without redeploying your service.

You can specify the requirement for a config store named ngwaf in the fastly.toml file, and the Fastly CLI will prompt you to create the store and populate it with values when you deploy the service for the first time. To do this, add the following lines to the fastly.toml file in the [setup] section:

fastly.toml
TOML
[setup.config_stores]
[setup.config_stores.ngwaf]
description = "Next-gen WAF configuration"
[setup.config_stores.ngwaf.items]
[setup.config_stores.ngwaf.items.corp]
[setup.config_stores.ngwaf.items.site]

This allows you to open the config store in your Rust code and read the corp (account) and site (workspace) values. Add the following code to the top of the request handler in your src/main.rs file:

src/main.rs
Rust
let ngwaf_config = fastly::config_store::ConfigStore::open("ngwaf");
let corp_name = ngwaf_config
.get("corp")
.expect("no `corp` present in config");
let site_name = ngwaf_config
.get("site")
.expect("no `site` present in config");

Next, initialize the Next-Gen WAF client with the corp (account) and site (workspace) values. Add the following lines below the config code that you just added:

src/main.rs
Rust
let (req_handle, req_body) = req.into_handles();
let req_body = req_body.unwrap_or_else(|| BodyHandle::new());
let config = InspectConfig::new(&req_handle, &req_body)
.corp(corp_name)
.workspace(site_name);

Inspect the request and handle the verdict

The Next-Gen WAF is now available to inspect requests. The inspect function returns a verdict that your code can use to decide how to handle the request. Add the following code to inspect the request and forward it to the origin only if the verdict is to allow the request:

src/main.rs
Rust
match inspect(config) {
Ok(resp) => match resp.verdict() {
InspectVerdict::Block => Ok(Response::from_status(StatusCode::NOT_ACCEPTABLE)),
InspectVerdict::Allow => {
Ok(Request::from_handles(req_handle, Some(req_body)).send("content_backend")?)
},
InspectVerdict::Unauthorized => {
panic!("The service is not authorized to inspect the request")
},
_ => Ok(Response::from_status(StatusCode::INTERNAL_SERVER_ERROR)
.with_body("Unable to inspect request")),
},
Err(err) => {
let msg = format!("Invalid request: {err:?}");
Ok(Response::from_status(StatusCode::BAD_REQUEST).with_body(msg))
}
}

Your code is ready! You can now deploy the service.

Deploy the service

To deploy the service, run the following command. Make sure to have your Next-Gen WAF corp (account) and site (workspace) names available, as you'll be prompted to enter them here:

$ fastly compute publish
✓ Verifying fastly.toml
✓ Identifying package name
✓ Identifying toolchain
✓ Running [scripts.build]
✓ Creating package archive
SUCCESS: Built package (pkg/fastly-compute-project.tar.gz)
✓ Verifying fastly.toml
There is no Fastly service associated with this package. To connect to an existing service add the Service ID to the fastly.toml
file, otherwise follow the prompts to create a service now.
Press ^C at any time to quit.
INFO: Processing of the fastly.toml [setup] configuration happens only when there is no existing service. Once a
service is created, any further changes to the service or its resources must be made manually.
Create new service: [y/N] y
Service name: [fastly-compute-project]
✓ Creating service
Domain: [annually-polite-akita.edgecompute.app]
Configure a backend called 'content_backend'
Hostname or IP address: [http-me.glitch.me]
Port: [443]
Configuring config store 'ngwaf'
Next-gen WAF configuration
Create a config store key called 'corp'
Value: [example] my-corp-name
Create a config store key called 'site'
Value: [example] my-site-name
✓ Creating domain 'annually-polite-akita.edgecompute.app'
✓ Creating backend 'content_backend' (host: http-me.glitch.me, port: 443)
✓ Creating config store 'ngwaf'
✓ Creating config store item 'corp'
✓ Creating config store item 'site'
✓ Creating resource link between service and config store 'ngwaf'...
✓ Uploading package
✓ Activating service (version 1)
✓ Checking service availability (status: 200)
Manage this service at:
https://manage.fastly.com/configure/services/Tav180DsKiePBJ1MTb0zK2
View this service at:
https://annually-polite-akita.edgecompute.app
SUCCESS: Deployed package (service Tav180DsKiePBJ1MTb0zK2, version 1)

Your service is now deployed, but Next-Gen WAF is not yet linked to the service. Follow the instructions in the Edge WAF deployment tutorial to set up the WAF deployment for the newly-created Compute service.

Once this is complete, visit the link in the fastly compute publish output to view the service in your browser.

Test the Next-Gen WAF

Although visiting your service using your web browser will cause the request to be logged on the Next-Gen WAF dashboard, it is also helpful to be able to test that the inspection responses from the WAF are being handled correctly by your code. A great way to do this is with attack tooling, which you can try by following the Testing with attack tooling guide.