Traffic Policy
Traffic Policy is currently in preview. Breaking changes may occur at any time with no notice, including changes to the structure of policy documents, the behaviors of policies, and the pricing of this feature.
Overview
This module enables you to assign a policy to your endpoints by defining a set of rules for three traffic management phases:
on_tcp_connect
, on_http_request
, and on_http_response
. These rules allow you to influence and control traffic to and from
your upstream service.
Traffic Policy rules are composed of expressions that filter the traffic on which they are applicable and actions that should take effect.
Example Usage
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 80 --traffic-policy-file /path/to/policy.yml
on_http_request:
- name: FooBarParamNotFound
expressions:
- "'bar' in getQueryParam('foo')"
actions:
- type: "custom-response"
config:
status_code: 404
content: not found
headers:
content-type: text/plain
- name: "BazCookieForLargeRequests"
expressions:
- "!hasReqCookie('baz')"
- "req.content_length > 5000"
actions:
- type: deny
on_http_response:
- name: "LogUnsuccessfulRequests"
expressions:
- "res.status_code < 200 && res.status_code >= 300"
actions:
- type: log
config:
metadata:
hostport: example.com:443
success: false
tunnels:
example:
proto: http
addr: 80
traffic_policy:
on_http_request:
- name: FooBarParamNotFound
expressions:
- "'bar' in getQueryParam('foo')"
actions:
- type: "custom-response"
config:
status_code: 404
content: not found
headers:
content-type: text/plain
- name: "BazCookieForLargeRequests"
expressions:
- "!hasReqCookie('baz')"
- "req.content_length > 5000"
actions:
- type: deny
on_http_response:
- name: "LogUnsuccessfulRequests"
expressions:
- "res.status_code < 200 && res.status_code >= 300"
actions:
- type: log
config:
metadata:
hostport: example.com:443
success: false
Traffic Policy is not configurable via SSH
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
"golang.ngrok.com/ngrok/policy"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.HTTPEndpoint(
config.WithTrafficPolicy(getPolicyFromFile()),
),
ngrok.WithAuthtokenFromEnv(),
)
}
func getPolicyFromFile() string {
b, _ := os.ReadFile("./policy.yaml")
return string(b)
}
Traffic Policies can be defined in json
or yaml
!
{
"on_http_request": [
{
"name": "FooBarParamNotFound",
"expressions": ["'bar' in getQueryParam('foo')"],
"actions": [
{
"type": "custom-response",
"config": {
"status_code": 404,
"content": "not found",
"headers": { "content-type": "text/plain" }
}
}
]
},
{
"name": "BazCookieForLargeRequests",
"expressions": ["!hasReqCookie('baz')", "req.content_length > 5000"],
"actions": [{ "type": "deny" }]
}
],
"on_http_response": [
{
"name": "LogUnsuccessfulRequests",
"expressions": ["res.status_code < 200 && res.status_code >= 300"],
"actions": [
{
"type": "log",
"config": {
"metadata": { "hostport": "example.com:443", "success": false }
}
}
]
}
]
}
---
on_http_request:
- name: FooBarParamNotFound
expressions:
- "'bar' in getQueryParam('foo')"
actions:
- type: custom-response
config:
status_code: 404
content: not found
headers:
content-type: text/plain
- name: BazCookieForLargeRequests
expressions:
- "!hasReqCookie('baz')"
- req.content_length > 5000
actions:
- type: deny
on_http_response:
- name: LogUnsuccessfulRequests
expressions:
- res.status_code < 200 && res.status_code >= 300
actions:
- type: log
config:
metadata:
hostport: example.com:443
success: false
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
const fs = require("fs");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
traffic_policy: fs.readFileSync("/path/to/policy.json", "utf8"),
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Traffic Policies can be defined in json
or yaml
!
{
"on_http_request": [
{
"name": "FooBarParamNotFound",
"expressions": ["'bar' in getQueryParam('foo')"],
"actions": [
{
"type": "custom-response",
"config": {
"status_code": 404,
"content": "not found",
"headers": { "content-type": "text/plain" }
}
}
]
},
{
"name": "BazCookieForLargeRequests",
"expressions": ["!hasReqCookie('baz')", "req.content_length > 5000"],
"actions": [{ "type": "deny" }]
}
],
"on_http_response": [
{
"name": "LogUnsuccessfulRequests",
"expressions": ["res.status_code < 200 && res.status_code >= 300"],
"actions": [
{
"type": "log",
"config": {
"metadata": { "hostport": "example.com:443", "success": false }
}
}
]
}
]
}
---
on_http_request:
- name: FooBarParamNotFound
expressions:
- "'bar' in getQueryParam('foo')"
actions:
- type: custom-response
config:
status_code: 404
content: not found
headers:
content-type: text/plain
- name: BazCookieForLargeRequests
expressions:
- "!hasReqCookie('baz')"
- req.content_length > 5000
actions:
- type: deny
on_http_response:
- name: LogUnsuccessfulRequests
expressions:
- res.status_code < 200 && res.status_code >= 300
actions:
- type: log
config:
metadata:
hostport: example.com:443
success: false
Javascript SDK Docs:
with open('/path/to/policy.json') as f:
policy = json.load(f)
listener = await session.http_endpoint().traffic_policy(policy).listen()
Traffic Policies can be defined in json
or yaml
!
{
"on_http_request": [
{
"name": "FooBarParamNotFound",
"expressions": ["'bar' in getQueryParam('foo')"],
"actions": [
{
"type": "custom-response",
"config": {
"status_code": 404,
"content": "not found",
"headers": { "content-type": "text/plain" }
}
}
]
},
{
"name": "BazCookieForLargeRequests",
"expressions": ["!hasReqCookie('baz')", "req.content_length > 5000"],
"actions": [{ "type": "deny" }]
}
],
"on_http_response": [
{
"name": "LogUnsuccessfulRequests",
"expressions": ["res.status_code < 200 && res.status_code >= 300"],
"actions": [
{
"type": "log",
"config": {
"metadata": { "hostport": "example.com:443", "success": false }
}
}
]
}
]
}
---
on_http_request:
- name: FooBarParamNotFound
expressions:
- "'bar' in getQueryParam('foo')"
actions:
- type: custom-response
config:
status_code: 404
content: not found
headers:
content-type: text/plain
- name: BazCookieForLargeRequests
expressions:
- "!hasReqCookie('baz')"
- req.content_length > 5000
actions:
- type: deny
on_http_response:
- name: LogUnsuccessfulRequests
expressions:
- res.status_code < 200 && res.status_code >= 300
actions:
- type: log
config:
metadata:
hostport: example.com:443
success: false
Python SDK Docs:
Traffic Policy is coming soon via rust-sdk
---
kind: NgrokModuleSet
apiVersion: ingress.k8s.ngrok.com/v1alpha1
metadata:
name: policy-module
modules:
policy:
inbound:
- name: FooBarParamNotFound
expressions:
- "'bar' in getQueryParam('foo')"
actions:
- type: "custom-response"
config:
status_code: 404
content: not found
headers:
content-type: text/plain
- name: BazCookieForLargeRequests
expressions:
- "!hasReqCookie('baz')"
- "req.content_length > 5000"
actions:
- type: "deny"
outbound:
- name: LogUnsuccessfulRequests
expressions:
- "res.status_code != 200 && res.status_code != 204"
actions:
- type: "log"
config:
metadata:
hostport: example.com:443
success: false
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: policy-module
spec:
ingressClassName: ngrok
rules:
- host: your-domain.ngrok.app
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Behavior
Traffic Policy rules are evaluated sequentially in the order they are configured with on_tcp_connect
and on_http_request
rules taking effect before the
upstream server is reached and on_http_response
rules taking effect after the upstream server responds. Whether or not the configured
actions are performed is determined at runtime by the expressions.
Expression Evaluation
Traffic Policy expressions are written using the Common Expression Language (CEL). Traffic Policy expressions must evaluate to true
in order for policy actions to take effect. There is no behavioral difference between adding multiple expressions to a single policy rule and having one
single expression with multiple statements logically conjoined together (i.e. ["1 == 1 && 2 == 2"]
is the same
s ["1 == 1", "2 == 2"]
).
If no expressions are specified on a policy rule, its actions will always take effect.
See expressions for variable and macro definitions.
Action Execution
If the expressions of a traffic policy rule evaluate to true, the policy's actions will be executed. If multiple actions are defined on a traffic policy, the actions will execute sequentially.
See actions for all available actions.
Reference
Configuration
Parameter | Description |
---|---|
name | Traffic Policy rules can optionally be given a name for convenience. |
expressions | A list of CEL expressions that filter which traffic a policy rule will apply to. |
actions | A list of actions that will execute sequentially if the associated policy rule's expressions all match on the traffic. |
type | The type of action. |
config | The configuration details of how an action should execute. Each action has its own configuration structure. |
Edges
Traffic Policy is an HTTPS Edge module which can be applied to Routes.
The Traffic Policy module can be configured via the ngrok dashboard or API.