elastic / kibana

Your window into the Elastic Stack
https://www.elastic.co/products/kibana
Other
19.71k stars 8.13k forks source link

Expose a security header API to plugins #181812

Open jeramysoucy opened 4 months ago

jeramysoucy commented 4 months ago

Related issues: #177777, #179061

Describe the feature:

Our default Content Security Policy (CSP) and Permissions Policy headers are purposefully restrictive. This means that sometimes we encounter plugins that require additional directive values in order to support features (see related issues). Rather than maintaining a cumbersome hardcoded list of trusted sources for each directive that each plugin requires, we should expose an API for plugins to augment our default directive settings.

This feature entails two parts:

  1. An API for plugins to specify additional values for CSP and Permissions Policy directives
  2. An implementation for always including a minimum default value for each security directive (like we currently do when applying a custom configuration for CSP - see https://github.com/elastic/kibana/blob/6fc0d971dd00b83964093aec8ad013d77b2fd5e2/packages/core/http/core-http-server-internal/src/csp/csp_directives.ts#L33-L40 & https://github.com/elastic/kibana/blob/6fc0d971dd00b83964093aec8ad013d77b2fd5e2/packages/core/http/core-http-server-internal/src/csp/csp_directives.ts#L42-L53)

This logic would be applied for both CSP and Permissions Policy directives when custom values are applied (either via this new API or any other means of external configuration, e.g. setting http.securityHeaders.permissionsPolicy).

Kibana also should log the security header value whenever a non-default value is applied to a directive. This will allow us to better assist customers and troubleshoot systems.

Describe a specific use case for the feature:

Within the Kibana security domain, we will not always be aware of specific trusted sources for content. This feature would allow plugins to augment a default set of security header directive with trusted sources specific to their solution or application. In issue #177777, the Security Solution plugin would use the new API to add the required source to our Permissions Policy's fullscreen directive. The result would be a fullscreen directive containing the default 'self' value along with the added value.

cc: @semd @pgayvallet

Before proceeding, we should create a 1-pager to align on the proposed implementation

elasticmachine commented 4 months ago

Pinging @elastic/kibana-security (Team:Security)

pgayvallet commented 4 months ago

I wonder if we should / want to do for this permission policy header the same thing with did regarding the CSP's header configuration in https://github.com/elastic/kibana/pull/102059 : splitting it into per-directive configuration.

Note that internally, we will have to do that separation / split anyway given we want to be perform per-directive addition from this new API anyway, so my question is only regarding the way we want to expose this configuration to consumers.

jeramysoucy commented 4 months ago

I wonder if we should / want to do for this permission policy header the same thing with did regarding the CSP's header configuration in https://github.com/elastic/kibana/pull/102059 : splitting it into per-directive configuration.

I opted not to include implementation details in the issue, but I pondered the same thing. I think this is reasonable, and keeps it simple. 👍

pgayvallet commented 4 months ago

Sounds good to me. Please tell if you need anything from the Core team to move forward on this.

azasypkin commented 4 months ago

Thanks for filing this issue @jeramysoucy, and thanks for the feedback @pgayvallet!

I'm wondering if instead of exposing programmatic APIs, we could provide a way to configure this declaratively and statically somehow (e.g., via a "permissions policies" section in the plugin manifest or something along these lines)? As far as I understand, these values are known upfront and not calculated at runtime.

This would give us two benefits:

What do you think?

legrego commented 4 months ago

Thanks for filing this issue @jeramysoucy, and thanks for the feedback @pgayvallet!

I'm wondering if instead of exposing programmatic APIs, we could provide a way to configure this declaratively and statically somehow (e.g., via a "permissions policies" section in the plugin manifest or something along these lines)? As far as I understand, these values are known upfront and not calculated at runtime.

This would give us two benefits:

  • It'd be much easier to audit who is doing what with our CSP.
  • It'd be much harder to abuse this subsystem by runtime exploits should the plugin become vulnerable to any of this.

What do you think?

I certainly appreciate the benefits of a statically defined configuration, but I also worry about the limitations. A static definition will never support user customization, so administrators will have no way to control the resulting CSP. Plugin authors may need to curate the CSP based on something more than just the enabled/disabled state of their plugin.

I worry that investing in a static configuration now will lead to a lack of extensibility down the road, causing us to eventually throw this out in favor of a more dynamic system. Perhaps there's a middle ground where we can design a plugin API that allows us to extend it in the future, without giving plugins too much power in the initial design?


A similar argument could be made for our feature registration system that powers our authorization model. Our original requirements could have worked with a static system, but we have significantly benefited from the ability to programmatically adapt this over time.

azasypkin commented 4 months ago

Thanks for sharing your thoughts, @legrego!

I certainly appreciate the benefits of a statically defined configuration, but I also worry about the limitations. A static definition will never support user customization, so administrators will have no way to control the resulting CSP.

That's a fair point that static registration is much more limiting compared to programmatic APIs, I agree. But the programmatic API on its own doesn't provide Kibana administrators with any customization controls either unless platform or consumer (plugin) build something else on top of this API (e.g., expose additional configuration options from consumer plugins).

Plugin authors may need to curate the CSP based on something more than just the enabled/disabled state of their plugin.

It's hard to tell if they need this in practice or not at this point, but if they do, do we want every consumer to model this curation experience differently, or do we want the platform to give consumer "tools" to do that so that Kibana admins have exactly the same curation experience when they configure CSP, irrespective of the solution/plugin/area that needs CSP extensions?

I assume we're not talking about decisions that are made at runtime since I don't think it's possible or desirable today: you should know what CSP you need before start, and before start, you can only use config, hard-coded values, or plugin manifest to make your decision - so it's always static, in one way or another. Or what do you mean?

I worry that investing in a static configuration now will lead to a lack of extensibility down the road, causing us to eventually throw this out in favor of a more dynamic system. Perhaps there's a middle ground where we can design a plugin API that allows us to extend it in the future, without giving plugins too much power in the initial design?

It's definitely a valid concern, I agree. Using plugin manifests for something like this might be too extreme in terms of upfront cost and rigidity at this point, even though I'd like us to use more static configuration for things like this. That being said, I'm not so concerned about giving plugins a lot of power. After all, if the need to tweak CSP or Permissions Policies, there is usually a valid business reason for that.

I'm more concerned about the lack of visibility when it's done via programmatic APIs. Maybe we can consider exposing core config extensions via config declaration somehow. I'll give it some thought.


A similar argument could be made for our feature registration system that powers our authorization model. Our original requirements could have worked with a static system, but we have significantly benefited from the ability to programmatically adapt this over time.

These two cases might appear similar, but I'm not sure it's a completely fair comparison: allowing developers to add new features and extend existing ones with as little friction as possible was (likely) a requirement for the feature registration system, as for any other public programmatic APIs that are supposed to be used frequently. But is it the same for deployment-level security controls such as CSP that aren't supposed to evolve and be modified at the same pace?

Moreover, this flexibility isn't free either: auditing what every feature registered in Kibana is incredibly hard today. I'm referring to all the diverse abstractions that plugin developers built around the feature registration API over time. I wish we were more prescriptive here.

legrego commented 4 months ago

That's a fair point that static registration is much more limiting compared to programmatic APIs, I agree. But the programmatic API on its own doesn't provide Kibana administrators with any customization controls either unless platform or consumer (plugin) build something else on top of this API (e.g., expose additional configuration options from consumer plugins).

Correct. My concern was that a static config would preclude this possibility altogether.

It's hard to tell if they need this in practice or not at this point, but if they do, do we want every consumer to model this curation experience differently, or do we want the platform to give consumer "tools" to do that so that Kibana admins have exactly the same curation experience when they configure CSP, irrespective of the solution/plugin/area that needs CSP extensions?

That's a good question. The capabilities that we protect via CSP and Permissions Policy are vast, and I could see us exposing this configuration in different ways.

Example 1: an administrator would likely want a checkbox to control whether or not Kibana can be embedded into other domains -- they probably don't want to control the CSP directives and X-Frame-Options directly.

Example 2: Configuring an external source for map layers. Would we want administrators to know to have to adjust connect-src somehow, or would it make sense for Kibana to automatically react to their configured map layers and adjust connect-src automatically?

I guess I'm leaning more towards domain-specific user experiences. Having said that, I do see benefit in an administrative UX which tells administrators what their effective CSP is, and why it's configured in that way. So I want to have my cake and eat it too 😬.

I assume we're not talking about decisions that are made at runtime since I don't think it's possible or desirable today: you should know what CSP you need before start, and before start, you can only use config, hard-coded values, or plugin manifest to make your decision - so it's always static, in one way or another. Or what do you mean?

Hmm. For stateful, I think making this determination during setup would be fine. I think serverless projects may need a more dynamic configuration, if end-users are ever to influence the CSP like in my examples above.

I'm more concerned about the lack of visibility when it's done via programmatic APIs. Maybe we can consider exposing core config extensions via config declaration somehow. I'll give it some thought.

++ we definitely need visibility and oversight into the final policies, and how plugins are influencing it.

These two cases might appear similar, but I'm not sure it's a completely fair comparison: allowing developers to add new features and extend existing ones with as little friction as possible was (likely) a requirement for the feature registration system, as for any other public programmatic APIs that are supposed to be used frequently. But is it the same for deployment-level security controls such as CSP that aren't supposed to evolve and be modified at the same pace?

It would be generous to say that we had such a requirement for feature registration, but I take your point. CSP (hopefully) won't need to evolve nearly as much -- but it will still evolve.

Moreover, this flexibility isn't free either: auditing what every feature registered in Kibana is incredibly hard today. I'm referring to all the diverse abstractions that plugin developers built around the feature registration API over time. I wish we were more prescriptive here.

That's a good point, and a fair criticism of the feature registration API

azasypkin commented 4 months ago

Example 1: an administrator would likely want a checkbox to control whether or not Kibana can be embedded into other domains -- they probably don't want to control the CSP directives and X-Frame-Options directly.

Agreed, we should abstract this away with something that makes more sense. We already do this somewhat with server.securityResponseHeaders.disableEmbedding.

Example 2: Configuring an external source for map layers. Would we want administrators to know to have to adjust connect-src somehow, or would it make sense for Kibana to automatically react to their configured map layers and adjust connect-src automatically? I guess I'm leaning more towards domain-specific user experiences. Having said that, I do see benefit in an administrative UX which tells administrators what their effective CSP is, and why it's configured in that way. So I want to have my cake and eat it too 😬.

The issue I see here is that it'd result in domain-specific experiences for deployment-level configuration. If we weren't a single-page application, I could see Maps Admin being able to affect security configuration just for the maps domain through domain-specific experiences, with any level of abstraction we want. That'd be ideal, but it's not how it works today. Today, changes in security configuration to support Maps will affect the entire deployment, and it feels like it should only be the Deployment Admin who can change it, and they aren't necessarily familiar with Maps UX. It doesn't mean domain-specific experiences aren't applicable here though, I can see Maps providing Deployment Admin with the configuration necessary for it to function properly in one way or another.

I agree, it's a tough one to do properly once and for all, but I'm glad we're brainstorming here.

Hmm. For stateful, I think making this determination during setup would be fine. I think serverless projects may need a more dynamic configuration, if end-users are ever to influence the CSP like in my examples above.

Assuming by end-users you mean project/deployment admins, I agree. I just don't see a good way to embed these decisions into specific domains before we build something for the deployment-level that these domain-specific experiences can complement.

++ we definitely need visibility and oversight into the final policies, and how plugins are influencing it.

Yeah, if we can solve this somehow, I'd feel much more comfortable giving more control to the consumers/domains.

That's a good point, and a fair criticism of the feature registration API

It wasn't to criticize the feature registration API - it does its job well considering all the constraints we had and still have 🙂 I just wanted to point out that there are a lot of lessons we can learn from it and apply to new functionality.

legrego commented 4 months ago

That'd be ideal, but it's not how it works today. Today, changes in security configuration to support Maps will affect the entire deployment, and it feels like it should only be the Deployment Admin who can change it, and they aren't necessarily familiar with Maps UX. It doesn't mean domain-specific experiences aren't applicable here though, I can see Maps providing Deployment Admin with the configuration necessary for it to function properly in one way or another.

++ I understand what you're saying now. That makes sense to me.

Assuming by end-users you mean project/deployment admins, I agree. I just don't see a good way to embed these decisions into specific domains before we build something for the deployment-level that these domain-specific experiences can complement.

I agree. My fear was the static nature of the deployment-level configuration would prevent us from building the domain-specific experiences due to its lack of flexibility at runtime. Ideally, we should eventually be able to apply these domain-specific experiences without requiring a restart of the deployment/project. There might be a way to do this with the static configuration, so I don't want to discourage us from exploring that option. I just want to make sure we consider these use cases when we're designing the solution.