Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gateway: harden path prefix #1988

Merged
merged 1 commit into from
Apr 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
corehttp.CommandsROOption(*req.InvocContext()),
corehttp.VersionOption(),
corehttp.IPNSHostnameOption(),
corehttp.GatewayOption(writable),
corehttp.GatewayOption(writable, cfg.Gateway.PathPrefixes),
}

if len(cfg.Gateway.RootRedirect) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion cmd/ipfswatch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func run(ipfsPath, watchPath string) error {
if *http {
addr := "/ip4/127.0.0.1/tcp/5001"
var opts = []corehttp.ServeOption{
corehttp.GatewayOption(true),
corehttp.GatewayOption(true, nil),
corehttp.WebUIOption,
corehttp.CommandsOption(cmdCtx(node, ipfsPath)),
}
Expand Down
14 changes: 8 additions & 6 deletions core/corehttp/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ type Gateway struct {
}

type GatewayConfig struct {
Headers map[string][]string
BlockList *BlockList
Writable bool
Headers map[string][]string
BlockList *BlockList
Writable bool
PathPrefixes []string
}

func NewGateway(conf GatewayConfig) *Gateway {
Expand Down Expand Up @@ -48,10 +49,11 @@ func (g *Gateway) ServeOption() ServeOption {
}
}

func GatewayOption(writable bool) ServeOption {
func GatewayOption(writable bool, prefixes []string) ServeOption {
g := NewGateway(GatewayConfig{
Writable: writable,
BlockList: &BlockList{},
Writable: writable,
BlockList: &BlockList{},
PathPrefixes: prefixes,
})
return g.ServeOption()
}
Expand Down
9 changes: 7 additions & 2 deletions core/corehttp/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,13 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
// It will be prepended to links in directory listings and the index.html redirect.
prefix := ""
if prefixHdr := r.Header["X-Ipfs-Gateway-Prefix"]; len(prefixHdr) > 0 {
log.Debugf("X-Ipfs-Gateway-Prefix: %s", prefixHdr[0])
prefix = prefixHdr[0]
prfx := prefixHdr[0]
for _, p := range i.config.PathPrefixes {
if prfx == p || strings.HasPrefix(prfx, p+"/") {
prefix = prfx
break
}
}
}

// IPNSHostnameOption might have constructed an IPNS path using the Host header.
Expand Down
60 changes: 52 additions & 8 deletions core/corehttp/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, *core
ts.Listener,
VersionOption(),
IPNSHostnameOption(),
GatewayOption(false),
GatewayOption(false, []string{"/good-prefix"}),
)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -227,7 +227,7 @@ func TestIPNSHostnameRedirect(t *testing.T) {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")
req.Header.Set("X-Ipfs-Gateway-Prefix", "/good-prefix")

res, err = doWithoutRedirect(req)
if err != nil {
Expand All @@ -241,8 +241,8 @@ func TestIPNSHostnameRedirect(t *testing.T) {
hdr = res.Header["Location"]
if len(hdr) < 1 {
t.Errorf("location header not present")
} else if hdr[0] != "/prefix/foo/" {
t.Errorf("location header is %v, expected /prefix/foo/", hdr[0])
} else if hdr[0] != "/good-prefix/foo/" {
t.Errorf("location header is %v, expected /good-prefix/foo/", hdr[0])
}
}

Expand Down Expand Up @@ -387,7 +387,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")
req.Header.Set("X-Ipfs-Gateway-Prefix", "/good-prefix")

res, err = doWithoutRedirect(req)
if err != nil {
Expand All @@ -402,13 +402,57 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
s = string(body)
t.Logf("body: %s\n", string(body))

if !strings.Contains(s, "Index of /prefix") {
if !strings.Contains(s, "Index of /good-prefix") {
t.Fatalf("expected a path in directory listing")
}
if !strings.Contains(s, "<a href=\"/prefix/\">") {
if !strings.Contains(s, "<a href=\"/good-prefix/\">") {
t.Fatalf("expected backlink in directory listing")
}
if !strings.Contains(s, "<a href=\"/prefix/file.txt\">") {
if !strings.Contains(s, "<a href=\"/good-prefix/file.txt\">") {
t.Fatalf("expected file in directory listing")
}

// make request to directory listing with illegal prefix
req, err = http.NewRequest("GET", ts.URL, nil)
if err != nil {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "/bad-prefix")

res, err = doWithoutRedirect(req)
if err != nil {
t.Fatal(err)
}

// make request to directory listing with evil prefix
req, err = http.NewRequest("GET", ts.URL, nil)
if err != nil {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "//good-prefix/foo")

res, err = doWithoutRedirect(req)
if err != nil {
t.Fatal(err)
}

// expect correct backlinks without illegal prefix
body, err = ioutil.ReadAll(res.Body)
if err != nil {
t.Fatalf("error reading response: %s", err)
}
s = string(body)
t.Logf("body: %s\n", string(body))

if !strings.Contains(s, "Index of /") {
t.Fatalf("expected a path in directory listing")
}
if !strings.Contains(s, "<a href=\"/\">") {
t.Fatalf("expected backlink in directory listing")
}
if !strings.Contains(s, "<a href=\"/file.txt\">") {
t.Fatalf("expected file in directory listing")
}
}
Expand Down
1 change: 1 addition & 0 deletions repo/config/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ type Gateway struct {
HTTPHeaders map[string][]string // HTTP headers to return with the gateway
RootRedirect string
Writable bool
PathPrefixes []string
}
1 change: 1 addition & 0 deletions repo/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func Init(out io.Writer, nBitsForKeypair int) (*Config, error) {
Gateway: Gateway{
RootRedirect: "",
Writable: false,
PathPrefixes: []string{},
},
}

Expand Down
2 changes: 1 addition & 1 deletion test/supernode_client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func run() error {

opts := []corehttp.ServeOption{
corehttp.CommandsOption(cmdCtx(node, repoPath)),
corehttp.GatewayOption(false),
corehttp.GatewayOption(false, nil),
}

if *cat {
Expand Down