Repository navigation
IBX-11181: Filtered client-supplied X-Forwarded-* headers in Varnish VCL #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
vidarl
wants to merge
7
commits into
4.6
Choose a base branch
from
IBX-11181-Trusted_Proxies_is_not_set_on_Ibexa_Cloud
base: 4.6
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
53f12a3
IBX-11181: Filtered client-supplied X-Forwarded-* headers in Varnish VCL
vidarl 6a7a503
Added varnishtest coverage for reverse proxy header filtering
vidarl 6e28043
Moved the varnishtest image off end-of-life Debian 11
vidarl 1fbbefb
Update .github/workflows/backend-ci.yaml
vidarl e03898d
fixup! IBX-11181: Filtered client-supplied X-Forwarded-* headers in V…
vidarl 82927b3
fixup! Added varnishtest coverage for reverse proxy header filtering
vidarl ee33491
Moved the Varnish VCL tests job to ubuntu-26.04
vidarl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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,45 @@ | ||
| # Image for running the varnishtest suite in tests/varnish/. | ||
| # | ||
| # Mirrors doc/docker/Dockerfile-varnish from ibexa/docker. It has to be Varnish 6.0LTS specifically: | ||
| # this VCL returns "miss" from vcl_hit, which 6.5 and later reject. The xkey vmod is not packaged | ||
| # for 6.0LTS, so varnish-modules is built from source, as in the image this mirrors. | ||
|
|
||
| FROM debian:bookworm-slim | ||
|
|
||
| ENV DEBIAN_FRONTEND=noninteractive | ||
|
|
||
| ARG PACKAGECLOUD_URL=https://packagecloud.io/install/repositories/varnishcache/varnish60lts/script.deb.sh | ||
| ARG VARNISH_MODULES_VERSION=0.15.0 | ||
|
|
||
| RUN set -xe \ | ||
| && buildDeps=" \ | ||
| make \ | ||
| automake \ | ||
| autotools-dev \ | ||
| libedit-dev \ | ||
| libjemalloc-dev \ | ||
| libncurses-dev \ | ||
| libpcre3-dev \ | ||
| libtool \ | ||
| pkg-config \ | ||
| python3-docutils \ | ||
| sphinx-common \ | ||
| varnish-dev=6.0.12-1~bookworm \ | ||
| " \ | ||
| && apt-get update -q -y \ | ||
| && apt-get install -q -y --no-install-recommends ca-certificates curl \ | ||
| \ | ||
| && curl -s ${PACKAGECLOUD_URL} | bash \ | ||
| && apt-get install -q -y --allow-unauthenticated --no-install-recommends varnish=6.0.12-1~bookworm $buildDeps \ | ||
| \ | ||
| && curl -A "Docker" -o /tmp/varnish-modules.tar.gz -D - -L -s https://github.com/varnish/varnish-modules/archive/refs/tags/${VARNISH_MODULES_VERSION}.tar.gz \ | ||
| && tar zxpf /tmp/varnish-modules.tar.gz -C /tmp/ \ | ||
| && cd /tmp/varnish-modules-${VARNISH_MODULES_VERSION} \ | ||
| && ./bootstrap \ | ||
| && ./configure \ | ||
| && make \ | ||
| && make install \ | ||
| && rm -f /tmp/varnish-modules.tar.gz && rm -Rf /tmp/varnish-modules-${VARNISH_MODULES_VERSION} \ | ||
| \ | ||
| && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $buildDeps \ | ||
| && rm -rf /var/lib/apt/lists/* |
This file contains hidden or 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,21 @@ | ||
| # Image for running the varnishtest suite in tests/varnish/ against varnish9.vcl. | ||
| # | ||
| # Mirrors docker/Dockerfile-varnish9 from ibexa/docker: the VCL imports the xkey vmod, which the | ||
| # official image does not ship. | ||
|
|
||
| FROM varnish:9.0 | ||
|
|
||
| SHELL ["/bin/bash", "-c"] | ||
|
|
||
| # Root is only needed to install the vmod; the image drops back to the unprivileged user the base | ||
| # image runs as by default. | ||
| USER root | ||
| RUN set -e && \ | ||
| read -ra vmod_deps <<< "$VMOD_DEPS" && \ | ||
| varnish_version=$(dpkg-query -W -f='${Version}' varnish) && \ | ||
| apt-get update && \ | ||
| apt-get -y --no-install-recommends install "${vmod_deps[@]/varnish-dev/varnish-dev=$varnish_version}" && \ | ||
| install-vmod https://github.com/varnish/varnish-modules/releases/download/0.28.0/varnish-modules-0.28.0.tar.gz && \ | ||
| rm -rf /var/lib/apt/lists/* | ||
|
|
||
| USER varnish |
This file contains hidden or 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,44 @@ | ||
| Varnish VCL tests | ||
| ================= | ||
|
|
||
| `varnishtest` cases for `resources/platformsh/common/4.6/.platform/varnish.vcl` and `varnish9.vcl`, | ||
| the VCLs shipped to Platform.sh / Ibexa Cloud installations. | ||
|
|
||
| They load the real VCL file into a real Varnish and assert on the request the **backend** receives, | ||
| which is where the reverse proxy header filtering in `vcl_recv` can be observed. | ||
|
|
||
| Cases | ||
| ----- | ||
|
|
||
| | Case | Scenario | Asserts | | ||
| |---|---|---| | ||
| | `no-cdn.vtc` | request straight through the Ibexa Cloud router | the client supplied part of `X-Forwarded-For` is dropped and the header is rebuilt from `X-Client-IP`; `X-Forwarded-Host` / `-Prefix` / `Forwarded` are stripped; `X-Forwarded-Proto` is kept | | ||
| | `via-cdn.vtc` | request arriving through a supported CDN | `X-Forwarded-For`, `X-Client-IP` and `Client-Cdn` are kept as the router left them, so Fastly detection still works | | ||
| | `no-client-ip.vtc` | no `X-Client-IP`, so the request did not come through the router | `X-Forwarded-For` is dropped entirely rather than trusted | | ||
|
|
||
| The header behaviour being relied on is documented at | ||
| <https://fixed.docs.upsun.com/development/headers.html>. | ||
|
|
||
| Running them | ||
| ------------ | ||
|
|
||
| ```bash | ||
| docker build -t ibexa-varnishtest:6.0 tests/varnish | ||
| IMAGE=ibexa-varnishtest:6.0 tests/varnish/run.sh | ||
|
|
||
| docker build -t ibexa-varnishtest:9 -f tests/varnish/Dockerfile-9 tests/varnish | ||
| IMAGE=ibexa-varnishtest:9 VCL=resources/platformsh/common/4.6/.platform/varnish9.vcl tests/varnish/run.sh | ||
| ``` | ||
|
|
||
| Notes | ||
| ----- | ||
|
|
||
| - The `varnish.vcl` image has to be **Varnish 6.0LTS specifically**. This VCL returns `miss` from | ||
| `vcl_hit`, which 6.5 and later reject, and the xkey vmod it imports is not packaged for 6.0LTS — | ||
| hence the source build in `Dockerfile`, mirroring `doc/docker/Dockerfile-varnish` in `ibexa/docker`. | ||
| - The VCL cannot be loaded as it stands: Platform.sh supplies the VCL version declaration, the `std` | ||
| import and the `app.backend()` director. `run.sh` prepends the first two and swaps the director for | ||
| a stub, so the shipped file itself is what gets tested. | ||
| - The backend listens on a fixed port (9081) so the stub can point at it. | ||
| - Each case uses its own URL and its own Varnish instance: `X-Forwarded-*` is not part of the cache | ||
| key, so cases sharing a cache would be served each other's responses. |
This file contains hidden or 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,38 @@ | ||
| varnishtest "Without a CDN, X-Forwarded-For is rebuilt from the router supplied X-Client-IP" | ||
|
|
||
| # The Ibexa Cloud router only *appends* the real client IP to whatever the client sent in | ||
| # X-Forwarded-For, so every leading entry is client controlled and must not survive. | ||
| # See https://fixed.docs.upsun.com/development/headers.html | ||
|
|
||
| server s1 -listen "127.0.0.1:9081" { | ||
| rxreq | ||
|
|
||
| # Client supplied prefix dropped: only the router's own value is left. | ||
| expect req.http.X-Forwarded-For == "203.0.113.9" | ||
|
|
||
| # Not filtered by the router, so the VCL strips them. | ||
| expect req.http.X-Forwarded-Host == <undef> | ||
| expect req.http.X-Forwarded-Prefix == <undef> | ||
| expect req.http.Forwarded == <undef> | ||
|
|
||
| # Router authoritative, kept as is. | ||
| expect req.http.X-Client-IP == "203.0.113.9" | ||
| expect req.http.X-Forwarded-Proto == "https" | ||
| expect req.http.X-Forwarded-Port == "443" | ||
|
|
||
| txresp | ||
| } -start | ||
|
|
||
| varnish v1 -arg "-f /etc/varnish/default.vcl" -start | ||
|
|
||
| client c1 { | ||
| txreq -url "/no-cdn" \ | ||
| -hdr "X-Forwarded-For: 6.6.6.6, 203.0.113.9" \ | ||
| -hdr "X-Client-IP: 203.0.113.9" \ | ||
| -hdr "X-Forwarded-Proto: https" \ | ||
| -hdr "X-Forwarded-Host: evil.example" \ | ||
| -hdr "X-Forwarded-Prefix: /admin" \ | ||
| -hdr "Forwarded: for=6.6.6.6;host=evil.example;proto=https" | ||
| rxresp | ||
| expect resp.status == 200 | ||
| } -run |
This file contains hidden or 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,29 @@ | ||
| varnishtest "Without X-Client-IP there is nothing trustworthy, so X-Forwarded-For is dropped" | ||
|
|
||
| # X-Client-IP is always set by the Ibexa Cloud router. If it is absent the request did not come | ||
| # through the router, so the client supplied X-Forwarded-For must not be handed to the application. | ||
|
|
||
| server s1 -listen "127.0.0.1:9081" { | ||
| rxreq | ||
|
|
||
| expect req.http.X-Forwarded-For == <undef> | ||
| expect req.http.X-Client-IP == <undef> | ||
|
|
||
| expect req.http.X-Forwarded-Host == <undef> | ||
| expect req.http.X-Forwarded-Prefix == <undef> | ||
| expect req.http.Forwarded == <undef> | ||
|
|
||
| txresp | ||
| } -start | ||
|
|
||
| varnish v1 -arg "-f /etc/varnish/default.vcl" -start | ||
|
|
||
| client c1 { | ||
| txreq -url "/no-client-ip" \ | ||
| -hdr "X-Forwarded-For: 6.6.6.6" \ | ||
| -hdr "X-Forwarded-Host: evil.example" \ | ||
| -hdr "X-Forwarded-Prefix: /admin" \ | ||
| -hdr "Forwarded: for=6.6.6.6;host=evil.example" | ||
| rxresp | ||
| expect resp.status == 200 | ||
| } -run |
This file contains hidden or 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,45 @@ | ||
| #!/bin/bash | ||
| # | ||
| # Runs the varnishtest suite for the Platform.sh / Ibexa Cloud VCL. | ||
| # | ||
| # That VCL is not loadable as it stands: Platform.sh supplies the VCL version declaration, the std | ||
| # import and the app.backend() director. This script prepends the first two and swaps the director | ||
| # for a stub pointing at the test backend, then loads the resulting file into a real Varnish, so | ||
| # what is tested is the shipped file rather than a copy of it. | ||
| # | ||
| # Usage: | ||
| # docker build -t ibexa-varnishtest:7 tests/varnish | ||
| # IMAGE=ibexa-varnishtest:7 tests/varnish/run.sh | ||
| # | ||
| # docker build -t ibexa-varnishtest:9 -f tests/varnish/Dockerfile-9 tests/varnish | ||
| # IMAGE=ibexa-varnishtest:9 VCL=resources/platformsh/common/4.6/.platform/varnish9.vcl tests/varnish/run.sh | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| TESTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| ROOTDIR="$(cd "$TESTDIR/../.." && pwd)" | ||
| VCL="${VCL:-$ROOTDIR/resources/platformsh/common/4.6/.platform/varnish.vcl}" | ||
| IMAGE="${IMAGE:-ibexa-varnishtest:7}" | ||
|
|
||
| WORKDIR="$(mktemp -d)" | ||
| trap 'rm -rf "$WORKDIR"' EXIT | ||
|
|
||
| { | ||
| echo "// Prologue supplied by Platform.sh at runtime, added here so the file can be loaded." | ||
| echo "vcl 4.1;" | ||
| echo "import std;" | ||
| echo "backend stub { .host = \"127.0.0.1\"; .port = \"9081\"; }" | ||
| sed 's/app\.backend()/stub/' "$VCL" | ||
| } > "$WORKDIR/default.vcl" | ||
|
|
||
| echo "==> $(basename "$(dirname "$(dirname "$VCL")")")/$(basename "$VCL") (${IMAGE})" | ||
|
|
||
| for vtc in "$TESTDIR"/*.vtc; do | ||
| echo "--> $(basename "$vtc")" | ||
| docker run --rm \ | ||
| -v "$WORKDIR/default.vcl:/etc/varnish/default.vcl:ro" \ | ||
| -v "$vtc:/case.vtc:ro" \ | ||
| --entrypoint varnishtest "$IMAGE" /case.vtc | ||
| done | ||
|
|
||
| echo "All varnishtest cases passed." |
This file contains hidden or 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,36 @@ | ||
| varnishtest "Behind a supported CDN, the router supplied end client IP and Client-Cdn are kept" | ||
|
|
||
| # When the request arrives via a CDN the router replaces X-Forwarded-For with the end client IP and | ||
| # sets Client-Cdn, discarding whatever the client sent for either. Ibexa DXP uses Client-Cdn to | ||
| # detect Fastly, so it has to reach the application. | ||
| # See https://fixed.docs.upsun.com/development/headers.html | ||
|
|
||
| server s1 -listen "127.0.0.1:9081" { | ||
| rxreq | ||
|
|
||
| expect req.http.X-Forwarded-For == "198.51.100.7" | ||
| expect req.http.X-Client-IP == "198.51.100.7" | ||
| expect req.http.Client-Cdn == "fastly" | ||
|
|
||
| expect req.http.X-Forwarded-Host == <undef> | ||
| expect req.http.X-Forwarded-Prefix == <undef> | ||
| expect req.http.Forwarded == <undef> | ||
|
|
||
| expect req.http.X-Forwarded-Proto == "https" | ||
| expect req.http.X-Forwarded-Port == "443" | ||
|
|
||
| txresp | ||
| } -start | ||
|
|
||
| varnish v1 -arg "-f /etc/varnish/default.vcl" -start | ||
|
|
||
| client c1 { | ||
| txreq -url "/via-cdn" \ | ||
| -hdr "X-Forwarded-For: 198.51.100.7" \ | ||
| -hdr "X-Client-IP: 198.51.100.7" \ | ||
| -hdr "Client-Cdn: fastly" \ | ||
| -hdr "X-Forwarded-Proto: https" \ | ||
| -hdr "X-Forwarded-Host: evil.example" | ||
| rxresp | ||
| expect resp.status == 200 | ||
| } -run |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arent we missing VCL: resources/platformsh/common/4.6/.platform/varnish9.vcl here as well? I know, there is default fallback to it, but it will be easier to fix on merge up?