Skip to content

nginx: obscure internal rewrite URLs #991

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
wants to merge 34 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e97c01a
nginx: add test for proxy_redirect config
Apr 28, 2025
3584a30
fix test title
Apr 28, 2025
7db2e75
add temporary logging
Apr 28, 2025
79fc597
Merge branch 'next' into test-redirect-rewrites
alxndrsn Apr 28, 2025
faa7d1f
add missing v1
Apr 28, 2025
93035e0
assert backend received
Apr 28, 2025
fb6148b
use https
Apr 28, 2025
b1e13e7
parseInt()
Apr 28, 2025
91e6d03
log request
Apr 28, 2025
7bdb075
assert correct request path
Apr 28, 2025
df9f4a6
remove proxy_redirect rule
Apr 28, 2025
66cabba
remove correct proxy_redirect rule
Apr 28, 2025
f46b0d7
try another test pass
Apr 28, 2025
a1a396b
add tests for enketo
Apr 28, 2025
2992fd0
fix enekto tesdt
Apr 28, 2025
ebb9c9b
remove rule
Apr 28, 2025
8c9260b
wip: remove rewrite rule to see if test than passes
Apr 28, 2025
5e50f67
test more paths
Apr 28, 2025
e523f29
tabulate tests
Apr 28, 2025
57bf820
add dodgy tests which _should_ be failing
Apr 28, 2025
0a22c36
upate tests; remove rewrite to see what it affects
Apr 28, 2025
aaa3e64
remove unexpected test example
Apr 28, 2025
4060945
add explicit default
Apr 28, 2025
2fea713
update test name
Apr 28, 2025
74d68be
revert to existing
Apr 28, 2025
8a46440
try fix
Apr 28, 2025
c6a1db6
update tests
Apr 28, 2025
e0703c6
assert correct location for requests
Apr 28, 2025
fe63051
fix tests
Apr 28, 2025
456de9c
revert proxy_redirect changes
Apr 28, 2025
239ca47
Revert "revert proxy_redirect changes"
Apr 28, 2025
7e89ee8
Merge branch 'next' into test-redirect-rewrites
alxndrsn Jun 9, 2025
bd0673b
remove withStdLogging()
Jun 9, 2025
5147fa8
remove assertCommonHeaders() calls
Jun 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions files/nginx/odk.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ server {
location ~ ^/(?:-|enketo-passthrough)(?:/|$) {
rewrite ^/enketo-passthrough(/.*)?$ /-$1 break;
proxy_pass http://enketo:8005;
proxy_redirect off;
proxy_set_header Host $host;
proxy_hide_header Vary;
proxy_hide_header Cache-Control;
Expand All @@ -137,7 +136,6 @@ server {
location ~ ^/v\d {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://service:8383;
proxy_redirect off;

# buffer requests, but not responses, so streaming out works.
proxy_request_buffering on;
Expand Down
10 changes: 10 additions & 0 deletions test/nginx/mock-http-server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ app.get('/v1/projects', (_, res) => {
res.send('OK');
});

const redirectGenerator = (req, res) => {
requests.push({ method:req.method, path:req.originalUrl });
const { status } = req.params;
const { location } = req.query;
log('/generate-redirect', { params:req.params, query:req.query });
res.redirect(Number.parseInt(status, 10), location);
};
app.get('/v1/generate-redirect/:status', redirectGenerator);
app.get('/-/generate-redirect/:status', redirectGenerator);

[
'delete',
'get',
Expand Down
124 changes: 124 additions & 0 deletions test/nginx/test-nginx.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,130 @@ describe('nginx config', () => {
socket.on('error', reject);
}));

describe('proxy_redirect directive', () => {
[
'/some/path',
'/-/some/path',
'/v1/some/path',
'https://example.org/some/path',
'https://example.org/-/some/path',
'https://example.org/v1/some/path',
'https://subdomain.example.org:1234/some/path',
'https://subdomain.example.org:1234/-/some/path',
'https://subdomain.example.org:1234/v1/some/path',

'http://service:8383', // not subject to /v1 proxy rules, so not rewritten
'http://service:8005', // not subject to /- proxy rules, so not rewritten

// these tests should actually fail, but they may not be currently...
'http://enketo:8005/-',
'http://enketo:8005/-/',
'http://enketo:8005/-/some/path',
'http://enketo:8005/enketo-passthrough',
'http://enketo:8005/enketo-passthrough/',
'http://enketo:8005/enketo-passthrough/some/path',
].forEach(redirectLocation => {
it(`should not rewrite central-backend redirect to ${redirectLocation}`, async () => {
// given
const requestPath = `/v1/generate-redirect/301?location=${encodeURIComponent(redirectLocation)}`;

// when
const res = await fetchHttps(requestPath);

// then
assert.equal(res.status, 301);
assert.equal(res.headers.get('Location'), redirectLocation);
// and
await assertBackendReceived(
{ method:'GET', path:requestPath },
);
});
});

[
'/some/path',
'/-/some/path',
'/v1/some/path',
'https://example.org/some/path',
'https://example.org/-/some/path',
'https://example.org/v1/some/path',
'https://subdomain.example.org:1234/some/path',
'https://subdomain.example.org:1234/-/some/path',
'https://subdomain.example.org:1234/v1/some/path',

'http://service:8383', // not subject to /v1 proxy rules, so not rewritten
'http://service:8383/', // not subject to /v1 proxy rules, so not rewritten
'http://service:8383/some/path', // not subject to /v1 proxy rules, so not rewritten
'http://service:8005', // not subject to /- proxy rules, so not rewritten
'http://service:8005/', // not subject to /- proxy rules, so not rewritten
'http://service:8005/some/path', // not subject to /- proxy rules, so not rewritten
].forEach(redirectLocation => {
it(`should not rewrite enketo redirect to ${redirectLocation}`, async () => {
// given
const requestPath = `/-/generate-redirect/301?location=${encodeURIComponent(redirectLocation)}`;

// when
const res = await fetchHttps(requestPath);

// then
assert.equal(res.status, 301);
assert.equal(res.headers.get('Location'), redirectLocation);
// and
await assertEnketoReceived(
{ method:'GET', path:requestPath },
);
});
});

[
[ 'http://service:8383/v1', 'https://odk-nginx.example.test/v1' ],
[ 'http://service:8383/v1/', 'https://odk-nginx.example.test/v1/' ],
[ 'http://service:8383/v1/some/path', 'https://odk-nginx.example.test/v1/some/path' ],
].forEach(([ original, expected ]) => {
it(`should rewrite central-backend redirect from ${original} to ${expected}`, async () => {
// given
const requestPath = `/v1/generate-redirect/301?location=${encodeURIComponent(original)}`;

// when
const res = await fetchHttps(requestPath);

// then
assert.equal(res.status, 301);
assert.equal(res.headers.get('Location'), expected);
// and
await assertBackendReceived(
{ method:'GET', path:requestPath },
);
});
});

[
[ 'http://enketo:8005/', 'https://odk-nginx.example.test/' ],
[ 'http://enketo:8005/-', 'https://odk-nginx.example.test/-' ],
[ 'http://enketo:8005/-/', 'https://odk-nginx.example.test/-/' ],
[ 'http://enketo:8005/-/some/path', 'https://odk-nginx.example.test/-/some/path' ],
[ 'http://enketo:8005/enketo-passthrough', 'https://odk-nginx.example.test/enketo-passthrough' ],
[ 'http://enketo:8005/enketo-passthrough/', 'https://odk-nginx.example.test/enketo-passthrough/' ],
[ 'http://enketo:8005/enketo-passthrough/some/path', 'https://odk-nginx.example.test/enketo-passthrough/some/path' ],
].forEach(([ original, expected ]) => {
it(`should rewrite enketo redirect from ${original} to ${expected}`, async () => {
// given
const requestPath = `/-/generate-redirect/301?location=${encodeURIComponent(original)}`;

// when
const res = await fetchHttps(requestPath);

// then
assert.equal(res.status, 301);
assert.equal(res.headers.get('Location'), expected);
// and
await assertEnketoReceived(
{ method:'GET', path:requestPath },
);
});
});
});

describe('general caching', () => {
[
// general
Expand Down