fix cors: defaults should take the port of the listener

need to do it this way to avoid VERY confusing situations where
the user would change the API port (to another port, or maybe even
to :0). this way things dont break on the user, and by default,
users only need to change the API address and things should still
"just work"

License: MIT
Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
This commit is contained in:
Juan Batiz-Benet 2015-07-31 17:36:02 -04:00
parent 3f1cbe2f43
commit 3ee83a7c5e
3 changed files with 68 additions and 23 deletions

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -4,6 +4,7 @@ import (
"net"
"net/http"
"os"
"strconv"
"strings"
cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
@ -29,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 != "" {
@ -58,6 +66,39 @@ 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, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
@ -69,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)