Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 21e5e32

Browse files
committedJul 29, 2015
implement access control lists for gateway
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
1 parent 681da0a commit 21e5e32

File tree

5 files changed

+141
-78
lines changed

5 files changed

+141
-78
lines changed
 

‎blocks/key/key_set.go

+53-21
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,73 @@ import (
66

77
type KeySet interface {
88
Add(Key)
9+
Has(Key) bool
910
Remove(Key)
1011
Keys() []Key
1112
}
1213

13-
type ks struct {
14-
lock sync.RWMutex
15-
data map[Key]struct{}
14+
type keySet struct {
15+
keys map[Key]struct{}
1616
}
1717

18-
func NewKeySet() KeySet {
19-
return &ks{
20-
data: make(map[Key]struct{}),
18+
func NewKeySet() *keySet {
19+
return &keySet{make(map[Key]struct{})}
20+
}
21+
22+
func (gcs *keySet) Add(k Key) {
23+
gcs.keys[k] = struct{}{}
24+
}
25+
26+
func (gcs *keySet) Has(k Key) bool {
27+
_, has := gcs.keys[k]
28+
return has
29+
}
30+
31+
func (ks *keySet) Keys() []Key {
32+
var out []Key
33+
for k, _ := range ks.keys {
34+
out = append(out, k)
2135
}
36+
return out
37+
}
38+
39+
func (ks *keySet) Remove(k Key) {
40+
delete(ks.keys, k)
2241
}
2342

24-
func (wl *ks) Add(k Key) {
25-
wl.lock.Lock()
26-
defer wl.lock.Unlock()
43+
// TODO: implement disk-backed keyset for working with massive DAGs
2744

28-
wl.data[k] = struct{}{}
45+
type threadsafe struct {
46+
lk sync.Mutex
47+
ks KeySet
2948
}
3049

31-
func (wl *ks) Remove(k Key) {
32-
wl.lock.Lock()
33-
defer wl.lock.Unlock()
50+
func Threadsafe(ks KeySet) *threadsafe {
51+
return &threadsafe{ks: ks}
52+
}
3453

35-
delete(wl.data, k)
54+
func (ts *threadsafe) Has(k Key) bool {
55+
ts.lk.Lock()
56+
out := ts.ks.Has(k)
57+
ts.lk.Unlock() //defer is slow
58+
return out
3659
}
3760

38-
func (wl *ks) Keys() []Key {
39-
wl.lock.RLock()
40-
defer wl.lock.RUnlock()
41-
keys := make([]Key, 0)
42-
for k := range wl.data {
43-
keys = append(keys, k)
44-
}
61+
func (ts *threadsafe) Remove(k Key) {
62+
ts.lk.Lock()
63+
ts.ks.Remove(k)
64+
ts.lk.Unlock() //defer is slow
65+
}
66+
67+
func (ts *threadsafe) Add(k Key) {
68+
ts.lk.Lock()
69+
ts.ks.Add(k)
70+
ts.lk.Unlock() //defer is slow
71+
}
72+
73+
func (ts *threadsafe) Keys() []Key {
74+
ts.lk.Lock()
75+
keys := ts.ks.Keys()
76+
ts.lk.Unlock() //defer is slow
4577
return keys
4678
}

‎cmd/ipfs/daemon.go

+58-17
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
package main
22

33
import (
4+
"bufio"
45
_ "expvar"
56
"fmt"
67
"net/http"
78
_ "net/http/pprof"
89
"os"
910
"sort"
10-
"strings"
1111
"sync"
1212

1313
_ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics/runtime"
1414
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
1515
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
1616

17+
key "github.com/ipfs/go-ipfs/blocks/key"
1718
cmds "github.com/ipfs/go-ipfs/commands"
1819
"github.com/ipfs/go-ipfs/core"
1920
commands "github.com/ipfs/go-ipfs/core/commands"
@@ -281,23 +282,20 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) {
281282
return fmt.Errorf("serveHTTPApi: Option(%s) failed: %s", unrestrictedApiAccessKwd, err), nil
282283
}
283284

285+
var whitelist key.KeySet
286+
if !unrestricted {
287+
whitelist = key.Threadsafe(key.NewKeySet())
288+
for _, webuipath := range corehttp.WebUIPaths {
289+
// extract the key
290+
whitelist.Add(key.B58KeyDecode(webuipath[6:]))
291+
}
292+
}
293+
284294
apiGw := corehttp.NewGateway(corehttp.GatewayConfig{
285-
Writable: true,
286-
BlockList: &corehttp.BlockList{
287-
Decider: func(s string) bool {
288-
if unrestricted {
289-
return true
290-
}
291-
// for now, only allow paths in the WebUI path
292-
for _, webuipath := range corehttp.WebUIPaths {
293-
if strings.HasPrefix(s, webuipath) {
294-
return true
295-
}
296-
}
297-
return false
298-
},
299-
},
295+
Writable: true,
296+
WhiteList: whitelist,
300297
})
298+
301299
var opts = []corehttp.ServeOption{
302300
corehttp.CommandsOption(*req.InvocContext()),
303301
corehttp.WebUIOption,
@@ -372,10 +370,35 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
372370
fmt.Printf("Gateway (readonly) server listening on %s\n", gatewayMaddr)
373371
}
374372

373+
var blacklist key.KeySet
374+
var whitelist key.KeySet
375+
if cfg.Gateway.BlackList != "" {
376+
l, err := loadKeySetFromURL(cfg.Gateway.BlackList)
377+
if err != nil {
378+
return err, nil
379+
}
380+
blacklist = l
381+
}
382+
383+
if cfg.Gateway.WhiteList != "" {
384+
l, err := loadKeySetFromURL(cfg.Gateway.WhiteList)
385+
if err != nil {
386+
return err, nil
387+
}
388+
whitelist = l
389+
}
390+
391+
gateway := corehttp.Gateway{
392+
Config: corehttp.GatewayConfig{
393+
BlackList: blacklist,
394+
WhiteList: whitelist,
395+
},
396+
}
397+
375398
var opts = []corehttp.ServeOption{
376399
corehttp.VersionOption(),
377400
corehttp.IPNSHostnameOption(),
378-
corehttp.GatewayOption(writable),
401+
gateway.ServeOption(),
379402
}
380403

381404
if len(cfg.Gateway.RootRedirect) > 0 {
@@ -395,6 +418,24 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
395418
return nil, errc
396419
}
397420

421+
func loadKeySetFromURL(url string) (key.KeySet, error) {
422+
resp, err := http.Get(url)
423+
if err != nil {
424+
return nil, err
425+
}
426+
427+
ks := key.NewKeySet()
428+
scan := bufio.NewScanner(resp.Body)
429+
for scan.Scan() {
430+
k := key.B58KeyDecode(scan.Text())
431+
if k == "" {
432+
return nil, fmt.Errorf("invalid key in set")
433+
}
434+
ks.Add(k)
435+
}
436+
return key.Threadsafe(ks), nil
437+
}
438+
398439
//collects options and opens the fuse mountpoint
399440
func mountFuse(req cmds.Request) error {
400441
cfg, err := req.InvocContext().GetConfig()

‎core/corehttp/gateway.go

+6-35
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package corehttp
33
import (
44
"fmt"
55
"net/http"
6-
"sync"
76

7+
key "github.com/ipfs/go-ipfs/blocks/key"
88
core "github.com/ipfs/go-ipfs/core"
99
id "github.com/ipfs/go-ipfs/p2p/protocol/identify"
1010
)
@@ -15,8 +15,10 @@ type Gateway struct {
1515
}
1616

1717
type GatewayConfig struct {
18-
BlockList *BlockList
19-
Writable bool
18+
BlackList key.KeySet
19+
WhiteList key.KeySet
20+
21+
Writable bool
2022
}
2123

2224
func NewGateway(conf GatewayConfig) *Gateway {
@@ -39,8 +41,7 @@ func (g *Gateway) ServeOption() ServeOption {
3941

4042
func GatewayOption(writable bool) ServeOption {
4143
g := NewGateway(GatewayConfig{
42-
Writable: writable,
43-
BlockList: &BlockList{},
44+
Writable: writable,
4445
})
4546
return g.ServeOption()
4647
}
@@ -54,33 +55,3 @@ func VersionOption() ServeOption {
5455
return mux, nil
5556
}
5657
}
57-
58-
// Decider decides whether to Allow string
59-
type Decider func(string) bool
60-
61-
type BlockList struct {
62-
mu sync.RWMutex
63-
Decider Decider
64-
}
65-
66-
func (b *BlockList) ShouldAllow(s string) bool {
67-
b.mu.RLock()
68-
d := b.Decider
69-
b.mu.RUnlock()
70-
if d == nil {
71-
return true
72-
}
73-
return d(s)
74-
}
75-
76-
// SetDecider atomically swaps the blocklist's decider. This method is
77-
// thread-safe.
78-
func (b *BlockList) SetDecider(d Decider) {
79-
b.mu.Lock()
80-
b.Decider = d
81-
b.mu.Unlock()
82-
}
83-
84-
func (b *BlockList) ShouldBlock(s string) bool {
85-
return !b.ShouldAllow(s)
86-
}

‎core/corehttp/gateway_handler.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,28 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
8888

8989
urlPath := r.URL.Path
9090

91-
if i.config.BlockList != nil && i.config.BlockList.ShouldBlock(urlPath) {
92-
w.WriteHeader(http.StatusForbidden)
93-
w.Write([]byte("403 - Forbidden"))
91+
nd, err := core.Resolve(ctx, i.node, path.Path(urlPath))
92+
if err != nil {
93+
webError(w, "Path Resolve error", err, http.StatusBadRequest)
9494
return
9595
}
9696

97-
nd, err := core.Resolve(ctx, i.node, path.Path(urlPath))
97+
k, err := nd.Key()
9898
if err != nil {
99-
webError(w, "Path Resolve error", err, http.StatusBadRequest)
99+
log.Error("failed to get key from node: %s", err)
100+
webError(w, "Marshaling Error", err, http.StatusInternalServerError)
101+
return
102+
}
103+
104+
if i.config.BlackList != nil && i.config.BlackList.Has(k) {
105+
w.WriteHeader(http.StatusForbidden)
106+
w.Write([]byte("403 - Forbidden (content on blacklist)"))
107+
return
108+
}
109+
110+
if i.config.WhiteList != nil && !i.config.WhiteList.Has(k) {
111+
w.WriteHeader(http.StatusForbidden)
112+
w.Write([]byte("403 - Forbidden (content on blacklist)"))
100113
return
101114
}
102115

‎repo/config/gateway.go

+6
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,10 @@ package config
44
type Gateway struct {
55
RootRedirect string
66
Writable bool
7+
8+
// The url of a newline delimited list of keys that the gateway should not serve
9+
BlackList string
10+
11+
// The url of a newline delimited list of keys that the gateway should only serve
12+
WhiteList string
713
}

0 commit comments

Comments
 (0)
Please sign in to comment.