@cryptotaxi247 / kubo / commits / 3ee83a7c5

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>

Juan Batiz-Benet committed Jul 31, 2015 at 17:36 UTC 3ee83a7c5eae50496a9a349330bc70e4d635393c
3 files changed +68 -23
commands/http/handler.go
+13 -23
@@ -6,6 +6,7 @@ import (
6 "fmt"
7 "io"
8 "net/http"
9 + "net/url"
10 "strconv"
11 "strings"
12
@@ -55,13 +56,6 @@ const (
56 ACACredentials = "Access-Control-Allow-Credentials"
57 )
58
58 -var localhostOrigins = []string{
59 - "http://127.0.0.1",
60 - "https://127.0.0.1",
61 - "http://localhost",
62 - "https://localhost",
63 -}
64 -
59 var mimeTypes = map[string]string{
60 cmds.JSON: "application/json",
61 cmds.XML: "application/xml",
@@ -91,21 +85,7 @@ func skipAPIHeader(h string) bool {
85
86 func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handler {
87 if cfg == nil {
94 - cfg = &ServerConfig{}
95 - }
96 -
97 - if cfg.CORSOpts == nil {
98 - cfg.CORSOpts = new(cors.Options)
99 - }
100 -
101 - // by default, use GET, PUT, POST
102 - if cfg.CORSOpts.AllowedMethods == nil {
103 - cfg.CORSOpts.AllowedMethods = []string{"GET", "POST", "PUT"}
104 - }
105 -
106 - // by default, only let 127.0.0.1 through.
107 - if cfg.CORSOpts.AllowedOrigins == nil {
108 - cfg.CORSOpts.AllowedOrigins = localhostOrigins
88 + panic("must provide a valid ServerConfig")
89 }
90
91 // Wrap the internal handler with CORS handling-middleware.
@@ -375,6 +355,16 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
355 return true
356 }
357
358 + u, err := url.Parse(referer)
359 + if err != nil {
360 + // bad referer. but there _is_ something, so bail.
361 + log.Debug("failed to parse referer: ", referer)
362 + // debug because referer comes straight from the client. dont want to
363 + // let people DOS by putting a huge referer that gets stored in log files.
364 + return false
365 + }
366 + origin := u.Scheme + "://" + u.Host
367 +
368 // check CORS ACAOs and pretend Referer works like an origin.
369 // this is valid for many (most?) sane uses of the API in
370 // other applications, and will have the desired effect.
@@ -384,7 +374,7 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
374 }
375
376 // referer is allowed explicitly
387 - if o == referer {
377 + if o == origin {
378 return true
379 }
380 }
commands/http/handler_test.go
+12
@@ -31,6 +31,7 @@ func originCfg(origins []string) *ServerConfig {
31 return &ServerConfig{
32 CORSOpts: &cors.Options{
33 AllowedOrigins: origins,
34 + AllowedMethods: []string{"GET", "PUT", "POST"},
35 },
36 }
37 }
@@ -46,6 +47,13 @@ type testCase struct {
47 ResHeaders map[string]string
48 }
49
50 +var defaultOrigins = []string{
51 + "http://localhost",
52 + "http://127.0.0.1",
53 + "https://localhost",
54 + "https://127.0.0.1",
55 +}
56 +
57 func getTestServer(t *testing.T, origins []string) *httptest.Server {
58 cmdsCtx, err := coremock.MockCmdsCtx()
59 if err != nil {
@@ -59,6 +67,10 @@ func getTestServer(t *testing.T, origins []string) *httptest.Server {
67 },
68 }
69
70 + if len(origins) == 0 {
71 + origins = defaultOrigins
72 + }
73 +
74 handler := NewHandler(cmdsCtx, cmdRoot, originCfg(origins))
75 return httptest.NewServer(handler)
76 }
core/corehttp/commands.go
+43
@@ -4,6 +4,7 @@ import (
4 "net"
5 "net/http"
6 "os"
7 + "strconv"
8 "strings"
9
10 cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
@@ -29,6 +30,13 @@ or
30 ipfs daemon --api-http-header 'Access-Control-Allow-Origin: *'
31 `
32
33 +var defaultLocalhostOrigins = []string{
34 + "http://127.0.0.1:<port>",
35 + "https://127.0.0.1:<port>",
36 + "http://localhost:<port>",
37 + "https://localhost:<port>",
38 +}
39 +
40 func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
41 origin := os.Getenv(originEnvKey)
42 if origin != "" {
@@ -58,6 +66,39 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
66 c.Headers = nc.API.HTTPHeaders
67 }
68
69 +func addCORSDefaults(c *cmdsHttp.ServerConfig) {
70 + // by default use localhost origins
71 + if len(c.CORSOpts.AllowedOrigins) == 0 {
72 + c.CORSOpts.AllowedOrigins = defaultLocalhostOrigins
73 + }
74 +
75 + // by default, use GET, PUT, POST
76 + if len(c.CORSOpts.AllowedMethods) == 0 {
77 + c.CORSOpts.AllowedMethods = []string{"GET", "POST", "PUT"}
78 + }
79 +}
80 +
81 +func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) {
82 +
83 + // we have to grab the port from an addr, which may be an ip6 addr.
84 + // TODO: this should take multiaddrs and derive port from there.
85 + port := ""
86 + if tcpaddr, ok := addr.(*net.TCPAddr); ok {
87 + port = strconv.Itoa(tcpaddr.Port)
88 + } else if udpaddr, ok := addr.(*net.UDPAddr); ok {
89 + port = strconv.Itoa(udpaddr.Port)
90 + }
91 +
92 + // we're listening on tcp/udp with ports. ("udp!?" you say? yeah... it happens...)
93 + for i, o := range c.CORSOpts.AllowedOrigins {
94 + // TODO: allow replacing <host>. tricky, ip4 and ip6 and hostnames...
95 + if port != "" {
96 + o = strings.Replace(o, "<port>", port, -1)
97 + }
98 + c.CORSOpts.AllowedOrigins[i] = o
99 + }
100 +}
101 +
102 func CommandsOption(cctx commands.Context) ServeOption {
103 return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
104
@@ -69,6 +110,8 @@ func CommandsOption(cctx commands.Context) ServeOption {
110
111 addHeadersFromConfig(cfg, n.Repo.Config())
112 addCORSFromEnv(cfg)
113 + addCORSDefaults(cfg)
114 + patchCORSVars(cfg, l.Addr())
115
116 cmdHandler := cmdsHttp.NewHandler(cctx, corecommands.Root, cfg)
117 mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)