diff --git a/cli/server.go b/cli/server.go index da84b8ded0bc7..166bb2ad59fb0 100644 --- a/cli/server.go +++ b/cli/server.go @@ -636,6 +636,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. HostnamePrefix: cfg.SSHConfig.DeploymentName.String(), SSHConfigOptions: configSSHOptions, }, + WorkspaceOwnerConnectionOnly: cfg.WorkspaceOwnerConnectionOnly.Value(), } if tlsConfig != nil { options.TLSCertificates = tlsConfig.Certificates diff --git a/coderd/coderd.go b/coderd/coderd.go index e17ce255b34f4..e5de92ff210a0 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -142,6 +142,8 @@ type Options struct { SSHConfig codersdk.SSHConfigResponse HTTPClient *http.Client + + WorkspaceOwnerConnectionOnly bool } // @title Coder API diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 1d4290f485145..c95870e2fc712 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -555,6 +555,16 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { return } + if api.Options.WorkspaceOwnerConnectionOnly { + user := httpmw.UserAuthorization(r) + if user.Actor.ID != workspace.OwnerID.String() { + httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{ + Message: "Only workspace owners can connect to workspace", + }) + return + } + } + apiAgent, err := convertWorkspaceAgent( api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), @@ -1087,6 +1097,17 @@ func (api *API) workspaceAgentClientCoordinate(rw http.ResponseWriter, r *http.R httpapi.ResourceNotFound(rw) return } + + if api.Options.WorkspaceOwnerConnectionOnly { + user := httpmw.UserAuthorization(r) + if user.Actor.ID != workspace.OwnerID.String() { + httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{ + Message: "Only workspace owners can connect to workspace", + }) + return + } + } + // This is used by Enterprise code to control the functionality of this route. override := api.WorkspaceClientCoordinateOverride.Load() if override != nil { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 9548afec47c6f..07e8e6cae8e48 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -162,6 +162,7 @@ type DeploymentValues struct { GitAuthProviders clibase.Struct[[]GitAuthConfig] `json:"git_auth,omitempty" typescript:",notnull"` SSHConfig SSHConfig `json:"config_ssh,omitempty" typescript:",notnull"` WgtunnelHost clibase.String `json:"wgtunnel_host,omitempty" typescript:",notnull"` + WorkspaceOwnerConnectionOnly clibase.Bool `json:"workspace_owner_connection_only,omitempty" typescript:",notnull"` Config clibase.String `json:"config,omitempty" typescript:",notnull"` WriteConfig clibase.Bool `json:"write_config,omitempty" typescript:",notnull"` @@ -1379,6 +1380,15 @@ Write out the current server configuration to the path specified by --config.`, Default: "", // empty string means pick best server Hidden: true, }, + { + Name: "Workspace Owner Connection Only", + Description: "Specifies whether owners only have access to their workspaces.", + Flag: "workspace-owner-connection-only", + Env: "CODER_WORKSPACE_OWNER_CONNECTION_ONLY", + Default: "false", + Value: &c.WorkspaceOwnerConnectionOnly, + YAML: "workspaceOwnerConnectionOnly", + }, } return opts } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 5a17c399aafde..52b98a9f93ec0 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -368,6 +368,7 @@ export interface DeploymentValues { readonly git_auth?: any readonly config_ssh?: SSHConfig readonly wgtunnel_host?: string + readonly workspace_owner_connection_only?: boolean readonly config?: string readonly write_config?: boolean // Named type "github.com/coder/coder/cli/clibase.HostPort" unknown, using "any"
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: