mirror of https://github.com/coder/coder.git
chore: CORs option for yarn dev server (#7630)
* chore: Yarn dev servers require CORs headers for external proxies Adds a flag to set CORs headers to `*` for yarn dev servers
This commit is contained in:
parent
1f4f0efed6
commit
5d711fc95a
|
@ -7252,6 +7252,9 @@ const docTemplate = `{
|
|||
"codersdk.DangerousConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow_all_cors": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"allow_path_app_sharing": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -6454,6 +6454,9 @@
|
|||
"codersdk.DangerousConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow_all_cors": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"allow_path_app_sharing": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -393,8 +393,10 @@ func New(options *Options) *API {
|
|||
|
||||
derpHandler := derphttp.Handler(api.DERPServer)
|
||||
derpHandler, api.derpCloseFunc = tailnet.WithWebsocketSupport(api.DERPServer, derpHandler)
|
||||
cors := httpmw.Cors(options.DeploymentValues.Dangerous.AllowAllCors.Value())
|
||||
|
||||
r.Use(
|
||||
cors,
|
||||
httpmw.Recover(api.Logger),
|
||||
tracing.StatusWriterMiddleware,
|
||||
tracing.Middleware(api.TracerProvider),
|
||||
|
@ -799,6 +801,10 @@ func New(options *Options) *API {
|
|||
// Add CSP headers to all static assets and pages. CSP headers only affect
|
||||
// browsers, so these don't make sense on api routes.
|
||||
cspMW := httpmw.CSPHeaders(func() []string {
|
||||
if api.DeploymentValues.Dangerous.AllowAllCors {
|
||||
// In this mode, allow all external requests
|
||||
return []string{"*"}
|
||||
}
|
||||
if f := api.WorkspaceProxyHostsFn.Load(); f != nil {
|
||||
return (*f)()
|
||||
}
|
||||
|
@ -813,7 +819,7 @@ func New(options *Options) *API {
|
|||
// This is the only route we add before all the middleware.
|
||||
// We want to time the latency of the request, so any middleware will
|
||||
// interfere with that timing.
|
||||
rootRouter.Get("/latency-check", LatencyCheck(api.AccessURL))
|
||||
rootRouter.Get("/latency-check", cors(LatencyCheck(options.DeploymentValues.Dangerous.AllowAllCors.Value(), api.AccessURL)).ServeHTTP)
|
||||
rootRouter.Mount("/", r)
|
||||
api.RootHandler = rootRouter
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package httpmw
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/cors"
|
||||
)
|
||||
|
||||
//nolint:revive
|
||||
func Cors(allowAll bool, origins ...string) func(next http.Handler) http.Handler {
|
||||
if len(origins) == 0 {
|
||||
// The default behavior is '*', so putting the empty string defaults to
|
||||
// the secure behavior of blocking CORs requests.
|
||||
origins = []string{""}
|
||||
}
|
||||
if allowAll {
|
||||
origins = []string{"*"}
|
||||
}
|
||||
return cors.Handler(cors.Options{
|
||||
AllowedOrigins: origins,
|
||||
// We only need GET for latency requests
|
||||
AllowedMethods: []string{http.MethodOptions, http.MethodGet},
|
||||
AllowedHeaders: []string{"Accept", "Content-Type", "X-LATENCY-CHECK", "X-CSRF-TOKEN"},
|
||||
// Do not send any cookies
|
||||
AllowCredentials: false,
|
||||
})
|
||||
}
|
|
@ -103,6 +103,11 @@ func CSPHeaders(websocketHosts func() []string) func(next http.Handler) http.Han
|
|||
extraConnect := websocketHosts()
|
||||
if len(extraConnect) > 0 {
|
||||
for _, extraHost := range extraConnect {
|
||||
if extraHost == "*" {
|
||||
// '*' means all
|
||||
cspSrcs.Append(cspDirectiveConnectSrc, "*")
|
||||
continue
|
||||
}
|
||||
cspSrcs.Append(cspDirectiveConnectSrc, fmt.Sprintf("wss://%[1]s ws://%[1]s", extraHost))
|
||||
// We also require this to make http/https requests to the workspace proxy for latency checking.
|
||||
cspSrcs.Append(cspDirectiveConnectSrc, fmt.Sprintf("https://%[1]s http://%[1]s", extraHost))
|
||||
|
|
|
@ -6,7 +6,12 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func LatencyCheck(allowedOrigins ...*url.URL) http.HandlerFunc {
|
||||
// LatencyCheck is an endpoint for the web ui to measure latency with.
|
||||
// allowAll allows any Origin to get timing information. The allowAll should
|
||||
// only be set in dev modes.
|
||||
//
|
||||
//nolint:revive
|
||||
func LatencyCheck(allowAll bool, allowedOrigins ...*url.URL) http.HandlerFunc {
|
||||
allowed := make([]string, 0, len(allowedOrigins))
|
||||
for _, origin := range allowedOrigins {
|
||||
// Allow the origin without a path
|
||||
|
@ -14,6 +19,9 @@ func LatencyCheck(allowedOrigins ...*url.URL) http.HandlerFunc {
|
|||
tmp.Path = ""
|
||||
allowed = append(allowed, strings.TrimSuffix(origin.String(), "/"))
|
||||
}
|
||||
if allowAll {
|
||||
allowed = append(allowed, "*")
|
||||
}
|
||||
origins := strings.Join(allowed, ",")
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
// Allowing timing information to be shared. This allows the browser
|
||||
|
|
|
@ -330,6 +330,7 @@ type LoggingConfig struct {
|
|||
type DangerousConfig struct {
|
||||
AllowPathAppSharing clibase.Bool `json:"allow_path_app_sharing" typescript:",notnull"`
|
||||
AllowPathAppSiteOwnerAccess clibase.Bool `json:"allow_path_app_site_owner_access" typescript:",notnull"`
|
||||
AllowAllCors clibase.Bool `json:"allow_all_cors" typescript:",notnull"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1167,6 +1168,16 @@ when required by your organization's security policy.`,
|
|||
Annotations: clibase.Annotations{}.Mark(annotationExternalProxies, "true"),
|
||||
},
|
||||
// ☢️ Dangerous settings
|
||||
{
|
||||
Name: "DANGEROUS: Allow all CORs requests",
|
||||
Description: "For security reasons, CORs requests are blocked. If external requests are required, setting this to true will set all cors headers as '*'. This should never be used in production.",
|
||||
Flag: "dangerous-allow-cors-requests",
|
||||
Env: "CODER_DANGEROUS_ALLOW_CORS_REQUESTS",
|
||||
Hidden: true, // Hidden, should only be used by yarn dev server
|
||||
Value: &c.Dangerous.AllowAllCors,
|
||||
Group: &deploymentGroupDangerous,
|
||||
Annotations: clibase.Annotations{}.Mark(annotationExternalProxies, "true"),
|
||||
},
|
||||
{
|
||||
Name: "DANGEROUS: Allow Path App Sharing",
|
||||
Description: "Allow workspace apps that are not served from subdomains to be shared. Path-based app sharing is DISABLED by default for security purposes. Path-based apps can make requests to the Coder API and pose a security risk when the workspace serves malicious JavaScript. Path-based apps can be disabled entirely with --disable-path-apps for further security.",
|
||||
|
|
|
@ -161,6 +161,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
|
|||
"sshconfigOptions": ["string"]
|
||||
},
|
||||
"dangerous": {
|
||||
"allow_all_cors": true,
|
||||
"allow_path_app_sharing": true,
|
||||
"allow_path_app_site_owner_access": true
|
||||
},
|
||||
|
|
|
@ -1800,6 +1800,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
|
|||
|
||||
```json
|
||||
{
|
||||
"allow_all_cors": true,
|
||||
"allow_path_app_sharing": true,
|
||||
"allow_path_app_site_owner_access": true
|
||||
}
|
||||
|
@ -1809,6 +1810,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
|
|||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ---------------------------------- | ------- | -------- | ------------ | ----------- |
|
||||
| `allow_all_cors` | boolean | false | | |
|
||||
| `allow_path_app_sharing` | boolean | false | | |
|
||||
| `allow_path_app_site_owner_access` | boolean | false | | |
|
||||
|
||||
|
@ -1857,6 +1859,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
|
|||
"sshconfigOptions": ["string"]
|
||||
},
|
||||
"dangerous": {
|
||||
"allow_all_cors": true,
|
||||
"allow_path_app_sharing": true,
|
||||
"allow_path_app_site_owner_access": true
|
||||
},
|
||||
|
@ -2201,6 +2204,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
|
|||
"sshconfigOptions": ["string"]
|
||||
},
|
||||
"dangerous": {
|
||||
"allow_all_cors": true,
|
||||
"allow_path_app_sharing": true,
|
||||
"allow_path_app_site_owner_access": true
|
||||
},
|
||||
|
|
|
@ -245,6 +245,7 @@ func (*RootCmd) proxyServer() *clibase.Cmd {
|
|||
SecureAuthCookie: cfg.SecureAuthCookie.Value(),
|
||||
DisablePathApps: cfg.DisablePathApps.Value(),
|
||||
ProxySessionToken: proxySessionToken.Value(),
|
||||
AllowAllCors: cfg.Dangerous.AllowAllCors.Value(),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create workspace proxy: %w", err)
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
@ -61,6 +60,10 @@ type Options struct {
|
|||
DisablePathApps bool
|
||||
|
||||
ProxySessionToken string
|
||||
// AllowAllCors will set all CORs headers to '*'.
|
||||
// By default, CORs is set to accept external requests
|
||||
// from the dashboardURL. This should only be used in development.
|
||||
AllowAllCors bool
|
||||
}
|
||||
|
||||
func (o *Options) Validate() error {
|
||||
|
@ -189,18 +192,7 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
|
|||
|
||||
// The primary coderd dashboard needs to make some GET requests to
|
||||
// the workspace proxies to check latency.
|
||||
corsMW := cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{
|
||||
// Allow the dashboard to make requests to the proxy for latency
|
||||
// checks.
|
||||
opts.DashboardURL.String(),
|
||||
},
|
||||
// Only allow GET requests for latency checks.
|
||||
AllowedMethods: []string{http.MethodOptions, http.MethodGet},
|
||||
AllowedHeaders: []string{"Accept", "Content-Type", "X-LATENCY-CHECK", "X-CSRF-TOKEN"},
|
||||
// Do not send any cookies
|
||||
AllowCredentials: false,
|
||||
})
|
||||
corsMW := httpmw.Cors(opts.AllowAllCors, opts.DashboardURL.String())
|
||||
|
||||
// Routes
|
||||
apiRateLimiter := httpmw.RateLimit(opts.APIRateLimit, time.Minute)
|
||||
|
@ -266,7 +258,7 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
|
|||
// See coderd/coderd.go for why we need this.
|
||||
rootRouter := chi.NewRouter()
|
||||
// Make sure to add the cors middleware to the latency check route.
|
||||
rootRouter.Get("/latency-check", corsMW(coderd.LatencyCheck(s.DashboardURL, s.AppServer.AccessURL)).ServeHTTP)
|
||||
rootRouter.Get("/latency-check", corsMW(coderd.LatencyCheck(opts.AllowAllCors, s.DashboardURL, s.AppServer.AccessURL)).ServeHTTP)
|
||||
rootRouter.Mount("/", r)
|
||||
s.Handler = rootRouter
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ fatal() {
|
|||
trap 'fatal "Script encountered an error"' ERR
|
||||
|
||||
cdroot
|
||||
start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000 --swagger-enable --access-url "http://127.0.0.1:3000" --experiments "*" "$@"
|
||||
start_cmd API "" "${CODER_DEV_SHIM}" server --http-address 0.0.0.0:3000 --swagger-enable --access-url "http://127.0.0.1:3000" --dangerous-allow-cors-requests=true --experiments "*" "$@"
|
||||
|
||||
echo '== Waiting for Coder to become ready'
|
||||
# Start the timeout in the background so interrupting this script
|
||||
|
@ -185,7 +185,7 @@ fatal() {
|
|||
# Create the proxy
|
||||
proxy_session_token=$("${CODER_DEV_SHIM}" wsproxy create --name=local-proxy --display-name="Local Proxy" --icon="/emojis/1f4bb.png" --only-token)
|
||||
# Start the proxy
|
||||
start_cmd PROXY "" "${CODER_DEV_SHIM}" wsproxy server --http-address=localhost:3010 --proxy-session-token="${proxy_session_token}" --primary-access-url=http://localhost:3000
|
||||
start_cmd PROXY "" "${CODER_DEV_SHIM}" wsproxy server --dangerous-allow-cors-requests=true --http-address=localhost:3010 --proxy-session-token="${proxy_session_token}" --primary-access-url=http://localhost:3000
|
||||
) || echo "Failed to create workspace proxy. No workspace proxy created."
|
||||
fi
|
||||
|
||||
|
|
|
@ -313,6 +313,7 @@ export interface DERPServerConfig {
|
|||
export interface DangerousConfig {
|
||||
readonly allow_path_app_sharing: boolean
|
||||
readonly allow_path_app_site_owner_access: boolean
|
||||
readonly allow_all_cors: boolean
|
||||
}
|
||||
|
||||
// From codersdk/deployment.go
|
||||
|
|
Loading…
Reference in New Issue