From 40049c3da0c958aee546f54fe7e9e30d8fb6a545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 12 May 2022 10:10:11 +0200 Subject: [PATCH 1/8] chore: builds wasm proxy filter. --- .github/workflows/test.yaml | 31 +++++++++ .gitignore | 1 + .gitmodules | 3 + Makefile | 7 ++ README.md | 1 + coraza | 1 + envoy-config.yaml | 92 +++++++++++++++++++++++++++ go.mod | 26 ++++++++ go.sum | 84 ++++++++++++++++++++++++ main.go | 123 ++++++++++++++++++++++++++++++++++++ main_test.go | 61 ++++++++++++++++++ 11 files changed, 430 insertions(+) create mode 100644 .github/workflows/test.yaml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 README.md create mode 160000 coraza create mode 100644 envoy-config.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 main_test.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000000000..263a65984f109 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,31 @@ +name: test +on: + push: + branches: + - main + paths-ignore: + - "**/*.md" + - "LICENSE" + pull_request: + +jobs: + test: + runs-on: ubuntu-20.04 + strategy: + matrix: + go: ["1.17", "1.18"] + steps: + # Set fetch-depth: 0 to fetch commit history and tags for use in version calculation + - name: Check out code + uses: actions/checkout@v2.3.4 + with: + fetch-depth: 0 + + - name: Setup go + uses: actions/setup-go@v1 + with: + go-version: ${{ matrix.go }} + + - name: Run tests + shell: bash + run: make test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000..c795b054e5ade --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..d76d1cbbd10b0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "coraza"] + path = coraza + url = git@github.com:jcchavezs/coraza.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000..f61cd87ad80cf --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: build +build: + mkdir -p ./build + tinygo build -o build/main.wasm -scheduler=asyncify -target=wasi ./main.go + +test: + go test -tags=proxytest ./... \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..601926f577c99 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Coraza WASM filter diff --git a/coraza b/coraza new file mode 160000 index 0000000000000..ca4b50d4af61b --- /dev/null +++ b/coraza @@ -0,0 +1 @@ +Subproject commit ca4b50d4af61beb04b618b368cc2850de31aa9f8 diff --git a/envoy-config.yaml b/envoy-config.yaml new file mode 100644 index 0000000000000..d8d4435d7453d --- /dev/null +++ b/envoy-config.yaml @@ -0,0 +1,92 @@ +static_resources: + listeners: + - name: main + address: + socket_address: + address: 0.0.0.0 + port_value: 18000 + filter_chains: + - filters: + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: auto + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: web_service + http_filters: + - name: envoy.filters.http.wasm + typed_config: + "@type": type.googleapis.com/udpa.type.v1.TypedStruct + type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm + value: + config: + vm_config: + runtime: "envoy.wasm.runtime.v8" + code: + local: + filename: "./build/main.wasm" + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + - name: staticreply + address: + socket_address: + address: 127.0.0.1 + port_value: 8099 + filter_chains: + - filters: + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: auto + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + direct_response: + status: 200 + body: + inline_string: "example body\n" + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + clusters: + - name: web_service + connect_timeout: 0.25s + type: STATIC + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: mock_service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 8099 + +admin: + access_log_path: "/dev/null" + address: + socket_address: + address: 0.0.0.0 + port_value: 8001 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000..ca81530154c86 --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module github.com/jcchavezs/coraza-wasm-filter + +go 1.17 + +require ( + github.com/corazawaf/coraza/v2 v2.0.0 + 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.1 +) + +require ( + github.com/cloudflare/ahocorasick v0.0.0-20210425175752-730270c3e184 // indirect + github.com/corazawaf/libinjection-go v0.0.0-20220207031228-44e9c4250eb5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) + +replace github.com/corazawaf/coraza/v2 => ./coraza diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000..6cf9da3eb4d42 --- /dev/null +++ b/go.sum @@ -0,0 +1,84 @@ +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/cloudflare/ahocorasick v0.0.0-20210425175752-730270c3e184 h1:8yL+85JpbwrIc6m+7N1iYrjn/22z68jwrTIBOJHNe4k= +github.com/cloudflare/ahocorasick v0.0.0-20210425175752-730270c3e184/go.mod h1:tGWUZLZp9ajsxUOnHmFFLnqnlKXsCn6GReG4jAD59H0= +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= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +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/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tetratelabs/proxy-wasm-go-sdk v0.18.1-0.20220510133519-6240ca761207 h1:2QpMF4ADtHe8QDgvDJVa04Bu3diiQ04iezw7AaSH9PU= +github.com/tetratelabs/proxy-wasm-go-sdk v0.18.1-0.20220510133519-6240ca761207/go.mod h1:Aq7ewLe7qv2pmzRgoYWk+5kYLj/oSYMJjvmPXpGSAbc= +github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo= +github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000000000..5677525f10305 --- /dev/null +++ b/main.go @@ -0,0 +1,123 @@ +package main + +import ( + "fmt" + + "github.com/corazawaf/coraza/v2" + "github.com/corazawaf/coraza/v2/seclang" + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" + "github.com/tidwall/gjson" +) + +func main() { + proxywasm.SetVMContext(&vmContext{}) +} + +type vmContext struct { + // Embed the default VM context here, + // so that we don't need to reimplement all the methods. + types.DefaultVMContext +} + +// Override types.DefaultVMContext. +func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { + return &pluginContext{} +} + +type pluginContext struct { + // Embed the default plugin context here, + // so that we don't need to reimplement all the methods. + types.DefaultPluginContext + + configuration pluginConfiguration +} + +// pluginConfiguration is a type to represent an example configuration for this wasm plugin. +type pluginConfiguration struct { + rules string +} + +// Override types.DefaultPluginContext. +func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus { + data, err := proxywasm.GetPluginConfiguration() + if err != nil && err != types.ErrorStatusNotFound { + proxywasm.LogCriticalf("error reading plugin configuration: %v", err) + return types.OnPluginStartStatusFailed + } + config, err := parsePluginConfiguration(data) + if err != nil { + proxywasm.LogCriticalf("error parsing plugin configuration: %v", err) + return types.OnPluginStartStatusFailed + } + ctx.configuration = config + return types.OnPluginStartStatusOK +} + +func parsePluginConfiguration(data []byte) (pluginConfiguration, error) { + if len(data) == 0 { + return pluginConfiguration{}, nil + } + config := &pluginConfiguration{} + if !gjson.ValidBytes(data) { + return pluginConfiguration{}, fmt.Errorf("the plugin configuration is not a valid json: %q", string(data)) + } + + jsonData := gjson.ParseBytes(data) + config.rules = jsonData.Get("rules").String() + + return *config, nil +} + +// 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} +} + +type httpHeaders 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 +} + +// Override types.DefaultHttpContext. +func (ctx *httpHeaders) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { + tx := ctx.waf.NewTransaction() + + hs, err := proxywasm.GetHttpRequestHeaders() + if err != nil { + proxywasm.LogCriticalf("failed to get request headers: %v", err) + return types.ActionContinue + } + + for _, h := range hs { + tx.AddRequestHeader(h[0], h[1]) + } + + interruption := tx.ProcessRequestHeaders() + if interruption != nil { + proxywasm.LogInfof("%d interrupted: %v", ctx.contextID, interruption) + return types.ActionPause + } + + return types.ActionContinue +} + +// Override types.DefaultHttpContext. +func (ctx *httpHeaders) OnHttpStreamDone() { + proxywasm.LogInfof("%d finished", ctx.contextID) +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000000000..8a6ae7086c7a3 --- /dev/null +++ b/main_test.go @@ -0,0 +1,61 @@ +// These tests are supposed to run with `proxytest` build tag, and this way we can leverage the testing framework in "proxytest" package. +// The framework emulates the expected behavior of Envoyproxy, and you can test your extensions without running Envoy and with +// the standard Go CLI. To run tests, simply run +// go test -tags=proxytest ./... + +//go:build proxytest + +package main + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/proxytest" + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" +) + +func TestHttpHeaders_OnHttpRequestHeaders(t *testing.T) { + type testCase struct { + requestURI string + expectedAction types.Action + } + + for name, tCase := range map[string]testCase{ + "not matching URL": { + requestURI: "/", + expectedAction: types.ActionContinue, + }, + "matching URL": { + requestURI: "/admin", + expectedAction: types.ActionPause, + }, + } { + t.Run(name, func(t *testing.T) { + opt := proxytest. + NewEmulatorOption(). + WithVMContext(&vmContext{}). + WithPluginConfiguration([]byte(` + { + "rules" : "SecRuleEngine On\nSecRule REQUEST_URI \"@streqr /admin\" \"id:101,phase:1,t:lowercase,deny\"" + } + `)) + host, reset := proxytest.NewHostEmulator(opt) + defer reset() + + require.Equal(t, types.OnPluginStartStatusOK, host.StartPlugin()) + + // Initialize http context. + id := host.InitializeHttpContext() + + // Call OnHttpResponseHeaders. + hs := [][2]string{{"REQUEST_URI", tCase.requestURI}} + action := host.CallOnRequestHeaders(id, hs, false) + require.Equal(t, tCase.expectedAction, action) + + // Call OnHttpStreamDone. + host.CompleteHttpContext(id) + }) + } +} From c7e277a1474324f4d0c8399d00488c06e1f01ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 12 May 2022 10:13:09 +0200 Subject: [PATCH 2/8] chore: pull submodules. --- .github/workflows/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 263a65984f109..e068fbc3eb3cf 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,6 +20,7 @@ jobs: uses: actions/checkout@v2.3.4 with: fetch-depth: 0 + submodules: true - name: Setup go uses: actions/setup-go@v1 From 91d51a5d2380956a27e50205bc840b143b5ca52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 12 May 2022 23:21:10 +0200 Subject: [PATCH 3/8] chore: fixes the test. --- main.go | 14 ++++++++++++++ main_test.go | 12 ++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 5677525f10305..e492278774d90 100644 --- a/main.go +++ b/main.go @@ -98,6 +98,20 @@ type httpHeaders struct { func (ctx *httpHeaders) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { tx := ctx.waf.NewTransaction() + path, err := proxywasm.GetHttpRequestHeader(":path") + if err != nil { + proxywasm.LogCriticalf("failed to get path header: %v", err) + return types.ActionContinue + } + + method, err := proxywasm.GetHttpRequestHeader(":method") + if err != nil { + proxywasm.LogCriticalf("failed to get method header: %v", err) + return types.ActionContinue + } + + tx.ProcessURI(path, method, "1.1") // TODO use the right version + hs, err := proxywasm.GetHttpRequestHeaders() if err != nil { proxywasm.LogCriticalf("failed to get request headers: %v", err) diff --git a/main_test.go b/main_test.go index 8a6ae7086c7a3..13e985fc2f5cf 100644 --- a/main_test.go +++ b/main_test.go @@ -18,17 +18,17 @@ import ( func TestHttpHeaders_OnHttpRequestHeaders(t *testing.T) { type testCase struct { - requestURI string + path string expectedAction types.Action } for name, tCase := range map[string]testCase{ "not matching URL": { - requestURI: "/", + path: "/", expectedAction: types.ActionContinue, }, "matching URL": { - requestURI: "/admin", + path: "/admin", expectedAction: types.ActionPause, }, } { @@ -38,7 +38,7 @@ func TestHttpHeaders_OnHttpRequestHeaders(t *testing.T) { WithVMContext(&vmContext{}). WithPluginConfiguration([]byte(` { - "rules" : "SecRuleEngine On\nSecRule REQUEST_URI \"@streqr /admin\" \"id:101,phase:1,t:lowercase,deny\"" + "rules" : "SecRuleEngine On\nSecRule REQUEST_URI \"@streq /admin\" \"id:101,phase:1,t:lowercase,deny\"" } `)) host, reset := proxytest.NewHostEmulator(opt) @@ -49,8 +49,8 @@ func TestHttpHeaders_OnHttpRequestHeaders(t *testing.T) { // Initialize http context. id := host.InitializeHttpContext() - // Call OnHttpResponseHeaders. - hs := [][2]string{{"REQUEST_URI", tCase.requestURI}} + // Call OnHttpRequestHeaders. + hs := [][2]string{{":path", tCase.path}, {":method", "GET"}} action := host.CallOnRequestHeaders(id, hs, false) require.Equal(t, tCase.expectedAction, action) From 405e6ac1aea805029ba321d229d6263d1319980e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 12 May 2022 23:23:38 +0200 Subject: [PATCH 4/8] chore: adds build process to the CI. --- .github/workflows/test.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e068fbc3eb3cf..c476ce3bab06f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -30,3 +30,7 @@ jobs: - name: Run tests shell: bash run: make test + + - name: Build WASM filter + shell: bash + run: make build From d89e7b4edf608f168f4553a4dd64edc30b22d99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Fri, 13 May 2022 09:59:32 +0200 Subject: [PATCH 5/8] chore: fixes build. --- .github/workflows/test.yaml | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c476ce3bab06f..07a2e0bafffe8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,4 +1,4 @@ -name: test +name: CI check on: push: branches: @@ -11,9 +11,6 @@ on: jobs: test: runs-on: ubuntu-20.04 - strategy: - matrix: - go: ["1.17", "1.18"] steps: # Set fetch-depth: 0 to fetch commit history and tags for use in version calculation - name: Check out code @@ -22,15 +19,36 @@ jobs: fetch-depth: 0 submodules: true - - name: Setup go + - name: Install Go uses: actions/setup-go@v1 with: - go-version: ${{ matrix.go }} + go-version: 1.18.1 - name: Run tests shell: bash run: make test + build: + needs: [test] + runs-on: ubuntu-20.04 + container: + image: tinygo/tinygo:0.23.0 + steps: + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: "1.18.1" + + - name: Check out code + uses: actions/checkout@v2.3.4 + with: + fetch-depth: 0 + submodules: true + + # TinyGo's release container does not have Make command. + - name: Install Make + run: apt install make + - name: Build WASM filter shell: bash run: make build From 9f88542e5faf307b76b4a999d6910e2a3b872242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Fri, 13 May 2022 10:02:52 +0200 Subject: [PATCH 6/8] fix: fixes git submodule checkout. --- .github/workflows/test.yaml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 07a2e0bafffe8..571c61627262e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -34,10 +34,17 @@ jobs: container: image: tinygo/tinygo:0.23.0 steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: "1.18.1" + # submodule needs .git folder, which is missing without installing a newer git command + # https://github.com/actions/checkout/issues/335 + - name: "Install latest `git`" + run: | + apt purge git -y + add-apt-repository ppa:git-core/ppa -y + # apt update fails to fetch some repo due to cert failure. Skip them. + apt update || true; apt install -y --no-install-recommends \ + git \ + && apt clean \ + && rm -rf /var/lib/apt/lists/* - name: Check out code uses: actions/checkout@v2.3.4 @@ -45,6 +52,11 @@ jobs: fetch-depth: 0 submodules: true + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: "1.18.1" + # TinyGo's release container does not have Make command. - name: Install Make run: apt install make From ad76827b8d126b66963c2bca285a94a6407c3691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Fri, 13 May 2022 10:05:16 +0200 Subject: [PATCH 7/8] fix: adds support for add-apt-repository. --- .github/workflows/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 571c61627262e..704ad985ba44d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -39,6 +39,7 @@ jobs: - name: "Install latest `git`" run: | apt purge git -y + apt-get update && apt-get install -y software-properties-common add-apt-repository ppa:git-core/ppa -y # apt update fails to fetch some repo due to cert failure. Skip them. apt update || true; apt install -y --no-install-recommends \ From c27a3723616c20d55e8ba59c88bd7e33100a9a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Fri, 13 May 2022 10:26:33 +0200 Subject: [PATCH 8/8] chore: improves make install. --- .github/workflows/test.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 704ad985ba44d..8f055df2c9949 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -39,7 +39,7 @@ jobs: - name: "Install latest `git`" run: | apt purge git -y - apt-get update && apt-get install -y software-properties-common + apt-get update && apt-get install -y software-properties-common make add-apt-repository ppa:git-core/ppa -y # apt update fails to fetch some repo due to cert failure. Skip them. apt update || true; apt install -y --no-install-recommends \ @@ -58,10 +58,6 @@ jobs: with: go-version: "1.18.1" - # TinyGo's release container does not have Make command. - - name: Install Make - run: apt install make - - name: Build WASM filter shell: bash run: make build