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

fix allowed origins to make webui work again #1544

Merged
merged 2 commits into from
Aug 2, 2015
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
5 changes: 3 additions & 2 deletions cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
_ "expvar"
"fmt"
"net"
"net/http"
_ "net/http/pprof"
"os"
Expand Down Expand Up @@ -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
}
Expand Down
36 changes: 13 additions & 23 deletions commands/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -375,6 +355,16 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
return true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, i think the protocol matters to CORS, one should be able to set CORS on https only 👎

}

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.
Expand All @@ -384,7 +374,7 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
}

// referer is allowed explicitly
if o == referer {
if o == origin {
return true
}
}
Expand Down
12 changes: 12 additions & 0 deletions commands/http/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func originCfg(origins []string) *ServerConfig {
return &ServerConfig{
CORSOpts: &cors.Options{
AllowedOrigins: origins,
AllowedMethods: []string{"GET", "PUT", "POST"},
},
}
}
Expand All @@ -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 {
Expand All @@ -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)
}
Expand Down
46 changes: 45 additions & 1 deletion core/corehttp/commands.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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 != "" {
Expand Down Expand Up @@ -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
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incorrect. these constants are already in commands/http/handler


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{
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions core/corehttp/corehttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
5 changes: 3 additions & 2 deletions core/corehttp/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package corehttp

import (
"fmt"
"net"
"net/http"
"sync"

Expand All @@ -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

Expand All @@ -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)
Expand Down
20 changes: 16 additions & 4 deletions core/corehttp/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -65,17 +73,21 @@ 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),
)
if err != nil {
t.Fatal(err)
}

ts := httptest.NewServer(h)
defer ts.Close()

t.Log(ts.URL)
for _, test := range []struct {
host string
Expand Down
3 changes: 2 additions & 1 deletion core/corehttp/ipns_hostname.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package corehttp

import (
"net"
"net/http"
"strings"

Expand All @@ -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())
Expand Down
3 changes: 2 additions & 1 deletion core/corehttp/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package corehttp

import (
"io"
"net"
"net/http"

core "github.com/ipfs/go-ipfs/core"
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion core/corehttp/prometheus.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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
}
Expand Down
3 changes: 2 additions & 1 deletion core/corehttp/redirect.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package corehttp

import (
"net"
"net/http"

core "github.com/ipfs/go-ipfs/core"
)

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
}
Expand Down