A security extension for Knative Serving that provides firewall capabilities for serverless applications. SAF integrates with Knative's queue-proxy to filter and validate HTTP requests and responses based on configurable rules.
- Overview
- Key Features
- Architecture
- Installation
- Firewall Language
- CNCF Serverless Workflow Integration
- Usage
- Correctness Testing
- Performance Evaluation
- Configuration Reference
- Important Notes
- License
- Support
The Serverless Application Firewall (SAF) is designed to enhance the security of serverless applications running on Knative. It operates as an extension to Knative's queue-proxy, allowing for fine-grained control over HTTP traffic through configurable rules.
- Request and Response Filtering: Validate both incoming requests and outgoing responses
- Flexible Rule System: Use jq expressions for powerful pattern matching and validation
- Knative Integration: Seamless integration with Knative Serving through annotations
- Preliminary Testing: Performance evaluation framework included
The SAF consists of several key components:
The core firewall implementation that integrates with Knative's queue-proxy:
- Location:
queue-proxy-firewall/pkg/firewall/ - Language: Go
- Key Features:
- Implements Knative's
RoundTripPluginterface - Request and response validation
- Rule evaluation using jq expressions
- Implements Knative's
A dedicated language for defining firewall rules:
- Location:
rules-reference/ - Purpose: Define security rules in a structured format
- Components:
- Schema definition and validation
- Example rules and patterns
- Rule extraction tools
Correctness testing and performance benchmarking tools:
- Location:
evaluation/ - Purpose: Validate enforcement behavior and measure firewall performance impact
- Components:
- Enforcement-engine correctness tests
- Invocation and teardown performance tests
- Latency performance tests
- Container image size comparison
- Data analysis and visualization tools
Example applications demonstrating firewall usage:
- Location:
apps/ - Examples:
- Bank application with multiple services
- Simple sample application for testing
- Kubernetes cluster with Knative Serving installed
kofor container image building- Go 1.16+ for development
cd queue-proxy-firewall
./build.shThis builds and pushes the queue-proxy container image with firewall capabilities to your container registry.
cd queue-proxy-firewall
./patch.shThis will set the queue-proxy image as the SAF firewall.
# Apply a sample application
kubectl apply -f apps/sample-app/kubernetes.yaml
# Or deploy the bank application example (you will also need SonataFlow installed)
kubectl apply -f apps/bank/kubernetes.yamlSAF includes a dedicated language for defining firewall rules with a formal schema and validation system.
The firewall language is defined in rules-reference/firewall-schema.yaml and provides:
- Structured Rule Definition: YAML-based format for firewall rules
- Schema Validation: JSON Schema validation for rule syntax
- jq Expression Support: Powerful pattern matching using jq expressions
- Context Variables: Access to request and response context
The schema defines the structure for firewall rules:
firewall:
request:
default-action: accept # accept, reject, or drop
rules:
- action: reject # accept, reject, drop, or log
expression: "jq expression"
# Optional schema validation
schema:
type: json|yaml
path: "path/to/schema"
response:
default-action: accept
rules:
- action: reject
expression: "jq expression"SAF provides tools for integrating firewall rules with CNCF Serverless Workflows (for example for usage in SonataFlow).
The workflow-extractor.py script:
- Extracts rules from workflow function metadata
- Validates rules against the firewall schema
- Injects rules into Kubernetes configuration files
cd rules-reference
python workflow-extractor.pyThis processes workflows like apps/bank/workflow/src/main/resources/workflow.sw.yaml and automatically adds firewall rules to the corresponding Knative Services in the Kubernetes configuration.
In the bank workflow example, the login function includes comprehensive firewall rules:
functions:
- name: login
type: custom
operation: knative:services.v1.serving.knative.dev/login?method=POST
metadata:
firewall:
request:
default-action: accept
rules:
- action: drop
expression: |
.REQUEST.METHOD | ascii_upcase | . != "POST"
# Additional rules for input validation
response:
default-action: accept
rules:
- action: drop
expression: |
.RESPONSE.BODY.error? | type == "string"The extractor generates Kubernetes configurations with the rules embedded:
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: login
spec:
template:
metadata:
annotations:
qpoption.knative.dev/firewall-activate: "enable"
qpoption.knative.dev/firewall-config-rules-json: '{"request": {"default-action": "accept", "rules": [...]}}'The firewall is configured through Kubernetes annotations on Knative Services:
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: my-service
spec:
template:
metadata:
annotations:
qpoption.knative.dev/firewall-activate: "enable"
qpoption.knative.dev/firewall-config-rules: |
request:
default-action: accept
rules:
- action: reject
expression: |
(.REQUEST.BODY.username | test("admin"))Rules are defined using a YAML structure with jq expressions:
request:
default-action: accept # or reject
rules:
- action: reject # accept, reject, or drop
expression: |
(.REQUEST.HEADERS.User-Agent | test("malicious-bot"))
- action: accept
expression: |
(.REQUEST.BODY.api_key == "valid-key")
response:
default-action: accept
rules:
- action: reject
expression: |
(.RESPONSE.BODY.error != null)-
REQUEST: Contains request information
HEADERS: HTTP headersBODY: Parsed request body (JSON)METHOD: HTTP methodPATH: Request pathQUERY: Query parameters
-
RESPONSE: Contains response information
HEADERS: HTTP response headersBODY: Parsed response body (JSON)STATUS: HTTP status code
The table-driven test in queue-proxy-firewall/pkg/firewall/rules_test.go checks the behavior of the enforcement engine independently of the quality of any particular security policy. It verifies that:
- request and response rules produce the configured
accept,reject, anddropactions; - the configured default action is returned when no rule matches;
- rules are evaluated in order and evaluation stops at the first matching terminal action; and
- malformed JSON is reported as an error, causing the request or response to be blocked rather than silently accepted, while preserving the original body.
Run the test with:
cd queue-proxy-firewall
go test ./pkg/firewallThe evaluation framework provides comprehensive performance testing:
# Invocation performance tests (deployment/teardown times)
cd evaluation/invocation
./test.sh
# Latency performance tests (request/response processing)
cd evaluation/latency
./test.sh
# Container root-filesystem size comparison
cd evaluation/container-size
./compare.sh <baseline-image> <saf-image>evaluation/container-size/compare.sh compares the uncompressed root filesystems of two container images. For each image, it:
- pulls the image selected for the host's container platform;
- creates a stopped container;
- exports the container's merged root filesystem to a tar archive; and
- compares the archive sizes in bytes and reports their absolute difference.
The test deliberately measures an exported root filesystem instead of Docker's local image-storage usage. Local storage figures can depend on the storage driver, layer cache, shared layers, and filesystem implementation. Exporting the merged filesystem removes those machine-local factors and measures the files that the container actually exposes.
Consequently, the result is reproducible across machines using the same OS and CPU architecture, provided they resolve the same image contents. For reproducible published measurements, use immutable image digests rather than mutable tags; multi-platform tags can select different images on different architectures or operating systems.
Example:
cd evaluation/container-size
./compare.sh gcr.io/knative-releases/knative.dev/serving/cmd/queue:v1.22.1 ghcr.io/atnog/serverless-workflow-firewall/queue:latestThis value is the uncompressed runtime root-filesystem size. It is not the compressed registry download size and not the amount of local disk space consumed by Docker.
# Invocation/teardown
cd evaluation/invocation
python graphs.py
# Latency
cd evaluation/latency
python graphs.pyThis generates PDF graphs showing performance impact with different rule counts.
Results are stored in:
Each test run creates a timestamped directory with:
- Request traces
- Pod logs
- Performance metrics
Graphs are stored in:
- Deployment: evaluation/invocation/deployment-automatic-gc.pdf
- Teardown: evaluation/invocation/teardown-automatic-gc.pdf
- Deployment with manual GC: evaluation/invocation/deployment-manual-gc.pdf
- Teardown with manual GC: evaluation/invocation/teardown-manual-gc.pdf
- Latency: evaluation/latency/sample-app.pdf
| Annotation | Description | Values |
|---|---|---|
qpoption.knative.dev/firewall-activate |
Enable/disable firewall | enable, disable |
qpoption.knative.dev/firewall-config-rules |
Firewall rules configuration | YAML string |
| Action | Description |
|---|---|
accept |
Allow the request/response |
reject |
Block with error response |
drop |
Silent block (currently same as reject) |
Development Warning: The firewall logs "Never use in production" during initialization
This project is licensed under the GPL-3.0 License - see the LICENSE file for details.
For issues and questions, please open an issue in the GitHub repository or send an e-mail.