mirror of
https://github.com/ipfs/kubo.git
synced 2026-03-10 10:47:51 +08:00
refactor: support percent-encoded /unix paths
This is a PoC that aims to support https://github.com/multiformats/multiaddr/pull/174 while not breaking existing Kubo users. See TODO in daemon.go – likely we want to move this to https://github.com/multiformats/go-multiaddr
This commit is contained in:
parent
df2d1c77ae
commit
d57e6bb77a
@ -18,6 +18,7 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
legacy "github.com/ipfs/go-ipld-legacy"
|
||||
ipfs "github.com/ipfs/kubo"
|
||||
daemon "github.com/ipfs/kubo/cmd/ipfs/kubo"
|
||||
iface "github.com/ipfs/kubo/core/coreiface"
|
||||
caopts "github.com/ipfs/kubo/core/coreiface/options"
|
||||
"github.com/ipfs/kubo/misc/fsutil"
|
||||
@ -109,6 +110,7 @@ func NewApi(a ma.Multiaddr) (*HttpApi, error) {
|
||||
return nil, err
|
||||
}
|
||||
if network == "unix" {
|
||||
address = daemon.NormalizeUnixMultiaddr(address)
|
||||
transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return net.Dial("unix", address)
|
||||
}
|
||||
|
||||
@ -9,7 +9,9 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
@ -704,6 +706,24 @@ take effect.
|
||||
return errs
|
||||
}
|
||||
|
||||
// TODO: should a version of this live in https://github.com/multiformats/go-multiaddr
|
||||
// so we dont need to duplicate code here and in client/rpc/api.go ?
|
||||
func NormalizeUnixMultiaddr(address string) string {
|
||||
// Support legacy and modern /unix addrs
|
||||
// https://github.com/multiformats/multiaddr/pull/174
|
||||
socketPath, err := url.PathUnescape(address)
|
||||
if err != nil {
|
||||
return address // nil, fmt.Errorf("failed to unescape /unix socket path: %w", err)
|
||||
}
|
||||
// Ensure the path is absolute
|
||||
if !strings.HasPrefix(socketPath, string(filepath.Separator)) {
|
||||
socketPath = string(filepath.Separator) + socketPath
|
||||
}
|
||||
// Normalize path
|
||||
socketPath = filepath.Clean(socketPath)
|
||||
return socketPath
|
||||
}
|
||||
|
||||
// serveHTTPApi collects options, creates listener, prints status message and starts serving requests.
|
||||
func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) {
|
||||
cfg, err := cctx.GetConfig()
|
||||
@ -730,6 +750,9 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
|
||||
}
|
||||
|
||||
for _, addr := range apiAddrs {
|
||||
if strings.HasPrefix(addr, "/unix/") {
|
||||
addr = NormalizeUnixMultiaddr(addr)
|
||||
}
|
||||
apiMaddr, err := ma.NewMultiaddr(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serveHTTPApi: invalid API address: %q (err: %s)", addr, err)
|
||||
@ -919,6 +942,9 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
|
||||
|
||||
gatewayAddrs := cfg.Addresses.Gateway
|
||||
for _, addr := range gatewayAddrs {
|
||||
if strings.HasPrefix(addr, "/unix/") {
|
||||
addr = NormalizeUnixMultiaddr(addr)
|
||||
}
|
||||
gatewayMaddr, err := ma.NewMultiaddr(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("serveHTTPGateway: invalid gateway address: %q (err: %s)", addr, err)
|
||||
|
||||
@ -249,7 +249,7 @@ the local [Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/) (`/api/v0`)
|
||||
Supported Transports:
|
||||
|
||||
* tcp/ip{4,6} - `/ipN/.../tcp/...`
|
||||
* unix - `/unix/path/to/socket`
|
||||
* unix - `/unix/path/to/socket` or `/unix/path%2Fto%2Fsocket`
|
||||
|
||||
> [!CAUTION]
|
||||
> **NEVER EXPOSE UNPROTECTED ADMIN RPC TO LAN OR THE PUBLIC INTERNET**
|
||||
@ -276,7 +276,7 @@ the local [HTTP gateway](https://specs.ipfs.tech/http-gateways/) (`/ipfs`, `/ipn
|
||||
Supported Transports:
|
||||
|
||||
* tcp/ip{4,6} - `/ipN/.../tcp/...`
|
||||
* unix - `/unix/path/to/socket`
|
||||
* unix - `/unix/path/to/socket` or `/unix/path%2Fto%2Fsocket`
|
||||
|
||||
Default: `/ip4/127.0.0.1/tcp/8080`
|
||||
|
||||
|
||||
@ -2,7 +2,10 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
rpcapi "github.com/ipfs/kubo/client/rpc"
|
||||
@ -13,39 +16,72 @@ import (
|
||||
)
|
||||
|
||||
func TestRPCUnixSocket(t *testing.T) {
|
||||
node := harness.NewT(t).NewNode().Init()
|
||||
t.Parallel()
|
||||
|
||||
sockDir := node.Dir
|
||||
sockAddr := path.Join("/unix", sockDir, "sock")
|
||||
|
||||
node.UpdateConfig(func(cfg *config.Config) {
|
||||
//cfg.Addresses.API = append(cfg.Addresses.API, sockPath)
|
||||
cfg.Addresses.API = []string{sockAddr}
|
||||
})
|
||||
t.Log("Starting daemon with unix socket:", sockAddr)
|
||||
node.StartDaemon()
|
||||
|
||||
unixMaddr, err := multiaddr.NewMultiaddr(sockAddr)
|
||||
require.NoError(t, err)
|
||||
|
||||
apiClient, err := rpcapi.NewApi(unixMaddr)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ver struct {
|
||||
Version string
|
||||
testCases := []struct {
|
||||
name string
|
||||
getSockMultiaddr func(sockPath string) (unixMultiaddr string)
|
||||
}{
|
||||
{
|
||||
name: "Legacy /unix: unescaped socket path",
|
||||
getSockMultiaddr: func(sockDir string) string {
|
||||
return path.Join("/unix", sockDir, "sock")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Spec-compliant /unix: percent-encoded socket path without leading slash",
|
||||
getSockMultiaddr: func(sockDir string) string {
|
||||
sockPath := path.Join(sockDir, "sock")
|
||||
pathWithoutLeadingSlash := strings.TrimPrefix(sockPath, string(filepath.Separator))
|
||||
escapedPath := url.PathEscape(pathWithoutLeadingSlash)
|
||||
return path.Join("/unix", escapedPath)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Spec-compliant /unix: percent-encoded socket path with leading slash",
|
||||
getSockMultiaddr: func(sockDir string) string {
|
||||
sockPath := path.Join(sockDir, "sock")
|
||||
escapedPath := url.PathEscape(sockPath)
|
||||
return path.Join("/unix", escapedPath)
|
||||
},
|
||||
},
|
||||
}
|
||||
err = apiClient.Request("version").Exec(context.Background(), &ver)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, ver)
|
||||
t.Log("Got version:", ver.Version)
|
||||
|
||||
var res struct {
|
||||
ID string
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
node := harness.NewT(t).NewNode().Init()
|
||||
sockDir := node.Dir
|
||||
sockAddr := tc.getSockMultiaddr(sockDir)
|
||||
node.UpdateConfig(func(cfg *config.Config) {
|
||||
//cfg.Addresses.API = append(cfg.Addresses.API, sockPath)
|
||||
cfg.Addresses.API = []string{sockAddr}
|
||||
})
|
||||
t.Log("Starting daemon with unix socket:", sockAddr)
|
||||
node.StartDaemon()
|
||||
|
||||
unixMaddr, err := multiaddr.NewMultiaddr(sockAddr)
|
||||
require.NoError(t, err)
|
||||
|
||||
apiClient, err := rpcapi.NewApi(unixMaddr)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ver struct {
|
||||
Version string
|
||||
}
|
||||
err = apiClient.Request("version").Exec(context.Background(), &ver)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, ver)
|
||||
t.Log("Got version:", ver.Version)
|
||||
|
||||
var res struct {
|
||||
ID string
|
||||
}
|
||||
err = apiClient.Request("id").Exec(context.Background(), &res)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, res)
|
||||
t.Log("Got ID:", res.ID)
|
||||
|
||||
node.StopDaemon()
|
||||
})
|
||||
}
|
||||
err = apiClient.Request("id").Exec(context.Background(), &res)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, res)
|
||||
t.Log("Got ID:", res.ID)
|
||||
|
||||
node.StopDaemon()
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user