diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index ba5d65746669..25dbcc079612 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -281,7 +281,7 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) { var opts = []corehttp.ServeOption{ corehttp.CommandsOption(*req.Context()), corehttp.WebUIOption, - apiGw.ServeOption(), + apiGw.ServeOption(nil), corehttp.VersionOption(), defaultMux("/debug/vars"), defaultMux("/debug/pprof/"), @@ -339,7 +339,7 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) { var opts = []corehttp.ServeOption{ corehttp.VersionOption(), corehttp.IPNSHostnameOption(), - corehttp.GatewayOption(writable), + corehttp.GatewayOption(writable, req.Context()), } if len(cfg.Gateway.RootRedirect) > 0 { diff --git a/commands/http/handler.go b/commands/http/handler.go index 774c5ca4a623..418ce25bdf59 100644 --- a/commands/http/handler.go +++ b/commands/http/handler.go @@ -12,6 +12,7 @@ import ( context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" cmds "github.com/ipfs/go-ipfs/commands" + commands "github.com/ipfs/go-ipfs/core/commands" u "github.com/ipfs/go-ipfs/util" ) @@ -21,6 +22,7 @@ var log = u.Logger("commands/http") type internalHandler struct { ctx cmds.Context root *cmds.Command + readOnly bool } // The Handler struct is funny because we want to wrap our internal handler @@ -47,6 +49,10 @@ var mimeTypes = map[string]string{ cmds.Text: "text/plain", } +var readOnlyCmds = map[*cmds.Command]bool{ + commands.RefsCmd: true, +} + func NewHandler(ctx cmds.Context, root *cmds.Command, allowedOrigin string) *Handler { // allow whitelisted origins (so we can make API requests from the browser) if len(allowedOrigin) > 0 { @@ -54,7 +60,7 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, allowedOrigin string) *Han } // Create a handler for the API. - internal := internalHandler{ctx, root} + internal := internalHandler{ctx, root, false} // Create a CORS object for wrapping the internal handler. c := cors.New(cors.Options{ @@ -71,6 +77,32 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, allowedOrigin string) *Han return &Handler{internal, c.Handler(internal)} } +func NewReadOnlyHandler(ctx cmds.Context, root *cmds.Command, allowedOrigin string) *Handler { + // allow whitelisted origins (so we can make API requests from the browser) + if len(allowedOrigin) > 0 { + log.Info("Allowing API requests from origin: " + allowedOrigin) + } + + // Create a handler for the API. + internal := internalHandler{ctx, root, true} + + // Create a CORS object for wrapping the internal handler. + c := cors.New(cors.Options{ + AllowedMethods: []string{"GET"}, + + // use AllowOriginFunc instead of AllowedOrigins because we want to be + // restrictive by default. + AllowOriginFunc: func(origin string) bool { + return (allowedOrigin == "*") || (origin == allowedOrigin) + }, + }) + + fmt.Println("Read Only API") + + // Wrap the internal handler with CORS handling-middleware. + return &Handler{internal, c.Handler(internal)} +} + func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { log.Debug("Incoming API request: ", r.URL) @@ -89,6 +121,7 @@ func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } req, err := Parse(r, i.root) + if err != nil { if err == ErrNotFound { w.WriteHeader(http.StatusNotFound) @@ -99,6 +132,15 @@ func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + if i.readOnly == true { + if _, ok := readOnlyCmds[req.Command()]; !ok { + // Or a 404? + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("You may not execute this request on the read-only api.")) + return + } + } + // get the node's context to pass into the commands. node, err := i.ctx.GetNode() if err != nil { diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go index 0a84178b8c0e..260aea2ec978 100644 --- a/core/corehttp/gateway.go +++ b/core/corehttp/gateway.go @@ -4,7 +4,11 @@ import ( "fmt" "net/http" "sync" + "os" + commands "github.com/ipfs/go-ipfs/commands" + cmdsHttp "github.com/ipfs/go-ipfs/commands/http" + corecommands "github.com/ipfs/go-ipfs/core/commands" core "github.com/ipfs/go-ipfs/core" id "github.com/ipfs/go-ipfs/p2p/protocol/identify" ) @@ -25,7 +29,7 @@ func NewGateway(conf GatewayConfig) *Gateway { } } -func (g *Gateway) ServeOption() ServeOption { +func (g *Gateway) ServeOption(cctx * commands.Context) ServeOption { return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { gateway, err := newGatewayHandler(n, g.Config) if err != nil { @@ -33,18 +37,26 @@ func (g *Gateway) ServeOption() ServeOption { } mux.Handle("/ipfs/", gateway) mux.Handle("/ipns/", gateway) + + if cctx != nil { + origin := os.Getenv(originEnvKey) + cmdHandler := cmdsHttp.NewReadOnlyHandler(*cctx, corecommands.Root, origin) + mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler) + } return mux, nil } } -func GatewayOption(writable bool) ServeOption { + +func GatewayOption(writable bool, cctx * commands.Context) ServeOption { g := NewGateway(GatewayConfig{ Writable: writable, BlockList: &BlockList{}, }) - return g.ServeOption() + return g.ServeOption(cctx) } + func VersionOption() ServeOption { return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {