Skip to content

Commit

Permalink
[8.x] [Core] log on external requests to internal routes (#195696) (#…
Browse files Browse the repository at this point in the history
…195974)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Core] log on external requests to internal routes
(#195696)](#195696)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Christiane (Tina)
Heiligers","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-11T18:02:31Z","message":"[Core]
log on external requests to internal routes (#195696)\n\nfix
[#194772](https://github.com/elastic/kibana/issues/194772)\r\n\r\nKibana
logs a warning when detecting requests from integrations
with\r\ninternal APIs when the restriction is not enforced or explicitly
set to\r\n`false`.\r\nConsumers can use these logs to audit their
integrations and make\r\nchanges before the restriction becomes
enforced.\r\n\r\n### Note ###\r\nAfter 9.0, the restriction will be
enforced. Explicitly disabling the\r\nrestriction effectively opts into
using internal routes, which are\r\nsubject to change and not
recommended for non-Elastic consumption.\r\n\r\nBypassing the
restriction for specific routes by adding the necessary\r\nheader or
query parameter that allows access is also not recommended
for\r\nnon-Elastic consumption.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n## How to
test this ##\r\n\r\n#### Test as an external consumer making a request
to an internal route\r\n####\r\n\r\n<details>\r\n<summary>1. Enforce
restricted internal APIs & enable
logging:</summary>\r\n\r\n```//kibana.yml\r\nserver.restrictInternalApis:
true // or don't declare\r\n...\r\nlogging\r\n appenders:\r\n
http-custom:\r\n type: console\r\n layout:\r\n type: pattern\r\n
highlight: true\r\n pattern:
\"HTTP_CUSTOM--[%date][%level][%logger]---%message\"\r\n root:\r\n
appenders: [console]\r\n level: warn\r\n loggers\r\n - name:
http.server.kbn-internal-api-restricted\r\n level: warn\r\n appenders:
[http-custom]\r\n```\r\n</details>\r\n\r\n2. Start ES (any
license)\r\n3. Start Kbn\r\n\r\n<details>\r\n<summary>4. GET global
settings:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n5. The response
from the curl request shoud
be:\r\n```\r\n{\"statusCode\":400,\"error\":\"Bad
Request\",\"message\":\"uri [/internal/kibana/global_settings] with
method [get] exists but is not available with the current
configuration\"}\r\n```\r\n6. You should see an error log from
the\r\n`http.server.kbnInternalApiRestricted`
logger:\r\n```\r\nHTTP_CUSTOM--[2024-10-10T13:04:51.287-07:00][ERROR][http.server.kbn-internal-api-restricted]---Access
to uri [/internal/kibana/global_settings] with method [get] is not
available with the current configuration\r\n```\r\n\r\n#### Bypass the
restriction for the global settings route (opt in to use\r\nthe internal
route)\r\nKeeping the same configuration and with ES and Kbn still
running, add\r\nthe 'x-elastic-internal-origin' header to the curl
request ####\r\n\r\n<details>\r\n<summary>1. Opt in to use the global
settings route:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'x-elastic-internal-origin: kibana'
\\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n2. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n3.
You shouldn't see any new logs from
the\r\n`http.server.kbnInternalApiRestricted` logger\r\n\r\n#### Test as
an external consumer explicitly disabling the restriction\r\ncompletely
(not recommended and not guaranteed)
####\r\n\r\n<details>\r\n<summary>1. Disable restricted internal APIs &
enable
logging:</summary>\r\n\r\n```//kibana.yml\r\nserver.restrictInternalApis:
false\r\n...\r\nlogging\r\n appenders:\r\n http-custom:\r\n type:
console\r\n layout:\r\n type: pattern\r\n highlight: true\r\n pattern:
\"HTTP_CUSTOM--[%date][%level][%logger]---%message\"\r\n root:\r\n
appenders: [console]\r\n level: warn\r\n loggers\r\n - name:
http.server.kbn-internal-api-restricted\r\n level: warn\r\n appenders:
[http-custom]\r\n```\r\n</details>\r\n\r\n2. Start ES (any
license)\r\n3. Start Kbn\r\n\r\n<details>\r\n<summary>4. GET global
settings:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n5. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n6.
You should see a warning log from
the\r\n`http.server.kbnInternalApiRestricted`
logger:\r\n```\r\nHTTP_CUSTOM--[2024-10-10T13:31:48.729-07:00][WARN
][http.server.kbn-internal-api-restricted]---Access to uri
[/internal/kibana/global_settings] with method [get] is
deprecated\r\n```\r\n<details>\r\n<summary>7. Add the internal origin
header to the request:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'x-elastic-internal-origin: kibana'
\\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n8. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n9.
You shouldn't see any new logs from
the\r\n`http.server.kbnInternalApiRestricted`
logger.\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"d0bdbdddb200a2656567c9b9f05d1e934c5a4cea","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","release_note:skip","v9.0.0","backport:prev-minor"],"title":"[Core]
log on external requests to internal
routes","number":195696,"url":"https://github.com/elastic/kibana/pull/195696","mergeCommit":{"message":"[Core]
log on external requests to internal routes (#195696)\n\nfix
[#194772](https://github.com/elastic/kibana/issues/194772)\r\n\r\nKibana
logs a warning when detecting requests from integrations
with\r\ninternal APIs when the restriction is not enforced or explicitly
set to\r\n`false`.\r\nConsumers can use these logs to audit their
integrations and make\r\nchanges before the restriction becomes
enforced.\r\n\r\n### Note ###\r\nAfter 9.0, the restriction will be
enforced. Explicitly disabling the\r\nrestriction effectively opts into
using internal routes, which are\r\nsubject to change and not
recommended for non-Elastic consumption.\r\n\r\nBypassing the
restriction for specific routes by adding the necessary\r\nheader or
query parameter that allows access is also not recommended
for\r\nnon-Elastic consumption.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n## How to
test this ##\r\n\r\n#### Test as an external consumer making a request
to an internal route\r\n####\r\n\r\n<details>\r\n<summary>1. Enforce
restricted internal APIs & enable
logging:</summary>\r\n\r\n```//kibana.yml\r\nserver.restrictInternalApis:
true // or don't declare\r\n...\r\nlogging\r\n appenders:\r\n
http-custom:\r\n type: console\r\n layout:\r\n type: pattern\r\n
highlight: true\r\n pattern:
\"HTTP_CUSTOM--[%date][%level][%logger]---%message\"\r\n root:\r\n
appenders: [console]\r\n level: warn\r\n loggers\r\n - name:
http.server.kbn-internal-api-restricted\r\n level: warn\r\n appenders:
[http-custom]\r\n```\r\n</details>\r\n\r\n2. Start ES (any
license)\r\n3. Start Kbn\r\n\r\n<details>\r\n<summary>4. GET global
settings:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n5. The response
from the curl request shoud
be:\r\n```\r\n{\"statusCode\":400,\"error\":\"Bad
Request\",\"message\":\"uri [/internal/kibana/global_settings] with
method [get] exists but is not available with the current
configuration\"}\r\n```\r\n6. You should see an error log from
the\r\n`http.server.kbnInternalApiRestricted`
logger:\r\n```\r\nHTTP_CUSTOM--[2024-10-10T13:04:51.287-07:00][ERROR][http.server.kbn-internal-api-restricted]---Access
to uri [/internal/kibana/global_settings] with method [get] is not
available with the current configuration\r\n```\r\n\r\n#### Bypass the
restriction for the global settings route (opt in to use\r\nthe internal
route)\r\nKeeping the same configuration and with ES and Kbn still
running, add\r\nthe 'x-elastic-internal-origin' header to the curl
request ####\r\n\r\n<details>\r\n<summary>1. Opt in to use the global
settings route:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'x-elastic-internal-origin: kibana'
\\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n2. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n3.
You shouldn't see any new logs from
the\r\n`http.server.kbnInternalApiRestricted` logger\r\n\r\n#### Test as
an external consumer explicitly disabling the restriction\r\ncompletely
(not recommended and not guaranteed)
####\r\n\r\n<details>\r\n<summary>1. Disable restricted internal APIs &
enable
logging:</summary>\r\n\r\n```//kibana.yml\r\nserver.restrictInternalApis:
false\r\n...\r\nlogging\r\n appenders:\r\n http-custom:\r\n type:
console\r\n layout:\r\n type: pattern\r\n highlight: true\r\n pattern:
\"HTTP_CUSTOM--[%date][%level][%logger]---%message\"\r\n root:\r\n
appenders: [console]\r\n level: warn\r\n loggers\r\n - name:
http.server.kbn-internal-api-restricted\r\n level: warn\r\n appenders:
[http-custom]\r\n```\r\n</details>\r\n\r\n2. Start ES (any
license)\r\n3. Start Kbn\r\n\r\n<details>\r\n<summary>4. GET global
settings:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n5. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n6.
You should see a warning log from
the\r\n`http.server.kbnInternalApiRestricted`
logger:\r\n```\r\nHTTP_CUSTOM--[2024-10-10T13:31:48.729-07:00][WARN
][http.server.kbn-internal-api-restricted]---Access to uri
[/internal/kibana/global_settings] with method [get] is
deprecated\r\n```\r\n<details>\r\n<summary>7. Add the internal origin
header to the request:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'x-elastic-internal-origin: kibana'
\\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n8. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n9.
You shouldn't see any new logs from
the\r\n`http.server.kbnInternalApiRestricted`
logger.\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"d0bdbdddb200a2656567c9b9f05d1e934c5a4cea"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195696","number":195696,"mergeCommit":{"message":"[Core]
log on external requests to internal routes (#195696)\n\nfix
[#194772](https://github.com/elastic/kibana/issues/194772)\r\n\r\nKibana
logs a warning when detecting requests from integrations
with\r\ninternal APIs when the restriction is not enforced or explicitly
set to\r\n`false`.\r\nConsumers can use these logs to audit their
integrations and make\r\nchanges before the restriction becomes
enforced.\r\n\r\n### Note ###\r\nAfter 9.0, the restriction will be
enforced. Explicitly disabling the\r\nrestriction effectively opts into
using internal routes, which are\r\nsubject to change and not
recommended for non-Elastic consumption.\r\n\r\nBypassing the
restriction for specific routes by adding the necessary\r\nheader or
query parameter that allows access is also not recommended
for\r\nnon-Elastic consumption.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n## How to
test this ##\r\n\r\n#### Test as an external consumer making a request
to an internal route\r\n####\r\n\r\n<details>\r\n<summary>1. Enforce
restricted internal APIs & enable
logging:</summary>\r\n\r\n```//kibana.yml\r\nserver.restrictInternalApis:
true // or don't declare\r\n...\r\nlogging\r\n appenders:\r\n
http-custom:\r\n type: console\r\n layout:\r\n type: pattern\r\n
highlight: true\r\n pattern:
\"HTTP_CUSTOM--[%date][%level][%logger]---%message\"\r\n root:\r\n
appenders: [console]\r\n level: warn\r\n loggers\r\n - name:
http.server.kbn-internal-api-restricted\r\n level: warn\r\n appenders:
[http-custom]\r\n```\r\n</details>\r\n\r\n2. Start ES (any
license)\r\n3. Start Kbn\r\n\r\n<details>\r\n<summary>4. GET global
settings:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n5. The response
from the curl request shoud
be:\r\n```\r\n{\"statusCode\":400,\"error\":\"Bad
Request\",\"message\":\"uri [/internal/kibana/global_settings] with
method [get] exists but is not available with the current
configuration\"}\r\n```\r\n6. You should see an error log from
the\r\n`http.server.kbnInternalApiRestricted`
logger:\r\n```\r\nHTTP_CUSTOM--[2024-10-10T13:04:51.287-07:00][ERROR][http.server.kbn-internal-api-restricted]---Access
to uri [/internal/kibana/global_settings] with method [get] is not
available with the current configuration\r\n```\r\n\r\n#### Bypass the
restriction for the global settings route (opt in to use\r\nthe internal
route)\r\nKeeping the same configuration and with ES and Kbn still
running, add\r\nthe 'x-elastic-internal-origin' header to the curl
request ####\r\n\r\n<details>\r\n<summary>1. Opt in to use the global
settings route:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'x-elastic-internal-origin: kibana'
\\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n2. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n3.
You shouldn't see any new logs from
the\r\n`http.server.kbnInternalApiRestricted` logger\r\n\r\n#### Test as
an external consumer explicitly disabling the restriction\r\ncompletely
(not recommended and not guaranteed)
####\r\n\r\n<details>\r\n<summary>1. Disable restricted internal APIs &
enable
logging:</summary>\r\n\r\n```//kibana.yml\r\nserver.restrictInternalApis:
false\r\n...\r\nlogging\r\n appenders:\r\n http-custom:\r\n type:
console\r\n layout:\r\n type: pattern\r\n highlight: true\r\n pattern:
\"HTTP_CUSTOM--[%date][%level][%logger]---%message\"\r\n root:\r\n
appenders: [console]\r\n level: warn\r\n loggers\r\n - name:
http.server.kbn-internal-api-restricted\r\n level: warn\r\n appenders:
[http-custom]\r\n```\r\n</details>\r\n\r\n2. Start ES (any
license)\r\n3. Start Kbn\r\n\r\n<details>\r\n<summary>4. GET global
settings:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n5. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n6.
You should see a warning log from
the\r\n`http.server.kbnInternalApiRestricted`
logger:\r\n```\r\nHTTP_CUSTOM--[2024-10-10T13:31:48.729-07:00][WARN
][http.server.kbn-internal-api-restricted]---Access to uri
[/internal/kibana/global_settings] with method [get] is
deprecated\r\n```\r\n<details>\r\n<summary>7. Add the internal origin
header to the request:</summary>\r\n\r\n```\r\ncurl --location
'localhost:5601/abc/internal/kibana/global_settings' \\\r\n--header
'Content-Type: application/json' \\\r\n--header 'Accept-Encoding: gzip,
deflate, br' \\\r\n--header 'kbn-xsrf: kibana' \\\r\n--header
'Kbn-Version: 9.0.0' \\\r\n--header 'x-elastic-internal-origin: kibana'
\\\r\n--header 'Authorization: Basic
ZWxhc3RpYzpjaGFuZ2VtZQ=='\r\n```\r\n</details>\r\n\r\n8. The response
from the curl request shoud
be:\r\n```\r\n{\"settings\":{\"buildNum\":{\"userValue\":9007199254740991},\"isDefaultIndexMigrated\":{\"userValue\":true}}}\r\n```\r\n9.
You shouldn't see any new logs from
the\r\n`http.server.kbnInternalApiRestricted`
logger.\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"d0bdbdddb200a2656567c9b9f05d1e934c5a4cea"}}]}]
BACKPORT-->

Co-authored-by: Christiane (Tina) Heiligers <[email protected]>
  • Loading branch information
kibanamachine and TinaHeiligers authored Oct 11, 2024
1 parent 7d25b26 commit bf56b4e
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
} from '@kbn/core-http-server';
import { mockRouter } from '@kbn/core-http-router-server-mocks';
import {
INTERNAL_API_RESTRICTED_LOGGER_NAME,
createBuildNrMismatchLoggerPreResponseHandler,
createCustomHeadersPreResponseHandler,
createRestrictInternalRoutesPostAuthHandler,
Expand Down Expand Up @@ -267,11 +268,19 @@ describe('versionCheck post-auth handler', () => {
describe('restrictInternal post-auth handler', () => {
let toolkit: ToolkitMock;
let responseFactory: ReturnType<typeof mockRouter.createResponseFactory>;
let logger: jest.Mocked<Logger>;
let config: HttpConfig;

beforeEach(() => {
toolkit = createToolkit();
responseFactory = mockRouter.createResponseFactory();
logger = loggerMock.create();
config = createConfig({
name: 'my-server-name',
restrictInternalApis: true,
});
});

const createForgeRequest = (
access: 'internal' | 'public',
headers: Record<string, string> | undefined = {},
Expand All @@ -298,92 +307,115 @@ describe('restrictInternal post-auth handler', () => {
expect(result).toBe('next');
};

describe('when restriction is enabled', () => {
const config = createConfig({
name: 'my-server-name',
restrictInternalApis: true,
});
it('injects a logger prefix', () => {
createRestrictInternalRoutesPostAuthHandler(config, logger);
expect(logger.get).toHaveBeenCalledTimes(1);
expect(logger.get).toHaveBeenCalledWith(`server`, INTERNAL_API_RESTRICTED_LOGGER_NAME);
});

it('returns a bad request if called without internal origin header for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('internal');
it('when enabled, does not log deprecation warning for internal API access restriction', () => {
createRestrictInternalRoutesPostAuthHandler(config, logger);
expect(logger.warn).not.toHaveBeenCalled();
});

responseFactory.badRequest.mockReturnValue('badRequest' as any);
it('when enabled, returns a bad request if called without internal origin header for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config, logger);
const request = createForgeRequest('internal');

const result = handler(request, responseFactory, toolkit);
responseFactory.badRequest.mockReturnValue('badRequest' as any);

expect(toolkit.next).not.toHaveBeenCalled();
expect(responseFactory.badRequest.mock.calls[0][0]?.body).toMatchInlineSnapshot(
`"uri [/internal/some-path] with method [get] exists but is not available with the current configuration"`
);
expect(result).toBe('badRequest');
});
const result = handler(request, responseFactory, toolkit);

it('forward the request to the next interceptor if called with internal origin header for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('internal', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});
expect(toolkit.next).not.toHaveBeenCalled();
expect(responseFactory.badRequest.mock.calls[0][0]?.body).toMatchInlineSnapshot(
`"uri [/internal/some-path] with method [get] exists but is not available with the current configuration"`
);
expect(result).toBe('badRequest');
});

it('forward the request to the next interceptor if called with internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('public', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});
it('when enabled, forward the request to the next interceptor if called with internal origin header for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config, logger);
const request = createForgeRequest('internal', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});

it('forward the request to the next interceptor if called without internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('public');
createForwardSuccess(handler, request);
});
it('when enabled, forward the request to the next interceptor if called with internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config, logger);
const request = createForgeRequest('public', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});

it('forward the request to the next interceptor if called with internal origin query param for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('internal', undefined, { elasticInternalOrigin: 'true' });
createForwardSuccess(handler, request);
});
it('when enabled, forward the request to the next interceptor if called without internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config, logger);
const request = createForgeRequest('public');
createForwardSuccess(handler, request);
});

it('forward the request to the next interceptor if called with internal origin query param for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('internal', undefined, { elasticInternalOrigin: 'true' });
createForwardSuccess(handler, request);
});
it('when enabled, forward the request to the next interceptor if called with internal origin query param for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config, logger);
const request = createForgeRequest('internal', undefined, { elasticInternalOrigin: 'true' });
createForwardSuccess(handler, request);
});

it('forward the request to the next interceptor if called without internal origin query param for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('public');
createForwardSuccess(handler, request);
});
it('when enabled, forward the request to the next interceptor if called with internal origin query param for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config, logger);
const request = createForgeRequest('internal', undefined, { elasticInternalOrigin: 'true' });
createForwardSuccess(handler, request);
});

describe('when restriction is not enabled', () => {
const config = createConfig({
name: 'my-server-name',
restrictInternalApis: false,
});
it('forward the request to the next interceptor if called without internal origin header for internal APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('internal');
createForwardSuccess(handler, request);
});
it('when enabled, forward the request to the next interceptor if called without internal origin query param for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config, logger);
const request = createForgeRequest('public');
createForwardSuccess(handler, request);
});

it('forward the request to the next interceptor if called with internal origin header for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('internal', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});
it('when not enabled, logs deprecation warning for internal API access restriction', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(
{ ...config, restrictInternalApis: false },
logger
);
const request = createForgeRequest('internal');
createForwardSuccess(handler, request);
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenCalledWith(
`Access to uri [/internal/some-path] with method [get] is deprecated`
);
});

it('forward the request to the next interceptor if called without internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('public');
createForwardSuccess(handler, request);
});
it('when not enabled, forward the request to the next interceptor if called without internal origin header for internal APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(
{ ...config, restrictInternalApis: false },
logger
);
const request = createForgeRequest('internal');
createForwardSuccess(handler, request);
});

it('forward the request to the next interceptor if called with internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(config as HttpConfig);
const request = createForgeRequest('public', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});
it('when not enabled, forward the request to the next interceptor if called with internal origin header for internal API', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(
{ ...config, restrictInternalApis: false },
logger
);
const request = createForgeRequest('internal', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});

it('when not enabled, forward the request to the next interceptor if called without internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(
{ ...config, restrictInternalApis: false },
logger
);
const request = createForgeRequest('public');
createForwardSuccess(handler, request);
});

it('when not enabled, forward the request to the next interceptor if called with internal origin header for public APIs', () => {
const handler = createRestrictInternalRoutesPostAuthHandler(
{ ...config, restrictInternalApis: false },
logger
);
const request = createForgeRequest('public', { 'x-elastic-internal-origin': 'Kibana' });
createForwardSuccess(handler, request);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,35 @@ export const createXsrfPostAuthHandler = (config: HttpConfig): OnPostAuthHandler
};
};

/**
* This should remain part of the logger prefix so that we can notify/track
* when we see this logged for observability purposes.
*/
export const INTERNAL_API_RESTRICTED_LOGGER_NAME = 'kbn-internal-api-restricted';
export const createRestrictInternalRoutesPostAuthHandler = (
config: HttpConfig
config: HttpConfig,
log: Logger
): OnPostAuthHandler => {
const isRestrictionEnabled = config.restrictInternalApis;
log = log.get('server', `${INTERNAL_API_RESTRICTED_LOGGER_NAME}`);

return (request, response, toolkit) => {
const isInternalRoute = request.route.options.access === 'internal';
if (isRestrictionEnabled && isInternalRoute && !request.isInternalApiRequest) {
// throw 400
return response.badRequest({
body: `uri [${request.url.pathname}] with method [${request.route.method}] exists but is not available with the current configuration`,
});
if (isInternalRoute && !request.isInternalApiRequest) {
if (!isRestrictionEnabled) {
// warn if the restriction is not enforced
log.warn(
`Access to uri [${request.url.pathname}] with method [${request.route.method}] is deprecated`
);
} else {
log.error(
`Access to uri [${request.url.pathname}] with method [${request.route.method}] is not available with the current configuration`
);
// throw 400
return response.badRequest({
body: `uri [${request.url.pathname}] with method [${request.route.method}] exists but is not available with the current configuration`,
});
}
}
return toolkit.next();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ export const registerCoreHandlers = (
);
}
// add check on header if the route is internal
registrar.registerOnPostAuth(createRestrictInternalRoutesPostAuthHandler(config)); // strictly speaking, we should have access to route.options.access from the request on postAuth
registrar.registerOnPostAuth(createRestrictInternalRoutesPostAuthHandler(config, log)); // strictly speaking, we should have access to route.options.access from the request on postAuth
};
15 changes: 7 additions & 8 deletions src/core/server/integration_tests/http/lifecycle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1019,11 +1019,9 @@ describe('Auth', () => {
const response = await supertest(innerServer.listener).get('/').expect(200);

expect(response.header['www-authenticate']).toBe('from auth interceptor');
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn[1]).toMatchInlineSnapshot(`
Array [
Array [
"onPreResponseHandler rewrote a response header [www-authenticate].",
],
"onPreResponseHandler rewrote a response header [www-authenticate].",
]
`);
});
Expand Down Expand Up @@ -1054,6 +1052,9 @@ describe('Auth', () => {
expect(response.header['www-authenticate']).toBe('from auth interceptor');
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Access to uri [/] with method [get] is deprecated",
],
Array [
"onPreResponseHandler rewrote a response header [www-authenticate].",
],
Expand Down Expand Up @@ -1288,11 +1289,9 @@ describe('OnPreResponse', () => {

await supertest(innerServer.listener).get('/').expect(200);

expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
expect(loggingSystemMock.collect(logger).warn[1]).toMatchInlineSnapshot(`
Array [
Array [
"onPreResponseHandler rewrote a response header [x-kibana-header].",
],
"onPreResponseHandler rewrote a response header [x-kibana-header].",
]
`);
});
Expand Down
Loading

0 comments on commit bf56b4e

Please sign in to comment.