Skip to content

Commit 3d9b35e

Browse files
committed
CONSOLE-4292: Console-operator should configure console with the CSP allowed directives
1 parent 7524e8f commit 3d9b35e

File tree

5 files changed

+163
-26
lines changed

5 files changed

+163
-26
lines changed

pkg/console/subresource/configmap/configmap.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func DefaultConfigMap(
8484
Monitoring(monitoringSharedConfig).
8585
Plugins(getPluginsEndpointMap(availablePlugins)).
8686
I18nNamespaces(pluginsWithI18nNamespace(availablePlugins)).
87+
ContentSecurityPolicies(aggregateCSPDirectives(availablePlugins)).
8788
Proxy(getPluginsProxyServices(availablePlugins)).
8889
CustomLogoFile(operatorConfig.Spec.Customization.CustomLogoFile.Key).
8990
CustomProductName(operatorConfig.Spec.Customization.CustomProductName).
@@ -131,6 +132,32 @@ func DefaultConfigMap(
131132
return configMap, willMergeConfigOverrides, nil
132133
}
133134

135+
func aggregateCSPDirectives(plugins []*v1.ConsolePlugin) map[v1.DirectiveType][]string {
136+
aggregated := make(map[v1.DirectiveType]map[string]struct{}) // Use a map to ensure uniqueness
137+
138+
for _, plugin := range plugins {
139+
for _, csp := range plugin.Spec.ContentSecurityPolicy {
140+
if aggregated[csp.Directive] == nil {
141+
aggregated[csp.Directive] = make(map[string]struct{}) // Initialize if not already done
142+
}
143+
for _, v := range csp.Values {
144+
stringValue := string(v)
145+
aggregated[csp.Directive][stringValue] = struct{}{} // Use empty struct for uniqueness
146+
}
147+
}
148+
}
149+
150+
// Convert back to the desired format
151+
result := make(map[v1.DirectiveType][]string)
152+
for directive, valuesMap := range aggregated {
153+
for value := range valuesMap {
154+
result[directive] = append(result[directive], value)
155+
}
156+
}
157+
158+
return result
159+
}
160+
134161
func pluginsWithI18nNamespace(availablePlugins []*v1.ConsolePlugin) []string {
135162
i18nNamespaces := []string{}
136163
for _, plugin := range availablePlugins {

pkg/console/subresource/configmap/configmap_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import (
88
yaml "gopkg.in/yaml.v2"
99

1010
"github.com/go-test/deep"
11+
"github.com/google/go-cmp/cmp"
1112

1213
corev1 "k8s.io/api/core/v1"
1314
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1415
"k8s.io/utils/ptr"
1516

1617
configv1 "github.com/openshift/api/config/v1"
18+
consolev1 "github.com/openshift/api/console/v1"
1719
v1 "github.com/openshift/api/console/v1"
1820
operatorv1 "github.com/openshift/api/operator/v1"
1921
routev1 "github.com/openshift/api/route/v1"
@@ -1295,3 +1297,85 @@ customization:
12951297
})
12961298
}
12971299
}
1300+
1301+
func TestAggregateCSPDirectives(t *testing.T) {
1302+
tests := []struct {
1303+
name string
1304+
input []*consolev1.ConsolePlugin
1305+
output map[consolev1.DirectiveType][]string
1306+
}{
1307+
{
1308+
name: "Test aggregate CSP directives",
1309+
input: []*consolev1.ConsolePlugin{
1310+
{
1311+
Spec: consolev1.ConsolePluginSpec{
1312+
ContentSecurityPolicy: []consolev1.ConsolePluginCSP{
1313+
{
1314+
Directive: consolev1.DefaultSrc,
1315+
Values: []consolev1.CSPDirectiveValue{"source1", "source2"},
1316+
},
1317+
{
1318+
Directive: consolev1.ScriptSrc,
1319+
Values: []consolev1.CSPDirectiveValue{"script1"},
1320+
},
1321+
},
1322+
},
1323+
},
1324+
{
1325+
Spec: consolev1.ConsolePluginSpec{
1326+
ContentSecurityPolicy: []consolev1.ConsolePluginCSP{
1327+
{
1328+
Directive: consolev1.DefaultSrc,
1329+
Values: []consolev1.CSPDirectiveValue{"source2", "source3"},
1330+
},
1331+
{
1332+
Directive: consolev1.StyleSrc,
1333+
Values: []consolev1.CSPDirectiveValue{"style1", "style2"},
1334+
},
1335+
},
1336+
},
1337+
},
1338+
},
1339+
output: map[consolev1.DirectiveType][]string{
1340+
consolev1.DefaultSrc: {"source1", "source2", "source3"},
1341+
consolev1.ScriptSrc: {"script1"},
1342+
consolev1.StyleSrc: {"style1", "style2"},
1343+
},
1344+
},
1345+
{
1346+
name: "Test aggregate CSP directives",
1347+
input: []*consolev1.ConsolePlugin{
1348+
{
1349+
Spec: consolev1.ConsolePluginSpec{},
1350+
},
1351+
{
1352+
Spec: consolev1.ConsolePluginSpec{
1353+
ContentSecurityPolicy: []consolev1.ConsolePluginCSP{
1354+
{
1355+
Directive: consolev1.DefaultSrc,
1356+
Values: []consolev1.CSPDirectiveValue{"source1", "source2"},
1357+
},
1358+
{
1359+
Directive: consolev1.StyleSrc,
1360+
Values: []consolev1.CSPDirectiveValue{"style1", "style2"},
1361+
},
1362+
},
1363+
},
1364+
},
1365+
},
1366+
output: map[consolev1.DirectiveType][]string{
1367+
consolev1.DefaultSrc: {"source1", "source2"},
1368+
consolev1.StyleSrc: {"style1", "style2"},
1369+
},
1370+
},
1371+
}
1372+
1373+
for _, tt := range tests {
1374+
t.Run(tt.name, func(t *testing.T) {
1375+
result := aggregateCSPDirectives(tt.input)
1376+
if diff := cmp.Diff(tt.output, result); len(diff) > 0 {
1377+
t.Error(diff)
1378+
}
1379+
})
1380+
}
1381+
}

pkg/console/subresource/consoleserver/config_builder.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
configv1 "github.com/openshift/api/config/v1"
9+
v1 "github.com/openshift/api/console/v1"
910
operatorv1 "github.com/openshift/api/operator/v1"
1011
"github.com/openshift/console-operator/pkg/api"
1112
authconfigsub "github.com/openshift/console-operator/pkg/console/subresource/authentication"
@@ -75,6 +76,7 @@ type ConsoleServerCLIConfigBuilder struct {
7576
sessionEncryptionFile string
7677
sessionAuthenticationFile string
7778
capabilities []operatorv1.Capability
79+
contentSecurityPolicyList map[v1.DirectiveType][]string
7880
}
7981

8082
func (b *ConsoleServerCLIConfigBuilder) Host(host string) *ConsoleServerCLIConfigBuilder {
@@ -207,6 +209,11 @@ func (b *ConsoleServerCLIConfigBuilder) Plugins(plugins map[string]string) *Cons
207209
return b
208210
}
209211

212+
func (b *ConsoleServerCLIConfigBuilder) ContentSecurityPolicies(cspList map[v1.DirectiveType][]string) *ConsoleServerCLIConfigBuilder {
213+
b.contentSecurityPolicyList = cspList
214+
return b
215+
}
216+
210217
func (b *ConsoleServerCLIConfigBuilder) I18nNamespaces(i18nNamespaces []string) *ConsoleServerCLIConfigBuilder {
211218
b.i18nNamespaceList = i18nNamespaces
212219
return b
@@ -244,19 +251,20 @@ func (b *ConsoleServerCLIConfigBuilder) CopiedCSVsDisabled(copiedCSVsDisabled bo
244251

245252
func (b *ConsoleServerCLIConfigBuilder) Config() Config {
246253
return Config{
247-
Kind: "ConsoleConfig",
248-
APIVersion: "console.openshift.io/v1",
249-
Auth: b.auth(),
250-
Session: b.session(),
251-
ClusterInfo: b.clusterInfo(),
252-
Customization: b.customization(),
253-
ServingInfo: b.servingInfo(),
254-
Providers: b.providers(),
255-
MonitoringInfo: b.monitoringInfo(),
256-
Plugins: b.plugins(),
257-
I18nNamespaces: b.i18nNamespaces(),
258-
Proxy: b.proxy(),
259-
Telemetry: b.telemetry,
254+
Kind: "ConsoleConfig",
255+
APIVersion: "console.openshift.io/v1",
256+
Auth: b.auth(),
257+
Session: b.session(),
258+
ClusterInfo: b.clusterInfo(),
259+
Customization: b.customization(),
260+
ServingInfo: b.servingInfo(),
261+
Providers: b.providers(),
262+
MonitoringInfo: b.monitoringInfo(),
263+
Plugins: b.plugins(),
264+
I18nNamespaces: b.i18nNamespaces(),
265+
Proxy: b.proxy(),
266+
ContentSecurityPolicy: b.contentSecurityPolicy(),
267+
Telemetry: b.telemetry,
260268
}
261269
}
262270

@@ -520,6 +528,10 @@ func (b *ConsoleServerCLIConfigBuilder) i18nNamespaces() []string {
520528
return b.i18nNamespaceList
521529
}
522530

531+
func (b *ConsoleServerCLIConfigBuilder) contentSecurityPolicy() map[v1.DirectiveType][]string {
532+
return b.contentSecurityPolicyList
533+
}
534+
523535
func (b *ConsoleServerCLIConfigBuilder) proxy() Proxy {
524536
return Proxy{
525537
Services: b.proxyServices,

pkg/console/subresource/consoleserver/config_builder_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,8 @@ customization:
12881288
resource: namespaces
12891289
subresource: ""
12901290
name: ""
1291+
fieldselector: null
1292+
labelselector: null
12911293
missing:
12921294
- namespace: ""
12931295
verb: list
@@ -1296,6 +1298,8 @@ customization:
12961298
resource: clusterroles
12971299
subresource: ""
12981300
name: ""
1301+
fieldselector: null
1302+
labelselector: null
12991303
- id: perspective2
13001304
visibility:
13011305
state: Disabled
@@ -1354,6 +1358,8 @@ customization:
13541358
resource: namespaces
13551359
subresource: ""
13561360
name: ""
1361+
fieldselector: null
1362+
labelselector: null
13571363
missing:
13581364
- namespace: ""
13591365
verb: list
@@ -1362,6 +1368,8 @@ customization:
13621368
resource: clusterroles
13631369
subresource: ""
13641370
name: ""
1371+
fieldselector: null
1372+
labelselector: null
13651373
pinnedResources:
13661374
- group: apps
13671375
version: v1
@@ -1424,6 +1432,8 @@ customization:
14241432
resource: namespaces
14251433
subresource: ""
14261434
name: ""
1435+
fieldselector: null
1436+
labelselector: null
14271437
missing:
14281438
- namespace: ""
14291439
verb: list
@@ -1432,6 +1442,8 @@ customization:
14321442
resource: clusterroles
14331443
subresource: ""
14341444
name: ""
1445+
fieldselector: null
1446+
labelselector: null
14351447
pinnedResources: []
14361448
- id: perspective2
14371449
visibility:

pkg/console/subresource/consoleserver/types.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package consoleserver
22

33
import (
44
configv1 "github.com/openshift/api/config/v1"
5+
v1 "github.com/openshift/api/console/v1"
56
operatorv1 "github.com/openshift/api/operator/v1"
67
authorizationv1 "k8s.io/api/authorization/v1"
78
)
@@ -17,19 +18,20 @@ import (
1718

1819
// Config is the top-level console server cli configuration.
1920
type Config struct {
20-
APIVersion string `yaml:"apiVersion"`
21-
Kind string `yaml:"kind"`
22-
ServingInfo `yaml:"servingInfo"`
23-
ClusterInfo `yaml:"clusterInfo"`
24-
Auth `yaml:"auth"`
25-
Session `yaml:"session"`
26-
Customization `yaml:"customization"`
27-
Providers `yaml:"providers"`
28-
MonitoringInfo `yaml:"monitoringInfo,omitempty"`
29-
Plugins map[string]string `yaml:"plugins,omitempty"`
30-
I18nNamespaces []string `yaml:"i18nNamespaces,omitempty"`
31-
Proxy Proxy `yaml:"proxy,omitempty"`
32-
Telemetry map[string]string `yaml:"telemetry,omitempty"`
21+
APIVersion string `yaml:"apiVersion"`
22+
Kind string `yaml:"kind"`
23+
ServingInfo `yaml:"servingInfo"`
24+
ClusterInfo `yaml:"clusterInfo"`
25+
Auth `yaml:"auth"`
26+
Session `yaml:"session"`
27+
Customization `yaml:"customization"`
28+
Providers `yaml:"providers"`
29+
MonitoringInfo `yaml:"monitoringInfo,omitempty"`
30+
Plugins map[string]string `yaml:"plugins,omitempty"`
31+
I18nNamespaces []string `yaml:"i18nNamespaces,omitempty"`
32+
Proxy Proxy `yaml:"proxy,omitempty"`
33+
ContentSecurityPolicy map[v1.DirectiveType][]string `yaml:"contentSecurityPolicy,omitempty"`
34+
Telemetry map[string]string `yaml:"telemetry,omitempty"`
3335
}
3436

3537
type Proxy struct {

0 commit comments

Comments
 (0)