Skip to content

Commit 5d711fc

Browse files
authored
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
1 parent 1f4f0ef commit 5d711fc

File tree

13 files changed

+80
-18
lines changed

13 files changed

+80
-18
lines changed

coderd/apidoc/docs.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,10 @@ func New(options *Options) *API {
393393

394394
derpHandler := derphttp.Handler(api.DERPServer)
395395
derpHandler, api.derpCloseFunc = tailnet.WithWebsocketSupport(api.DERPServer, derpHandler)
396+
cors := httpmw.Cors(options.DeploymentValues.Dangerous.AllowAllCors.Value())
396397

397398
r.Use(
399+
cors,
398400
httpmw.Recover(api.Logger),
399401
tracing.StatusWriterMiddleware,
400402
tracing.Middleware(api.TracerProvider),
@@ -799,6 +801,10 @@ func New(options *Options) *API {
799801
// Add CSP headers to all static assets and pages. CSP headers only affect
800802
// browsers, so these don't make sense on api routes.
801803
cspMW := httpmw.CSPHeaders(func() []string {
804+
if api.DeploymentValues.Dangerous.AllowAllCors {
805+
// In this mode, allow all external requests
806+
return []string{"*"}
807+
}
802808
if f := api.WorkspaceProxyHostsFn.Load(); f != nil {
803809
return (*f)()
804810
}
@@ -813,7 +819,7 @@ func New(options *Options) *API {
813819
// This is the only route we add before all the middleware.
814820
// We want to time the latency of the request, so any middleware will
815821
// interfere with that timing.
816-
rootRouter.Get("/latency-check", LatencyCheck(api.AccessURL))
822+
rootRouter.Get("/latency-check", cors(LatencyCheck(options.DeploymentValues.Dangerous.AllowAllCors.Value(), api.AccessURL)).ServeHTTP)
817823
rootRouter.Mount("/", r)
818824
api.RootHandler = rootRouter
819825

coderd/httpmw/cors.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package httpmw
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/go-chi/cors"
7+
)
8+
9+
//nolint:revive
10+
func Cors(allowAll bool, origins ...string) func(next http.Handler) http.Handler {
11+
if len(origins) == 0 {
12+
// The default behavior is '*', so putting the empty string defaults to
13+
// the secure behavior of blocking CORs requests.
14+
origins = []string{""}
15+
}
16+
if allowAll {
17+
origins = []string{"*"}
18+
}
19+
return cors.Handler(cors.Options{
20+
AllowedOrigins: origins,
21+
// We only need GET for latency requests
22+
AllowedMethods: []string{http.MethodOptions, http.MethodGet},
23+
AllowedHeaders: []string{"Accept", "Content-Type", "X-LATENCY-CHECK", "X-CSRF-TOKEN"},
24+
// Do not send any cookies
25+
AllowCredentials: false,
26+
})
27+
}

coderd/httpmw/csp.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ func CSPHeaders(websocketHosts func() []string) func(next http.Handler) http.Han
103103
extraConnect := websocketHosts()
104104
if len(extraConnect) > 0 {
105105
for _, extraHost := range extraConnect {
106+
if extraHost == "*" {
107+
// '*' means all
108+
cspSrcs.Append(cspDirectiveConnectSrc, "*")
109+
continue
110+
}
106111
cspSrcs.Append(cspDirectiveConnectSrc, fmt.Sprintf("wss://%[1]s ws://%[1]s", extraHost))
107112
// We also require this to make http/https requests to the workspace proxy for latency checking.
108113
cspSrcs.Append(cspDirectiveConnectSrc, fmt.Sprintf("https://%[1]s http://%[1]s", extraHost))

coderd/latencycheck.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@ import (
66
"strings"
77
)
88

9-
func LatencyCheck(allowedOrigins ...*url.URL) http.HandlerFunc {
9+
// LatencyCheck is an endpoint for the web ui to measure latency with.
10+
// allowAll allows any Origin to get timing information. The allowAll should
11+
// only be set in dev modes.
12+
//
13+
//nolint:revive
14+
func LatencyCheck(allowAll bool, allowedOrigins ...*url.URL) http.HandlerFunc {
1015
allowed := make([]string, 0, len(allowedOrigins))
1116
for _, origin := range allowedOrigins {
1217
// Allow the origin without a path
1318
tmp := *origin
1419
tmp.Path = ""
1520
allowed = append(allowed, strings.TrimSuffix(origin.String(), "/"))
1621
}
22+
if allowAll {
23+
allowed = append(allowed, "*")
24+
}
1725
origins := strings.Join(allowed, ",")
1826
return func(rw http.ResponseWriter, r *http.Request) {
1927
// Allowing timing information to be shared. This allows the browser

codersdk/deployment.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ type LoggingConfig struct {
330330
type DangerousConfig struct {
331331
AllowPathAppSharing clibase.Bool `json:"allow_path_app_sharing" typescript:",notnull"`
332332
AllowPathAppSiteOwnerAccess clibase.Bool `json:"allow_path_app_site_owner_access" typescript:",notnull"`
333+
AllowAllCors clibase.Bool `json:"allow_all_cors" typescript:",notnull"`
333334
}
334335

335336
const (
@@ -1167,6 +1168,16 @@ when required by your organization's security policy.`,
11671168
Annotations: clibase.Annotations{}.Mark(annotationExternalProxies, "true"),
11681169
},
11691170
// ☢️ Dangerous settings
1171+
{
1172+
Name: "DANGEROUS: Allow all CORs requests",
1173+
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.",
1174+
Flag: "dangerous-allow-cors-requests",
1175+
Env: "CODER_DANGEROUS_ALLOW_CORS_REQUESTS",
1176+
Hidden: true, // Hidden, should only be used by yarn dev server
1177+
Value: &c.Dangerous.AllowAllCors,
1178+
Group: &deploymentGroupDangerous,
1179+
Annotations: clibase.Annotations{}.Mark(annotationExternalProxies, "true"),
1180+
},
11701181
{
11711182
Name: "DANGEROUS: Allow Path App Sharing",
11721183
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.",

docs/api/general.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
161161
"sshconfigOptions": ["string"]
162162
},
163163
"dangerous": {
164+
"allow_all_cors": true,
164165
"allow_path_app_sharing": true,
165166
"allow_path_app_site_owner_access": true
166167
},

docs/api/schemas.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
18001800

18011801
```json
18021802
{
1803+
"allow_all_cors": true,
18031804
"allow_path_app_sharing": true,
18041805
"allow_path_app_site_owner_access": true
18051806
}
@@ -1809,6 +1810,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
18091810

18101811
| Name | Type | Required | Restrictions | Description |
18111812
| ---------------------------------- | ------- | -------- | ------------ | ----------- |
1813+
| `allow_all_cors` | boolean | false | | |
18121814
| `allow_path_app_sharing` | boolean | false | | |
18131815
| `allow_path_app_site_owner_access` | boolean | false | | |
18141816

@@ -1857,6 +1859,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
18571859
"sshconfigOptions": ["string"]
18581860
},
18591861
"dangerous": {
1862+
"allow_all_cors": true,
18601863
"allow_path_app_sharing": true,
18611864
"allow_path_app_site_owner_access": true
18621865
},
@@ -2201,6 +2204,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
22012204
"sshconfigOptions": ["string"]
22022205
},
22032206
"dangerous": {
2207+
"allow_all_cors": true,
22042208
"allow_path_app_sharing": true,
22052209
"allow_path_app_site_owner_access": true
22062210
},

enterprise/cli/proxyserver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ func (*RootCmd) proxyServer() *clibase.Cmd {
245245
SecureAuthCookie: cfg.SecureAuthCookie.Value(),
246246
DisablePathApps: cfg.DisablePathApps.Value(),
247247
ProxySessionToken: proxySessionToken.Value(),
248+
AllowAllCors: cfg.Dangerous.AllowAllCors.Value(),
248249
})
249250
if err != nil {
250251
return xerrors.Errorf("create workspace proxy: %w", err)

0 commit comments

Comments
 (0)