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:
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user