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 48 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
6 changes: 6 additions & 0 deletions plugins/content-manager/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ loggerUsageCheck.enabled = false
// No need to validate pom, as we do not upload to maven/sonatype
validateNebulaPom.enabled = false

// Skip forbiddenAPIs check on dependencies
thirdPartyAudit.enabled = false

//Skip checking for third party licenses
dependencyLicenses.enabled = false

repositories {
mavenLocal()
maven { url "https://aws.oss.sonatype.org/content/repositories/snapshots" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@

public class ContentManagerPlugin extends Plugin implements ActionPlugin {

public static String CTI_VD_CONSUMER_URL;
public static String CTI_CHANGES_URL;

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

Expand All @@ -64,13 +61,7 @@ public Collection<Object> createComponents(
NamedWriteableRegistry namedWriteableRegistry,
IndexNameExpressionResolver indexNameExpressionResolver,
Supplier<RepositoriesService> repositoriesServiceSupplier) {
PluginSettings.getInstance(environment.settings());
CTI_VD_CONSUMER_URL =
PluginSettings.getInstance().getCtiBaseUrl()
+ "/catalog/contexts/vd_1.0.0/consumers/vd_4.8.0";
CTI_CHANGES_URL =
PluginSettings.getInstance().getCtiBaseUrl()
+ "/catalog/contexts/vd_1.0.0/consumers/vd_4.8.0/changes";
PluginSettings.getInstance(environment.settings(), clusterService);
return Collections.emptyList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.wazuh.contentmanager.action.cti;

import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.core5.http.Header;
import org.opensearch.common.xcontent.XContentFactory;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.core.rest.RestStatus;
Expand All @@ -26,9 +25,9 @@

import java.io.IOException;

import com.wazuh.contentmanager.ContentManagerPlugin;
import com.wazuh.contentmanager.client.CTIClient;
import com.wazuh.contentmanager.model.ctiapi.ContextConsumerCatalog;
import com.wazuh.contentmanager.privileged.PrivilegedHttpAction;
import com.wazuh.contentmanager.util.Privileged;

/**
* Action class handling Catalog logic. This is mainly useful to get the last offset value, as well
Expand All @@ -50,8 +49,7 @@ public static BytesRestResponse run() throws IOException, IllegalArgumentExcepti
XContent xContent = XContentType.JSON.xContent();
XContentBuilder builder = XContentFactory.jsonBuilder();
SimpleHttpResponse response =
PrivilegedHttpAction.get(
ContentManagerPlugin.CTI_VD_CONSUMER_URL, null, null, (Header) null);
Privileged.doPrivilegedRequest(() -> CTIClient.getInstance().getCatalog());
ContextConsumerCatalog.parse(
xContent.createParser(
NamedXContentRegistry.EMPTY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
package com.wazuh.contentmanager.action.cti;

import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.core5.http.Header;
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;
Expand All @@ -28,15 +29,18 @@
import java.util.HashMap;
import java.util.Map;

import com.wazuh.contentmanager.ContentManagerPlugin;
import com.wazuh.contentmanager.client.CTIClient;
import com.wazuh.contentmanager.client.CommandManagerClient;
import com.wazuh.contentmanager.model.commandmanager.Command;
import com.wazuh.contentmanager.model.ctiapi.Offsets;
import com.wazuh.contentmanager.privileged.PrivilegedHttpAction;
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";
Expand All @@ -63,14 +67,18 @@ public BytesRestResponse run() throws IOException, IllegalArgumentException {
XContent xContent = XContentType.JSON.xContent();
XContentBuilder builder = XContentFactory.jsonBuilder();
SimpleHttpResponse response =
PrivilegedHttpAction.get(
ContentManagerPlugin.CTI_CHANGES_URL, null, buildQueryParametersMap(), (Header) null);
Privileged.doPrivilegedRequest(
() -> CTIClient.getInstance().getChanges(buildQueryParametersMap()));
Offsets.parse(
xContent.createParser(
NamedXContentRegistry.EMPTY,
DeprecationHandler.IGNORE_DEPRECATIONS,
response.getBodyBytes()))
.toXContent(builder, ToXContent.EMPTY_PARAMS);
// Post new command informing the new changes. (Call may be need to be moved elsewhere)
SimpleHttpResponse commandResponse =
CommandManagerClient.getInstance().postCommand(Command.generateCtiCommand());
log.info("Command Manager response: {}", commandResponse);
return new BytesRestResponse(RestStatus.fromCode(response.getCode()), builder.toString());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.client;

import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.core5.http.Header;

import java.net.URI;
import java.util.Map;

import com.wazuh.contentmanager.settings.PluginSettings;

/**
* CTIClient is a singleton class responsible for interacting with the CTI (Cyber Threat
* Intelligence) API. It extends HttpClient to handle HTTP requests.
*/
public class CTIClient extends HttpClient {
private static CTIClient instance;

private static final String apiUrl =
PluginSettings.getInstance().getCtiBaseUrl()
+ "/catalog/contexts/vd_1.0.0/consumers/vd_4.8.0";
private static final String CONTENT_CHANGES_ENDPOINT = "/changes";

/**
* Private constructor to enforce singleton pattern. Initializes the HTTP client with the CTI API
* base URL.
*/
protected CTIClient() {
super(URI.create(apiUrl));
}

/**
* Retrieves the singleton instance of CTIClient. Ensures thread-safe lazy initialization.
*
* @return The singleton instance of CTIClient.
*/
public static CTIClient getInstance() {
if (instance == null) {
synchronized (CTIClient.class) {
if (instance == null) {
instance = new CTIClient();
}
}
}
return instance;
}

/**
* Fetches content changes from the CTI API.
*
* @param queryParameters A map containing query parameters to filter the request.
* @return A SimpleHttpResponse containing the response from the API.
*/
public SimpleHttpResponse getChanges(Map<String, String> queryParameters) {
return sendRequest("GET", CONTENT_CHANGES_ENDPOINT, null, queryParameters, (Header) null);
}

/**
* Fetches the entire CTI catalog from the API.
*
* @return A SimpleHttpResponse containing the response from the API.
*/
public SimpleHttpResponse getCatalog() {
return sendRequest("GET", null, null, null, (Header) null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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.client;

import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.message.BasicHeader;

import java.net.URI;

import com.wazuh.contentmanager.settings.PluginSettings;

/**
* CommandManagerClient is a singleton class responsible for managing HTTP communication with the
* Command Manager API.
*/
public class CommandManagerClient extends HttpClient {
private static volatile CommandManagerClient instance;

/** Base Content Manager Plugin API endpoint. */
public static final String BASE_COMMAND_MANAGER_URI = "/_plugins/_command_manager";

/** Endpoint to post new commands. */
public static final String POST_COMMAND_ENDPOINT = "/commands";

/** Private constructor to initialize the CommandManagerClient with the base API URI. */
private CommandManagerClient() {
super(URI.create(PluginSettings.getInstance().getClusterBaseUrl() + BASE_COMMAND_MANAGER_URI));
}

/**
* Returns the singleton instance of CommandManagerClient. Uses double-checked locking to ensure
* thread safety.
*
* @return The singleton instance of CommandManagerClient.
*/
public static CommandManagerClient getInstance() {
if (instance == null) {
synchronized (CommandManagerClient.class) {
if (instance == null) {
instance = new CommandManagerClient();
}
}
}
return instance;
}

/**
* Sends a POST request to execute a command via the Command Manager API.
*
* @param requestBody The JSON request body containing the command details.
* @return A SimpleHttpResponse object containing the API response.
*/
public SimpleHttpResponse postCommand(String requestBody) {
Header header = new BasicHeader("Content-Type", "application/json");
return sendRequest("POST", POST_COMMAND_ENDPOINT, requestBody, null, header);
}
}
Loading
Loading