Skip to content

Commit

Permalink
fix(subdomain-gw): no API endpoint on subdomain root
Browse files Browse the repository at this point in the history
This removes API endpoint from subdomains that represent arbitrary
content roots, eg. $cid.ipfs.localhost/api/v0.

Main reason: $cid could have a subdirectory or a file named "api" which
would be eclipsed and not reachable due to /api/v0 being mounted on top of
it.

API is still available at the root hostname localhost/api/v0

We also added test to ensure similar eclipse does not happen if someone
sets up DNSLink-only gateway and tries to load /ipfs/* paths.

#6096 (comment)

License: MIT
Signed-off-by: Marcin Rataj <[email protected]>
  • Loading branch information
lidel committed Mar 17, 2020
1 parent 4637ab8 commit dd8c804
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 13 deletions.
1 change: 1 addition & 0 deletions core/corehttp/hostname.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ func HostnameOption() ServeOption {

// Rewrite the path to not use subdomains
r.URL.Path = pathPrefix + r.URL.Path

// Serve path request
childMux.ServeHTTP(w, r)
return
Expand Down
86 changes: 73 additions & 13 deletions test/sharness/t0114-gateway-subdomains.sh
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ test_expect_success "Add the test directory" '
mkdir -p testdirlisting/subdir1/subdir2 &&
echo "hello" > testdirlisting/hello &&
echo "subdir2-bar" > testdirlisting/subdir1/subdir2/bar &&
mkdir -p testdirlisting/api &&
mkdir -p testdirlisting/ipfs &&
echo "I am a txt file" > testdirlisting/api/file.txt &&
echo "I am a txt file" > testdirlisting/ipfs/file.txt &&
DIR_CID=$(ipfs add -Qr --cid-version 1 testdirlisting)
'

Expand Down Expand Up @@ -187,17 +191,22 @@ test_localhost_gateway_response_should_contain \

# API on localhost subdomain gateway

# on root hostname
# /api/v0 present on the root hostname
test_localhost_gateway_response_should_contain \
"request for localhost/api" \
"http://localhost:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \
"Ref"

# on content root subdomain (avoid CORS issues)
# /api/v0 not mounted on content root subdomains
test_localhost_gateway_response_should_contain \
"request for {cid}.localhost/api" \
"http://${DIR_CID}.localhost:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \
"Ref"
"request for {cid}.ipfs.localhost/api returns data if present on the content root" \
"http://${DIR_CID}.ipfs.localhost:$GWAY_PORT/api/file.txt" \
"I am a txt file"

test_localhost_gateway_response_should_contain \
"request for {cid}.ipfs.localhost/api/v0/refs returns 404" \
"http://${DIR_CID}.ipfs.localhost:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \
"404 Not Found"

## ============================================================================
## Test subdomain-based requests to a local gateway with default config
Expand All @@ -211,11 +220,18 @@ test_localhost_gateway_response_should_contain \
"http://${CIDv1}.ipfs.localhost:$GWAY_PORT" \
"$CID_VAL"

# ensure /ipfs/ namespace is not mounted on subdomain
test_localhost_gateway_response_should_contain \
"request for {CID}.ipfs.localhost/ipfs/{CID} should return HTTP 404" \
"http://${CIDv1}.ipfs.localhost:$GWAY_PORT/ipfs/$CIDv1" \
"404 Not Found"

# ensure requests to /ipfs/* are not blocked, if content root has such subdirectory
test_localhost_gateway_response_should_contain \
"request for {CID}.ipfs.localhost/ipfs/file.txt should return data from a file in CID content root" \
"http://${DIR_CID}.ipfs.localhost:$GWAY_PORT/ipfs/file.txt" \
"I am a txt file"

# {CID}.ipfs.localhost/sub/dir (Directory Listing)
DIR_HOSTNAME="${DIR_CID}.ipfs.localhost:$GWAY_PORT"

Expand Down Expand Up @@ -390,22 +406,52 @@ test_hostname_gateway_response_should_contain \
"Location: http://${IPNS_IDv1}.ipns.example.com/"

# API on subdomain gateway example.com
# (should be available on both gateway root and every content root subdomain)
# ============================================================================

# an gateway root
# present at the root domain
test_hostname_gateway_response_should_contain \
"request for example.com/api/v0/refs returns expected payload when /api is on Paths whitelist" \
"example.com" \
"http://127.0.0.1:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \
"Ref"

# on content root (avoids CORS issues)
# not mounted on content root subdomains
test_hostname_gateway_response_should_contain \
"request for {cid}.ipfs.example.com/api returns data if present on the content root" \
"$DIR_CID.ipfs.example.com" \
"http://127.0.0.1:$GWAY_PORT/api/file.txt" \
"I am a txt file"

test_hostname_gateway_response_should_contain \
"request for {cid}.example.com/api/v0/refs returns expected payload when /api is on Paths whitelist" \
"$CIDv1.example.com" \
"request for {cid}.ipfs.example.com/api/v0/refs returns 404" \
"$CIDv1.ipfs.example.com" \
"http://127.0.0.1:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \
"Ref"
"404 Not Found"

# disable /api on example.com
ipfs config --json Gateway.PublicGateways '{
"example.com": {
"UseSubdomains": true,
"Paths": ["/ipfs", "/ipns"]
}
}' || exit 1
# restart daemon to apply config changes
test_kill_ipfs_daemon
test_launch_ipfs_daemon --offline

# not mounted at the root domain
test_hostname_gateway_response_should_contain \
"request for example.com/api/v0/refs returns 404 if /api not on Paths whitelist" \
"example.com" \
"http://127.0.0.1:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \
"404 Not Found"

# not mounted on content root subdomains
test_hostname_gateway_response_should_contain \
"request for {cid}.ipfs.example.com/api returns data if present on the content root" \
"$DIR_CID.ipfs.example.com" \
"http://127.0.0.1:$GWAY_PORT/api/file.txt" \
"I am a txt file"

# DNSLink: <dnslink-fqdn>.ipns.example.com
# (not really useful outside of localhost, as setting TLS for more than one
Expand Down Expand Up @@ -503,6 +549,11 @@ ipfs config --json Gateway.PublicGateways '{
"UseSubdomains": false,
"Paths": ["/ipfs"]
},
"only-dnslink-enabled-on-fqdn.example.org": {
"NoDNSLink": false,
"UseSubdomains": false,
"Paths": []
},
"dnslink-disabled-on-fqdn.example.com": {
"NoDNSLink": true,
"UseSubdomains": false,
Expand All @@ -512,8 +563,9 @@ ipfs config --json Gateway.PublicGateways '{

# DNSLink test requires a daemon in online mode with precached /ipns/ mapping
DNSLINK_FQDN="dnslink-enabled-on-fqdn.example.org"
ONLY_DNSLINK_FQDN="only-dnslink-enabled-on-fqdn.example.org"
NO_DNSLINK_FQDN="dnslink-disabled-on-fqdn.example.com"
export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1"
export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1,$ONLY_DNSLINK_FQDN:/ipfs/$DIR_CID"

# restart daemon to apply config changes
test_launch_ipfs_daemon
Expand All @@ -535,11 +587,19 @@ test_hostname_gateway_response_should_contain \
"$CID_VAL"

test_hostname_gateway_response_should_contain \
"request for {dnslink-fqdn}/ipfs/{cid} returns expected payload when path is whitelisted" \
"request for {dnslink-fqdn}/ipfs/{cid} returns expected payload when /ipfs is on Paths whitelist" \
"$DNSLINK_FQDN" \
"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1" \
"$CID_VAL"

# Test for a fun edge case: DNSLink-only gateway without /ipfs/ namespace
# mounted, and with subdirectory named "ipfs" ¯\_(ツ)_/¯
test_hostname_gateway_response_should_contain \
"request for {dnslink-fqdn}/ipfs/file.txt returns data from content root when /ipfs in not on Paths whitelist" \
"$ONLY_DNSLINK_FQDN" \
"http://127.0.0.1:$GWAY_PORT/ipfs/file.txt" \
"I am a txt file"

test_hostname_gateway_response_should_contain \
"request for {dnslink-fqdn}/ipns/{peerid} returns 404 when path is not whitelisted" \
"$DNSLINK_FQDN" \
Expand Down

0 comments on commit dd8c804

Please sign in to comment.