From 99ffabb871fa942b2b5e4de46cdb260c0562b1d7 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 11 Jan 2019 12:36:04 +0100 Subject: [PATCH 1/7] Fix #639: Import rs/cors with Gx License: MIT Signed-off-by: Hector Sanjuan --- package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package.json b/package.json index 93efdbbd..878ed0a4 100644 --- a/package.json +++ b/package.json @@ -155,6 +155,12 @@ "hash": "QmeP7Gybon3hs9KhoxSFvzqAHQS6xgyKYvsnjqktaXX3QN", "name": "go-libp2p-pubsub", "version": "100.11.9" + }, + { + "author": "hsanjuan", + "hash": "QmNNk4iczWp8Q4R1mXQ2mrrjQvWisYqMqbW1an8qGbJZsM", + "name": "cors", + "version": "1.6.0" } ], "gxVersion": "0.11.0", From 7067745f1ad3d7830e57d43b520d7fe4e80c2bb3 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 11 Jan 2019 12:36:25 +0100 Subject: [PATCH 2/7] Fix #639: Add CORS options to restapi License: MIT Signed-off-by: Hector Sanjuan --- api/rest/config.go | 61 +++++++++++++++++++++++++++++++++++++---- api/rest/config_test.go | 10 +++++-- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/api/rest/config.go b/api/rest/config.go index b93c21e3..6e6ce8a7 100644 --- a/api/rest/config.go +++ b/api/rest/config.go @@ -32,11 +32,22 @@ const ( // These are the default values for Config. var ( - DefaultHeaders = map[string][]string{ - "Access-Control-Allow-Headers": []string{"X-Requested-With", "Range"}, - "Access-Control-Allow-Methods": []string{"GET"}, - "Access-Control-Allow-Origin": []string{"*"}, + DefaultHeaders = map[string][]string{} +) + +// CORS defaults +var ( + DefaultCORSAllowedOrigins = []string{"*"} + DefaultCORSAllowedMethods = []string{"GET"} + DefaultCORSAllowedHeaders = []string{} + DefaultCORSExposedHeaders = []string{ + "Content-Type", + "X-Stream-Output", + "X-Chunked-Output", + "X-Content-Length", } + DefaultCORSAllowCredentials = false + DefaultCORSMaxAge time.Duration = 0 ) // Config is used to intialize the API object and allows to @@ -85,8 +96,16 @@ type Config struct { BasicAuthCreds map[string]string // Headers provides customization for the headers returned - // by the API. By default it sets a CORS policy. + // by the API on existing routes. Headers map[string][]string + + // CORS header management + CORSAllowedOrigins []string + CORSAllowedMethods []string + CORSAllowedHeaders []string + CORSExposedHeaders []string + CORSAllowCredentials bool + CORSMaxAge time.Duration } type jsonConfig struct { @@ -105,6 +124,13 @@ type jsonConfig struct { BasicAuthCreds map[string]string `json:"basic_auth_credentials"` Headers map[string][]string `json:"headers"` + + CORSAllowedOrigins []string `json:"cors_allowed_origins"` + CORSAllowedMethods []string `json:"cors_allowed_methods"` + CORSAllowedHeaders []string `json:"cors_allowed_headers"` + CORSExposedHeaders []string `json:"cors_exposed_headers"` + CORSAllowCredentials bool `json:"cors_allow_credentials"` + CORSMaxAge string `json:"cors_max_age"` } // ConfigKey returns a human-friendly identifier for this type of @@ -136,6 +162,13 @@ func (cfg *Config) Default() error { // Headers cfg.Headers = DefaultHeaders + cfg.CORSAllowedOrigins = DefaultCORSAllowedOrigins + cfg.CORSAllowedMethods = DefaultCORSAllowedMethods + cfg.CORSAllowedHeaders = DefaultCORSAllowedHeaders + cfg.CORSExposedHeaders = DefaultCORSExposedHeaders + cfg.CORSAllowCredentials = DefaultCORSAllowCredentials + cfg.CORSMaxAge = DefaultCORSMaxAge + return nil } @@ -154,7 +187,9 @@ func (cfg *Config) Validate() error { case cfg.BasicAuthCreds != nil && len(cfg.BasicAuthCreds) == 0: return errors.New("restapi.basic_auth_creds should be null or have at least one entry") case (cfg.pathSSLCertFile != "" || cfg.pathSSLKeyFile != "") && cfg.TLS == nil: - return errors.New("missing TLS configuration") + return errors.New("restapi: missing TLS configuration") + case (cfg.CORSMaxAge < 0): + return errors.New("restapi.cors_max_age is invalid") } return cfg.validateLibp2p() @@ -232,12 +267,20 @@ func (cfg *Config) loadHTTPOptions(jcfg *jsonConfig) error { return err } + // CORS + cfg.CORSAllowedOrigins = jcfg.CORSAllowedOrigins + cfg.CORSAllowedMethods = jcfg.CORSAllowedMethods + cfg.CORSAllowedHeaders = jcfg.CORSAllowedHeaders + cfg.CORSExposedHeaders = jcfg.CORSExposedHeaders + cfg.CORSAllowCredentials = jcfg.CORSAllowCredentials + return config.ParseDurations( "restapi", &config.DurationOpt{Duration: jcfg.ReadTimeout, Dst: &cfg.ReadTimeout, Name: "read_timeout"}, &config.DurationOpt{Duration: jcfg.ReadHeaderTimeout, Dst: &cfg.ReadHeaderTimeout, Name: "read_header_timeout"}, &config.DurationOpt{Duration: jcfg.WriteTimeout, Dst: &cfg.WriteTimeout, Name: "write_timeout"}, &config.DurationOpt{Duration: jcfg.IdleTimeout, Dst: &cfg.IdleTimeout, Name: "idle_timeout"}, + &config.DurationOpt{Duration: jcfg.CORSMaxAge, Dst: &cfg.CORSMaxAge, Name: "cors_max_age"}, ) } @@ -323,6 +366,12 @@ func (cfg *Config) ToJSON() (raw []byte, err error) { IdleTimeout: cfg.IdleTimeout.String(), BasicAuthCreds: cfg.BasicAuthCreds, Headers: cfg.Headers, + CORSAllowedOrigins: cfg.CORSAllowedOrigins, + CORSAllowedMethods: cfg.CORSAllowedMethods, + CORSAllowedHeaders: cfg.CORSAllowedHeaders, + CORSExposedHeaders: cfg.CORSExposedHeaders, + CORSAllowCredentials: cfg.CORSAllowCredentials, + CORSMaxAge: cfg.CORSMaxAge.String(), } if cfg.ID != "" { diff --git a/api/rest/config_test.go b/api/rest/config_test.go index f8ba4801..2500beae 100644 --- a/api/rest/config_test.go +++ b/api/rest/config_test.go @@ -13,14 +13,20 @@ import ( var cfgJSON = []byte(` { - "listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", + "listen_multiaddress": "/ip4/127.0.0.1/tcp/12122", "ssl_cert_file": "test/server.crt", "ssl_key_file": "test/server.key", "read_timeout": "30s", "read_header_timeout": "5s", "write_timeout": "1m0s", "idle_timeout": "2m0s", - "basic_auth_credentials": null + "basic_auth_credentials": null, + "cors_allowed_origins": ["myorigin"], + "cors_allowed_methods": ["GET"], + "cors_allowed_headers": ["X-Custom"], + "cors_exposed_headers": ["X-Chunked-Output"], + "cors_allow_credentials": false, + "cors_max_age": "1s" } `) From adb290ed2d7de9877d22ddca40e2d0ad163b1fc2 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 11 Jan 2019 13:25:21 +0100 Subject: [PATCH 3/7] Fix #639: restapi: Handle CORS preflight requests (OPTIONS) This adds support for handling preflight requests in the REST API and fixes currently mostly broken CORS. Before we just let the user add custom response headers to the configuration "headers" key but this is not the best way because CORs headers and requests need special handling and doing it wrong has security implications. Therefore, I have added specific CORS-related configuration options which control CORS behavour. We are forced to change the "headers" defaults and will notify the users about this in the changelog. License: MIT Signed-off-by: Hector Sanjuan --- api/rest/config.go | 24 +++++++++- api/rest/restapi.go | 5 ++- api/rest/restapi_test.go | 94 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 114 insertions(+), 9 deletions(-) diff --git a/api/rest/config.go b/api/rest/config.go index 6e6ce8a7..2b805e40 100644 --- a/api/rest/config.go +++ b/api/rest/config.go @@ -6,10 +6,12 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "path/filepath" "time" "github.com/ipfs/ipfs-cluster/config" + "github.com/rs/cors" "github.com/kelseyhightower/envconfig" @@ -38,7 +40,11 @@ var ( // CORS defaults var ( DefaultCORSAllowedOrigins = []string{"*"} - DefaultCORSAllowedMethods = []string{"GET"} + DefaultCORSAllowedMethods = []string{ + http.MethodGet, + } + // rs/cors this will set sensible defaults when empty: + // {"Origin", "Accept", "Content-Type", "X-Requested-With"} DefaultCORSAllowedHeaders = []string{} DefaultCORSExposedHeaders = []string{ "Content-Type", @@ -46,7 +52,7 @@ var ( "X-Chunked-Output", "X-Content-Length", } - DefaultCORSAllowCredentials = false + DefaultCORSAllowCredentials = true DefaultCORSMaxAge time.Duration = 0 ) @@ -392,6 +398,20 @@ func (cfg *Config) ToJSON() (raw []byte, err error) { return } +func (cfg *Config) corsOptions() *cors.Options { + maxAgeSeconds := int(cfg.CORSMaxAge / time.Second) + + return &cors.Options{ + AllowedOrigins: cfg.CORSAllowedOrigins, + AllowedMethods: cfg.CORSAllowedMethods, + AllowedHeaders: cfg.CORSAllowedHeaders, + ExposedHeaders: cfg.CORSExposedHeaders, + AllowCredentials: cfg.CORSAllowCredentials, + MaxAge: maxAgeSeconds, + Debug: false, + } +} + func newTLSConfig(certFile, keyFile string) (*tls.Config, error) { cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { diff --git a/api/rest/restapi.go b/api/rest/restapi.go index 4bfb19d7..6ea6088e 100644 --- a/api/rest/restapi.go +++ b/api/rest/restapi.go @@ -23,6 +23,7 @@ import ( "github.com/ipfs/ipfs-cluster/adder/adderutils" types "github.com/ipfs/ipfs-cluster/api" + "github.com/rs/cors" mux "github.com/gorilla/mux" gostream "github.com/hsanjuan/go-libp2p-gostream" @@ -109,12 +110,14 @@ func NewAPIWithHost(cfg *Config, h host.Host) (*API, error) { } router := mux.NewRouter().StrictSlash(true) + c := cors.New(*cfg.corsOptions()) + withCorsRouter := c.Handler(router) s := &http.Server{ ReadTimeout: cfg.ReadTimeout, ReadHeaderTimeout: cfg.ReadHeaderTimeout, WriteTimeout: cfg.WriteTimeout, IdleTimeout: cfg.IdleTimeout, - Handler: router, + Handler: withCorsRouter, } // See: https://github.com/ipfs/go-ipfs/issues/5168 diff --git a/api/rest/restapi_test.go b/api/rest/restapi_test.go index 2689d369..d5afeed5 100644 --- a/api/rest/restapi_test.go +++ b/api/rest/restapi_test.go @@ -12,6 +12,7 @@ import ( "net/http" "strings" "testing" + "time" "github.com/ipfs/ipfs-cluster/api" "github.com/ipfs/ipfs-cluster/test" @@ -25,8 +26,9 @@ import ( ) const ( - SSLCertFile = "test/server.crt" - SSLKeyFile = "test/server.key" + SSLCertFile = "test/server.crt" + SSLKeyFile = "test/server.key" + clientOrigin = "myorigin" ) func testAPI(t *testing.T) *API { @@ -39,6 +41,10 @@ func testAPI(t *testing.T) *API { cfg := &Config{} cfg.Default() cfg.HTTPListenAddr = apiMAddr + cfg.CORSAllowedOrigins = []string{clientOrigin} + cfg.CORSAllowedMethods = []string{"GET", "POST", "DELETE"} + //cfg.CORSAllowedHeaders = []string{"Content-Type"} + cfg.CORSMaxAge = 10 * time.Minute rest, err := NewAPIWithHost(cfg, h) if err != nil { @@ -133,6 +139,10 @@ func checkHeaders(t *testing.T, rest *API, url string, headers http.Header) { if headers.Get("Content-Type") != "application/json" { t.Errorf("%s is not application/json", url) } + + if eh := headers.Get("Access-Control-Expose-Headers"); eh == "" { + t.Error("AC-Expose-Headers not set") + } } // makes a libp2p host that knows how to talk to the rest API host. @@ -194,7 +204,9 @@ func makeGet(t *testing.T, rest *API, url string, resp interface{}) { h := makeHost(t, rest) defer h.Close() c := httpClient(t, h, isHTTPS(url)) - httpResp, err := c.Get(url) + req, _ := http.NewRequest(http.MethodGet, url, nil) + req.Header.Set("Origin", clientOrigin) + httpResp, err := c.Do(req) processResp(t, httpResp, err, resp) checkHeaders(t, rest, url, httpResp.Header) } @@ -207,7 +219,10 @@ func makePostWithContentType(t *testing.T, rest *API, url string, body []byte, c h := makeHost(t, rest) defer h.Close() c := httpClient(t, h, isHTTPS(url)) - httpResp, err := c.Post(url, contentType, bytes.NewReader(body)) + req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) + req.Header.Set("Content-Type", contentType) + req.Header.Set("Origin", clientOrigin) + httpResp, err := c.Do(req) processResp(t, httpResp, err, resp) checkHeaders(t, rest, url, httpResp.Header) } @@ -216,17 +231,32 @@ func makeDelete(t *testing.T, rest *API, url string, resp interface{}) { h := makeHost(t, rest) defer h.Close() c := httpClient(t, h, isHTTPS(url)) - req, _ := http.NewRequest("DELETE", url, bytes.NewReader([]byte{})) + req, _ := http.NewRequest(http.MethodDelete, url, bytes.NewReader([]byte{})) + req.Header.Set("Origin", clientOrigin) httpResp, err := c.Do(req) processResp(t, httpResp, err, resp) checkHeaders(t, rest, url, httpResp.Header) } +func makeOptions(t *testing.T, rest *API, url string, reqHeaders http.Header) http.Header { + h := makeHost(t, rest) + defer h.Close() + c := httpClient(t, h, isHTTPS(url)) + req, _ := http.NewRequest(http.MethodOptions, url, nil) + req.Header = reqHeaders + httpResp, err := c.Do(req) + processResp(t, httpResp, err, nil) + return httpResp.Header +} + func makeStreamingPost(t *testing.T, rest *API, url string, body io.Reader, contentType string, resp interface{}) { h := makeHost(t, rest) defer h.Close() c := httpClient(t, h, isHTTPS(url)) - httpResp, err := c.Post(url, contentType, body) + req, _ := http.NewRequest(http.MethodPost, url, body) + req.Header.Set("Content-Type", contentType) + req.Header.Set("Origin", clientOrigin) + httpResp, err := c.Do(req) processStreamingResp(t, httpResp, err, resp) checkHeaders(t, rest, url, httpResp.Header) } @@ -825,3 +855,55 @@ func TestAPIRecoverAllEndpoint(t *testing.T) { testBothEndpoints(t, tf) } + +func TestCORS(t *testing.T) { + rest := testAPI(t) + defer rest.Shutdown() + + type testcase struct { + method string + path string + } + + tf := func(t *testing.T, url urlF) { + reqHeaders := make(http.Header) + reqHeaders.Set("Origin", "myorigin") + reqHeaders.Set("Access-Control-Request-Headers", "Content-Type") + + for _, tc := range []testcase{ + testcase{"GET", "/pins"}, + // testcase{}, + } { + reqHeaders.Set("Access-Control-Request-Method", tc.method) + headers := makeOptions(t, rest, url(rest)+tc.path, reqHeaders) + aorigin := headers.Get("Access-Control-Allow-Origin") + amethods := headers.Get("Access-Control-Allow-Methods") + aheaders := headers.Get("Access-Control-Allow-Headers") + acreds := headers.Get("Access-Control-Allow-Credentials") + maxage := headers.Get("Access-Control-Max-Age") + + if aorigin != "myorigin" { + t.Error("Bad ACA-Origin:", aorigin) + } + + if amethods != tc.method { + t.Error("Bad ACA-Methods:", amethods) + } + + if aheaders != "Content-Type" { + t.Error("Bad ACA-Headers:", aheaders) + } + + if acreds != "true" { + t.Error("Bad ACA-Credentials:", acreds) + } + + if maxage != "600" { + t.Error("Bad AC-Max-Age:", maxage) + } + } + + } + + testBothEndpoints(t, tf) +} From a3720c29c207c4970c5953dad932641882870d7d Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 11 Jan 2019 13:40:07 +0100 Subject: [PATCH 4/7] restapi: minor codeclimate issue License: MIT Signed-off-by: Hector Sanjuan --- api/rest/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/rest/config.go b/api/rest/config.go index 2b805e40..ce0496af 100644 --- a/api/rest/config.go +++ b/api/rest/config.go @@ -52,8 +52,8 @@ var ( "X-Chunked-Output", "X-Content-Length", } - DefaultCORSAllowCredentials = true - DefaultCORSMaxAge time.Duration = 0 + DefaultCORSAllowCredentials = true + DefaultCORSMaxAge time.Duration // 0. Means always. ) // Config is used to intialize the API object and allows to From efafe9b7b5aeff9dc04de9188745c54bd6d090ff Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 11 Jan 2019 15:32:37 +0100 Subject: [PATCH 5/7] Fix #639: Do not break start by complaining of unset CORSMaxAge License: MIT Signed-off-by: Hector Sanjuan --- api/rest/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/rest/config.go b/api/rest/config.go index ce0496af..8a62c4c3 100644 --- a/api/rest/config.go +++ b/api/rest/config.go @@ -279,6 +279,9 @@ func (cfg *Config) loadHTTPOptions(jcfg *jsonConfig) error { cfg.CORSAllowedHeaders = jcfg.CORSAllowedHeaders cfg.CORSExposedHeaders = jcfg.CORSExposedHeaders cfg.CORSAllowCredentials = jcfg.CORSAllowCredentials + if jcfg.CORSMaxAge == "" { // compatibility + jcfg.CORSMaxAge = "0s" + } return config.ParseDurations( "restapi", From c9151f004838303c870497ec02c364e8f4452512 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 11 Jan 2019 15:49:12 +0100 Subject: [PATCH 6/7] Fix #639: Enforce basic auth for all requests when enabled License: MIT Signed-off-by: Hector Sanjuan --- api/rest/restapi.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/api/rest/restapi.go b/api/rest/restapi.go index 6ea6088e..60693349 100644 --- a/api/rest/restapi.go +++ b/api/rest/restapi.go @@ -21,9 +21,10 @@ import ( "sync" "time" + "github.com/rs/cors" + "github.com/ipfs/ipfs-cluster/adder/adderutils" types "github.com/ipfs/ipfs-cluster/api" - "github.com/rs/cors" mux "github.com/gorilla/mux" gostream "github.com/hsanjuan/go-libp2p-gostream" @@ -109,15 +110,20 @@ func NewAPIWithHost(cfg *Config, h host.Host) (*API, error) { return nil, err } + // Our handler is a gorilla router, + // wrapped with the cors handler, + // wrapped with the basic auth handler. router := mux.NewRouter().StrictSlash(true) - c := cors.New(*cfg.corsOptions()) - withCorsRouter := c.Handler(router) + handler := basicAuthHandler( + cfg.BasicAuthCreds, + cors.New(*cfg.corsOptions()).Handler(router), + ) s := &http.Server{ ReadTimeout: cfg.ReadTimeout, ReadHeaderTimeout: cfg.ReadHeaderTimeout, WriteTimeout: cfg.WriteTimeout, IdleTimeout: cfg.IdleTimeout, - Handler: withCorsRouter, + Handler: handler, } // See: https://github.com/ipfs/go-ipfs/issues/5168 @@ -228,9 +234,6 @@ func (api *API) Host() host.Host { func (api *API) addRoutes(router *mux.Router) { for _, route := range api.routes() { - if api.config.BasicAuthCreds != nil { - route.HandlerFunc = basicAuth(route.HandlerFunc, api.config.BasicAuthCreds) - } router. Methods(route.Method). Path(route.Pattern). @@ -240,8 +243,13 @@ func (api *API) addRoutes(router *mux.Router) { api.router = router } -func basicAuth(h http.HandlerFunc, credentials map[string]string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { +// basicAuth wraps a given handler with basic authentication +func basicAuthHandler(credentials map[string]string, h http.Handler) http.Handler { + if credentials == nil { + return h + } + + wrap := func(w http.ResponseWriter, r *http.Request) { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) username, password, ok := r.BasicAuth() if !ok { @@ -271,6 +279,7 @@ func basicAuth(h http.HandlerFunc, credentials map[string]string) http.HandlerFu } h.ServeHTTP(w, r) } + return http.HandlerFunc(wrap) } func unauthorizedResp() (string, error) { From afc45918b52716704925b0fe61fb46d9ac7fcdd3 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Fri, 11 Jan 2019 16:13:50 +0100 Subject: [PATCH 7/7] Sharness: update configuration files used in sharness Maintenance License: MIT Signed-off-by: Hector Sanjuan --- sharness/config/basic_auth/service.json | 51 ++++++++++++++++---- sharness/config/ssl-basic_auth/service.json | 53 +++++++++++++++++---- sharness/config/ssl/service.json | 52 ++++++++++++++++---- 3 files changed, 131 insertions(+), 25 deletions(-) diff --git a/sharness/config/basic_auth/service.json b/sharness/config/basic_auth/service.json index 2f64d716..0dac6c44 100644 --- a/sharness/config/basic_auth/service.json +++ b/sharness/config/basic_auth/service.json @@ -24,10 +24,18 @@ } }, "api": { + "ipfsproxy": { + "listen_multiaddress": "/ip4/127.0.0.1/tcp/9095", + "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", + "read_timeout": "10m0s", + "read_header_timeout": "5s", + "write_timeout": "10m0s", + "idle_timeout": "1m0s" + }, "restapi": { "ssl_cert_file": "", "ssl_key_file": "", - "listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", + "http_listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", "read_timeout": "30s", "read_header_timeout": "5s", "write_timeout": "1m0s", @@ -35,23 +43,50 @@ "basic_auth_credentials": { "testuser": "testpass", "userwithoutpass": "" - } + }, + "cors_allowed_origins": [ + "*" + ], + "cors_allowed_methods": [ + "GET" + ], + "cors_allowed_headers": [], + "cors_exposed_headers": [ + "Content-Type", + "X-Stream-Output", + "X-Chunked-Output", + "X-Content-Length" + ], + "cors_allow_credentials": true, + "cors_max_age": "0s" } }, "ipfs_connector": { "ipfshttp": { - "proxy_listen_multiaddress": "/ip4/127.0.0.1/tcp/9095", "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", - "connect_swarms_delay": "7s", - "proxy_read_timeout": "10m0s", - "proxy_read_header_timeout": "5s", - "proxy_write_timeout": "10m0s", - "proxy_idle_timeout": "1m0s" + "connect_swarms_delay": "30s", + "pin_method": "refs", + "ipfs_request_timeout": "5m0s", + "pin_timeout": "24h0m0s", + "unpin_timeout": "3h0m0s" + } + }, + "pin_tracker": { + "maptracker": { + "max_pin_queue_size": 50000, + "concurrent_pins": 10 + }, + "stateless": { + "max_pin_queue_size": 50000, + "concurrent_pins": 10 } }, "monitor": { "monbasic": { "check_interval": "15s" + }, + "pubsubmon": { + "check_interval": "15s" } }, "informer": { diff --git a/sharness/config/ssl-basic_auth/service.json b/sharness/config/ssl-basic_auth/service.json index bb51a93f..6403d671 100644 --- a/sharness/config/ssl-basic_auth/service.json +++ b/sharness/config/ssl-basic_auth/service.json @@ -24,34 +24,69 @@ } }, "api": { + "ipfsproxy": { + "listen_multiaddress": "/ip4/127.0.0.1/tcp/9095", + "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", + "read_timeout": "10m0s", + "read_header_timeout": "5s", + "write_timeout": "10m0s", + "idle_timeout": "1m0s" + }, "restapi": { "ssl_cert_file": "server.crt", "ssl_key_file": "server.key", - "listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", + "http_listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", "read_timeout": "30s", "read_header_timeout": "5s", "write_timeout": "1m0s", "idle_timeout": "2m0s", "basic_auth_credentials": { - "testuser" : "testpass", + "testuser": "testpass", "userwithoutpass": "" - } + }, + "cors_allowed_origins": [ + "*" + ], + "cors_allowed_methods": [ + "GET" + ], + "cors_allowed_headers": [], + "cors_exposed_headers": [ + "Content-Type", + "X-Stream-Output", + "X-Chunked-Output", + "X-Content-Length" + ], + "cors_allow_credentials": true, + "cors_max_age": "0s" } }, "ipfs_connector": { "ipfshttp": { - "proxy_listen_multiaddress": "/ip4/127.0.0.1/tcp/9095", "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", - "connect_swarms_delay": "7s", - "proxy_read_timeout": "10m0s", - "proxy_read_header_timeout": "5s", - "proxy_write_timeout": "10m0s", - "proxy_idle_timeout": "1m0s" + "connect_swarms_delay": "30s", + "pin_method": "refs", + "ipfs_request_timeout": "5m0s", + "pin_timeout": "24h0m0s", + "unpin_timeout": "3h0m0s" + } + }, + "pin_tracker": { + "maptracker": { + "max_pin_queue_size": 50000, + "concurrent_pins": 10 + }, + "stateless": { + "max_pin_queue_size": 50000, + "concurrent_pins": 10 } }, "monitor": { "monbasic": { "check_interval": "15s" + }, + "pubsubmon": { + "check_interval": "15s" } }, "informer": { diff --git a/sharness/config/ssl/service.json b/sharness/config/ssl/service.json index 4e2902b3..f639810e 100644 --- a/sharness/config/ssl/service.json +++ b/sharness/config/ssl/service.json @@ -24,30 +24,66 @@ } }, "api": { + "ipfsproxy": { + "listen_multiaddress": "/ip4/127.0.0.1/tcp/9095", + "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", + "read_timeout": "10m0s", + "read_header_timeout": "5s", + "write_timeout": "10m0s", + "idle_timeout": "1m0s" + }, "restapi": { "ssl_cert_file": "server.crt", "ssl_key_file": "server.key", - "listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", + "http_listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", "read_timeout": "30s", "read_header_timeout": "5s", "write_timeout": "1m0s", - "idle_timeout": "2m0s" + "idle_timeout": "2m0s", + "basic_auth_credentials": null, + "cors_allowed_origins": [ + "*" + ], + "cors_allowed_methods": [ + "GET" + ], + "cors_allowed_headers": [], + "cors_exposed_headers": [ + "Content-Type", + "X-Stream-Output", + "X-Chunked-Output", + "X-Content-Length" + ], + "cors_allow_credentials": true, + "cors_max_age": "0s" } }, "ipfs_connector": { "ipfshttp": { - "proxy_listen_multiaddress": "/ip4/127.0.0.1/tcp/9095", "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", - "connect_swarms_delay": "7s", - "proxy_read_timeout": "10m0s", - "proxy_read_header_timeout": "5s", - "proxy_write_timeout": "10m0s", - "proxy_idle_timeout": "1m0s" + "connect_swarms_delay": "30s", + "pin_method": "refs", + "ipfs_request_timeout": "5m0s", + "pin_timeout": "24h0m0s", + "unpin_timeout": "3h0m0s" + } + }, + "pin_tracker": { + "maptracker": { + "max_pin_queue_size": 50000, + "concurrent_pins": 10 + }, + "stateless": { + "max_pin_queue_size": 50000, + "concurrent_pins": 10 } }, "monitor": { "monbasic": { "check_interval": "15s" + }, + "pubsubmon": { + "check_interval": "15s" } }, "informer": {