Skip to content

Commit c5b0428

Browse files
authored
Merge pull request #10019 from djdv/uds-client
fix(rpc): cross-platform support for /unix/ socket maddrs in Addresses.API
2 parents c8007dd + 85ab35d commit c5b0428

File tree

5 files changed

+109
-5
lines changed

5 files changed

+109
-5
lines changed

client/rpc/api.go

+23-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"net"
89
"net/http"
910
"os"
1011
"path/filepath"
@@ -98,11 +99,29 @@ func ApiAddr(ipfspath string) (ma.Multiaddr, error) {
9899

99100
// NewApi constructs HttpApi with specified endpoint.
100101
func NewApi(a ma.Multiaddr) (*HttpApi, error) {
102+
transport := &http.Transport{
103+
Proxy: http.ProxyFromEnvironment,
104+
DisableKeepAlives: true,
105+
}
106+
107+
network, address, err := manet.DialArgs(a)
108+
if err != nil {
109+
return nil, err
110+
}
111+
if network == "unix" {
112+
transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
113+
return net.Dial("unix", address)
114+
}
115+
c := &http.Client{
116+
Transport: transport,
117+
}
118+
// This will create an API client which
119+
// makes requests to `http://unix`.
120+
return NewURLApiWithClient(network, c)
121+
}
122+
101123
c := &http.Client{
102-
Transport: &http.Transport{
103-
Proxy: http.ProxyFromEnvironment,
104-
DisableKeepAlives: true,
105-
},
124+
Transport: transport,
106125
}
107126

108127
return NewApiWithClient(a, c)

cmd/ipfs/kubo/daemon.go

+5
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ control your node remotely. If you need to control the node remotely,
113113
make sure to protect the port as you would other services or database
114114
(firewall, authenticated proxy, etc), or at least set API.Authorizations.
115115
116+
If you do not want to open any ports for RPC, and only want to use
117+
kubo CLI client, it is possible to expose the RPC over Unix socket:
118+
119+
ipfs config Addresses.API /unix/var/run/kubo.socket
120+
116121
HTTP Headers
117122
118123
Kubo supports passing arbitrary headers to the RPC API and Gateway. You can

docs/changelogs/v0.30.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [AutoNAT V2 Service Introduced Alongside V1](#autonat-v2-service-introduced-alongside-v1)
1111
- [Automated `ipfs version check`](#automated-ipfs-version-check)
1212
- [Version Suffix Configuration](#version-suffix-configuration)
13+
- [`/unix/` socket support in `Addresses.API`](#unix-socket-support-in-addressesapi)
1314
- [Cleaned Up `ipfs daemon` Startup Log](#cleaned-up-ipfs-daemon-startup-log)
1415
- [📝 Changelog](#-changelog)
1516
- [👨‍👩‍👧‍👦 Contributors](#-contributors)
@@ -49,7 +50,24 @@ Defining the optional agent version suffix is now simpler. The [`Version.AgentSu
4950

5051
> [!NOTE]
5152
> Setting a custom version suffix helps with ecosystem analysis, such as Amino DHT reports published at https://stats.ipfs.network
52-
>
53+
54+
#### `/unix/` socket support in `Addresses.API`
55+
56+
This release fixes a bug which blocked users from using Unix domain sockets for [Kubo's RPC](https://docs.ipfs.tech/reference/kubo/rpc/) (instead of a local HTTP port).
57+
58+
```console
59+
$ ipfs config Addresses.API "/unix/tmp/kubo.socket"
60+
$ ipfs daemon # start with rpc socket
61+
...
62+
RPC API server listening on /unix/tmp/kubo.socket
63+
64+
$ # cli client, in different terminal can find socket via /api file
65+
$ cat $IPFS_PATH/api
66+
/unix/tmp/kubo.socket
67+
68+
$ # or have it pased via --api
69+
$ ipfs --api=/unix/tmp/kubo.socket id
70+
```
5371

5472
#### Cleaned Up `ipfs daemon` Startup Log
5573

test/cli/harness/node.go

+11
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,17 @@ func (n *Node) checkAPI(authorization string) bool {
350350
log.Debugf("node %d API addr not available yet: %s", n.ID, err.Error())
351351
return false
352352
}
353+
354+
if unixAddr, err := apiAddr.ValueForProtocol(multiaddr.P_UNIX); err == nil {
355+
parts := strings.SplitN(unixAddr, "/", 2)
356+
if len(parts) < 1 {
357+
panic("malformed unix socket address")
358+
}
359+
fileName := "/" + parts[1]
360+
_, err := os.Stat(fileName)
361+
return !errors.Is(err, fs.ErrNotExist)
362+
}
363+
353364
ip, err := apiAddr.ValueForProtocol(multiaddr.P_IP4)
354365
if err != nil {
355366
panic(err)

test/cli/rpc_unixsocket_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"path"
6+
"testing"
7+
8+
rpcapi "github.com/ipfs/kubo/client/rpc"
9+
"github.com/ipfs/kubo/config"
10+
"github.com/ipfs/kubo/test/cli/harness"
11+
"github.com/multiformats/go-multiaddr"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestRPCUnixSocket(t *testing.T) {
16+
node := harness.NewT(t).NewNode().Init()
17+
18+
sockDir := node.Dir
19+
sockAddr := path.Join("/unix", sockDir, "sock")
20+
21+
node.UpdateConfig(func(cfg *config.Config) {
22+
//cfg.Addresses.API = append(cfg.Addresses.API, sockPath)
23+
cfg.Addresses.API = []string{sockAddr}
24+
})
25+
t.Log("Starting daemon with unix socket:", sockAddr)
26+
node.StartDaemon()
27+
28+
unixMaddr, err := multiaddr.NewMultiaddr(sockAddr)
29+
require.NoError(t, err)
30+
31+
apiClient, err := rpcapi.NewApi(unixMaddr)
32+
require.NoError(t, err)
33+
34+
var ver struct {
35+
Version string
36+
}
37+
err = apiClient.Request("version").Exec(context.Background(), &ver)
38+
require.NoError(t, err)
39+
require.NotEmpty(t, ver)
40+
t.Log("Got version:", ver.Version)
41+
42+
var res struct {
43+
ID string
44+
}
45+
err = apiClient.Request("id").Exec(context.Background(), &res)
46+
require.NoError(t, err)
47+
require.NotEmpty(t, res)
48+
t.Log("Got ID:", res.ID)
49+
50+
node.StopDaemon()
51+
}

0 commit comments

Comments
 (0)