forked from opensearch-project/security
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'upstream/main' into resource-access-con…
…trol
- Loading branch information
Showing
13 changed files
with
658 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
release-notes/opensearch-security.release-notes-2.17.0.0.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
268 changes: 268 additions & 0 deletions
268
src/main/java/org/opensearch/security/dlic/rest/api/AuthFailureListenersApiAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.