Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add Content-Security-Policy configuration option #8943

Merged
merged 3 commits into from
Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions cmd/argocd-server/commands/argocd_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func NewCommand() *cobra.Command {
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
cacheSrc func() (*servercache.Cache, error)
frameOptions string
contentSecurityPolicy string
repoServerPlaintext bool
repoServerStrictTLS bool
staticAssetsDir string
Expand Down Expand Up @@ -126,23 +127,24 @@ func NewCommand() *cobra.Command {
}

argoCDOpts := server.ArgoCDServerOpts{
Insecure: insecure,
ListenPort: listenPort,
MetricsPort: metricsPort,
Namespace: namespace,
BaseHRef: baseHRef,
RootPath: rootPath,
KubeClientset: kubeclientset,
AppClientset: appClientSet,
RepoClientset: repoclientset,
DexServerAddr: dexServerAddress,
DisableAuth: disableAuth,
EnableGZip: enableGZip,
TLSConfigCustomizer: tlsConfigCustomizer,
Cache: cache,
XFrameOptions: frameOptions,
RedisClient: redisClient,
StaticAssetsDir: staticAssetsDir,
Insecure: insecure,
ListenPort: listenPort,
MetricsPort: metricsPort,
Namespace: namespace,
BaseHRef: baseHRef,
RootPath: rootPath,
KubeClientset: kubeclientset,
AppClientset: appClientSet,
RepoClientset: repoclientset,
DexServerAddr: dexServerAddress,
DisableAuth: disableAuth,
EnableGZip: enableGZip,
TLSConfigCustomizer: tlsConfigCustomizer,
Cache: cache,
XFrameOptions: frameOptions,
ContentSecurityPolicy: contentSecurityPolicy,
RedisClient: redisClient,
StaticAssetsDir: staticAssetsDir,
}

stats.RegisterStackDumper()
Expand Down Expand Up @@ -176,6 +178,7 @@ func NewCommand() *cobra.Command {
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".")
command.Flags().StringVar(&contentSecurityPolicy, "content-security-policy", env.StringFromEnv("ARGOCD_SERVER_CONTENT_SECURITY_POLICY", "frame-ancestors 'self';"), "Set Content-Security-Policy header in HTTP responses to `value`. To disable, set to \"\".")
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to repository server")
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to repo server")
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
Expand Down
1 change: 1 addition & 0 deletions docs/operator-manual/server-commands/argocd-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ argocd-server [flags]
--client-key string Path to a client key file for TLS
--cluster string The name of the kubeconfig cluster to use
--connection-status-cache-expiration duration Cache expiration for cluster/repo connection status (default 1h0m0s)
--content-security-policy value Set Content-Security-Policy header in HTTP responses to value. To disable, set to "". (default "frame-ancestors 'self';")
--context string The name of the kubeconfig context to use
--default-cache-expiration duration Cache expiration default (default 24h0m0s)
--dex-server string Dex server address (default "http://argocd-dex-server:5556")
Expand Down
6 changes: 6 additions & 0 deletions manifests/base/server/argocd-server-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ spec:
name: argocd-cmd-params-cm
key: server.x.frame.options
optional: true
- name: ARGOCD_SERVER_CONTENT_SECURITY_POLICY
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: server.content.security.policy
optional: true
- name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT
valueFrom:
configMapKeyRef:
Expand Down
6 changes: 6 additions & 0 deletions manifests/ha/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10480,6 +10480,12 @@ spec:
key: server.x.frame.options
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_CONTENT_SECURITY_POLICY
valueFrom:
configMapKeyRef:
key: server.content.security.policy
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT
valueFrom:
configMapKeyRef:
Expand Down
6 changes: 6 additions & 0 deletions manifests/ha/namespace-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,12 @@ spec:
key: server.x.frame.options
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_CONTENT_SECURITY_POLICY
valueFrom:
configMapKeyRef:
key: server.content.security.policy
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT
valueFrom:
configMapKeyRef:
Expand Down
6 changes: 6 additions & 0 deletions manifests/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9810,6 +9810,12 @@ spec:
key: server.x.frame.options
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_CONTENT_SECURITY_POLICY
valueFrom:
configMapKeyRef:
key: server.content.security.policy
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT
valueFrom:
configMapKeyRef:
Expand Down
6 changes: 6 additions & 0 deletions manifests/namespace-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,12 @@ spec:
key: server.x.frame.options
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_CONTENT_SECURITY_POLICY
valueFrom:
configMapKeyRef:
key: server.content.security.policy
name: argocd-cmd-params-cm
optional: true
- name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT
valueFrom:
configMapKeyRef:
Expand Down
41 changes: 23 additions & 18 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,24 +171,25 @@ type ArgoCDServer struct {
}

type ArgoCDServerOpts struct {
DisableAuth bool
EnableGZip bool
Insecure bool
StaticAssetsDir string
ListenPort int
MetricsPort int
Namespace string
DexServerAddr string
BaseHRef string
RootPath string
KubeClientset kubernetes.Interface
AppClientset appclientset.Interface
RepoClientset repoapiclient.Clientset
Cache *servercache.Cache
RedisClient *redis.Client
TLSConfigCustomizer tlsutil.ConfigCustomizer
XFrameOptions string
ListenHost string
DisableAuth bool
EnableGZip bool
Insecure bool
StaticAssetsDir string
ListenPort int
MetricsPort int
Namespace string
DexServerAddr string
BaseHRef string
RootPath string
KubeClientset kubernetes.Interface
AppClientset appclientset.Interface
RepoClientset repoapiclient.Clientset
Cache *servercache.Cache
RedisClient *redis.Client
TLSConfigCustomizer tlsutil.ConfigCustomizer
XFrameOptions string
ContentSecurityPolicy string
ListenHost string
}

// initializeDefaultProject creates the default project if it does not already exist
Expand Down Expand Up @@ -886,6 +887,10 @@ func (server *ArgoCDServer) newStaticAssetsHandler() func(http.ResponseWriter, *
if server.XFrameOptions != "" {
w.Header().Set("X-Frame-Options", server.XFrameOptions)
}
// Set Content-Security-Policy according to configuration
if server.ContentSecurityPolicy != "" {
w.Header().Set("Content-Security-Policy", server.ContentSecurityPolicy)
}
w.Header().Set("X-XSS-Protection", "1")

// serve index.html for non file requests to support HTML5 History API
Expand Down
11 changes: 8 additions & 3 deletions server/server_norace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func Test_StaticHeaders(t *testing.T) {
// !race:
// Same as TestUserAgent

// Test default policy "sameorigin"
// Test default policy "sameorigin" and "frame-ancestors 'self';"
{
s, closer := fakeServer()
defer closer()
Expand Down Expand Up @@ -129,13 +129,15 @@ func Test_StaticHeaders(t *testing.T) {
resp, err := client.Do(req)
assert.NoError(t, err)
assert.Equal(t, "sameorigin", resp.Header.Get("X-Frame-Options"))
assert.Equal(t, "frame-ancestors 'self';", resp.Header.Get("Content-Security-Policy"))
}

// Test custom policy
// Test custom policy for X-Frame-Options and Content-Security-Policy
{
s, closer := fakeServer()
defer closer()
s.XFrameOptions = "deny"
s.ContentSecurityPolicy = "frame-ancestors 'none';"
cancelInformer := test.StartInformer(s.projInformer)
defer cancelInformer()
port, err := test.GetFreePort()
Expand All @@ -160,13 +162,15 @@ func Test_StaticHeaders(t *testing.T) {
resp, err := client.Do(req)
assert.NoError(t, err)
assert.Equal(t, "deny", resp.Header.Get("X-Frame-Options"))
assert.Equal(t, "frame-ancestors 'none';", resp.Header.Get("Content-Security-Policy"))
}

// Test disabled
// Test disabled X-Frame-Options and Content-Security-Policy
{
s, closer := fakeServer()
defer closer()
s.XFrameOptions = ""
s.ContentSecurityPolicy = ""
cancelInformer := test.StartInformer(s.projInformer)
defer cancelInformer()
port, err := test.GetFreePort()
Expand All @@ -191,5 +195,6 @@ func Test_StaticHeaders(t *testing.T) {
resp, err := client.Do(req)
assert.NoError(t, err)
assert.Empty(t, resp.Header.Get("X-Frame-Options"))
assert.Empty(t, resp.Header.Get("Content-Security-Policy"))
}
}
13 changes: 7 additions & 6 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ func fakeServer() (*ArgoCDServer, func()) {
redis, closer := test.NewInMemoryRedis()

argoCDOpts := ArgoCDServerOpts{
Namespace: test.FakeArgoCDNamespace,
KubeClientset: kubeclientset,
AppClientset: appClientSet,
Insecure: true,
DisableAuth: true,
XFrameOptions: "sameorigin",
Namespace: test.FakeArgoCDNamespace,
KubeClientset: kubeclientset,
AppClientset: appClientSet,
Insecure: true,
DisableAuth: true,
XFrameOptions: "sameorigin",
ContentSecurityPolicy: "frame-ancestors 'self';",
Cache: servercache.NewCache(
appstatecache.NewCache(
cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)),
Expand Down