Add L7 policies for user-agent blocking and required headers
Per-route HTTP-level blocking policies for L7 routes. Two rule types: block_user_agent (substring match against User-Agent, returns 403) and require_header (named header must be present, returns 403). Config: L7Policy struct with type/value fields, added as L7Policies slice on Route. Validated in config (type enum, non-empty value, warning if set on L4 routes). DB: Migration 4 creates l7_policies table with route_id FK (cascade delete), type CHECK constraint, UNIQUE(route_id, type, value). New l7policies.go with ListL7Policies, CreateL7Policy, DeleteL7Policy, GetRouteID. Seed updated to persist policies from config. L7 middleware: PolicyMiddleware in internal/l7/policy.go evaluates rules in order, returns 403 on first match, no-op if empty. Composed into the handler chain between context injection and reverse proxy. Server: L7PolicyRule type on RouteInfo with AddL7Policy/RemoveL7Policy mutation methods on ListenerState. handleL7 threads policies into l7.RouteConfig. Startup loads policies per L7 route from DB. Proto: L7Policy message, repeated l7_policies on Route. Three new RPCs: ListL7Policies, AddL7Policy, RemoveL7Policy. All follow the write-through pattern. Client: L7Policy type, ListL7Policies/AddL7Policy/RemoveL7Policy methods. CLI: mcproxyctl policies list/add/remove subcommands. Tests: 6 PolicyMiddleware unit tests (no policies, UA match/no-match, header present/absent, multiple rules). 4 DB tests (CRUD, cascade, duplicate, GetRouteID). 3 gRPC tests (add+list, remove, validation). 2 end-to-end L7 tests (UA block, required header with allow/deny). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,9 @@ const (
|
||||
ProxyAdminService_AddFirewallRule_FullMethodName = "/mc_proxy.v1.ProxyAdminService/AddFirewallRule"
|
||||
ProxyAdminService_RemoveFirewallRule_FullMethodName = "/mc_proxy.v1.ProxyAdminService/RemoveFirewallRule"
|
||||
ProxyAdminService_SetListenerMaxConnections_FullMethodName = "/mc_proxy.v1.ProxyAdminService/SetListenerMaxConnections"
|
||||
ProxyAdminService_ListL7Policies_FullMethodName = "/mc_proxy.v1.ProxyAdminService/ListL7Policies"
|
||||
ProxyAdminService_AddL7Policy_FullMethodName = "/mc_proxy.v1.ProxyAdminService/AddL7Policy"
|
||||
ProxyAdminService_RemoveL7Policy_FullMethodName = "/mc_proxy.v1.ProxyAdminService/RemoveL7Policy"
|
||||
ProxyAdminService_GetStatus_FullMethodName = "/mc_proxy.v1.ProxyAdminService/GetStatus"
|
||||
)
|
||||
|
||||
@@ -43,6 +46,10 @@ type ProxyAdminServiceClient interface {
|
||||
RemoveFirewallRule(ctx context.Context, in *RemoveFirewallRuleRequest, opts ...grpc.CallOption) (*RemoveFirewallRuleResponse, error)
|
||||
// Connection limits
|
||||
SetListenerMaxConnections(ctx context.Context, in *SetListenerMaxConnectionsRequest, opts ...grpc.CallOption) (*SetListenerMaxConnectionsResponse, error)
|
||||
// L7 policies
|
||||
ListL7Policies(ctx context.Context, in *ListL7PoliciesRequest, opts ...grpc.CallOption) (*ListL7PoliciesResponse, error)
|
||||
AddL7Policy(ctx context.Context, in *AddL7PolicyRequest, opts ...grpc.CallOption) (*AddL7PolicyResponse, error)
|
||||
RemoveL7Policy(ctx context.Context, in *RemoveL7PolicyRequest, opts ...grpc.CallOption) (*RemoveL7PolicyResponse, error)
|
||||
// Status
|
||||
GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error)
|
||||
}
|
||||
@@ -125,6 +132,36 @@ func (c *proxyAdminServiceClient) SetListenerMaxConnections(ctx context.Context,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *proxyAdminServiceClient) ListL7Policies(ctx context.Context, in *ListL7PoliciesRequest, opts ...grpc.CallOption) (*ListL7PoliciesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListL7PoliciesResponse)
|
||||
err := c.cc.Invoke(ctx, ProxyAdminService_ListL7Policies_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *proxyAdminServiceClient) AddL7Policy(ctx context.Context, in *AddL7PolicyRequest, opts ...grpc.CallOption) (*AddL7PolicyResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AddL7PolicyResponse)
|
||||
err := c.cc.Invoke(ctx, ProxyAdminService_AddL7Policy_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *proxyAdminServiceClient) RemoveL7Policy(ctx context.Context, in *RemoveL7PolicyRequest, opts ...grpc.CallOption) (*RemoveL7PolicyResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RemoveL7PolicyResponse)
|
||||
err := c.cc.Invoke(ctx, ProxyAdminService_RemoveL7Policy_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *proxyAdminServiceClient) GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetStatusResponse)
|
||||
@@ -149,6 +186,10 @@ type ProxyAdminServiceServer interface {
|
||||
RemoveFirewallRule(context.Context, *RemoveFirewallRuleRequest) (*RemoveFirewallRuleResponse, error)
|
||||
// Connection limits
|
||||
SetListenerMaxConnections(context.Context, *SetListenerMaxConnectionsRequest) (*SetListenerMaxConnectionsResponse, error)
|
||||
// L7 policies
|
||||
ListL7Policies(context.Context, *ListL7PoliciesRequest) (*ListL7PoliciesResponse, error)
|
||||
AddL7Policy(context.Context, *AddL7PolicyRequest) (*AddL7PolicyResponse, error)
|
||||
RemoveL7Policy(context.Context, *RemoveL7PolicyRequest) (*RemoveL7PolicyResponse, error)
|
||||
// Status
|
||||
GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error)
|
||||
mustEmbedUnimplementedProxyAdminServiceServer()
|
||||
@@ -182,6 +223,15 @@ func (UnimplementedProxyAdminServiceServer) RemoveFirewallRule(context.Context,
|
||||
func (UnimplementedProxyAdminServiceServer) SetListenerMaxConnections(context.Context, *SetListenerMaxConnectionsRequest) (*SetListenerMaxConnectionsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SetListenerMaxConnections not implemented")
|
||||
}
|
||||
func (UnimplementedProxyAdminServiceServer) ListL7Policies(context.Context, *ListL7PoliciesRequest) (*ListL7PoliciesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListL7Policies not implemented")
|
||||
}
|
||||
func (UnimplementedProxyAdminServiceServer) AddL7Policy(context.Context, *AddL7PolicyRequest) (*AddL7PolicyResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AddL7Policy not implemented")
|
||||
}
|
||||
func (UnimplementedProxyAdminServiceServer) RemoveL7Policy(context.Context, *RemoveL7PolicyRequest) (*RemoveL7PolicyResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RemoveL7Policy not implemented")
|
||||
}
|
||||
func (UnimplementedProxyAdminServiceServer) GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStatus not implemented")
|
||||
}
|
||||
@@ -332,6 +382,60 @@ func _ProxyAdminService_SetListenerMaxConnections_Handler(srv interface{}, ctx c
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ProxyAdminService_ListL7Policies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListL7PoliciesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ProxyAdminServiceServer).ListL7Policies(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ProxyAdminService_ListL7Policies_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ProxyAdminServiceServer).ListL7Policies(ctx, req.(*ListL7PoliciesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ProxyAdminService_AddL7Policy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddL7PolicyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ProxyAdminServiceServer).AddL7Policy(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ProxyAdminService_AddL7Policy_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ProxyAdminServiceServer).AddL7Policy(ctx, req.(*AddL7PolicyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ProxyAdminService_RemoveL7Policy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RemoveL7PolicyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ProxyAdminServiceServer).RemoveL7Policy(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ProxyAdminService_RemoveL7Policy_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ProxyAdminServiceServer).RemoveL7Policy(ctx, req.(*RemoveL7PolicyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ProxyAdminService_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetStatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -385,6 +489,18 @@ var ProxyAdminService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "SetListenerMaxConnections",
|
||||
Handler: _ProxyAdminService_SetListenerMaxConnections_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListL7Policies",
|
||||
Handler: _ProxyAdminService_ListL7Policies_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AddL7Policy",
|
||||
Handler: _ProxyAdminService_AddL7Policy_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RemoveL7Policy",
|
||||
Handler: _ProxyAdminService_RemoveL7Policy_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStatus",
|
||||
Handler: _ProxyAdminService_GetStatus_Handler,
|
||||
|
||||
Reference in New Issue
Block a user