Skip to content

Commit

Permalink
Added helper commands to the ssh server, implemented whoami command
Browse files Browse the repository at this point in the history
  • Loading branch information
Ignas committed Dec 17, 2023
1 parent f8511cf commit b4a7873
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 30 deletions.
27 changes: 27 additions & 0 deletions src/main/java/lt/pow/nukagit/ssh/NukagitCliCommandFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lt.pow.nukagit.ssh;

import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.command.CommandFactory;
import org.apache.sshd.server.shell.UnknownCommandFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.io.IOException;

public class NukagitCliCommandFactory implements CommandFactory {
Logger LOGGER = LoggerFactory.getLogger(NukagitCliCommandFactory.class);

@Override
public Command createCommand(ChannelSession channel, String command) throws IOException {
String username = (String) channel.getSession().getIoSession().getAttribute("username");
MDC.put("username", username);
MDC.put("command", command);
LOGGER.info("User '{}' ran command '{}'", username, command);
if ("whoami".equals(command.strip()))
return new WhoAmICommand(command);
else
return UnknownCommandFactory.INSTANCE.createCommand(channel, command);
}
}
61 changes: 32 additions & 29 deletions src/main/java/lt/pow/nukagit/ssh/SshModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,39 @@
@Module
public abstract class SshModule {

@Provides
static SshServerConfig configuration(Gestalt configurationProvider) {
try {
return configurationProvider.getConfig("ssh", SshServerConfig.class);
} catch (GestaltException e) {
throw new RuntimeException("SSH Server configuration is missing!", e);
@Provides
static SshServerConfig configuration(Gestalt configurationProvider) {
try {
return configurationProvider.getConfig("ssh", SshServerConfig.class);
} catch (GestaltException e) {
throw new RuntimeException("SSH Server configuration is missing!", e);
}
}
}

@Binds
@IntoSet
abstract Managed bindServer(ManagedSshServer server);
@Binds
@IntoSet
abstract Managed bindServer(ManagedSshServer server);

@Provides
@Singleton
static SshServer createServer(
DfsRepositoryResolver dfsRepositoryResolver,
SshServerConfig sshServerConfig,
PublicKeyRepository publicKeyRepository) {
var sshServer = SshServer.setUpDefaultServer();
sshServer.setHost(sshServerConfig.hostname());
sshServer.setPort(sshServerConfig.port());
var keyPairGenerator = new SimpleGeneratorHostKeyProvider(Path.of(sshServerConfig.hostKey()));
keyPairGenerator.setAlgorithm(sshServerConfig.hostKeyAlgorithm());
sshServer.setKeyPairProvider(keyPairGenerator);
sshServer.setPublickeyAuthenticator(new UsernameResolvingPublickeyAuthenticator(
publicKeyRepository.getPublicKeySupplier()
));
sshServer.setCommandFactory(
new GitDfsPackCommandFactory().withDfsRepositoryResolver(dfsRepositoryResolver));
return sshServer;
}
@Provides
@Singleton
static SshServer createServer(
DfsRepositoryResolver dfsRepositoryResolver,
SshServerConfig sshServerConfig,
PublicKeyRepository publicKeyRepository) {
var sshServer = SshServer.setUpDefaultServer();
sshServer.setHost(sshServerConfig.hostname());
sshServer.setPort(sshServerConfig.port());
var keyPairGenerator = new SimpleGeneratorHostKeyProvider(Path.of(sshServerConfig.hostKey()));
keyPairGenerator.setAlgorithm(sshServerConfig.hostKeyAlgorithm());
sshServer.setKeyPairProvider(keyPairGenerator);
sshServer.setPublickeyAuthenticator(new UsernameResolvingPublickeyAuthenticator(
publicKeyRepository.getPublicKeySupplier()
));
sshServer.setCommandFactory(
new GitDfsPackCommandFactory()
.withDfsRepositoryResolver(dfsRepositoryResolver)
.withDelegate(new NukagitCliCommandFactory())
);
return sshServer;
}
}
23 changes: 23 additions & 0 deletions src/main/java/lt/pow/nukagit/ssh/WhoAmICommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package lt.pow.nukagit.ssh;

import org.apache.sshd.server.command.AbstractCommandSupport;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class WhoAmICommand extends AbstractCommandSupport {
public WhoAmICommand(String command) {
super(command, null);
}

@Override
public void run() {
String username = (String) this.getServerSession().getIoSession().getAttribute("username");
try {
this.getOutputStream().write(String.format("You are: %s\n", username).getBytes(StandardCharsets.UTF_8));
onExit(0);
} catch (IOException e) {
onExit(1);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import lt.pow.nukagit.proto.RepositoriesServiceGrpc
import lt.pow.nukagit.proto.Users
import lt.pow.nukagit.proto.UsersServiceGrpc
import org.apache.sshd.client.SshClient
import org.apache.sshd.client.channel.ClientChannel
import org.apache.sshd.client.channel.ClientChannelEvent
import org.apache.sshd.common.channel.Channel
import org.apache.sshd.git.transport.GitSshdSessionFactory
import org.bouncycastle.openssl.PEMKeyPair
import org.bouncycastle.openssl.PEMParser
Expand All @@ -26,7 +29,9 @@ import org.testcontainers.utility.DockerImageName
import spock.lang.Specification
import spock.lang.TempDir

import java.nio.charset.StandardCharsets
import java.security.KeyPair
import java.util.concurrent.TimeUnit

@Testcontainers
class NukagitIntegrationTest extends Specification {
Expand All @@ -41,6 +46,7 @@ class NukagitIntegrationTest extends Specification {
.withExposedPorts(9000)
.withCommand("server /data --console-address :9001")

static final String USERNAME = "testuser"
var component = DaggerTestComponent.create()
SshClient sshClient
KeyPair keyPair
Expand Down Expand Up @@ -98,9 +104,10 @@ class NukagitIntegrationTest extends Specification {

keyPair = loadKeyPair('/fixtures/id_ecdsa')
sshClient.addPublicKeyIdentity(keyPair)
sshClient.start()

usersGrpcClient.createUser(Users.CreateUserRequest.newBuilder()
.setUsername("user")
.setUsername(USERNAME)
.setPublicKey(loadPublicKeyString('/fixtures/id_ecdsa.pub'))
.build())

Expand Down Expand Up @@ -158,4 +165,36 @@ class NukagitIntegrationTest extends Specification {
git.commit().setAuthor("test", "[email protected]").setMessage("Test Change").call()
git.push().call()
}

def sshRun(String command) {
def session = sshClient.connect("git", "localhost", sshPort).verify().getSession()
session.auth().verify()
ByteArrayOutputStream responseStream = new ByteArrayOutputStream()
ByteArrayOutputStream errorStream = new ByteArrayOutputStream()
ClientChannel channel = session.createChannel(Channel.CHANNEL_EXEC, command)
channel.setOut(responseStream)
channel.setErr(errorStream)
channel.open().await(1, TimeUnit.SECONDS)
channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 5000)
channel.close(false)
return [responseStream.toString(StandardCharsets.UTF_8), errorStream.toString(StandardCharsets.UTF_8), channel.exitStatus]
}

def "test whoami"() {
when:
def (out, err, status) = sshRun("whoami")
then:
status == 0
err == ""
out == "You are: ${USERNAME}\n"
}

def "test invalid command"() {
when:
def (out, err, status) = sshRun("dummy")
then:
status == 1
err == "Unknown command: dummy\n"
out == ""
}
}

0 comments on commit b4a7873

Please sign in to comment.