#security #default-value #perimeterx #enforcer

perimeterx-fastly-enforcer

PerimeterX Fastly Compute@Edge Rust Enforcer

4 stable releases

1.1.5 Sep 26, 2024
1.1.4 Sep 4, 2024
1.1.3 Aug 27, 2024
1.1.1 May 14, 2024

#223 in Network programming

Download history 2/week @ 2024-07-25 95/week @ 2024-08-22 108/week @ 2024-08-29 50/week @ 2024-09-05 37/week @ 2024-09-12 17/week @ 2024-09-19 149/week @ 2024-09-26

331 downloads per month

MIT license

90KB
2K SLoC

image

PerimeterX Fastly Compute@Edge Rust Enforcer

Crates.io License Badge

Table of Contents

Project layout

  • src folder contains Fastly Compute@Edge Rust Module
  • example folder contains a sample Fastly Compute@Edge Rust application
  • contrib/pxconfig.sh script to create Fastly Config store and populate with default values
  • contrib/pxbackend.sh script to create and configure PX Backend host

Prerequisites

In order to compile and deploy Fastly Compute@Edge Package, Fastly CLI must be installed and configured: Compute@Edge services

Configuration

Module configuration is done using Compute@Edge Config store. Please refer PerimeterX documentation for individual configuration option description.

This is the list of Config store (PXConfig) items and datatypes:

configuration name type default
px_app_id string required -
px_cookie_secret string required -
px_auth_token string required -
px_module_enabled bool optional false
px_module_mode "active_blocking", "monitor" optional "monitor"
px_debug bool optional false
px_blocking_score number optional 100
px_sensitive_headers list optional []
px_sensitive_routes list optional []
px_filter_by_route list optional []
px_filter_by_user_agent list optional []
px_filter_by_ip list optional []
px_filter_by_http_method list optional []
px_custom_cookie_header string optional ""
px_enforced_routes list optional []
px_monitored_routes list optional []
px_bypass_monitor_header string optional ""
px_first_party_enabled bool optional true
px_custom_logo string optional ""
px_js_ref string optional ""
px_css_ref string optional ""
px_ip_headers list optional []
log_endpoint string optional ""
px_graphql_enabled bool optional false
px_graphql_routes list optional ["^/graphql$"]
px_sensitive_graphql_operation_names list optional []
px_sensitive_graphql_operation_types list optional []

pxconfig.sh script could be used to populate Config store with the required values. Usage:

Required options:
-s, --service-id=SERVICE_ID       specify a service to deploy the Config store
-v, --version=VER                 specify a service version
-a, --appid=APPID                 specify an appid
-c, --cookie_secret=SECRET        specify a cookie_secret
-t, --auth_token=TOKEN            specify an auth_token

PerimeterX Backend

In order for PerimeterX Enforcer to communicate with PerimeterX Collector server, a special "backend" server must be added and configured in Fastly UI (or using contrib/pxbackend.sh script). Backend parameters (replace ${APP_ID} with your PerimeterX Application ID):

  • Name: PX_BACKEND_${APP_ID}
  • Hostname: sapi-${APP_ID}.perimeterx.net
  • Override host: (empty value)
  • Use SSL/TLS: Yes

Installation

Include perimeterx-fastly-enforcer dependency to Cargo.toml:

cargo add perimeterx-fastly-enforcer

Module integration

To integrate PerimeterX Rust module into existing Rust code, the following base snippet could be used (for a more advanced example see the project in "example" folder):

    let mut px: PXEnforcer = PXEnforcer::new(perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME);
    let px_result = px.enforce(&mut req)?;
    if let Some(r) = px_result {
        return Ok(r);
    };

    //... communicate with Origin server / process request and response

    px.post_enforce(&response);

Enforcer API

Initialize PXEnforcer structure, it takes a name of Fastly "ConfigStore" (it's possible to use the default name: perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME)

pub fn new(config_store_name: &str) -> Self

This function takes Request and returns a Result which optionally contains "Response" (for "blocked" or "first party" requests):

pub fn enforce(&mut self, req: &mut Request) -> Result<Option<Response>, Error>

At the end of request processing, the following function must be called to finalize PerimeterX enforcer code:

pub fn post_enforce(&self, res: &Response)

It is possible to access PXContext structure with various Enforcer variables via px.ctx member:

    // send "score" value to the Origin
    req.set_header("x-px-score", px.ctx.score.to_string());

To set "custom_parameters" variables, the following callback function could be used:

pub type PXEnrichCustomParamsFn = fn (req: &Request, conf: &PXConfig, params: &mut PXCustomParams);

where: req: fastly::Request conf: PXConfig params: modifiable structure with custom_param1 .. custom_param10 fields

To set custom parameters callback function, use the following setter:

pub fn set_enrich_custom_params_fn(&mut self, f: PXEnrichCustomParamsFn)

Logging

PerimeterX Rust Enforcer logs the most messages (both "info" and "debug") using Level::Info level.

In order to enable Enforcer to produce "debug" messages, set px_debug configuration value to true.

Sample code

This is the simplest example how to use PerimeterX Rust module:

use fastly::{Error, Request, Response};
use perimeterx_fastly_enforcer::pxenforce::PXEnforcer;

const ORIGIN_BACKEND: &str = "origin_backend";

// send a request to the Origin server
fn send_to_origin(req: Request) -> Result<Response, Error> {
    match req.send(ORIGIN_BACKEND) {
        Ok(r) => return Ok(r),
        Err(e) => return Err(e.into()),
    }
}

#[fastly::main]
fn main(mut req: Request) -> Result<Response, Error> {

    // initialize PX Enforcer
    let mut px: PXEnforcer = PXEnforcer::new(perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME);

    // execute PX Enforcer for Request
    let px_result = px.enforce(&mut req)?;

    // return, if it's a "blocked" or "first party" response
    if let Some(r) = px_result {
        return Ok(r);
    };

    // ... process Client request ...

    // we can access "PXContext" structure.
    // as an example: send "score" value to the Origin
    req.set_header("x-px-score", px.ctx.score.to_string());

    // a client function to communicate with the Origin
    let response = send_to_origin(req)?;

    // ... process Origin response ...

    // must be called at the end
    px.post_enforce(&response);

    // we are ok to send response back to client
    return Ok(response);
}

For a complete example: example folder contains a sample project.

Dependencies

~12–22MB
~334K SLoC