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

[http] Add log for route path access #152621

Merged

Conversation

TinaHeiligers
Copy link
Contributor

@TinaHeiligers TinaHeiligers commented Mar 2, 2023

part of 151940
follow up to 152404

As part of implementing API versioning, we need an easy way to identify which HTTP APIs are intended for internal vs public consumption and added an access route option param laying the foundation for future work.

The access setting is configurable and falls back to a naïve interpretation of the path string.

Given that we now have some internal/public classification, we can use logs to generate a list of all HTTP APIs registered with core's HTTP server.

This PR adds a debug logger (http.server.kibana.options) that will log the route path and the access value on startup.

Usage

Configure logging as follows:

// kibana.yml
logging:
  appenders:
    http-console:
      type: console
      layout:
        type: pattern
        highlight: true
        pattern: "HTTP--[%date][%level][%logger]---%message"
  root:
    appenders: [default, console]
    level: info
  loggers:
    - name: http.server
      level: debug
      appenders: [http-console]
    - name: http.server.kibana.options
      level: debug
      appenders: [http-console]

On server startup, you should see the logs similar to:

HTTP--[2023-03-02T13:45:44.600-07:00][DEBUG][http.server.Kibana.options]---kibanaRouteOptions [public] for path [/api/apm/fleet/apm_server_schema]
HTTP--[2023-03-02T13:45:44.601-07:00][DEBUG][http.server.Kibana]---registering route handler for [/internal/apm/fleet/apm_server_schema/unsupported]
HTTP--[2023-03-02T13:45:44.601-07:00][DEBUG][http.server.Kibana.options]---kibanaRouteOptions [internal] for path [/internal/apm/fleet/apm_server_schema/unsupported]

@TinaHeiligers TinaHeiligers added Feature:http release_note:skip Skip the PR/issue when compiling release notes backport:skip This commit does not require backporting Epic:VersionedAPIs Kibana Versioned APIs Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v8.8.0 labels Mar 2, 2023
@TinaHeiligers TinaHeiligers self-assigned this Mar 2, 2023
Copy link
Contributor Author

@TinaHeiligers TinaHeiligers left a comment

Choose a reason for hiding this comment

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

Comments to reviewers.

@@ -526,6 +527,10 @@ export class HttpServer {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
access: route.options.access ?? (route.path.startsWith('/internal') ? 'internal' : 'public'),
};
// Log HTTP API target consumer. Warning: may log sensitive information on paths including secrets
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a warning to remind ourselves (and API owners) where debug logs could cause issues.

Copy link
Member

Choose a reason for hiding this comment

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

IIUC, something like POST /api/my-secret/is/:mySecret would log ... for path [/api/my-secret/is/:mySecret], and not the actual value in the secret when the user performs the request. Am I missing anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're not missing anything, the warning is to remind folks that anything in the path will log as is and to think very carefully about what they are fine with exposing is.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry... I'm a bit slow today... 😞

AFAIK, this comment is only read by Kibana Core devs. I don't get how this comment would warn a plugin developer to design their API so that they don't register an HTTP route like POST /api/my/plugin/${config.secret}/:id. Bear in mind we are logging the registered path, not the final path that is requested with the variables replaced by the values (:id in the example)

Copy link
Contributor

Choose a reason for hiding this comment

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

@afharo I believe what you are describing has already been introduced with

this.log.debug(`registering route handler for [${route.path}]`);

Copy link
Contributor

@jloleysens jloleysens Mar 6, 2023

Choose a reason for hiding this comment

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

Not a reason skip reviewing the decision, just saying it's not being added with this PR :)

Copy link
Member

Choose a reason for hiding this comment

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

I don't want to block this PR 😇. These are nits rather than anything. Also, I noticed @TinaHeiligers removed the comment.

@afharo I believe what you are describing has already been introduced with

this.log.debug(`registering route handler for [${route.path}]`);

IIUC, I'm describing both lines. This PR adds a new entry in the logs that rephrases and adds info to the already existing entry. The new log entry is also logging the registered path, not the accessed path.

@@ -516,6 +516,7 @@ export class HttpServer {
}

private configureRoute(route: RouterRoute) {
const optionsLogger = this.logger.get('http', 'server', this.name, 'options');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I chose to add a new logger to make it easier to filter for these specific log lines

Copy link
Member

Choose a reason for hiding this comment

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

If we want to avoid repeating the http and server part, I think we can define the logger asthis.log.get(this.name, 'options);.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We'd lose the full context around where the log comes from if we remove http and server, which makes it very hard to analyze the debug-level logs.
Personally, I'd prefer being able to filter logs by a full logger name that keeps some sort of record of where it's coming from.

Copy link
Member

Choose a reason for hiding this comment

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

We'd lose the full context around where the log comes from if we remove http and server, which makes it very hard to analyze the debug-level logs.

Mind that I'm using this.log (instead of this.logger). AFAIK, this.log is already scoped to http.server, calling this.log.get calls a nested logger. If we ever change the parent, it all changes together (as opposed to using the root logger this.logger). I hope it makes sense.

Personally, I'd prefer being able to filter logs by a full logger name that keeps some sort of record of where it's coming from.

++ I agree.

Copy link
Contributor Author

@TinaHeiligers TinaHeiligers Mar 6, 2023

Choose a reason for hiding this comment

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

using this.log (instead of this.logger)

I did a quick test with this.log.get(this.name, 'options') with the same http-console` appender. We end up duplicating the server name:

HTTP--[2023-03-06T10:05:25.632-07:00][DEBUG][http.server.Kibana.Kibana.options]---access [internal] for path [/internal/apm/service-map/service/{serviceName}]

Copy link
Member

@afharo afharo Mar 6, 2023

Choose a reason for hiding this comment

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

oh! cool! I didn't notice the existing entry is logged as http.server.Kibana. In that case, this.log.get('options') is enough :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done: commit fb86412 (#152621)

@TinaHeiligers
Copy link
Contributor Author

@elasticmachine merge upstream

@TinaHeiligers TinaHeiligers marked this pull request as ready for review March 2, 2023 23:48
@TinaHeiligers TinaHeiligers requested a review from a team as a code owner March 2, 2023 23:48
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-core (Team:Core)

@TinaHeiligers
Copy link
Contributor Author

@elasticmachine merge upstream

@@ -526,6 +527,10 @@ export class HttpServer {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
access: route.options.access ?? (route.path.startsWith('/internal') ? 'internal' : 'public'),
};
// Log HTTP API target consumer. Warning: may log sensitive information on paths including secrets
optionsLogger.debug(
`kibanaRouteOptions [${kibanaRouteOptions.access}] for path [${route.path}]`
Copy link
Contributor

Choose a reason for hiding this comment

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

Shall we log the method too? IMO "access" is a clearer prefix than "kibanaRouteOptions":

Suggested change
`kibanaRouteOptions [${kibanaRouteOptions.access}] for path [${route.path}]`
`access [${kibanaRouteOptions.access}] for path [${route.method} ${route.path}]`

Copy link
Contributor Author

@TinaHeiligers TinaHeiligers Mar 6, 2023

Choose a reason for hiding this comment

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

Approved!
I've also removed the code comment's warning.
Please TAL.

Copy link
Member

@afharo afharo left a comment

Choose a reason for hiding this comment

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

Having this is awesome! Thanks for making it happen, Tina!

I added a few comments that I'd like to clarify before LGTM :)

@@ -516,6 +516,7 @@ export class HttpServer {
}

private configureRoute(route: RouterRoute) {
const optionsLogger = this.logger.get('http', 'server', this.name, 'options');
this.log.debug(`registering route handler for [${route.path}]`);
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if extending this log entry with the values suggested in https://github.com/elastic/kibana/pull/152621/files/bf7a3c36cb4394b06a3b3fa09a0553c3f958bfed#r1126120651 would suffice. Both entries look too similar and are logged at the same time. If the only reason is so that we can use a new logger, I wonder if we can use it here (as long as it's nested, it should still log with the same config)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that but went a new logger route to be able to silence the extra info or to later remove the logger completely without losing the original log

@@ -516,6 +516,7 @@ export class HttpServer {
}

private configureRoute(route: RouterRoute) {
const optionsLogger = this.logger.get('http', 'server', this.name, 'options');
Copy link
Member

Choose a reason for hiding this comment

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

If we want to avoid repeating the http and server part, I think we can define the logger asthis.log.get(this.name, 'options);.

@@ -526,6 +527,10 @@ export class HttpServer {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
access: route.options.access ?? (route.path.startsWith('/internal') ? 'internal' : 'public'),
};
// Log HTTP API target consumer. Warning: may log sensitive information on paths including secrets
Copy link
Member

Choose a reason for hiding this comment

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

IIUC, something like POST /api/my-secret/is/:mySecret would log ... for path [/api/my-secret/is/:mySecret], and not the actual value in the secret when the user performs the request. Am I missing anything?

@@ -526,6 +527,8 @@ export class HttpServer {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
access: route.options.access ?? (route.path.startsWith('/internal') ? 'internal' : 'public'),
};
// Log HTTP API target consumer.
optionsLogger.debug(`access [${kibanaRouteOptions.access}] for path [${route.path}]`);
Copy link
Member

Choose a reason for hiding this comment

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

mind that @jloleysens's comment also suggested adding the method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Apologies, adding that now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done: commit fb86412

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Unknown metric groups

ESLint disabled line counts

id before after diff
securitySolution 428 430 +2

Total ESLint disabled count

id before after diff
securitySolution 506 508 +2

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @TinaHeiligers

@TinaHeiligers TinaHeiligers merged commit cf1fbc7 into elastic:main Mar 6, 2023
bmorelli25 pushed a commit to bmorelli25/kibana that referenced this pull request Mar 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Epic:VersionedAPIs Kibana Versioned APIs Feature:http release_note:skip Skip the PR/issue when compiling release notes Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v8.8.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants