-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add trafficpolicy package and conversion util (#564)
* feat: Add trafficpolicy package and conversion util * fix: typo
- Loading branch information
Showing
8 changed files
with
908 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package secrets | ||
|
||
import "context" | ||
|
||
type Resolver interface { | ||
GetSecret(ctx context.Context, namespace, name, key string) (string, error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
package trafficpolicy | ||
|
||
import "time" | ||
|
||
// ActionType is a type of action that can be taken. Ref: https://ngrok.com/docs/traffic-policy/actions/ | ||
type ActionType string | ||
|
||
// Expression is a string that represents a traffic policy expression. | ||
type Expression string | ||
|
||
const ( | ||
ActionType_AddHeaders ActionType = "add-headers" | ||
ActionType_BasicAuth ActionType = "basic-auth" | ||
ActionType_CircuitBreaker ActionType = "circuit-breaker" | ||
ActionType_CompressResponse ActionType = "compress-response" | ||
ActionType_CustomResponse ActionType = "custom-response" | ||
ActionType_Deny ActionType = "deny" | ||
ActionType_ForwardInternal ActionType = "forward-internal" | ||
ActionType_JWTValidation ActionType = "jwt-validation" | ||
ActionType_Log ActionType = "log" | ||
ActionType_RateLimit ActionType = "rate-limit" | ||
ActionType_Redirect ActionType = "redirect" | ||
ActionType_RemoveHeaders ActionType = "remove-headers" | ||
ActionType_RestrictIPs ActionType = "restrict-ips" | ||
ActionType_TerminateTLS ActionType = "terminate-tls" | ||
ActionType_URLRewrite ActionType = "url-rewrite" | ||
ActionType_VerifyWebhook ActionType = "verify-webhook" | ||
) | ||
|
||
// TrafficPolicy is the configuration language for handling traffic received by ngrok | ||
// for Edges and Endpoints. Specifically, it allows you to define rules for each | ||
// phase in a connections lifecycle (on_http_request, on_http_response, and tcp_connect). These | ||
// rules containin expressessions that match traffc and actions to take when those expressions match. | ||
// | ||
// Ref: https://ngrok.com/docs/traffic-policy/ | ||
type TrafficPolicy struct { | ||
OnHTTPRequest []Rule `json:"on_http_request,omitempty"` | ||
OnHTTPResponse []Rule `json:"on_http_response,omitempty"` | ||
OnTCPConnect []Rule `json:"on_tcp_connect,omitempty"` | ||
} | ||
|
||
// NewTrafficPolicy creates a new TrafficPolicy with empty rules. | ||
func NewTrafficPolicy() *TrafficPolicy { | ||
return &TrafficPolicy{ | ||
OnHTTPRequest: []Rule{}, | ||
OnHTTPResponse: []Rule{}, | ||
OnTCPConnect: []Rule{}, | ||
} | ||
} | ||
|
||
// AddRuleOnHTTPRequest adds a rule to the OnHTTPRequest phase of the TrafficPolicy. | ||
func (tp *TrafficPolicy) AddRuleOnHTTPRequest(rule Rule) { | ||
tp.OnHTTPRequest = append(tp.OnHTTPRequest, rule) | ||
} | ||
|
||
// AddRuleOnHTTPResponse adds a rule to the OnHTTPResponse phase of the TrafficPolicy. | ||
func (tp *TrafficPolicy) AddRuleOnHTTPResponse(rule Rule) { | ||
tp.OnHTTPResponse = append(tp.OnHTTPResponse, rule) | ||
} | ||
|
||
// AddRuleOnTCPConnect adds a rule to the OnTCPConnect phase of the TrafficPolicy. | ||
func (tp *TrafficPolicy) AddRuleOnTCPConnect(rule Rule) { | ||
tp.OnTCPConnect = append(tp.OnTCPConnect, rule) | ||
} | ||
|
||
// IsEmpty returns true if the TrafficPolicy has no rules. | ||
func (tp TrafficPolicy) IsEmpty() bool { | ||
return len(tp.OnHTTPRequest) == 0 && | ||
len(tp.OnHTTPResponse) == 0 && | ||
len(tp.OnTCPConnect) == 0 | ||
} | ||
|
||
// A Rule allows you to define how traffic is filtered and processed within a phase. Rules | ||
// consist of expressions and actions. Ref: https://ngrok.com/docs/traffic-policy/concepts/phase-rules/ | ||
type Rule struct { | ||
Expressions []string `json:"expressions,omitempty"` | ||
Actions []Action `json:"actions"` | ||
} | ||
|
||
// An action allows you to manipulate, route, or manage traffic on an endpoint. | ||
// Ref: https://ngrok.com/docs/traffic-policy/actions/ | ||
type Action struct { | ||
Type ActionType `json:"type"` | ||
Config any `json:"config"` | ||
} | ||
|
||
// NewAddHeadersAction creates a new action that adds headers to the request(OnHTTPRequest phase) or | ||
// response(OnHTTPResponse phase). | ||
func NewAddHeadersAction(headers map[string]string) Action { | ||
config := struct { | ||
Headers map[string]string `json:"headers"` | ||
}{ | ||
Headers: headers, | ||
} | ||
|
||
return Action{ | ||
Type: ActionType_AddHeaders, | ||
Config: config, | ||
} | ||
} | ||
|
||
// NewRemoveHeadersAction creates a new action that removes headers from the request(OnHTTPRequest phase) or | ||
// response(OnHTTPResponse phase). | ||
func NewRemoveHeadersAction(headers []string) Action { | ||
config := struct { | ||
Headers []string `json:"headers"` | ||
}{ | ||
Headers: headers, | ||
} | ||
|
||
return Action{ | ||
Type: ActionType_RemoveHeaders, | ||
Config: config, | ||
} | ||
} | ||
|
||
// NewCompressResponseAction creates a new action that compresses the response. Can only be used | ||
// in the OnHTTPResponse phase. | ||
func NewCompressResponseAction(algorithms []string) Action { | ||
config := struct { | ||
Algorithms []string `json:"algorithms,omitempty"` | ||
}{ | ||
Algorithms: algorithms, | ||
} | ||
|
||
return Action{ | ||
Type: ActionType_CompressResponse, | ||
Config: config, | ||
} | ||
} | ||
|
||
// NewCicuitBreakerAction creates a new action that rejects requests when the error rate and request volume within a rolling | ||
// window exceeds defined thresholds. Can only be used in the OnHTTPRequest phase. | ||
func NewCircuitBreakerAction(errorThreshold float64, volumeThreshold *uint32, windowDuration *time.Duration, trippedDuration *time.Duration) Action { | ||
config := struct { | ||
ErrorThreshold float64 `json:"error_threshold"` | ||
VolumeThreshold *uint32 `json:"volume_threshold,omitempty"` | ||
WindowDuration *time.Duration `json:"window_duration,omitempty"` | ||
TrippedDuration *time.Duration `json:"tripped_duration,omitempty"` | ||
}{ | ||
ErrorThreshold: errorThreshold, | ||
VolumeThreshold: volumeThreshold, | ||
WindowDuration: windowDuration, | ||
TrippedDuration: trippedDuration, | ||
} | ||
return Action{ | ||
Type: ActionType_CircuitBreaker, | ||
Config: config, | ||
} | ||
} | ||
|
||
// NewRestrictIPsActionFromIPPolicies creates a new action that restricts access to a set of IP policies. | ||
// Supported on OnHTTPRequest, OnTCPConnect, and OnHTTPResponse phases. | ||
func NewRestricIPsActionFromIPPolicies(policies []string) Action { | ||
config := struct { | ||
IPPolicies []string `json:"ip_policies"` | ||
}{ | ||
IPPolicies: policies, | ||
} | ||
|
||
return Action{ | ||
Type: ActionType_RestrictIPs, | ||
Config: config, | ||
} | ||
} | ||
|
||
// TLSTerminationConfig is the configuration for terminating TLS on an endpoint. | ||
type TLSTerminationConfig struct { | ||
MinVersion *string `json:"min_version,omitempty"` | ||
MaxVersion *string `json:"max_version,omitempty"` | ||
ServerPrivateKey *string `json:"server_private_key,omitempty"` | ||
ServerCertificate *string `json:"server_certificate,omitempty"` | ||
MutualTLSCertificateAuthorities []string `json:"mutual_tls_certificate_authorities,omitempty"` | ||
MutualTLSVerificationStrategy *string `json:"mutual_tls_verification_strategy,omitempty"` | ||
} | ||
|
||
// NewTerminateTLSAction creates a new action that configures how TLS is terminated on the endpoint. | ||
func NewTerminateTLSAction(config TLSTerminationConfig) Action { | ||
return Action{ | ||
Type: ActionType_TerminateTLS, | ||
Config: config, | ||
} | ||
} | ||
|
||
// NewWebhookVerificationAction creates a new action that verifies a webhook request. | ||
func NewWebhookVerificationAction(provider, secret string) Action { | ||
config := struct { | ||
Provider string `json:"provider"` | ||
Secret string `json:"secret"` | ||
}{ | ||
Provider: provider, | ||
Secret: secret, | ||
} | ||
|
||
return Action{ | ||
Type: ActionType_VerifyWebhook, | ||
Config: config, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package trafficpolicy | ||
|
||
import ( | ||
"encoding/json" | ||
"os" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"k8s.io/utils/ptr" | ||
) | ||
|
||
func assertTrafficPolicyContent(t *testing.T, tp *TrafficPolicy, expected string) { | ||
content, err := json.Marshal(tp) | ||
assert.NoError(t, err) | ||
assert.JSONEq(t, expected, string(content)) | ||
} | ||
|
||
func loadTestData(name string) string { | ||
data, err := os.ReadFile("testdata/" + name) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return string(data) | ||
} | ||
|
||
func TestEmptyTrafficPolicy(t *testing.T) { | ||
tp := NewTrafficPolicy() | ||
assert.True(t, tp.IsEmpty()) | ||
assertTrafficPolicyContent(t, tp, `{}`) | ||
} | ||
|
||
func TestTrafficPolicy(t *testing.T) { | ||
tp := NewTrafficPolicy() | ||
if tp == nil { | ||
t.Error("TrafficPolicy is nil") | ||
} | ||
|
||
tp.AddRuleOnHTTPRequest( | ||
Rule{ | ||
Actions: []Action{ | ||
NewWebhookVerificationAction("github", "secret"), | ||
}, | ||
}, | ||
) | ||
assertTrafficPolicyContent(t, tp, loadTestData("policy-1.json")) | ||
|
||
tp = NewTrafficPolicy() | ||
tp.AddRuleOnTCPConnect( | ||
Rule{ | ||
Expressions: []string{"[1,2,3].all(x, x > 0)"}, | ||
Actions: []Action{ | ||
NewRestricIPsActionFromIPPolicies([]string{"ipp_123", "ipp_456"}), | ||
NewTerminateTLSAction(TLSTerminationConfig{MinVersion: ptr.To("1.2")}), | ||
}, | ||
}, | ||
) | ||
tp.AddRuleOnHTTPRequest( | ||
Rule{ | ||
Actions: []Action{ | ||
NewCircuitBreakerAction(0.10, nil, nil, ptr.To(2*time.Minute)), | ||
}, | ||
}, | ||
) | ||
|
||
tp.AddRuleOnHTTPResponse( | ||
Rule{ | ||
Actions: []Action{ | ||
NewAddHeadersAction(map[string]string{ | ||
"X-Header-1": "value1", | ||
"X-Header-2": "value2", | ||
}), | ||
NewRemoveHeadersAction([]string{ | ||
"X-Header-3", | ||
"X-Header-4", | ||
}), | ||
NewCompressResponseAction(nil), | ||
}, | ||
}, | ||
) | ||
|
||
assertTrafficPolicyContent(t, tp, loadTestData("policy-2.json")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"on_http_request": [ | ||
{ | ||
"actions": [ | ||
{ | ||
"type":"verify-webhook", | ||
"config": { | ||
"provider": "github", | ||
"secret": "secret" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
{ | ||
"on_tcp_connect": [ | ||
{ | ||
"expressions": [ | ||
"[1,2,3].all(x, x > 0)" | ||
], | ||
"actions": [ | ||
{ | ||
"type": "restrict-ips", | ||
"config": { | ||
"ip_policies": [ | ||
"ipp_123", | ||
"ipp_456" | ||
] | ||
} | ||
}, | ||
{ | ||
"type": "terminate-tls", | ||
"config": { | ||
"min_version": "1.2" | ||
} | ||
} | ||
] | ||
} | ||
], | ||
"on_http_request": [ | ||
{ | ||
"actions": [ | ||
{ | ||
"type": "circuit-breaker", | ||
"config": { | ||
"error_threshold": 0.1, | ||
"tripped_duration": 120000000000 | ||
} | ||
} | ||
] | ||
} | ||
], | ||
"on_http_response": [ | ||
{ | ||
"actions": [ | ||
{ | ||
"type": "add-headers", | ||
"config": { | ||
"headers": { | ||
"X-Header-1": "value1", | ||
"X-Header-2": "value2" | ||
} | ||
} | ||
}, | ||
{ | ||
"type": "remove-headers", | ||
"config": { | ||
"headers": [ | ||
"X-Header-3", | ||
"X-Header-4" | ||
] | ||
} | ||
}, | ||
{ | ||
"type": "compress-response", | ||
"config": {} | ||
} | ||
] | ||
} | ||
] | ||
} |
Oops, something went wrong.