Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@ ngwaf-compute-integration/ngwaf_edge_deployment_link.sh
protect-cached-content/terraform.auto.tfvars
/protect-cached-content/registry.terraform.io
gold-standard-starter/terraform.auto.tfvars
on-prem-ngwaf-integrations/.DS_Store
*/.DS_Store
ngwaf-terraform-edge-deployment-unified-ui/terraform.auto.tfvars
/ngwaf-compute-interface/.cargo
ngwaf-compute-interface/Cargo.lock
4 changes: 4 additions & 0 deletions ngwaf-compute-interface/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
**/*.rs.bk
/bin
/pkg
17 changes: 17 additions & 0 deletions ngwaf-compute-interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "fastly-compute-project"
version = "0.1.0"
authors = []
edition = "2021"
# Remove this line if you want to be able to publish this crate on crates.io.
# Otherwise, `publish = false` prevents an accidental `cargo publish` from revealing private source.
publish = false

[profile.release]
debug = 1
codegen-units = 1
lto = "fat"

[dependencies]
fastly = "0.11.9"
serde_json = "1.0.143"
161 changes: 161 additions & 0 deletions ngwaf-compute-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Fastly Compute@Edge NGWAF Interface

This service performs WAF inspection on incoming requests and returns inspection results without forwarding to an origin backend.

## Features

- **Authentication**: Validates requests using a `cdn-secret` header
- **WAF Inspection**: Performs NGWAF inspection using Fastly's security inspection API
- **Custom Client IP**: Supports custom client IP via `x-source-ip` header
- **Detailed Response**: Returns JSON with inspection results including:
- Decision time in milliseconds
- Request ID (Fastly trace ID)
- WAF agent response status
- Security tags applied by NGWAF
- Inspection verdict
- **Response Headers**: Includes `waf-info` header with formatted inspection details

## Configuration

The service requires a Fastly Config Store named `ngwaf` with the following keys:
- `corp`: Your NGWAF corporation name
- `workspace`: Your NGWAF workspace name

## Request Headers

### Required
- `cdn-secret`: Must be set to `foo` (authentication header)

### Optional
- `x-source-ip`: Custom client IP address for WAF inspection (e.g., `169.254.5.5`)

## Response Format

### Success Response
```json
{
"decisionms": 123,
"requestid": "abc123...",
"agentResponse": 200,
"tags": ["tag1", "tag2"],
"verdict": "Allow"
}
```

### Response Headers
- `waf-info`: Formatted string with inspection details
- `compute-version`: Fastly service version
- `Content-Type`: application/json

### Status Codes
- `200-499`: Returns the status code from NGWAF inspection
- `500`: Returned if NGWAF status is outside 200-499 range
- `403`: Returned if `cdn-secret` header is missing or incorrect

## Setup

### 1. Link Service to NGWAF

Follow the [Fastly NGWAF documentation](https://www.fastly.com/documentation/guides/next-gen-waf/setup-and-configuration/edge-deployment/ngwaf-control-panel/setting-up-edge-waf-deployments-using-the-next-gen-waf-control-panel/#creating-the-edge-security-service) to create an edge security service.

```bash
curl -X PUT "https://dashboard.signalsciences.net/api/v0/corps/${corpName}/sites/${siteName}/edgeDeployment" \
-H "x-api-user:${SIGSCI_EMAIL}" \
-H "x-api-token:${SIGSCI_TOKEN}" \
-H "Fastly-Key: ${FASTLY_KEY}" \
-H "Content-Type: application/json" \
-d '{"authorizedServices": [ "${fastlySID}" ] }'
```

### 2. Verify Edge Deployment Configuration

```bash
curl -H "x-api-user:${SIGSCI_EMAIL}" -H "x-api-token:${SIGSCI_TOKEN}" \
-H "Content-Type: application/json" \
"https://dashboard.signalsciences.net/api/v0/corps/${corpName}/sites/${siteName}/edgeDeployment"
```

### 3. Configure Config Store

Create a config store named `ngwaf` with your corporation and workspace values:
```bash
fastly config-store create --name=ngwaf
fastly config-store-entry create --store-id=<store-id> --key=corp --value=<your-corp-name>
fastly config-store-entry create --store-id=<store-id> --key=workspace --value=<your-workspace-name>
```

## Test Requests

### Basic Request
```bash
curl -i "https://YOURDOMAIN/test" \
-H "cdn-secret: foo"
```

### With Custom Client IP
```bash
curl -i "https://YOURDOMAIN/test" \
-H "cdn-secret: foo" \
-H "x-source-ip: 169.254.5.5"
```

### Test with Suspicious User-Agent (should trigger NGWAF tags)
```bash
curl -i "https://YOURDOMAIN/anything/test" \
-H "cdn-secret: foo"
```

### Test with Path Traversal (should trigger NGWAF detection)
```bash
curl -i "https://YOURDOMAIN/test?path=../../../../etc/passwd" \
-H "cdn-secret: foo"
```

### Test Authentication Failure
```bash
curl -i "https://YOURDOMAIN/test"
# Should return 403 Forbidden
```

## Development

### Build
```bash
cargo build
```

### Deploy
```bash
fastly compute publish
```

## Implementation Details

### Modules

#### `main.rs`
- Entry point for the Compute@Edge service
- Validates `cdn-secret` header
- Delegates WAF inspection to `waf_inspection` module
- Returns 403 if authentication fails

#### `waf_inspection.rs`
Contains three main functions:

1. **`do_waf_inspection(req: Request)`**
- Reads NGWAF config from config store
- Extracts client IP from `x-source-ip` header if present
- Configures and executes NGWAF inspection
- Returns rebuilt request and inspection response

2. **`format_waf_inspection_header(inspect_resp: InspectResponse, client_req_id: &str)`**
- Formats inspection results into a header-friendly string
- Includes agent response, tags, decision time, and request ID

3. **`waf_inspect_and_respond(req: Request)`**
- Main orchestration function
- Adds metadata headers (`inspected-by`, `compute-version`)
- Performs WAF inspection
- Builds JSON response with inspection results
- Returns appropriate HTTP status code

30 changes: 30 additions & 0 deletions ngwaf-compute-interface/fastly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file describes a Fastly Compute package. To learn more visit:
# https://www.fastly.com/documentation/reference/compute/fastly-toml

authors = [""]
cloned_from = "https://github.com/fastly/compute-starter-kit-rust-empty"
description = ""
language = "rust"
manifest_version = 3
name = "ngwaf-compute-interface"
service_id = ""

[setup]
[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.workspace]

[local_server]
[local_server.config_stores]
[local_server.config_stores.ngwaf]
format = "inline-toml"
[local_server.config_stores.ngwaf.contents]
"corp" = "my_corp"
"workspace" = "my_workspace"

[scripts]
build = "cargo build --profile release"

4 changes: 4 additions & 0 deletions ngwaf-compute-interface/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
targets = [ "wasm32-wasip1" ]
profile = "default"
32 changes: 32 additions & 0 deletions ngwaf-compute-interface/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Interface for Fastly Compute@Edge with NGWAF

use fastly::{Error, Request, Response};

mod waf_inspection;

#[fastly::main]
fn main(req: Request) -> Result<Response, Error> {
// Reject request if the request header cdn-secret is not present or not equal to "foo"
match req.get_header("cdn-secret") {
Some(value) if value.to_str().unwrap_or("") == "foo" => {},
_ => {
return Ok(Response::from_status(403)
.with_body_text_plain("Forbidden"));
}
}

// Start - NGWAF
let resp = match waf_inspection::waf_inspect_and_respond(req) {
Ok(response) => response,
Err(e) => panic!("WAF inspection error: {e:?}"),
};
// End - NGWAF

Ok(resp)
}

/*
curl -i "https://YOUR_DOMAIN/anything/asdfasdfasf" -H cdn-secret:foo
curl -i https://YOUR_DOMAIN/test?brooks=../../../../etc/passwd -H cdn-secret:foo
*/

Loading