Skip to content

Commit

Permalink
Implement all phases (envoyproxy#1)
Browse files Browse the repository at this point in the history
* Implement all phases
  • Loading branch information
anuraaga committed Aug 18, 2022
1 parent 9d2e777 commit 4d7dbf0
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 36 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CONTAINER_NAME=$(ARTIFACT_NAME)-build
build:
mkdir -p ./build
tinygo build -o build/mainraw.wasm -scheduler=none -target=wasi ./main.go
wasm2wat build/mainraw.wasm -o build/mainraw.wat
# Removes unused code, which is important since compiled unused code may import unavailable host functions
wasm-opt -Os -c build/mainraw.wasm -o build/mainopt.wasm
# Unfortuantely the imports themselves are left due to potential use with call_indirect. Hack away missing functions
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/jcchavezs/coraza-wasm-filter
go 1.17

require (
github.com/corazawaf/coraza/v3 v3.0.0-20220816155047-a86760516fd9
github.com/corazawaf/coraza/v3 v3.0.0-20220818013656-f749c07295aa
github.com/stretchr/testify v1.7.1
github.com/tetratelabs/proxy-wasm-go-sdk v0.18.1-0.20220510133519-6240ca761207
github.com/tidwall/gjson v1.14.2
Expand All @@ -13,6 +13,7 @@ require (
github.com/corazawaf/libinjection-go v0.0.0-20220207031228-44e9c4250eb5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/magefile/mage v1.13.0 // indirect
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
Expand Down
7 changes: 5 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
github.com/corazawaf/coraza/v3 v3.0.0-20220816155047-a86760516fd9 h1:StT0O9ZPmihr25eJeFvWFkHfyLEaqd41vg8CYAE6qHY=
github.com/corazawaf/coraza/v3 v3.0.0-20220816155047-a86760516fd9/go.mod h1:HW14DrTnueAukBpS06YbaY46FtVI3L1v4MElDD+aKCk=
github.com/anuraaga/go-modsecurity v0.0.0-20220816070944-f36055ce7d5d/go.mod h1:7jguE759ADzy2EkxGRXigiC0ER1Yq2IFk2qNtwgzc7U=
github.com/corazawaf/coraza/v3 v3.0.0-20220818013656-f749c07295aa h1:VcGQMo37Il7VWGGaTKfDs+/1BaJaDEFiwuq6N39uKDg=
github.com/corazawaf/coraza/v3 v3.0.0-20220818013656-f749c07295aa/go.mod h1:WhAgJsa8yn/1Yx25VbLaRmPLOUAT0vkA5TzlVOkXsMc=
github.com/corazawaf/libinjection-go v0.0.0-20220207031228-44e9c4250eb5 h1:SukhxLQRRBM3nJFEUF+ePG7l0JTWAvaxaG/o6X/FQVY=
github.com/corazawaf/libinjection-go v0.0.0-20220207031228-44e9c4250eb5/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -12,6 +13,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magefile/mage v1.13.0 h1:XtLJl8bcCM7EFoO8FyH8XK3t7G5hQAeK+i4tq+veT9M=
github.com/magefile/mage v1.13.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
Expand Down
173 changes: 140 additions & 33 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package main
import (
"context"
"fmt"
"strconv"

"github.com/corazawaf/coraza/v3"
"github.com/corazawaf/coraza/v3/seclang"
ctypes "github.com/corazawaf/coraza/v3/types"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
"github.com/tidwall/gjson"
Expand All @@ -23,15 +25,15 @@ type vmContext struct {

// Override types.DefaultVMContext.
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
return &corazaPlugin{}
}

type pluginContext struct {
type corazaPlugin struct {
// Embed the default plugin context here,
// so that we don't need to reimplement all the methods.
types.DefaultPluginContext

configuration pluginConfiguration
waf *coraza.Waf
}

// pluginConfiguration is a type to represent an example configuration for this wasm plugin.
Expand All @@ -40,7 +42,7 @@ type pluginConfiguration struct {
}

// Override types.DefaultPluginContext.
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
func (ctx *corazaPlugin) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
data, err := proxywasm.GetPluginConfiguration()
if err != nil && err != types.ErrorStatusNotFound {
proxywasm.LogCriticalf("error reading plugin configuration: %v", err)
Expand All @@ -51,7 +53,21 @@ func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPlu
proxywasm.LogCriticalf("error parsing plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
ctx.configuration = config

// First we initialize our waf and our seclang parser
waf := coraza.NewWaf()
parser, err := seclang.NewParser(waf)
if err != nil {
proxywasm.LogCriticalf("failed to create seclang parser: %v", err)
}

err = parser.FromString(config.rules)
if err != nil {
proxywasm.LogCriticalf("failed to parse rules: %v", err)
}

ctx.waf = waf

return types.OnPluginStartStatusOK
}

Expand All @@ -71,34 +87,23 @@ func parsePluginConfiguration(data []byte) (pluginConfiguration, error) {
}

// Override types.DefaultPluginContext.
func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
// First we initialize our waf and our seclang parser
waf := coraza.NewWaf()
parser, err := seclang.NewParser(waf)
if err != nil {
proxywasm.LogCriticalf("failed to create seclang parser: %v", err)
}

err = parser.FromString(ctx.configuration.rules)
if err != nil {
proxywasm.LogCriticalf("failed to parse rules: %v", err)
}

return &httpHeaders{contextID: contextID, waf: waf}
func (ctx *corazaPlugin) NewHttpContext(contextID uint32) types.HttpContext {
return &httpContext{contextID: contextID, tx: ctx.waf.NewTransaction(context.Background())}
}

type httpHeaders struct {
type httpContext struct {
// Embed the default http context here,
// so that we don't need to reimplement all the methods.
types.DefaultHttpContext
contextID uint32
waf *coraza.Waf
tx *coraza.Transaction
}

// Override types.DefaultHttpContext.
func (ctx *httpHeaders) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
tx := ctx.waf.NewTransaction(context.Background())
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
tx := ctx.tx

// TODO(anuraaga): Do these work with HTTP/1?
path, err := proxywasm.GetHttpRequestHeader(":path")
if err != nil {
proxywasm.LogCriticalf("failed to get path header: %v", err)
Expand All @@ -111,7 +116,7 @@ func (ctx *httpHeaders) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t
return types.ActionContinue
}

tx.ProcessURI(path, method, "1.1") // TODO use the right HTTP version
tx.ProcessURI(path, method, "2.0") // TODO use the right HTTP version

hs, err := proxywasm.GetHttpRequestHeaders()
if err != nil {
Expand All @@ -125,22 +130,124 @@ func (ctx *httpHeaders) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t

interruption := tx.ProcessRequestHeaders()
if interruption != nil {
proxywasm.LogInfof("%d interrupted, action %q", ctx.contextID, interruption.Action)
statusCode := interruption.Status
if statusCode == 0 {
statusCode = 403
}
ctx.handleInterruption(interruption)
return types.ActionContinue
}

return types.ActionContinue
}

func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action {
tx := ctx.tx

if err := proxywasm.SendHttpResponse(uint32(statusCode), nil, nil, -1); err != nil {
panic(err)
}
body, err := proxywasm.GetHttpRequestBody(0, bodySize)
if err != nil {
proxywasm.LogCriticalf("failed to get request body: %v", err)
return types.ActionContinue
}

_, err = tx.RequestBodyBuffer.Write(body)
if err != nil {
proxywasm.LogCriticalf("failed to read request body: %v", err)
return types.ActionContinue
}

if !endOfStream {
return types.ActionContinue
}

interruption, err := tx.ProcessRequestBody()
if err != nil {
proxywasm.LogCriticalf("failed to process request body: %v", err)
return types.ActionContinue
}
if interruption != nil {
ctx.handleInterruption(interruption)
return types.ActionContinue
}

return types.ActionContinue
}

func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
tx := ctx.tx

status, err := proxywasm.GetHttpResponseHeader(":status")
if err != nil {
proxywasm.LogCriticalf("failed to get status header: %v", err)
return types.ActionContinue
}
code, err := strconv.Atoi(status)
if err != nil {
code = 0
}

hs, err := proxywasm.GetHttpResponseHeaders()
if err != nil {
proxywasm.LogCriticalf("failed to get response headers: %v", err)
return types.ActionContinue
}

for _, h := range hs {
tx.AddResponseHeader(h[0], h[1])
}

interruption := tx.ProcessResponseHeaders(code, "2.0")
if interruption != nil {
ctx.handleInterruption(interruption)
return types.ActionContinue
}

return types.ActionContinue
}

func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action {
tx := ctx.tx

body, err := proxywasm.GetHttpResponseBody(0, bodySize)
if err != nil {
proxywasm.LogCriticalf("failed to get response body: %v", err)
return types.ActionContinue
}

_, err = tx.ResponseBodyBuffer.Write(body)
if err != nil {
proxywasm.LogCriticalf("failed to read response body: %v", err)
return types.ActionContinue
}

if !endOfStream {
return types.ActionContinue
}

interruption, err := tx.ProcessResponseBody()
if err != nil {
proxywasm.LogCriticalf("failed to process response body: %v", err)
return types.ActionContinue
}
if interruption != nil {
ctx.handleInterruption(interruption)
return types.ActionContinue
}

return types.ActionContinue
}

// Override types.DefaultHttpContext.
func (ctx *httpHeaders) OnHttpStreamDone() {
func (ctx *httpContext) OnHttpStreamDone() {
ctx.tx.ProcessLogging()
_ = ctx.tx.Clean()
proxywasm.LogInfof("%d finished", ctx.contextID)
}

func (ctx *httpContext) handleInterruption(interruption *ctypes.Interruption) {
proxywasm.LogInfof("%d interrupted, action %q", ctx.contextID, interruption.Action)
statusCode := interruption.Status
if statusCode == 0 {
statusCode = 403
}

if err := proxywasm.SendHttpResponse(uint32(statusCode), nil, nil, -1); err != nil {
panic(err)
}
}

0 comments on commit 4d7dbf0

Please sign in to comment.