Movatterモバイル変換


[0]ホーム

URL:


workspaceapps

package
v2.23.0Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2025 License:AGPL-3.0Imports:41Imported by:0

Details

Repository

github.com/coder/coder

Links

Documentation

Index

Constants

View Source
const (// TODO(@deansheather): configurable expiryDefaultTokenExpiry =time.Minute// RedirectURIQueryParam is the query param for the app URL to be passed// back to the API auth endpoint on the main access URL.RedirectURIQueryParam = "redirect_uri")
View Source
const (DefaultStatsCollectorReportInterval = 30 *time.SecondDefaultStatsCollectorRollupWindow   = 1 *time.MinuteDefaultStatsDBReporterBatchSize     = 1024)
View Source
const (// This needs to be a super unique query parameter because we don't want to// conflict with query parameters that users may use.//nolint:gosecSubdomainProxyAPIKeyParam = "coder_application_connect_api_key_35e783")

Variables

This section is empty.

Functions

funcAppConnectSessionTokenCookieNameadded inv2.1.5

func AppConnectSessionTokenCookieName(accessMethodAccessMethod)string

AppConnectSessionTokenCookieName returns the cookie name for the sessiontoken for the given access method.

funcAppConnectSessionTokenFromRequestadded inv2.1.5

func AppConnectSessionTokenFromRequest(r *http.Request, accessMethodAccessMethod)string

AppConnectSessionTokenFromRequest returns the session token from the requestif it exists. The access method is used to determine which cookie name touse.

We use different cookie names for path apps and for subdomain apps to avoidboth being set and sent to the server at the same time and the server usingthe wrong value.

We use different cookie names for:- path apps on primary access URL: coder_session_token- path apps on proxies: coder_path_app_session_token- subdomain apps: coder_subdomain_app_session_token

First we try the default function to get a token from request, which supportsquery parameters, the Coder-Session-Token header and the coder_session_tokencookie.

Then we try the specific cookie name for the access method.

funcWebsocketNetConn

func WebsocketNetConn(ctxcontext.Context, conn *websocket.Conn, msgTypewebsocket.MessageType) (context.Context,net.Conn)

WebsocketNetConn wraps websocket.NetConn and returns a context thatis tied to the parent context and the lifetime of the conn. Any errorduring read or write will cancel the context, but not close theconn. Close should be called to release context resources.

funcWriteWorkspaceApp404

func WriteWorkspaceApp404(logslog.Logger, accessURL *url.URL, rwhttp.ResponseWriter, r *http.Request, appReq *Request, warnings []string, detailsstring)

WriteWorkspaceApp404 writes a HTML 404 error page for a workspace app. IfappReq is not nil, it will be used to log the request details at debug level.

The 'warnings' parameter is sent to the user, 'details' is only shown in the logs.

funcWriteWorkspaceApp500

func WriteWorkspaceApp500(logslog.Logger, accessURL *url.URL, rwhttp.ResponseWriter, r *http.Request, appReq *Request, errerror, msgstring)

WriteWorkspaceApp500 writes a HTML 500 error page for a workspace app. IfappReq is not nil, it's fields will be added to the logged error message.

funcWriteWorkspaceAppOffline

func WriteWorkspaceAppOffline(logslog.Logger, accessURL *url.URL, rwhttp.ResponseWriter, r *http.Request, appReq *Request, msgstring)

WriteWorkspaceAppOffline writes a HTML 502 error page for a workspace app. IfappReq is not nil, it will be used to log the request details at debug level.

funcWriteWorkspaceOfflineadded inv2.7.0

func WriteWorkspaceOffline(logslog.Logger, accessURL *url.URL, rwhttp.ResponseWriter, r *http.Request, appReq *Request)

WriteWorkspaceOffline writes a HTML 400 error page for a workspace app. IfappReq is not nil, it will be used to log the request details at debug level.

Types

typeAccessMethod

type AccessMethodstring
const (AccessMethodPathAccessMethod = "path"AccessMethodSubdomainAccessMethod = "subdomain"// AccessMethodTerminal is special since it's not a real app and only// applies to the PTY endpoint on the API.AccessMethodTerminalAccessMethod = "terminal")

typeAgentProvider

type AgentProvider interface {// ReverseProxy returns an httputil.ReverseProxy for proxying HTTP requests// to the specified agent.ReverseProxy(targetURL, dashboardURL *url.URL, agentIDuuid.UUID, appappurl.ApplicationURL, wildcardHoststring) *httputil.ReverseProxy// AgentConn returns a new connection to the specified agent.AgentConn(ctxcontext.Context, agentIDuuid.UUID) (_ *workspacesdk.AgentConn, release func(), _error)ServeHTTPDebug(whttp.ResponseWriter, r *http.Request)Close()error}

typeDBTokenProvider

type DBTokenProvider struct {Loggerslog.Logger// DashboardURL is the main dashboard access URL for error pages.DashboardURL                    *url.URLAuthorizerrbac.AuthorizerAuditor                         *atomic.Pointer[audit.Auditor]Databasedatabase.StoreDeploymentValues                *codersdk.DeploymentValuesOAuth2Configs                   *httpmw.OAuth2ConfigsWorkspaceAgentInactiveTimeouttime.DurationWorkspaceAppAuditSessionTimeouttime.DurationKeycachecryptokeys.SigningKeycache}

DBTokenProvider provides authentication and authorization for workspace appsby querying the database if the request is missing a valid token.

func (*DBTokenProvider)FromRequest

func (p *DBTokenProvider) FromRequest(r *http.Request) (*SignedToken,bool)

func (*DBTokenProvider)Issue

typeEncryptedAPIKeyPayload

type EncryptedAPIKeyPayload struct {jwtutils.RegisteredClaimsAPIKeystring `json:"api_key"`}

func (*EncryptedAPIKeyPayload)Filladded inv2.17.0

func (e *EncryptedAPIKeyPayload) Fill(nowtime.Time)

func (EncryptedAPIKeyPayload)Validateadded inv2.17.0

typeIssueTokenRequest

type IssueTokenRequest struct {AppRequestRequest `json:"app_request"`// PathAppBaseURL is required.PathAppBaseURLstring `json:"path_app_base_url"`// AppHostname is the optional hostname for subdomain apps on the external// proxy. It must start with an asterisk.AppHostnamestring `json:"app_hostname"`// AppPath is the path of the user underneath the app base path.AppPathstring `json:"app_path"`// AppQuery is the query parameters the user provided in the app request.AppQuerystring `json:"app_query"`// SessionToken is the session token provided by the user.SessionTokenstring `json:"session_token"`}

func (IssueTokenRequest)AppBaseURL

func (rIssueTokenRequest) AppBaseURL() (*url.URL,error)

AppBaseURL returns the base URL of this specific app request. An error isreturned if a subdomain app hostname is not provided but the app is asubdomain app.

typeRequest

type Request struct {AccessMethodAccessMethod `json:"access_method"`// BasePath of the app. For path apps, this is the path prefix in the router// for this particular app. For subdomain apps, this should be "/". This is// used for setting the cookie path.BasePathstring `json:"base_path"`// Prefix is the prefix of the subdomain app URL. Prefix should have a// trailing "---" if set.Prefixstring `json:"app_prefix"`// For the following fields, if the AccessMethod is AccessMethodTerminal,// then only AgentNameOrID may be set and it must be a UUID. The other// fields must be left blank.UsernameOrIDstring `json:"username_or_id"`// WorkspaceAndAgent xor WorkspaceNameOrID are required.WorkspaceAndAgentstring `json:"-"`// "workspace" or "workspace.agent"WorkspaceNameOrIDstring `json:"workspace_name_or_id"`// AgentNameOrID is not required if the workspace has only one agent.AgentNameOrIDstring `json:"agent_name_or_id"`AppSlugOrPortstring `json:"app_slug_or_port"`}

func (Request)Checkadded inv2.17.0

func (rRequest) Check()error

Check ensures the request is correct and contains the necessaryparameters.

func (Request)Normalize

func (rRequest) Normalize()Request

Normalize replaces WorkspaceAndAgent with WorkspaceNameOrID andAgentNameOrID. This must be called before Validate.

typeResolveRequestOptions

type ResolveRequestOptions struct {Loggerslog.LoggerSignedTokenProviderSignedTokenProviderCookieCfgcodersdk.HTTPCookieConfigDashboardURL   *url.URLPathAppBaseURL *url.URLAppHostnamestringAppRequestRequest// TODO: Replace these 2 fields with a "BrowserURL" field which is used for// redirecting the user back to their initial request after authenticating.// AppPath is the path under the app that was hit.AppPathstring// AppQuery is the raw query of the request.AppQuerystring}

typeServer

type Server struct {Loggerslog.Logger// DashboardURL should be a url to the coderd dashboard. This can be the// same as the AccessURL if the Server is embedded.DashboardURL *url.URLAccessURL    *url.URL// Hostname should be the wildcard hostname to use for workspace// applications INCLUDING the asterisk, (optional) suffix and leading dot.// It will use the same scheme and port number as the access URL.// E.g. "*.apps.coder.com" or "*-apps.coder.com".Hostnamestring// HostnameRegex contains the regex version of Hostname as generated by// appurl.CompileHostnamePattern(). It MUST be set if Hostname is set.HostnameRegex *regexp.RegexpRealIPConfig  *httpmw.RealIPConfigSignedTokenProviderSignedTokenProviderAPIKeyEncryptionKeycachecryptokeys.EncryptionKeycache// DisablePathApps disables path-based apps. This is a security feature as path// based apps share the same cookie as the dashboard, and are susceptible to XSS// by a malicious workspace app.//// Subdomain apps are safer with their cookies scoped to the subdomain, and XSS// calls to the dashboard are not possible due to CORs.DisablePathAppsboolCookiescodersdk.HTTPCookieConfigAgentProviderAgentProviderStatsCollector *StatsCollector// contains filtered or unexported fields}

Server serves workspace apps endpoints, including:- Path-based apps- Subdomain app middleware- Workspace reconnecting-pty (aka. web terminal)

func (*Server)Attach

func (s *Server) Attach(r chi.Router)

func (*Server)Close

func (s *Server) Close()error

Close waits for all reconnecting-pty WebSocket connections to drain beforereturning.

func (*Server)HandleSubdomain

func (s *Server) HandleSubdomain(middlewares ...func(http.Handler)http.Handler) func(http.Handler)http.Handler

HandleSubdomain handles subdomain-based application proxy requests (aka.DevURLs in Coder V1).

There are a lot of paths here:

  1. If api.Hostname is not set then we pass on.
  2. If we can't read the request hostname then we return a 400.
  3. If the request hostname matches api.AccessURL then we pass on.
  4. We split the subdomain into the subdomain and the "rest". If there are noperiods in the hostname then we pass on.
  5. We parse the subdomain into a appurl.ApplicationURL struct. If weencounter an error:a. If the "rest" does not match api.Hostname then we pass on;b. Otherwise, we return a 400.
  6. Finally, we verify that the "rest" matches api.Hostname, else wereturn a 404.

Rationales for each of the above steps:

  1. We pass on if api.Hostname is not set to avoid returning any errors if`--app-hostname` is not configured.
  2. Every request should have a valid Host header anyways.
  3. We pass on if the request hostname matches api.AccessURL so we cansupport having the access URL be at the same level as the applicationbase hostname.
  4. We pass on if there are no periods in the hostname as application URLsmust be a subdomain of a hostname, which implies there must be at leastone period.
  5. a. If the request subdomain is not a valid application URL, and the"rest" does not match api.Hostname, then it is very unlikely thatthe request was intended for this handler. We pass on.b. If the request subdomain is not a valid application URL, but the"rest" matches api.Hostname, then we return a 400 because therequest is probably a typo or something.
  6. We finally verify that the "rest" matches api.Hostname for securitypurposes regarding re-authentication and application proxy sessiontokens.

typeSignedToken

type SignedToken struct {jwtutils.RegisteredClaims// Request details.Request `json:"request"`UserIDuuid.UUID `json:"user_id"`WorkspaceIDuuid.UUID `json:"workspace_id"`AgentIDuuid.UUID `json:"agent_id"`AppURLstring    `json:"app_url"`}

SignedToken is the struct data contained inside a workspace app JWE. Itcontains the details of the workspace app that the token is valid for toavoid database queries.

funcFromRequest

FromRequest returns the signed token from the request, if it exists and isvalid. The caller must check that the token matches the request.

func (SignedToken)MatchesRequest

func (tSignedToken) MatchesRequest(reqRequest)bool

MatchesRequest returns true if the token matches the request. Any token thatdoes not match the request should be considered invalid.

typeSignedTokenProvider

type SignedTokenProvider interface {// FromRequest returns a parsed token from the request. If the request does// not contain a signed app token or is is invalid (expired, invalid// signature, etc.), it returns false.FromRequest(r *http.Request) (*SignedToken,bool)// Issue mints a new token for the given app request. It uses the long-lived// session token in the HTTP request to authenticate and authorize the// client for the given workspace app. The token is returned in struct and// string form. The string form should be written as a cookie.//// If the request is invalid or the user is not authorized to access the// app, false is returned. An error page is written to the response writer// in this case.Issue(ctxcontext.Context, rwhttp.ResponseWriter, r *http.Request, appReqIssueTokenRequest) (*SignedToken,string,bool)}

SignedTokenProvider provides signed workspace app tokens (aka. app tickets).

funcNewDBTokenProvider

func NewDBTokenProvider(logslog.Logger,accessURL *url.URL,authzrbac.Authorizer,auditor *atomic.Pointer[audit.Auditor],dbdatabase.Store,cfg *codersdk.DeploymentValues,oauth2Cfgs *httpmw.OAuth2Configs,workspaceAgentInactiveTimeouttime.Duration,workspaceAppAuditSessionTimeouttime.Duration,signercryptokeys.SigningKeycache,)SignedTokenProvider

typeStatsCollector

type StatsCollector struct {// contains filtered or unexported fields}

StatsCollector collects workspace app StatsReports and reports themin batches, stats compaction is performed for short-lived sessions.

func (*StatsCollector)Close

func (sc *StatsCollector) Close()error

func (*StatsCollector)Collect

func (sc *StatsCollector) Collect(reportStatsReport)

Collect the given StatsReport for later reporting (non-blocking).

typeStatsCollectorOptions

type StatsCollectorOptions struct {Logger   *slog.LoggerReporterStatsReporter// ReportInterval is the interval at which stats are reported, both partial// and fully formed stats.ReportIntervaltime.Duration// RollupWindow is the window size for rolling up stats, session shorter// than this will be rolled up and longer than this will be tracked// individually.RollupWindowtime.Duration// Options for tests.Flush <-chan chan<- struct{}Now   func()time.Time}

typeStatsReport

type StatsReport struct {UserIDuuid.UUID    `json:"user_id"`WorkspaceIDuuid.UUID    `json:"workspace_id"`AgentIDuuid.UUID    `json:"agent_id"`AccessMethodAccessMethod `json:"access_method"`SlugOrPortstring       `json:"slug_or_port"`SessionIDuuid.UUID    `json:"session_id"`SessionStartedAttime.Time    `json:"session_started_at"`SessionEndedAttime.Time    `json:"session_ended_at"`// Updated periodically while app is in use active and when the last connection is closed.Requestsint          `json:"requests"`// contains filtered or unexported fields}

StatsReport is a report of a workspace app session.

typeStatsReporter

type StatsReporter interface {ReportAppStats(context.Context, []StatsReport)error}

StatsReporter reports workspace app StatsReports.

Source Files

View all Source files

Directories

PathSynopsis
Package appurl handles all parsing/validation/etc around application URLs.
Package appurl handles all parsing/validation/etc around application URLs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f orF : Jump to
y orY : Canonical URL
go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp