-
-
Save quidryan/5449155 to your computer and use it in GitHub Desktop.
buildscript { | |
repositories { | |
mavenCentral() | |
} | |
dependencies { | |
classpath 'org.ajoberstar:gradle-git:0.5.0' // not used in this example, but it's what brings in JGit | |
classpath 'com.jcraft:jsch.agentproxy.jsch:0.0.5' | |
classpath 'com.jcraft:jsch.agentproxy.usocket-jna:0.0.5' | |
classpath 'com.jcraft:jsch.agentproxy.sshagent:0.0.5' | |
} | |
} | |
import com.jcraft.jsch.*; | |
import com.jcraft.jsch.agentproxy.usocket.JNAUSocketFactory; | |
import com.jcraft.jsch.agentproxy.connector.SSHAgentConnector; | |
import org.eclipse.jgit.transport.JschConfigSessionFactory; | |
import org.eclipse.jgit.transport.OpenSshConfig; | |
import org.eclipse.jgit.transport.SshSessionFactory; | |
import org.eclipse.jgit.util.FS; | |
import java.io.FileInputStream; | |
def sessionFactory = new JschConfigSessionFactory() { | |
@Override | |
protected void configure(OpenSshConfig.Host host, Session session) { | |
// This can be removed, but the overriden method is required since JschConfigSessionFactory is abstract | |
session.setConfig("StrictHostKeyChecking", "false"); | |
} | |
@Override | |
protected JSch createDefaultJSch(FS fs) throws JSchException { | |
Connector con = null; | |
try { | |
if(SSHAgentConnector.isConnectorAvailable()){ | |
//USocketFactory usf = new JUnixDomainSocketFactory(); | |
USocketFactory usf = new JNAUSocketFactory(); | |
con = new SSHAgentConnector(usf); | |
} | |
} catch(AgentProxyException e){ | |
System.out.println(e); | |
} | |
if (con == null) { | |
return super.createDefaultJSch(fs) | |
} else { | |
final JSch jsch = new JSch(); | |
jsch.setConfig("PreferredAuthentications", "publickey"); | |
IdentityRepository irepo = new RemoteIdentityRepository(con); | |
jsch.setIdentityRepository(irepo); | |
knownHosts(jsch, fs) // private method from parent class, yeah for Groovy! | |
return jsch | |
} | |
} | |
} | |
SshSessionFactory.setInstance(sessionFactory) |
I managed to get it working with this code:
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.util.FS;
import com.jcraft.jsch.IdentityRepository;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.agentproxy.Connector;
import com.jcraft.jsch.agentproxy.ConnectorFactory;
import com.jcraft.jsch.agentproxy.RemoteIdentityRepository;
import com.jcraft.jsch.agentproxy.connector.SSHAgentConnector;
public static void trySetupSSHAgent() {
if (SSHAgentConnector.isConnectorAvailable()) {
try {
ConnectorFactory cf = ConnectorFactory.getDefault();
Connector connector = cf.createConnector();
IdentityRepository identityRepository = new RemoteIdentityRepository(connector);
JschConfigSessionFactory factory = new JschConfigSessionFactory() {
@Override
protected JSch createDefaultJSch(FS fs) throws JSchException {
JSch jsch = super.createDefaultJSch(fs);
jsch.setIdentityRepository(identityRepository);
return jsch;
}
};
SshSessionFactory.setInstance(factory);
} catch (Exception e) {
LOGGER.debug("Failed to setup ssh agent connector", e);
}
}
}
and these dependencies:
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
<version>5.12.0.202106070339-r</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch.agentproxy.connector-factory</artifactId>
<version>0.0.9</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch.agentproxy.jsch</artifactId>
<version>0.0.9</version>
</dependency>
This allows org.eclipse.jgit.ssh.jsch
v5.12 to use my ssh agent without me interfering with the jgit calls directly.
Tested on Windows 10, but the transitive dependencies should allow running this on any other system as well.
Note: The IdentityFile
property seems to take precedent over the ssh-agent setup.
Thus if the public key related to the password-encrypted IdentityFile
is accepted by the remote site, then you will get auth failures, because it doesn't know/request the private key's password.
I moved the following line to my .ssh/config
to avoid differences between my ssh.exe / git.exe and jgit.
JSch.setConfig("PreferredAuthentications", "publickey");
In case anybody stumbles upon this, jsch is kind of abandonware, the Eclipse Bugtracker says they want to move away to Apache MINA, but recent jgit versions today still use jsch with the aforementioned IdentityFile
issue. I fiddled around with different settings in my .ssh/config
, but other than commenting out the respective IdentityFile
entries nothing helped.
Here is the code in org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory
that creates the issue:
protected JSch getJSch(OpenSshConfig.Host hc, FS fs) throws JSchException {
if (defaultJSch == null) {
defaultJSch = createDefaultJSch(fs);
if (defaultJSch.getConfigRepository() == null) {
defaultJSch.setConfigRepository(
new JschBugFixingConfigRepository(config));
}
for (Object name : defaultJSch.getIdentityNames())
byIdentityFile.put((String) name, defaultJSch);
// ^^^ the identities coming from the agent are identified by their name (comment)
}
final File identityFile = hc.getIdentityFile();
// ^^^ here the identity file from .ssh/config is returned
if (identityFile == null)
return defaultJSch;
// ^^^ no identity file, no problem, our JSch instance with the remote identity repository is used
final String identityKey = identityFile.getAbsolutePath();
JSch jsch = byIdentityFile.get(identityKey);
// ^^^ now that can never succeed, because the comment of the identity is of course unequal to the path of it's file
if (jsch == null) {
jsch = new JSch();
configureJSch(jsch);
if (jsch.getConfigRepository() == null) {
jsch.setConfigRepository(defaultJSch.getConfigRepository());
}
jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
jsch.addIdentity(identityKey);
byIdentityFile.put(identityKey, jsch);
}
return jsch;
}
Reported upstream as eclipse-jgit/jgit#23
Since your code actually gave me a lot of help, in the spirit of OSS I'd like to contribute back :)
In my java code I managed it in that way:
In that way:
you don't need to explicitly invoke the
knownHosts(JSch, FS)
you won't miss the
identities(JSch, FS)
invocationHTH! :)