Skip to content
Draft
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
31 changes: 29 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Please see the source code of `SshConfiguration.java` for list of configuration
However, most configuration will be probably fine with the usual `host`, `username` and `password`.

The `argumentStyle` configuration property can take following values:

|====
|argumentStyle | Example command | Description

Expand Down Expand Up @@ -45,6 +46,7 @@ The `argumentStyle` configuration property can take following values:
|====

The `handleNullValues` configuration property can take the following values:

|====
| handleNullValues | Description | Combination with `argumentStyle` | Example where `foo` is `null`

Expand All @@ -64,15 +66,36 @@ The `handleNullValues` configuration property can take the following values:

|====

The `variableTransport` configuration property can take the following values:

|====
| variableTransport | Description | Combination with `argumentStyle` | Example with `variableTransportEnvPrefix` set to `CONNID_ARG_`

| `inplace` | Arguments will be moved inplace quoted in single quotes.
If arguments contain secrets, consider using transport env.
Single quotes in the value will be escaped by doubling them.
| `variables-bash` | `foo='value1'; bar='valueWithQuotes''Inplace'; command $foo $bar`
||| `variables-powershell` | `$foo='value1'; $bar='valueWithQuotes''Inplace'; command $foo $bar`

| `env`
| Arguments will be set as ssh envs with the `variableTransportEnvPrefix` as prefix.
Make sure that the server allows env passing.
| `variables-bash` | `foo=$CONNID_ARG_foo; bar=$CONNID_ARG_bar; command $foo $bar`
||| `variables-powershell` | `$foo=$env:CONNID_ARG_foo; $bar=$env:CONNID_ARG_bar; command $foo $bar`

|====

The `variableTransportEnvPrefix` configuration property sets the prefix for environment variable transport.
The default value is `CONNID_ARG_`.

== Limitations

* Only "execution mode" of SSH is supported.
The connector will create SSH connection, authenticate, execute the command and tear down the connection.
The connector will create SSH connection, authenticate, (maybe) set envs, execute the command and tear down the connection.
This is slow, but it is reliable.
The "session mode" would allow to set up a session and keep it open.
This is supposed to be much faster, as we would avoid connection overhead.
However, that would also mean that we will have problems of detecting where command execution ends, the commands may influence session state, this may be shell-specific (different method for bash and powershell), etc.
However, that would also mean that we will have problems of detecting where command execution ends, the commands may influence session state, this may be shell-specific (different method for bash and powershell), the envs may leak into other commands, etc.

* Script language parameter is ignored.
However, for future compatibility, we recommend using following values:
Expand Down Expand Up @@ -104,4 +127,8 @@ The error stream (stderr) is not processed by the connector yet.
* The connector cannot process script exit code.
SSH provides the exit code, but there is no good way how to pass the exit code through the ConnId layer.

* Passing arguments as envs is only supported for `variables-bash` and `variables-powershell` argument styles.
The value length is limited by the maximum env size on the server side.
Windows is more restrictive than Linux.

If you do not like the limitations, we will be more than happy to accept a contribution.
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.identityconnectors.framework.common.exceptions.ConfigurationException;
import org.identityconnectors.framework.common.objects.ScriptContext;

import java.util.HashMap;
import java.util.Map;

public class CommandProcessor {
Expand All @@ -28,43 +29,43 @@ public CommandProcessor(SshConfiguration configuration) {
this.configuration = configuration;
}

public String process(ScriptContext scriptCtx) {
public ProcessedCommand process(ScriptContext scriptCtx) {
String command = scriptCtx.getScriptText();
if (command == null) {
return null;
}
Map<String, Object> arguments = scriptCtx.getScriptArguments();
if (arguments == null) {
return command;
return new ProcessedCommand(command, Map.of());
}
if (configuration.getArgumentStyle() == null) {
return encodeArgumentsAndCommandToString(command, arguments, "-");
}
switch (configuration.getArgumentStyle()) {
case SshConfiguration.ARGUMENT_STYLE_VARIABLES_BASH:
return encodeVariablesAndCommandToString(command, arguments, "", false);
case SshConfiguration.ARGUMENT_STYLE_VARIABLES_POWERSHELL:
return encodeVariablesAndCommandToString(command, arguments, "$", true);
case SshConfiguration.ARGUMENT_STYLE_DASH:
return encodeArgumentsAndCommandToString(command, arguments, "-");
case SshConfiguration.ARGUMENT_STYLE_SLASH:
return encodeArgumentsAndCommandToString(command, arguments, "/");
default:
throw new ConfigurationException("Unknown value of argument style: "+configuration.getArgumentStyle());
}
return switch (configuration.getArgumentStyle()) {
case SshConfiguration.ARGUMENT_STYLE_VARIABLES_BASH ->
encodeVariablesAndCommandToString(command, arguments, "", "$", false);
case SshConfiguration.ARGUMENT_STYLE_VARIABLES_POWERSHELL ->
encodeVariablesAndCommandToString(command, arguments, "$", "$env:", true);
case SshConfiguration.ARGUMENT_STYLE_DASH -> encodeArgumentsAndCommandToString(command, arguments, "-");
case SshConfiguration.ARGUMENT_STYLE_SLASH -> encodeArgumentsAndCommandToString(command, arguments, "/");
default -> throw new ConfigurationException(
"Unknown value of argument style: " + configuration.getArgumentStyle());
};
}

private String encodeArgumentsAndCommandToString(String command, Map<String, Object> arguments, String paramPrefix) {
private ProcessedCommand encodeArgumentsAndCommandToString(String command,
Map<String, Object> arguments,
String paramPrefix) {
StringBuilder commandLineBuilder = new StringBuilder();
commandLineBuilder.append(command);
for (Map.Entry<String, Object> argEntry: arguments.entrySet()) {
for (Map.Entry<String, Object> argEntry : arguments.entrySet()) {
if (argEntry.getKey() == null) {
// we want this to go last
continue;
}
Object value = argEntry.getValue();
boolean insertAttribute = true;
if(value == null) {
if (value == null) {
switch (configuration.getHandleNullValues()) {
case SshConfiguration.HANDLE_NULL_AS_EMPTY_STRING:
value = "";
Expand All @@ -73,10 +74,11 @@ private String encodeArgumentsAndCommandToString(String command, Map<String, Obj
insertAttribute = false;
break;
default:
throw new ConfigurationException("Unknown value of handleNullValues: " + configuration.getHandleNullValues());
throw new ConfigurationException(
"Unknown value of handleNullValues: " + configuration.getHandleNullValues());
}
}
if(insertAttribute) {
if (insertAttribute) {
commandLineBuilder.append(" ");
commandLineBuilder.append(paramPrefix).append(argEntry.getKey());
commandLineBuilder.append(" ");
Expand All @@ -87,22 +89,27 @@ private String encodeArgumentsAndCommandToString(String command, Map<String, Obj
commandLineBuilder.append(" ");
commandLineBuilder.append(arguments.get(null));
}
return commandLineBuilder.toString();
return new ProcessedCommand(commandLineBuilder.toString(), Map.of());
}

private String encodeVariablesAndCommandToString(String command, Map<String, Object> arguments, String variablePrefix, boolean spaces) {
private ProcessedCommand encodeVariablesAndCommandToString(String command,
Map<String, Object> arguments,
String variablePrefix,
String envPrefix,
boolean spaces) {
if (arguments == null) {
return command;
return new ProcessedCommand(command, Map.of());
}
StringBuilder commandLineBuilder = new StringBuilder();
for (Map.Entry<String, Object> argEntry: arguments.entrySet()) {
Map<String, String> envs = new HashMap<>();
for (Map.Entry<String, Object> argEntry : arguments.entrySet()) {
if (argEntry.getKey() == null) {
// we want this to go last
continue;
}
Object value = argEntry.getValue();
boolean insertAttribute = true;
if(value == null) {
if (value == null) {
switch (configuration.getHandleNullValues()) {
case SshConfiguration.HANDLE_NULL_AS_EMPTY_STRING:
value = "";
Expand All @@ -111,26 +118,48 @@ private String encodeVariablesAndCommandToString(String command, Map<String, Obj
insertAttribute = false;
break;
default:
throw new ConfigurationException("Unknown value of handleNullValues: " + configuration.getHandleNullValues());
throw new ConfigurationException(
"Unknown value of handleNullValues: " + configuration.getHandleNullValues());
}
}
if(insertAttribute) {
commandLineBuilder.append(variablePrefix).append(argEntry.getKey());
if (spaces) {
commandLineBuilder.append(" = ");
} else {
commandLineBuilder.append("=");
if (insertAttribute) {
switch (configuration.getVariableTransport()) {
case SshConfiguration.VARIABLE_TRANSPORT_INPLACE -> {
commandLineBuilder.append(variablePrefix).append(argEntry.getKey());
if (spaces) {
commandLineBuilder.append(" = ");
} else {
commandLineBuilder.append("=");
}
commandLineBuilder.append(quoteSingle(value));
commandLineBuilder.append("; ");
}
case SshConfiguration.VARIABLE_TRANSPORT_ENV -> {
envs.put(configuration.getVariableTransportEnvPrefix() + argEntry.getKey(), value.toString());
commandLineBuilder.append(variablePrefix).append(argEntry.getKey());
if (spaces) {
commandLineBuilder.append(" = ");
} else {
commandLineBuilder.append("=");
}
commandLineBuilder.append(envPrefix)
.append(configuration.getVariableTransportEnvPrefix())
.append(argEntry.getKey())
.append("; ");
}
default -> throw new ConfigurationException(
"Unknown value of variable transport: " + configuration.getVariableTransport());

}
commandLineBuilder.append(quoteSingle(value));
commandLineBuilder.append("; ");

}
}
commandLineBuilder.append(command);
if (arguments.get(null) != null) {
commandLineBuilder.append(" ");
commandLineBuilder.append(arguments.get(null));
}
return commandLineBuilder.toString();
return new ProcessedCommand(commandLineBuilder.toString(), envs);
}

private String quoteSingle(Object value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.evolveum.polygon.connector.ssh;

import java.util.Map;

public record ProcessedCommand(String commandString, Map<String, String> envs) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.identityconnectors.framework.spi.AbstractConfiguration;
import org.identityconnectors.framework.spi.ConfigurationProperty;

import java.util.Objects;

/**
* SSH connector configuration.
*
Expand Down Expand Up @@ -105,6 +107,33 @@ public class SshConfiguration extends AbstractConfiguration {
public static final String HANDLE_NULL_AS_EMPTY_STRING = "asEmptyString";
public static final String HANDLE_NULL_AS_GONE = "asGone";

/**
* Defines how arguments will be transported into the script:
* <ul>
* <li>[default] "inplace" means that arguments will be inserted into the script as variables or parameters, wrapped by single quotes.</li>
* <li>"env" means that arguments will be transported as environment variables. This is only supported for
* the argument styles based on variables (variables-powershell, variables-bash). Make sure that the sshd_config
* of the ssh daemon is configured to allow setting envs via ssh. To prevent name clashes, the environment variable names
* will be prefixed by the value of variableTransportEnvPrefix property (default: CONNID_ARG_).
* <p>Example sshd_config: <pre>AcceptEnv CONNID_ARG_*</pre></p>
* </li>
* </ul>
*
* @see <a href="https://man.openbsd.org/sshd_config#AcceptEnv">sshd_config man page</a>
*/
private String variableTransport = VARIABLE_TRANSPORT_INPLACE;

public static final String VARIABLE_TRANSPORT_INPLACE = "inplace";
public static final String VARIABLE_TRANSPORT_ENV = "env";

/**
* Prefix for environment variables used for argument transport, if variableTransport is set to "env".
* Make sure that the sshd_config of the ssh daemon is configured to allow setting envs with the given prefix via ssh.
* Make sure that the prefix does not collide with other environment variable names.
* Default: CONNID_ARG_
*/
private String variableTransportEnvPrefix = "CONNID_ARG_";

@ConfigurationProperty(order = 100)
public String getHost() {
return host;
Expand Down Expand Up @@ -195,8 +224,34 @@ public void setHandleNullValues(String handleNullValues) {
this.handleNullValues = handleNullValues;
}

@ConfigurationProperty(order = 140)
public String getVariableTransport() {
return variableTransport;
}

public void setVariableTransport(String variableTransport) {
this.variableTransport = variableTransport;
}

@ConfigurationProperty(order = 150)
public String getVariableTransportEnvPrefix() {
return variableTransportEnvPrefix;
}

public void setVariableTransportEnvPrefix(String variableTransportEnvPrefix) {
this.variableTransportEnvPrefix = variableTransportEnvPrefix;
}

@Override
public void validate() {
if (Objects.equals(variableTransport, VARIABLE_TRANSPORT_ENV) &&
!(ARGUMENT_STYLE_VARIABLES_BASH.equals(argumentStyle) ||
ARGUMENT_STYLE_VARIABLES_POWERSHELL.equals(argumentStyle))) {
throw new IllegalArgumentException(
"variableTransport set to 'env' is only supported for argument styles '" +
ARGUMENT_STYLE_VARIABLES_BASH + "' and '" + ARGUMENT_STYLE_VARIABLES_POWERSHELL + "'");
}

}

}
Loading