Skip to content

Commit

Permalink
core/corehttp!: remove /api/v0 from gateway port
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias authored Mar 5, 2024
1 parent d77a9e6 commit e22f47a
Show file tree
Hide file tree
Showing 13 changed files with 12 additions and 367 deletions.
2 changes: 0 additions & 2 deletions cmd/ipfs/kubo/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -850,8 +850,6 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
corehttp.GatewayOption("/ipfs", "/ipns"),
corehttp.VersionOption(),
corehttp.CheckVersionOption(),
// TODO[api-on-gw]: remove for 0.28.0: https://github.com/ipfs/kubo/issues/10312
corehttp.CommandsROOption(cmdctx),
}

if cfg.Experimental.P2pHttpProxy {
Expand Down
2 changes: 1 addition & 1 deletion config/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const (

type GatewaySpec struct {
// Paths is explicit list of path prefixes that should be handled by
// this gateway. Example: `["/ipfs", "/ipns", "/api"]`
// this gateway. Example: `["/ipfs", "/ipns"]`
Paths []string

// UseSubdomains indicates whether or not this gateway uses subdomains
Expand Down
57 changes: 0 additions & 57 deletions core/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,63 +15,6 @@ func collectPaths(prefix string, cmd *cmds.Command, out map[string]struct{}) {
}
}

func TestROCommands(t *testing.T) {
list := []string{
"/block",
"/block/get",
"/block/stat",
"/cat",
"/commands",
"/commands/completion",
"/commands/completion/bash",
"/commands/completion/fish",
"/commands/completion/zsh",
"/dag",
"/dag/get",
"/dag/resolve",
"/dag/stat",
"/dag/export",
"/get",
"/ls",
"/name",
"/name/resolve",
"/object",
"/object/data",
"/object/get",
"/object/links",
"/object/stat",
"/refs",
"/resolve",
"/version",
}

cmdSet := make(map[string]struct{})
collectPaths("", RootRO, cmdSet)

for _, path := range list {
if _, ok := cmdSet[path]; !ok {
t.Errorf("%q not in result", path)
} else {
delete(cmdSet, path)
}
}

for path := range cmdSet {
t.Errorf("%q in result but shouldn't be", path)
}

for _, path := range list {
path = path[1:] // remove leading slash
split := strings.Split(path, "/")
sub, err := RootRO.Get(split)
if err != nil {
t.Errorf("error getting subcommand %q: %v", path, err)
} else if sub == nil {
t.Errorf("subcommand %q is nil even though there was no error", path)
}
}
}

func TestCommands(t *testing.T) {
list := []string{
"/add",
Expand Down
63 changes: 0 additions & 63 deletions core/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,72 +162,9 @@ var rootSubcommands = map[string]*cmds.Command{
"multibase": MbaseCmd,
}

// RootRO is the readonly version of Root
var RootRO = &cmds.Command{}

var CommandsDaemonROCmd = CommandsCmd(RootRO)

// RefsROCmd is `ipfs refs` command
var RefsROCmd = &cmds.Command{}

// VersionROCmd is `ipfs version` command (without deps).
var VersionROCmd = &cmds.Command{}

var rootROSubcommands = map[string]*cmds.Command{
"commands": CommandsDaemonROCmd,
"cat": CatCmd,
"block": {
Subcommands: map[string]*cmds.Command{
"stat": blockStatCmd,
"get": blockGetCmd,
},
},
"get": GetCmd,
"ls": LsCmd,
"name": {
Subcommands: map[string]*cmds.Command{
"resolve": name.IpnsCmd,
},
},
"object": {
Subcommands: map[string]*cmds.Command{
"data": ocmd.ObjectDataCmd,
"links": ocmd.ObjectLinksCmd,
"get": ocmd.ObjectGetCmd,
"stat": ocmd.ObjectStatCmd,
},
},
"dag": {
Subcommands: map[string]*cmds.Command{
"get": dag.DagGetCmd,
"resolve": dag.DagResolveCmd,
"stat": dag.DagStatCmd,
"export": dag.DagExportCmd,
},
},
"resolve": ResolveCmd,
}

func init() {
Root.ProcessHelp()
*RootRO = *Root

// this was in the big map definition above before,
// but if we leave it there lgc.NewCommand will be executed
// before the value is updated (:/sanitize readonly refs command/)

// sanitize readonly refs command
*RefsROCmd = *RefsCmd
RefsROCmd.Subcommands = map[string]*cmds.Command{}
rootROSubcommands["refs"] = RefsROCmd

// sanitize readonly version command (no need to expose precise deps)
*VersionROCmd = *VersionCmd
VersionROCmd.Subcommands = map[string]*cmds.Command{}
rootROSubcommands["version"] = VersionROCmd

Root.Subcommands = rootSubcommands
RootRO.Subcommands = rootROSubcommands
}

type MessageOutput struct {
Expand Down
1 change: 0 additions & 1 deletion core/commands/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ func TestCommandTree(t *testing.T) {
}
}
printErrors(Root.DebugValidate())
printErrors(RootRO.DebugValidate())
}
22 changes: 2 additions & 20 deletions core/corehttp/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"strconv"
"strings"

"github.com/ipfs/boxo/gateway"
cmds "github.com/ipfs/go-ipfs-cmds"
cmdsHttp "github.com/ipfs/go-ipfs-cmds/http"
version "github.com/ipfs/kubo"
Expand Down Expand Up @@ -122,14 +121,10 @@ func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) {
c.SetAllowedOrigins(newOrigins...)
}

func commandsOption(cctx oldcmds.Context, command *cmds.Command, allowGet bool) ServeOption {
func commandsOption(cctx oldcmds.Context, command *cmds.Command) ServeOption {
return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
cfg := cmdsHttp.NewServerConfig()
cfg.AllowGet = allowGet
corsAllowedMethods := []string{http.MethodPost}
if allowGet {
corsAllowedMethods = append(corsAllowedMethods, http.MethodGet)
}

cfg.SetAllowedMethods(corsAllowedMethods...)
cfg.APIPath = APIPath
Expand All @@ -150,13 +145,6 @@ func commandsOption(cctx oldcmds.Context, command *cmds.Command, allowGet bool)
cmdHandler = withAuthSecrets(authorizations, cmdHandler)
}

// TODO[api-on-gw]: remove for Kubo 0.28
if command == corecommands.RootRO && allowGet {
cmdHandler = gateway.NewHeaders(map[string][]string{
"Link": {`<https://github.com/ipfs/kubo/issues/10312>; rel="deprecation"; type="text/html"`},
}).Wrap(cmdHandler)
}

cmdHandler = otelhttp.NewHandler(cmdHandler, "corehttp.cmdsHandler")
mux.Handle(APIPath+"/", cmdHandler)
return mux, nil
Expand Down Expand Up @@ -211,13 +199,7 @@ func withAuthSecrets(authorizations map[string]rpcAuthScopeWithUser, next http.H
// CommandsOption constructs a ServerOption for hooking the commands into the
// HTTP server. It will NOT allow GET requests.
func CommandsOption(cctx oldcmds.Context) ServeOption {
return commandsOption(cctx, corecommands.Root, false)
}

// CommandsROOption constructs a ServerOption for hooking the read-only commands
// into the HTTP server. It will allow GET requests.
func CommandsROOption(cctx oldcmds.Context) ServeOption {
return commandsOption(cctx, corecommands.RootRO, true)
return commandsOption(cctx, corecommands.Root)
}

// CheckVersionOption returns a ServeOption that checks whether the client ipfs version matches. Does nothing when the user agent string does not contain `/kubo/` or `/go-ipfs/`
Expand Down
2 changes: 1 addition & 1 deletion core/corehttp/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func (o *offlineGatewayErrWrapper) GetDNSLinkRecord(ctx context.Context, s strin

var _ gateway.IPFSBackend = (*offlineGatewayErrWrapper)(nil)

var defaultPaths = []string{"/ipfs/", "/ipns/", "/api/", "/p2p/"}
var defaultPaths = []string{"/ipfs/", "/ipns/", "/p2p/"}

var subdomainGatewaySpec = &gateway.PublicGateway{
Paths: defaultPaths,
Expand Down
9 changes: 7 additions & 2 deletions docs/changelogs/v0.28.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@
- [Overview](#overview)
- [🔦 Highlights](#-highlights)
- [RPC client: removed deprecated DHT API](#rpc-client-removed-deprecated-dht-api)
- [Gateway: `/api/v0` is removed](#gateway-apiv0-is-removed)
- [📝 Changelog](#-changelog)
- [👨‍👩‍👧‍👦 Contributors](#-contributors)

### Overview

### 🔦 Highlights

#### RPC client: removed deprecated DHT API

The deprecated DHT API commands in the RPC client have been removed. Instead, use the Routing API.

#### Gateway: `/api/v0` is removed

The legacy subset of the Kubo RPC that was available via the Gateway port and was deprecated is now completely removed. You can read more in <https://github.com/ipfs/kubo/issues/10312>.

If you have a legacy software that relies on this behavior, and want to expose parts of `/api/v0` next to `/ipfs`, use reverse-proxy in front of Kubo to mount both Gateway and RPC on the same port. NOTE: exposing RPC to the internet comes with security risk: make sure to specify access control via [API.Authorizations](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations).

### 📝 Changelog

### 👨‍👩‍👧‍👦 Contributors
9 changes: 0 additions & 9 deletions docs/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,3 @@ Right now only 'full DAG' implicit selector is implemented.
Support for user-provided IPLD selectors is tracked in https://github.com/ipfs/kubo/issues/8769.

This is a rough equivalent of `ipfs dag export`.

## Deprecated Subset of RPC API

For legacy reasons, some gateways may expose a small subset of RPC API under `/api/v0/`.
While this read-only API exposes a read-only, "safe" subset of the normal API,
it is deprecated and should not be used for greenfield projects.

Where possible, leverage `/ipfs/` and `/ipns/` endpoints.
along with `application/vnd.ipld.*` Content-Types instead.
71 changes: 0 additions & 71 deletions test/cli/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (

"github.com/ipfs/kubo/config"
"github.com/ipfs/kubo/test/cli/harness"
. "github.com/ipfs/kubo/test/cli/testutils"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
Expand Down Expand Up @@ -344,76 +343,6 @@ func TestGateway(t *testing.T) {
})
})

t.Run("readonly API", func(t *testing.T) {
t.Parallel()

client := node.GatewayClient()

fileContents := "12345"
h.WriteFile("readonly/dir/test", fileContents)
cids := node.IPFS("add", "-r", "-q", filepath.Join(h.Dir, "readonly/dir")).Stdout.Lines()

rootCID := cids[len(cids)-1]
client.TemplateData = map[string]string{"RootCID": rootCID}

t.Run("Get IPFS directory file through readonly API succeeds", func(t *testing.T) {
t.Parallel()
resp := client.Get("/api/v0/cat?arg={{.RootCID}}/test")
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, fileContents, resp.Body)
})

t.Run("refs IPFS directory file through readonly API succeeds", func(t *testing.T) {
t.Parallel()
resp := client.Get("/api/v0/refs?arg={{.RootCID}}/test")
assert.Equal(t, 200, resp.StatusCode)
})

t.Run("test gateway API is sanitized", func(t *testing.T) {
t.Parallel()
for _, cmd := range []string{
"add",
"block/put",
"bootstrap",
"config",
"dag/put",
"dag/import",
"dht",
"diag",
"id",
"mount",
"name/publish",
"object/put",
"object/new",
"object/patch",
"pin",
"ping",
"repo",
"stats",
"swarm",
"file",
"update",
"bitswap",
} {
t.Run(cmd, func(t *testing.T) {
cmd := cmd
t.Parallel()
assert.Equal(t, 404, client.Get("/api/v0/"+cmd).StatusCode)
})
}
})
})

t.Run("refs/local", func(t *testing.T) {
t.Parallel()
gatewayAddr := URLStrToMultiaddr(node.GatewayURL())
res := node.RunIPFS("--api", gatewayAddr.String(), "refs", "local")
assert.Contains(t,
res.Stderr.Trimmed(),
`Error: invalid path "local":`,
)
})

t.Run("raw leaves node", func(t *testing.T) {
t.Parallel()
contents := "This is RAW!"
Expand Down
2 changes: 1 addition & 1 deletion test/sharness/t0002-docker-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test_expect_success "docker image runs" '
'

test_expect_success "docker container gateway is up" '
pollEndpoint -host=/ip4/127.0.0.1/tcp/8080 -http-url http://localhost:8080/api/v0/version -v -tries 30 -tout 1s
pollEndpoint -host=/ip4/127.0.0.1/tcp/8080 -http-url http://localhost:8080/ipfs/bafkqaddimvwgy3zao5xxe3debi -v -tries 30 -tout 1s
'

test_expect_success "docker container API is up" '
Expand Down
Loading

0 comments on commit e22f47a

Please sign in to comment.