Allow custom csp header#5477
Conversation
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
1 similar comment
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as resolved.
This comment was marked as resolved.
|
@cla-bot check |
735beb2 to
cdef4c4
Compare
This comment was marked as resolved.
This comment was marked as resolved.
| use ipl\Web\Compat\CompatForm; | ||
| use ipl\Web\Widget\Callout; | ||
|
|
||
| class CspConfigForm extends CompatForm |
There was a problem hiding this comment.
This currently doesn't output the config.ini to the screen like the old ConfigForm did.
I think changing the form type to a CompatForm is still the right call, because we don't want to rely on Zend_Forms forever.
If this behavior essential, we should reimplement the behavior of ConfigFrom as a CompatForm instead.
There was a problem hiding this comment.
Even in new modules we reimplemented this behavior: Icinga/icingadb-web#1269
So I'd not downgrade this functionality.
| ); | ||
|
|
||
| $this->addPolicyTable( | ||
| t('Dashboard'), |
There was a problem hiding this comment.
If you're concerned about #5477 (comment), you should be about this one too:
As not a IW2 dev, I'd have absolutely no clue that these are only my dashboards (if any!) and others may have their own.
Consider listing the dashboard-CSPs of all users, after all we have them (by name) in the preference store or something idk.
Module-provided dashboards should already be included as you should see all modules as admin. I guess they may contain external URLs as well.
There was a problem hiding this comment.
I'm not sure if we always have access to all users in something like LDAP.
Also listing all dashboards and menu entries for all users in an organization on every initial request seems pretty expensive.
If a module wants something whitelisted beyond what is automatically detected, it can already do that.
There was a problem hiding this comment.
We always have access to all users' dashboards and menu entries.
Imagine you make a custom CSP based on the generated one, but latter misses non-your dashboards.
There was a problem hiding this comment.
Extended Navigation- and DashboardCspLoader to optionally list all required CSP directives instead of only for the currently authenticated one.
An open question is if we should add a parameter to the Hook so that modules can handle this case as well.
There was a problem hiding this comment.
I can't even imagine what the latter might be good for. But in doubt, add a such parameter. (See #5419 (comment).) Altering hooks afterwards is... wait a minute, you already know how it's like (Icinga/icingaweb2-module-pdfexport#88).
There was a problem hiding this comment.
Added a $allUsers bool to Csp::getCsp method
There was a problem hiding this comment.
💡
@lippserd What about instead having two methods in the hook? Or, better, just one for now and when we need an additional one, we provide a default implementation in the abstract hook class.
There was a problem hiding this comment.
@lippserd's solution to this problem is to give the user object to the Hook.
This should allow for easier testing, and maybe impersonation later.
Calling the hook without a user object would return the CSP for all users.
f9f19b4 to
3a4e7ac
Compare
a07b626 to
9c1bb6b
Compare
9c1bb6b to
fe9ed22
Compare
97b1f15 to
9d50bc0
Compare
fe9ed22 to
5a85fc1
Compare
1f65629 to
6081235
Compare
08a5bf3 to
aa70225
Compare
| * | ||
| * @return void | ||
| * | ||
| * @throws SecurityException If the user lacks the permission for configuring the security configuration |
There was a problem hiding this comment.
I'm not sure about this. I think we do only document exceptions directly thrown by functions.
c3c7fee to
d87813a
Compare
The hardcoded CSP directives are replaced with dynamic collection from three sources: user-defined navigation iframe items, dashboard dashlets, and a new CspDirectiveHook for modules to contribute their own directives. This allows the generated CSP header to accommodate user-configured external URLs rather than blocking them outright. Co-authored-by: Davide Zeni <davidezeni02@gmail.com> Co-authored-by: Alexander Rieß <alexander.riess@icinga.com>
d87813a to
cf6621d
Compare
jrauh01
left a comment
There was a problem hiding this comment.
Some general stuff regarding phpdocs:
- The lines do not need to necessarily end on a period. They should in general be wrapped after a specific line length.
- Please use imperative voice in phpdoc short description.
- In
@returnstatements please do not write "Returns ...". - Please do not use types like
null|string. Rather use?string.
I already suggested changes in some places, but please check all docs on your own to fix this.
jrauh01
left a comment
There was a problem hiding this comment.
Some general stuff regarding phpdocs:
- The lines do not need to necessarily end on a period. They should in general be wrapped after a specific line length.
- Please use imperative voice in phpdoc short description.
- In
@returnstatements please do not write "Returns ...". - Please do not use types like
null|string. Rather use?string.
I already suggested changes in some places, but please check all docs on your own to fix this.
Replaces ad-hoc fetching logic with a CspLoader interface and concrete implementations for arrays, dashboards, navigation items and modules. Each directive set is wrapped in an AttributedCsp that pairs the Csp instance with a CspReason, tracking which entity (pane, navigation item, module) contributed it. This attribution is required for a planned config UI to explain to admins why a specific directive is present in the generated header.
d4f3768 to
05f681f
Compare
Delegates all directive fetching in Csp to the new CspLoader abstractions. CspDirectiveHook, which returned a raw array, is replaced by CspHook, which returns a typed Csp instance per-user and for all users. The Csp class drops the singleton pattern for the CSP instance and gains isEnabled(), isDashboardEnabled() and isNavigationEnabled() helpers that read from config.ini, making feature checks consistent across the codebase.
Moves CSP settings out of the General configuration form into a new
Security tab under Configuration > Application, gated by a new
config/security permission. The new CspConfigForm lets admins toggle the
CSP header, control which directive sources (modules, dashboards,
navigation) feed the generated policy, and optionally supply a custom
CSP string that fully overrides it. The editor analyses the custom input
and flags unsafe keywords ('unsafe-inline', 'unsafe-eval') and schemes
(http:, blob:).
Adds a Security Configuration section to 03-Configuration.md covering
all new config.ini keys (use_custom_csp, custom_csp, csp_enable_modules,
csp_enable_dashboards, csp_enable_navigation). Expands the CSP section
in 20-Advanced-Topics.md to explain per-source controls, the CspHook
extension point and the custom CSP override, including the {style_nonce}
placeholder. Adds a CspHook reference entry with an example
implementation to 60-Hooks.md.
When the CSP header is enabled but dashboards or navigation are not included in the generated policy, editing a dashlet or navigation item now shows an informational callout explaining that external URLs may not render correctly and pointing the user to the Security configuration page. Without this hint the breakage is silent and hard to connect to the CSP settings.
05f681f to
b596eab
Compare







This PR extends the Content Security Policy (CSP) support in Icinga Web 2. Previously the CSP header was a fixed, hardcoded string. It is now configurable and automatically assembled from multiple sources.
Note: This PR is an extension of #5337
Automatic CSP generation
The CSP header is now built dynamically from a set of loaders:
StaticCspLoader: provides the baseline system directives (default-src,style-src,font-src,img-src,frame-src)DashboardCspLoader: adds frame-src entries for URLs configured in dashletsNavigationCspLoader: adds frame-src entries for URLs configured in navigation itemsModuleCspLoader: invokes the newCspHookto collect directives declared by modulesEach loader produces
AttributedCspinstances that carry aCspReasondescribing their origin. This metadata is surfaced in the config UI as a table showing where each directive comes from.Custom CSP header
Administrators can opt out of automatic generation, either completely or for each category separately and supply a raw CSP string instead. The string supports a
{style_nonce}placeholder that is replaced with the per-request nonce at runtime.CspHook
A new
CspHooklets modules register custom CSP directives without modifying core configuration. Modules implementgetCspForUser(User $user)(user-specific additions, e.g. URLs stored per-user) and `getCspForAllUsers() (all directives that apply to any user on the icingaweb instance)Configuration UI
A new
CspConfigFormreplaces the single checkbox that previously lived in the general application settings. It provides:Callouts in affected forms
DashletFormandNavigationItemFormnow show an info callout when CSP is active, but the corresponding loader is disabled, so users understand why an external URL might be blocked.Configuration
All new options live under [security] in config.ini:
As well as adding a table below the form which displays the source of the automatically generated CSP-Header.
Requires
Introduce Callout ipl-web#358
Implement Csp ipl-web#361
Add
Str::isEmpty()ipl-stdlib#77Introduce
DisplayFormElementfor embedding HTML inCompatFormipl-web#385closes Allow editing of the CSP trusted image sources. #5337
closes Allow customization of the CSP #5333