mirror of
https://github.com/ipfs/kubo.git
synced 2025-07-01 02:30:39 +08:00
Merge pull request #1544 from ipfs/fix/allowed-origins
fix allowed origins to make webui work again
This commit is contained in:
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
_ "expvar"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"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,
|
||||
// and don't provide a convenient http.Handler entry point, such as
|
||||
// expvar and http/pprof.
|
||||
func defaultMux(path string) func(node *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) {
|
||||
return func(node *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) {
|
||||
func defaultMux(path string) corehttp.ServeOption {
|
||||
return func(node *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
|
||||
mux.Handle(path, http.DefaultServeMux)
|
||||
return mux, nil
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -55,13 +56,6 @@ const (
|
||||
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{
|
||||
cmds.JSON: "application/json",
|
||||
cmds.XML: "application/xml",
|
||||
@ -91,21 +85,7 @@ func skipAPIHeader(h string) bool {
|
||||
|
||||
func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handler {
|
||||
if cfg == nil {
|
||||
cfg = &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
|
||||
panic("must provide a valid ServerConfig")
|
||||
}
|
||||
|
||||
// Wrap the internal handler with CORS handling-middleware.
|
||||
@ -375,6 +355,16 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
|
||||
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.
|
||||
// this is valid for many (most?) sane uses of the API in
|
||||
// other applications, and will have the desired effect.
|
||||
@ -384,7 +374,7 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
|
||||
}
|
||||
|
||||
// referer is allowed explicitly
|
||||
if o == referer {
|
||||
if o == origin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ func originCfg(origins []string) *ServerConfig {
|
||||
return &ServerConfig{
|
||||
CORSOpts: &cors.Options{
|
||||
AllowedOrigins: origins,
|
||||
AllowedMethods: []string{"GET", "PUT", "POST"},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -46,6 +47,13 @@ type testCase struct {
|
||||
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 {
|
||||
cmdsCtx, err := coremock.MockCmdsCtx()
|
||||
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))
|
||||
return httptest.NewServer(handler)
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package corehttp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
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: *'
|
||||
`
|
||||
|
||||
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) {
|
||||
origin := os.Getenv(originEnvKey)
|
||||
if origin != "" {
|
||||
@ -57,8 +66,41 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
|
||||
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 {
|
||||
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{
|
||||
CORSOpts: &cors.Options{
|
||||
@ -68,6 +110,8 @@ func CommandsOption(cctx commands.Context) ServeOption {
|
||||
|
||||
addHeadersFromConfig(cfg, n.Repo.Config())
|
||||
addCORSFromEnv(cfg)
|
||||
addCORSDefaults(cfg)
|
||||
patchCORSVars(cfg, l.Addr())
|
||||
|
||||
cmdHandler := cmdsHttp.NewHandler(cctx, corecommands.Root, cfg)
|
||||
mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)
|
||||
|
@ -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
|
||||
// is interested in mediating requests to future options, or the same mux
|
||||
// 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
|
||||
// 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()
|
||||
mux := topMux
|
||||
for _, option := range options {
|
||||
var err error
|
||||
mux, err = option(n, mux)
|
||||
mux, err = option(n, l, mux)
|
||||
if err != nil {
|
||||
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 {
|
||||
handler, err := makeHandler(node, options...)
|
||||
handler, err := makeHandler(node, lis, options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package corehttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
@ -27,7 +28,7 @@ func NewGateway(conf GatewayConfig) *Gateway {
|
||||
}
|
||||
|
||||
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
|
||||
g.Config.Headers = n.Repo.Config().Gateway.HTTPHeaders
|
||||
|
||||
@ -50,7 +51,7 @@ func GatewayOption(writable bool) 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) {
|
||||
fmt.Fprintf(w, "Client Version: %s\n", id.ClientVersion)
|
||||
fmt.Fprintf(w, "Protocol Version: %s\n", id.IpfsVersion)
|
||||
|
@ -55,6 +55,14 @@ func newNodeWithMockNamesys(t *testing.T, ns mockNamesys) *core.IpfsNode {
|
||||
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) {
|
||||
t.Skip("not sure whats going on here")
|
||||
ns := mockNamesys{}
|
||||
@ -65,7 +73,14 @@ func TestGatewayGet(t *testing.T) {
|
||||
}
|
||||
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(),
|
||||
GatewayOption(false),
|
||||
)
|
||||
@ -73,9 +88,6 @@ func TestGatewayGet(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(h)
|
||||
defer ts.Close()
|
||||
|
||||
t.Log(ts.URL)
|
||||
for _, test := range []struct {
|
||||
host string
|
||||
|
@ -1,6 +1,7 @@
|
||||
package corehttp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@ -13,7 +14,7 @@ import (
|
||||
// an IPNS name.
|
||||
// The rewritten request points at the resolved name on the gateway handler.
|
||||
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()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithCancel(n.Context())
|
||||
|
@ -2,6 +2,7 @@ package corehttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
@ -36,7 +37,7 @@ func (w *writeErrNotifier) Write(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
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) {
|
||||
w.WriteHeader(200)
|
||||
wnf, errs := newWriteErrNotifier(w)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package corehttp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
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 {
|
||||
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())
|
||||
return mux, nil
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package corehttp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
@ -8,7 +9,7 @@ import (
|
||||
|
||||
func RedirectOption(path string, redirect string) ServeOption {
|
||||
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)
|
||||
return mux, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user