Skip to content

Commit

Permalink
Proof of concept for extension reserved indices
Browse files Browse the repository at this point in the history
Signed-off-by: Craig Perkins <cwperx@amazon.com>
  • Loading branch information
cwperks committed Jul 14, 2023
1 parent 7e8740f commit 0e7f23e
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.opensearch.OpenSearchSecurityException;
import org.opensearch.common.Randomness;
import org.opensearch.identity.IdentityService;
import org.opensearch.identity.tokens.AuthToken;
Expand All @@ -29,6 +30,7 @@
import org.passay.EnglishCharacterData;
import org.passay.PasswordGenerator;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.opensearch.identity.noop.NoopTokenManager.NOOP_AUTH_TOKEN;

/**
* Extracts Shiro's {@link AuthenticationToken} from different types of auth headers
Expand Down Expand Up @@ -68,6 +70,11 @@ public AuthToken issueToken(String audience) {
return token;
}

@Override
public AuthToken issueServiceAccountToken(String extensionUniqueId) throws OpenSearchSecurityException {
return NOOP_AUTH_TOKEN;
}

public boolean validateToken(AuthToken token) {
if (token instanceof BasicAuthToken) {
final BasicAuthToken basicAuthToken = (BasicAuthToken) token;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.
*/

package org.opensearch.discovery;

import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.transport.TransportRequest;

import java.io.IOException;
import java.util.Objects;

/**
* InitializeExtensionRequest to initialize plugin
*
* @opensearch.internal
*/
public class InitializeExtensionSecurityRequest extends TransportRequest {

private final String serviceAccountToken;

public InitializeExtensionSecurityRequest(String serviceAccountToken) {
this.serviceAccountToken = serviceAccountToken;
}

public InitializeExtensionSecurityRequest(StreamInput in) throws IOException {
super(in);
serviceAccountToken = in.readString();
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(serviceAccountToken);
}

public String getServiceAccountToken() {
return serviceAccountToken;
}

@Override
public String toString() {
return "InitializeExtensionsRequest{" + "serviceAccountToken= " + serviceAccountToken + "}";
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InitializeExtensionSecurityRequest that = (InitializeExtensionSecurityRequest) o;
return Objects.equals(serviceAccountToken, that.serviceAccountToken);
}

@Override
public int hashCode() {
return Objects.hash(serviceAccountToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.
*/

/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

package org.opensearch.discovery;

import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.transport.TransportResponse;

import java.io.IOException;
import java.util.Objects;

/**
* PluginResponse to intialize plugin
*
* @opensearch.internal
*/
public class InitializeExtensionSecurityResponse extends TransportResponse {
private String name;

public InitializeExtensionSecurityResponse(String name) {
this.name = name;
}

public InitializeExtensionSecurityResponse(StreamInput in) throws IOException {
name = in.readString();
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
}

/**
* @return the node that is currently leading, according to the responding node.
*/

public String getName() {
return this.name;
}

@Override
public String toString() {
return "InitializeExtensionResponse{" + "name = " + name + "}";
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InitializeExtensionSecurityResponse that = (InitializeExtensionSecurityResponse) o;
return Objects.equals(name, that.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
Expand All @@ -38,6 +41,8 @@

import org.opensearch.discovery.InitializeExtensionRequest;
import org.opensearch.discovery.InitializeExtensionResponse;
import org.opensearch.discovery.InitializeExtensionSecurityRequest;
import org.opensearch.discovery.InitializeExtensionSecurityResponse;
import org.opensearch.extensions.ExtensionsSettings.Extension;
import org.opensearch.extensions.action.ExtensionActionRequest;
import org.opensearch.extensions.action.ExtensionActionResponse;
Expand All @@ -49,6 +54,8 @@
import org.opensearch.extensions.rest.RestActionsRequestHandler;
import org.opensearch.extensions.settings.CustomSettingsRequestHandler;
import org.opensearch.extensions.settings.RegisterCustomSettingsRequest;
import org.opensearch.identity.IdentityService;
import org.opensearch.identity.tokens.AuthToken;
import org.opensearch.threadpool.ThreadPool;
import org.opensearch.transport.ConnectTransportException;
import org.opensearch.transport.TransportException;
Expand All @@ -70,6 +77,7 @@ public class ExtensionsManager {
public static final String REQUEST_EXTENSION_ADD_SETTINGS_UPDATE_CONSUMER = "internal:discovery/addsettingsupdateconsumer";
public static final String REQUEST_EXTENSION_UPDATE_SETTINGS = "internal:discovery/updatesettings";
public static final String REQUEST_EXTENSION_DEPENDENCY_INFORMATION = "internal:discovery/dependencyinformation";
public static final String REQUEST_EXTENSION_REGISTER_SECURITY_SETTINGS = "internal:discovery/registersecuritysettings";
public static final String REQUEST_EXTENSION_REGISTER_CUSTOM_SETTINGS = "internal:discovery/registercustomsettings";
public static final String REQUEST_EXTENSION_REGISTER_REST_ACTIONS = "internal:discovery/registerrestactions";
public static final String REQUEST_EXTENSION_REGISTER_TRANSPORT_ACTIONS = "internal:discovery/registertransportactions";
Expand Down Expand Up @@ -97,6 +105,8 @@ public static enum OpenSearchRequestType {
private CustomSettingsRequestHandler customSettingsRequestHandler;
private TransportService transportService;
private ClusterService clusterService;

private IdentityService identityService;
private final Set<Setting<?>> additionalSettings;
private Settings environmentSettings;
private AddSettingsUpdateConsumerRequestHandler addSettingsUpdateConsumerRequestHandler;
Expand Down Expand Up @@ -397,10 +407,62 @@ protected void doRun() throws Exception {
new InitializeExtensionRequest(transportService.getLocalNode(), extension),
initializeExtensionResponseHandler
);
initializeExtensionSecurity(extension);
}
});
}

private void initializeExtensionSecurity(DiscoveryExtensionNode extension) {
final CompletableFuture<InitializeExtensionSecurityResponse> inProgressFuture = new CompletableFuture<>();
final TransportResponseHandler<InitializeExtensionSecurityResponse> initializeExtensionSecurityResponseHandler =
new TransportResponseHandler<InitializeExtensionSecurityResponse>() {

@Override
public InitializeExtensionSecurityResponse read(StreamInput in) throws IOException {
return new InitializeExtensionSecurityResponse(in);
}

@Override
public void handleResponse(InitializeExtensionSecurityResponse response) {
System.out.println("Registered security settings for " + response.getName());
inProgressFuture.complete(response);
}

@Override
public void handleException(TransportException exp) {
logger.error(new ParameterizedMessage("Extension initialization failed"), exp);
inProgressFuture.completeExceptionally(exp);
}

@Override
public String executor() {
return ThreadPool.Names.GENERIC;
}
};
try {
logger.info("Sending extension request type: " + REQUEST_EXTENSION_REGISTER_SECURITY_SETTINGS);
AuthToken serviceAccountToken = identityService.getTokenManager().issueServiceAccountToken(extension.getId());
transportService.sendRequest(
extension,
REQUEST_EXTENSION_REGISTER_SECURITY_SETTINGS,
new InitializeExtensionSecurityRequest(serviceAccountToken.getTokenValue()),
initializeExtensionSecurityResponseHandler
);

inProgressFuture.orTimeout(EXTENSION_REQUEST_WAIT_TIMEOUT, TimeUnit.SECONDS).join();
} catch (CompletionException | ConnectTransportException e) {
if (e.getCause() instanceof TimeoutException || e instanceof ConnectTransportException) {
logger.info("No response from extension to request.", e);
} else if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
} else if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
} else {
throw new RuntimeException(e.getCause());
}
}
}

/**
* Handles an {@link ExtensionRequest}.
*
Expand Down Expand Up @@ -485,6 +547,10 @@ void setClusterService(ClusterService clusterService) {
this.clusterService = clusterService;
}

public void setIdentityService(IdentityService identityService) {
this.identityService = identityService;
}

CustomSettingsRequestHandler getCustomSettingsRequestHandler() {
return customSettingsRequestHandler;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.OpenSearchSecurityException;
import org.opensearch.identity.IdentityService;
import org.opensearch.identity.tokens.AuthToken;
import org.opensearch.identity.tokens.TokenManager;
Expand All @@ -21,13 +22,24 @@ public class NoopTokenManager implements TokenManager {

private static final Logger log = LogManager.getLogger(IdentityService.class);

public static AuthToken NOOP_AUTH_TOKEN = new AuthToken() {
@Override
public String getTokenValue() {
return "";
}
};

/**
* Issue a new Noop Token
* @return a new Noop Token
*/
@Override
public AuthToken issueToken(String audience) {
return new AuthToken() {
};
return NOOP_AUTH_TOKEN;
}

@Override
public AuthToken issueServiceAccountToken(String extensionUniqueId) throws OpenSearchSecurityException {
return NOOP_AUTH_TOKEN;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
*/
public interface AuthToken {

String getTokenValue();
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,13 @@ public void revoke() {
this.password = "";
this.user = "";
}

@Override
public String getTokenValue() {
if (user == null || password == null) {
return "";
}
String usernamepassword = user + ":" + password;
return Base64.getEncoder().encodeToString(usernamepassword.getBytes(StandardCharsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,9 @@ public String getTokenIdentifier() {
public String toString() {
return "Bearer auth token with header=" + header + ", payload=" + payload + ", signature=" + signature;
}

@Override
public String getTokenValue() {
return completeToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

package org.opensearch.identity.tokens;

import org.opensearch.OpenSearchSecurityException;

/**
* This interface defines the expected methods of a token manager
*/
Expand All @@ -19,4 +21,9 @@ public interface TokenManager {
* @return A new auth token
*/
public AuthToken issueToken(String audience);

/**
* Issue a service account token for an extension's service account
* */
AuthToken issueServiceAccountToken(String extensionUniqueId) throws OpenSearchSecurityException;
}
1 change: 1 addition & 0 deletions server/src/main/java/org/opensearch/node/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ protected Node(
additionalSettings.addAll(extAwarePlugin.getExtensionSettings());
}
this.extensionsManager = new ExtensionsManager(additionalSettings);
this.extensionsManager.setIdentityService(identityService);
} else {
this.extensionsManager = new NoopExtensionsManager();
}
Expand Down

0 comments on commit 0e7f23e

Please sign in to comment.