javajgitapache-sshd

Setting ssh keys to use with jgit with ssh from apache sshd


I'm running git commands using jgit from a java application running in a kubernetes container, so I want to allow customizing the ssh key storage location and the like.

My first approach was with ssh supported with the jsch implementation but ran into troubles(I checked the methodology here Using Keys with JGit to Access a Git Repository Securely and have had no luck with it) so I'm adding the ssh support via the org.eclipse.jgit.ssh.apache artifact. Using an external ssh app isn't really an option due to the way this is being packaged/distributed.

I haven't had much luck finding information but assume that I just want to be emulating the jsch approach and replacing the session factory used in calls. Initially the approach I took was setting up a session factory with the desired ssh path

        final SshdSessionFactoryBuilder builder = new SshdSessionFactoryBuilder();
        builder.setHomeDirectory(new File(homeDir))
                .setConfigFile((f)-> new File(f, "config"))
                .setSshDirectory(new File(sshDir));

        return builder.build(null);

and passing this factory to the various commands.

final CloneCommand cmd = Git.cloneRepository().setDirectory(new File(basePath))
                            .setURI(remotePath);
                    if (sshSessionFactory != null) {
                        cmd.setTransportConfigCallback(new TransportConfigCallback() {
                            @Override
                            public void configure(Transport transport) {
                                final SshTransport sshTransport = (SshTransport)transport;
                                ((SshTransport) transport).setSshSessionFactory(sshSessionFactory);
                            }
                        });
                    }
                    git = cmd.call();

However with this approach, commands like clone would fail to auth with the git repo, attempting to use the default ssh keys. On following this in the debugger I found that inside the clone command, it's generating a fetch command but isn't passing the session factory or session to it, so the fetch simply builds a default config and fails to auth.

2021-05-31 11:48:09,793 INFO  GitChainRepository.java:114 - Couldn't find git dir, creating it
2021-05-31 11:48:10,748 INFO  AbstractSecurityProviderRegistrar.java:112 - getOrCreateProvider(EdDSA) created instance of net.i2p.crypto.eddsa.EdDSASecurityProvider
2021-05-31 11:48:10,970 INFO  DefaultIoServiceFactoryFactory.java:67 - No detected/configured IoServiceFactoryFactory using Nio2ServiceFactoryFactory
2021-05-31 11:48:12,206 INFO  GitChainRepository.java:133 - Initialized by clone of remote
2021-05-31 11:48:12,207 INFO  GitChainRepository.java:159 - Prepping remote repo git@ghe.snip/gitchainstore-synctest.git
2021-05-31 11:48:12,318 WARN  LoggingUtils.java:634 - exceptionCaught(JGitClientSession[git@ghe.ca-tools.org/52.69.168.141:22])[state=Opened] SshException: Server key did not validate
org.apache.sshd.common.SshException: Server key did not validate
    at org.apache.sshd.client.session.AbstractClientSession.checkKeys(AbstractClientSession.java:583) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.session.helpers.AbstractSession.handleKexMessage(AbstractSession.java:611) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.session.helpers.AbstractSession.doHandleMessage(AbstractSession.java:500) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.session.helpers.AbstractSession.handleMessage(AbstractSession.java:428) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.session.helpers.AbstractSession.decode(AbstractSession.java:1463) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.session.helpers.AbstractSession.messageReceived(AbstractSession.java:388) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.eclipse.jgit.internal.transport.sshd.JGitClientSession.messageReceived(JGitClientSession.java:197) ~[org.eclipse.jgit.ssh.apache-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
    at org.apache.sshd.common.session.helpers.AbstractSessionIoHandler.messageReceived(AbstractSessionIoHandler.java:64) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.io.nio2.Nio2Session.handleReadCycleCompletion(Nio2Session.java:358) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:335) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:332) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at org.apache.sshd.common.io.nio2.Nio2CompletionHandler.lambda$completed$0(Nio2CompletionHandler.java:38) ~[sshd-osgi-2.6.0.jar:2.6.0]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
    at org.apache.sshd.common.io.nio2.Nio2CompletionHandler.completed(Nio2CompletionHandler.java:37) [sshd-osgi-2.6.0.jar:2.6.0]
    at sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127) [?:?]
    at sun.nio.ch.Invoker$2.run(Invoker.java:219) [?:?]
    at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112) [?:?]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
    at java.lang.Thread.run(Thread.java:834) [?:?]
2021-05-31 11:48:12,331 INFO  SessionHelper.java:1034 - Disconnecting(JGitClientSession[git@ghe.ca-tools.org/52.69.168.141:22]): SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE - Server key did not validate

org.eclipse.jgit.api.errors.TransportException: git@ghe.snip/gitchainstore-synctest.git: Server key did not validate

    at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:224)
    at org.eclipse.jgit.api.PullCommand.call(PullCommand.java:263)
    at snip.GitChainRepository.setupRemote(GitChainRepository.java:173)
    at snip.GitChainRepository.start(GitChainRepository.java:144)
    at snip.GitChainRepositoryTest.setupGit(GitChainRepositoryTest.java:59)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.eclipse.jgit.errors.TransportException: git@ghe.snip/gitchainstore-synctest.git: Server key did not validate
    at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:248)
    at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:1)
    at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:107)
    at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:281)
    at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:153)
    at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:142)
    at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:94)
    at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1309)
    at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:213)
    ... 31 more
Caused by: org.apache.sshd.common.SshException: Server key did not validate
    at org.apache.sshd.common.future.AbstractSshFuture.verifyResult(AbstractSshFuture.java:126)
    at org.apache.sshd.client.future.DefaultAuthFuture.verify(DefaultAuthFuture.java:39)
    at org.apache.sshd.client.future.DefaultAuthFuture.verify(DefaultAuthFuture.java:32)
    at org.apache.sshd.common.future.VerifiableFuture.verify(VerifiableFuture.java:68)
    at org.eclipse.jgit.transport.sshd.SshdSession.connect(SshdSession.java:164)
    at org.eclipse.jgit.transport.sshd.SshdSession.connect(SshdSession.java:99)
    at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:235)
    ... 39 more
Caused by: org.apache.sshd.common.SshException: Server key did not validate
    at org.apache.sshd.client.session.AbstractClientSession.checkKeys(AbstractClientSession.java:583)
    at org.apache.sshd.common.session.helpers.AbstractSession.handleKexMessage(AbstractSession.java:611)
    at org.apache.sshd.common.session.helpers.AbstractSession.doHandleMessage(AbstractSession.java:500)
    at org.apache.sshd.common.session.helpers.AbstractSession.handleMessage(AbstractSession.java:428)
    at org.apache.sshd.common.session.helpers.AbstractSession.decode(AbstractSession.java:1463)
    at org.apache.sshd.common.session.helpers.AbstractSession.messageReceived(AbstractSession.java:388)
    at org.eclipse.jgit.internal.transport.sshd.JGitClientSession.messageReceived(JGitClientSession.java:197)
    at org.apache.sshd.common.session.helpers.AbstractSessionIoHandler.messageReceived(AbstractSessionIoHandler.java:64)
    at org.apache.sshd.common.io.nio2.Nio2Session.handleReadCycleCompletion(Nio2Session.java:358)
    at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:335)
    at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:332)
    at org.apache.sshd.common.io.nio2.Nio2CompletionHandler.lambda$completed$0(Nio2CompletionHandler.java:38)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at org.apache.sshd.common.io.nio2.Nio2CompletionHandler.completed(Nio2CompletionHandler.java:37)
    at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
    at java.base/sun.nio.ch.Invoker$2.run(Invoker.java:219)
    at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)


Process finished with exit code -1

Eventually I settled to just overriding the default session factory, which sort of works, but not really.

SshSessionFactory.setInstance(sshSessionFactory);

This ends up working for a bit but then falls apart unable to create new sessions from the factory(I haven't figured why but I'd prefer not to be overriding the default instance anyway hence posting this question)

org.eclipse.jgit.api.errors.TransportException: git@ghe.snip/gitchainstore-synctest.git: Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
        at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:224) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.api.PullCommand.call(PullCommand.java:263) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at snip.GitChainRepository.updateRemote(GitChainRepository.java:190) ~[core-0.1.0-28.jar:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
        at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) [?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
        at java.lang.Thread.run(Thread.java:829) [?:?]
Caused by: org.eclipse.jgit.errors.TransportException: git@ghe.snip/gitchainstore-synctest.git: Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
        at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:248) ~[org.eclipse.jgit.ssh.apache-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:1) ~[org.eclipse.jgit.ssh.apache-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:107) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:281) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:153) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:142) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:94) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1309) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:213) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        ... 8 more
Caused by: java.io.IOException: Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
        at org.eclipse.jgit.transport.sshd.SshdSessionFactory.register(SshdSessionFactory.java:272) ~[org.eclipse.jgit.ssh.apache-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:234) ~[org.eclipse.jgit.ssh.apache-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:1) ~[org.eclipse.jgit.ssh.apache-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:107) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:281) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:153) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:142) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:94) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1309) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:213) ~[org.eclipse.jgit-5.11.0.202103091610-r.jar:5.11.0.202103091610-r]
        ... 8 more

So in conclusion: What is the correct way to customize ssh session creation and make sure it persists through porcelain commands that call other commands? Is this a bug with jgit? Am I missing something, or doing something here wrong?


Solution

  • File sshDir = new File(FS.DETECTED.userHome(), "/.ssh");
    SshdSessionFactory sshSessionFactory = new SshdSessionFactoryBuilder()
            .setPreferredAuthentications("publickey")
            .setHomeDirectory(FS.DETECTED.userHome())
            .setSshDirectory(sshDir)
            .build(null);
    

    The above code worked for me, just need to have the id_rsa file in the .ssh folder. and the key has to be without a passphrase. Still looking for a way to pass a passphrase with a key.

    You can use this link for better understanding: https://medium.com/@pratikshende99/how-to-clone-git-repo-with-ssh-url-by-executing-command-through-java-using-processbuilder-e162010266f5