1 of 29

Bringing WAF to Envoy and Istio with WebAssembly and... Go?

Anuraag (ラグ) Agrawal - tetrate.io

2 of 29

Self-introduction

  • OSS Enthusiast
  • Armeria - HTTP / RPC framework
  • Zipkin, OpenTelemetry - observability / tracing
  • Coraza - Web Application Firewall
  • wazero - Pure Go WebAssembly runtime

http://github.com/anuraaga

Allow developers to solve real problems with WebAssembly without creating more

Drink a new type of craft beer

3 of 29

Envoy

  • “Open source edge and service proxy, designed for cloud-native applications”
  • CNCF project
  • Load balancing, observability, authentication, TLS routing, static file serving
  • HTTP and gRPC
  • Sidecar process for polyglot architecture
  • Core component of Istio service mesh
    • Istio allows configuring Envoy for k8s

4 of 29

Web Application Firewall (WAF)

  • Filters requests based on rules
  • OWASP project manages standard OSS implementation
  • CoreRuleSet provides a standard set of rules for basic protection
    • SQL Injection, Log4Shell, etc
  • Two main implementations
    • ModSecurity - C++, commonly used with Apache + nginx
    • Coraza - Go, up and coming project
  • Can be audit-only or blocking

5 of 29

One rule

SecRule REQUEST_LINE|ARGS|ARGS_NAMES|REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_HEADERS|XML:/*|XML://@* "@rx (?i)(?:\$|$?)(?:\{|&(?:lbrace|lcub);?)(?:[^}]{0,15}(?:\$|$?)(?:\{|&(?:lbrace|lcub);?)|(?:jndi|ctx))" \

"id:944150,\

phase:2,\

block,\

t:none,t:urlDecodeUni,t:jsDecode,t:htmlEntityDecode,\

log,\

msg:'Potential Remote Command Execution: Log4j / Log4shell',\

tag:'application-multi',\

tag:'language-java',\

tag:'platform-multi',\

tag:'attack-rce',\

tag:'OWASP_CRS',\

tag:'capec/1000/152/137/6',\

tag:'PCI/6.5.2',\

tag:'paranoia-level/1',\

ver:'OWASP_CRS/4.0.0-rc1',\

severity:'CRITICAL',\

setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\

setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

6 of 29

Language Reference

7 of 29

Why WAF?

8 of 29

Why Envoy WAF?

  • Located at all ingress/egress points in architecture
  • Works well on premise or in k8s, etc
  • Static C++ binary, but can be extended with WebAssembly

9 of 29

WebAssembly

10 of 29

WebAssembly

11 of 29

WebAssembly is not Assembly

  • Stack-based virtual machine bytecode
    • So is Java
  • No semantics
    • No operations on structs
  • Some hardware bias
    • SIMD instructions

Common hardware are register machines, not stack machines

12 of 29

WebAssembly bytecode

C source code

WebAssembly .wat text format

WebAssembly .wasm binary format

int factorial(int n) {

if (n == 0)

return 1;

else

return n * factorial(n-1);

}

(func (param i64) (result i64)

local.get 0

i64.eqz

if (result i64)

i64.const 1

else

local.get 0

local.get 0

i64.const 1

i64.sub

call 0

i64.mul

end)

00 61 73 6D 01 00 00 00

01 00 01 60 01 73 01 73 06

03 00 01 00 02

0A 00 01

00 00

20 00

50

04 7E

42 01

05

20 00

20 00

42 01

7D

10 00

7E

0B

0B 15 17

13 of 29

So not fast?

  • Often no: https://zaplib.com/docs/blog_post_mortem.html
  • Overhead when calling functions across sandbox
  • Self-contained apps might be “pretty fast”

14 of 29

WebAssembly has a bias but is not only for web

  • WebAssembly is a web standard
    • Biggest contributors are Google and Mozilla
    • Much tooling focuses on integrating wasm and browser
  • Language runtimes can allow running outside the browser
    • Go: wazero, Rust: wasmtime, JS: V8, etc
    • Tooling still immature for integrating wasm and host process

15 of 29

What is WebAssembly?

  • General bytecode for targeting from any programming language
  • Sandboxed execution model
    • No access to host memory, syscalls without explicitly exposing through ABI
  • No structure, standard library, etc
    • Compile the entire language into binary

Compile existing binary for loading in web (not as fast as native but maybe enough)

Allow extending apps in a safe way (or only way, e.g. Go)

16 of 29

Host

WebAssembly Runtime

Sandbox Memory

Compiled wasm

str

ld

Wasm binary

Read

Compile

Host function

Guest function

17 of 29

What is an ABI?

  • Interface to allow interaction between two separately compiled executables
  • Generally refers to code executing in the same process
    • IPC is the interface between different processes on a single machine
    • RPC is the interface between different processes (often) on different machines

18 of 29

What is a WebAssembly ABI

  • Interface for WebAssembly program to interact with the host
  • By default, WebAssembly is in a sandbox that cannot interact at all
  • When extending / writing plugins, gives access to the extended object

19 of 29

ABI parts

  • Functions with numeric parameters
  • Nope that’s it

20 of 29

proxy-wasm ABI

21 of 29

proxy-wasm-go-sdk

  • Works with TinyGo, compiler from Go to Wasm
  • Implements proxy-wasm ABI and presents idiomatic Go interfaces
  • Testing framework
  • Cannot expose a Wasm ABI without an SDK for most developers

https://github.com/tetratelabs/proxy-wasm-go-sdk

22 of 29

WAF Flow

tx := waf.NewTransaction()

tx.ProcessConnection(clientIP, clientPort)

tx.ProcessURI(url, method)

for header := range request.Header { tx.AddRequestHeader(header) }

if tx.ProcessRequestHeaders() { response.WriteStatus(403); return }

tx.RequestBodyWriter().Write(request.Body)

If tx.ProcessRequestBody() { response.WriteStatus(403); return }

// Repeat for response

23 of 29

WAF Flow in Envoy

proxy_on_http_request_headers:

tx := waf.NewTransaction()

tx.ProcessConnection(clientIP, clientPort)

tx.ProcessURI(url, method)

for header := range request.Header { tx.AddRequestHeader(header) }

if tx.ProcessRequestHeaders() { response.WriteStatus(403); return }

process_on_http_request_body:

tx.RequestBodyWriter().Write(request.Body)

If tx.ProcessRequestBody() { response.WriteStatus(403); return }

process_on_http_response_headers: / process_on_http_response_body:

// Repeat for response

24 of 29

coraza-proxy-wasm

25 of 29

Overhead

WAF:

All done 1000 calls (plus 0 warmup) 4.487 ms avg, 99.9 qps

No WAF:

All done 1000 calls (plus 0 warmup) 2.900 ms avg, 100.0 qps

26 of 29

Polyglot binary

TinyGo compiler more for embedded systems than Wasm. Swapped components for performance

  • regexp with libre2
  • malloc/free with mimalloc
  • GC with bdwgc

Now possible to write high performance Wasm with Go

27 of 29

Try it now

28 of 29

Future

https://github.com/mosn/mosn/pull/2168

https://github.com/dapr/components-contrib/blob/master/middleware/http/wasm/httpwasm.go

One WAF Wasm binary for all the servers

Towards a future where experts work in one language and apply to all

29 of 29

Real-time WAF in wasm with Go (and others)