-
Notifications
You must be signed in to change notification settings - Fork 515
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
CONSOLE-4265: Add new API field to ConsolePlugin CRD for allowing additional CSP sources #2042
base: master
Are you sure you want to change the base?
Conversation
@jhadvig: This pull request references CONSOLE-4265 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.18.0" version, but no target version was set. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
Hello @jhadvig! Some important instructions when contributing to openshift/api: |
console/v1/types_console_plugin.go
Outdated
ImgSrc DirectiveType = "img-src" | ||
StyleSrc DirectiveType = "style-src" | ||
FontSrc DirectiveType = "font-src" | ||
BaseUri DirectiveType = "base-uri" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't want plugins to be able to change the base-uri
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spadgett not sure how they could, since these are enums?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, we should remove base-uri
from the enums. It's not something we want to expose to plugins.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry, I've misunderstood the comment.
Yes I agree that base-uri
directive should be only set by console backend.
console/v1/types_console_plugin.go
Outdated
DefaultSrc DirectiveType = "default-src" | ||
ScriptSrc DirectiveType = "script-src" | ||
ImgSrc DirectiveType = "img-src" | ||
StyleSrc DirectiveType = "style-src" | ||
FontSrc DirectiveType = "font-src" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should describe what these are or link to documentation. A link to the MDN topic would be good (unless we have a policy against linking to 3rd party docs).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I see we are using links for 3rd party docs in the API - https://github.com/openshift/api/blob/master/config/v1/types_dns.go#L79-L81
console/v1/types_console_plugin.go
Outdated
// ConsolePluginCSP | ||
type ConsolePluginCSP struct { | ||
Directive DirectiveType `json:"directive"` | ||
Sources []string `json:"source"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make this something generic like values
since we might have directives in the future that aren't sources.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to make clear that this adds to the existing console directive values rather than replacing them.
console/v1/types_console_plugin.go
Outdated
@@ -48,6 +48,26 @@ type ConsolePluginSpec struct { | |||
// i18n is the configuration of plugin's localization resources. | |||
// +optional | |||
I18n ConsolePluginI18n `json:"i18n"` | |||
// csp is a list of Content Security Policy directives for the plugin. | |||
CSP []ConsolePluginCSP `json:"csp"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would lean toward spelling it out: contentSecurityPolicy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely 👍
console/v1/types_console_plugin.go
Outdated
@@ -48,6 +48,26 @@ type ConsolePluginSpec struct { | |||
// i18n is the configuration of plugin's localization resources. | |||
// +optional | |||
I18n ConsolePluginI18n `json:"i18n"` | |||
// csp is a list of Content Security Policy directives for the plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the final doc, we'll want more detail about what this is and why you'd need to change it. Examples would be helpful.
15cfc10
to
5521582
Compare
@spadgett thank you for the review. I've address the comments. PTAL |
console/v1/types_console_plugin.go
Outdated
// OpenShift web console server CSP response header: | ||
// script-src: https://script1.com/ https://script2.com/ https://script3.com/ | ||
// font-src: https://font1.com/ https://font2.com/ | ||
// img-src: https://img1.com/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These would still include self
as well which the console sets.
console/v1/types_console_plugin.go
Outdated
// Each directive specifies a list of values that indicate server origins and script endpoints | ||
// from which the plugin's assets can be loaded. | ||
// This helps guard against cross-site scripting (XSS) attacks. | ||
// Available directive types are default-src, script-src, img-src, style-src and font-src. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Available directive types are default-src, script-src, img-src, style-src and font-src. | |
// Available directives are default-src, script-src, img-src, style-src and font-src. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also note that by default console adds the value self
for the various src
directives.
console/v1/types_console_plugin.go
Outdated
// The OpenShift web console server aggregates the CSP directives and values across | ||
// all enabled ConsolePlugin CRs, merging them to set a unified CSP header. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The OpenShift web console server aggregates the CSP directives and values across | |
// all enabled ConsolePlugin CRs, merging them to set a unified CSP header. | |
// The OpenShift web console server aggregates the CSP directives and values across | |
// its own default values and all enabled ConsolePlugin CRs, merging them to set a unified | |
// CSP header. |
console/v1/types_console_plugin.go
Outdated
// +kubebuilder:validation:Enum:="default-src";"script-src";"img-src";"style-src";"font-src" | ||
// +kubebuilder:validation:Required | ||
Directive DirectiveType `json:"directive"` | ||
// values defines an array of source values mostly specifying server origins |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// values defines an array of source values mostly specifying server origins | |
// values defines an array of additional values to append to the console defaults for this directive. |
We might add directives that take something other than a source in the future.
console/v1/types_console_plugin.go
Outdated
|
||
// ConsolePluginCSP holds configuration for a specific CSP directive | ||
type ConsolePluginCSP struct { | ||
// directive is a type of CSP directive. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// directive is a type of CSP directive. | |
// directive specifies which Content-Security-Policy directive to configure. |
@spadgett thank you for the review. Addressed your comments in the additional commit. |
/assign @JoelSpeed |
@jhadvig: This pull request references CONSOLE-4265 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.18.0" version, but no target version was set. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
Thanks, @jhadvig. I agree with the approach and appreciate the detailed doc. Let's have @JoelSpeed take a look 👍 |
console/v1/types_console_plugin.go
Outdated
// of the plugin in the OpenShift web console. | ||
// Available directives are default-src, script-src, img-src, style-src and font-src. | ||
// Each of the available CSP directive may be defined only once in the list. | ||
// By default the console server adds the value 'self'to all the various 'src' directives. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// By default the console server adds the value 'self'to all the various 'src' directives. | |
// By default the console server adds the value 'self' to all directives. |
Reading this I'd wonder by self
is added? And you say by default, does this mean I can disable this behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console needs self
to function for all of these values. It can't be removed.
All of the plugin customizations append to the default console values that console needs to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that in the future we could add directives that don't specify sources (although right now I think everything we expose is a "src" directive). We'd have to come back and update the text here later if we leave that out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that is to say, if this were a directive that were not a src
style, then it wouldn't also need the self
prepending?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, self
only makes sense as a value for the various CSP src
directives.
console/v1/types_console_plugin.go
Outdated
// ConsolePluginCSP holds configuration for a specific CSP directive | ||
type ConsolePluginCSP struct { | ||
// directive specifies which Content-Security-Policy directive to configure. | ||
// Available directive types are default-src, script-src, img-src, style-src and font-src. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you expand the godoc here briefly to explain what the directives do, in case the user hasn't explained the higher level field
Eg what is a default vs a script src?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are explained as part of the enum definition, together with the link for official docs. Thought that it might be more suitable to explain them briefly there together with link.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The enum definition won't be generated into docs, please copy to here and make sure that it ends up in the CRD schema for oc explain
use later
console/v1/types_console_plugin.go
Outdated
// its CSP header. | ||
// +kubebuilder:validation:Required | ||
// +kubebuilder:validation:MinItems=1 | ||
Values []string `json:"values"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the format of this string? Is it always a URL? What is the maximum possible length? What characters are valid, which are not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JoelSpeed I think we have to leave it open since CSP directive values are not always URLs and don't always follow a specific format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree to leave it open and add additional validation on the backend
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should disallow ;
since that is a separator in the header between directives. Maybe we can do more validation on the values. Here is the syntax from the standard:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
directive-value = *( WSP / <VCHAR except ";" and ","> )
Looks like ;
and ,
are not allowed in values and whitespace separates values, so can be disallowed as well. VCHAR
refers to printing characters, so I think pretty much anything else is allowed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a length limit on specific values, although some HTTP servers like Apache have limits on the total size of response headers. If we pick it a limit, it would be an arbitrary value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spadgett this would require following pattern
// +kubebuilder:validation:Pattern=`^[\t\x20!#\$%&'\(\)\*\+\-\.\/0-9:<=>\?@A-Za-z\[\\\]\^_`\{\|\}~]*$`
Whitespace Characters:
- \t: Tab
- \x20: Space
Visible (printable) characters (VCHAR) except ; and ,:
- ! to #: !, #
- $ to +: $, %, &, ', (, ), *, +
- .: -, .
- /: /
- 0-9: All digits 0 through 9
- : to <: :, <
- = to >: =, >
- ? to @: ?, @
- A-Z: Uppercase letters A through Z
- a-z: Lowercase letters a through z
- [ to ]: [, , ]
- ^_: ^, _
- `: Backtick
- { to ~: {, |, }, ~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the regex here not support negative matching? ie, match not this character?
If it doesn't, you could use CEL validations quite easily, !self.Contains(';')
, which would be readable.
I don't see a length limit on specific values, although some HTTP servers like Apache have limits on the total size of response headers. If we pick it a limit, it would be an arbitrary value.
Even an arbitrary limit is better than none, how about starting at 1024
and seeing if you get any customers complain that it is too small?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even an arbitrary limit is better than none, how about starting at 1024 and seeing if you get any customers complain that it is too small?
@JoelSpeed I agree. Works for me.
@JoelSpeed thank you for the comments. Addressed them in a separate commit and replied to some of them. |
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: jhadvig The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
console/v1/types_console_plugin.go
Outdated
// its Content Security Policy header. | ||
// +kubebuilder:validation:Required | ||
// +kubebuilder:validation:MinItems=1 | ||
Values []string `json:"values"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be a reasonable/arbitrary length limit for this list, would you expect someone to have more than say, 32 values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
32 should be more than enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, I dont think any plugin would ever contribute more then 16
@JoelSpeed comments addressed. PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure when making updates to generate the CRD schema, at the moment I don't think the generation would succeed but you haven't generated so we can't actually see whether it would or not
I'd also like you to write some integration tests (you can see the style in the tests folder in each API group) to check the validations that you are adding actually work and return sensible looking errors
console/v1/types_console_plugin.go
Outdated
// The OpenShift web console server aggregates the CSP directives and values across | ||
// its own default values and all enabled ConsolePlugin CRs, merging them to set a unified | ||
// CSP header. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a combined maximum that we need to be aware of?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's recommended to keep CSP headers under 8 KB to ensure compatibility across all browsers, servers, and proxies, since different browsers impose their own limits on HTTP header size. For example:
- Chrome and Edge generally support headers up to 8 KB.
- Firefox and Safari can support headers up to 16 KB or more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, should we impose a total limit in size? You could do this with CEL and a map function that maps every entry in the list to its size and then sum them. We are limiting to 32 items at 1kb each so in theory we could reach 32kb for each of the 5 source types.
I think something like the following on CSP field would work.
// +kubebuilder:validation:XValidation:rule="self.map(x, x.values.map(y, y.size()).sum()).sum() < 8192"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right but we are talking only about a single ConsolePlugin CR. There could be multiple CRs, which could contribute the same amount of values, which could cause problem, since console will gather all the directives form all the plugins and aggregates them. But that would need to be handled programatically I guess.
Thinking if we should not start with more strict MaxItems
for the values
field, something like 4 and if that will cause any issues we could bump the number.
Realistically I dont think plugins will contribute more then 4 values per directive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have the option to restrict the limits down further if you think they can be restricted down, you're the SME on what the quantity of expected values might be here.
And yes, you're right, multiple plugins may contribute, and we may not catch everything with this validation, but, this would at least ensure that the single console plugin doesn't, by itself, violate the restriction. It is the strictest we can be
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yes, you're right, multiple plugins may contribute, and we may not catch everything with this validation, but, this would at least ensure that the single console plugin doesn't, by itself, violate the restriction. It is the strictest we can be
👍
@JoelSpeed addressing most of the comments. The only missing piece is the size of the directives, size this will only handle a since ConsolePlugin CR and there could be multiple running on the server. Adding generated CRD schema + integration test. PTAL note: Ive accidentally forced pushed the change... sorry :-/ |
- directive: ScriptSrc | ||
values: | ||
- https://script1.com/ | ||
expectedError: "Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have the complete error here? Looks like it's been truncated?
This will do a substring match but I'm curious what comes after this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JoelSpeed the whole msg is:
"ConsolePlugin.console.openshift.io \"test-vgzng\" is invalid: spec.contentSecurityPolicy[1]: Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, we should update this line then to spec.contentSecurityPolicy[1]: Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"}
to capture the field name and the end that got truncated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outstanding to fix
expectedError: "Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"" | |
expectedError: "spec.contentSecurityPolicy[1]: Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"}" |
- directive: TestSrc | ||
values: | ||
- https://script1.com/ | ||
expectedError: "spec.contentSecurityPolicy[0].directive: Unsupported value: \"TestSrc\": supported values: \"DefaultSrc\", \"ScriptSrc\", \"ImgSrc\", \"StyleSrc\", \"FontSrc\"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please finish all files with an empty new line char
console/v1/types_console_plugin.go
Outdated
// whitespace, commas, or semicolons. | ||
// +kubebuilder:validation:MinLength=1 | ||
// +kubebuilder:validation:MaxLength=1024 | ||
// +kubebuilder:validation:XValidation:rule="!self.contains(' ')",message="CSP directive value cannot contain a whitespace" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This covers spaces, do we also need to cover tabs somehow? Does the regex used in CEL support a whitespace grouping? Perhaps using another function like the findAll function would help to find any whitespace using regex and then catch all whitespace in a single rule?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
console/v1/types_console_plugin.go
Outdated
// Each directive value must have a maximum length of 1024 characters and must not contain | ||
// whitespace, commas, or semicolons. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This must go into the godoc for the field that is using this type as well please
// +listType=map | ||
// +listMapKey=directive | ||
// +optional | ||
ContentSecurityPolicy []ConsolePluginCSP `json:"contentSecurityPolicy"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder to add the validation suggested in #2042 (comment)
- directive: ScriptSrc | ||
values: | ||
- https://script1.com/ | ||
expectedError: "Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outstanding to fix
expectedError: "Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"" | |
expectedError: "spec.contentSecurityPolicy[1]: Duplicate value: map[string]interface {}{\"directive\":\"ScriptSrc\"}" |
- directive: ScriptSrc | ||
values: | ||
- "*" | ||
expectedError: "Invalid value: \"string\": CSP directive value cannot be a wildcard" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please include the field path part of the error
console/v1/types_console_plugin.go
Outdated
// Content-Security-Policy: default-src 'self'; base-uri 'self'; script-src 'self' https://script1.com/ https://script2.com/ https://script3.com/; font-src 'self' https://font1.com/ https://font2.com/; img-src 'self' https://img1.com/; style-src 'self'; frame-src 'none'; object-src 'none' | ||
// | ||
// +kubebuilder:validation:MaxItems=5 | ||
// +kubebuilder:validation:XValidation:rule="self.map(x, x.values.map(y, y.size()).sum()).sum() < 8192" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a message to explain it
// +kubebuilder:validation:XValidation:rule="self.map(x, x.values.map(y, y.size()).sum()).sum() < 8192" | |
// +kubebuilder:validation:XValidation:rule="self.map(x, x.values.map(y, y.size()).sum()).sum() < 8192",message="the total combined size of values of all directives must not exceed 8192 (8kb)" |
console/v1/types_console_plugin.go
Outdated
// values defines an array of values to append to the console defaults for this directive. | ||
// Each ConsolePlugin may define their own directives with their values. These will be set | ||
// by the OpenShift web console's backend, as part of its Content-Security-Policy header. | ||
// The array can contain at most 32 values. Each directive value must have a maximum length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The array can contain at most 32 values. Each directive value must have a maximum length | |
// The array can contain at most 8 values. Each directive value must have a maximum length |
console/v1/types_console_plugin.go
Outdated
// Each ConsolePlugin may define their own directives with their values. These will be set | ||
// by the OpenShift web console's backend, as part of its Content-Security-Policy header. | ||
// The array can contain at most 32 values. Each directive value must have a maximum length | ||
// of 1024 characters and must not contain whitespace, commas, or semicolons. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// of 1024 characters and must not contain whitespace, commas, or semicolons. | |
// of 1024 characters and must not contain whitespace, commas, or semicolons. | |
// The value '*' is not permitted. |
console/v1/types_console_plugin.go
Outdated
// values defines an array of values to append to the console defaults for this directive. | ||
// Each ConsolePlugin may define their own directives with their values. These will be set | ||
// by the OpenShift web console's backend, as part of its Content-Security-Policy header. | ||
// The array can contain at most 32 values. Each directive value must have a maximum length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need to mention that each value in the array must be unique
console/v1/types_console_plugin.go
Outdated
// Each directive specifies a list of values that indicate server origins and script endpoints | ||
// from which the plugin's assets can be loaded. This helps guard against cross-site | ||
// scripting (XSS) attacks by enforcing strict security policies for asset loading. | ||
// Dynamic plugins should specify this field if they are loading assets from outside |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Dynamic plugins should specify this field if they are loading assets from outside | |
// Dynamic plugins should always prefer loading their assets via Console Bridge /api/plugins endpoint. If a plugin really needs to load assets from other endpoints, it should declare these endpoints using the appropriate CSP source directives. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the wording a bit:
// Dynamic plugins should specify this field if need to load assets from outside
// the cluster or if violation reports are observed. Dynamic plugins should always prefer
// loading their assets from within the cluster, either by vendoring them, or fetching
// from a cluster service.
WDYT ?
console/v1/types_console_plugin.go
Outdated
// of the plugin in the OpenShift web console. | ||
// Available directive types are DefaultSrc, ScriptSrc, ImgSrc, StyleSrc and FontSrc. | ||
// Each of the available directives may be defined only once in the list. | ||
// The value 'self' will be prepended to all source type directives. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The value 'self' will be prepended to all source type directives. | |
// The value 'self' is automatically included in all fetch directives when generating the Console policy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The value 'self' is automatically included in all fetch directives by the OpenShift web
// console's backend.
WDYT?
// +kubebuilder:validation:XValidation:rule="!self.contains(',')",message="CSP directive value cannot contain a comma" | ||
// +kubebuilder:validation:XValidation:rule="!self.contains(';')",message="CSP directive value cannot contain a semi-colon" | ||
// +kubebuilder:validation:XValidation:rule="self != '*'",message="CSP directive value cannot be a wildcard" | ||
type CSPDirectiveValue string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Important ❗ I think we should explicitly disallow any CSP directive values which are enclosed in single quotes.
'self'
, 'unsafe-eval'
etc. are all tokens with special meaning when interpreted by the browser and I think that only Console web application itself should be allowed to use these when generating the security policy.
For example, it makes no sense for a plugin to use 'self'
value since that's already included by default. Also, it makes no sense for a plugin to e.g. use 'unsafe-hashes'
or 'unsafe-inline'
to lower the security constraint for specific asset fetch directives.
The above validation list should include a rule that disallows a string that begins and ends with '
character.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added following validation
// +kubebuilder:validation:XValidation:rule="!(self.startsWith(\"'\") && self.endsWith(\"'\"))",message="CSP directive value cannot start or and with a quote"
values: | ||
- https://scr ipt1.com/ | ||
expectedError: "spec.contentSecurityPolicy[0].values[0]: Invalid value: \"string\": CSP directive value cannot contain a whitespace" | ||
- name: Should throw an error for invalid CSP directive values with whitespace, using tab |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given following validation checks, it looks like we also need add more tests here
- name: Should throw an error for invalid CSP directive values with comma
- name: Should throw an error for invalid CSP directive values with semicolon
- name: Should throw an error for invalid CSP directive values with strings enclosed in single quotes(based on vojtechszocs's comment)
features/features.go
Outdated
reportProblemsToJiraComponent("Management Console"). | ||
contactPerson("jhadvig"). | ||
productScope(ocpSpecific). | ||
enableIn(configv1.Default, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add a gate if you are adding it to all sets at once? I think you want to not have it as Default for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JoelSpeed hmm but will the field in this case be available by default? Cause thats what we need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To complete the loop from Slack, it should be introduced as TP and then promoted to Default only once QE are happy that the feature is complete and stable
contactPerson("jhadvig"). | ||
productScope(ocpSpecific). | ||
enableIn(configv1.Default, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). | ||
mustRegister() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need to add an enhancementPR link for your feature
mustRegister() | |
enhancementPR(""). | |
mustRegister() |
…itional CSP sources
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the CRD generation needs to be re-run please
console/v1/tests/consoleplugins.console.openshift.io/AAA_ungated.yaml
Outdated
Show resolved
Hide resolved
- name: Should throw an error for invalid CSP directive values, starting and ending with quotes | ||
initial: | | ||
apiVersion: console.openshift.io/v1 | ||
kind: ConsolePlugin | ||
spec: | ||
displayName: foo | ||
backend: | ||
type: Service | ||
contentSecurityPolicy: | ||
- directive: ScriptSrc | ||
values: | ||
- "'none'" | ||
expectedError: "spec.contentSecurityPolicy[0].values[0]: Invalid value: \"string\": CSP directive value cannot start or and with a quote" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about unbalanced quotes? What if I had
- a'bad
- thing'b
concatenated this becomes a'bad thing'b
, is the 'bad thing'
in there problematic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a CSP header like Content-Security-Policy: script-src a'bad thing'b;
is used, the browser will consider it invalid due to the unescaped or unrecognized syntax in a'bad thing'b. The result is that the entire CSP directive could be ignored. That said I think we should disable the quote completely. If you agree will also keep the previous check for a value which starts and ends with quote?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we allow hash values?
We want to support that for plugins that have inline scripts. It requires using '
characters, though. (nonce we shouldn't support, however, since there's no way to generate a unique value each time.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spadgett then I think we need to define the validation rules as:
- Hash directive format: Check for a valid hash algorithm (like SHA-256) followed by a hyphen and a valid Base64 value.
- Host source format: Check that the input matches the pattern of a valid URL.
For the CEL rule below accomplishes this by checking for two conditions:
- The value starts with a valid hash algorithm (sha256, sha384, or sha512) followed by a - and a valid Base64 string.
- The value matches the pattern of a valid host source (beginning with http:// or https://).
WDYT ?
ccing @vojtechszocs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Host sources don't need to start with http://
or https://
. For example, *.example.com
is a valid host source.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, sorry. I've unintentionally leaved it out, but it should be valid as well 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spadgett @jhadvig I think the currently proposed validation rules
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:XValidation:rule="!self.contains(\"'\")",message="CSP directive value cannot contain a quote"
// +kubebuilder:validation:XValidation:rule="!self.matches('\\\\s')",message="CSP directive value cannot contain a whitespace"
// +kubebuilder:validation:XValidation:rule="!self.contains(',')",message="CSP directive value cannot contain a comma"
// +kubebuilder:validation:XValidation:rule="!self.contains(';')",message="CSP directive value cannot contain a semi-colon"
// +kubebuilder:validation:XValidation:rule="self != '*'",message="CSP directive value cannot be a wildcard"
type CSPDirectiveValue string
are reasonably strict, we can always loosen these rules based on CSP violation reports once we start collecting them with telemetry.
Additional validation logic can be placed into the Console operator (since it's responsible for ensuring valid configuration of Console and all of its resources) or directly into Bridge server.go
function indexHandler
where we generate the actual CSP string.
One example of additional validation would be e.g. DefaultSrc
and ScriptSrc
containing the same value, in which case we want to remove that value from ScriptSrc
since the DefaultSrc
directive acts as a fallback for the ScriptSrc
directive.
Another example of additional validation would be to ensure the plugin provided CSP source values are valid URLs with potential *
wildcards.
The only 'something'
exception that makes sense to me is '<hash-algorithm>-<base64-value>'
but I think it's better to wait for CSP violation reports first before we start allowing such exceptions. (Other 'something'
special values don't seem too relevant to me at the moment, the main focus should be allowing additional plugin-specific remote sources so they can be loaded without issues in the browser.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree that lets start with stricter validation and relax it down the road, since we will be in report mode for couple of releases.
/retest |
@jhadvig: all tests passed! Full PR test history. Your PR dashboard. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
When serving Console HTML index page, we generate the policy that includes allowed (trustworthy) sources.
It may be necessary for some dynamic plugins to add new sources in order to avoid CSP violations at Console runtime.
We need to extend the ConsolePlugin CRD, in order to give the plugin creators a mechanism how to specify these source.
We discussed with @vojtechszocs two possible implementation:
Add a new field to spec for specifying an array of sources which would apply for all the CSP directives -
spec.csp.allowedSources
. This implementation is pretty straight-forward but automatically applying the same source across different directives could unintentionally block legitimate content or reduce security.Example:
(Proposed solution) Add a new field to spec for specifying an array of sources for each of the directives. Letting users manage different whitelists for different types of content, gives them more flexibility in controlling security settings.
Example:
Note: The PR is missing some descriptions and validation and unit tests. Will address those once we align on the API structure.
/assign @spadgett @vojtechszocs
Story: https://issues.redhat.com/browse/CONSOLE-4265