Skip to content

Commit 1afebc2

Browse files
author
Lars Gierth
committed
gateway: clean up its surface, and remove BlockList
This patch is in preparation for the gateway's extraction. It's interesting to trace technical debt back to its origin, understanding the circumstances in which it was introduced and built up, and then cutting it back at exactly the right places. - Clean up the gateway's surface The option builder GatewayOption() now takes only arguments which are relevant for HTTP handler muxing, i.e. the paths where the gateway should be mounted. All other configuration happens through the GatewayConfig object. - Remove BlockList I know why this was introduced in the first place, but it never ended up fulfilling that purpose. Somehow it was only ever used by the API server, not the gateway, which really doesn't make sense. It was also never wired up with CLI nor fs-repo. Eventually @krl started punching holes into it to make the Web UI accessible. - Remove --unrestricted-api This was holes being punched into BlockList too, for accessing /ipfs and /ipn on the API server. With BlockList removed and /ipfs and /ipns freely accessible, putting this option out of action is safe. With the next major release, the option can be removed for good. License: MIT Signed-off-by: Lars Gierth <[email protected]>
1 parent 868a1e3 commit 1afebc2

File tree

9 files changed

+37
-119
lines changed

9 files changed

+37
-119
lines changed

cmd/ipfs/daemon.go

+5-28
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
_ "net/http/pprof"
99
"os"
1010
"sort"
11-
"strings"
1211
"sync"
1312

1413
"gx/ipfs/QmPpRcbNUXauP3zWZ1NJMLWpe4QnmEHrd2ba2D3yqWznw7/go-multiaddr-net"
@@ -135,7 +134,7 @@ Headers.
135134
cmds.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)").Default(false),
136135
cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."),
137136
cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."),
138-
cmds.BoolOption(unrestrictedApiAccessKwd, "Allow API access to unlisted hashes").Default(false),
137+
cmds.BoolOption(unrestrictedApiAccessKwd, "This option has no effect since v0.4.3").Default(false),
139138
cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)").Default(false),
140139
cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection").Default(false),
141140
cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").Default(false),
@@ -364,33 +363,11 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) {
364363
apiMaddr = apiLis.Multiaddr()
365364
fmt.Printf("API server listening on %s\n", apiMaddr)
366365

367-
unrestricted, _, err := req.Option(unrestrictedApiAccessKwd).Bool()
368-
if err != nil {
369-
return fmt.Errorf("serveHTTPApi: Option(%s) failed: %s", unrestrictedApiAccessKwd, err), nil
370-
}
371-
372-
apiGw := corehttp.NewGateway(corehttp.GatewayConfig{
373-
Writable: true,
374-
BlockList: &corehttp.BlockList{
375-
Decider: func(s string) bool {
376-
if unrestricted {
377-
return true
378-
}
379-
// for now, only allow paths in the WebUI path
380-
for _, webuipath := range corehttp.WebUIPaths {
381-
if strings.HasPrefix(s, webuipath) {
382-
return true
383-
}
384-
}
385-
return false
386-
},
387-
},
388-
})
389366
var opts = []corehttp.ServeOption{
390367
corehttp.MetricsCollectionOption("api"),
391368
corehttp.CommandsOption(*req.InvocContext()),
392369
corehttp.WebUIOption,
393-
apiGw.ServeOption(),
370+
corehttp.GatewayOption("/ipfs", "/ipns"),
394371
corehttp.VersionOption(),
395372
defaultMux("/debug/vars"),
396373
defaultMux("/debug/pprof/"),
@@ -452,8 +429,8 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
452429
if err != nil {
453430
return fmt.Errorf("serveHTTPGateway: req.Option(%s) failed: %s", writableKwd, err), nil
454431
}
455-
if !writableOptionFound {
456-
writable = cfg.Gateway.Writable
432+
if writableOptionFound {
433+
cfg.Gateway.Writable = writable
457434
}
458435

459436
gwLis, err := manet.Listen(gatewayMaddr)
@@ -474,7 +451,7 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
474451
corehttp.CommandsROOption(*req.InvocContext()),
475452
corehttp.VersionOption(),
476453
corehttp.IPNSHostnameOption(),
477-
corehttp.GatewayOption(writable, cfg.Gateway.PathPrefixes),
454+
corehttp.GatewayOption("/ipfs", "/ipns"),
478455
}
479456

480457
if len(cfg.Gateway.RootRedirect) > 0 {

cmd/ipfswatch/main.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,16 @@ func run(ipfsPath, watchPath string) error {
8181
}
8282
defer node.Close()
8383

84+
cfg, err := node.Repo.Config()
85+
if err != nil {
86+
return err
87+
}
88+
cfg.Gateway.Writable = true
89+
8490
if *http {
8591
addr := "/ip4/127.0.0.1/tcp/5001"
8692
var opts = []corehttp.ServeOption{
87-
corehttp.GatewayOption(true, nil),
93+
corehttp.GatewayOption("/ipfs", "/ipns"),
8894
corehttp.WebUIOption,
8995
corehttp.CommandsOption(cmdCtx(node, ipfsPath)),
9096
}

core/corehttp/gateway.go

+8-60
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,38 @@ import (
44
"fmt"
55
"net"
66
"net/http"
7-
"sync"
87

98
core "github.com/ipfs/go-ipfs/core"
109
config "github.com/ipfs/go-ipfs/repo/config"
1110
id "gx/ipfs/QmdBpVuSYuTGDA8Kn66CbKvEThXqKUh2nTANZEhzSxqrmJ/go-libp2p/p2p/protocol/identify"
1211
)
1312

14-
// Gateway should be instantiated using NewGateway
15-
type Gateway struct {
16-
Config GatewayConfig
17-
}
18-
1913
type GatewayConfig struct {
2014
Headers map[string][]string
21-
BlockList *BlockList
2215
Writable bool
2316
PathPrefixes []string
2417
}
2518

26-
func NewGateway(conf GatewayConfig) *Gateway {
27-
return &Gateway{
28-
Config: conf,
29-
}
30-
}
31-
32-
func (g *Gateway) ServeOption() ServeOption {
19+
func GatewayOption(paths ...string) ServeOption {
3320
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
34-
// pass user's HTTP headers
3521
cfg, err := n.Repo.Config()
3622
if err != nil {
3723
return nil, err
3824
}
3925

40-
g.Config.Headers = cfg.Gateway.HTTPHeaders
26+
gateway := newGatewayHandler(n, GatewayConfig{
27+
Headers: cfg.Gateway.HTTPHeaders,
28+
Writable: cfg.Gateway.Writable,
29+
PathPrefixes: cfg.Gateway.PathPrefixes,
30+
})
4131

42-
gateway, err := newGatewayHandler(n, g.Config)
43-
if err != nil {
44-
return nil, err
32+
for _, p := range paths {
33+
mux.Handle(p+"/", gateway)
4534
}
46-
mux.Handle("/ipfs/", gateway)
47-
mux.Handle("/ipns/", gateway)
4835
return mux, nil
4936
}
5037
}
5138

52-
func GatewayOption(writable bool, prefixes []string) ServeOption {
53-
g := NewGateway(GatewayConfig{
54-
Writable: writable,
55-
BlockList: &BlockList{},
56-
PathPrefixes: prefixes,
57-
})
58-
return g.ServeOption()
59-
}
60-
6139
func VersionOption() ServeOption {
6240
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
6341
mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
@@ -68,33 +46,3 @@ func VersionOption() ServeOption {
6846
return mux, nil
6947
}
7048
}
71-
72-
// Decider decides whether to Allow string
73-
type Decider func(string) bool
74-
75-
type BlockList struct {
76-
mu sync.RWMutex
77-
Decider Decider
78-
}
79-
80-
func (b *BlockList) ShouldAllow(s string) bool {
81-
b.mu.RLock()
82-
d := b.Decider
83-
b.mu.RUnlock()
84-
if d == nil {
85-
return true
86-
}
87-
return d(s)
88-
}
89-
90-
// SetDecider atomically swaps the blocklist's decider. This method is
91-
// thread-safe.
92-
func (b *BlockList) SetDecider(d Decider) {
93-
b.mu.Lock()
94-
b.Decider = d
95-
b.mu.Unlock()
96-
}
97-
98-
func (b *BlockList) ShouldBlock(s string) bool {
99-
return !b.ShouldAllow(s)
100-
}

core/corehttp/gateway_handler.go

+2-8
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ type gatewayHandler struct {
3636
config GatewayConfig
3737
}
3838

39-
func newGatewayHandler(node *core.IpfsNode, conf GatewayConfig) (*gatewayHandler, error) {
39+
func newGatewayHandler(node *core.IpfsNode, conf GatewayConfig) *gatewayHandler {
4040
i := &gatewayHandler{
4141
node: node,
4242
config: conf,
4343
}
44-
return i, nil
44+
return i
4545
}
4646

4747
// TODO(cryptix): find these helpers somewhere else
@@ -152,12 +152,6 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
152152
ipnsHostname = true
153153
}
154154

155-
if i.config.BlockList != nil && i.config.BlockList.ShouldBlock(urlPath) {
156-
w.WriteHeader(http.StatusForbidden)
157-
w.Write([]byte("403 - Forbidden"))
158-
return
159-
}
160-
161155
nd, err := core.Resolve(ctx, i.node, path.Path(urlPath))
162156
// If node is in offline mode the error code and message should be different
163157
if err == core.ErrNoNamesys && !i.node.OnlineMode() {

core/corehttp/gateway_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, *core
8989
t.Fatal(err)
9090
}
9191

92+
cfg, err := n.Repo.Config()
93+
if err != nil {
94+
t.Fatal(err)
95+
}
96+
cfg.Gateway.PathPrefixes = []string{"/good-prefix"}
97+
9298
// need this variable here since we need to construct handler with
9399
// listener, and server with handler. yay cycles.
94100
dh := &delegatedHandler{}
@@ -98,7 +104,7 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, *core
98104
ts.Listener,
99105
VersionOption(),
100106
IPNSHostnameOption(),
101-
GatewayOption(false, []string{"/good-prefix"}),
107+
GatewayOption("/ipfs", "/ipns"),
102108
)
103109
if err != nil {
104110
t.Fatal(err)

misc/completion/ipfs-completion.bash

+6-6
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ _ipfs_config_show()
104104
_ipfs_daemon()
105105
{
106106
_ipfs_comp "--init --routing= --mount --writable --mount-ipfs= \
107-
--mount-ipns= --unrestricted-api --disable-transport-encryption \
107+
--mount-ipns= --disable-transport-encryption \
108108
--help"
109109
}
110110

@@ -314,7 +314,7 @@ _ipfs_resolve()
314314

315315
_ipfs_stats()
316316
{
317-
_ipfs_comp "bw --help"
317+
_ipfs_comp "bw --help"
318318
}
319319

320320
_ipfs_stats_bw()
@@ -401,17 +401,17 @@ _ipfs()
401401
{
402402
COMPREPLY=()
403403
local word="${COMP_WORDS[COMP_CWORD]}"
404-
404+
405405
case "${COMP_CWORD}" in
406-
1)
406+
1)
407407
local opts="add bitswap block bootstrap cat commands config daemon dht \
408408
diag dns file get id init log ls mount name object pin ping \
409409
refs repo stats swarm tour update version"
410410
COMPREPLY=( $(compgen -W "${opts}" -- ${word}) );;
411-
2)
411+
2)
412412
local command="${COMP_WORDS[1]}"
413413
eval "_ipfs_$command" 2> /dev/null ;;
414-
*)
414+
*)
415415
local command="${COMP_WORDS[1]}"
416416
local subcommand="${COMP_WORDS[2]}"
417417
eval "_ipfs_${command}_${subcommand}" 2> /dev/null && return

test/sharness/t0061-daemon-opts.sh

+1-10
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,11 @@ test_description="Test daemon command"
1111

1212
test_init_ipfs
1313

14-
test_launch_ipfs_daemon --unrestricted-api --disable-transport-encryption
14+
test_launch_ipfs_daemon --disable-transport-encryption
1515

1616
gwyaddr=$GWAY_ADDR
1717
apiaddr=$API_ADDR
1818

19-
test_expect_success 'api gateway should be unrestricted' '
20-
echo "hello mars :$gwyaddr :$apiaddr" >expected &&
21-
HASH=$(ipfs add -q expected) &&
22-
curl -sfo actual1 "http://$gwyaddr/ipfs/$HASH" &&
23-
curl -sfo actual2 "http://$apiaddr/ipfs/$HASH" &&
24-
test_cmp expected actual1 &&
25-
test_cmp expected actual2
26-
'
27-
2819
# Odd. this fails here, but the inverse works on t0060-daemon.
2920
test_expect_success 'transport should be unencrypted' '
3021
nc -w 1 localhost $SWARM_PORT > swarmnc < ../t0060-data/mss-ls &&

test/sharness/t0110-gateway.sh

-4
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ test_expect_success "GET IPFS path output looks good" '
3232
rm actual
3333
'
3434

35-
test_expect_success "GET IPFS path on API forbidden" '
36-
test_curl_resp_http_code "http://127.0.0.1:$apiport/ipfs/$HASH" "HTTP/1.1 403 Forbidden"
37-
'
38-
3935
test_expect_success "GET IPFS directory path succeeds" '
4036
mkdir dir &&
4137
echo "12345" >dir/test &&

test/supernode_client/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func run() error {
109109

110110
opts := []corehttp.ServeOption{
111111
corehttp.CommandsOption(cmdCtx(node, repoPath)),
112-
corehttp.GatewayOption(false, nil),
112+
corehttp.GatewayOption(),
113113
}
114114

115115
if *cat {

0 commit comments

Comments
 (0)