Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5b7fa78

Browse files
authored
chore: add deployment config option to append custom csp directives (#15596)
Allows adding custom static CSP directives to Coder. Niche use case butmakes this easier then creating a reverse proxy that has to replace theheader. We want to preserve our directives, so having an append optionis preferred to a "replace" option via a reverse proxy.Closes#15118
1 parentf38f746 commit5b7fa78

File tree

13 files changed

+140
-39
lines changed

13 files changed

+140
-39
lines changed

‎cli/testdata/coder_server_--help.golden‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ backed by Tailscale and WireGuard.
294294
+ 1`. Use special value 'disable' to turn off STUN completely.
295295

296296
NETWORKING / HTTP OPTIONS:
297+
--additional-csp-policy string-array, $CODER_ADDITIONAL_CSP_POLICY
298+
Coder configures a Content Security Policy (CSP) to protect against
299+
XSS attacks. This setting allows you to add additional CSP directives,
300+
which can open the attack surface of the deployment. Format matches
301+
the CSP directive format, e.g. --additional-csp-policy="script-src
302+
https://example.com".
303+
297304
--disable-password-auth bool, $CODER_DISABLE_PASSWORD_AUTH
298305
Disable password authentication. This is recommended for security
299306
purposes in production deployments that rely on an identity provider.

‎cli/testdata/server-config.yaml.golden‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ networking:
1616
# HTTP bind address of the server. Unset to disable the HTTP endpoint.
1717
# (default: 127.0.0.1:3000, type: string)
1818
httpAddress: 127.0.0.1:3000
19+
# Coder configures a Content Security Policy (CSP) to protect against XSS attacks.
20+
# This setting allows you to add additional CSP directives, which can open the
21+
# attack surface of the deployment. Format matches the CSP directive format, e.g.
22+
# --additional-csp-policy="script-src https://example.com".
23+
# (default: <unset>, type: string-array)
24+
additionalCSPPolicy: []
1925
# The maximum lifetime duration users can specify when creating an API token.
2026
# (default: 876600h0m0s, type: duration)
2127
maxTokenLifetime: 876600h0m0s

‎coderd/apidoc/docs.go‎

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json‎

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/coderd.go‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/tls"
66
"crypto/x509"
77
"database/sql"
8+
"errors"
89
"expvar"
910
"flag"
1011
"fmt"
@@ -1378,6 +1379,26 @@ func New(options *Options) *API {
13781379
r.Get("/swagger/*",swaggerDisabled)
13791380
}
13801381

1382+
additionalCSPHeaders:=make(map[httpmw.CSPFetchDirective][]string,len(api.DeploymentValues.AdditionalCSPPolicy))
1383+
varcspParseErrorserror
1384+
for_,v:=rangeapi.DeploymentValues.AdditionalCSPPolicy {
1385+
// Format is "<directive> <value> <value> ..."
1386+
v=strings.TrimSpace(v)
1387+
parts:=strings.Split(v," ")
1388+
iflen(parts)<2 {
1389+
cspParseErrors=errors.Join(cspParseErrors,xerrors.Errorf("invalid CSP header %q, not enough parts to be valid",v))
1390+
continue
1391+
}
1392+
additionalCSPHeaders[httpmw.CSPFetchDirective(strings.ToLower(parts[0]))]=parts[1:]
1393+
}
1394+
1395+
ifcspParseErrors!=nil {
1396+
// Do not fail Coder deployment startup because of this. Just log an error
1397+
// and continue
1398+
api.Logger.Error(context.Background(),
1399+
"parsing additional CSP headers",slog.Error(cspParseErrors))
1400+
}
1401+
13811402
// Add CSP headers to all static assets and pages. CSP headers only affect
13821403
// browsers, so these don't make sense on api routes.
13831404
cspMW:=httpmw.CSPHeaders(options.Telemetry.Enabled(),func() []string {
@@ -1390,7 +1411,7 @@ func New(options *Options) *API {
13901411
}
13911412
// By default we do not add extra websocket connections to the CSP
13921413
return []string{}
1393-
})
1414+
},additionalCSPHeaders)
13941415

13951416
// Static file handler must be wrapped with HSTS handler if the
13961417
// StrictTransportSecurityAge is set. We only need to set this header on

‎coderd/httpmw/csp.go‎

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,39 @@ func (s cspDirectives) Append(d CSPFetchDirective, values ...string) {
2323
typeCSPFetchDirectivestring
2424

2525
const (
26-
cspDirectiveDefaultSrc="default-src"
27-
cspDirectiveConnectSrc="connect-src"
28-
cspDirectiveChildSrc="child-src"
29-
cspDirectiveScriptSrc="script-src"
30-
cspDirectiveFontSrc="font-src"
31-
cspDirectiveStyleSrc="style-src"
32-
cspDirectiveObjectSrc="object-src"
33-
cspDirectiveManifestSrc="manifest-src"
34-
cspDirectiveFrameSrc="frame-src"
35-
cspDirectiveImgSrc="img-src"
36-
cspDirectiveReportURI="report-uri"
37-
cspDirectiveFormAction="form-action"
38-
cspDirectiveMediaSrc="media-src"
39-
cspFrameAncestors="frame-ancestors"
40-
cspDirectiveWorkerSrc="worker-src"
26+
CSPDirectiveDefaultSrcCSPFetchDirective="default-src"
27+
CSPDirectiveConnectSrcCSPFetchDirective="connect-src"
28+
CSPDirectiveChildSrcCSPFetchDirective="child-src"
29+
CSPDirectiveScriptSrcCSPFetchDirective="script-src"
30+
CSPDirectiveFontSrcCSPFetchDirective="font-src"
31+
CSPDirectiveStyleSrcCSPFetchDirective="style-src"
32+
CSPDirectiveObjectSrcCSPFetchDirective="object-src"
33+
CSPDirectiveManifestSrcCSPFetchDirective="manifest-src"
34+
CSPDirectiveFrameSrcCSPFetchDirective="frame-src"
35+
CSPDirectiveImgSrcCSPFetchDirective="img-src"
36+
CSPDirectiveReportURICSPFetchDirective="report-uri"
37+
CSPDirectiveFormActionCSPFetchDirective="form-action"
38+
CSPDirectiveMediaSrcCSPFetchDirective="media-src"
39+
CSPFrameAncestorsCSPFetchDirective="frame-ancestors"
40+
CSPDirectiveWorkerSrcCSPFetchDirective="worker-src"
4141
)
4242

4343
// CSPHeaders returns a middleware that sets the Content-Security-Policy header
44-
// for coderd. It takes a function that allows adding supported external websocket
45-
// hosts. This is primarily to support the terminal connecting to a workspace proxy.
44+
// for coderd.
45+
//
46+
// Arguments:
47+
// - websocketHosts: a function that returns a list of supported external websocket hosts.
48+
// This is to support the terminal connecting to a workspace proxy.
49+
// The origin of the terminal request does not match the url of the proxy,
50+
// so the CSP list of allowed hosts must be dynamic and match the current
51+
// available proxy urls.
52+
// - staticAdditions: a map of CSP directives to append to the default CSP headers.
53+
// Used to allow specific static additions to the CSP headers. Allows some niche
54+
// use cases, such as embedding Coder in an iframe.
55+
// Example: https://github.com/coder/coder/issues/15118
4656
//
4757
//nolint:revive
48-
funcCSPHeaders(telemetrybool,websocketHostsfunc() []string)func(next http.Handler) http.Handler {
58+
funcCSPHeaders(telemetrybool,websocketHostsfunc() []string,staticAdditionsmap[CSPFetchDirective][]string)func(next http.Handler) http.Handler {
4959
returnfunc(next http.Handler) http.Handler {
5060
returnhttp.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
5161
// Content-Security-Policy disables loading certain content types and can prevent XSS injections.
@@ -55,30 +65,30 @@ func CSPHeaders(telemetry bool, websocketHosts func() []string) func(next http.H
5565
//The list of CSP options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
5666
cspSrcs:=cspDirectives{
5767
// All omitted fetch csp srcs default to this.
58-
cspDirectiveDefaultSrc: {"'self'"},
59-
cspDirectiveConnectSrc: {"'self'"},
60-
cspDirectiveChildSrc: {"'self'"},
68+
CSPDirectiveDefaultSrc: {"'self'"},
69+
CSPDirectiveConnectSrc: {"'self'"},
70+
CSPDirectiveChildSrc: {"'self'"},
6171
// https://github.com/suren-atoyan/monaco-react/issues/168
62-
cspDirectiveScriptSrc: {"'self'"},
63-
cspDirectiveStyleSrc: {"'self' 'unsafe-inline'"},
72+
CSPDirectiveScriptSrc: {"'self'"},
73+
CSPDirectiveStyleSrc: {"'self' 'unsafe-inline'"},
6474
// data: is used by monaco editor on FE for Syntax Highlight
65-
cspDirectiveFontSrc: {"'self' data:"},
66-
cspDirectiveWorkerSrc: {"'self' blob:"},
75+
CSPDirectiveFontSrc: {"'self' data:"},
76+
CSPDirectiveWorkerSrc: {"'self' blob:"},
6777
// object-src is needed to support code-server
68-
cspDirectiveObjectSrc: {"'self'"},
78+
CSPDirectiveObjectSrc: {"'self'"},
6979
// blob: for loading the pwa manifest for code-server
70-
cspDirectiveManifestSrc: {"'self' blob:"},
71-
cspDirectiveFrameSrc: {"'self'"},
80+
CSPDirectiveManifestSrc: {"'self' blob:"},
81+
CSPDirectiveFrameSrc: {"'self'"},
7282
// data: for loading base64 encoded icons for generic applications.
7383
// https: allows loading images from external sources. This is not ideal
7484
// but is required for the templates page that renders readmes.
7585
//We should find a better solution in the future.
76-
cspDirectiveImgSrc: {"'self' https: data:"},
77-
cspDirectiveFormAction: {"'self'"},
78-
cspDirectiveMediaSrc: {"'self'"},
86+
CSPDirectiveImgSrc: {"'self' https: data:"},
87+
CSPDirectiveFormAction: {"'self'"},
88+
CSPDirectiveMediaSrc: {"'self'"},
7989
// Report all violations back to the server to log
80-
cspDirectiveReportURI: {"/api/v2/csp/reports"},
81-
cspFrameAncestors: {"'none'"},
90+
CSPDirectiveReportURI: {"/api/v2/csp/reports"},
91+
CSPFrameAncestors: {"'none'"},
8292

8393
// Only scripts can manipulate the dom. This prevents someone from
8494
// naming themselves something like '<svg onload="alert(/cross-site-scripting/)" />'.
@@ -87,7 +97,7 @@ func CSPHeaders(telemetry bool, websocketHosts func() []string) func(next http.H
8797

8898
iftelemetry {
8999
// If telemetry is enabled, we report to coder.com.
90-
cspSrcs.Append(cspDirectiveConnectSrc,"https://coder.com")
100+
cspSrcs.Append(CSPDirectiveConnectSrc,"https://coder.com")
91101
}
92102

93103
// This extra connect-src addition is required to support old webkit
@@ -102,7 +112,7 @@ func CSPHeaders(telemetry bool, websocketHosts func() []string) func(next http.H
102112
// We can add both ws:// and wss:// as browsers do not let https
103113
// pages to connect to non-tls websocket connections. So this
104114
// supports both http & https webpages.
105-
cspSrcs.Append(cspDirectiveConnectSrc,fmt.Sprintf("wss://%[1]s ws://%[1]s",host))
115+
cspSrcs.Append(CSPDirectiveConnectSrc,fmt.Sprintf("wss://%[1]s ws://%[1]s",host))
106116
}
107117

108118
// The terminal requires a websocket connection to the workspace proxy.
@@ -112,15 +122,19 @@ func CSPHeaders(telemetry bool, websocketHosts func() []string) func(next http.H
112122
for_,extraHost:=rangeextraConnect {
113123
ifextraHost=="*" {
114124
// '*' means all
115-
cspSrcs.Append(cspDirectiveConnectSrc,"*")
125+
cspSrcs.Append(CSPDirectiveConnectSrc,"*")
116126
continue
117127
}
118-
cspSrcs.Append(cspDirectiveConnectSrc,fmt.Sprintf("wss://%[1]s ws://%[1]s",extraHost))
128+
cspSrcs.Append(CSPDirectiveConnectSrc,fmt.Sprintf("wss://%[1]s ws://%[1]s",extraHost))
119129
// We also require this to make http/https requests to the workspace proxy for latency checking.
120-
cspSrcs.Append(cspDirectiveConnectSrc,fmt.Sprintf("https://%[1]s http://%[1]s",extraHost))
130+
cspSrcs.Append(CSPDirectiveConnectSrc,fmt.Sprintf("https://%[1]s http://%[1]s",extraHost))
121131
}
122132
}
123133

134+
fordirective,values:=rangestaticAdditions {
135+
cspSrcs.Append(directive,values...)
136+
}
137+
124138
varcsp strings.Builder
125139
forsrc,vals:=rangecspSrcs {
126140
_,_=fmt.Fprintf(&csp,"%s %s; ",src,strings.Join(vals," "))

‎coderd/httpmw/csp_test.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ func TestCSPConnect(t *testing.T) {
1515
t.Parallel()
1616

1717
expected:= []string{"example.com","coder.com"}
18+
expectedMedia:= []string{"media.com","media2.com"}
1819

1920
r:=httptest.NewRequest(http.MethodGet,"/",nil)
2021
rw:=httptest.NewRecorder()
2122

2223
httpmw.CSPHeaders(false,func() []string {
2324
returnexpected
25+
},map[httpmw.CSPFetchDirective][]string{
26+
httpmw.CSPDirectiveMediaSrc:expectedMedia,
2427
})(http.HandlerFunc(func(rw http.ResponseWriter,r*http.Request) {
2528
rw.WriteHeader(http.StatusOK)
2629
})).ServeHTTP(rw,r)
@@ -30,4 +33,7 @@ func TestCSPConnect(t *testing.T) {
3033
require.Containsf(t,rw.Header().Get("Content-Security-Policy"),fmt.Sprintf("ws://%s",e),"Content-Security-Policy header should contain ws://%s",e)
3134
require.Containsf(t,rw.Header().Get("Content-Security-Policy"),fmt.Sprintf("wss://%s",e),"Content-Security-Policy header should contain wss://%s",e)
3235
}
36+
for_,e:=rangeexpectedMedia {
37+
require.Containsf(t,rw.Header().Get("Content-Security-Policy"),e,"Content-Security-Policy header should contain %s",e)
38+
}
3339
}

‎codersdk/deployment.go‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ type DeploymentValues struct {
391391
CLIUpgradeMessage serpent.String`json:"cli_upgrade_message,omitempty" typescript:",notnull"`
392392
TermsOfServiceURL serpent.String`json:"terms_of_service_url,omitempty" typescript:",notnull"`
393393
NotificationsNotificationsConfig`json:"notifications,omitempty" typescript:",notnull"`
394+
AdditionalCSPPolicy serpent.StringArray`json:"additional_csp_policy,omitempty" typescript:",notnull"`
394395

395396
Config serpent.YAMLConfigPath`json:"config,omitempty" typescript:",notnull"`
396397
WriteConfig serpent.Bool`json:"write_config,omitempty" typescript:",notnull"`
@@ -2147,6 +2148,18 @@ when required by your organization's security policy.`,
21472148
Group:&deploymentGroupIntrospectionLogging,
21482149
YAML:"enableTerraformDebugMode",
21492150
},
2151+
{
2152+
Name:"Additional CSP Policy",
2153+
Description:"Coder configures a Content Security Policy (CSP) to protect against XSS attacks. "+
2154+
"This setting allows you to add additional CSP directives, which can open the attack surface of the deployment. "+
2155+
"Format matches the CSP directive format, e.g. --additional-csp-policy=\"script-src https://example.com\".",
2156+
Flag:"additional-csp-policy",
2157+
Env:"CODER_ADDITIONAL_CSP_POLICY",
2158+
YAML:"additionalCSPPolicy",
2159+
Value:&c.AdditionalCSPPolicy,
2160+
Group:&deploymentGroupNetworkingHTTP,
2161+
},
2162+
21502163
// ☢️ Dangerous settings
21512164
{
21522165
Name:"DANGEROUS: Allow all CORS requests",

‎docs/reference/api/general.md‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎docs/reference/api/schemas.md‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp