1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-07-02 20:32:58 +08:00

Merge pull request #1544 from ipfs/fix/allowed-origins

fix allowed origins to make webui work again
This commit is contained in:
Juan Benet
2015-08-02 08:30:01 +02:00
11 changed files with 104 additions and 40 deletions

View File

@ -3,6 +3,7 @@ package main
import ( import (
_ "expvar" _ "expvar"
"fmt" "fmt"
"net"
"net/http" "net/http"
_ "net/http/pprof" _ "net/http/pprof"
"os" "os"
@ -126,8 +127,8 @@ future version, along with this notice. Please move to setting the HTTP Headers.
// mostly useful to hook up things that register in the default muxer, // mostly useful to hook up things that register in the default muxer,
// and don't provide a convenient http.Handler entry point, such as // and don't provide a convenient http.Handler entry point, such as
// expvar and http/pprof. // expvar and http/pprof.
func defaultMux(path string) func(node *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { func defaultMux(path string) corehttp.ServeOption {
return func(node *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(node *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
mux.Handle(path, http.DefaultServeMux) mux.Handle(path, http.DefaultServeMux)
return mux, nil return mux, nil
} }

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
@ -55,13 +56,6 @@ const (
ACACredentials = "Access-Control-Allow-Credentials" ACACredentials = "Access-Control-Allow-Credentials"
) )
var localhostOrigins = []string{
"http://127.0.0.1",
"https://127.0.0.1",
"http://localhost",
"https://localhost",
}
var mimeTypes = map[string]string{ var mimeTypes = map[string]string{
cmds.JSON: "application/json", cmds.JSON: "application/json",
cmds.XML: "application/xml", cmds.XML: "application/xml",
@ -91,21 +85,7 @@ func skipAPIHeader(h string) bool {
func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handler { func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handler {
if cfg == nil { if cfg == nil {
cfg = &ServerConfig{} panic("must provide a valid ServerConfig")
}
if cfg.CORSOpts == nil {
cfg.CORSOpts = new(cors.Options)
}
// by default, use GET, PUT, POST
if cfg.CORSOpts.AllowedMethods == nil {
cfg.CORSOpts.AllowedMethods = []string{"GET", "POST", "PUT"}
}
// by default, only let 127.0.0.1 through.
if cfg.CORSOpts.AllowedOrigins == nil {
cfg.CORSOpts.AllowedOrigins = localhostOrigins
} }
// Wrap the internal handler with CORS handling-middleware. // Wrap the internal handler with CORS handling-middleware.
@ -375,6 +355,16 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
return true return true
} }
u, err := url.Parse(referer)
if err != nil {
// bad referer. but there _is_ something, so bail.
log.Debug("failed to parse referer: ", referer)
// debug because referer comes straight from the client. dont want to
// let people DOS by putting a huge referer that gets stored in log files.
return false
}
origin := u.Scheme + "://" + u.Host
// check CORS ACAOs and pretend Referer works like an origin. // check CORS ACAOs and pretend Referer works like an origin.
// this is valid for many (most?) sane uses of the API in // this is valid for many (most?) sane uses of the API in
// other applications, and will have the desired effect. // other applications, and will have the desired effect.
@ -384,7 +374,7 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
} }
// referer is allowed explicitly // referer is allowed explicitly
if o == referer { if o == origin {
return true return true
} }
} }

View File

@ -31,6 +31,7 @@ func originCfg(origins []string) *ServerConfig {
return &ServerConfig{ return &ServerConfig{
CORSOpts: &cors.Options{ CORSOpts: &cors.Options{
AllowedOrigins: origins, AllowedOrigins: origins,
AllowedMethods: []string{"GET", "PUT", "POST"},
}, },
} }
} }
@ -46,6 +47,13 @@ type testCase struct {
ResHeaders map[string]string ResHeaders map[string]string
} }
var defaultOrigins = []string{
"http://localhost",
"http://127.0.0.1",
"https://localhost",
"https://127.0.0.1",
}
func getTestServer(t *testing.T, origins []string) *httptest.Server { func getTestServer(t *testing.T, origins []string) *httptest.Server {
cmdsCtx, err := coremock.MockCmdsCtx() cmdsCtx, err := coremock.MockCmdsCtx()
if err != nil { if err != nil {
@ -59,6 +67,10 @@ func getTestServer(t *testing.T, origins []string) *httptest.Server {
}, },
} }
if len(origins) == 0 {
origins = defaultOrigins
}
handler := NewHandler(cmdsCtx, cmdRoot, originCfg(origins)) handler := NewHandler(cmdsCtx, cmdRoot, originCfg(origins))
return httptest.NewServer(handler) return httptest.NewServer(handler)
} }

View File

@ -1,8 +1,10 @@
package corehttp package corehttp
import ( import (
"net"
"net/http" "net/http"
"os" "os"
"strconv"
"strings" "strings"
cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors" cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
@ -28,6 +30,13 @@ or
ipfs daemon --api-http-header 'Access-Control-Allow-Origin: *' ipfs daemon --api-http-header 'Access-Control-Allow-Origin: *'
` `
var defaultLocalhostOrigins = []string{
"http://127.0.0.1:<port>",
"https://127.0.0.1:<port>",
"http://localhost:<port>",
"https://localhost:<port>",
}
func addCORSFromEnv(c *cmdsHttp.ServerConfig) { func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
origin := os.Getenv(originEnvKey) origin := os.Getenv(originEnvKey)
if origin != "" { if origin != "" {
@ -57,8 +66,41 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
c.Headers = nc.API.HTTPHeaders c.Headers = nc.API.HTTPHeaders
} }
func addCORSDefaults(c *cmdsHttp.ServerConfig) {
// by default use localhost origins
if len(c.CORSOpts.AllowedOrigins) == 0 {
c.CORSOpts.AllowedOrigins = defaultLocalhostOrigins
}
// by default, use GET, PUT, POST
if len(c.CORSOpts.AllowedMethods) == 0 {
c.CORSOpts.AllowedMethods = []string{"GET", "POST", "PUT"}
}
}
func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) {
// we have to grab the port from an addr, which may be an ip6 addr.
// TODO: this should take multiaddrs and derive port from there.
port := ""
if tcpaddr, ok := addr.(*net.TCPAddr); ok {
port = strconv.Itoa(tcpaddr.Port)
} else if udpaddr, ok := addr.(*net.UDPAddr); ok {
port = strconv.Itoa(udpaddr.Port)
}
// we're listening on tcp/udp with ports. ("udp!?" you say? yeah... it happens...)
for i, o := range c.CORSOpts.AllowedOrigins {
// TODO: allow replacing <host>. tricky, ip4 and ip6 and hostnames...
if port != "" {
o = strings.Replace(o, "<port>", port, -1)
}
c.CORSOpts.AllowedOrigins[i] = o
}
}
func CommandsOption(cctx commands.Context) ServeOption { func CommandsOption(cctx commands.Context) ServeOption {
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
cfg := &cmdsHttp.ServerConfig{ cfg := &cmdsHttp.ServerConfig{
CORSOpts: &cors.Options{ CORSOpts: &cors.Options{
@ -68,6 +110,8 @@ func CommandsOption(cctx commands.Context) ServeOption {
addHeadersFromConfig(cfg, n.Repo.Config()) addHeadersFromConfig(cfg, n.Repo.Config())
addCORSFromEnv(cfg) addCORSFromEnv(cfg)
addCORSDefaults(cfg)
patchCORSVars(cfg, l.Addr())
cmdHandler := cmdsHttp.NewHandler(cctx, corecommands.Root, cfg) cmdHandler := cmdsHttp.NewHandler(cctx, corecommands.Root, cfg)
mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler) mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)

View File

@ -23,16 +23,16 @@ var log = eventlog.Logger("core/server")
// It returns the mux to expose to future options, which may be a new mux if it // It returns the mux to expose to future options, which may be a new mux if it
// is interested in mediating requests to future options, or the same mux // is interested in mediating requests to future options, or the same mux
// initially passed in if not. // initially passed in if not.
type ServeOption func(*core.IpfsNode, *http.ServeMux) (*http.ServeMux, error) type ServeOption func(*core.IpfsNode, net.Listener, *http.ServeMux) (*http.ServeMux, error)
// makeHandler turns a list of ServeOptions into a http.Handler that implements // makeHandler turns a list of ServeOptions into a http.Handler that implements
// all of the given options, in order. // all of the given options, in order.
func makeHandler(n *core.IpfsNode, options ...ServeOption) (http.Handler, error) { func makeHandler(n *core.IpfsNode, l net.Listener, options ...ServeOption) (http.Handler, error) {
topMux := http.NewServeMux() topMux := http.NewServeMux()
mux := topMux mux := topMux
for _, option := range options { for _, option := range options {
var err error var err error
mux, err = option(n, mux) mux, err = option(n, l, mux)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -65,7 +65,7 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv
} }
func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error { func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error {
handler, err := makeHandler(node, options...) handler, err := makeHandler(node, lis, options...)
if err != nil { if err != nil {
return err return err
} }

View File

@ -2,6 +2,7 @@ package corehttp
import ( import (
"fmt" "fmt"
"net"
"net/http" "net/http"
"sync" "sync"
@ -27,7 +28,7 @@ func NewGateway(conf GatewayConfig) *Gateway {
} }
func (g *Gateway) ServeOption() ServeOption { func (g *Gateway) ServeOption() ServeOption {
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
// pass user's HTTP headers // pass user's HTTP headers
g.Config.Headers = n.Repo.Config().Gateway.HTTPHeaders g.Config.Headers = n.Repo.Config().Gateway.HTTPHeaders
@ -50,7 +51,7 @@ func GatewayOption(writable bool) ServeOption {
} }
func VersionOption() ServeOption { func VersionOption() ServeOption {
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Client Version: %s\n", id.ClientVersion) fmt.Fprintf(w, "Client Version: %s\n", id.ClientVersion)
fmt.Fprintf(w, "Protocol Version: %s\n", id.IpfsVersion) fmt.Fprintf(w, "Protocol Version: %s\n", id.IpfsVersion)

View File

@ -55,6 +55,14 @@ func newNodeWithMockNamesys(t *testing.T, ns mockNamesys) *core.IpfsNode {
return n return n
} }
type delegatedHandler struct {
http.Handler
}
func (dh *delegatedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
dh.Handler.ServeHTTP(w, r)
}
func TestGatewayGet(t *testing.T) { func TestGatewayGet(t *testing.T) {
t.Skip("not sure whats going on here") t.Skip("not sure whats going on here")
ns := mockNamesys{} ns := mockNamesys{}
@ -65,7 +73,14 @@ func TestGatewayGet(t *testing.T) {
} }
ns["example.com"] = path.FromString("/ipfs/" + k) ns["example.com"] = path.FromString("/ipfs/" + k)
h, err := makeHandler(n, // need this variable here since we need to construct handler with
// listener, and server with handler. yay cycles.
dh := &delegatedHandler{}
ts := httptest.NewServer(dh)
defer ts.Close()
dh.Handler, err = makeHandler(n,
ts.Listener,
IPNSHostnameOption(), IPNSHostnameOption(),
GatewayOption(false), GatewayOption(false),
) )
@ -73,9 +88,6 @@ func TestGatewayGet(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ts := httptest.NewServer(h)
defer ts.Close()
t.Log(ts.URL) t.Log(ts.URL)
for _, test := range []struct { for _, test := range []struct {
host string host string

View File

@ -1,6 +1,7 @@
package corehttp package corehttp
import ( import (
"net"
"net/http" "net/http"
"strings" "strings"
@ -13,7 +14,7 @@ import (
// an IPNS name. // an IPNS name.
// The rewritten request points at the resolved name on the gateway handler. // The rewritten request points at the resolved name on the gateway handler.
func IPNSHostnameOption() ServeOption { func IPNSHostnameOption() ServeOption {
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
childMux := http.NewServeMux() childMux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithCancel(n.Context()) ctx, cancel := context.WithCancel(n.Context())

View File

@ -2,6 +2,7 @@ package corehttp
import ( import (
"io" "io"
"net"
"net/http" "net/http"
core "github.com/ipfs/go-ipfs/core" core "github.com/ipfs/go-ipfs/core"
@ -36,7 +37,7 @@ func (w *writeErrNotifier) Write(b []byte) (int, error) {
} }
func LogOption() ServeOption { func LogOption() ServeOption {
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
mux.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200) w.WriteHeader(200)
wnf, errs := newWriteErrNotifier(w) wnf, errs := newWriteErrNotifier(w)

View File

@ -1,6 +1,7 @@
package corehttp package corehttp
import ( import (
"net"
"net/http" "net/http"
prom "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus" prom "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
@ -9,7 +10,7 @@ import (
) )
func PrometheusOption(path string) ServeOption { func PrometheusOption(path string) ServeOption {
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
mux.Handle(path, prom.Handler()) mux.Handle(path, prom.Handler())
return mux, nil return mux, nil
} }

View File

@ -1,6 +1,7 @@
package corehttp package corehttp
import ( import (
"net"
"net/http" "net/http"
core "github.com/ipfs/go-ipfs/core" core "github.com/ipfs/go-ipfs/core"
@ -8,7 +9,7 @@ import (
func RedirectOption(path string, redirect string) ServeOption { func RedirectOption(path string, redirect string) ServeOption {
handler := &redirectHandler{redirect} handler := &redirectHandler{redirect}
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
mux.Handle("/"+path+"/", handler) mux.Handle("/"+path+"/", handler)
return mux, nil return mux, nil
} }