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

Add config properties for HTTP security headers #97158

Merged
merged 15 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion config/kibana.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#server.publicBaseUrl: ""

# The maximum payload size in bytes for incoming server requests.
#server.maxPayloadBytes: 1048576
#server.maxPayload: 1048576

# The Kibana server's name. This is used for display purposes.
#server.name: "your-hostname"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CspConfig](./kibana-plugin-core-server.cspconfig.md) &gt; [disableEmbedding](./kibana-plugin-core-server.cspconfig.disableembedding.md)

## CspConfig.disableEmbedding property

<b>Signature:</b>

```typescript
readonly disableEmbedding: boolean;
```
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The constructor for this class is marked as internal. Third-party code should no
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [DEFAULT](./kibana-plugin-core-server.cspconfig.default.md) | <code>static</code> | <code>CspConfig</code> | |
| [disableEmbedding](./kibana-plugin-core-server.cspconfig.disableembedding.md) | | <code>boolean</code> | |
| [header](./kibana-plugin-core-server.cspconfig.header.md) | | <code>string</code> | |
| [rules](./kibana-plugin-core-server.cspconfig.rules.md) | | <code>string[]</code> | |
| [strict](./kibana-plugin-core-server.cspconfig.strict.md) | | <code>boolean</code> | |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [ICspConfig](./kibana-plugin-core-server.icspconfig.md) &gt; [disableEmbedding](./kibana-plugin-core-server.icspconfig.disableembedding.md)

## ICspConfig.disableEmbedding property

Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled \*and\* no custom rules have been defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.

<b>Signature:</b>

```typescript
readonly disableEmbedding: boolean;
```
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface ICspConfig

| Property | Type | Description |
| --- | --- | --- |
| [disableEmbedding](./kibana-plugin-core-server.icspconfig.disableembedding.md) | <code>boolean</code> | Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled \*and\* no custom rules have been defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. |
| [header](./kibana-plugin-core-server.icspconfig.header.md) | <code>string</code> | The CSP rules in a formatted directives string for use in a <code>Content-Security-Policy</code> header. |
| [rules](./kibana-plugin-core-server.icspconfig.rules.md) | <code>string[]</code> | The CSP rules used for Kibana. |
| [strict](./kibana-plugin-core-server.icspconfig.strict.md) | <code>boolean</code> | Specify whether browsers that do not support CSP should be able to use Kibana. Use <code>true</code> to block and <code>false</code> to allow. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Limits the size of incoming payloads to the specified byte count. Allowing very large payloads may cause the server to run out of memory.

Default value: The one set in the kibana.yml config file under the parameter `server.maxPayloadBytes`<!-- -->.
Default value: The one set in the kibana.yml config file under the parameter `server.maxPayload`<!-- -->.

<b>Signature:</b>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface RouteConfigOptionsBody
| Property | Type | Description |
| --- | --- | --- |
| [accepts](./kibana-plugin-core-server.routeconfigoptionsbody.accepts.md) | <code>RouteContentType &#124; RouteContentType[] &#124; string &#124; string[]</code> | A string or an array of strings with the allowed mime types for the endpoint. Use this settings to limit the set of allowed mime types. Note that allowing additional mime types not listed above will not enable them to be parsed, and if parse is true, the request will result in an error response.<!-- -->Default value: allows parsing of the following mime types: \* application/json \* application/\*+json \* application/octet-stream \* application/x-www-form-urlencoded \* multipart/form-data \* text/\* |
| [maxBytes](./kibana-plugin-core-server.routeconfigoptionsbody.maxbytes.md) | <code>number</code> | Limits the size of incoming payloads to the specified byte count. Allowing very large payloads may cause the server to run out of memory.<!-- -->Default value: The one set in the kibana.yml config file under the parameter <code>server.maxPayloadBytes</code>. |
| [maxBytes](./kibana-plugin-core-server.routeconfigoptionsbody.maxbytes.md) | <code>number</code> | Limits the size of incoming payloads to the specified byte count. Allowing very large payloads may cause the server to run out of memory.<!-- -->Default value: The one set in the kibana.yml config file under the parameter <code>server.maxPayload</code>. |
| [output](./kibana-plugin-core-server.routeconfigoptionsbody.output.md) | <code>typeof validBodyOutput[number]</code> | The processed payload format. The value must be one of: \* 'data' - the incoming payload is read fully into memory. If parse is true, the payload is parsed (JSON, form-decoded, multipart) based on the 'Content-Type' header. If parse is false, a raw Buffer is returned. \* 'stream' - the incoming payload is made available via a Stream.Readable interface. If the payload is 'multipart/form-data' and parse is true, field values are presented as text while files are provided as streams. File streams from a 'multipart/form-data' upload will also have a hapi property containing the filename and headers properties. Note that payload streams for multipart payloads are a synthetic interface created on top of the entire multipart content loaded into memory. To avoid loading large multipart payloads into memory, set parse to false and handle the multipart payload in the handler using a streaming parser (e.g. pez).<!-- -->Default value: 'data', unless no validation.body is provided in the route definition. In that case the default is 'stream' to alleviate memory pressure. |
| [parse](./kibana-plugin-core-server.routeconfigoptionsbody.parse.md) | <code>boolean &#124; 'gunzip'</code> | Determines if the incoming payload is processed or presented raw. Available values: \* true - if the request 'Content-Type' matches the allowed mime types set by allow (for the whole payload as well as parts), the payload is converted into an object when possible. If the format is unknown, a Bad Request (400) error response is sent. Any known content encoding is decoded. \* false - the raw payload is returned unmodified. \* 'gunzip' - the raw payload is returned unmodified after any known content encoding is decoded.<!-- -->Default value: true, unless no validation.body is provided in the route definition. In that case the default is false to alleviate memory pressure. |

2 changes: 1 addition & 1 deletion docs/maps/trouble-shooting.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ image::maps/images/inspector.png[]
** Ensure your geospatial field is searchable and aggregatable.
** If your geospatial field type does not match your Elasticsearch mapping, click the *Refresh* button to refresh the field list from Elasticsearch.
* Index patterns with thousands of fields can exceed the default maximum payload size.
Increase <<settings, `server.maxPayloadBytes`>> for large index patterns.
Increase <<settings, `server.maxPayload`>> for large index patterns.

[float]
==== Features are not displayed
Expand Down
59 changes: 56 additions & 3 deletions docs/setup/settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ which may cause a delay before pages start being served.
Set to `false` to disable Console. *Default: `true`*

| `cpu.cgroup.path.override:`
| deprecated:[7.10.0,"This setting will no longer be supported as of 8.0."]
| deprecated:[7.10.0,"In 8.0 and later, this setting will no longer be supported."]
This setting has been renamed to
<<ops-cGroupOverrides-cpuPath,`ops.cGroupOverrides.cpuPath`>>.

| `cpuacct.cgroup.path.override:`
| deprecated:[7.10.0,"This setting will no longer be supported as of 8.0."]
| deprecated:[7.10.0,"In 8.0 and later, this setting will no longer be supported."]
This setting has been renamed to
<<ops-cGroupOverrides-cpuAcctPath, `ops.cGroupOverrides.cpuAcctPath`>>.

Expand Down Expand Up @@ -473,7 +473,7 @@ confident your server can hold this many objects in memory.
| The maximum byte size of a saved objects import that the {kib} server will accept.
This setting exists to prevent the {kib} server from runnning out of memory when handling
a large import payload. Note that this setting overrides the more general
<<server-maxPayloadBytes, `server.maxPayloadBytes`>> for saved object imports only.
<<server-maxPayload, `server.maxPayload`>> for saved object imports only.
*Default: `26214400`*

|[[server-basePath]] `server.basePath:`
Expand Down Expand Up @@ -504,6 +504,55 @@ deprecation warning at startup. This setting cannot end in a slash (`/`).
proxy sitting in front of it. This determines whether HTTP compression may be used for responses, based on the request `Referer` header.
This setting may not be used when <<server-compression, `server.compression.enabled`>> is set to `false`. *Default: `none`*


a| [[server-securityResponseHeaders-strictTransportSecurity]]
----
server.securityResponseHeaders:
strictTransportSecurity:
----
| Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security[`Strict-Transport-Security`]
header is used in all responses to the client from the {kib} server, and specifies what value is used. Allowed values are any text value or
`null`. To disable, set to `null`. *Default:* `max-age=31536000` (1 year)

a| [[server-securityResponseHeaders-xContentTypeOptions]]
----
server.securityResponseHeaders:
xContentTypeOptions:
----
| Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options[`X-Content-Type-Options`] header is
used in all responses to the client from the {kib} server, and specifies what value is used. Allowed values are `nosniff` or `null`. To
disable, set to `null`. *Default:* `"nosniff"`

a| [[server-securityResponseHeaders-referrerPolicy]]
----
server.securityResponseHeaders:
referrerPolicy:
----
| Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy[`Referrer-Policy`] header is used in all
responses to the client from the {kib} server, and specifies what value is used. Allowed values are `no-referrer`,
`no-referrer-when-downgrade`, `origin`, `origin-when-cross-origin`, `same-origin`, `strict-origin`, `strict-origin-when-cross-origin`,
`unsafe-url`, or `null`. To disable, set to `null`. *Default:* `"no-referrer-when-downgrade"`

a| [[server-securityResponseHeaders-permissionsPolicy]]
----
server.securityResponseHeaders:
permissionsPolicy:
----
| Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy[`Permissions-Policy`] header is used in all
responses to the client from the {kib} server, and specifies what value is used. Allowed values are any text value or `null`. To disable,
set to `null`. *Default:* `"camera=(), microphone=()"` (disables the camera and microphone APIs in the browser)

a| [[server-securityResponseHeaders-disableEmbedding]]
----
server.securityResponseHeaders:
disableEmbedding:
----
| Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy[`Content-Security-Policy`] and
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options[`X-Frame-Options`] headers are configured to disable embedding
{kib} in other webpages using iframes. When set to `true`, secure headers are used to disable embedding, which adds the `frame-ancestors:
'self'` directive to the `Content-Security-Policy` response header (if you are using the default CSP rules), and adds the `X-Frame-Options:
SAMEORIGIN` response header. *Default:* `false`

| `server.customResponseHeaders:` {ess-icon}
| Header names and values to
send on all responses to the client from the {kib} server. *Default: `{}`*
Expand All @@ -517,6 +566,10 @@ back end server. To allow remote users to connect, set the value to the IP addre
the <<server-socketTimeout, `server.socketTimeout`>> counter. *Default: `"120000"`*

|[[server-maxPayloadBytes]] `server.maxPayloadBytes:`
| deprecated:[7.13.0,"In 8.0 and later, this setting will no longer be supported."]
This setting has been renamed to <<server-maxPayload,`server.maxPayload`>>.

|[[server-maxPayload]] `server.maxPayload:`
| The maximum payload size in bytes
for incoming server requests. *Default: `1048576`*

Expand Down
2 changes: 2 additions & 0 deletions src/core/server/csp/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ export const config = {
warnLegacyBrowsers: schema.boolean({ defaultValue: true }),
}),
};

export const FRAME_ANCESTORS_RULE = `frame-ancestors 'self'`; // only used by CspConfig when embedding is disabled
86 changes: 45 additions & 41 deletions src/core/server/csp/csp_config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* Side Public License, v 1.
*/

import { CspConfig } from '.';
import { CspConfig } from './csp_config';
import { FRAME_ANCESTORS_RULE } from './config';

// CSP rules aren't strictly additive, so any change can potentially expand or
// restrict the policy in a way we consider a breaking change. For that reason,
Expand All @@ -25,6 +26,7 @@ describe('CspConfig', () => {
test('DEFAULT', () => {
expect(CspConfig.DEFAULT).toMatchInlineSnapshot(`
CspConfig {
"disableEmbedding": false,
"header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
"rules": Array [
"script-src 'unsafe-eval' 'self'",
Expand All @@ -38,49 +40,51 @@ describe('CspConfig', () => {
});

test('defaults from config', () => {
expect(new CspConfig()).toMatchInlineSnapshot(`
CspConfig {
"header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
"rules": Array [
"script-src 'unsafe-eval' 'self'",
"worker-src blob: 'self'",
"style-src 'unsafe-inline' 'self'",
],
"strict": true,
"warnLegacyBrowsers": true,
}
`);
expect(new CspConfig()).toEqual(CspConfig.DEFAULT);
});

test('creates from partial config', () => {
expect(new CspConfig({ strict: false, warnLegacyBrowsers: false })).toMatchInlineSnapshot(`
CspConfig {
"header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
"rules": Array [
"script-src 'unsafe-eval' 'self'",
"worker-src blob: 'self'",
"style-src 'unsafe-inline' 'self'",
],
"strict": false,
"warnLegacyBrowsers": false,
}
`);
});
describe('partial config', () => {
test('allows "rules" to be set and changes header', () => {
const rules = ['foo', 'bar'];
const config = new CspConfig({ rules });
expect(config.rules).toEqual(rules);
expect(config.header).toMatchInlineSnapshot(`"foo; bar"`);
});

test('computes header from rules', () => {
const cspConfig = new CspConfig({ rules: ['alpha', 'beta', 'gamma'] });
test('allows "strict" to be set', () => {
const config = new CspConfig({ strict: false });
expect(config.strict).toEqual(false);
expect(config.strict).not.toEqual(CspConfig.DEFAULT.strict);
});

expect(cspConfig).toMatchInlineSnapshot(`
CspConfig {
"header": "alpha; beta; gamma",
"rules": Array [
"alpha",
"beta",
"gamma",
],
"strict": true,
"warnLegacyBrowsers": true,
}
`);
test('allows "warnLegacyBrowsers" to be set', () => {
const warnLegacyBrowsers = false;
const config = new CspConfig({ warnLegacyBrowsers });
expect(config.warnLegacyBrowsers).toEqual(warnLegacyBrowsers);
expect(config.warnLegacyBrowsers).not.toEqual(CspConfig.DEFAULT.warnLegacyBrowsers);
});

describe('allows "disableEmbedding" to be set', () => {
const disableEmbedding = true;

test('and changes rules/header if custom rules are not defined', () => {
const config = new CspConfig({ disableEmbedding });
expect(config.disableEmbedding).toEqual(disableEmbedding);
expect(config.disableEmbedding).not.toEqual(CspConfig.DEFAULT.disableEmbedding);
expect(config.rules).toEqual(expect.arrayContaining([FRAME_ANCESTORS_RULE]));
expect(config.header).toMatchInlineSnapshot(
`"script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'; frame-ancestors 'self'"`
);
});

test('and does not change rules/header if custom rules are defined', () => {
const rules = ['foo', 'bar'];
const config = new CspConfig({ disableEmbedding, rules });
expect(config.disableEmbedding).toEqual(disableEmbedding);
expect(config.disableEmbedding).not.toEqual(CspConfig.DEFAULT.disableEmbedding);
expect(config.rules).toEqual(rules);
expect(config.header).toMatchInlineSnapshot(`"foo; bar"`);
});
});
});
});
22 changes: 18 additions & 4 deletions src/core/server/csp/csp_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
* Side Public License, v 1.
*/

import { config } from './config';
import { config, FRAME_ANCESTORS_RULE } from './config';

const DEFAULT_CONFIG = Object.freeze(config.schema.validate({}));
const DEFAULT_CONFIG = Object.freeze({
...config.schema.validate({}),
disableEmbedding: false,
});
jportner marked this conversation as resolved.
Show resolved Hide resolved

/**
* CSP configuration for use in Kibana.
Expand All @@ -32,6 +35,12 @@ export interface ICspConfig {
*/
readonly warnLegacyBrowsers: boolean;

/**
* Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled *and* no custom rules have been
* defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.
*/
readonly disableEmbedding: boolean;

/**
* The CSP rules in a formatted directives string for use
* in a `Content-Security-Policy` header.
Expand All @@ -49,6 +58,7 @@ export class CspConfig implements ICspConfig {
public readonly rules: string[];
public readonly strict: boolean;
public readonly warnLegacyBrowsers: boolean;
public readonly disableEmbedding: boolean;
public readonly header: string;

/**
Expand All @@ -58,9 +68,13 @@ export class CspConfig implements ICspConfig {
constructor(rawCspConfig: Partial<Omit<ICspConfig, 'header'>> = {}) {
const source = { ...DEFAULT_CONFIG, ...rawCspConfig };

this.rules = source.rules;
this.rules = [...source.rules];
this.strict = source.strict;
this.warnLegacyBrowsers = source.warnLegacyBrowsers;
this.header = source.rules.join('; ');
this.disableEmbedding = source.disableEmbedding;
if (!rawCspConfig.rules?.length && source.disableEmbedding) {
this.rules.push(FRAME_ANCESTORS_RULE);
}
Comment on lines +72 to +74
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to log a warning if disableEmbedding is enabled but custom CSP rules are used that do not include the frame-ancestors directive. But I think we can leave that for a future enhancement, as we don't have the ability to log when config is ingested at the moment.

this.header = this.rules.join('; ');
}
}
7 changes: 7 additions & 0 deletions src/core/server/http/__snapshots__/http_config.test.ts.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading