diff --git a/coderd/httpmw/csrf.go b/coderd/httpmw/csrf.go index 2a1f383a7490a..7888365741873 100644 --- a/coderd/httpmw/csrf.go +++ b/coderd/httpmw/csrf.go @@ -19,25 +19,23 @@ func CSRF(secureCookie bool) func(next http.Handler) http.Handler { mw.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Something is wrong with your CSRF token. Please refresh the page. If this error persists, try clearing your cookies.", http.StatusBadRequest) })) - // Exempt all requests that do not require CSRF protection. // All GET requests are exempt by default. mw.ExemptPath("/api/v2/csp/reports") - // Top level agent routes. - mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceagents/[^/]*$")) // Agent authenticated routes mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceagents/me/*")) + mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceagents/*")) + // Workspace Proxy routes + mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceproxies/me/*")) // Derp routes mw.ExemptRegexp(regexp.MustCompile("derp/*")) + // Scim + mw.ExemptRegexp(regexp.MustCompile("api/v2/scim/*")) + // Provisioner daemon routes + mw.ExemptRegexp(regexp.MustCompile("/organizations/[^/]+/provisionerdaemons/*")) mw.ExemptFunc(func(r *http.Request) bool { - // Enable CSRF in November 2022 by deleting this "return true" line. - // CSRF is not enforced to ensure backwards compatibility with older - // cli versions. - //nolint:revive - return true - // CSRF only affects requests that automatically attach credentials via a cookie. // If no cookie is present, then there is no risk of CSRF. //nolint:govet @@ -59,6 +57,13 @@ func CSRF(secureCookie bool) func(next http.Handler) http.Handler { return true } + if r.Header.Get(codersdk.ProvisionerDaemonPSK) != "" { + // If present, the provisioner daemon also is providing an api key + // that will make them exempt from CSRF. But this is still useful + // for enumerating the external auths. + return true + } + // If the X-CSRF-TOKEN header is set, we can exempt the func if it's valid. // This is the CSRF check. sent := r.Header.Get("X-CSRF-TOKEN") diff --git a/site/vite.config.ts b/site/vite.config.ts index b0fa3e8be5168..82d32c63a655f 100644 --- a/site/vite.config.ts +++ b/site/vite.config.ts @@ -38,6 +38,18 @@ export default defineConfig({ }, server: { port: process.env.PORT ? Number(process.env.PORT) : 8080, + headers: { + // This header corresponds to "src/api/api.ts"'s hardcoded FE token. + // This is the secret side of the CSRF double cookie submit method. + // This should be sent on **every** response from the webserver. + // + // This is required because in production, the Golang webserver generates + // this "Set-Cookie" header. The Vite webserver needs to replicate this + // behavior. Instead of implementing CSRF though, we just use static + // values for simplicity. + "Set-Cookie": + "csrf_token=JXm9hOUdZctWt0ZZGAy9xiS/gxMKYOThdxjjMnMUyn4=; Path=/; HttpOnly; SameSite=Lax", + }, proxy: { "/api": { ws: true,