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

Implement a new KeyStore file to configure the persistence logic #97

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7329507
Create the initial structure
mcasas993 Oct 9, 2024
ae988a8
Manage the settings of the plugin. Create and reload the keystore.
mcasas993 Oct 11, 2024
a4e5f72
Add the security settings
mcasas993 Oct 11, 2024
d92ce1b
Fix license of CommandManagerSettings and CommandManagerSettingsExcep…
mcasas993 Oct 14, 2024
4342cb2
Merge branch 'master' into 95-configuration-persistence-logic
AlexRuiz7 Oct 14, 2024
a97e2d6
Rename test class to match convention
AlexRuiz7 Oct 14, 2024
65f6f9d
Fix comments in the merge request
mcasas993 Oct 14, 2024
f69c68d
Add password in save of KeyStoreWrapper and do some changes to reload…
mcasas993 Oct 15, 2024
67c7434
Plugin Settings Unit Test
mcasas993 Oct 16, 2024
4eb7339
Refactor of CommandManagerSettings to manage all the settings of the …
mcasas993 Oct 17, 2024
bba618d
Fix some errors in COmmandManagerSettings and create the associated u…
mcasas993 Oct 17, 2024
6f8a9b5
Merge branch 'master' of github.com:wazuh/wazuh-indexer-plugins into …
AlexRuiz7 Oct 18, 2024
99cf140
Log settings on load
AlexRuiz7 Oct 18, 2024
4b8cc0e
Apply spotless
AlexRuiz7 Oct 18, 2024
a7cf4b6
Unificate the CommandManagerSettings getSetting methods. Add some log…
mcasas993 Oct 21, 2024
4c105dd
Add another test for the diferents possibilities on the method getSet…
mcasas993 Oct 21, 2024
bc61647
Fix settings name in CommanManagerSettings
mcasas993 Oct 22, 2024
24f1699
Delete @Test annotation
mcasas993 Oct 22, 2024
d643f1f
Send commands to the M_API using the configuration service
AlexRuiz7 Oct 23, 2024
428b365
Commments
AlexRuiz7 Oct 23, 2024
78f29dc
Merge branch 'master' into 95-configuration-persistence-logic
AlexRuiz7 Oct 23, 2024
49ef455
Replace @Ignore with @AwaitsFix
AlexRuiz7 Oct 23, 2024
9ff0ff5
Merge branch '95-configuration-persistence-logic' of github.com:wazuh…
AlexRuiz7 Oct 23, 2024
a6f60ae
Simplify the general logic of CommandManagerSettings. Apply Singleton…
mcasas993 Oct 24, 2024
8e587e5
Modify the test according to the new behavior of CommandManagerSettings
mcasas993 Oct 24, 2024
57f4d4c
Merge branch 'master' into 95-configuration-persistence-logic
AlexRuiz7 Oct 25, 2024
0d14eda
Delete testing logs of CommandManagerPlugin
mcasas993 Oct 25, 2024
ad18cad
Delete unnecesary resource wazuh-indexer.keystore.json
mcasas993 Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions plugins/command-manager/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import org.opensearch.gradle.test.RestIntegTestTask

buildscript {
Expand Down Expand Up @@ -75,7 +76,9 @@ def versions = [
httpcore5: "5.3.1",
slf4j: "2.0.16",
log4j: "2.23.1",
conscrypt: "2.5.2"
conscrypt: "2.5.2",
mockito: "5.12.0",
junit:"4.13.2"
]

dependencies {
Expand All @@ -85,6 +88,9 @@ dependencies {
api "org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}"
api "org.slf4j:slf4j-api:${versions.slf4j}"
api "org.conscrypt:conscrypt-openjdk-uber:${versions.conscrypt}"

testImplementation "org.mockito:mockito-core:${versions.mockito}"
testImplementation "junit:junit:${versions.junit}"
}

// This requires an additional Jar not published as part of build-tools
Expand All @@ -108,6 +114,7 @@ repositories {

test {
include '**/*Tests.class'
jvmArgs "-Djava.security.policy=./plugins/command-manager/src/main/plugin-metadata/plugin-security.policy/plugin-security.policy"
}

task integTest(type: RestIntegTestTask) {
Expand All @@ -117,6 +124,7 @@ task integTest(type: RestIntegTestTask) {
}
tasks.named("check").configure { dependsOn(integTest) }


integTest {
// The --debug-jvm command-line option makes the cluster debuggable; this makes the tests debuggable
if (System.getProperty("test.debug") != null) {
Expand All @@ -126,9 +134,14 @@ integTest {

testClusters.integTest {
testDistribution = "INTEG_TEST"

//testDistribution = "ARCHIVE"
// This installs our plugin into the testClusters
plugin(project.tasks.bundlePlugin.archiveFile)

// add customized keystore
keystore 'm_api.auth.username', 'admin'
keystore 'm_api.auth.password', 'test'
keystore 'm_api.uri', 'https://httpbin.org/post'
}

run {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
import org.opensearch.cluster.node.DiscoveryNodes;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.common.settings.ClusterSettings;
import org.opensearch.common.settings.IndexScopedSettings;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.settings.SettingsFilter;
import org.opensearch.common.settings.*;
import org.opensearch.core.common.io.stream.NamedWriteableRegistry;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.env.Environment;
import org.opensearch.env.NodeEnvironment;
import org.opensearch.plugins.ActionPlugin;
import org.opensearch.plugins.Plugin;
import org.opensearch.plugins.ReloadablePlugin;
import org.opensearch.repositories.RepositoriesService;
import org.opensearch.rest.RestController;
import org.opensearch.rest.RestHandler;
Expand All @@ -30,28 +28,31 @@
import org.opensearch.watcher.ResourceWatcherService;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;

import com.wazuh.commandmanager.index.CommandIndex;
import com.wazuh.commandmanager.rest.RestPostCommandAction;
import com.wazuh.commandmanager.settings.CommandManagerSettings;
import com.wazuh.commandmanager.utils.httpclient.HttpRestClient;
import com.wazuh.commandmanager.utils.httpclient.HttpRestClientDemo;

/**
* The Command Manager plugin exposes an HTTP API with a single endpoint to receive raw commands
* from the Wazuh Server. These commands are processed, indexed and sent back to the Server for its
* delivery to, in most cases, the Agents.
*/
public class CommandManagerPlugin extends Plugin implements ActionPlugin {
public class CommandManagerPlugin extends Plugin implements ActionPlugin, ReloadablePlugin {
public static final String COMMAND_MANAGER_BASE_URI = "/_plugins/_command_manager";
public static final String COMMANDS_URI = COMMAND_MANAGER_BASE_URI + "/commands";
public static final String COMMAND_MANAGER_INDEX_NAME = ".commands";
public static final String COMMAND_MANAGER_INDEX_TEMPLATE_NAME = "index-template-commands";

private CommandIndex commandIndex;
private CommandManagerSettings commandManagerSettings;
// private static final Logger log = LogManager.getLogger(CommandManagerSettings.class);

@Override
public Collection<Object> createComponents(
Expand All @@ -68,10 +69,8 @@ public Collection<Object> createComponents(
Supplier<RepositoriesService> repositoriesServiceSupplier) {
this.commandIndex = new CommandIndex(client, clusterService, threadPool);

// HttpRestClient stuff
String uri = "https://httpbin.org/post";
String payload = "{\"message\": \"Hello world!\"}";
HttpRestClientDemo.run(uri, payload);
this.commandManagerSettings = CommandManagerSettings.getInstance(environment);

return Collections.emptyList();
}

Expand All @@ -83,7 +82,26 @@ public List<RestHandler> getRestHandlers(
SettingsFilter settingsFilter,
IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<DiscoveryNodes> nodesInCluster) {
return Collections.singletonList(new RestPostCommandAction(this.commandIndex));
return Collections.singletonList(
new RestPostCommandAction(this.commandIndex, this.commandManagerSettings));
}

@Override
public List<Setting<?>> getSettings() {
return Arrays.asList(
// Register API settings
CommandManagerSettings.M_API_AUTH_USERNAME,
CommandManagerSettings.M_API_AUTH_PASSWORD,
CommandManagerSettings.M_API_URI);
}

@Override
public void reload(Settings settings) {
// secure settings should be readable
// final CommandManagerSettings commandManagerSettings =
// CommandManagerSettings.getClientSettings(secureSettingsPassword);
// I don't know what I have to do when we want to reload the settings already
// xxxService.refreshAndClearCache(commandManagerSettings);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
package com.wazuh.commandmanager.rest;

import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.client.node.NodeClient;
Expand All @@ -23,8 +22,6 @@
import org.opensearch.rest.RestRequest;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
Expand All @@ -34,7 +31,8 @@
import com.wazuh.commandmanager.model.Agent;
import com.wazuh.commandmanager.model.Command;
import com.wazuh.commandmanager.model.Document;
import com.wazuh.commandmanager.utils.httpclient.HttpRestClient;
import com.wazuh.commandmanager.settings.CommandManagerSettings;
import com.wazuh.commandmanager.utils.httpclient.HttpRestClientDemo;

import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken;
import static org.opensearch.rest.RestRequest.Method.POST;
Expand All @@ -49,14 +47,16 @@ public class RestPostCommandAction extends BaseRestHandler {
"post_command_action_request_details";
private static final Logger log = LogManager.getLogger(RestPostCommandAction.class);
private final CommandIndex commandIndex;
private final CommandManagerSettings settings;

/**
* Default constructor
*
* @param commandIndex persistence layer
*/
public RestPostCommandAction(CommandIndex commandIndex) {
public RestPostCommandAction(CommandIndex commandIndex, CommandManagerSettings settings) {
this.commandIndex = commandIndex;
this.settings = settings;
}

public String getName() {
Expand Down Expand Up @@ -108,19 +108,15 @@ private RestChannelConsumer handlePost(RestRequest request) throws IOException {

// Commands delivery to the Management API.
// Note: needs to be decoupled from the Rest handler (job scheduler task).
HttpRestClient httpClient = HttpRestClient.getInstance();
try {
String uri = "https://httpbin.org/post";
// String uri = "https://127.0.0.1:5000";
URI receiverURI = new URIBuilder(uri).build();
String receiverURI = this.settings.getUri();
String payload =
document.toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS)
.toString();
SimpleHttpResponse response = httpClient.post(receiverURI, payload, document.getId());
SimpleHttpResponse response =
HttpRestClientDemo.runWithResponse(receiverURI, payload, document.getId());
log.info("Received response to POST request with code [{}]", response.getCode());
log.info("Raw response:\n{}", response.getBodyText());
} catch (URISyntaxException e) {
log.error("Bad URI: {}", e.getMessage());
} catch (Exception e) {
log.error("Error reading response: {}", e.getMessage());
}
Expand Down
mcasas993 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright OpenSearch Contributors
* 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 com.wazuh.commandmanager.settings;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.common.settings.SecureSetting;
import org.opensearch.common.settings.Setting;
import org.opensearch.common.settings.Settings;
import org.opensearch.core.common.settings.SecureString;
import org.opensearch.env.Environment;


public class CommandManagerSettings {

/** The access key (ie login username) for connecting to api. */
public static final Setting<SecureString> M_API_AUTH_USERNAME =
SecureSetting.secureString("m_api.auth.username", null);

/** The secret key (ie password) for connecting to api. */
public static final Setting<SecureString> M_API_AUTH_PASSWORD =
SecureSetting.secureString("m_api.auth.password", null);

/** The uri for connecting to api. */
public static final Setting<SecureString> M_API_URI =
SecureSetting.secureString("m_api.uri", null);

/** The access key (ie login username) for connecting to api. */
private final String authUsername;

/** The password for connecting to api. */
private final String authPassword;

/** The uri for connecting to api. */
private final String uri;

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

/** Private default constructor */
private CommandManagerSettings(
String authUsername, String authPassword, String uri) {
this.authUsername = authUsername;
this.authPassword = authPassword;
this.uri = uri;
log.info("CommandManagerSettings created ");
}

/**
* Singleton instance accessor
*
* @return {@link CommandManagerSettings#instance}
*/
public static CommandManagerSettings getInstance(Environment environment) {
if (CommandManagerSettings.instance == null) {
instance = CommandManagerSettings.getSettings(environment);
}
return CommandManagerSettings.instance;
}

/** Parse settings for a single client. */
public static CommandManagerSettings getSettings(
Environment environment) {

final Settings settings = environment.settings();
if (settings != null) {
log.info("Settings created with the keystore information.");
try (SecureString authUsername = M_API_AUTH_USERNAME.get(settings);
SecureString authPassword = M_API_AUTH_PASSWORD.get(settings);
SecureString uri = M_API_URI.get(settings); ) {
return new CommandManagerSettings(
authUsername.toString(),
authPassword.toString(),
uri.toString());
}
}else{
return null;
}
}


public String getAuthPassword() {
return authPassword;
}

public String getAuthUsername() {
return authUsername;
}

public String getUri() {
return uri;
}

@Override
public String toString() {
return "CommandManagerSettings{"
+ " authUsername='"
+ authUsername
+ '\''
+ ", authPassword='"
+ authPassword
+ '\''
+ ", uri='"
+ uri
+ '\''
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright OpenSearch Contributors
* 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 com.wazuh.commandmanager.settings;

public class CommandManagerSettingsException extends Exception {

// Default constructor
public CommandManagerSettingsException() {
super();
}

// Constructor that accepts a message
public CommandManagerSettingsException(String message) {
super(message);
}

// Constructor that accepts a message and a cause
public CommandManagerSettingsException(String message, Throwable cause) {
super(message, cause);
}

// Constructor that accepts a cause
public CommandManagerSettingsException(Throwable cause) {
super(cause);
}

// Exception for the case when load keystore failed
public static CommandManagerSettingsException loadSettingsFailed(String keyStorePath, String errorMessage) {
return new CommandManagerSettingsException("Load settings from: " + keyStorePath + " failed. Error: " + errorMessage);
}

// Exception for the case when reload plugin with the keystore failed
public static CommandManagerSettingsException reloadPluginFailed(String pluginName) {
return new CommandManagerSettingsException("Reload failed for plugin: " + pluginName);
}
}
Loading