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

feat: New ksql.properties.overrides.denylist to deny clients configs overrides #5877

Merged
merged 3 commits into from
Aug 6, 2020

Conversation

spena
Copy link
Member

@spena spena commented Jul 24, 2020

Description

Added a new configuration ksql.properties.overrides.denylist to specify which server properties cannot be overridden by the KSQL clients/users.

There is currently an internal hard-coded list of properties that cannot be overriden. However, there are KSQL admins that also want other properties to be in the list. To make this dynamic, this new denylist property allow those admins to specify which properties should not be overridden, without making changes in the code.

The only disadvantage on this dynamic property over the hard-coded is the validation happens only until executing a DDL or query statements.

Testing done

  • Added unit tests
  • Verify the CLI manually
ksql> set 'ksql.streams.num.stream.threads'='4';
Successfully changed local property 'ksql.streams.num.stream.threads' from '4' to '4'.

ksql> show streams;
Cannot override property 'ksql.streams.num.stream.threads'

ksql> unset 'ksql.streams.num.stream.threads';
Successfully unset local property 'ksql.streams.num.stream.threads' (value was '4').

ksql> show streams;

 Stream Name         | Kafka Topic                 | Format 
------------------------------------------------------------
 KSQL_PROCESSING_LOG | default_ksql_processing_log | JSON   
------------------------------------------------------------

Reviewer checklist

  • Ensure docs are updated if necessary. (eg. if a user visible feature is being added or changed).
  • Ensure relevant issues are linked (description should include text like "Fixes #")

@@ -214,15 +219,21 @@ private EndpointResponse handleStatement(
statement.getStatement())
);

final Map<String, Object> requestProperties = request.getRequestProperties();
Copy link
Contributor

Choose a reason for hiding this comment

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

it seems weird to need to do this validation at each endpoint, what if we add another one in the future? is there a place in the engine we can do this validation in one place that would work for all endpoints?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's hard to find a good place for this. I initially wanted the validation to happen inside the getRequestProperties, but I need a KsqlConfig inside the request which is created by the Vert.x endpoint code. Will require some refactoring just to pass the KsqlConfig.

I looked doing it in the engine.execute(), but it's risky too if the request properties are obtained before calling the execute. See the below code as an example:

final KsqlEntityList entities = handler.execute(
          securityContext,
          statements,
          new SessionProperties(
              configProperties,
              localHost,
              localUrl,
              requestConfig.getBoolean(KsqlRequestConfig.KSQL_REQUEST_INTERNAL_REQUEST)
          )
      );

The requestConfig.getBoolean(KsqlRequestConfig.KSQL_REQUEST_INTERNAL_REQUEST) won't be validated if we do the properties validation inside the Engine. If we encapsulate it in the Engine, then developers won't know there is some validation done inside. Here at least, if someone looks at the endpoint to add another endpoint, they could see there is some validation to do in the properties.

Copy link
Contributor

Choose a reason for hiding this comment

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

The engine is littered with code that passes the KsqlConfig ksqlConfig and Map<String, ?> overrides pair around. I think the overrides is now mostly being correctly applied before the config is used... but... it's pretty ugly and unclear if the config you've got has overrides or not.

I've thought for a while that it would be much cleaner to have:

  • KsqlConfig which is always the server config.
  • SessionConfig which wraps a KsqlConfig and a map of overrides.

Headless mode just has a single session, where as each user request can build its own SessionConfig and pass this around, rather than the two params.

Ideally, the engine would take SessionConfig, not KsqlConfig. SessionConfig can have a very similar / same interface as KsqlConfig.

The SessionConfig constructor, which takes the server's KsqlConfig and the map of overrides can then validate the overrides, i.e. only one place for override validation.

The SessionConfig would allow access to the serverConfig, then overrides and then 'mergedConfig`. Different parts of the code need each of these, if memory serves me right.

Of course, this is a much bigger change. Simple, but wide ranging. However, I wonder if this PR could start us down this road (Assuming we agree it's a good idea). For example, this PR could add the SessionConfig class, and pass this around in the rest-server model, before converting to a KsqlConfig that's passed to the engine. If you want to also switch the engine to use SessionConfig, then awesome - but maybe in follow up PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

I addressed the rest of the feedback except this. I found there is an existing SessionProperties that is passed to the KsqlEngine to execute the request and we could use it. I will create a Builder for it that accepts the DenyListPropertyValidator on its constructor, and then build the SessionProperties on every request. The build will fail if the request contains overrides that are prohibited.

I will added in a another commit.

Copy link
Member Author

Choose a reason for hiding this comment

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

@big-andy-coates I'd like to do this change in a follow-up PR to close this deny list task. The SessionProperties is only supported in the KsqlResource, and I need changes in the StreamedQueryResource and WSQueryEndpoint and other internal methods, so better doing it as a different patch.

If you're good with this current PR, could you approve it?

Copy link
Contributor

@big-andy-coates big-andy-coates left a comment

Choose a reason for hiding this comment

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

I like this idea @spena. I think it'll be very useful.

Couple of suggestions below.

@@ -316,6 +316,12 @@
.put(KSQL_STREAMS_PREFIX + StreamsConfig.MAX_TASK_IDLE_MS_CONFIG, 500L)
.build();

public static final String KSQL_PROPERTIES_OVERRIDES_DENYLIST =
"ksql.properties.overrides.denylist";
public static final String KSQL_PROPERTIES_OVERRIDES_DENYLIST_DEFAULT = "";
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this can be inlined. It isn't referenced from else where.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@@ -214,15 +219,21 @@ private EndpointResponse handleStatement(
statement.getStatement())
);

final Map<String, Object> requestProperties = request.getRequestProperties();
Copy link
Contributor

Choose a reason for hiding this comment

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

The engine is littered with code that passes the KsqlConfig ksqlConfig and Map<String, ?> overrides pair around. I think the overrides is now mostly being correctly applied before the config is used... but... it's pretty ugly and unclear if the config you've got has overrides or not.

I've thought for a while that it would be much cleaner to have:

  • KsqlConfig which is always the server config.
  • SessionConfig which wraps a KsqlConfig and a map of overrides.

Headless mode just has a single session, where as each user request can build its own SessionConfig and pass this around, rather than the two params.

Ideally, the engine would take SessionConfig, not KsqlConfig. SessionConfig can have a very similar / same interface as KsqlConfig.

The SessionConfig constructor, which takes the server's KsqlConfig and the map of overrides can then validate the overrides, i.e. only one place for override validation.

The SessionConfig would allow access to the serverConfig, then overrides and then 'mergedConfig`. Different parts of the code need each of these, if memory serves me right.

Of course, this is a much bigger change. Simple, but wide ranging. However, I wonder if this PR could start us down this road (Assuming we agree it's a good idea). For example, this PR could add the SessionConfig class, and pass this around in the rest-server model, before converting to a KsqlConfig that's passed to the engine. If you want to also switch the engine to use SessionConfig, then awesome - but maybe in follow up PR.

@@ -228,7 +233,7 @@ private KsqlRequest parseRequest(final MultiMap requestParams) {
throw new IllegalArgumentException("\"ksql\" field of \"request\" must be populated");
}
// To validate props:
request.getConfigOverrides();
denyListPropertyValidator.validateAll(request.getConfigOverrides());
Copy link
Contributor

Choose a reason for hiding this comment

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

missing a unit test.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@@ -245,6 +250,7 @@ private KsqlRequest parseRequest(final MultiMap requestParams) {

private void handleQuery(final RequestContext info, final Query query) {
final Map<String, Object> clientLocalProperties = info.request.getConfigOverrides();
denyListPropertyValidator.validateAll(clientLocalProperties);
Copy link
Contributor

Choose a reason for hiding this comment

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

missing a unit test.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

- change error message for a more descriptive error
- inject DenyListPropertyValidator to Resource classes
- add unit tests for WSQueryEndpoint
- validate only streamProperties (no requestProperties)
- minor fixes
Copy link
Contributor

@big-andy-coates big-andy-coates left a comment

Choose a reason for hiding this comment

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

LGTM. Looks forward to the follow up PR!

@spena spena merged commit 7d1ad25 into confluentinc:master Aug 6, 2020
@spena spena deleted the blacklist_client_configs branch August 6, 2020 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants