From 86f0f39863a27040acd17dd6bc354cc6a430df7c Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 21 Oct 2025 09:15:37 -0500 Subject: [PATCH] chore: make authz recorder opt in (#20310) The authz recorder is causing a lot of memory to be allocated, and is a memory leak for websocket connections. This change makes it opt-in on a per request basis (ontop of `isDev`). To get the authz headers, use `Copy as cURL` on chrome and append the header `x-authz-checks=true`. --- coderd/apidoc/docs.go | 3 +++ coderd/apidoc/swagger.json | 3 +++ coderd/coderd.go | 2 +- coderd/httpmw/authz.go | 23 +++++++++++++++++------ codersdk/deployment.go | 14 ++++++++++++++ docs/reference/api/general.md | 1 + docs/reference/api/schemas.md | 3 +++ site/src/api/typesGenerated.ts | 1 + 8 files changed, 43 insertions(+), 7 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 8046cab6c5..6c5cb1506f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -13977,6 +13977,9 @@ const docTemplate = `{ "docs_url": { "$ref": "#/definitions/serpent.URL" }, + "enable_authz_recording": { + "type": "boolean" + }, "enable_terraform_debug_mode": { "type": "boolean" }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index d5b625a2e6..1b492d7649 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -12595,6 +12595,9 @@ "docs_url": { "$ref": "#/definitions/serpent.URL" }, + "enable_authz_recording": { + "type": "boolean" + }, "enable_terraform_debug_mode": { "type": "boolean" }, diff --git a/coderd/coderd.go b/coderd/coderd.go index 122018f96d..dd8d053624 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -493,7 +493,7 @@ func New(options *Options) *API { // We add this middleware early, to make sure that authorization checks made // by other middleware get recorded. if buildinfo.IsDev() { - r.Use(httpmw.RecordAuthzChecks) + r.Use(httpmw.RecordAuthzChecks(options.DeploymentValues.EnableAuthzRecording.Value())) } ctx, cancel := context.WithCancel(context.Background()) diff --git a/coderd/httpmw/authz.go b/coderd/httpmw/authz.go index 9f1f397c85..758f95cad2 100644 --- a/coderd/httpmw/authz.go +++ b/coderd/httpmw/authz.go @@ -4,6 +4,7 @@ package httpmw import ( "net/http" + "strconv" "github.com/go-chi/chi/v5" @@ -39,14 +40,24 @@ func AsAuthzSystem(mws ...func(http.Handler) http.Handler) func(http.Handler) ht } } -// RecordAuthzChecks enables recording all of the authorization checks that +// RecordAuthzChecks enables recording all the authorization checks that // occurred in the processing of a request. This is mostly helpful for debugging // and understanding what permissions are required for a given action. // +// Can either be toggled on by a deployment wide configuration value, or opt-in on +// a per-request basis by setting the `x-record-authz-checks` header to a truthy value. +// // Requires using a Recorder Authorizer. -func RecordAuthzChecks(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - r = r.WithContext(rbac.WithAuthzCheckRecorder(r.Context())) - next.ServeHTTP(rw, r) - }) +// +//nolint:revive +func RecordAuthzChecks(always bool) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if enabled, _ := strconv.ParseBool(r.Header.Get("x-record-authz-checks")); enabled || always { + r = r.WithContext(rbac.WithAuthzCheckRecorder(r.Context())) + } + + next.ServeHTTP(rw, r) + }) + } } diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 9549b0b98e..008ae6e2b9 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -487,6 +487,7 @@ type DeploymentValues struct { Sessions SessionLifetime `json:"session_lifetime,omitempty" typescript:",notnull"` DisablePasswordAuth serpent.Bool `json:"disable_password_auth,omitempty" typescript:",notnull"` Support SupportConfig `json:"support,omitempty" typescript:",notnull"` + EnableAuthzRecording serpent.Bool `json:"enable_authz_recording,omitempty" typescript:",notnull"` ExternalAuthConfigs serpent.Struct[[]ExternalAuthConfig] `json:"external_auth,omitempty" typescript:",notnull"` SSHConfig SSHConfig `json:"config_ssh,omitempty" typescript:",notnull"` WgtunnelHost serpent.String `json:"wgtunnel_host,omitempty" typescript:",notnull"` @@ -3293,6 +3294,19 @@ Write out the current server config as YAML to stdout.`, YAML: "key", Hidden: true, }, + { + Name: "Enable Authorization Recordings", + Description: "All api requests will have a header including all authorization calls made during the request. " + + "This is used for debugging purposes and only available for dev builds.", + Required: false, + Flag: "enable-authz-recordings", + Env: "CODER_ENABLE_AUTHZ_RECORDINGS", + Default: "false", + Value: &c.EnableAuthzRecording, + // Do not show this option ever. It is a developer tool only, and not to be + // used externally. + Hidden: true, + }, } return opts diff --git a/docs/reference/api/general.md b/docs/reference/api/general.md index a89376db38..0f3330da79 100644 --- a/docs/reference/api/general.md +++ b/docs/reference/api/general.md @@ -237,6 +237,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "scheme": "string", "user": {} }, + "enable_authz_recording": true, "enable_terraform_debug_mode": true, "ephemeral_deployment": true, "experiments": [ diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 91a894528e..654dc0f45b 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -2892,6 +2892,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "scheme": "string", "user": {} }, + "enable_authz_recording": true, "enable_terraform_debug_mode": true, "ephemeral_deployment": true, "experiments": [ @@ -3397,6 +3398,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "scheme": "string", "user": {} }, + "enable_authz_recording": true, "enable_terraform_debug_mode": true, "ephemeral_deployment": true, "experiments": [ @@ -3731,6 +3733,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `disable_password_auth` | boolean | false | | | | `disable_path_apps` | boolean | false | | | | `docs_url` | [serpent.URL](#serpenturl) | false | | | +| `enable_authz_recording` | boolean | false | | | | `enable_terraform_debug_mode` | boolean | false | | | | `ephemeral_deployment` | boolean | false | | | | `experiments` | array of string | false | | | diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 732fbb5af0..932f72a383 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1755,6 +1755,7 @@ export interface DeploymentValues { readonly session_lifetime?: SessionLifetime; readonly disable_password_auth?: boolean; readonly support?: SupportConfig; + readonly enable_authz_recording?: boolean; readonly external_auth?: SerpentStruct; readonly config_ssh?: SSHConfig; readonly wgtunnel_host?: string;