Add per-IP rate limiting and Unix socket support for gRPC admin API
Rate limiting: per-source-IP connection rate limiter in the firewall layer with configurable limit and sliding window. Blocklisted IPs are rejected before rate limit evaluation to avoid wasting quota. Unix socket: the gRPC admin API can now listen on a Unix domain socket (no TLS required), secured by file permissions (0600), as a simpler alternative for local-only access. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,7 +62,7 @@ func setup(t *testing.T) *testEnv {
|
||||
}
|
||||
|
||||
// Build server with matching in-memory state.
|
||||
fwObj, err := firewall.New("", []string{"10.0.0.1"}, nil, nil)
|
||||
fwObj, err := firewall.New("", []string{"10.0.0.1"}, nil, nil, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("firewall: %v", err)
|
||||
}
|
||||
@@ -268,6 +268,15 @@ func TestAddRouteValidation(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("expected error for empty backend")
|
||||
}
|
||||
|
||||
// Invalid backend (not host:port).
|
||||
_, err = env.client.AddRoute(ctx, &pb.AddRouteRequest{
|
||||
ListenerAddr: ":443",
|
||||
Route: &pb.Route{Hostname: "y.test", Backend: "not-a-host-port"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid backend address")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveRoute(t *testing.T) {
|
||||
@@ -410,6 +419,61 @@ func TestAddFirewallRuleValidation(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("expected error for empty value")
|
||||
}
|
||||
|
||||
// Invalid IP address.
|
||||
_, err = env.client.AddFirewallRule(ctx, &pb.AddFirewallRuleRequest{
|
||||
Rule: &pb.FirewallRule{
|
||||
Type: pb.FirewallRuleType_FIREWALL_RULE_TYPE_IP,
|
||||
Value: "not-an-ip",
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid IP")
|
||||
}
|
||||
|
||||
// Invalid CIDR.
|
||||
_, err = env.client.AddFirewallRule(ctx, &pb.AddFirewallRuleRequest{
|
||||
Rule: &pb.FirewallRule{
|
||||
Type: pb.FirewallRuleType_FIREWALL_RULE_TYPE_CIDR,
|
||||
Value: "not-a-cidr",
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid CIDR")
|
||||
}
|
||||
|
||||
// Non-canonical CIDR.
|
||||
_, err = env.client.AddFirewallRule(ctx, &pb.AddFirewallRuleRequest{
|
||||
Rule: &pb.FirewallRule{
|
||||
Type: pb.FirewallRuleType_FIREWALL_RULE_TYPE_CIDR,
|
||||
Value: "192.168.1.5/16",
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-canonical CIDR")
|
||||
}
|
||||
|
||||
// Invalid country code (lowercase).
|
||||
_, err = env.client.AddFirewallRule(ctx, &pb.AddFirewallRuleRequest{
|
||||
Rule: &pb.FirewallRule{
|
||||
Type: pb.FirewallRuleType_FIREWALL_RULE_TYPE_COUNTRY,
|
||||
Value: "cn",
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for lowercase country code")
|
||||
}
|
||||
|
||||
// Invalid country code (too long).
|
||||
_, err = env.client.AddFirewallRule(ctx, &pb.AddFirewallRuleRequest{
|
||||
Rule: &pb.FirewallRule{
|
||||
Type: pb.FirewallRuleType_FIREWALL_RULE_TYPE_COUNTRY,
|
||||
Value: "USA",
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for 3-letter country code")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveFirewallRule(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user