-
Notifications
You must be signed in to change notification settings - Fork 435
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal/appsec: internal/appsec: add support for WAF actions
ASM now instanciates an action handler that will perform various actions commanded by the WAF after a match is performed. The "block_request" action type is the only kind of action currently supported, allowing to block an HTTP request.
- Loading branch information
Showing
6 changed files
with
261 additions
and
18 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,85 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2022 Datadog, Inc. | ||
|
||
//go:build appsec | ||
// +build appsec | ||
|
||
package grpcsec | ||
|
||
import ( | ||
"context" | ||
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
) | ||
|
||
type ActionParam interface{} | ||
|
||
type action struct { | ||
id string | ||
params ActionParam | ||
} | ||
|
||
// ActionHandler handles WAF actions registration and execution | ||
type ActionHandler interface { | ||
RegisterAction(id string, params ActionParam) | ||
Exec(id string, op *HandlerOperation) | ||
} | ||
|
||
type actionsHandler struct { | ||
actions map[string]action | ||
} | ||
|
||
// NewActionsHandler returns an action handler holding the default ASM actions. | ||
// Currently, only the default "block" action is supported | ||
func NewActionsHandler() ActionHandler { | ||
defaultBlockAction := action{ | ||
id: "block", | ||
params: BlockRequestParams{ | ||
Status: codes.Aborted, | ||
}, | ||
} | ||
// Register the default "block" action as specified in the RFC for HTTP blocking | ||
actions := map[string]action{defaultBlockAction.id: defaultBlockAction} | ||
|
||
return &actionsHandler{ | ||
actions: actions, | ||
} | ||
} | ||
|
||
// RegisterAction registers a specific action to the actions handler. If the action kind is unknown | ||
// the action will have no effect | ||
func (h *actionsHandler) RegisterAction(id string, params ActionParam) { | ||
h.actions[id] = action{ | ||
id: id, | ||
params: params, | ||
} | ||
} | ||
|
||
// Exec executes the action identified by `id` | ||
func (h *actionsHandler) Exec(id string, op *HandlerOperation) { | ||
a, ok := h.actions[id] | ||
if !ok { | ||
return | ||
} | ||
// Currently, only the "block_request" type is supported, so we only need to check for blockRequestParams | ||
if p, ok := a.params.(BlockRequestParams); ok { | ||
err := status.Error(p.Status, "Request blocked") | ||
op.UnaryHandler = func(ctx context.Context, req interface{}) (interface{}, error) { | ||
return nil, err | ||
} | ||
op.StreamHandler = func(srv interface{}, stream grpc.ServerStream) error { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
// BlockRequestParams is the parameter struct used to perform actions of kind ActionBlockRequest | ||
type BlockRequestParams struct { | ||
ActionParam | ||
// Status is the return code to use when blocking the request | ||
Status codes.Code | ||
} |
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,84 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2022 Datadog, Inc. | ||
|
||
//go:build appsec | ||
// +build appsec | ||
|
||
package httpsec | ||
|
||
import ( | ||
"net/http" | ||
) | ||
|
||
// Action is used to identify any action kind | ||
type Action interface { | ||
isAction() | ||
} | ||
|
||
// BlockRequestAction is the parameter struct used to perform actions of kind ActionBlockRequest | ||
type BlockRequestAction struct { | ||
Action | ||
// Status is the return code to use when blocking the request | ||
Status int | ||
// Template is the payload template to use to write the response (html or json) | ||
Template string | ||
// handler is the http handler be used to block the request (see wrap()) | ||
handler http.Handler | ||
} | ||
|
||
func (*BlockRequestAction) isAction() {} | ||
|
||
func blockedPayload(a *BlockRequestAction) []byte { | ||
payload := BlockedTemplateJSON | ||
if a.Template == "html" { | ||
payload = BlockedTemplateHTML | ||
} | ||
return payload | ||
} | ||
|
||
// ActionsHandler handles actions registration and their application to operations | ||
type ActionsHandler struct { | ||
actions map[string]Action | ||
} | ||
|
||
// NewActionsHandler returns an action handler holding the default ASM actions. | ||
// Currently, only the default "block" action is supported | ||
func NewActionsHandler() *ActionsHandler { | ||
handler := ActionsHandler{ | ||
actions: map[string]Action{}, | ||
} | ||
// Register the default "block" action as specified in the RFC for HTTP blocking | ||
handler.RegisterAction("block", &BlockRequestAction{ | ||
Status: 403, | ||
Template: "html", | ||
}) | ||
|
||
return &handler | ||
} | ||
|
||
// RegisterAction registers a specific action to the handler. If the action kind is unknown | ||
// the action will not be registered | ||
func (h *ActionsHandler) RegisterAction(id string, action Action) { | ||
switch a := action.(type) { | ||
case *BlockRequestAction: | ||
payload := blockedPayload(a) | ||
a.handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { | ||
writer.Write(payload) | ||
writer.WriteHeader(a.Status) | ||
}) | ||
h.actions[id] = a | ||
default: | ||
break | ||
} | ||
} | ||
|
||
// Apply applies the action identified by `id` for the given operation | ||
func (h *ActionsHandler) Apply(id string, op *Operation) { | ||
a, ok := h.actions[id] | ||
if !ok { | ||
return | ||
} | ||
op.actions = append(op.actions, a) | ||
} |
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
Oops, something went wrong.