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

Command Manager client prototype #274

Merged
merged 50 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
54ccdd9
Add imposter for cti API
f-galland Jan 28, 2025
a395282
Add authorization to imposter API
f-galland Jan 28, 2025
e9b009d
Fix authorization
f-galland Jan 28, 2025
58cb592
Add http client
f-galland Jan 28, 2025
310d7b8
Add placeholder authorization headers
f-galland Jan 28, 2025
aeb9de5
Add CTI API reply to console
f-galland Jan 28, 2025
45d0c55
Add CTI API reply to logs
f-galland Jan 28, 2025
55f1035
Fixing GET route
f-galland Jan 30, 2025
2af159f
Fix cti_api.uri SecureSetting
f-galland Jan 30, 2025
fe039df
Improve class structure
f-galland Jan 30, 2025
70418ec
Populate enum and model classes
f-galland Jan 30, 2025
5a3e091
Fix HttpClient permissions
f-galland Jan 31, 2025
fb9b5e6
Decouple privileged action execution
f-galland Jan 31, 2025
6dc3a2e
Add a model for the cti api context consumer catalog endpoint
f-galland Jan 31, 2025
3169457
Apply spotless
f-galland Jan 31, 2025
a0e42db
Make endpoint reply with offset and snapshot link
f-galland Jan 31, 2025
bea549b
Iterate over consumers. Parse and return their information
f-galland Jan 31, 2025
913f62b
Improve multiple endpoints handling
f-galland Jan 31, 2025
44f81d8
Move API logic from handler to action class
f-galland Feb 3, 2025
a7b2b58
Add parser for offset payload
f-galland Feb 5, 2025
d721132
Fix parsing data array
f-galland Feb 5, 2025
7f29bcc
Make payload parseable to any depth
f-galland Feb 5, 2025
9c94f39
Add javadocs
f-galland Feb 5, 2025
954c860
Removing unused API mock
f-galland Feb 6, 2025
97f695d
Changing exception message
f-galland Feb 6, 2025
10dac65
Adding comments about provisional code
f-galland Feb 6, 2025
0ef7988
Remove implementation of ClusterPlugin
f-galland Feb 6, 2025
5025e40
Merge branch 'master' into 238-cti-client-prototype
f-galland Feb 6, 2025
3881cdf
Spotless run
f-galland Feb 6, 2025
8e37216
Add URL as a setting
f-galland Feb 6, 2025
b6f2302
Add action and model packages to command manager client scaffolding
QU3B1M Feb 12, 2025
bf90275
Merge branch '238-cti-client-prototype' into enhancement/239-command-…
QU3B1M Feb 12, 2025
ecad1b0
New HttpClient class proposal
QU3B1M Feb 12, 2025
c0cea51
Implement new HttpCLient 'CommandManagerClient'
QU3B1M Feb 12, 2025
69d8ee8
Implemente PostCommand action to generate new commands to the CM API
QU3B1M Feb 12, 2025
cdf6004
Add API Uri definition on HttpClient classes
QU3B1M Feb 13, 2025
00ca8cc
Remove unnecesary handler
QU3B1M Feb 13, 2025
fd82517
Implement Privileged util to handle privileged actions
QU3B1M Feb 14, 2025
f572a1c
Remove references to previous HttpClient
QU3B1M Feb 14, 2025
27bdbf3
Apply spotless formatting
QU3B1M Feb 14, 2025
53f951a
Add query params to changes endpoint
QU3B1M Feb 14, 2025
509536d
Merge branch 'main' into enhancement/239-command-manager-client
QU3B1M Feb 14, 2025
f3dcee1
Add unit tests for HttpClient class
QU3B1M Feb 14, 2025
841254f
Apply spotless to tests
QU3B1M Feb 14, 2025
953751f
Merge branch 'main' into enhancement/239-command-manager-client
QU3B1M Feb 14, 2025
5514616
Move CTI and CommandManager clients to base client/ directory
QU3B1M Feb 14, 2025
7fd174c
Implement more Unit Tests scenarios for HttpClient
QU3B1M Feb 14, 2025
6efc285
Update command body with new defined fields
QU3B1M Feb 14, 2025
d9fe505
Replace Http related string values (methods, headers) with enum values
AlexRuiz7 Feb 17, 2025
87a7a25
Replace single use constant
AlexRuiz7 Feb 17, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ private RestChannelConsumer handlePost(RestRequest request, final NodeClient cli
return channel -> {
channel.sendResponse(
new BytesRestResponse(
RestStatus.BAD_REQUEST,
"Cannot generate orders. Invalid agent IDs or groups."));
RestStatus.BAD_REQUEST, "Cannot generate orders. Invalid agent IDs or groups."));
};
}

Expand Down
8 changes: 8 additions & 0 deletions plugins/content-manager/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ opensearchplugin {
noticeFile rootProject.file('NOTICE.txt')
}

dependencies {
implementation "org.apache.httpcomponents.client5:httpclient5:5.4"
implementation "org.apache.httpcomponents.core5:httpcore5-h2:5.3.1"
implementation "org.apache.httpcomponents.core5:httpcore5:5.3.1"
implementation "org.apache.logging.log4j:log4j-slf4j-impl:2.23.1"
implementation "org.slf4j:slf4j-api:2.0.16"
}

// No need to validate license headers since spotless checks and applies it
licenseHeaders.enabled = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,69 @@
*/
package com.wazuh.contentmanager;

import org.opensearch.client.Client;
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
import org.opensearch.cluster.node.DiscoveryNodes;
import org.opensearch.cluster.service.ClusterService;
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.repositories.RepositoriesService;
import org.opensearch.rest.RestController;
import org.opensearch.rest.RestHandler;
import org.opensearch.script.ScriptService;
import org.opensearch.threadpool.ThreadPool;
import org.opensearch.watcher.ResourceWatcherService;

public class ContentManagerPlugin extends Plugin {
// Implement the relevant Plugin Interfaces here
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;

import com.wazuh.contentmanager.resthandler.CatalogHandler;
import com.wazuh.contentmanager.resthandler.ChangesHandler;
import com.wazuh.contentmanager.settings.PluginSettings;

public class ContentManagerPlugin extends Plugin implements ActionPlugin {

/** ClassConstructor * */
public ContentManagerPlugin() {}

@Override
public Collection<Object> createComponents(
Client client,
ClusterService clusterService,
ThreadPool threadPool,
ResourceWatcherService resourceWatcherService,
ScriptService scriptService,
NamedXContentRegistry xContentRegistry,
Environment environment,
NodeEnvironment nodeEnvironment,
NamedWriteableRegistry namedWriteableRegistry,
IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<RepositoriesService> repositoriesServiceSupplier) {
PluginSettings.getInstance(environment.settings(), clusterService);
return Collections.emptyList();
}

@Override
public List<RestHandler> getRestHandlers(
Settings settings,
RestController restController,
ClusterSettings clusterSettings,
IndexScopedSettings indexScopedSettings,
SettingsFilter settingsFilter,
IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<DiscoveryNodes> nodesInCluster) {
return List.of(new CatalogHandler(), new ChangesHandler());
}

@Override
public List<Setting<?>> getSettings() {
return Collections.singletonList(PluginSettings.CTI_BASE_URL);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2024, Wazuh Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.wazuh.contentmanager.action.cti;

import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.opensearch.common.xcontent.XContentFactory;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.*;
import org.opensearch.rest.BytesRestResponse;

import java.io.IOException;

import com.wazuh.contentmanager.client.cti.CTIClient;
import com.wazuh.contentmanager.model.ctiapi.ContextConsumerCatalog;
import com.wazuh.contentmanager.util.Privileged;

/**
* Action class handling Catalog logic. This is mainly useful to get the last offset value, as well
* as the link of the latest snapshot
*/
public class GetCatalogAction {

/** Empty constructor */
public GetCatalogAction() {}

/**
* Submits a catalog query to the CTI API
*
* @return The parsed response from the CTI API
* @throws IOException rethrown from parse()
* @throws IllegalArgumentException rethrown from parse()
*/
public static BytesRestResponse run() throws IOException, IllegalArgumentException {
XContent xContent = XContentType.JSON.xContent();
XContentBuilder builder = XContentFactory.jsonBuilder();
SimpleHttpResponse response =
Privileged.doPrivilegedRequest(() -> CTIClient.getInstance().getCatalog());
ContextConsumerCatalog.parse(
xContent.createParser(
NamedXContentRegistry.EMPTY,
DeprecationHandler.IGNORE_DEPRECATIONS,
response.getBodyBytes()))
.toXContent(builder, ToXContent.EMPTY_PARAMS);
return new BytesRestResponse(RestStatus.fromCode(response.getCode()), builder.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2024, Wazuh Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.wazuh.contentmanager.action.cti;

import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.common.xcontent.XContentFactory;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.*;
import org.opensearch.rest.BytesRestResponse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.wazuh.contentmanager.client.commandmanager.CommandManagerClient;
import com.wazuh.contentmanager.client.cti.CTIClient;
import com.wazuh.contentmanager.model.commandmanager.Command;
import com.wazuh.contentmanager.model.ctiapi.Offsets;
import com.wazuh.contentmanager.util.Privileged;

/**
* Action class handling Offsets logic. This is used to get the json patches to the current
* vulnerability data
*/
public class GetChangesAction {
private static final Logger log = LogManager.getLogger(GetChangesAction.class);

private static String FROM_OFFSET_FIELD = "from_offset";
private static String TO_OFFSET_FIELD = "to_offset";
private static String WITH_EMPTIES_FIELD = "with_empties";
private String fromOffset = null;
private String toOffset = null;
private String withEmpties = null;

/** Constructor method */
public GetChangesAction(String fromOffset, String toOffset, String withEmpties) {
this.fromOffset = fromOffset;
this.toOffset = toOffset;
this.withEmpties = withEmpties;
}

/**
* Submits a changes query to the CTI API
*
* @return The parsed response from the CTI API
* @throws IOException rethrown from parse()
* @throws IllegalArgumentException rethrown from parse()
*/
public BytesRestResponse run() throws IOException, IllegalArgumentException {
XContent xContent = XContentType.JSON.xContent();
XContentBuilder builder = XContentFactory.jsonBuilder();
SimpleHttpResponse response =
Privileged.doPrivilegedRequest(() -> CTIClient.getInstance().getChanges(buildQueryParametersMap()));
Offsets.parse(
xContent.createParser(
NamedXContentRegistry.EMPTY,
DeprecationHandler.IGNORE_DEPRECATIONS,
response.getBodyBytes()))
.toXContent(builder, ToXContent.EMPTY_PARAMS);
SimpleHttpResponse commandResponse =
CommandManagerClient.getInstance()
.postCommand(Command.generateCtiCommand("Offset_version"));
log.info("Command Manager response: {}", commandResponse);
return new BytesRestResponse(RestStatus.fromCode(response.getCode()), builder.toString());
}

/**
* Builds a Map with the query parameters for the CTI API call
*
* @return The map with the parameters
*/
private Map<String, String> buildQueryParametersMap() {
Map<String, String> params = new HashMap<>();
params.put(FROM_OFFSET_FIELD, fromOffset);
params.put(TO_OFFSET_FIELD, toOffset);
params.put(WITH_EMPTIES_FIELD, withEmpties);
return params;
}
}
Loading
Loading