From 91c1edd68b9ba3aeda989b667520b5af8831ed1e Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 18 May 2023 17:20:17 +0200 Subject: [PATCH 01/15] feat: move t0114 to use gateway helper --- tests/t0114_gateway_subdomains_test.go | 683 +++++++++++-------------- 1 file changed, 288 insertions(+), 395 deletions(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index c44e4d2d2..ef711d8b5 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -1,15 +1,14 @@ package tests import ( - "fmt" "net/url" "testing" "github.com/ipfs/gateway-conformance/tooling/car" . "github.com/ipfs/gateway-conformance/tooling/check" + "github.com/ipfs/gateway-conformance/tooling/helpers" "github.com/ipfs/gateway-conformance/tooling/specs" . "github.com/ipfs/gateway-conformance/tooling/test" - "github.com/ipfs/gateway-conformance/tooling/tmpl" ) func TestGatewaySubdomains(t *testing.T) { @@ -25,16 +24,6 @@ func TestGatewaySubdomains(t *testing.T) { tests := SugarTests{} - // sugar: readable way to add more tests - with := func(moreTests SugarTests) { - tests = append(tests, moreTests...) - } - - // sugar: nicer looking sprintf call - URL := func(path string, args ...interface{}) string { - return tmpl.Fmt(path, args...) - } - // We're going to run the same test against multiple gateways (localhost, and a subdomain gateway) gatewayURLs := []string{ SubdomainGatewayURL, @@ -47,397 +36,301 @@ func TestGatewaySubdomains(t *testing.T) { t.Fatal(err) } - with(testGatewayWithManyProtocols(t, - "request for example.com/ipfs/{CIDv1} redirects to subdomain", - ` - subdomains should not return payload directly, - but redirect to URL with proper origin isolation - `, - URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv1), - Expect(). - Status(301). - Headers( - Header("Location"). - Hint("request for example.com/ipfs/{CIDv1} returns Location HTTP header for subdomain redirect in browsers"). - Contains("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), - ), - )) - - with(testGatewayWithManyProtocols(t, - "request for example.com/ipfs/{DirCID} redirects to subdomain", - ` - subdomains should not return payload directly, - but redirect to URL with proper origin isolation - `, - URL("{{url}}/ipfs/{{cid}}/", gatewayURL, DirCID), - Expect(). - Status(301). - Headers( - Header("Location"). - Hint("request for example.com/ipfs/{DirCID} returns Location HTTP header for subdomain redirect in browsers"). - Contains("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, DirCID, u.Host), - ), - )) - - with(testGatewayWithManyProtocols(t, - "request for example.com/ipfs/{CIDv0} redirects to CIDv1 representation in subdomain", - "", - URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv0), - Expect(). - Status(301). - Headers( - Header("Location"). - Hint("request for example.com/ipfs/{CIDv0to1} returns Location HTTP header for subdomain redirect in browsers"). - Contains("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv0to1, u.Host), - ), - )) - - // TODO: ipns - // TODO: dns link test - - // ============================================================================ - // Test subdomain-based requests to a local gateway with default config - // (origin per content root at http://*.example.com) - // ============================================================================ - - with(testGatewayWithManyProtocols(t, - "request for {CID}.ipfs.example.com should return expected payload", - "", - URL("{{scheme}}://{{cid}}.ipfs.{{host}}", u.Scheme, CIDv1, u.Host), - Expect(). - Status(200). - Body(Contains(CIDVal)), - )) - - with(testGatewayWithManyProtocols(t, - "request for {CID}.ipfs.example.com/ipfs/{CID} should return HTTP 404", - "ensure /ipfs/ namespace is not mounted on subdomain", - URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/{{cid}}", u.Scheme, CIDv1, u.Host), - Expect(). - Status(404), - )) - - with(testGatewayWithManyProtocols(t, - "request for {CID}.ipfs.example.com/ipfs/file.txt should return data from a file in CID content root", - "ensure requests to /ipfs/* are not blocked, if content root has such subdirectory", - URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/file.txt", u.Scheme, DirCID, u.Host), - Expect(). - Status(200). - Body(Contains("I am a txt file")), - )) - - with(testGatewayWithManyProtocols(t, - "valid file and subdirectory paths in directory listing at {cid}.ipfs.example.com", - "{CID}.ipfs.example.com/sub/dir (Directory Listing)", - URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, DirCID, u.Host), - Expect(). - Status(200). - Body(And( - // TODO: implement html expectations - Contains(`hello`), - Contains(`ipfs`), - )), - )) - - with(testGatewayWithManyProtocols(t, - "valid parent directory path in directory listing at {cid}.ipfs.example.com/sub/dir", - "", - URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), - Expect(). - Status(200). - Body(And( - // TODO: implement html expectations - Contains(`..`), - Contains(`bar`), - )), - )) - - with(testGatewayWithManyProtocols(t, - "request for deep path resource at {cid}.ipfs.localhost/sub/dir/file", - "", - URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/bar", u.Scheme, DirCID, u.Host), - Expect(). - Status(200). - Body(Contains("text-file-content")), - )) - - with(testGatewayWithManyProtocols(t, - "valid breadcrumb links in the header of directory listing at {cid}.ipfs.example.com/sub/dir", - ` + tests = append(tests, SugarTests{ + { + Name: "request for example.com/ipfs/{CIDv1} redirects to subdomain", + Hint: ` + subdomains should not return payload directly, + but redirect to URL with proper origin isolation + `, + Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv1), + Response: Expect(). + Status(301). + Headers( + Header("Location"). + Hint("request for example.com/ipfs/{CIDv1} returns Location HTTP header for subdomain redirect in browsers"). + Contains("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), + ). + BodyWithHint(` + We return body with HTTP 301 so existing cli scripts that use path-based + gateway do not break (curl doesn't auto-redirect without passing -L; wget + does not span across hostnames by default) + Context: https://github.com/ipfs/go-ipfs/issues/6975 + `, + IsEqual("hello\n"), + ), + }, + { + Name: "request for example.com/ipfs/{DirCID} redirects to subdomain", + Hint: ` + subdomains should not return payload directly, + but redirect to URL with proper origin isolation + `, + Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, DirCID), + Response: Expect(). + Status(301). + Headers( + Header("Location"). + Hint("request for example.com/ipfs/{DirCID} returns Location HTTP header for subdomain redirect in browsers"). + Contains("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, DirCID, u.Host), + ), + }, + { + Name: "request for example.com/ipfs/{CIDv0} redirects to CIDv1 representation in subdomain", + Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv0), + Response: Expect(). + Status(301). + Headers( + Header("Location"). + Hint("request for example.com/ipfs/{CIDv0to1} returns Location HTTP header for subdomain redirect in browsers"). + Contains("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv0to1, u.Host), + ), + }, + // ============================================================================ + // Test subdomain-based requests to a local gateway with default config + // (origin per content root at http://*.example.com) + // ============================================================================ + { + Name: "request for {CID}.ipfs.example.com should return expected payload", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}", u.Scheme, CIDv1, u.Host), + Response: Expect(). + Status(200). + Body(Contains(CIDVal)), + }, + { + Name: "request for {CID}.ipfs.example.com/ipfs/{CID} should return HTTP 404", + Hint: "ensure /ipfs/ namespace is not mounted on subdomain", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/{{cid}}", u.Scheme, CIDv1, u.Host), + Response: Expect(). + Status(404), + }, + { + Name: "request for {CID}.ipfs.example.com/ipfs/file.txt should return data from a file in CID content root", + Hint: "ensure requests to /ipfs/* are not blocked, if content root has such subdirectory", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/file.txt", u.Scheme, DirCID, u.Host), + Response: Expect(). + Status(200). + Body(Contains("I am a txt file")), + }, + { + Name: "valid file and subdirectory paths in directory listing at {cid}.ipfs.example.com", + Hint: "{CID}.ipfs.example.com/sub/dir (Directory Listing)", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, DirCID, u.Host), + Response: Expect(). + Status(200). + Body(And( + // TODO: implement html expectations + Contains(`hello`), + Contains(`ipfs`), + )), + }, + { + Name: "valid parent directory path in directory listing at {cid}.ipfs.example.com/sub/dir", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), + Response: Expect(). + Status(200). + Body(And( + // TODO: implement html expectations + Contains(`..`), + Contains(`bar`), + )), + }, + { + Name: "request for deep path resource at {cid}.ipfs.localhost/sub/dir/file", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/bar", u.Scheme, DirCID, u.Host), + Response: Expect(). + Status(200). + Body(Contains("text-file-content")), + }, + { + Name: "valid breadcrumb links in the header of directory listing at {cid}.ipfs.example.com/sub/dir", + Hint: ` Note 1: we test for sneaky subdir names {cid}.ipfs.example.com/ipfs/ipns/ :^) Note 2: example.com/ipfs/.. present in HTML will be redirected to subdomain, so this is expected behavior `, - URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), - Expect(). - Status(200). - Body( - And( - Contains("Index of"), - Contains(`/ipfs/{{cid}}/ipfs/ipns`, - u.Host, DirCID), + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), + Response: Expect(). + Status(200). + Body( + And( + Contains("Index of"), + Contains(`/ipfs/{{cid}}/ipfs/ipns`, + u.Host, DirCID), + ), ), - ), - )) - - // TODO: # *.ipns.localhost - // TODO: # .ipns.localhost - // TODO: # .ipns.localhost - - // ## ============================================================================ - // ## Test DNSLink inlining on HTTP gateways - // ## ============================================================================ - - // TODO - - // ## ============================================================================ - // ## Test subdomain-based requests with a custom hostname config - // ## (origin per content root at http://*.example.com) - // ## ============================================================================ - - // # example.com/ip(f|n)s/* - // # ============================================================================= - - // # path requests to the root hostname should redirect - // # to a subdomain URL with proper origin isolation - - with(testGatewayWithManyProtocols(t, - "request for example.com/ipfs/{CIDv1} produces redirect to {CIDv1}.ipfs.example.com", - "path requests to the root hostname should redirect to a subdomain URL with proper origin isolation", - URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1), - Expect(). - Headers( - Header("Location").Equals("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), - ), - )) - - with(testGatewayWithManyProtocols(t, - "request for example.com/ipfs/{InvalidCID} produces useful error before redirect", - "error message should include original CID (and it should be case-sensitive, as we can't assume everyone uses base32)", - URL("{{scheme}}://{{host}}/ipfs/QmInvalidCID", u.Scheme, u.Host), - Expect(). - Body(Contains(`invalid path "/ipfs/QmInvalidCID"`)), - )) - - with(testGatewayWithManyProtocols(t, - "request for example.com/ipfs/{CIDv0} produces redirect to {CIDv1}.ipfs.example.com", - "", - URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv0), - Expect(). - Status(301). - Headers( - Header("Location").Equals("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv0to1, u.Host), - ), - )) - - with(testGatewayWithManyProtocols(t, - "request for http://example.com/ipfs/{CID} with X-Forwarded-Proto: https produces redirect to HTTPS URL", - "Support X-Forwarded-Proto", - Request(). - URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1). - Header("X-Forwarded-Proto", "https"), - Expect(). - Status(301). - Headers( - Header("Location").Equals("https://{{cid}}.ipfs.{{host}}/", CIDv1, u.Host), - ), - )) - - with(testGatewayWithManyProtocols(t, - "request for example.com/ipfs/?uri=ipfs%3A%2F%2F.. produces redirect to /ipfs/.. content path", - "Support ipfs:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler", - Request(). - URL("{{scheme}}://{{host}}/ipfs/", u.Scheme, u.Host). - Query( - "uri", "ipfs://{{host}}/wiki/Diego_Maradona.html", CIDWikipedia, - ), - Expect(). - Status(301). - Headers( - Header("Location").Equals("/ipfs/{{cid}}/wiki/Diego_Maradona.html", CIDWikipedia), - ), - )) - - // # example.com/ipns/ - // TODO - - // # example.com/ipns/ - // TODO - - // # DNSLink on Public gateway with a single-level wildcard TLS cert - // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 - // TODO - - // # Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler - // TODO - - // # *.ipns.example.com - // # ============================================================================ - - // # .ipns.example.com - - // # API on subdomain gateway example.com - // # ============================================================================ - - // # DNSLink: .ipns.example.com - // # (not really useful outside of localhost, as setting TLS for more than one - // # level of wildcard is a pain, but we support it if someone really wants it) - // # ============================================================================ - // TODO - - // # DNSLink on Public gateway with a single-level wildcard TLS cert - // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 - - // ## Test subdomain handling of CIDs that do not fit in a single DNS Label (>63chars) - // ## https://github.com/ipfs/go-ipfs/issues/7318 - // ## ============================================================================ - // TODO - - with(testGatewayWithManyProtocols(t, - "request for a too long CID at localhost/ipfs/{CIDv1} returns human readable error", - "router should not redirect to hostnames that could fail due to DNS limits", - URL("{{url}}/ipfs/{{cid}}", gatewayURL, CIDv1_TOO_LONG), - Expect(). - Status(400). - Body(Contains("CID incompatible with DNS label length limit of 63")), - )) - - with(testGatewayWithManyProtocols(t, - "request for a too long CID at {CIDv1}.ipfs.localhost returns expected payload", - "direct request should also fail (provides the same UX as router and avoids confusion)", - URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1_TOO_LONG, u.Host), - Expect(). - Status(400). - Body(Contains("CID incompatible with DNS label length limit of 63")), - )) - - // # public subdomain gateway: *.example.com - // TODO: IPNS - - // # Disable selected Paths for the subdomain gateway hostname - // # ============================================================================= - - // # disable /ipns for the hostname by not whitelisting it - - // # refuse requests to Paths that were not explicitly whitelisted for the hostname - - // MANY TODOs here - - // ## ============================================================================ - // ## Test support for X-Forwarded-Host - // ## ============================================================================ - - with(testGatewayWithManyProtocols(t, - "request for http://fake.domain.com/ipfs/{CID} doesn't match the example.com gateway", - "", - URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1), - Expect(). - Status(200), - )) - - with(testGatewayWithManyProtocols(t, - "request for http://fake.domain.com/ipfs/{CID} with X-Forwarded-Host: example.com match the example.com gateway", - "", - Request(). - URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). - Header("X-Forwarded-Host", u.Host), - Expect(). - Status(301). - Headers( - Header("Location").Equals("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), - ), - )) - - with(testGatewayWithManyProtocols(t, - "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", - "", - Request(). - URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). - Header("X-Forwarded-Host", u.Host). - Header("X-Forwarded-Proto", "https"), - Expect(). - Status(301). - Headers( - Header("Location").Equals("https://{{cid}}.ipfs.{{host}}/", CIDv1, u.Host), - ), - )) - } + }, - if specs.SubdomainGateway.IsEnabled() { - Run(t, tests) - } else { - t.Skip("subdomain gateway disabled") - } -} + // TODO: # *.ipns.localhost + // TODO: # .ipns.localhost + // TODO: # .ipns.localhost -func testGatewayWithManyProtocols(t *testing.T, label string, hint string, reqURL interface{}, expected ExpectBuilder) SugarTests { - t.Helper() + // ## ============================================================================ + // ## Test DNSLink inlining on HTTP gateways + // ## ============================================================================ - baseURL := "" - baseReq := Request() + // TODO - switch req := reqURL.(type) { - case string: - baseURL = reqURL.(string) - case RequestBuilder: - baseReq = req - baseURL = req.GetURL() - default: - t.Fatalf("invalid type for reqURL: %T", reqURL) - } + // ## ============================================================================ + // ## Test subdomain-based requests with a custom hostname config + // ## (origin per content root at http://*.example.com) + // ## ============================================================================ - u, err := url.Parse(baseURL) - if err != nil { - t.Fatal(err) - } - // Because you might be testing an IPFS node in CI, or on your local machine, the test are designed - // to test the subdomain behavior (querying http://{CID}.my-subdomain-gateway.io/) even if the node is - // actually living on http://127.0.0.1:8080 or somewhere else. - // - // The test knows two addresses: - // - GatewayURL: the URL we connect to, it might be "dweb.link", "127.0.0.1:8080", etc. - // - SubdomainGatewayURL: the URL we test for subdomain requests, it might be "dweb.link", "localhost", "example.com", etc. - - // host is the hostname of the gateway we are testing, it might be `localhost` or `example.com` - host := u.Host - - // raw url is the url but we replace the host with our local url, it might be `http://127.0.0.1/ipfs/something` - u.Host = GatewayHost - rawURL := u.String() - - return SugarTests{ - { - Name: fmt.Sprintf("%s (direct HTTP)", label), - Hint: fmt.Sprintf("%s\n%s", hint, "direct HTTP request (hostname in URL, raw IP in Host header)"), - Request: baseReq. - URL(rawURL). - Headers( - Header("Host", host), - ), - Response: expected, - }, - { - Name: fmt.Sprintf("%s (HTTP proxy)", label), - Hint: fmt.Sprintf("%s\n%s", hint, "HTTP proxy (hostname is passed via URL)"), - Request: baseReq. - URL(baseURL). - Proxy(GatewayURL), - Response: expected, - }, - { - Name: fmt.Sprintf("%s (HTTP proxy tunneling via CONNECT)", label), - Hint: fmt.Sprintf("%s\n%s", hint, `HTTP proxy - In HTTP/1.x, the pseudo-method CONNECT, - can be used to convert an HTTP connection into a tunnel to a remote host - https://tools.ietf.org/html/rfc7231#section-4.3.6 - `), - Request: baseReq. - URL(baseURL). - Proxy(GatewayURL). - WithProxyTunnel(). - Headers( - Header("Host", host), - ), - Response: expected, - }, + // # example.com/ip(f|n)s/* + // # ============================================================================= + + // # path requests to the root hostname should redirect + // # to a subdomain URL with proper origin isolation + + { + Name: "request for example.com/ipfs/{CIDv1} produces redirect to {CIDv1}.ipfs.example.com", + Hint: "path requests to the root hostname should redirect to a subdomain URL with proper origin isolation", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1), + Response: Expect(). + Headers( + Header("Location").Equals("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), + ), + }, + + { + Name: "request for example.com/ipfs/{InvalidCID} produces useful error before redirect", + Hint: "error message should include original CID (and it should be case-sensitive, as we can't assume everyone uses base32)", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/QmInvalidCID", u.Scheme, u.Host), + Response: Expect(). + Body(Contains(`invalid path "/ipfs/QmInvalidCID"`)), + }, + + { + Name: "request for example.com/ipfs/{CIDv0} produces redirect to {CIDv1}.ipfs.example.com", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv0), + Response: Expect(). + Status(301). + Headers( + Header("Location").Equals("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv0to1, u.Host), + ), + }, + + { + Name: "request for http://example.com/ipfs/{CID} with X-Forwarded-Proto: https produces redirect to HTTPS URL", + Hint: "Support X-Forwarded-Proto", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1). + Header("X-Forwarded-Proto", "https"), + Response: Expect(). + Status(301). + Headers( + Header("Location").Equals("https://{{cid}}.ipfs.{{host}}/", CIDv1, u.Host), + ), + }, + + { + Name: "request for example.com/ipfs/?uri=ipfs%3A%2F%2F.. produces redirect to /ipfs/.. content path", + Hint: "Support ipfs:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/", u.Scheme, u.Host). + Query( + "uri", "ipfs://{{host}}/wiki/Diego_Maradona.html", CIDWikipedia, + ), + Response: Expect(). + Status(301). + Headers( + Header("Location").Equals("/ipfs/{{cid}}/wiki/Diego_Maradona.html", CIDWikipedia), + ), + }, + // # example.com/ipns/ + // TODO + + // # example.com/ipns/ + // TODO + + // # DNSLink on Public gateway with a single-level wildcard TLS cert + // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 + // TODO + + // # Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler + // TODO + + // # *.ipns.example.com + // # ============================================================================ + + // # .ipns.example.com + + // # API on subdomain gateway example.com + // # ============================================================================ + + // # DNSLink: .ipns.example.com + // # (not really useful outside of localhost, as setting TLS for more than one + // # level of wildcard is a pain, but we support it if someone really wants it) + // # ============================================================================ + // TODO + + // # DNSLink on Public gateway with a single-level wildcard TLS cert + // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 + + // ## Test subdomain handling of CIDs that do not fit in a single DNS Label (>63chars) + // ## https://github.com/ipfs/go-ipfs/issues/7318 + // ## ============================================================================ + // TODO + + { + Name: "request for a too long CID at localhost/ipfs/{CIDv1} returns human readable error", + Hint: "router should not redirect to hostnames that could fail due to DNS limits", + Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}", gatewayURL, CIDv1_TOO_LONG), + Response: Expect(). + Status(400). + Body(Contains("CID incompatible with DNS label length limit of 63")), + }, + { + Name: "request for a too long CID at {CIDv1}.ipfs.localhost returns expected payload", + Hint: "direct request should also fail (provides the same UX as router and avoids confusion)", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1_TOO_LONG, u.Host), + Response: Expect(). + Status(400). + Body(Contains("CID incompatible with DNS label length limit of 63")), + }, + + // # public subdomain gateway: *.example.com + // TODO: IPNS + + // # Disable selected Paths for the subdomain gateway hostname + // # ============================================================================= + + // # disable /ipns for the hostname by not whitelisting it + + // # refuse requests to Paths that were not explicitly whitelisted for the hostname + + // MANY TODOs here + + // ## ============================================================================ + // ## Test support for X-Forwarded-Host + // ## ============================================================================ + + { + Name: "request for http://fake.domain.com/ipfs/{CID} doesn't match the example.com gateway", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1), + Response: Expect(). + Status(200), + }, + { + Name: "request for http://fake.domain.com/ipfs/{CID} with X-Forwarded-Host: example.com match the example.com gateway", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). + Header("X-Forwarded-Host", u.Host), + Response: Expect(). + Status(301). + Headers( + Header("Location").Equals("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), + ), + }, + { + Name: "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", + Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). + Header("X-Forwarded-Host", u.Host). + Header("X-Forwarded-Proto", "https"), + Response: Expect(). + Status(301). + Headers( + Header("Location").Equals("https://{{cid}}.ipfs.{{host}}/", CIDv1, u.Host), + ), + }, + }...) } + + RunIfSpecsAreEnabled(t, helpers.UnwrapSubdomainTests(t, tests), specs.SubdomainGateway) } From 17c5eb527444c733c07ac97217f723e2175ce341 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 18 May 2023 17:42:37 +0200 Subject: [PATCH 02/15] t0114: add the ipns and dnslink templates --- tests/t0114_gateway_subdomains_test.go | 449 +++++++++++++++++++++---- 1 file changed, 389 insertions(+), 60 deletions(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index ef711d8b5..1bec50ed6 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -159,17 +159,6 @@ func TestGatewaySubdomains(t *testing.T) { ), ), }, - - // TODO: # *.ipns.localhost - // TODO: # .ipns.localhost - // TODO: # .ipns.localhost - - // ## ============================================================================ - // ## Test DNSLink inlining on HTTP gateways - // ## ============================================================================ - - // TODO - // ## ============================================================================ // ## Test subdomain-based requests with a custom hostname config // ## (origin per content root at http://*.example.com) @@ -234,41 +223,6 @@ func TestGatewaySubdomains(t *testing.T) { Header("Location").Equals("/ipfs/{{cid}}/wiki/Diego_Maradona.html", CIDWikipedia), ), }, - // # example.com/ipns/ - // TODO - - // # example.com/ipns/ - // TODO - - // # DNSLink on Public gateway with a single-level wildcard TLS cert - // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 - // TODO - - // # Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler - // TODO - - // # *.ipns.example.com - // # ============================================================================ - - // # .ipns.example.com - - // # API on subdomain gateway example.com - // # ============================================================================ - - // # DNSLink: .ipns.example.com - // # (not really useful outside of localhost, as setting TLS for more than one - // # level of wildcard is a pain, but we support it if someone really wants it) - // # ============================================================================ - // TODO - - // # DNSLink on Public gateway with a single-level wildcard TLS cert - // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 - - // ## Test subdomain handling of CIDs that do not fit in a single DNS Label (>63chars) - // ## https://github.com/ipfs/go-ipfs/issues/7318 - // ## ============================================================================ - // TODO - { Name: "request for a too long CID at localhost/ipfs/{CIDv1} returns human readable error", Hint: "router should not redirect to hostnames that could fail due to DNS limits", @@ -285,23 +239,9 @@ func TestGatewaySubdomains(t *testing.T) { Status(400). Body(Contains("CID incompatible with DNS label length limit of 63")), }, - - // # public subdomain gateway: *.example.com - // TODO: IPNS - - // # Disable selected Paths for the subdomain gateway hostname - // # ============================================================================= - - // # disable /ipns for the hostname by not whitelisting it - - // # refuse requests to Paths that were not explicitly whitelisted for the hostname - - // MANY TODOs here - // ## ============================================================================ // ## Test support for X-Forwarded-Host // ## ============================================================================ - { Name: "request for http://fake.domain.com/ipfs/{CID} doesn't match the example.com gateway", Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1), @@ -334,3 +274,392 @@ func TestGatewaySubdomains(t *testing.T) { RunIfSpecsAreEnabled(t, helpers.UnwrapSubdomainTests(t, tests), specs.SubdomainGateway) } + +func TestGatewaySubdomainAndIPNS(t *testing.T) { + tests := SugarTests{} + + // We're going to run the same test against multiple gateways (localhost, and a subdomain gateway) + gatewayURLs := []string{ + SubdomainGatewayURL, + SubdomainLocalhostGatewayURL, + } + + for _, gatewayURL := range gatewayURLs { + _, err := url.Parse(gatewayURL) + if err != nil { + t.Fatal(err) + } + + tests = append(tests, SugarTests{ + // # /ipns/ + + // test_localhost_gateway_response_should_contain \ + // "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "http://localhost:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + + // test_localhost_gateway_response_should_contain \ + // "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "http://localhost:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + + // # *.ipns.localhost + + // # .ipns.localhost + + // test_localhost_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ + // "http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ + // "$CID_VAL" + + // test_localhost_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ + // "http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ + // "$CID_VAL" + + // test_localhost_gateway_response_should_contain \ + // "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "http://${RSA_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + + // test_localhost_gateway_response_should_contain \ + // "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "http://${ED25519_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + + // # example.com/ipns/ + + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" + + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" + + // # *.ipns.example.com + // # ============================================================================ + + // # .ipns.example.com + + // test_hostname_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ + // "${RSA_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "$CID_VAL" + + // test_hostname_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ + // "${ED25519_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "$CID_VAL" + + // test_hostname_gateway_response_should_contain \ + // "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "${RSA_IPNS_IDv1_DAGPB}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" + + // test_hostname_gateway_response_should_contain \ + // "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "${ED25519_IPNS_IDv1_DAGPB}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" + + // ## Test subdomain handling of CIDs that do not fit in a single DNS Label (>63chars) + // ## https://github.com/ipfs/go-ipfs/issues/7318 + // ## ============================================================================ + + // # local: *.localhost + // test_localhost_gateway_response_should_contain \ + // "request for a ED25519 libp2p-key at localhost/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers" \ + // "http://localhost:$GWAY_PORT/ipns/$IPNS_ED25519_B58MH" \ + // "Location: http://${IPNS_ED25519_B36CID}.ipns.localhost:$GWAY_PORT/" + + // # public subdomain gateway: *.example.com + + // test_hostname_gateway_response_should_contain \ + // "request for a ED25519 libp2p-key at example.com/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$IPNS_ED25519_B58MH" \ + // "Location: http://${IPNS_ED25519_B36CID}.ipns.example.com" + + // # disable /ipns for the hostname by not whitelisting it + // ipfs config --json Gateway.PublicGateways '{ + // "example.com": { + // "UseSubdomains": true, + // "Paths": ["/ipfs"] + // } + // }' || exit 1 + // # restart daemon to apply config changes + // test_kill_ipfs_daemon + // test_launch_ipfs_daemon_without_network + + // # refuse requests to Paths that were not explicitly whitelisted for the hostname + // test_hostname_gateway_response_should_contain \ + // "request for *.ipns.example.com returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "${RSA_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "404 Not Found" + + // test_hostname_gateway_response_should_contain \ + // "request for *.ipns.example.com returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "${ED25519_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "404 Not Found" + + // # refuse requests to Paths that were not explicitly whitelisted for the hostname + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/ returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv1" \ + // "404 Not Found" + + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/ returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv1" \ + // "404 Not Found" + }...) + } + + RunIfSpecsAreEnabled(t, helpers.UnwrapSubdomainTests(t, tests), specs.SubdomainGateway, specs.IPNSResolver) +} + +func TestGatewaySubdomainAndDnsLink(t *testing.T) { + tests := SugarTests{} + + // We're going to run the same test against multiple gateways (localhost, and a subdomain gateway) + gatewayURLs := []string{ + SubdomainGatewayURL, + SubdomainLocalhostGatewayURL, + } + + for _, gatewayURL := range gatewayURLs { + _, err := url.Parse(gatewayURL) + if err != nil { + t.Fatal(err) + } + + tests = append(tests, SugarTests{ + // # /ipns/ + + // test_localhost_gateway_response_should_contain \ + // "request for localhost/ipns/{fqdn} redirects to DNSLink in subdomain" \ + // "http://localhost:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + // "Location: http://en.wikipedia-on-ipfs.org.ipns.localhost:$GWAY_PORT/wiki" + + // # .ipns.localhost + + // # DNSLink test requires a daemon in online mode with precached /ipns/ mapping + // test_kill_ipfs_daemon + // DNSLINK_FQDN="dnslink-test.example.com" + // export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1" + // test_launch_ipfs_daemon + + // test_localhost_gateway_response_should_contain \ + // "request for {dnslink}.ipns.localhost returns expected payload" \ + // "http://$DNSLINK_FQDN.ipns.localhost:$GWAY_PORT" \ + // "$CID_VAL" + + // ## ============================================================================ + // ## Test DNSLink inlining on HTTP gateways + // ## ============================================================================ + + // # set explicit subdomain gateway config for the hostname + // ipfs config --json Gateway.PublicGateways '{ + // "localhost": { + // "UseSubdomains": true, + // "InlineDNSLink": true, + // "Paths": ["/ipfs", "/ipns", "/api"] + // }, + // "example.com": { + // "UseSubdomains": true, + // "InlineDNSLink": true, + // "Paths": ["/ipfs", "/ipns", "/api"] + // } + // }' || exit 1 + // # restart daemon to apply config changes + // test_kill_ipfs_daemon + // test_launch_ipfs_daemon_without_network + + // test_localhost_gateway_response_should_contain \ + // "request for localhost/ipns/{fqdn} redirects to DNSLink in subdomain with DNS inlining" \ + // "http://localhost:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + // "Location: http://en-wikipedia--on--ipfs-org.ipns.localhost:$GWAY_PORT/wiki" + + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/{fqdn} redirects to DNSLink in subdomain with DNS inlining" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + // "Location: http://en-wikipedia--on--ipfs-org.ipns.example.com/wiki" + + // # example.com/ipns/ + + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/{fqdn} redirects to DNSLink in subdomain" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + // "Location: http://en.wikipedia-on-ipfs.org.ipns.example.com/wiki" + + // # DNSLink on Public gateway with a single-level wildcard TLS cert + // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 + // test_expect_success \ + // "request for example.com/ipns/{fqdn} with X-Forwarded-Proto redirects to TLS-safe label in subdomain" " + // curl -H \"Host: example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki\" > response && + // test_should_contain \"Location: https://en-wikipedia--on--ipfs-org.ipns.example.com/wiki\" response + // " + + // # Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/?uri=ipns%3A%2F%2F.. produces redirect to /ipns/.. content path" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/?uri=ipns%3A%2F%2Fen.wikipedia-on-ipfs.org" \ + // "Location: /ipns/en.wikipedia-on-ipfs.org" + + // # DNSLink: .ipns.example.com + // # (not really useful outside of localhost, as setting TLS for more than one + // # level of wildcard is a pain, but we support it if someone really wants it) + // # ============================================================================ + + // # DNSLink test requires a daemon in online mode with precached /ipns/ mapping + // test_kill_ipfs_daemon + // DNSLINK_FQDN="dnslink-subdomain-gw-test.example.org" + // export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1" + // test_launch_ipfs_daemon + + // test_hostname_gateway_response_should_contain \ + // "request for {dnslink}.ipns.example.com returns expected payload" \ + // "$DNSLINK_FQDN.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "$CID_VAL" + + // # DNSLink on Public gateway with a single-level wildcard TLS cert + // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 + // test_expect_success \ + // "request for {single-label-dnslink}.ipns.example.com with X-Forwarded-Proto returns expected payload" " + // curl -H \"Host: dnslink--subdomain--gw--test-example-org.ipns.example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT\" > response && + // test_should_contain \"$CID_VAL\" response + // " + + // ## ============================================================================ + // ## Test DNSLink requests with a custom PublicGateway (hostname config) + // ## (DNSLink site at http://dnslink-test.example.com) + // ## ============================================================================ + // # disable wildcard DNSLink gateway + // # and enable it on specific NSLink hostname + // ipfs config --json Gateway.NoDNSLink true && \ + // ipfs config --json Gateway.PublicGateways '{ + // "dnslink-enabled-on-fqdn.example.org": { + // "NoDNSLink": false, + // "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, + // "Paths": [] + // } + // }' || exit 1 + + // # 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,$ONLY_DNSLINK_FQDN:/ipfs/$DIR_CID" + + // # DNSLink enabled + + // test_hostname_gateway_response_should_contain \ + // "request for http://{dnslink-fqdn}/ PublicGateway returns expected payload" \ + // "$DNSLINK_FQDN" \ + // "http://127.0.0.1:$GWAY_PORT/" \ + // "$CID_VAL" + + // test_hostname_gateway_response_should_contain \ + // "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" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ + // "404 Not Found" + + // test_hostname_gateway_response_should_contain \ + // "request for {dnslink-fqdn}/ipns/{peerid} returns 404 when path is not whitelisted" \ + // "$DNSLINK_FQDN" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ + // "404 Not Found" + + // # DNSLink disabled + + // test_hostname_gateway_response_should_contain \ + // "request for http://{dnslink-fqdn}/ returns 404 when NoDNSLink=true" \ + // "$NO_DNSLINK_FQDN" \ + // "http://127.0.0.1:$GWAY_PORT/" \ + // "404 Not Found" + + // test_hostname_gateway_response_should_contain \ + // "request for {dnslink-fqdn}/ipfs/{cid} returns 404 when path is not whitelisted" \ + // "$NO_DNSLINK_FQDN" \ + // "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv0" \ + // "404 Not Found" + + // ## ============================================================================ + // ## Test wildcard DNSLink (any hostname, with default config) + // ## ============================================================================ + + // test_kill_ipfs_daemon + + // # enable wildcard DNSLink gateway (any value in Host header) + // # and remove custom PublicGateways + // ipfs config --json Gateway.NoDNSLink false && \ + // ipfs config --json Gateway.PublicGateways '{}' || exit 1 + + // # DNSLink test requires a daemon in online mode with precached /ipns/ mapping + // DNSLINK_FQDN="wildcard-dnslink-not-in-config.example.com" + // export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1" + + // # restart daemon to apply config changes + // test_launch_ipfs_daemon + + // # make sure test setup is valid (fail if CoreAPI is unable to resolve) + // test_expect_success "spoofed DNSLink record resolves in cli" " + // ipfs resolve /ipns/$DNSLINK_FQDN > result && + // test_should_contain \"$CIDv1\" result && + // ipfs cat /ipns/$DNSLINK_FQDN > result && + // test_should_contain \"$CID_VAL\" result + // " + + // # gateway test + // + // test_hostname_gateway_response_should_contain \ + // "request for http://{dnslink-fqdn}/ (wildcard) returns expected payload" \ + // "$DNSLINK_FQDN" \ + // "http://127.0.0.1:$GWAY_PORT/" \ + // "$CID_VAL" + }...) + } + + RunIfSpecsAreEnabled(t, helpers.UnwrapSubdomainTests(t, tests), specs.SubdomainGateway, specs.DNSLinkResolver) +} From 0ce5eda7e990d1f6e99e908fa59629896d5079e9 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 23 May 2023 17:28:39 +0200 Subject: [PATCH 03/15] t0114: implement the ipns fixtures + related tests --- ...XSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record | Bin 0 -> 394 bytes ...8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record | Bin 0 -> 1082 bytes fixtures/t0114/README.md | 111 +++++++ fixtures/t0114/fixtures.car | Bin 0 -> 1518 bytes tests/t0114_gateway_subdomains_test.go | 290 ++++++++++-------- ...XSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record | Bin 0 -> 394 bytes ...8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record | Bin 0 -> 1082 bytes tooling/ipns/ipns_test.go | 29 ++ tooling/ipns/record.go | 54 +++- 9 files changed, 360 insertions(+), 124 deletions(-) create mode 100644 fixtures/t0114/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record create mode 100644 fixtures/t0114/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record create mode 100644 fixtures/t0114/README.md create mode 100644 fixtures/t0114/fixtures.car create mode 100644 tooling/ipns/_fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record create mode 100644 tooling/ipns/_fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record diff --git a/fixtures/t0114/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record b/fixtures/t0114/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..39b2f41a40866132588557e1be7beb97f50b79d4 GIT binary patch literal 394 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D9<^J}Yul1#e?Les=qsP9*TpHppd@W%Xk@HwV4`bi6k=#> zWnyAwYN=;oU}$b+5v9Rk(9qDcxZ%Nrr`k>qKjaU!b6-4rYi;`fm-}a{vCo?xRu}4g z^l|FihwZAz%U!lw{rY|+(EGO7p=OmC>knNP>?`)U^*Whrs^fvF0qEJ$Sl0Mn+K ATmS$7 literal 0 HcmV?d00001 diff --git a/fixtures/t0114/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record b/fixtures/t0114/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..b37d9b75b50ce3baa472e0c790c63cc4476c8631 GIT binary patch literal 1082 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D92(*IMXR5Go)>GsSynodq(GRGFw`JL;^nAdmmZe7@< z?;9u0)~oe;B6n8l#B-O4jHLznwHFfawsGeD^WdI5dD6CW_D`?(IR6u4yS}n>0Xs|C zV|Deu10TJ;+15H9y#1%w)-Pu|{~C5NEh+2ohgE%4GcFi^=!o1|c=mvclzM~Aq?sH1 zV;9|>pJ(C4b;k1K_pY`7N=jcyC2D2fRy9#Bx;b;nX--YmkL>f5|4X=(dm6Aa@2=jn zPF^r?@jIi2gJJgHoU|F^9$YA16dbkmCI8(bi=coUk?re*CGPIOm;0ZgQ?hD>F0X=C zL#OarR>@Lloz*!)FR?K>|cBO@yVa}y&! z15licsfm%1;pVJGT^wtTb=KA$^<13Nv3!=^X2&xt9)0ASbyDGP?WdlXx7q)`d@Z+h z!6|`%_KMwXk1o8Mo?{-MVA*x}^cm9=t?8#{Bqsazy{TE_Y}T4V{h&^m3)r^)bGv-5U2WMvuSX z7vtkGPzgn;O5JiMe~&<38&ZhUC?flR8gS@+~<#_0!o~ z@AZ8}8?WuOn%~@gsOVv8C3nV1oCd(eK6PTncvj@)S}Wr`}S-X)$F5@;ne3PMT?z z=NIb*B<2n+zLRAc6lAY~eeG%iDJwNZBx`P>1GC& uWCn)RutZ?qi=aH?W(21e0yA-PDnka)$jp??lFA5t$v6k5Dx|U?l>q?Oq1V#@ literal 0 HcmV?d00001 diff --git a/fixtures/t0114/README.md b/fixtures/t0114/README.md new file mode 100644 index 000000000..005a7f0c5 --- /dev/null +++ b/fixtures/t0114/README.md @@ -0,0 +1,111 @@ +# Dataset description/sources + +- fixtures.car + - raw CARv1 + +- QmUKd....ipns-record + - ipns record, encoded with protocol buffer + +- 12D3K....ipns-record + - ipns record, encoded with protocol buffer + +Generated with: + +```sh +# using ipfs version 0.21.0-dev (03a98280e3e642774776cd3d0435ab53e5dfa867) + +# CIDv0to1 is necessary because raw-leaves are enabled by default during +# "ipfs add" with CIDv1 and disabled with CIDv0 +CID_VAL="hello" +CIDv1=$(echo $CID_VAL | ipfs add --cid-version 1 -Q) +CIDv0=$(echo $CID_VAL | ipfs add --cid-version 0 -Q) +CIDv0to1=$(echo "$CIDv0" | ipfs cid base32) +# sha512 will be over 63char limit, even when represented in Base36 +CIDv1_TOO_LONG=$(echo $CID_VAL | ipfs add --cid-version 1 --hash sha2-512 -Q) + +echo CID_VAL=${CID_VAL} +echo CIDv1=${CIDv1} +echo CIDv0=${CIDv0} +echo CIDv0to1=${CIDv0to1} +echo CIDv1_TOO_LONG=${CIDv1_TOO_LONG} + +# Directory tree crafted to test for edge cases like "/ipfs/ipfs/ipns/bar" +mkdir -p testdirlisting/ipfs/ipns && +echo "hello" > testdirlisting/hello && +echo "text-file-content" > testdirlisting/ipfs/ipns/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) + +echo DIR_CID=${DIR_CID} # ./testdirlisting + +ipfs files mkdir /t0114/ +ipfs files cp /ipfs/${CIDv1} /t0114/ +ipfs files cp /ipfs/${CIDv0} /t0114/ +ipfs files cp /ipfs/${CIDv0to1} /t0114/ +ipfs files cp /ipfs/${DIR_CID} /t0114/ +ipfs files cp /ipfs/${CIDv1_TOO_LONG} /t0114/ + +ROOT=`ipfs files stat /t0114/ --hash` + +ipfs dag export ${ROOT} > ./fixtures.car + +# Then the keys + +KEY_NAME=test_key_rsa_$RANDOM +RSA_KEY=$(ipfs key gen --ipns-base=b58mh --type=rsa --size=2048 ${KEY_NAME} | head -n1 | tr -d "\n") +RSA_IPNS_IDv0=$(echo "$RSA_KEY" | ipfs cid format -v 0) +RSA_IPNS_IDv1=$(echo "$RSA_KEY" | ipfs cid format -v 1 --mc libp2p-key -b base36) +RSA_IPNS_IDv1_DAGPB=$(echo "$RSA_IPNS_IDv0" | ipfs cid format -v 1 -b base36) + +# publish a record valid for a 100 years +ipfs name publish --key ${KEY_NAME} --allow-offline -Q --ttl=876600h --lifetime=876600h "/ipfs/$CIDv1" +ipfs routing get /ipns/${RSA_KEY} > ${RSA_KEY}.ipns-record + +echo RSA_KEY=${RSA_KEY} +echo RSA_IPNS_IDv0=${RSA_IPNS_IDv0} +echo RSA_IPNS_IDv1=${RSA_IPNS_IDv1} +echo RSA_IPNS_IDv1_DAGPB=${RSA_IPNS_IDv1_DAGPB} + +KEY_NAME=test_key_ed25519_$RANDOM +ED25519_KEY=$(ipfs key gen --ipns-base=b58mh --type=ed25519 ${KEY_NAME} | head -n1 | tr -d "\n") +ED25519_IPNS_IDv0=$ED25519_KEY +ED25519_IPNS_IDv1=$(ipfs key list -l --ipns-base=base36 | grep ${KEY_NAME} | cut -d " " -f1 | tr -d "\n") +ED25519_IPNS_IDv1_DAGPB=$(echo "$ED25519_IPNS_IDv1" | ipfs cid format -v 1 -b base36 --mc dag-pb) + +# ed25519 fits under 63 char limit when represented in base36 +IPNS_ED25519_B58MH=$(ipfs key list -l --ipns-base b58mh | grep $KEY_NAME | cut -d" " -f1 | tr -d "\n") +IPNS_ED25519_B36CID=$(ipfs key list -l --ipns-base base36 | grep $KEY_NAME | cut -d" " -f1 | tr -d "\n") + +# publish a record valid for a 100 years +ipfs name publish --key ${KEY_NAME} --allow-offline -Q --ttl=876600h --lifetime=876600h "/ipfs/$CIDv1" +ipfs routing get /ipns/${ED25519_KEY} > ${ED25519_KEY}.ipns-record + +echo ED25519_KEY=${ED25519_KEY} +echo ED25519_IPNS_IDv0=${ED25519_IPNS_IDv0} +echo ED25519_IPNS_IDv1=${ED25519_IPNS_IDv1} +echo ED25519_IPNS_IDv1_DAGPB=${ED25519_IPNS_IDv1_DAGPB} +echo IPNS_ED25519_B58MH=${IPNS_ED25519_B58MH} +echo IPNS_ED25519_B36CID=${IPNS_ED25519_B36CID} + +# CID_VAL=hello +# CIDv1=bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am +# CIDv0=QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN +# CIDv0to1=bafybeiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4 +# CIDv1_TOO_LONG=bafkrgqhhyivzstcz3hhswshfjgy6ertgmnqeleynhwt4dlfsthi4hn7zgh4uvlsb5xncykzapi3ocd4lzogukir6ksdy6wzrnz6ohnv4aglcs +# DIR_CID=bafybeiht6dtwk3les7vqm6ibpvz6qpohidvlshsfyr7l5mpysdw2vmbbhe # ./testdirlisting + +# RSA_KEY=QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3 +# RSA_IPNS_IDv0=QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3 +# RSA_IPNS_IDv1=k2k4r8m7xvggw5pxxk3abrkwyer625hg01hfyggrai7lk1m63fuihi7w +# RSA_IPNS_IDv1_DAGPB=k2jmtxu61bnhrtj301lw7zizknztocdbeqhxgv76l2q9t36fn9jbzipo + +# ED25519_KEY=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +# ED25519_IPNS_IDv0=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +# ED25519_IPNS_IDv1=k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam +# ED25519_IPNS_IDv1_DAGPB=k50rm9yjlt0jey4fqg6wafvqprktgbkpgkqdg27tpqje6iimzxewnhvtin9hhq +# IPNS_ED25519_B58MH=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +# IPNS_ED25519_B36CID=k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam +``` diff --git a/fixtures/t0114/fixtures.car b/fixtures/t0114/fixtures.car new file mode 100644 index 0000000000000000000000000000000000000000..bc7b913dfa27573c76673744a7d15df6a885e891 GIT binary patch literal 1518 zcmb`GeQXnD9LKqJF0;ypXM$%)qznuxqXXLO6<<(B>E0+~lC3KP)VtnuFTJ<6&-Jc% zAZ8emC~AekvSdRu6%$2pDmVyKbW4oUh*Ouak!YMEFO$(kV@4F?-TIe*wK4wxe1E_1 z_w)Tds|P?zQB>G{VOezX9MmN~(GT9Z{qdnmvbArh-#syZWbfg+Tan$T4@h55eY@f; ztAP~c6&blvUi>i{an_)&H@Tn7FI=1Wb${3JnFYlCebrK8&fHYrI5rZ0MrqbM(YnW^ zu8yz}42300@JCyjAlohnjRv+pl3BNTUDR0L)Y!N&qJ$g5d^FUGRiVJS1nEFs(N|8K zs2TgAQmW1m&iixxn*|^Km5d$7zuvuP;7Y?EIS2X>MKVGP@G%3jUZ240nhyu8mkuO# zz$Y@g0%^sYFmRF9aIZ{}X*MmUf$Y}^Qo<_GA;h^J>9~9C_)FH>jS%Uy6m6hZq5P>0cx{A{x167X4IXPgm9G=!hNcl5z z%n(d6!TXdLjfVQ>ldKqnmUWv@q6GFfA%g-u!^kuw<2on! zL_H34oz5g>Rf_#$dF}$EKoWDMP0zHBACLO-}0A%ME6Wp7|2ub%9_vC zUORPPXk=Gza3*{G?e`WvFk6JZhEoYz)vX->2DGF^@Ki!GiDW|Ic-o4|LB>c0M8A|U zAgy~fiJ~}wz2-oNnG5tU7n~W9Hr?-NQS^b33-9l?*R}IyIwW^X%ccU0^X$ zvx}5mq{|YyY_)UWEX<-I{(~j7t^mm0H@&Is{G{{mu_pNH>0R@m?J@7lwGC|;xu>Q5 zokxEewE@Wx3)vAmZ9YW>cC%L0g@>Q_W*sNTZUpY|gtsF3{I*8<`0M+oyE~6==;=-O cqYjEpVW>TqDCNBhGOEXh;)yA;3S`y!570JmG5`Po literal 0 HcmV?d00001 diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index 1bec50ed6..89c440597 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -7,8 +7,11 @@ import ( "github.com/ipfs/gateway-conformance/tooling/car" . "github.com/ipfs/gateway-conformance/tooling/check" "github.com/ipfs/gateway-conformance/tooling/helpers" + "github.com/ipfs/gateway-conformance/tooling/ipns" "github.com/ipfs/gateway-conformance/tooling/specs" . "github.com/ipfs/gateway-conformance/tooling/test" + "github.com/multiformats/go-multibase" + "github.com/multiformats/go-multicodec" ) func TestGatewaySubdomains(t *testing.T) { @@ -278,153 +281,200 @@ func TestGatewaySubdomains(t *testing.T) { func TestGatewaySubdomainAndIPNS(t *testing.T) { tests := SugarTests{} + rsaFixture := ipns.MustOpenIPNSRecordWithKey("t0114/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record") + ed25519Fixture := ipns.MustOpenIPNSRecordWithKey("t0114/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record") + + car := car.MustOpenUnixfsCar("t0114/fixtures.car") + helloCID := "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am" + payload := string(car.MustGetRawData(helloCID)) + // We're going to run the same test against multiple gateways (localhost, and a subdomain gateway) gatewayURLs := []string{ SubdomainGatewayURL, SubdomainLocalhostGatewayURL, } + ipnsRecords := []*ipns.IpnsRecord{ + rsaFixture, + ed25519Fixture, + } + for _, gatewayURL := range gatewayURLs { - _, err := url.Parse(gatewayURL) + u, err := url.Parse(gatewayURL) if err != nil { t.Fatal(err) } - tests = append(tests, SugarTests{ - // # /ipns/ - - // test_localhost_gateway_response_should_contain \ - // "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ - // "http://localhost:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ - // "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" - - // test_localhost_gateway_response_should_contain \ - // "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ - // "http://localhost:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ - // "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" - - // # *.ipns.localhost - - // # .ipns.localhost - - // test_localhost_gateway_response_should_contain \ - // "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ - // "http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ - // "$CID_VAL" - - // test_localhost_gateway_response_should_contain \ - // "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ - // "http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ - // "$CID_VAL" - - // test_localhost_gateway_response_should_contain \ - // "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ - // "http://${RSA_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ - // "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" - - // test_localhost_gateway_response_should_contain \ - // "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ - // "http://${ED25519_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ - // "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" - - // # example.com/ipns/ - - // test_hostname_gateway_response_should_contain \ - // "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ - // "example.com" \ - // "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ - // "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" - - // test_hostname_gateway_response_should_contain \ - // "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ - // "example.com" \ - // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ - // "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" - - // # *.ipns.example.com - // # ============================================================================ - - // # .ipns.example.com - - // test_hostname_gateway_response_should_contain \ - // "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ - // "${RSA_IPNS_IDv1}.ipns.example.com" \ - // "http://127.0.0.1:$GWAY_PORT" \ - // "$CID_VAL" - - // test_hostname_gateway_response_should_contain \ - // "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ - // "${ED25519_IPNS_IDv1}.ipns.example.com" \ - // "http://127.0.0.1:$GWAY_PORT" \ - // "$CID_VAL" - - // test_hostname_gateway_response_should_contain \ - // "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ - // "${RSA_IPNS_IDv1_DAGPB}.ipns.example.com" \ - // "http://127.0.0.1:$GWAY_PORT" \ - // "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" - - // test_hostname_gateway_response_should_contain \ - // "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ - // "${ED25519_IPNS_IDv1_DAGPB}.ipns.example.com" \ - // "http://127.0.0.1:$GWAY_PORT" \ - // "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" + for _, record := range ipnsRecords { + tests = append(tests, SugarTests{ + // # /ipns/ + // test_localhost_gateway_response_should_contain \ + // "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "http://localhost:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + // test_localhost_gateway_response_should_contain \ + // "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "http://localhost:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + { + Name: "request for /ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain", + Request: Request(). + DoNotFollowRedirects(). + URL("{{url}}/ipns/{{cid}}", gatewayURL, record.IdV0()), + Response: Expect(). + Status(301). + Headers( + Header("Location"). + Equals("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, record.IdV1(), u.Host), + ), + }, + // # *.ipns.localhost + // # .ipns.localhost + // test_localhost_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ + // "http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ + // "$CID_VAL" + // test_localhost_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ + // "http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ + // "$CID_VAL" + { + Name: "request for {CIDv1-libp2p-key}.ipns.{gateway} returns expected payload", + Request: Request(). + URL("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, record.IdV1(), u.Host), + Response: Expect(). + Status(200). + BodyWithHint("Request for {{cid}}.ipns.{{host}} returns expected payload", payload), + }, + // test_localhost_gateway_response_should_contain \ + // "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "http://${RSA_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + // test_localhost_gateway_response_should_contain \ + // "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "http://${ED25519_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + { + Name: "request for {CIDv1-dag-pb}.ipns.{gateway} redirects to CID with libp2p-key multicodec", + Request: Request(). + DoNotFollowRedirects(). + URL("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, record.IntoCID(multicodec.DagPb, multibase.Base36), u.Host), + Response: Expect(). + Status(301). + Headers( + Header("Location"). + Equals("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, record.IdV1(), u.Host), + ), + }, + // # example.com/ipns/ + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" + // Done above, thanks to the loop + // + // # *.ipns.example.com + // # ============================================================================ + + // # .ipns.example.com + + // test_hostname_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ + // "${RSA_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "$CID_VAL" + + // test_hostname_gateway_response_should_contain \ + // "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ + // "${ED25519_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "$CID_VAL" + + // test_hostname_gateway_response_should_contain \ + // "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "${RSA_IPNS_IDv1_DAGPB}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" + + // test_hostname_gateway_response_should_contain \ + // "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + // "${ED25519_IPNS_IDv1_DAGPB}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" + // # disable /ipns for the hostname by not whitelisting it + // ipfs config --json Gateway.PublicGateways '{ + // "example.com": { + // "UseSubdomains": true, + // "Paths": ["/ipfs"] + // } + // }' || exit 1 + // # restart daemon to apply config changes + // test_kill_ipfs_daemon + // test_launch_ipfs_daemon_without_network + + // TODO: what to do with these? + // # refuse requests to Paths that were not explicitly whitelisted for the hostname + // test_hostname_gateway_response_should_contain \ + // "request for *.ipns.example.com returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "${RSA_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "404 Not Found" + + // test_hostname_gateway_response_should_contain \ + // "request for *.ipns.example.com returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "${ED25519_IPNS_IDv1}.ipns.example.com" \ + // "http://127.0.0.1:$GWAY_PORT" \ + // "404 Not Found" + + // # refuse requests to Paths that were not explicitly whitelisted for the hostname + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/ returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv1" \ + // "404 Not Found" + + // test_hostname_gateway_response_should_contain \ + // "request for example.com/ipns/ returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ + // "example.com" \ + // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv1" \ + // "404 Not Found" + }...) + } + tests = append(tests, SugarTests{ // ## Test subdomain handling of CIDs that do not fit in a single DNS Label (>63chars) // ## https://github.com/ipfs/go-ipfs/issues/7318 // ## ============================================================================ - // # local: *.localhost // test_localhost_gateway_response_should_contain \ // "request for a ED25519 libp2p-key at localhost/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers" \ // "http://localhost:$GWAY_PORT/ipns/$IPNS_ED25519_B58MH" \ // "Location: http://${IPNS_ED25519_B36CID}.ipns.localhost:$GWAY_PORT/" - // # public subdomain gateway: *.example.com - // test_hostname_gateway_response_should_contain \ // "request for a ED25519 libp2p-key at example.com/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers" \ // "example.com" \ // "http://127.0.0.1:$GWAY_PORT/ipns/$IPNS_ED25519_B58MH" \ // "Location: http://${IPNS_ED25519_B36CID}.ipns.example.com" - - // # disable /ipns for the hostname by not whitelisting it - // ipfs config --json Gateway.PublicGateways '{ - // "example.com": { - // "UseSubdomains": true, - // "Paths": ["/ipfs"] - // } - // }' || exit 1 - // # restart daemon to apply config changes - // test_kill_ipfs_daemon - // test_launch_ipfs_daemon_without_network - - // # refuse requests to Paths that were not explicitly whitelisted for the hostname - // test_hostname_gateway_response_should_contain \ - // "request for *.ipns.example.com returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ - // "${RSA_IPNS_IDv1}.ipns.example.com" \ - // "http://127.0.0.1:$GWAY_PORT" \ - // "404 Not Found" - - // test_hostname_gateway_response_should_contain \ - // "request for *.ipns.example.com returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ - // "${ED25519_IPNS_IDv1}.ipns.example.com" \ - // "http://127.0.0.1:$GWAY_PORT" \ - // "404 Not Found" - - // # refuse requests to Paths that were not explicitly whitelisted for the hostname - // test_hostname_gateway_response_should_contain \ - // "request for example.com/ipns/ returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ - // "example.com" \ - // "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv1" \ - // "404 Not Found" - - // test_hostname_gateway_response_should_contain \ - // "request for example.com/ipns/ returns HTTP 404 Not Found when /ipns is not on Paths whitelist" \ - // "example.com" \ - // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv1" \ - // "404 Not Found" + { + Name: "request for a ED25519 libp2p-key at example.com/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers", + Request: Request(). + DoNotFollowRedirects(). + URL("{{url}}/ipns/{{cid}}", gatewayURL, ed25519Fixture.B58MH()), + Response: Expect(). + Headers( + Header("Location"). + Equals("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, ed25519Fixture.IntoCID(multicodec.Libp2pKey, multibase.Base36), u.Host), + ), + }, }...) + } RunIfSpecsAreEnabled(t, helpers.UnwrapSubdomainTests(t, tests), specs.SubdomainGateway, specs.IPNSResolver) diff --git a/tooling/ipns/_fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record b/tooling/ipns/_fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..39b2f41a40866132588557e1be7beb97f50b79d4 GIT binary patch literal 394 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D9<^J}Yul1#e?Les=qsP9*TpHppd@W%Xk@HwV4`bi6k=#> zWnyAwYN=;oU}$b+5v9Rk(9qDcxZ%Nrr`k>qKjaU!b6-4rYi;`fm-}a{vCo?xRu}4g z^l|FihwZAz%U!lw{rY|+(EGO7p=OmC>knNP>?`)U^*Whrs^fvF0qEJ$Sl0Mn+K ATmS$7 literal 0 HcmV?d00001 diff --git a/tooling/ipns/_fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record b/tooling/ipns/_fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..b37d9b75b50ce3baa472e0c790c63cc4476c8631 GIT binary patch literal 1082 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D92(*IMXR5Go)>GsSynodq(GRGFw`JL;^nAdmmZe7@< z?;9u0)~oe;B6n8l#B-O4jHLznwHFfawsGeD^WdI5dD6CW_D`?(IR6u4yS}n>0Xs|C zV|Deu10TJ;+15H9y#1%w)-Pu|{~C5NEh+2ohgE%4GcFi^=!o1|c=mvclzM~Aq?sH1 zV;9|>pJ(C4b;k1K_pY`7N=jcyC2D2fRy9#Bx;b;nX--YmkL>f5|4X=(dm6Aa@2=jn zPF^r?@jIi2gJJgHoU|F^9$YA16dbkmCI8(bi=coUk?re*CGPIOm;0ZgQ?hD>F0X=C zL#OarR>@Lloz*!)FR?K>|cBO@yVa}y&! z15licsfm%1;pVJGT^wtTb=KA$^<13Nv3!=^X2&xt9)0ASbyDGP?WdlXx7q)`d@Z+h z!6|`%_KMwXk1o8Mo?{-MVA*x}^cm9=t?8#{Bqsazy{TE_Y}T4V{h&^m3)r^)bGv-5U2WMvuSX z7vtkGPzgn;O5JiMe~&<38&ZhUC?flR8gS@+~<#_0!o~ z@AZ8}8?WuOn%~@gsOVv8C3nV1oCd(eK6PTncvj@)S}Wr`}S-X)$F5@;ne3PMT?z z=NIb*B<2n+zLRAc6lAY~eeG%iDJwNZBx`P>1GC& uWCn)RutZ?qi=aH?W(21e0yA-PDnka)$jp??lFA5t$v6k5Dx|U?l>q?Oq1V#@ literal 0 HcmV?d00001 diff --git a/tooling/ipns/ipns_test.go b/tooling/ipns/ipns_test.go index c1b33f73a..bdec11c35 100644 --- a/tooling/ipns/ipns_test.go +++ b/tooling/ipns/ipns_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + mbase "github.com/multiformats/go-multibase" + "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/assert" ) @@ -62,3 +64,30 @@ func TestLoadTestRecord(t *testing.T) { err = ipns.Valid() assert.NoError(t, err) } + +func TestIPNSFixtureVersionsConversion(t *testing.T) { + path := "./_fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record" + record, err := OpenIPNSRecordWithKey(path) + + assert.Nil(t, err) + + // 12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d is a ED25519 key, which is using the identity hash. + assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.Key()) + assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.IdV0()) // TODO: confirm with @lidel this makes sense. + assert.Equal(t, "k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam", record.IdV1()) + assert.Equal(t, "k50rm9yjlt0jey4fqg6wafvqprktgbkpgkqdg27tpqje6iimzxewnhvtin9hhq", record.IntoCID(multicodec.DagPb, mbase.Base36)) + assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.B58MH()) + assert.Equal(t, "k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam", record.IntoCID(multicodec.Libp2pKey, mbase.Base36)) + + path = "./_fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record" + record, err = OpenIPNSRecordWithKey(path) + + assert.Nil(t, err) + + // QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3 is a RSA key, which is using sha256 hash. + assert.Equal(t, "QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3", record.Key()) + assert.Equal(t, "QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3", record.IdV0()) + assert.Equal(t, "k2k4r8m7xvggw5pxxk3abrkwyer625hg01hfyggrai7lk1m63fuihi7w", record.IdV1()) + assert.Equal(t, "k2jmtxu61bnhrtj301lw7zizknztocdbeqhxgv76l2q9t36fn9jbzipo", record.IntoCID(multicodec.DagPb, mbase.Base36)) + assert.Equal(t, "QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3", record.B58MH()) +} diff --git a/tooling/ipns/record.go b/tooling/ipns/record.go index 4132711ab..2e4c7a379 100644 --- a/tooling/ipns/record.go +++ b/tooling/ipns/record.go @@ -1,16 +1,21 @@ package ipns import ( + "strings" "time" "github.com/ipfs/boxo/ipns" ipns_pb "github.com/ipfs/boxo/ipns/pb" + "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p/core/peer" + mbase "github.com/multiformats/go-multibase" + "github.com/multiformats/go-multicodec" ) type IpnsRecord struct { pb *ipns_pb.IpnsEntry key string + id peer.ID validity time.Time } @@ -25,7 +30,12 @@ func UnmarshalIpnsRecord(data []byte, pubKey string) (*IpnsRecord, error) { return nil, err } - return &IpnsRecord{pb: pb, key: pubKey, validity: validity}, nil + id, err := peer.Decode(pubKey) + if err != nil { + return nil, err + } + + return &IpnsRecord{pb: pb, key: pubKey, id: id, validity: validity}, nil } func (i *IpnsRecord) Value() string { @@ -41,10 +51,46 @@ func (i *IpnsRecord) Validity() time.Time { } func (i *IpnsRecord) Valid() error { - id, err := peer.Decode(i.key) + return ipns.ValidateWithPeerID(i.id, i.pb) +} + +func (i *IpnsRecord) idV1(codec multicodec.Code, base mbase.Encoding) (string, error) { + c := peer.ToCid(i.id) + c = cid.NewCidV1(uint64(codec), c.Hash()) + s, err := c.StringOfBase(base) + if err != nil { + return "", err + } + return s, nil +} + +func (i *IpnsRecord) IntoCID(codec multicodec.Code, base mbase.Encoding) string { + s, err := i.idV1(codec, base) if err != nil { - return err + panic(err) + } + return s +} + +func (i *IpnsRecord) IdV0() string { + if strings.HasPrefix(i.key, "Qm") || strings.HasPrefix(i.key, "1") { + return i.key } + panic("not a v0 id") + // This code won't work if key starts with "1", this is an identity multihash, which + // makes `NewCidV0` panic. + // c := peer.ToCid(i.id) + // s, err := cid.NewCidV0(c.Hash()).StringOfBase(mbase.Base58BTC) + // if err != nil { + // panic(err) + // } + // return s +} + +func (i *IpnsRecord) IdV1() string { + return i.IntoCID(cid.Libp2pKey, mbase.Base36) +} - return ipns.ValidateWithPeerID(id, i.pb) +func (i *IpnsRecord) B58MH() string { + return i.id.String() } From e79c287ccc7238adbe99cb13475c3736cbaefdab Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Wed, 24 May 2023 16:01:03 +0200 Subject: [PATCH 04/15] t0114: wip dns link implem --- fixtures/fixture.schema.json | 16 +++++- fixtures/t0114/dnslink.yml | 10 ++++ kubo-config.example.sh | 25 +++++---- tests/t0114_gateway_subdomains_test.go | 75 ++++++++++++++++++++++---- tooling/dnslink/dnslink.go | 25 +++++++-- tooling/dnslink/merge.go | 36 ++++++++++--- 6 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 fixtures/t0114/dnslink.yml diff --git a/fixtures/fixture.schema.json b/fixtures/fixture.schema.json index ae26a75bf..4b5f50476 100644 --- a/fixtures/fixture.schema.json +++ b/fixtures/fixture.schema.json @@ -7,6 +7,9 @@ "additionalProperties": { "type": "object", "properties": { + "domain": { + "type": "string" + }, "subdomain": { "type": "string" }, @@ -15,9 +18,20 @@ } }, "required": [ - "subdomain", "path" ], + "oneOf": [ + { + "required": [ + "domain" + ] + }, + { + "required": [ + "subdomain" + ] + } + ], "additionalProperties": false } } diff --git a/fixtures/t0114/dnslink.yml b/fixtures/t0114/dnslink.yml new file mode 100644 index 000000000..a4434a0d4 --- /dev/null +++ b/fixtures/t0114/dnslink.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=../fixture.schema.json +dnslinks: + wikipedia: + domain: en.wikipedia-on-ipfs.org + # Wikipedia CID + path: /ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze + test: + subdomain: dnslink-test + # CIDv1=$(echo "hello" | ipfs add --cid-version 1 -Q) + path: /ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am \ No newline at end of file diff --git a/kubo-config.example.sh b/kubo-config.example.sh index 14cf41969..3ef70cf07 100755 --- a/kubo-config.example.sh +++ b/kubo-config.example.sh @@ -1,17 +1,20 @@ #! /usr/bin/env bash -ipfs config --json Gateway.PublicGateways '{ - "example.com": { - "UseSubdomains": true, - "Paths": ["/ipfs", "/ipns", "/api"] - }, - "localhost": { - "UseSubdomains": true, - "InlineDNSLink": true, - "Paths": ["/ipfs", "/ipns", "/api"] - } +ipfs config --json Gateway.PublicGateways '{ + "example.com": { + "UseSubdomains": true, + "InlineDNSLink": true, + "Paths": ["/ipfs", "/ipns", "/api"] + }, + "localhost": { + "UseSubdomains": true, + "InlineDNSLink": true, + "Paths": ["/ipfs", "/ipns", "/api"] + } }' -export IPFS_NS_MAP=$(cat ./dnslinks.json | jq -r 'to_entries | map("\(.key).example.com:\(.value)") | join(",")') +export IPFS_NS_MAP=$(cat ./dnslinks.json | jq -r '.subdomains | to_entries | map("\(.key).example.com:\(.value)") | join(",")') +export IPFS_NS_MAP="$(cat ./dnslinks.json | jq -r '.domains | to_entries | map("\(.key):\(.value)") | join(",")'),${IPFS_NS_MAP}" + echo "Set the following IPFS_NS_MAP before starting the kubo daemon:" echo "IPFS_NS_MAP=${IPFS_NS_MAP}" diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index 89c440597..4bb1859f8 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -2,10 +2,12 @@ package tests import ( "net/url" + "strings" "testing" "github.com/ipfs/gateway-conformance/tooling/car" . "github.com/ipfs/gateway-conformance/tooling/check" + "github.com/ipfs/gateway-conformance/tooling/dnslink" "github.com/ipfs/gateway-conformance/tooling/helpers" "github.com/ipfs/gateway-conformance/tooling/ipns" "github.com/ipfs/gateway-conformance/tooling/specs" @@ -14,6 +16,13 @@ import ( "github.com/multiformats/go-multicodec" ) +func InlineDNS(s string) string { + // See spec at https://github.com/ipfs/specs/blob/main/src/http-gateways/subdomain-gateway.md#host-request-header + // Every - is replaced with -- + // Every . is replaced with - + return strings.ReplaceAll(strings.ReplaceAll(s, "-", "--"), ".", "-") +} + func TestGatewaySubdomains(t *testing.T) { fixture := car.MustOpenUnixfsCar("t0114-gateway_subdomains.car") @@ -489,37 +498,61 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { SubdomainLocalhostGatewayURL, } + dnsLinks := dnslink.MustOpenDNSLink("t0114/dnslink.yml") + wikipedia := dnsLinks.MustGet("wikipedia") + dnsLinkTest := dnsLinks.MustGet("test") + for _, gatewayURL := range gatewayURLs { - _, err := url.Parse(gatewayURL) + u, err := url.Parse(gatewayURL) if err != nil { t.Fatal(err) } tests = append(tests, SugarTests{ // # /ipns/ - // test_localhost_gateway_response_should_contain \ // "request for localhost/ipns/{fqdn} redirects to DNSLink in subdomain" \ // "http://localhost:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ // "Location: http://en.wikipedia-on-ipfs.org.ipns.localhost:$GWAY_PORT/wiki" - + { + Name: "request for /ipns/{fqdn} redirects to DNSLink in subdomain", + Request: Request(). + DoNotFollowRedirects(). + URL("{{url}}/ipns/{{fqdn}}/wiki/", gatewayURL, wikipedia), + Response: Expect(). + // TODO: @lidel - some tests relie on dns inlining vs no inlining, I'm not sure how we want to port these. + // Is it two specs and we'll need users to run the test suite twice with different configuration? + // Is there a mainstream configuration to do this? + // It's not like specs are just additive (you cover spec 1 + spec 2 + ...) + // it seems that this inlining and subdomain specs are impacting each other + // (if you enabled inlining, then the redirect value will be different for every tests about subdomains) + Headers( + Header("Location"). + Equals("{{scheme}}://{{fqdn}}.ipns.{{host}}/wiki/", u.Scheme, InlineDNS(wikipedia), u.Host), + ), + }, // # .ipns.localhost - // # DNSLink test requires a daemon in online mode with precached /ipns/ mapping // test_kill_ipfs_daemon // DNSLINK_FQDN="dnslink-test.example.com" // export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1" // test_launch_ipfs_daemon - // test_localhost_gateway_response_should_contain \ // "request for {dnslink}.ipns.localhost returns expected payload" \ // "http://$DNSLINK_FQDN.ipns.localhost:$GWAY_PORT" \ // "$CID_VAL" - + { + Name: "request for {dnslink}.ipns.{gateway} returns expected payload", + Request: Request(). + // TODO: Fix this `example.com` hardcoding, something is not right + // with the way we deal with dns linking and subdomains. + URL("{{scheme}}://{{fqdn}}.example.com.ipns.{{host}}", u.Scheme, dnsLinkTest, u.Host), + Response: Expect(). + Body("hello\n"), + }, // ## ============================================================================ // ## Test DNSLink inlining on HTTP gateways // ## ============================================================================ - // # set explicit subdomain gateway config for the hostname // ipfs config --json Gateway.PublicGateways '{ // "localhost": { @@ -563,14 +596,34 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // curl -H \"Host: example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki\" > response && // test_should_contain \"Location: https://en-wikipedia--on--ipfs-org.ipns.example.com/wiki\" response // " - + { + Name: "request for example.com/ipns/{fqdn} with X-Forwarded-Proto redirects to TLS-safe label in subdomain", + Request: Request(). + DoNotFollowRedirects(). + Header("X-Forwarded-Proto", "https"). + URL("{{url}}/ipns/{{wikipedia}}/wiki/", gatewayURL, wikipedia), + Response: Expect(). + Headers( + Header("Location"). + Equals("https://{{inlined}}.ipns.{{host}}/wiki/", InlineDNS(wikipedia), u.Host), + ), + }, // # Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler // test_hostname_gateway_response_should_contain \ // "request for example.com/ipns/?uri=ipns%3A%2F%2F.. produces redirect to /ipns/.. content path" \ // "example.com" \ // "http://127.0.0.1:$GWAY_PORT/ipns/?uri=ipns%3A%2F%2Fen.wikipedia-on-ipfs.org" \ // "Location: /ipns/en.wikipedia-on-ipfs.org" - + { + Name: "request for example.com/ipns/?uri=ipns%3A%2F%2F.. produces redirect to /ipns/.. content path", + Hint: "Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler", + Request: Request(). + URL("{{url}}/ipns/?uri=ipns%3A%2F%2F{{dnslink}}", gatewayURL, wikipedia), + Response: Expect(). + Headers( + Header("Location").Equals("/ipns/en.wikipedia-on-ipfs.org"), + ), + }, // # DNSLink: .ipns.example.com // # (not really useful outside of localhost, as setting TLS for more than one // # level of wildcard is a pain, but we support it if someone really wants it) @@ -587,6 +640,9 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // "$DNSLINK_FQDN.ipns.example.com" \ // "http://127.0.0.1:$GWAY_PORT" \ // "$CID_VAL" + // TODO(laurent): I am not sure what we're testing here, + // the test queries somethingsomething.example.org.ipns.example.com + // which seems covered by the wikipedia-on-ipfs.org test above. // # DNSLink on Public gateway with a single-level wildcard TLS cert // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 @@ -595,6 +651,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // curl -H \"Host: dnslink--subdomain--gw--test-example-org.ipns.example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT\" > response && // test_should_contain \"$CID_VAL\" response // " + // Same? // ## ============================================================================ // ## Test DNSLink requests with a custom PublicGateway (hostname config) diff --git a/tooling/dnslink/dnslink.go b/tooling/dnslink/dnslink.go index 2a8778ab9..22c8719f4 100644 --- a/tooling/dnslink/dnslink.go +++ b/tooling/dnslink/dnslink.go @@ -9,22 +9,23 @@ import ( "gopkg.in/yaml.v3" ) -type DNSLinks struct { +type ConfigFixture struct { DNSLinks map[string]DNSLink `yaml:"dnslinks"` } type DNSLink struct { Subdomain string `yaml:"subdomain"` + Domain string `yaml:"domain"` Path string `yaml:"path"` } -func OpenDNSLink(absPath string) (*DNSLinks, error) { +func OpenDNSLink(absPath string) (*ConfigFixture, error) { data, err := os.ReadFile(absPath) if err != nil { return nil, err } - var dnsLinks DNSLinks + var dnsLinks ConfigFixture err = yaml.Unmarshal(data, &dnsLinks) if err != nil { return nil, err @@ -33,7 +34,7 @@ func OpenDNSLink(absPath string) (*DNSLinks, error) { return &dnsLinks, nil } -func MustOpenDNSLink(file string) *DNSLinks { +func MustOpenDNSLink(file string) *ConfigFixture { fixturePath := path.Join(fixtures.Dir(), file) dnsLinks, err := OpenDNSLink(fixturePath) if err != nil { @@ -43,10 +44,24 @@ func MustOpenDNSLink(file string) *DNSLinks { return dnsLinks } -func (d *DNSLinks) MustGet(id string) string { +func (d *ConfigFixture) MustGet(id string) string { dnsLink, ok := d.DNSLinks[id] if !ok { panic(fmt.Errorf("dnslink %s not found", id)) } + if dnsLink.Domain != "" && dnsLink.Subdomain != "" { + panic(fmt.Errorf("dnslink %s has both domain and subdomain", id)) + } + if dnsLink.Domain == "" && dnsLink.Subdomain == "" { + panic(fmt.Errorf("dnslink %s has neither domain nor subdomain", id)) + } + if dnsLink.Path == "" { + panic(fmt.Errorf("dnslink %s has no path", id)) + } + + if dnsLink.Domain != "" { + return dnsLink.Domain + } + return dnsLink.Subdomain } diff --git a/tooling/dnslink/merge.go b/tooling/dnslink/merge.go index e1fb70b6c..446af9b19 100644 --- a/tooling/dnslink/merge.go +++ b/tooling/dnslink/merge.go @@ -6,8 +6,16 @@ import ( "os" ) -func Aggregate(inputPaths []string) (map[string]string, error) { - aggMap := make(map[string]string) +type DNSLinksAggregate struct { + Domains map[string]string `json:"domains"` + Subdomains map[string]string `json:"subdomains"` +} + +func Aggregate(inputPaths []string) (*DNSLinksAggregate, error) { + agg := DNSLinksAggregate{ + Domains: make(map[string]string), + Subdomains: make(map[string]string), + } for _, file := range inputPaths { dnsLinks, err := OpenDNSLink(file) @@ -16,15 +24,31 @@ func Aggregate(inputPaths []string) (map[string]string, error) { } for _, link := range dnsLinks.DNSLinks { - if _, ok := aggMap[link.Subdomain]; ok { - return nil, fmt.Errorf("collision detected for subdomain %s", link.Subdomain) + if link.Domain != "" && link.Subdomain != "" { + return nil, fmt.Errorf("dnslink %s has both domain and subdomain", link.Subdomain) + } + + if link.Domain != "" { + if _, ok := agg.Domains[link.Domain]; ok { + return nil, fmt.Errorf("collision detected for domain %s", link.Domain) + } + + agg.Domains[link.Domain] = link.Path + continue } - aggMap[link.Subdomain] = link.Path + if link.Subdomain != "" { + if _, ok := agg.Subdomains[link.Subdomain]; ok { + return nil, fmt.Errorf("collision detected for subdomain %s", link.Subdomain) + } + + agg.Subdomains[link.Subdomain] = link.Path + continue + } } } - return aggMap, nil + return &agg, nil } func Merge(inputPaths []string, outputPath string) error { From 83e66e90557cf5ecb423398aa3cfe20e77a7c9ed Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Mon, 29 May 2023 14:30:48 +0200 Subject: [PATCH 05/15] t0114: add lidel feedback --- tests/t0114_gateway_subdomains_test.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index 4bb1859f8..01861accb 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -520,12 +520,6 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { DoNotFollowRedirects(). URL("{{url}}/ipns/{{fqdn}}/wiki/", gatewayURL, wikipedia), Response: Expect(). - // TODO: @lidel - some tests relie on dns inlining vs no inlining, I'm not sure how we want to port these. - // Is it two specs and we'll need users to run the test suite twice with different configuration? - // Is there a mainstream configuration to do this? - // It's not like specs are just additive (you cover spec 1 + spec 2 + ...) - // it seems that this inlining and subdomain specs are impacting each other - // (if you enabled inlining, then the redirect value will be different for every tests about subdomains) Headers( Header("Location"). Equals("{{scheme}}://{{fqdn}}.ipns.{{host}}/wiki/", u.Scheme, InlineDNS(wikipedia), u.Host), @@ -598,6 +592,10 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // " { Name: "request for example.com/ipns/{fqdn} with X-Forwarded-Proto redirects to TLS-safe label in subdomain", + Hint: ` + DNSLink on Public gateway with a single-level wildcard TLS cert + "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 + `, Request: Request(). DoNotFollowRedirects(). Header("X-Forwarded-Proto", "https"). @@ -615,13 +613,13 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // "http://127.0.0.1:$GWAY_PORT/ipns/?uri=ipns%3A%2F%2Fen.wikipedia-on-ipfs.org" \ // "Location: /ipns/en.wikipedia-on-ipfs.org" { - Name: "request for example.com/ipns/?uri=ipns%3A%2F%2F.. produces redirect to /ipns/.. content path", + Name: `request for example.com/ipns/?uri=ipns%3A%2F%2F.. produces redirect to /ipns/.. content path`, Hint: "Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler", Request: Request(). - URL("{{url}}/ipns/?uri=ipns%3A%2F%2F{{dnslink}}", gatewayURL, wikipedia), + URL(`{{url}}/ipns/?uri=ipns%3A%2F%2F{{dnslink}}`, gatewayURL, wikipedia), Response: Expect(). Headers( - Header("Location").Equals("/ipns/en.wikipedia-on-ipfs.org"), + Header("Location").Equals("/ipns/{{wikipedia}}", wikipedia), ), }, // # DNSLink: .ipns.example.com @@ -769,4 +767,4 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { } RunIfSpecsAreEnabled(t, helpers.UnwrapSubdomainTests(t, tests), specs.SubdomainGateway, specs.DNSLinkResolver) -} +} \ No newline at end of file From 428928dab8c2266cd36cfb2f8d422b6fbafb6517 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 6 Jun 2023 14:32:41 +0200 Subject: [PATCH 06/15] wip --- tests/t0114_gateway_subdomains_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index 01861accb..03c5dfd89 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -640,7 +640,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // "$CID_VAL" // TODO(laurent): I am not sure what we're testing here, // the test queries somethingsomething.example.org.ipns.example.com - // which seems covered by the wikipedia-on-ipfs.org test above. + // which seems covered by the test above. // # DNSLink on Public gateway with a single-level wildcard TLS cert // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 @@ -650,6 +650,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // test_should_contain \"$CID_VAL\" response // " // Same? + // TODO: use something with .example.com instead of wikipedia with the spoofing. // ## ============================================================================ // ## Test DNSLink requests with a custom PublicGateway (hostname config) From 8889c91b103e673d17f3700733720f537db8c4f0 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Wed, 7 Jun 2023 14:07:13 +0200 Subject: [PATCH 07/15] fix: test with subdomains --- fixtures/t0114/dnslink.yml | 4 ++-- tests/t0114_gateway_subdomains_test.go | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/fixtures/t0114/dnslink.yml b/fixtures/t0114/dnslink.yml index a4434a0d4..54c793b72 100644 --- a/fixtures/t0114/dnslink.yml +++ b/fixtures/t0114/dnslink.yml @@ -1,10 +1,10 @@ # yaml-language-server: $schema=../fixture.schema.json dnslinks: wikipedia: - domain: en.wikipedia-on-ipfs.org + domain: dnslink-subdomain-gw-test.example.org # Wikipedia CID path: /ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze test: - subdomain: dnslink-test + domain: dnslink-test.example.com # CIDv1=$(echo "hello" | ipfs add --cid-version 1 -Q) path: /ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am \ No newline at end of file diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index 03c5dfd89..fc10ebf1e 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -538,9 +538,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { { Name: "request for {dnslink}.ipns.{gateway} returns expected payload", Request: Request(). - // TODO: Fix this `example.com` hardcoding, something is not right - // with the way we deal with dns linking and subdomains. - URL("{{scheme}}://{{fqdn}}.example.com.ipns.{{host}}", u.Scheme, dnsLinkTest, u.Host), + URL("{{scheme}}://{{fqdn}}.ipns.{{host}}", u.Scheme, dnsLinkTest, u.Host), Response: Expect(). Body("hello\n"), }, @@ -638,9 +636,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // "$DNSLINK_FQDN.ipns.example.com" \ // "http://127.0.0.1:$GWAY_PORT" \ // "$CID_VAL" - // TODO(laurent): I am not sure what we're testing here, - // the test queries somethingsomething.example.org.ipns.example.com - // which seems covered by the test above. + // Note: this test was merged with the test for wikipedia in the end. // # DNSLink on Public gateway with a single-level wildcard TLS cert // # "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 @@ -649,9 +645,8 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // curl -H \"Host: dnslink--subdomain--gw--test-example-org.ipns.example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT\" > response && // test_should_contain \"$CID_VAL\" response // " - // Same? - // TODO: use something with .example.com instead of wikipedia with the spoofing. - + // Note: this test was merged with the test for wikipedia in the end. + // ## ============================================================================ // ## Test DNSLink requests with a custom PublicGateway (hostname config) // ## (DNSLink site at http://dnslink-test.example.com) From 3cdaf8bd720220943883a2fb2d499167ad594691 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Wed, 7 Jun 2023 14:17:05 +0200 Subject: [PATCH 08/15] fix: new api --- tests/t0114_gateway_subdomains_test.go | 49 ++++++++++++-------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index fc10ebf1e..dd69e17d8 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -55,7 +55,7 @@ func TestGatewaySubdomains(t *testing.T) { subdomains should not return payload directly, but redirect to URL with proper origin isolation `, - Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv1), + Request: Request().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv1), Response: Expect(). Status(301). Headers( @@ -78,7 +78,7 @@ func TestGatewaySubdomains(t *testing.T) { subdomains should not return payload directly, but redirect to URL with proper origin isolation `, - Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, DirCID), + Request: Request().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, DirCID), Response: Expect(). Status(301). Headers( @@ -89,7 +89,7 @@ func TestGatewaySubdomains(t *testing.T) { }, { Name: "request for example.com/ipfs/{CIDv0} redirects to CIDv1 representation in subdomain", - Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv0), + Request: Request().URL("{{url}}/ipfs/{{cid}}/", gatewayURL, CIDv0), Response: Expect(). Status(301). Headers( @@ -104,7 +104,7 @@ func TestGatewaySubdomains(t *testing.T) { // ============================================================================ { Name: "request for {CID}.ipfs.example.com should return expected payload", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}", u.Scheme, CIDv1, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}", u.Scheme, CIDv1, u.Host), Response: Expect(). Status(200). Body(Contains(CIDVal)), @@ -112,14 +112,14 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "request for {CID}.ipfs.example.com/ipfs/{CID} should return HTTP 404", Hint: "ensure /ipfs/ namespace is not mounted on subdomain", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/{{cid}}", u.Scheme, CIDv1, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/{{cid}}", u.Scheme, CIDv1, u.Host), Response: Expect(). Status(404), }, { Name: "request for {CID}.ipfs.example.com/ipfs/file.txt should return data from a file in CID content root", Hint: "ensure requests to /ipfs/* are not blocked, if content root has such subdirectory", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/file.txt", u.Scheme, DirCID, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/file.txt", u.Scheme, DirCID, u.Host), Response: Expect(). Status(200). Body(Contains("I am a txt file")), @@ -127,7 +127,7 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "valid file and subdirectory paths in directory listing at {cid}.ipfs.example.com", Hint: "{CID}.ipfs.example.com/sub/dir (Directory Listing)", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, DirCID, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, DirCID, u.Host), Response: Expect(). Status(200). Body(And( @@ -138,7 +138,7 @@ func TestGatewaySubdomains(t *testing.T) { }, { Name: "valid parent directory path in directory listing at {cid}.ipfs.example.com/sub/dir", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), Response: Expect(). Status(200). Body(And( @@ -149,7 +149,7 @@ func TestGatewaySubdomains(t *testing.T) { }, { Name: "request for deep path resource at {cid}.ipfs.localhost/sub/dir/file", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/bar", u.Scheme, DirCID, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/bar", u.Scheme, DirCID, u.Host), Response: Expect(). Status(200). Body(Contains("text-file-content")), @@ -160,7 +160,7 @@ func TestGatewaySubdomains(t *testing.T) { Note 1: we test for sneaky subdir names {cid}.ipfs.example.com/ipfs/ipns/ :^) Note 2: example.com/ipfs/.. present in HTML will be redirected to subdomain, so this is expected behavior `, - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/ipfs/ipns/", u.Scheme, DirCID, u.Host), Response: Expect(). Status(200). Body( @@ -185,7 +185,7 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "request for example.com/ipfs/{CIDv1} produces redirect to {CIDv1}.ipfs.example.com", Hint: "path requests to the root hostname should redirect to a subdomain URL with proper origin isolation", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1), + Request: Request().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1), Response: Expect(). Headers( Header("Location").Equals("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), @@ -195,14 +195,14 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "request for example.com/ipfs/{InvalidCID} produces useful error before redirect", Hint: "error message should include original CID (and it should be case-sensitive, as we can't assume everyone uses base32)", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/QmInvalidCID", u.Scheme, u.Host), + Request: Request().URL("{{scheme}}://{{host}}/ipfs/QmInvalidCID", u.Scheme, u.Host), Response: Expect(). Body(Contains(`invalid path "/ipfs/QmInvalidCID"`)), }, { Name: "request for example.com/ipfs/{CIDv0} produces redirect to {CIDv1}.ipfs.example.com", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv0), + Request: Request().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv0), Response: Expect(). Status(301). Headers( @@ -213,7 +213,7 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "request for http://example.com/ipfs/{CID} with X-Forwarded-Proto: https produces redirect to HTTPS URL", Hint: "Support X-Forwarded-Proto", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1). + Request: Request().URL("{{scheme}}://{{host}}/ipfs/{{cid}}/", u.Scheme, u.Host, CIDv1). Header("X-Forwarded-Proto", "https"), Response: Expect(). Status(301). @@ -225,7 +225,7 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "request for example.com/ipfs/?uri=ipfs%3A%2F%2F.. produces redirect to /ipfs/.. content path", Hint: "Support ipfs:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{host}}/ipfs/", u.Scheme, u.Host). + Request: Request().URL("{{scheme}}://{{host}}/ipfs/", u.Scheme, u.Host). Query( "uri", "ipfs://{{host}}/wiki/Diego_Maradona.html", CIDWikipedia, ), @@ -238,7 +238,7 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "request for a too long CID at localhost/ipfs/{CIDv1} returns human readable error", Hint: "router should not redirect to hostnames that could fail due to DNS limits", - Request: Request().DoNotFollowRedirects().URL("{{url}}/ipfs/{{cid}}", gatewayURL, CIDv1_TOO_LONG), + Request: Request().URL("{{url}}/ipfs/{{cid}}", gatewayURL, CIDv1_TOO_LONG), Response: Expect(). Status(400). Body(Contains("CID incompatible with DNS label length limit of 63")), @@ -246,7 +246,7 @@ func TestGatewaySubdomains(t *testing.T) { { Name: "request for a too long CID at {CIDv1}.ipfs.localhost returns expected payload", Hint: "direct request should also fail (provides the same UX as router and avoids confusion)", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1_TOO_LONG, u.Host), + Request: Request().URL("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1_TOO_LONG, u.Host), Response: Expect(). Status(400). Body(Contains("CID incompatible with DNS label length limit of 63")), @@ -256,13 +256,13 @@ func TestGatewaySubdomains(t *testing.T) { // ## ============================================================================ { Name: "request for http://fake.domain.com/ipfs/{CID} doesn't match the example.com gateway", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1), + Request: Request().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1), Response: Expect(). Status(200), }, { Name: "request for http://fake.domain.com/ipfs/{CID} with X-Forwarded-Host: example.com match the example.com gateway", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). + Request: Request().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). Header("X-Forwarded-Host", u.Host), Response: Expect(). Status(301). @@ -272,7 +272,7 @@ func TestGatewaySubdomains(t *testing.T) { }, { Name: "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", - Request: Request().DoNotFollowRedirects().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). + Request: Request().URL("{{scheme}}://{{domain}}/ipfs/{{cid}}", u.Scheme, "fake.domain.com", CIDv1). Header("X-Forwarded-Host", u.Host). Header("X-Forwarded-Proto", "https"), Response: Expect(). @@ -328,7 +328,6 @@ func TestGatewaySubdomainAndIPNS(t *testing.T) { { Name: "request for /ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain", Request: Request(). - DoNotFollowRedirects(). URL("{{url}}/ipns/{{cid}}", gatewayURL, record.IdV0()), Response: Expect(). Status(301). @@ -366,7 +365,6 @@ func TestGatewaySubdomainAndIPNS(t *testing.T) { { Name: "request for {CIDv1-dag-pb}.ipns.{gateway} redirects to CID with libp2p-key multicodec", Request: Request(). - DoNotFollowRedirects(). URL("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, record.IntoCID(multicodec.DagPb, multibase.Base36), u.Host), Response: Expect(). Status(301). @@ -474,7 +472,6 @@ func TestGatewaySubdomainAndIPNS(t *testing.T) { { Name: "request for a ED25519 libp2p-key at example.com/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers", Request: Request(). - DoNotFollowRedirects(). URL("{{url}}/ipns/{{cid}}", gatewayURL, ed25519Fixture.B58MH()), Response: Expect(). Headers( @@ -517,7 +514,6 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { { Name: "request for /ipns/{fqdn} redirects to DNSLink in subdomain", Request: Request(). - DoNotFollowRedirects(). URL("{{url}}/ipns/{{fqdn}}/wiki/", gatewayURL, wikipedia), Response: Expect(). Headers( @@ -595,7 +591,6 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 `, Request: Request(). - DoNotFollowRedirects(). Header("X-Forwarded-Proto", "https"). URL("{{url}}/ipns/{{wikipedia}}/wiki/", gatewayURL, wikipedia), Response: Expect(). @@ -646,7 +641,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { // test_should_contain \"$CID_VAL\" response // " // Note: this test was merged with the test for wikipedia in the end. - + // ## ============================================================================ // ## Test DNSLink requests with a custom PublicGateway (hostname config) // ## (DNSLink site at http://dnslink-test.example.com) @@ -763,4 +758,4 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { } RunIfSpecsAreEnabled(t, helpers.UnwrapSubdomainTests(t, tests), specs.SubdomainGateway, specs.DNSLinkResolver) -} \ No newline at end of file +} From aca3b65712188be686a213159dcdab5d9be4bf90 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Wed, 7 Jun 2023 14:17:19 +0200 Subject: [PATCH 09/15] fix: do not check redirect payload --- tests/t0114_gateway_subdomains_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index dd69e17d8..f0c384332 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -62,14 +62,6 @@ func TestGatewaySubdomains(t *testing.T) { Header("Location"). Hint("request for example.com/ipfs/{CIDv1} returns Location HTTP header for subdomain redirect in browsers"). Contains("{{scheme}}://{{cid}}.ipfs.{{host}}/", u.Scheme, CIDv1, u.Host), - ). - BodyWithHint(` - We return body with HTTP 301 so existing cli scripts that use path-based - gateway do not break (curl doesn't auto-redirect without passing -L; wget - does not span across hostnames by default) - Context: https://github.com/ipfs/go-ipfs/issues/6975 - `, - IsEqual("hello\n"), ), }, { From 87e1fa526509347c6922a0259f0d51d140926194 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Wed, 7 Jun 2023 14:22:33 +0200 Subject: [PATCH 10/15] doc: note --- tests/t0114_gateway_subdomains_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index f0c384332..3ff9020fd 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -376,7 +376,7 @@ func TestGatewaySubdomainAndIPNS(t *testing.T) { // "example.com" \ // "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ // "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" - // Done above, thanks to the loop + // NOTE: Done above, thanks to the loop // // # *.ipns.example.com // # ============================================================================ From cac4ffcb61ba1f76a89e84ab88f100924eb59437 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 8 Jun 2023 13:46:45 +0200 Subject: [PATCH 11/15] fix: kubo provisioning --- .github/workflows/test.yml | 3 +-- kubo-config.example.sh | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e0c9b66e0..fdf4006a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,8 +37,7 @@ jobs: - name: Configure Kubo Gateway run: | ipfs init; - source ./gateway-conformance/kubo-config.example.sh; - IPFS_NS_MAP=$(cat ./fixtures/dnslinks.json | jq -r 'to_entries | map("\(.key).example.com:\(.value)") | join(",")') + source ./gateway-conformance/kubo-config.example.sh "$(pwd)/fixtures" echo "IPFS_NS_MAP=${IPFS_NS_MAP}" >> $GITHUB_ENV # note: the IPFS_NS_MAP set above will be passed the daemon - uses: ipfs/start-ipfs-daemon-action@v1 diff --git a/kubo-config.example.sh b/kubo-config.example.sh index 3ef70cf07..48f17a82f 100755 --- a/kubo-config.example.sh +++ b/kubo-config.example.sh @@ -1,4 +1,7 @@ #! /usr/bin/env bash + +FIXTURES_PATH=${1:-$(pwd)} + ipfs config --json Gateway.PublicGateways '{ "example.com": { "UseSubdomains": true, @@ -12,9 +15,8 @@ ipfs config --json Gateway.PublicGateways '{ } }' -export IPFS_NS_MAP=$(cat ./dnslinks.json | jq -r '.subdomains | to_entries | map("\(.key).example.com:\(.value)") | join(",")') -export IPFS_NS_MAP="$(cat ./dnslinks.json | jq -r '.domains | to_entries | map("\(.key):\(.value)") | join(",")'),${IPFS_NS_MAP}" - +export IPFS_NS_MAP=$(cat "${FIXTURES_PATH}/dnslinks.json" | jq -r '.subdomains | to_entries | map("\(.key).example.com:\(.value)") | join(",")') +export IPFS_NS_MAP="$(cat "${FIXTURES_PATH}/dnslinks.json" | jq -r '.domains | to_entries | map("\(.key):\(.value)") | join(",")'),${IPFS_NS_MAP}" echo "Set the following IPFS_NS_MAP before starting the kubo daemon:" echo "IPFS_NS_MAP=${IPFS_NS_MAP}" From 35319015bcf3bad45713186393eb9021e2836198 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 8 Jun 2023 14:28:04 +0200 Subject: [PATCH 12/15] doc: remove note --- tooling/ipns/ipns_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/ipns/ipns_test.go b/tooling/ipns/ipns_test.go index bdec11c35..b96c73ce8 100644 --- a/tooling/ipns/ipns_test.go +++ b/tooling/ipns/ipns_test.go @@ -73,7 +73,7 @@ func TestIPNSFixtureVersionsConversion(t *testing.T) { // 12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d is a ED25519 key, which is using the identity hash. assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.Key()) - assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.IdV0()) // TODO: confirm with @lidel this makes sense. + assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.IdV0()) assert.Equal(t, "k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam", record.IdV1()) assert.Equal(t, "k50rm9yjlt0jey4fqg6wafvqprktgbkpgkqdg27tpqje6iimzxewnhvtin9hhq", record.IntoCID(multicodec.DagPb, mbase.Base36)) assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.B58MH()) From 0938e393e65730c07ab4c25b3770163706011e1b Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 13 Jun 2023 11:26:51 +0200 Subject: [PATCH 13/15] doc: drop note --- tooling/ipns/record.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tooling/ipns/record.go b/tooling/ipns/record.go index 2e4c7a379..708149193 100644 --- a/tooling/ipns/record.go +++ b/tooling/ipns/record.go @@ -76,15 +76,8 @@ func (i *IpnsRecord) IdV0() string { if strings.HasPrefix(i.key, "Qm") || strings.HasPrefix(i.key, "1") { return i.key } + panic("not a v0 id") - // This code won't work if key starts with "1", this is an identity multihash, which - // makes `NewCidV0` panic. - // c := peer.ToCid(i.id) - // s, err := cid.NewCidV0(c.Hash()).StringOfBase(mbase.Base58BTC) - // if err != nil { - // panic(err) - // } - // return s } func (i *IpnsRecord) IdV1() string { From 34a35ec284e58c3e52a28c104ff14e842f88053a Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 13 Jun 2023 11:29:20 +0200 Subject: [PATCH 14/15] refactor: move dns link inlining to its own package --- tests/t0114_gateway_subdomains_test.go | 12 ++---------- tooling/dnslink/dnslink.go | 8 ++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index 3ff9020fd..eebe1af29 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -2,7 +2,6 @@ package tests import ( "net/url" - "strings" "testing" "github.com/ipfs/gateway-conformance/tooling/car" @@ -16,13 +15,6 @@ import ( "github.com/multiformats/go-multicodec" ) -func InlineDNS(s string) string { - // See spec at https://github.com/ipfs/specs/blob/main/src/http-gateways/subdomain-gateway.md#host-request-header - // Every - is replaced with -- - // Every . is replaced with - - return strings.ReplaceAll(strings.ReplaceAll(s, "-", "--"), ".", "-") -} - func TestGatewaySubdomains(t *testing.T) { fixture := car.MustOpenUnixfsCar("t0114-gateway_subdomains.car") @@ -510,7 +502,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { Response: Expect(). Headers( Header("Location"). - Equals("{{scheme}}://{{fqdn}}.ipns.{{host}}/wiki/", u.Scheme, InlineDNS(wikipedia), u.Host), + Equals("{{scheme}}://{{fqdn}}.ipns.{{host}}/wiki/", u.Scheme, dnslink.InlineDNS(wikipedia), u.Host), ), }, // # .ipns.localhost @@ -588,7 +580,7 @@ func TestGatewaySubdomainAndDnsLink(t *testing.T) { Response: Expect(). Headers( Header("Location"). - Equals("https://{{inlined}}.ipns.{{host}}/wiki/", InlineDNS(wikipedia), u.Host), + Equals("https://{{inlined}}.ipns.{{host}}/wiki/", dnslink.InlineDNS(wikipedia), u.Host), ), }, // # Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler diff --git a/tooling/dnslink/dnslink.go b/tooling/dnslink/dnslink.go index 22c8719f4..481eaa872 100644 --- a/tooling/dnslink/dnslink.go +++ b/tooling/dnslink/dnslink.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path" + "strings" "github.com/ipfs/gateway-conformance/tooling/fixtures" "gopkg.in/yaml.v3" @@ -19,6 +20,13 @@ type DNSLink struct { Path string `yaml:"path"` } +func InlineDNS(s string) string { + // See spec at https://github.com/ipfs/specs/blob/main/src/http-gateways/subdomain-gateway.md#host-request-header + // Every - is replaced with -- + // Every . is replaced with - + return strings.ReplaceAll(strings.ReplaceAll(s, "-", "--"), ".", "-") +} + func OpenDNSLink(absPath string) (*ConfigFixture, error) { data, err := os.ReadFile(absPath) if err != nil { From 905ef371e2590c3492299481dae4e45d6b15aa9c Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Tue, 13 Jun 2023 16:29:29 +0200 Subject: [PATCH 15/15] refactor: IntoCID to ToCID --- tests/t0114_gateway_subdomains_test.go | 4 ++-- tooling/ipns/ipns_test.go | 6 +++--- tooling/ipns/record.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index eebe1af29..5713d6c7b 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -349,7 +349,7 @@ func TestGatewaySubdomainAndIPNS(t *testing.T) { { Name: "request for {CIDv1-dag-pb}.ipns.{gateway} redirects to CID with libp2p-key multicodec", Request: Request(). - URL("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, record.IntoCID(multicodec.DagPb, multibase.Base36), u.Host), + URL("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, record.ToCID(multicodec.DagPb, multibase.Base36), u.Host), Response: Expect(). Status(301). Headers( @@ -460,7 +460,7 @@ func TestGatewaySubdomainAndIPNS(t *testing.T) { Response: Expect(). Headers( Header("Location"). - Equals("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, ed25519Fixture.IntoCID(multicodec.Libp2pKey, multibase.Base36), u.Host), + Equals("{{scheme}}://{{cid}}.ipns.{{host}}/", u.Scheme, ed25519Fixture.ToCID(multicodec.Libp2pKey, multibase.Base36), u.Host), ), }, }...) diff --git a/tooling/ipns/ipns_test.go b/tooling/ipns/ipns_test.go index b96c73ce8..f84265d8b 100644 --- a/tooling/ipns/ipns_test.go +++ b/tooling/ipns/ipns_test.go @@ -75,9 +75,9 @@ func TestIPNSFixtureVersionsConversion(t *testing.T) { assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.Key()) assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.IdV0()) assert.Equal(t, "k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam", record.IdV1()) - assert.Equal(t, "k50rm9yjlt0jey4fqg6wafvqprktgbkpgkqdg27tpqje6iimzxewnhvtin9hhq", record.IntoCID(multicodec.DagPb, mbase.Base36)) + assert.Equal(t, "k50rm9yjlt0jey4fqg6wafvqprktgbkpgkqdg27tpqje6iimzxewnhvtin9hhq", record.ToCID(multicodec.DagPb, mbase.Base36)) assert.Equal(t, "12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d", record.B58MH()) - assert.Equal(t, "k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam", record.IntoCID(multicodec.Libp2pKey, mbase.Base36)) + assert.Equal(t, "k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam", record.ToCID(multicodec.Libp2pKey, mbase.Base36)) path = "./_fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record" record, err = OpenIPNSRecordWithKey(path) @@ -88,6 +88,6 @@ func TestIPNSFixtureVersionsConversion(t *testing.T) { assert.Equal(t, "QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3", record.Key()) assert.Equal(t, "QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3", record.IdV0()) assert.Equal(t, "k2k4r8m7xvggw5pxxk3abrkwyer625hg01hfyggrai7lk1m63fuihi7w", record.IdV1()) - assert.Equal(t, "k2jmtxu61bnhrtj301lw7zizknztocdbeqhxgv76l2q9t36fn9jbzipo", record.IntoCID(multicodec.DagPb, mbase.Base36)) + assert.Equal(t, "k2jmtxu61bnhrtj301lw7zizknztocdbeqhxgv76l2q9t36fn9jbzipo", record.ToCID(multicodec.DagPb, mbase.Base36)) assert.Equal(t, "QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3", record.B58MH()) } diff --git a/tooling/ipns/record.go b/tooling/ipns/record.go index 708149193..31ed5f059 100644 --- a/tooling/ipns/record.go +++ b/tooling/ipns/record.go @@ -64,7 +64,7 @@ func (i *IpnsRecord) idV1(codec multicodec.Code, base mbase.Encoding) (string, e return s, nil } -func (i *IpnsRecord) IntoCID(codec multicodec.Code, base mbase.Encoding) string { +func (i *IpnsRecord) ToCID(codec multicodec.Code, base mbase.Encoding) string { s, err := i.idV1(codec, base) if err != nil { panic(err) @@ -81,7 +81,7 @@ func (i *IpnsRecord) IdV0() string { } func (i *IpnsRecord) IdV1() string { - return i.IntoCID(cid.Libp2pKey, mbase.Base36) + return i.ToCID(cid.Libp2pKey, mbase.Base36) } func (i *IpnsRecord) B58MH() string {