mirror of
https://github.com/coder/coder.git
synced 2025-07-10 23:53:15 +00:00
This commit disables path-based app sharing by default. It is possible for a workspace app on a path (not a subdomain) to make API requests to the Coder API. When accessing your own workspace, this is not much of a problem. When accessing a shared workspace app, the workspace owner could include malicious javascript in the page that makes requests to the Coder API on behalf of the visitor. This vulnerability does not affect subdomain apps. - Disables path-based app sharing by default. Previous behavior can be restored using the `--dangerous-allow-path-app-sharing` flag which is not recommended. - Disables users with the site "owner" role from accessing path-based apps from workspaces they do not own. Previous behavior can be restored using the `--dangerous-allow-path-app-site-owner-access` flag which is not recommended. - Adds a flag `--disable-path-apps` which can be used by security-conscious admins to disable all path-based apps across the entire deployment. This check is enforced at app-access time, not at template-ingest time.
243 lines
13 KiB
Go
243 lines
13 KiB
Go
package codersdk
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
// DeploymentConfig is the central configuration for the coder server.
|
|
type DeploymentConfig struct {
|
|
AccessURL *DeploymentConfigField[string] `json:"access_url" typescript:",notnull"`
|
|
WildcardAccessURL *DeploymentConfigField[string] `json:"wildcard_access_url" typescript:",notnull"`
|
|
HTTPAddress *DeploymentConfigField[string] `json:"http_address" typescript:",notnull"`
|
|
AutobuildPollInterval *DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval" typescript:",notnull"`
|
|
DERP *DERP `json:"derp" typescript:",notnull"`
|
|
GitAuth *DeploymentConfigField[[]GitAuthConfig] `json:"gitauth" typescript:",notnull"`
|
|
Prometheus *PrometheusConfig `json:"prometheus" typescript:",notnull"`
|
|
Pprof *PprofConfig `json:"pprof" typescript:",notnull"`
|
|
ProxyTrustedHeaders *DeploymentConfigField[[]string] `json:"proxy_trusted_headers" typescript:",notnull"`
|
|
ProxyTrustedOrigins *DeploymentConfigField[[]string] `json:"proxy_trusted_origins" typescript:",notnull"`
|
|
CacheDirectory *DeploymentConfigField[string] `json:"cache_directory" typescript:",notnull"`
|
|
InMemoryDatabase *DeploymentConfigField[bool] `json:"in_memory_database" typescript:",notnull"`
|
|
PostgresURL *DeploymentConfigField[string] `json:"pg_connection_url" typescript:",notnull"`
|
|
OAuth2 *OAuth2Config `json:"oauth2" typescript:",notnull"`
|
|
OIDC *OIDCConfig `json:"oidc" typescript:",notnull"`
|
|
Telemetry *TelemetryConfig `json:"telemetry" typescript:",notnull"`
|
|
TLS *TLSConfig `json:"tls" typescript:",notnull"`
|
|
Trace *TraceConfig `json:"trace" typescript:",notnull"`
|
|
SecureAuthCookie *DeploymentConfigField[bool] `json:"secure_auth_cookie" typescript:",notnull"`
|
|
SSHKeygenAlgorithm *DeploymentConfigField[string] `json:"ssh_keygen_algorithm" typescript:",notnull"`
|
|
MetricsCacheRefreshInterval *DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval" typescript:",notnull"`
|
|
AgentStatRefreshInterval *DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval" typescript:",notnull"`
|
|
AgentFallbackTroubleshootingURL *DeploymentConfigField[string] `json:"agent_fallback_troubleshooting_url" typescript:",notnull"`
|
|
AuditLogging *DeploymentConfigField[bool] `json:"audit_logging" typescript:",notnull"`
|
|
BrowserOnly *DeploymentConfigField[bool] `json:"browser_only" typescript:",notnull"`
|
|
SCIMAPIKey *DeploymentConfigField[string] `json:"scim_api_key" typescript:",notnull"`
|
|
Provisioner *ProvisionerConfig `json:"provisioner" typescript:",notnull"`
|
|
RateLimit *RateLimitConfig `json:"rate_limit" typescript:",notnull"`
|
|
Experiments *DeploymentConfigField[[]string] `json:"experiments" typescript:",notnull"`
|
|
UpdateCheck *DeploymentConfigField[bool] `json:"update_check" typescript:",notnull"`
|
|
MaxTokenLifetime *DeploymentConfigField[time.Duration] `json:"max_token_lifetime" typescript:",notnull"`
|
|
Swagger *SwaggerConfig `json:"swagger" typescript:",notnull"`
|
|
Logging *LoggingConfig `json:"logging" typescript:",notnull"`
|
|
Dangerous *DangerousConfig `json:"dangerous" typescript:",notnull"`
|
|
DisablePathApps *DeploymentConfigField[bool] `json:"disable_path_apps" typescript:",notnull"`
|
|
|
|
// DEPRECATED: Use HTTPAddress or TLS.Address instead.
|
|
Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"`
|
|
// DEPRECATED: Use Experiments instead.
|
|
Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"`
|
|
}
|
|
|
|
type DERP struct {
|
|
Server *DERPServerConfig `json:"server" typescript:",notnull"`
|
|
Config *DERPConfig `json:"config" typescript:",notnull"`
|
|
}
|
|
|
|
type DERPServerConfig struct {
|
|
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
|
|
RegionID *DeploymentConfigField[int] `json:"region_id" typescript:",notnull"`
|
|
RegionCode *DeploymentConfigField[string] `json:"region_code" typescript:",notnull"`
|
|
RegionName *DeploymentConfigField[string] `json:"region_name" typescript:",notnull"`
|
|
STUNAddresses *DeploymentConfigField[[]string] `json:"stun_addresses" typescript:",notnull"`
|
|
RelayURL *DeploymentConfigField[string] `json:"relay_url" typescript:",notnull"`
|
|
}
|
|
|
|
type DERPConfig struct {
|
|
URL *DeploymentConfigField[string] `json:"url" typescript:",notnull"`
|
|
Path *DeploymentConfigField[string] `json:"path" typescript:",notnull"`
|
|
}
|
|
|
|
type PrometheusConfig struct {
|
|
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
|
|
Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"`
|
|
}
|
|
|
|
type PprofConfig struct {
|
|
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
|
|
Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"`
|
|
}
|
|
|
|
type OAuth2Config struct {
|
|
Github *OAuth2GithubConfig `json:"github" typescript:",notnull"`
|
|
}
|
|
|
|
type OAuth2GithubConfig struct {
|
|
ClientID *DeploymentConfigField[string] `json:"client_id" typescript:",notnull"`
|
|
ClientSecret *DeploymentConfigField[string] `json:"client_secret" typescript:",notnull"`
|
|
AllowedOrgs *DeploymentConfigField[[]string] `json:"allowed_orgs" typescript:",notnull"`
|
|
AllowedTeams *DeploymentConfigField[[]string] `json:"allowed_teams" typescript:",notnull"`
|
|
AllowSignups *DeploymentConfigField[bool] `json:"allow_signups" typescript:",notnull"`
|
|
AllowEveryone *DeploymentConfigField[bool] `json:"allow_everyone" typescript:",notnull"`
|
|
EnterpriseBaseURL *DeploymentConfigField[string] `json:"enterprise_base_url" typescript:",notnull"`
|
|
}
|
|
|
|
type OIDCConfig struct {
|
|
AllowSignups *DeploymentConfigField[bool] `json:"allow_signups" typescript:",notnull"`
|
|
ClientID *DeploymentConfigField[string] `json:"client_id" typescript:",notnull"`
|
|
ClientSecret *DeploymentConfigField[string] `json:"client_secret" typescript:",notnull"`
|
|
EmailDomain *DeploymentConfigField[[]string] `json:"email_domain" typescript:",notnull"`
|
|
IssuerURL *DeploymentConfigField[string] `json:"issuer_url" typescript:",notnull"`
|
|
Scopes *DeploymentConfigField[[]string] `json:"scopes" typescript:",notnull"`
|
|
IgnoreEmailVerified *DeploymentConfigField[bool] `json:"ignore_email_verified" typescript:",notnull"`
|
|
UsernameField *DeploymentConfigField[string] `json:"username_field" typescript:",notnull"`
|
|
}
|
|
|
|
type TelemetryConfig struct {
|
|
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
|
|
Trace *DeploymentConfigField[bool] `json:"trace" typescript:",notnull"`
|
|
URL *DeploymentConfigField[string] `json:"url" typescript:",notnull"`
|
|
}
|
|
|
|
type TLSConfig struct {
|
|
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
|
|
Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"`
|
|
RedirectHTTP *DeploymentConfigField[bool] `json:"redirect_http" typescript:",notnull"`
|
|
CertFiles *DeploymentConfigField[[]string] `json:"cert_file" typescript:",notnull"`
|
|
ClientAuth *DeploymentConfigField[string] `json:"client_auth" typescript:",notnull"`
|
|
ClientCAFile *DeploymentConfigField[string] `json:"client_ca_file" typescript:",notnull"`
|
|
KeyFiles *DeploymentConfigField[[]string] `json:"key_file" typescript:",notnull"`
|
|
MinVersion *DeploymentConfigField[string] `json:"min_version" typescript:",notnull"`
|
|
ClientCertFile *DeploymentConfigField[string] `json:"client_cert_file" typescript:",notnull"`
|
|
ClientKeyFile *DeploymentConfigField[string] `json:"client_key_file" typescript:",notnull"`
|
|
}
|
|
|
|
type TraceConfig struct {
|
|
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
|
|
HoneycombAPIKey *DeploymentConfigField[string] `json:"honeycomb_api_key" typescript:",notnull"`
|
|
CaptureLogs *DeploymentConfigField[bool] `json:"capture_logs" typescript:",notnull"`
|
|
}
|
|
|
|
type GitAuthConfig struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
ClientID string `json:"client_id"`
|
|
ClientSecret string `json:"-" yaml:"client_secret"`
|
|
AuthURL string `json:"auth_url"`
|
|
TokenURL string `json:"token_url"`
|
|
ValidateURL string `json:"validate_url"`
|
|
Regex string `json:"regex"`
|
|
NoRefresh bool `json:"no_refresh"`
|
|
Scopes []string `json:"scopes"`
|
|
}
|
|
|
|
type ProvisionerConfig struct {
|
|
Daemons *DeploymentConfigField[int] `json:"daemons" typescript:",notnull"`
|
|
DaemonPollInterval *DeploymentConfigField[time.Duration] `json:"daemon_poll_interval" typescript:",notnull"`
|
|
DaemonPollJitter *DeploymentConfigField[time.Duration] `json:"daemon_poll_jitter" typescript:",notnull"`
|
|
ForceCancelInterval *DeploymentConfigField[time.Duration] `json:"force_cancel_interval" typescript:",notnull"`
|
|
}
|
|
|
|
type RateLimitConfig struct {
|
|
DisableAll *DeploymentConfigField[bool] `json:"disable_all" typescript:",notnull"`
|
|
API *DeploymentConfigField[int] `json:"api" typescript:",notnull"`
|
|
}
|
|
|
|
type SwaggerConfig struct {
|
|
Enable *DeploymentConfigField[bool] `json:"enable" typescript:",notnull"`
|
|
}
|
|
|
|
type LoggingConfig struct {
|
|
Human *DeploymentConfigField[string] `json:"human" typescript:",notnull"`
|
|
JSON *DeploymentConfigField[string] `json:"json" typescript:",notnull"`
|
|
Stackdriver *DeploymentConfigField[string] `json:"stackdriver" typescript:",notnull"`
|
|
}
|
|
|
|
type DangerousConfig struct {
|
|
AllowPathAppSharing *DeploymentConfigField[bool] `json:"allow_path_app_sharing" typescript:",notnull"`
|
|
AllowPathAppSiteOwnerAccess *DeploymentConfigField[bool] `json:"allow_path_app_site_owner_access" typescript:",notnull"`
|
|
}
|
|
|
|
type Flaggable interface {
|
|
string | time.Duration | bool | int | []string | []GitAuthConfig
|
|
}
|
|
|
|
type DeploymentConfigField[T Flaggable] struct {
|
|
Name string `json:"name"`
|
|
Usage string `json:"usage"`
|
|
Flag string `json:"flag"`
|
|
// EnvOverride will override the automatically generated environment
|
|
// variable name. Useful if you're moving values around but need to keep
|
|
// backwards compatibility with old environment variable names.
|
|
//
|
|
// NOTE: this is not supported for array flags.
|
|
EnvOverride string `json:"-"`
|
|
Shorthand string `json:"shorthand"`
|
|
Enterprise bool `json:"enterprise"`
|
|
Hidden bool `json:"hidden"`
|
|
Secret bool `json:"secret"`
|
|
Default T `json:"default"`
|
|
Value T `json:"value"`
|
|
}
|
|
|
|
// MarshalJSON removes the Value field from the JSON output of any fields marked Secret.
|
|
// nolint:revive
|
|
func (f *DeploymentConfigField[T]) MarshalJSON() ([]byte, error) {
|
|
copy := struct {
|
|
Name string `json:"name"`
|
|
Usage string `json:"usage"`
|
|
Flag string `json:"flag"`
|
|
Shorthand string `json:"shorthand"`
|
|
Enterprise bool `json:"enterprise"`
|
|
Hidden bool `json:"hidden"`
|
|
Secret bool `json:"secret"`
|
|
Default T `json:"default"`
|
|
Value T `json:"value"`
|
|
}{
|
|
Name: f.Name,
|
|
Usage: f.Usage,
|
|
Flag: f.Flag,
|
|
Shorthand: f.Shorthand,
|
|
Enterprise: f.Enterprise,
|
|
Hidden: f.Hidden,
|
|
Secret: f.Secret,
|
|
}
|
|
|
|
if !f.Secret {
|
|
copy.Default = f.Default
|
|
copy.Value = f.Value
|
|
}
|
|
|
|
return json.Marshal(copy)
|
|
}
|
|
|
|
// DeploymentConfig returns the deployment config for the coder server.
|
|
func (c *Client) DeploymentConfig(ctx context.Context) (DeploymentConfig, error) {
|
|
res, err := c.Request(ctx, http.MethodGet, "/api/v2/config/deployment", nil)
|
|
if err != nil {
|
|
return DeploymentConfig{}, xerrors.Errorf("execute request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
return DeploymentConfig{}, readBodyAsError(res)
|
|
}
|
|
|
|
var df DeploymentConfig
|
|
return df, json.NewDecoder(res.Body).Decode(&df)
|
|
}
|