Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into resource-access-con…
Browse files Browse the repository at this point in the history
…trol
  • Loading branch information
DarshitChanpura committed Sep 11, 2024
2 parents 01d55a4 + 014c8de commit 4c913de
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 15 deletions.
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,8 @@ configurations {
force "org.apache.httpcomponents:httpclient:4.5.14"
force "org.apache.httpcomponents:httpcore:4.4.16"
force "com.google.errorprone:error_prone_annotations:2.31.0"
force "org.checkerframework:checker-qual:3.46.0"
force "ch.qos.logback:logback-classic:1.5.7"
force "org.checkerframework:checker-qual:3.47.0"
force "ch.qos.logback:logback-classic:1.5.8"
}
}

Expand Down Expand Up @@ -646,7 +646,7 @@ dependencies {
runtimeOnly 'org.apache.ws.xmlschema:xmlschema-core:2.3.1'
runtimeOnly 'org.apache.santuario:xmlsec:2.3.4'
runtimeOnly "com.github.luben:zstd-jni:${versions.zstd}"
runtimeOnly 'org.checkerframework:checker-qual:3.46.0'
runtimeOnly 'org.checkerframework:checker-qual:3.47.0'
runtimeOnly "org.bouncycastle:bcpkix-jdk18on:${versions.bouncycastle}"
runtimeOnly 'org.scala-lang.modules:scala-java8-compat_3:1.0.2'

Expand Down
9 changes: 7 additions & 2 deletions config/roles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ alerting_ack_alerts:
- 'cluster:admin/opendistro/alerting/alerts/*'
- 'cluster:admin/opendistro/alerting/chained_alerts/*'
- 'cluster:admin/opendistro/alerting/workflow_alerts/*'
- 'cluster:admin/opensearch/alerting/comments/search'
- 'cluster:admin/opensearch/alerting/comments/*'

# Allows users to use all alerting functionality
alerting_full_access:
Expand Down Expand Up @@ -86,7 +86,12 @@ anomaly_full_access:
- '*'
allowed_actions:
- 'indices:admin/aliases/get'
- 'indices:admin/mappings/fields/get'
- 'indices:admin/mappings/fields/get*'
- 'indices:admin/mappings/get'
- 'indices:admin/resolve/index'
- 'indices:data/read/field_caps*'
- 'indices:data/read/search'
- 'indices_monitor'

# Allow users to execute read only k-NN actions
Expand Down Expand Up @@ -411,7 +416,7 @@ security_analytics_ack_alerts:
reserved: true
cluster_permissions:
- 'cluster:admin/opensearch/securityanalytics/alerts/*'
- 'cluster:admin/opensearch/securityanalytics/correlationAlerts/ack'
- 'cluster:admin/opensearch/securityanalytics/correlationAlerts/*'
- 'cluster:admin/opensearch/securityanalytics/threatintel/alerts/*'

# Allows users to use all Flow Framework functionality
Expand Down
39 changes: 39 additions & 0 deletions release-notes/opensearch-security.release-notes-2.17.0.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Version 2.17.0 Release Notes

Compatible with OpenSearch and OpenSearch Dashboards version 2.17.0

### Enhancements
* Add `ignore_hosts` config option for auth failure listener ([#4538](https://github.com/opensearch-project/security/pull/4538))
* added API roles for correlationAlerts ([#4689](https://github.com/opensearch-project/security/pull/4689))
* Allow multiple signing keys to be provided ([#4666](https://github.com/opensearch-project/security/pull/4666))
* adding alerting comments security actions to roles.yml ([#4700](https://github.com/opensearch-project/security/pull/4700))
* Permission changes for correlationAlerts ([#4704](https://github.com/opensearch-project/security/pull/4704))

### Bug Fixes
* Addresses a bug with `plugins.security.allow_unsafe_democertificates` setting ([#4603](https://github.com/opensearch-project/security/pull/4603))
* Fix covereage-report workflow ([#4684](https://github.com/opensearch-project/security/pull/4684), [#4683](https://github.com/opensearch-project/security/pull/4683))
* Handle the audit config being null ([#4664](https://github.com/opensearch-project/security/pull/4664))
* Fixes authtoken endpoint ([#4631](https://github.com/opensearch-project/security/pull/4631))
* Fixed READ_ACTIONS required by TermsAggregationEvaluator ([#4607](https://github.com/opensearch-project/security/pull/4607))
* Sort the DNS Names in the SANs ([#4640](https://github.com/opensearch-project/security/pull/4640))

### Maintenance
* Bump com.google.errorprone:error_prone_annotations from 2.30.0 to 2.31.0 ([#4696](https://github.com/opensearch-project/security/pull/4696))
* Bump org.passay:passay from 1.6.4 to 1.6.5 ([#4682](https://github.com/opensearch-project/security/pull/4682))
* Bump spring_version from 5.3.37 to 5.3.39 ([#4661](https://github.com/opensearch-project/security/pull/4661))
* Bump commons-cli:commons-cli from 1.8.0 to 1.9.0 ([#4659](https://github.com/opensearch-project/security/pull/4659))
* Bump org.junit.jupiter:junit-jupiter from 5.10.3 to 5.11.0 ([#4657](https://github.com/opensearch-project/security/pull/4657))
* Bump org.cryptacular:cryptacular from 1.2.6 to 1.2.7 ([#4656](https://github.com/opensearch-project/security/pull/4656))
* Update Gradle to 8.10 ([#4646](https://github.com/opensearch-project/security/pull/4646))
* Bump org.xerial.snappy:snappy-java from 1.1.10.5 to 1.1.10.6 ([#4639](https://github.com/opensearch-project/security/pull/4639))
* Bump com.google.googlejavaformat:google-java-format from 1.22.0 to 1.23.0 ([#4622](https://github.com/opensearch-project/security/pull/4622))
* Increment version to 2.17.0-SNAPSHOT ([#4615](https://github.com/opensearch-project/security/pull/4615))
* Backports PRs with `backport-failed` labels that weren't actually backported ([#4610](https://github.com/opensearch-project/security/pull/4610))
* Bump io.dropwizard.metrics:metrics-core from 4.2.26 to 4.2.27 ([#4660](https://github.com/opensearch-project/security/pull/4660))
* Bump com.netflix.nebula.ospackage from 11.9.1 to 11.10.0 ([#4681](https://github.com/opensearch-project/security/pull/4681))
* Interim build fix for PluginSubject related changes ([#4694](https://github.com/opensearch-project/security/pull/4694))
* Add Nils Bandener (Github: nibix) as a maintainer ([#4673](https://github.com/opensearch-project/security/pull/4673))
* Remove usages of org.apache.logging.log4j.util.Strings ([#4653](https://github.com/opensearch-project/security/pull/4653))
* Update backport section of PR template ([#4625](https://github.com/opensearch-project/security/pull/4625))
* Bump org.checkerframework:checker-qual from 3.45.0 to 3.46.0 ([#4623](https://github.com/opensearch-project/security/pull/4623))
* Refactor security provider instantiation ([#4611](https://github.com/opensearch-project/security/pull/4611))
Original file line number Diff line number Diff line change
Expand Up @@ -2142,7 +2142,7 @@ public PluginSubject getPluginSubject(Plugin plugin) {

@Override
public Optional<SecureSettingsFactory> getSecureSettingFactory(Settings settings) {
return Optional.of(new OpenSearchSecureSettingsFactory(threadPool, sks, sslExceptionHandler, securityRestHandler));
return Optional.of(new OpenSearchSecureSettingsFactory(threadPool, sks, evaluateSslExceptionHandler(), securityRestHandler));
}

@SuppressWarnings("removal")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

package org.opensearch.security.dlic.rest.api;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.fasterxml.jackson.databind.node.ObjectNode;

import org.opensearch.action.index.IndexResponse;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.common.settings.Settings;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.security.DefaultObjectMapper;
import org.opensearch.security.dlic.rest.validation.EndpointValidator;
import org.opensearch.security.dlic.rest.validation.RequestContentValidator;
import org.opensearch.security.dlic.rest.validation.RequestContentValidator.DataType;
import org.opensearch.security.dlic.rest.validation.ValidationResult;
import org.opensearch.security.securityconf.impl.CType;
import org.opensearch.security.securityconf.impl.v7.ConfigV7;
import org.opensearch.security.support.SecurityJsonNode;
import org.opensearch.threadpool.ThreadPool;

import static org.opensearch.rest.RestRequest.Method.DELETE;
import static org.opensearch.rest.RestRequest.Method.GET;
import static org.opensearch.rest.RestRequest.Method.PUT;
import static org.opensearch.security.dlic.rest.api.Responses.badRequest;
import static org.opensearch.security.dlic.rest.api.Responses.badRequestMessage;
import static org.opensearch.security.dlic.rest.api.Responses.notFound;
import static org.opensearch.security.dlic.rest.api.Responses.ok;
import static org.opensearch.security.dlic.rest.api.Responses.response;
import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix;
import static org.opensearch.security.securityconf.impl.v7.ConfigV7.ALLOWED_TRIES_DEFAULT;
import static org.opensearch.security.securityconf.impl.v7.ConfigV7.BLOCK_EXPIRY_SECONDS_DEFAULT;
import static org.opensearch.security.securityconf.impl.v7.ConfigV7.MAX_BLOCKED_CLIENTS_DEFAULT;
import static org.opensearch.security.securityconf.impl.v7.ConfigV7.MAX_TRACKED_CLIENTS_DEFAULT;
import static org.opensearch.security.securityconf.impl.v7.ConfigV7.TIME_WINDOW_SECONDS_DEFAULT;

public class AuthFailureListenersApiAction extends AbstractApiAction {

public static final String IP_TYPE = "ip";

public static final String USERNAME_TYPE = "username";

public static final String NAME_JSON_PROPERTY = "name";

public static final String TYPE_JSON_PROPERTY = "type";
public static final String IGNORE_HOSTS_JSON_PROPERTY = "ignore_hosts";
public static final String AUTHENTICATION_BACKEND_JSON_PROPERTY = "authentication_backend";
public static final String ALLOWED_TRIES_JSON_PROPERTY = "allowed_tries";
public static final String TIME_WINDOW_SECONDS_JSON_PROPERTY = "time_window_seconds";
public static final String BLOCK_EXPIRY_JSON_PROPERTY = "block_expiry_seconds";
public static final String MAX_BLOCKED_CLIENTS_JSON_PROPERTY = "max_blocked_clients";
public static final String MAX_TRACKED_CLIENTS_JSON_PROPERTY = "max_tracked_clients";

private static final List<Route> ROUTES = addRoutesPrefix(
ImmutableList.of(
new Route(GET, "/authfailurelisteners"),
new Route(DELETE, "/authfailurelisteners/{name}"),
new Route(PUT, "/authfailurelisteners/{name}")
)
);

protected AuthFailureListenersApiAction(
ClusterService clusterService,
ThreadPool threadPool,
SecurityApiDependencies securityApiDependencies
) {
super(Endpoint.AUTHFAILURELISTENERS, clusterService, threadPool, securityApiDependencies);
this.requestHandlersBuilder.configureRequestHandlers(this::authFailureConfigApiRequestHandlers);
}

@Override
public String getName() {
return "Auth failure listener actions to Retrieve / Update configs.";
}

@Override
public List<Route> routes() {
return ROUTES;
}

@Override
protected CType getConfigType() {
return CType.CONFIG;
}

@Override
protected EndpointValidator createEndpointValidator() {
return new EndpointValidator() {

@Override
public Endpoint endpoint() {
return endpoint;
}

@Override
public RestApiAdminPrivilegesEvaluator restApiAdminPrivilegesEvaluator() {
return securityApiDependencies.restApiAdminPrivilegesEvaluator();
}

@Override
public RequestContentValidator createRequestContentValidator(Object... params) {
return RequestContentValidator.of(new RequestContentValidator.ValidationContext() {
@Override
public Object[] params() {
return params;
}

@Override
public Settings settings() {
return securityApiDependencies.settings();
}

@Override
public Map<String, DataType> allowedKeys() {
final ImmutableMap.Builder<String, DataType> allowedKeys = ImmutableMap.builder();

return allowedKeys.put(TYPE_JSON_PROPERTY, DataType.STRING)
.put(IGNORE_HOSTS_JSON_PROPERTY, DataType.ARRAY)
.put(AUTHENTICATION_BACKEND_JSON_PROPERTY, DataType.STRING)
.put(ALLOWED_TRIES_JSON_PROPERTY, DataType.INTEGER)
.put(TIME_WINDOW_SECONDS_JSON_PROPERTY, DataType.INTEGER)
.put(BLOCK_EXPIRY_JSON_PROPERTY, DataType.INTEGER)
.put(MAX_BLOCKED_CLIENTS_JSON_PROPERTY, DataType.INTEGER)
.put(MAX_TRACKED_CLIENTS_JSON_PROPERTY, DataType.INTEGER)
.build();
}
});
}
};
}

private ToXContent authFailureContent(final ConfigV7 config) {
return (builder, params) -> {
builder.startObject();
for (String name : config.dynamic.auth_failure_listeners.getListeners().keySet()) {
ConfigV7.AuthFailureListener listener = config.dynamic.auth_failure_listeners.getListeners().get(name);
builder.startObject(name);
builder.field(NAME_JSON_PROPERTY, name)
.field(TYPE_JSON_PROPERTY, listener.type)
.field(IGNORE_HOSTS_JSON_PROPERTY, listener.ignore_hosts)
.field(AUTHENTICATION_BACKEND_JSON_PROPERTY, listener.authentication_backend)
.field(ALLOWED_TRIES_JSON_PROPERTY, listener.allowed_tries)
.field(TIME_WINDOW_SECONDS_JSON_PROPERTY, listener.time_window_seconds)
.field(BLOCK_EXPIRY_JSON_PROPERTY, listener.block_expiry_seconds)
.field(MAX_BLOCKED_CLIENTS_JSON_PROPERTY, listener.max_blocked_clients)
.field(MAX_TRACKED_CLIENTS_JSON_PROPERTY, listener.max_tracked_clients);
builder.endObject();
}
builder.endObject();
return builder;
};
}

private void authFailureConfigApiRequestHandlers(RequestHandler.RequestHandlersBuilder requestHandlersBuilder) {

requestHandlersBuilder.override(
GET,
(channel, request, client) -> loadConfiguration(getConfigType(), false, false).valid(configuration -> {
final var config = (ConfigV7) configuration.getCEntry(CType.CONFIG.toLCString());
ok(channel, authFailureContent(config));
}).error((status, toXContent) -> response(channel, status, toXContent))
).override(DELETE, (channel, request, client) -> loadConfiguration(getConfigType(), false, false).valid(configuration -> {
ConfigV7 config = (ConfigV7) configuration.getCEntry(CType.CONFIG.toLCString());

String listenerName = request.param(NAME_JSON_PROPERTY);

// Try to remove the listener by name
if (config.dynamic.auth_failure_listeners.getListeners().remove(listenerName) == null) {
notFound(channel, "listener not found");
}
saveOrUpdateConfiguration(client, configuration, new OnSucessActionListener<>(channel) {
@Override
public void onResponse(IndexResponse indexResponse) {
ok(channel, authFailureContent(config));
}
});
}).error((status, toXContent) -> response(channel, status, toXContent)))
.override(PUT, (channel, request, client) -> loadConfiguration(getConfigType(), false, false).valid(configuration -> {
ConfigV7 config = (ConfigV7) configuration.getCEntry(CType.CONFIG.toLCString());

String listenerName = request.param(NAME_JSON_PROPERTY);

ObjectNode body = (ObjectNode) DefaultObjectMapper.readTree(request.content().utf8ToString());
SecurityJsonNode authFailureListener = new SecurityJsonNode(body);
ValidationResult<SecurityJsonNode> validationResult = validateAuthFailureListener(authFailureListener, listenerName);

if (!validationResult.isValid()) {
badRequest(channel, validationResult.toString());
return;
}

// Try to put the listener by name
config.dynamic.auth_failure_listeners.getListeners()
.put(listenerName, createAuthFailureListenerWithDefaults(authFailureListener));
saveOrUpdateConfiguration(client, configuration, new OnSucessActionListener<>(channel) {
@Override
public void onResponse(IndexResponse indexResponse) {

ok(channel, authFailureContent(config));
}
});
}).error((status, toXContent) -> response(channel, status, toXContent)));

}

private ConfigV7.AuthFailureListener createAuthFailureListenerWithDefaults(SecurityJsonNode authFailureListener) {
List<String> ignoreHosts = authFailureListener.get(IGNORE_HOSTS_JSON_PROPERTY).isNull()
? Collections.emptyList()
: authFailureListener.get(IGNORE_HOSTS_JSON_PROPERTY).asList();

return new ConfigV7.AuthFailureListener(
authFailureListener.get(TYPE_JSON_PROPERTY).asString(),
authFailureListener.get(AUTHENTICATION_BACKEND_JSON_PROPERTY).asString(),
ignoreHosts,
authFailureListener.get(ALLOWED_TRIES_JSON_PROPERTY).asInt(ALLOWED_TRIES_DEFAULT),
authFailureListener.get(TIME_WINDOW_SECONDS_JSON_PROPERTY).asInt(TIME_WINDOW_SECONDS_DEFAULT),
authFailureListener.get(BLOCK_EXPIRY_JSON_PROPERTY).asInt(BLOCK_EXPIRY_SECONDS_DEFAULT),
authFailureListener.get(MAX_BLOCKED_CLIENTS_JSON_PROPERTY).asInt(MAX_BLOCKED_CLIENTS_DEFAULT),
authFailureListener.get(MAX_TRACKED_CLIENTS_JSON_PROPERTY).asInt(MAX_TRACKED_CLIENTS_DEFAULT)
);

}

private ValidationResult<SecurityJsonNode> validateAuthFailureListener(SecurityJsonNode authFailureListener, String name) {
if (name == null) {
return ValidationResult.error(RestStatus.BAD_REQUEST, badRequestMessage("name is required"));
}
if (authFailureListener.get(TYPE_JSON_PROPERTY).isNull()) {
return ValidationResult.error(RestStatus.BAD_REQUEST, badRequestMessage("type is required"));
}
if (!(Set.of(IP_TYPE, USERNAME_TYPE).contains(authFailureListener.get(TYPE_JSON_PROPERTY).asString()))) {
return ValidationResult.error(RestStatus.BAD_REQUEST, badRequestMessage("type must be username or ip"));
}
if (authFailureListener.get(TYPE_JSON_PROPERTY).asString().equals(USERNAME_TYPE)
&& (authFailureListener.get(AUTHENTICATION_BACKEND_JSON_PROPERTY).isNull()
|| !authFailureListener.get(AUTHENTICATION_BACKEND_JSON_PROPERTY).asString().equals("internal"))) {
return ValidationResult.error(
RestStatus.BAD_REQUEST,
badRequestMessage("username auth failure listeners must have 'internal' authentication backend")
);
}
if (authFailureListener.get(TYPE_JSON_PROPERTY).asString().equals(IP_TYPE)
&& !authFailureListener.get(AUTHENTICATION_BACKEND_JSON_PROPERTY).isNull()) {
return ValidationResult.error(
RestStatus.BAD_REQUEST,
badRequestMessage("ip auth failure listeners should not have an authentication backend")
);
}

return ValidationResult.success(authFailureListener);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public enum Endpoint {
PERMISSIONSINFO,
AUTHTOKEN,
TENANTS,
AUTHFAILURELISTENERS,
MIGRATE,
VALIDATE,
WHITELIST,
Expand Down
Loading

0 comments on commit 4c913de

Please sign in to comment.