Skip to content

Commit 7cdfbfe

Browse files
committed
chore: add custom samesite options to auth cookies
Advanced feature, not recommended to use
1 parent 30179ae commit 7cdfbfe

File tree

10 files changed

+78
-29
lines changed

10 files changed

+78
-29
lines changed

cli/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
630630
GoogleTokenValidator: googleTokenValidator,
631631
ExternalAuthConfigs: externalAuthConfigs,
632632
RealIPConfig: realIPConfig,
633-
SecureAuthCookie: vals.SecureAuthCookie.Value(),
633+
Cookies: vals.HTTPCookies,
634634
SSHKeygenAlgorithm: sshKeygenAlgorithm,
635635
TracerProvider: tracerProvider,
636636
Telemetry: telemetry.NewNoop(),

coderd/apikey.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -382,12 +382,10 @@ func (api *API) createAPIKey(ctx context.Context, params apikey.CreateParams) (*
382382
APIKeys: []telemetry.APIKey{telemetry.ConvertAPIKey(newkey)},
383383
})
384384

385-
return &http.Cookie{
385+
return api.Cookies.Apply(&http.Cookie{
386386
Name: codersdk.SessionTokenCookie,
387387
Value: sessionToken,
388388
Path: "/",
389389
HttpOnly: true,
390-
SameSite: http.SameSiteLaxMode,
391-
Secure: api.SecureAuthCookie,
392-
}, &newkey, nil
390+
}), &newkey, nil
393391
}

coderd/coderd.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ type Options struct {
154154
GithubOAuth2Config *GithubOAuth2Config
155155
OIDCConfig *OIDCConfig
156156
PrometheusRegistry *prometheus.Registry
157-
SecureAuthCookie bool
157+
Cookies codersdk.HTTPCookieConfig
158158
StrictTransportSecurityCfg httpmw.HSTSConfig
159159
SSHKeygenAlgorithm gitsshkey.Algorithm
160160
Telemetry telemetry.Reporter
@@ -728,7 +728,7 @@ func New(options *Options) *API {
728728
StatsCollector: workspaceapps.NewStatsCollector(options.WorkspaceAppsStatsCollectorOptions),
729729

730730
DisablePathApps: options.DeploymentValues.DisablePathApps.Value(),
731-
SecureAuthCookie: options.DeploymentValues.SecureAuthCookie.Value(),
731+
Cookies: options.DeploymentValues.HTTPCookies,
732732
APIKeyEncryptionKeycache: options.AppEncryptionKeyCache,
733733
}
734734

@@ -816,7 +816,7 @@ func New(options *Options) *API {
816816
next.ServeHTTP(w, r)
817817
})
818818
},
819-
httpmw.CSRF(options.SecureAuthCookie),
819+
httpmw.CSRF(options.Cookies),
820820
)
821821

822822
// This incurs a performance hit from the middleware, but is required to make sure

coderd/httpmw/authz.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,18 @@ func AsAuthzSystem(mws ...func(http.Handler) http.Handler) func(http.Handler) ht
3535
})
3636
}
3737
}
38+
<<<<<<< Updated upstream
39+
=======
40+
41+
// RecordAuthzChecks enables recording all the authorization checks that
42+
// occurred in the processing of a request. This is mostly helpful for debugging
43+
// and understanding what permissions are required for a given action without
44+
// needing to go hunting for checks in the code, where you're quite likely to
45+
// miss something subtle or a check happening somewhere you didn't expect.
46+
func RecordAuthzChecks(next http.Handler) http.Handler {
47+
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
48+
r = r.WithContext(rbac.WithAuthzCheckRecorder(r.Context()))
49+
next.ServeHTTP(rw, r)
50+
})
51+
}
52+
>>>>>>> Stashed changes

coderd/httpmw/csrf.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import (
1616
// for non-GET requests.
1717
// If enforce is false, then CSRF enforcement is disabled. We still want
1818
// to include the CSRF middleware because it will set the CSRF cookie.
19-
func CSRF(secureCookie bool) func(next http.Handler) http.Handler {
19+
func CSRF(cookieCfg codersdk.HTTPCookieConfig) func(next http.Handler) http.Handler {
2020
return func(next http.Handler) http.Handler {
2121
mw := nosurf.New(next)
22-
mw.SetBaseCookie(http.Cookie{Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, Secure: secureCookie})
22+
mw.SetBaseCookie(*cookieCfg.Apply(&http.Cookie{Path: "/", HttpOnly: true}))
2323
mw.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2424
sessCookie, err := r.Cookie(codersdk.SessionTokenCookie)
2525
if err == nil &&

coderd/httpmw/oauth2.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func OAuth2(r *http.Request) OAuth2State {
4040
// a "code" URL parameter will be redirected.
4141
// AuthURLOpts are passed to the AuthCodeURL function. If this is nil,
4242
// the default option oauth2.AccessTypeOffline will be used.
43-
func ExtractOAuth2(config promoauth.OAuth2Config, client *http.Client, authURLOpts map[string]string) func(http.Handler) http.Handler {
43+
func ExtractOAuth2(config promoauth.OAuth2Config, client *http.Client, cookieCfg codersdk.HTTPCookieConfig, authURLOpts map[string]string) func(http.Handler) http.Handler {
4444
opts := make([]oauth2.AuthCodeOption, 0, len(authURLOpts)+1)
4545
opts = append(opts, oauth2.AccessTypeOffline)
4646
for k, v := range authURLOpts {
@@ -118,22 +118,20 @@ func ExtractOAuth2(config promoauth.OAuth2Config, client *http.Client, authURLOp
118118
}
119119
}
120120

121-
http.SetCookie(rw, &http.Cookie{
121+
http.SetCookie(rw, cookieCfg.Apply(&http.Cookie{
122122
Name: codersdk.OAuth2StateCookie,
123123
Value: state,
124124
Path: "/",
125125
HttpOnly: true,
126-
SameSite: http.SameSiteLaxMode,
127-
})
126+
}))
128127
// Redirect must always be specified, otherwise
129128
// an old redirect could apply!
130-
http.SetCookie(rw, &http.Cookie{
129+
http.SetCookie(rw, cookieCfg.Apply(&http.Cookie{
131130
Name: codersdk.OAuth2RedirectCookie,
132131
Value: redirect,
133132
Path: "/",
134133
HttpOnly: true,
135-
SameSite: http.SameSiteLaxMode,
136-
})
134+
}))
137135

138136
http.Redirect(rw, r, config.AuthCodeURL(state, opts...), http.StatusTemporaryRedirect)
139137
return

coderd/userauth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func (api *API) postConvertLoginType(rw http.ResponseWriter, r *http.Request) {
203203
Path: "/",
204204
Value: token,
205205
Expires: claims.Expiry.Time(),
206-
Secure: api.SecureAuthCookie,
206+
Secure: api.Cookies.Secure.Value(),
207207
HttpOnly: true,
208208
// Must be SameSite to work on the redirected auth flow from the
209209
// oauth provider.
@@ -1911,7 +1911,7 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
19111911
Name: codersdk.SessionTokenCookie,
19121912
Path: "/",
19131913
MaxAge: -1,
1914-
Secure: api.SecureAuthCookie,
1914+
Secure: api.Cookies.Secure.Value(),
19151915
HttpOnly: true,
19161916
})
19171917
// This is intentional setting the key to the deleted old key,

coderd/workspaceapps/provider.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const (
2222
type ResolveRequestOptions struct {
2323
Logger slog.Logger
2424
SignedTokenProvider SignedTokenProvider
25+
CookieCfg codersdk.HTTPCookieConfig
2526

2627
DashboardURL *url.URL
2728
PathAppBaseURL *url.URL
@@ -75,12 +76,12 @@ func ResolveRequest(rw http.ResponseWriter, r *http.Request, opts ResolveRequest
7576
//
7677
// For subdomain apps, this applies to the entire subdomain, e.g.
7778
// app--agent--workspace--user.apps.example.com
78-
http.SetCookie(rw, &http.Cookie{
79+
http.SetCookie(rw, opts.CookieCfg.Apply(&http.Cookie{
7980
Name: codersdk.SignedAppTokenCookie,
8081
Value: tokenStr,
8182
Path: appReq.BasePath,
8283
Expires: token.Expiry.Time(),
83-
})
84+
}))
8485

8586
return token, true
8687
}

coderd/workspaceapps/proxy.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ type Server struct {
110110
//
111111
// Subdomain apps are safer with their cookies scoped to the subdomain, and XSS
112112
// calls to the dashboard are not possible due to CORs.
113-
DisablePathApps bool
114-
SecureAuthCookie bool
113+
DisablePathApps bool
114+
Cookies codersdk.HTTPCookieConfig
115115

116116
AgentProvider AgentProvider
117117
StatsCollector *StatsCollector
@@ -230,16 +230,14 @@ func (s *Server) handleAPIKeySmuggling(rw http.ResponseWriter, r *http.Request,
230230
// We use different cookie names for path apps and for subdomain apps to
231231
// avoid both being set and sent to the server at the same time and the
232232
// server using the wrong value.
233-
http.SetCookie(rw, &http.Cookie{
233+
http.SetCookie(rw, s.Cookies.Apply(&http.Cookie{
234234
Name: AppConnectSessionTokenCookieName(accessMethod),
235235
Value: payload.APIKey,
236236
Domain: domain,
237237
Path: "/",
238238
MaxAge: 0,
239239
HttpOnly: true,
240-
SameSite: http.SameSiteLaxMode,
241-
Secure: s.SecureAuthCookie,
242-
})
240+
}))
243241

244242
// Strip the query parameter.
245243
path := r.URL.Path
@@ -300,6 +298,7 @@ func (s *Server) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request)
300298
// permissions to connect to a workspace.
301299
token, ok := ResolveRequest(rw, r, ResolveRequestOptions{
302300
Logger: s.Logger,
301+
CookieCfg: s.Cookies,
303302
SignedTokenProvider: s.SignedTokenProvider,
304303
DashboardURL: s.DashboardURL,
305304
PathAppBaseURL: s.AccessURL,
@@ -405,6 +404,7 @@ func (s *Server) HandleSubdomain(middlewares ...func(http.Handler) http.Handler)
405404

406405
token, ok := ResolveRequest(rw, r, ResolveRequestOptions{
407406
Logger: s.Logger,
407+
CookieCfg: s.Cookies,
408408
SignedTokenProvider: s.SignedTokenProvider,
409409
DashboardURL: s.DashboardURL,
410410
PathAppBaseURL: s.AccessURL,
@@ -630,6 +630,7 @@ func (s *Server) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
630630

631631
appToken, ok := ResolveRequest(rw, r, ResolveRequestOptions{
632632
Logger: s.Logger,
633+
CookieCfg: s.Cookies,
633634
SignedTokenProvider: s.SignedTokenProvider,
634635
DashboardURL: s.DashboardURL,
635636
PathAppBaseURL: s.AccessURL,

codersdk/deployment.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ type DeploymentValues struct {
358358
Telemetry TelemetryConfig `json:"telemetry,omitempty" typescript:",notnull"`
359359
TLS TLSConfig `json:"tls,omitempty" typescript:",notnull"`
360360
Trace TraceConfig `json:"trace,omitempty" typescript:",notnull"`
361-
SecureAuthCookie serpent.Bool `json:"secure_auth_cookie,omitempty" typescript:",notnull"`
361+
HTTPCookies HTTPCookieConfig `json:"http_cookies,omitempty" typescript:",notnull"`
362362
StrictTransportSecurity serpent.Int64 `json:"strict_transport_security,omitempty" typescript:",notnull"`
363363
StrictTransportSecurityOptions serpent.StringArray `json:"strict_transport_security_options,omitempty" typescript:",notnull"`
364364
SSHKeygenAlgorithm serpent.String `json:"ssh_keygen_algorithm,omitempty" typescript:",notnull"`
@@ -585,6 +585,30 @@ type TraceConfig struct {
585585
DataDog serpent.Bool `json:"data_dog" typescript:",notnull"`
586586
}
587587

588+
type HTTPCookieConfig struct {
589+
Secure serpent.Bool `json:"secure_auth_cookie,omitempty" typescript:",notnull"`
590+
SameSite string `json:"same_site,omitempty" typescript:",notnull"`
591+
}
592+
593+
func (cfg HTTPCookieConfig) Apply(c *http.Cookie) *http.Cookie {
594+
c.Secure = cfg.Secure.Value()
595+
c.SameSite = cfg.HTTPSameSite()
596+
return c
597+
}
598+
599+
func (cfg HTTPCookieConfig) HTTPSameSite() http.SameSite {
600+
switch strings.ToLower(cfg.SameSite) {
601+
case "lax":
602+
return http.SameSiteLaxMode
603+
case "strict":
604+
return http.SameSiteStrictMode
605+
case "none":
606+
return http.SameSiteNoneMode
607+
default:
608+
return http.SameSiteDefaultMode
609+
}
610+
}
611+
588612
type ExternalAuthConfig struct {
589613
// Type is the type of external auth config.
590614
Type string `json:"type" yaml:"type"`
@@ -2363,11 +2387,23 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
23632387
Description: "Controls if the 'Secure' property is set on browser session cookies.",
23642388
Flag: "secure-auth-cookie",
23652389
Env: "CODER_SECURE_AUTH_COOKIE",
2366-
Value: &c.SecureAuthCookie,
2390+
Value: &c.HTTPCookies.Secure,
23672391
Group: &deploymentGroupNetworking,
23682392
YAML: "secureAuthCookie",
23692393
Annotations: serpent.Annotations{}.Mark(annotationExternalProxies, "true"),
23702394
},
2395+
{
2396+
Name: "SameSite Auth Cookie",
2397+
Description: "Controls the 'SameSite' property is set on browser session cookies.",
2398+
Flag: "samesite-auth-cookie",
2399+
Env: "CODER_SAMESITE_AUTH_COOKIE",
2400+
// Do not allow "strict" same-site cookies. That would potentially break workspace apps.
2401+
Value: serpent.EnumOf(&c.HTTPCookies.SameSite, "lax", "none"),
2402+
Default: "lax",
2403+
Group: &deploymentGroupNetworking,
2404+
YAML: "sameSiteAuthCookie",
2405+
Annotations: serpent.Annotations{}.Mark(annotationExternalProxies, "true"),
2406+
},
23712407
{
23722408
Name: "Terms of Service URL",
23732409
Description: "A URL to an external Terms of Service that must be accepted by users when logging in.",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy