@cryptotaxi247 / kubo / commits / 87dfc46e0

feat: support X-Forwarded-Host when doing gateway redirect

Michael Muré committed Jul 1, 2020 at 12:52 UTC 87dfc46e037c06e7580dcb645f967c736a3830af
2 files changed +46 -9
core/corehttp/hostname.go
+16 -9
@@ -81,8 +81,15 @@ func HostnameOption() ServeOption {
81 // and the paths that they serve "gateway" content on.
82 // That way, we can use DNSLink for everything else.
83
84 + // Support X-Forwarded-Host if added by a reverse proxy
85 + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
86 + host := r.Host
87 + if xHost := r.Header.Get("X-Forwarded-Host"); xHost != "" {
88 + host = xHost
89 + }
90 +
91 // HTTP Host & Path check: is this one of our "known gateways"?
85 - if gw, ok := isKnownHostname(r.Host, knownGateways); ok {
92 + if gw, ok := isKnownHostname(host, knownGateways); ok {
93 // This is a known gateway but request is not using
94 // the subdomain feature.
95
@@ -94,7 +101,7 @@ func HostnameOption() ServeOption {
101 if gw.UseSubdomains {
102 // Yes, redirect if applicable
103 // Example: dweb.link/ipfs/{cid} → {cid}.ipfs.dweb.link
97 - if newURL, ok := toSubdomainURL(r.Host, r.URL.Path, r); ok {
104 + if newURL, ok := toSubdomainURL(host, r.URL.Path, r); ok {
105 // Just to be sure single Origin can't be abused in
106 // web browsers that ignored the redirect for some
107 // reason, Clear-Site-Data header clears browsing
@@ -124,9 +131,9 @@ func HostnameOption() ServeOption {
131 // Not a whitelisted path
132
133 // Try DNSLink, if it was not explicitly disabled for the hostname
127 - if !gw.NoDNSLink && isDNSLinkRequest(r.Context(), coreApi, r) {
134 + if !gw.NoDNSLink && isDNSLinkRequest(r.Context(), coreApi, host) {
135 // rewrite path and handle as DNSLink
129 - r.URL.Path = "/ipns/" + stripPort(r.Host) + r.URL.Path
136 + r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path
137 childMux.ServeHTTP(w, r)
138 return
139 }
@@ -138,7 +145,7 @@ func HostnameOption() ServeOption {
145
146 // HTTP Host check: is this one of our subdomain-based "known gateways"?
147 // Example: {cid}.ipfs.localhost, {cid}.ipfs.dweb.link
141 - if gw, hostname, ns, rootID, ok := knownSubdomainDetails(r.Host, knownGateways); ok {
148 + if gw, hostname, ns, rootID, ok := knownSubdomainDetails(host, knownGateways); ok {
149 // Looks like we're using known subdomain gateway.
150
151 // Assemble original path prefix.
@@ -176,9 +183,9 @@ func HostnameOption() ServeOption {
183 // 1. is wildcard DNSLink enabled (Gateway.NoDNSLink=false)?
184 // 2. does Host header include a fully qualified domain name (FQDN)?
185 // 3. does DNSLink record exist in DNS?
179 - if !cfg.Gateway.NoDNSLink && isDNSLinkRequest(r.Context(), coreApi, r) {
186 + if !cfg.Gateway.NoDNSLink && isDNSLinkRequest(r.Context(), coreApi, host) {
187 // rewrite path and handle as DNSLink
181 - r.URL.Path = "/ipns/" + stripPort(r.Host) + r.URL.Path
188 + r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path
189 childMux.ServeHTTP(w, r)
190 return
191 }
@@ -236,8 +243,8 @@ func knownSubdomainDetails(hostname string, knownGateways map[string]config.Gate
243
244 // isDNSLinkRequest returns bool that indicates if request
245 // should return data from content path listed in DNSLink record (if exists)
239 -func isDNSLinkRequest(ctx context.Context, ipfs iface.CoreAPI, r *http.Request) bool {
240 - fqdn := stripPort(r.Host)
246 +func isDNSLinkRequest(ctx context.Context, ipfs iface.CoreAPI, host string) bool {
247 + fqdn := stripPort(host)
248 if len(fqdn) == 0 && !isd.IsDomain(fqdn) {
249 return false
250 }
test/sharness/t0114-gateway-subdomains.sh
+30
@@ -664,6 +664,36 @@ test_hostname_gateway_response_should_contain \
664 "http://127.0.0.1:$GWAY_PORT/" \
665 "$CID_VAL"
666
667 +## ============================================================================
668 +## Test support for X-Forwarded-Host
669 +## ============================================================================
670 +
671 +# set explicit subdomain gateway config for the hostname
672 +ipfs config --json Gateway.PublicGateways '{
673 + "example.com": {
674 + "UseSubdomains": true,
675 + "Paths": ["/ipfs", "/ipns", "/api"]
676 + }
677 +}' || exit 1
678 +# restart daemon to apply config changes
679 +test_kill_ipfs_daemon
680 +test_launch_ipfs_daemon --offline
681 +
682 +test_expect_success "request for http://fake.domain.com/ipfs/{CID} doesn't match the example.com gateway" "
683 + curl -H \"Host: fake.domain.com\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1\" > response &&
684 + test_should_contain \"200 OK\" response
685 +"
686 +
687 +test_expect_success "request for http://fake.domain.com/ipfs/{CID} with X-Forwarded-Host: example.com match the example.com gateway" "
688 + curl -H \"Host: fake.domain.com\" -H \"X-Forwarded-Host: example.com\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1\" > response &&
689 + test_should_contain \"Location: http://$CIDv1.ipfs.example.com/\" response
690 +"
691 +
692 +test_expect_success "request for http://fake.domain.com/ipfs/{CID} with X-Forwarded-Host: example.com and X-Forwarded-Proto: https match the example.com gateway, redirect with https" "
693 + curl -H \"Host: fake.domain.com\" -H \"X-Forwarded-Host: example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1\" > response &&
694 + test_should_contain \"Location: https://$CIDv1.ipfs.example.com/\" response
695 +"
696 +
697 # =============================================================================
698 # ensure we end with empty Gateway.PublicGateways
699 ipfs config --json Gateway.PublicGateways '{}'