Skip to content
This repository was archived by the owner on Dec 7, 2018. It is now read-only.

Commit

Permalink
Start on dummy shell to allow background forwarding
Browse files Browse the repository at this point in the history
We don't use ssh to deploy right now

 * special hack, because I can't seem to get ssh output to work write. without the \r\n on os-x and rhel6, the
 * terminal doesn't start on a newline. it just eats it, and starts again on the same line. need to test on a
 * non-OS-X terminal

Fix to use an enum to pick the shell mode

Rework the layout of the forwarding filtering
Add further tests for forwarding
Add restrictions to prevent forwarding to hosts other than the artifactory host
organize imports
update notes

Cleanup javadocs

Cleanup Shell

minor cleanup

Fix groovy javadoc
  • Loading branch information
Allen Reese committed Jan 6, 2015
1 parent 13b8b0a commit 2ae27b4
Show file tree
Hide file tree
Showing 18 changed files with 1,082 additions and 37 deletions.
39 changes: 39 additions & 0 deletions NOTES
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
To eat the auth.


Need to set the channelFactories:

ssh.setChannelFactories(channelFactories);


if (channelFactories == null) {
channelFactories = Arrays.asList(
new ChannelSession.Factory(),
new TcpipServerChannel.DirectTcpipFactory());
}


Need to override TcpipServerChannel.DirectTcpipFactory and friends.

Need to override the IOServiceFactory:
org.apache.sshd.common.forward.TcpipServerChannel

connector = getSession().getFactoryManager().getIoServiceFactory()
.createConnector(handler);

Which returns org.apache.sshd.common.io.nio2.Nio2ServiceFactory, which we need to override
To override org.apache.sshd.common.io.nio2.Nio2Connector, which returns an org.apache.sshd.common.io.nio2.Nio2Session

Which is where we can finally start eating bytes
and worse.

The bytes we need to eat may come across multiple writes, so we'll have to set a start eating and stop eating flag across peeks.

This will get really Ugly because if Authorization comes as multiple packets, we'll have to buffer that bit somewhere.

Basically we need to buffer at least the Authorization header:
WWW-Authenticate: Basic realm="nmrs_m7VKmomQ2YM3:"

So we need at a minimum a buffer of WWW-Authenticate: size, and if we read WWW-AuthFoo, then we can write that buffer, and the rest of the following bits, while the previous writes would have been no-ops causing latency.

Worse, yet we need a secondary buffer which is looking for the \r\n\r\n ala cheap-alive, and we need to eat that and write out our auth bits.
11 changes: 11 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

For auth override the following are required:

1. tcp port forwarding of port X to artifactory host:port.
2. Stream sniffing, we need to read the byte from the stream stripping out any Authorization headers.
3. Add the authorization header where it's: ssh_user:encrypted password.
4. encrypted password comes from encrypting a salt and the username.
Another option is to just pass a token, and then have an artifactory plugin verify the token
by connecting back to the ssh server, but that adds an extra RT.
5. if in the same jvm 4 may change to be more secure.
6. write a plugin to validate the password.
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,41 @@
*/
public class LocalForwardingFilter extends DenyingForwardingFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(LocalForwardingFilter.class);
private final String toHost;
private final int toPort;

public LocalForwardingFilter(String toHost, int toPort) {
this.toHost = toHost.toLowerCase();
this.toPort = toPort;
}

@Override
public boolean canConnect(SshdSocketAddress socketAddress, Session session) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Connect forwarding requested from {} for {}", socketAddress, session);
}

// we don't want to allow forwards to just anywhere, we only allow them to the artifactory host.
int sp = socketAddress.getPort();
if (sp != toPort) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Denying forwarding requested from {} for {} tohost {} toPort {}", socketAddress, session,
toHost, Integer.valueOf(toPort));
}

return false;
}

String spHost = socketAddress.getHostName().toLowerCase();
if (!toHost.equals(spHost)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Denying forwarding requested from {} for {} tohost {} toPort {}", socketAddress, session,
toHost, Integer.valueOf(toPort));
}

return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -38,7 +40,9 @@
import org.apache.sshd.common.cipher.ARCFOUR256;
import org.apache.sshd.common.cipher.BlowfishCBC;
import org.apache.sshd.common.cipher.TripleDESCBC;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.shell.ProcessShellFactory.TtyOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -49,6 +53,8 @@
import com.yahoo.sshd.server.Sshd;
import com.yahoo.sshd.server.command.DelegatingCommandFactory;
import com.yahoo.sshd.server.filters.DenyingForwardingFilter;
import com.yahoo.sshd.server.filters.LocalForwardingFilter;
import com.yahoo.sshd.server.shell.ForwardingShellFactory;
import com.yahoo.sshd.server.shell.MessageShellFactory;
import com.yahoo.sshd.server.shell.SshProxyMessage;
import com.yahoo.sshd.tools.artifactory.ArtifactoryInformation;
Expand All @@ -57,6 +63,21 @@
public class SshdProxySettings implements SshdSettingsInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(SshdProxySettings.class);

/**
* Type of shell that gets created when someone ssh's in.
*/
public static enum ShellMode {
/**
* Use {@link MessageShellFactory} to create a shell that simply presents a message.
*/
MESSAGE,

/**
* Use {@link ForwardingShellFactory} to create a shell which echo's input back, but also allows forwarding.
*/
FORWARDING_ECHO_SHELL
}

/**
* The port the ssh server listens on.
*/
Expand Down Expand Up @@ -96,6 +117,11 @@ public class SshdProxySettings implements SshdSettingsInterface {
*/
protected final boolean developmentMode;

/**
* Type of shell that gets created when someone ssh's in.
*/
protected final ShellMode shellMode;

public SshdProxySettings(SshdSettingsBuilder b) throws SshdConfigurationException {

this.port = b.getSshdPort();
Expand All @@ -107,9 +133,13 @@ public SshdProxySettings(SshdSettingsBuilder b) throws SshdConfigurationExceptio
String artifactoryUsername = b.getArtifactoryUsername();
String artifactoryPassword = b.getArtifactoryPassword();

this.artifactoryInfo =
createArtifactoryInformation(b.getArtifactoryUrl(), b.getArtifactoryUsername(),
b.getArtifactoryPassword());
try {
this.artifactoryInfo =
createArtifactoryInformation(b.getArtifactoryUrl(), b.getArtifactoryUsername(),
b.getArtifactoryPassword());
} catch (MalformedURLException e) {
throw new SshdConfigurationException(e);
}

RunnableComponent[] temp = b.getExternalComponents();
this.externalComponents = Arrays.copyOf(temp, temp.length);
Expand All @@ -134,6 +164,8 @@ public SshdProxySettings(SshdSettingsBuilder b) throws SshdConfigurationExceptio
}

this.developmentMode = b.getDevelopmentMode();

this.shellMode = b.getShellMode();
}

/**
Expand All @@ -145,9 +177,10 @@ public SshdProxySettings(SshdSettingsBuilder b) throws SshdConfigurationExceptio
* @param artifactoryUsername
* @param artifactoryPassword
* @return
* @throws MalformedURLException if artifactoryUrl is invalid
*/
protected ArtifactoryInformation createArtifactoryInformation(final String artifactoryUrl,
final String artifactoryUsername, final String artifactoryPassword) {
final String artifactoryUsername, final String artifactoryPassword) throws MalformedURLException {
return new ArtifactoryInformation(artifactoryUrl, artifactoryUsername, artifactoryPassword);
}

Expand Down Expand Up @@ -229,9 +262,36 @@ public MultiUserPKAuthenticator getPublickeyAuthenticator() throws IOException,

@Override
public Factory<Command> getShellFactory() {
// TODO when separating out settings, we'll provide a different success
// message, or a file path for it.
return new MessageShellFactory(SshProxyMessage.MESSAGE_STRING);

EnumSet<TtyOptions> ttyOptions;

if (OsUtils.isUNIX()) {
/**
* org.apache.sshd.server.shell.ProcessShellFactory does this: ttyOptions = EnumSet.of(TtyOptions.ONlCr);
*
* However, it doesn't seem to work for me. So in our copy of
* org.apache.sshd.server.shell.TtyFilterOutputStream.TtyFilterOutputStream(EnumSet<TtyOptions>,
* OutputStream, TtyFilterInputStream), we have a special hack that if TtyOptions.INlCr and TtyOptions.ICrNl
* are both set, send cr nl instead. no idea if the windows even works.
*/
// ttyOptions = EnumSet.of(TtyOptions.ONlCr);
ttyOptions = EnumSet.of(TtyOptions.OCrNl, TtyOptions.INlCr, TtyOptions.ICrNl);
} else {
ttyOptions = EnumSet.of(TtyOptions.Echo, TtyOptions.OCrNl, TtyOptions.INlCr, TtyOptions.ICrNl);
}

switch (shellMode) {
case FORWARDING_ECHO_SHELL:
return new ForwardingShellFactory(ttyOptions);

case MESSAGE:
default:
// TODO when separating out settings, we'll provide a different success
// message, or a file path for it.
return new MessageShellFactory(SshProxyMessage.MESSAGE_STRING);

}

}

@Override
Expand Down Expand Up @@ -386,6 +446,25 @@ public TcpipForwarderFactory getForwardingFactory() {

@Override
public ForwardingFilter getForwardingFilter() {
return new DenyingForwardingFilter();
if (isForwardingAllowed()) {
return new LocalForwardingFilter(artifactoryInfo.getArtifactoryHost(), artifactoryInfo.getArtifactoryPort());
} else {
return new DenyingForwardingFilter();
}
}

@Override
public boolean isForwardingAllowed() {
switch (shellMode) {
case FORWARDING_ECHO_SHELL:
return true;

case MESSAGE:
return false;

default:
return false;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.yahoo.sshd.server.command.DefaultScpCommandFactory;
import com.yahoo.sshd.server.command.DelegatingCommandFactory;
import com.yahoo.sshd.server.jetty.JettyRunnableComponent;
import com.yahoo.sshd.server.settings.SshdProxySettings.ShellMode;
import com.yahoo.sshd.utils.RunnableComponent;

/**
Expand Down Expand Up @@ -94,6 +95,8 @@ public class SshdSettingsBuilder {

private Boolean developerMode;

private ShellMode shellMode;

protected String overriddenRoot;

static final List<String> DEFAULT_COMMAND_FACTORIES = new ArrayList<>(Arrays.asList(new String[] {//
Expand Down Expand Up @@ -144,6 +147,7 @@ public SshdSettingsBuilder(String[] args) throws SshdConfigurationException {
requestLogPath = findRequestLogPath();
httpPort = findHttpPort();
webappsDir = findWebappDir();
shellMode = findShellMode();

// do this last, so it can rely on everything before/
externalComponents = createExternalComponents();
Expand Down Expand Up @@ -265,7 +269,6 @@ protected String findHostKeyPath() {
return getStringFromConfig("sshd.hostKeyPath", defaultHostKeyPath, "got host key");
}

@SuppressWarnings("unchecked")
List<DelegatingCommandFactory> createCfInstances() {
List<DelegatingCommandFactory> cfInstances = new ArrayList<>();
cfInstances.add(new DefaultScpCommandFactory());
Expand Down Expand Up @@ -643,4 +646,29 @@ public int getIntFromConfig(String config, int defaultValue, String message) {
public boolean getDevelopmentMode() {
return (null == developerMode) ? false : developerMode.booleanValue();
}


/**
* Generate the path for where request/access logs should be written to.
*
* @return a path to write access logs to.
*/
protected ShellMode findShellMode() {
ShellMode defaultMode = ShellMode.MESSAGE;
String modeString = getStringFromConfig("sshd.shellMode", defaultMode.name(), "got ShellMode");
return ShellMode.valueOf(modeString);
}


public SshdSettingsBuilder setSetShellMode(ShellMode shellMode) {
this.shellMode = shellMode;
return this;
}

public ShellMode getShellMode() {
if (null == shellMode) {
shellMode = findShellMode();
}
return shellMode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,23 @@ public interface SshdSettingsInterface {
*/
boolean isDevelopementMode();

/**
*
* @return Factory to use for filtering.
*/
TcpipForwarderFactory getForwardingFactory();

/**
*
* @return an implementation of {@link ForwardingFilter} that can be used to see if forwarding is allowed.
*/
ForwardingFilter getForwardingFilter();

/**
* This is used to determine what implementation of {@link ForwardingFilter}
* {@link SshdSettingsInterface#getForwardingFilter()} should return
*
* @return
*/
boolean isForwardingAllowed();
}
Loading

0 comments on commit 2ae27b4

Please sign in to comment.