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

Issue #112: Develop WinRm Extension #174

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion metricshub-agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@
<limit>
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
<minimum>0.57</minimum>
</limit>
</limits>
</rule>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,6 @@ public class ProtocolsConfig {
private IConfiguration osCommand;

@JsonSetter(nulls = SKIP)
private WinRmProtocolConfig winrm;
@JsonDeserialize(using = ExtensionConfigDeserializer.class)
private IConfiguration winrm;
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@
import org.sentrysoftware.metricshub.agent.config.ResourceConfig;
import org.sentrysoftware.metricshub.agent.config.ResourceGroupConfig;
import org.sentrysoftware.metricshub.agent.config.protocols.ProtocolsConfig;
import org.sentrysoftware.metricshub.agent.config.protocols.WinRmProtocolConfig;
import org.sentrysoftware.metricshub.agent.context.MetricDefinitions;
import org.sentrysoftware.metricshub.agent.security.PasswordEncrypt;
import org.sentrysoftware.metricshub.engine.common.exception.InvalidConfigurationException;
Expand Down Expand Up @@ -103,7 +102,6 @@
public class ConfigHelper {

private static final String WBEM_PROTOCOL = "WBEM";
private static final String WIN_RM_PROTOCOL = "WinRM";
private static final String TIMEOUT_ERROR =
"Resource %s - Timeout value is invalid for protocol %s." +
" Timeout value returned: %s. This resource will not be monitored. Please verify the configured timeout value.";
Expand Down Expand Up @@ -923,9 +921,9 @@ private static void validateProtocols(@NonNull final String resourceKey, final R
return;
}

final WinRmProtocolConfig winRmConfig = protocolsConfig.getWinrm();
final IConfiguration winRmConfig = protocolsConfig.getWinrm();
if (winRmConfig != null) {
validateWinRmInfo(resourceKey, winRmConfig.getPort(), winRmConfig.getTimeout(), winRmConfig.getUsername());
winRmConfig.validateConfiguration(resourceKey);
}

final IConfiguration snmpConfig = protocolsConfig.getSnmp();
Expand Down Expand Up @@ -978,25 +976,7 @@ static void validateWinRmInfo(
final Integer port,
final Long timeout,
final String username
) throws InvalidConfigurationException {
StringHelper.validateConfigurationAttribute(
port,
INVALID_PORT_CHECKER,
() -> String.format(PORT_ERROR, resourceKey, WIN_RM_PROTOCOL, port)
);

StringHelper.validateConfigurationAttribute(
timeout,
INVALID_TIMEOUT_CHECKER,
() -> String.format(TIMEOUT_ERROR, resourceKey, WIN_RM_PROTOCOL, timeout)
);

StringHelper.validateConfigurationAttribute(
username,
INVALID_STRING_CHECKER,
() -> String.format(USERNAME_ERROR, resourceKey, WIN_RM_PROTOCOL)
);
}
) throws InvalidConfigurationException {}

/**
* Validate the given WBEM information (username, timeout, port and vCenter)
Expand Down Expand Up @@ -1060,7 +1040,6 @@ static HostConfiguration buildHostConfiguration(
final String resourceKey
) {
final ProtocolsConfig protocols = resourceConfig.getProtocols();

final Map<Class<? extends IConfiguration>, IConfiguration> protocolConfigurations = protocols == null
? new HashMap<>()
: new HashMap<>(
Expand All @@ -1072,7 +1051,8 @@ static HostConfiguration buildHostConfiguration(
protocols.getOsCommand(),
protocols.getSsh(),
protocols.getWmi(),
protocols.getWbem()
protocols.getWbem(),
protocols.getWinrm()
)
.filter(Objects::nonNull)
.collect(Collectors.toMap(IConfiguration::getClass, Function.identity()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@
* ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
*/

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.sentrysoftware.metricshub.cli.service.CliExtensionManager;
import org.sentrysoftware.metricshub.engine.common.exception.InvalidConfigurationException;
import org.sentrysoftware.metricshub.engine.configuration.IConfiguration;
import org.sentrysoftware.metricshub.engine.configuration.TransportProtocols;
import org.sentrysoftware.metricshub.engine.configuration.WinRmConfiguration;
import org.sentrysoftware.winrm.service.client.auth.AuthenticationEnum;
import picocli.CommandLine.Option;

/**
* This class is used by MetricsHubCliService to configure WinRM protocol when using the MetricsHub CLI.
* It create the engine's {@link WinRmConfiguration} object that is used to monitor a specific resource through WinRm.
* It create the engine's {@link IConfiguration} object that is used to monitor a specific resource through WinRm.
*/
@Data
@EqualsAndHashCode(callSuper = true)
Expand Down Expand Up @@ -96,7 +100,7 @@ public class WinRmConfigCli extends AbstractTransportProtocolCli {
defaultValue = "" + DEFAULT_TIMEOUT,
description = "Timeout in seconds for WinRM operations (default: ${DEFAULT-VALUE} s)"
)
private long timeout;
private String timeout;

@Option(
names = "--winrm-auth",
Expand All @@ -106,7 +110,7 @@ public class WinRmConfigCli extends AbstractTransportProtocolCli {
paramLabel = "AUTH",
split = ","
)
private List<AuthenticationEnum> authentications;
private List<String> authentications;

@Option(
names = { "--winrm-force-namespace" },
Expand All @@ -120,19 +124,31 @@ public class WinRmConfigCli extends AbstractTransportProtocolCli {
* @param defaultUsername Username specified at the top level of the CLI (with the --username option)
* @param defaultPassword Password specified at the top level of the CLI (with the --password option)
* @return a WinRmProtocol instance corresponding to the options specified by the user in the CLI
* @throws InvalidConfigurationException
*/
@Override
public IConfiguration toProtocol(String defaultUsername, char[] defaultPassword) {
return WinRmConfiguration
.builder()
.username(username == null ? defaultUsername : username)
.password(username == null ? defaultPassword : password)
.namespace(namespace)
.port(getOrDeducePortNumber())
.protocol(TransportProtocols.interpretValueOf(protocol))
.authentications(authentications)
.timeout(timeout)
.build();
public IConfiguration toProtocol(String defaultUsername, char[] defaultPassword)
throws InvalidConfigurationException {
final ObjectNode configuration = JsonNodeFactory.instance.objectNode();

final String finalUsername = username == null ? defaultUsername : username;
final char[] finalPassword = username == null ? defaultPassword : password;

configuration.set("username", new TextNode(finalUsername));
if (finalPassword != null) {
configuration.set("password", new TextNode(String.valueOf(finalPassword)));
}

configuration.set("namespace", new TextNode(namespace));
configuration.set("port", new IntNode(getOrDeducePortNumber()));
configuration.set("protocol", new TextNode(protocol));
configuration.set("authentications", getAuthentications());
configuration.set("timeout", new TextNode(timeout));

return CliExtensionManager
.getExtensionManagerSingleton()
.buildConfigurationFromJsonNode("winrm", configuration, value -> value)
.orElseThrow();
}

/**
Expand All @@ -149,6 +165,20 @@ protected int defaultHttpPortNumber() {
return 5985;
}

/**
* @return authentication list if specified, null otherwise
*/
protected ArrayNode getAuthentications() {
// Create an arrayNode that will contain all the authentications that the user introduced
ArrayNode authenticationsList = null;
if (authentications != null) {
authenticationsList = JsonNodeFactory.instance.arrayNode();
// Add all the introduced authentications
authentications.stream().forEach(authenticationsList::add);
}
return authenticationsList;
}

/**
* Whether HTTPS is configured or not
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.sentrysoftware.metricshub.agent.extension;

/*-
* ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
* MetricsHub WinRm Extension
* ჻჻჻჻჻჻
* Copyright 2023 - 2024 Sentry Software
* ჻჻჻჻჻჻
* 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 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 <http://www.gnu.org/licenses/>.
* ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
*/

import static com.fasterxml.jackson.annotation.Nulls.SKIP;

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Builder.Default;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.sentrysoftware.metricshub.engine.common.exception.InvalidConfigurationException;
import org.sentrysoftware.metricshub.engine.common.helpers.StringHelper;
import org.sentrysoftware.metricshub.engine.configuration.IConfiguration;
import org.sentrysoftware.metricshub.engine.configuration.TransportProtocols;
import org.sentrysoftware.metricshub.engine.deserialization.TimeDeserializer;

/**
* The WinRmConfiguration interface represents the configuration for the Windows Management Instrumentation protocol
* in the MetricsHub Extension System.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class WinRmTestConfiguration implements IConfiguration {

private String username;

private char[] password;

private String namespace;

@Default
private Integer port = 5985;

@Default
@JsonSetter(nulls = SKIP)
private TransportProtocols protocol = TransportProtocols.HTTP;

private List<String> authentications;

@Default
@JsonDeserialize(using = TimeDeserializer.class)
private Long timeout = 120L;

public void validateConfiguration(String resourceKey) throws InvalidConfigurationException {
StringHelper.validateConfigurationAttribute(
port,
attr -> attr == null || attr < 1 || attr > 65535,
() ->
String.format(
"Resource %s - Invalid port configured for protocol %s. Port value returned: %s." +
" This resource will not be monitored. Please verify the configured port value.",
resourceKey,
"WinRm",
port
)
);

StringHelper.validateConfigurationAttribute(
timeout,
attr -> attr == null || attr < 0L,
() ->
String.format(
"Resource %s - Timeout value is invalid for protocol %s." +
" Timeout value returned: %s. This resource will not be monitored. Please verify the configured timeout value.",
resourceKey,
"WinRM",
timeout
)
);

StringHelper.validateConfigurationAttribute(
username,
attr -> attr == null || attr.isBlank(),
() ->
String.format(
"Resource %s - No username configured for protocol %s." +
" This resource will not be monitored. Please verify the configured username.",
resourceKey,
"WinRm"
)
);
}

@Override
public String toString() {
String desc = "WinRm";
if (username != null) {
desc = desc + " as " + username;
}
return desc;
}
}
Loading