coraza-caddy
coraza-caddy copied to clipboard
Websocket connections not working as soon as Coraza is active
Hello everyone,
as already announced in your Slack channel, I am currently facing issues with Websocket connections in conjunction with coraza-caddy.
These connections work fine if I set SecRuleEngine Off
or remove the corresponding snippet from my subdomain config in caddy, but as soon as Coraza is in the play, these websocket connections do not seem to be handled correctly, while the rest of HTTP requests seems to be fine.
The websocket connections also won't work with SecRuleEngine DetectionOnly
set, which makes me assume that this may be some internal Coraza handling problem, and not the OWASP rules, Coraza config etc.
Setup
Linux LXC Debian 11 Caddy 2.6.4 + coraza 3.0.0 + coraza-caddy 2.0.0-rc2 with xcaddy
build-info
go go1.20.5
path caddy
mod caddy (devel)
dep filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
dep github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
dep github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
dep github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
dep github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
dep github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
dep github.com/alecthomas/chroma/v2 v2.5.0 h1:CQCdj1BiBV17sD4Bd32b/Bzuiq/EqoNTrnIhyQAZ+Rk=
dep github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
dep github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
dep github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
dep github.com/caddyserver/caddy/v2 v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=
dep github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
dep github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
dep github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
dep github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
dep github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
dep github.com/corazawaf/coraza-caddy/v2 v2.0.0-rc.2 h1:HY101opLin5TVKvNlyRv25LQc4HVvdJ9cIiFMgc0UO8=
dep github.com/corazawaf/coraza-coreruleset v0.0.0-20230405190458-b4d2a6f6bdfh1:EU5PEFuQ4PTzkz2YFoCQwI73gu8GW95txz3dMNGsseA=
dep github.com/corazawaf/coraza/v3 v3.0.0 h1:GvTzxcgtfQ76LneYL19Nkb1/T+2E/s3BRAOEt6h2sY0=
dep github.com/corazawaf/libinjection-go v0.1.2 h1:oeiV9pc5rvJ+2oqOqXEAMJousPpGiup6f7Y3nZj5GoM=
dep github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
dep github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
dep github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
dep github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
dep github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
dep github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
dep github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
dep github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
dep github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
dep github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
dep github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
dep github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
dep github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
dep github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
dep github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
dep github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
dep github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
dep github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
dep github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU=
dep github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
dep github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
dep github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
dep github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
dep github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
dep github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
dep github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
dep github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
dep github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
dep github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
dep github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
dep github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
dep github.com/jcchavezs/mergefs v0.0.0-20230405222254-20429875efdd h1:wj0PapN9ZM27EnZqqtvVHUpRUWDHEK3/H7gkBFj1qyw=
dep github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
dep github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
dep github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
dep github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
dep github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
dep github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
dep github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
dep github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
dep github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
dep github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU=
dep github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
dep github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
dep github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
dep github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
dep github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 h1:lL+y4Xv20pVlCGyLzNHRC0I0rIHhIL1lTvHizoS/dU8=
dep github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
dep github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
dep github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
dep github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
dep github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
dep github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
dep github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
dep github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
dep github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
dep github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
dep github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
dep github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
dep github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
dep github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM=
dep github.com/smallstep/certificates v0.23.2 h1:7KSx9WfZ3CILV0XlsTrl+PK58YE4CHSgqobB6+ieQWs=
dep github.com/smallstep/nosql v0.5.0 h1:1BPyHy8bha8qSaxgULGEdqhXpNFXimAfudnauFVqmxw=
dep github.com/smallstep/truststore v0.12.1 h1:guLUKkc1UlsXeS3t6BuVMa4leOOpdiv02PCRTiy1WdY=
dep github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
dep github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
dep github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
dep github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
dep github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2 h1:TrgfmCXwtWyFw85UkRGXt9qZRzdzt3nWt2Rerdecn0w=
dep github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
dep github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
dep github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
dep github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
dep github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
dep github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
dep github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87 h1:Py16JEzkSdKAtEFJjiaYLYBOWGXc1r/xHj/Q/5lA37k=
dep go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
dep go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak=
dep go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.39.0 h1:vFEBG7SieZJzvnRWQ81jxpuEqe6J8Ex+hgc9CqOTzHc=
dep go.opentelemetry.io/otel v1.13.0 h1:1ZAKnNQKwBBxFtww/GwxNUyTf0AxkZzrukO8MeXqe4Y=
dep go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0 h1:j7AwzDdAQBJjcqayAaYbvpYeZzII7cEe5qJTu+De6UY=
dep go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.0 h1:lRpP10E8oTGVmY1nVXcwelCT1Z8ca41/l5ce7AqLAss=
dep go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0 h1:buSx4AMC/0Z232slPhicN/fU5KIlj0bMngct5pcZhkI=
dep go.opentelemetry.io/otel/metric v0.36.0 h1:t0lgGI+L68QWt3QtOIlqM9gXoxqxWLhZ3R/e5oOAY0Q=
dep go.opentelemetry.io/otel/sdk v1.13.0 h1:BHib5g8MvdqS65yo2vV1s6Le42Hm6rrw08qU6yz5JaM=
dep go.opentelemetry.io/otel/trace v1.13.0 h1:CBgRZ6ntv+Amuj1jDsMhZtlAPT6gbyIRdaIzFhfBSdY=
dep go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=
dep go.step.sm/cli-utils v0.7.5 h1:jyp6X8k8mN1B0uWJydTid0C++8tQhm2kaaAdXKQQzdk=
dep go.step.sm/crypto v0.23.2 h1:XGmQH9Pkpxop47cjYlUhF10L5roPCbu1BCZXopbeW8I=
dep go.step.sm/linkedca v0.19.0 h1:xuagkR35wrJI2gnu6FAM+q3VmjwsHScvGcJsfZ0GdsI=
dep go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
dep go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
dep go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
dep golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
dep golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
dep golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
dep golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
dep golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
dep golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
dep golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
dep google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs=
dep google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ=
dep google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
dep gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
dep gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
dep gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
dep rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
build -buildmode=exe
build -compiler=gc
build -trimpath=true
build CGO_ENABLED=0
build GOARCH=amd64
build GOOS=linux
build GOAMD64=v1
Reproduce
Please follow this example to reproduce the issue
- Run a instance of RustPad
docker run --rm -dp 3030:3030 ekzhang/rustpad
- Run your instance of caddy+Coraza with the following Caddyfile (adapt accordingly)
{
debug
auto_https off
order coraza_waf first
}
(tls) {
tls /etc/acme/{args.0}_ecc/fullchain.cer /etc/acme/{args.0}_ecc/{args.0}.key
}
(waf) {
coraza_waf {
directives `
SecRuleEngine Off
`
}
}
pad.example.com {
import tls example.com
import waf
reverse_proxy localhost:3030
}
-
When visiting the app via Caddy, you should then see Rustpad
Connecting to the server...
and thenYou are connected
(or check WS connection in your browsers network inspector) - everything works fine. -
In the Caddyfile, replace
SecRuleEngine Off
withSecRuleEngine DetectionOnly
and reload Caddy. -
Now, Rustpad wont be able to establish a websocket connection :( DEBUG log output should provide something like:
2023/06/24 14:45:22.808 DEBUG http.handlers.reverse_proxy selected upstream{"dial": "10.0.0.110:3030", "total_upstreams": 1}
2023/06/24 14:45:22.809 DEBUG http.handlers.reverse_proxy upstream roundtri{"upstream": "10.0.0.110:3030", "duration": 0.000513863, "request": {"remote_ip": "192.168.178.11", "remote_port": "56254", "proto": "HTTP/1.1", "method": "GET", "host": "pad.example.com:11443", "uri": "/api/socket/MCxiai", "headers": {"Connection": ["Upgrade"], "Origin": ["https://pad.example.com:11443"], "Sec-Websocket-Version": ["13"], "Accept-Language": ["de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"], "Sec-Websocket-Extensions": ["permessage-deflate; client_max_window_bits"], "Sec-Websocket-Key": ["FCOBxo+tJXz+iewTbsv4tQ=="], "X-Forwarded-Proto": ["https"], "Upgrade": ["websocket"], "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"], "Accept-Encoding": ["gzip, deflate, br"], "X-Forwarded-Host": ["pad.example.com:11443"], "Cache-Control": ["no-cache"], "Pragma": ["no-cache"], "X-Forwarded-For": ["192.168.178.11"]}, "tls": {"resumed": true, "version": 772, "cipher_suite": 4865, "proto": "http/1.1", "server_name": "pad.example.com"}}, "headers": {"Connection": ["upgrade"], "Upgrade": ["websocket"], "Sec-Websocket-Accept": ["Aqzjvc/3P75wBozHmowR/c98QBI="], "Date": ["Sat, 24 Jun 2023 14:45:21 GMT"]}, "status": 101}
2023/06/24 14:45:22.809 DEBUG http.handlers.reverse_proxy upgrading connection {"upstream": "10.0.0.110:3030", "duration": 0.000513863, "request": {"remote_ip": "192.168.178.11", "remote_port": "56254", "proto": "HTTP/1.1", "method": "GET", "host": "pad.example.com:11443", "uri": "/api/socket/MCxiai", "headers": {"Connection": ["Upgrade"], "Origin": ["https://pad.example.com:11443"], "Sec-Websocket-Version": ["13"], "Accept-Language": ["de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"], "Sec-Websocket-Extensions": ["permessage-deflate; client_max_window_bits"], "Sec-Websocket-Key": ["FCOBxo+tJXz+iewTbsv4tQ=="], "X-Forwarded-Proto": ["https"], "Upgrade": ["websocket"], "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"], "Accept-Encoding": ["gzip, deflate, br"], "X-Forwarded-Host": ["pad.example.com:11443"], "Cache-Control": ["no-cache"], "Pragma": ["no-cache"], "X-Forwarded-For": ["192.168.178.11"]}, "tls": {"resumed": true, "version": 772, "cipher_suite": 4865, "proto": "http/1.1", "server_name": "pad.example.com"}}}
2023/06/24 14:45:22.809 DEBUG http.handlers.reverse_proxy connection closed{"upstream": "10.0.0.110:3030", "duration": 0.000513863, "request": {"remote_ip": "192.168.178.11", "remote_port": "56254", "proto": "HTTP/1.1", "method": "GET", "host": "pad.example.com:11443", "uri": "/api/socket/MCxiai", "headers": {"Connection": ["Upgrade"], "Origin": ["https://pad.example.com:11443"], "Sec-Websocket-Version": ["13"], "Accept-Language": ["de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"], "Sec-Websocket-Extensions": ["permessage-deflate; client_max_window_bits"], "Sec-Websocket-Key": ["FCOBxo+tJXz+iewTbsv4tQ=="], "X-Forwarded-Proto": ["https"], "Upgrade": ["websocket"], "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"], "Accept-Encoding": ["gzip, deflate, br"], "X-Forwarded-Host": ["pad.example.com:11443"], "Cache-Control": ["no-cache"], "Pragma": ["no-cache"], "X-Forwarded-For": ["192.168.178.11"]}, "tls": {"resumed": true, "version": 772, "cipher_suite": 4865, "proto": "http/1.1", "server_name": "pad.example.com"}}, "duration": 0.000393468}
2023/06/24 14:45:22.809 DEBUG http.stdlib http: response.WriteHeader on hijacked connection from github.com/caddyserver/caddy/v2/modules/caddyhttp.(*responseRecorder).WriteHeader (responsewriter.go:191)
I hope this helps - would be a pity if I couldnt use Coraza with apps that need websockets :) Thanks in advance
Hey! We have to take a look on this. Coraza is not designed to handle websockets.
In the meantime the proper configuration is to set handlers to specify which URLs are coraza protected and which URLs are websockets.
Probably something like:
handle_path /ws {
reverse_proxy websocket_server
}
handle_path / {
coraza_waf ...
reverse_proxy web_app
}
Right now, we cannot assume the connection is a web socket initialization and disable coraza, because that would lead to potential coraza bypasses.
This is my working example:
handle /ws {
@websockets {
header_regexp Connection Upgrade
header Upgrade websocket
}
reverse_proxy @websockets tosso:8080
}
handle * {
coraza_waf {
load_owasp_crs
directives `
Include @coraza.conf-recommended
SecRuleEngine On
Include @crs-setup.conf.example
Include @owasp_crs/*.conf
`
}
reverse_proxy tosso:8080
}
You can test it with wscat:
➜ istio-profiles wscat -L --connect wss://tosso.io/ws
Connected (press CTRL+C to quit)
> asdfasdf
< Hello World!
> qewr
< Hello World!
Then you can test that the rest of the site is still blocking:
curl "https://tosso.io/?id=<script>alert(123)</script>"
Thank you @jptosso for providing a nice workaround - can confirm it's working. I made a Caddy snippet to make it easier to integrate with several site in the form of, see below. Maybe this is of help for someone.
Shall I keep the issue open to keep track of this or do you want to close it? I'm fine either way, but would be cool if caddy/coraza could handle this automagically in the future :)
(websocket) {
handle {args.0} {
@websockets {
header_regexp Connection Upgrade
header Upgrade websocket
}
reverse_proxy @websockets {args.1}
}
}
...
rustpad.example.com {
import settings rustpad
import websocket /api/socket/* 10.0.0.110:3030
handle {
import waf
reverse_proxy 10.0.0.110:3030
}
}
I had the same problem, in my scenario there was no way to know in advance which websocket URLs there were, so I had to make coraza websocket compatible. I debugged and found that when the url is websocket, net\http\server.go will set conn.hijackedv to true, and then when coraza calls flushWriteHeader in processResponse, it will determine whether hijackedv is true inside conn.WriteHeader. If true An exception will be thrown. So I modified the func (i *rwInterceptor) WriteHeader(statusCode int) function and added an if condition. It seems that websocket can work normally, but I am not sure whether this modification will cause other problems. I hope to get an official answer.
func (i *rwInterceptor) WriteHeader(statusCode int) {
if i.wroteHeader {
log.Println("http: superfluous response.WriteHeader call")
return
}
for k, vv := range i.w.Header() {
for _, v := range vv {
i.tx.AddResponseHeader(k, v)
}
}
i.statusCode = statusCode
// add this if
if statusCode == 101 {
i.flushWriteHeader()
return
}
if it := i.tx.ProcessResponseHeaders(statusCode, i.proto); it != nil {
i.statusCode = obtainStatusCodeFromInterruptionOrDefault(it, i.statusCode)
i.flushWriteHeader()
return
}
i.wroteHeader = true
}
I had the same problem, in my scenario there was no way to know in advance which websocket URLs there were, so I had to make coraza websocket compatible. I debugged and found that when the url is websocket, net\http\server.go will set conn.hijackedv to true, and then when coraza calls flushWriteHeader in processResponse, it will determine whether hijackedv is true inside conn.WriteHeader. If true An exception will be thrown. So I modified the func (i *rwInterceptor) WriteHeader(statusCode int) function and added an if condition. It seems that websocket can work normally, but I am not sure whether this modification will cause other problems. I hope to get an official answer.
func (i *rwInterceptor) WriteHeader(statusCode int) { if i.wroteHeader { log.Println("http: superfluous response.WriteHeader call") return } for k, vv := range i.w.Header() { for _, v := range vv { i.tx.AddResponseHeader(k, v) } } i.statusCode = statusCode // add this if if statusCode == 101 { i.flushWriteHeader() return } if it := i.tx.ProcessResponseHeaders(statusCode, i.proto); it != nil { i.statusCode = obtainStatusCodeFromInterruptionOrDefault(it, i.statusCode) i.flushWriteHeader() return } i.wroteHeader = true }
There are no plans to support websocket, please take a look at this comment: https://github.com/corazawaf/coraza-caddy/issues/78#issuecomment-1635797541
I've also encountered this problem but in another scenario: server send events, or event source.
It seems that Coraza blocks the response until it receives the complete response, at which point it obtains all the response headers.