Open devvthedev opened 2 months ago
I don't know why the GitSshdSession
asks for a password up front. It should set appropriate FilePasswordProvider
and UserInteraction
implementations based on the JGit CredentialsProvider
instead (and they should be null-safe).
Note that the JGit CredentialsProvider
is not just for getting credentials. It is a general interface for user interaction similar to javax.security.auth.callback.CallbackHandler
. In JGit it is used for instance also to prompt the user what to do when an SSH server's host key is not known yet. (If CredentialsProvider.isInteractive() == true
.) So it might be a good idea anyway to provide a general CredentialsProvider
implementation. (Though I don't see what this GitSshdSession
does with it further on -- apparently nothing.)
As I mentioned in the JGit issue, another idea might be to use JGit's own binding to Apache MINA sshd.
Hi @tomaswolf
I've moved to using JGit's own binding to Apache MINA sshd as suggested but have some questions on using the SshdSessionFactoryBuilder.
I'm building a SshdSessionFactory where I supply a key pair programmatically (.setDefaultKeysProvider
).
However, the factory builder requires me to set both HomeDirectory and SshDirectory despite the Javadoc stating:
may be null, in which case the home directory as defined by FS.userHome() is assumed
If I pass null or omit these setters from the builder, I get a null pointer exception for both directories:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.io.File.isAbsolute()" because "homeDir" is null
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.io.File.isAbsolute()" because "sshDir" is null
Since I am providing the key pair programatically, I don't want to use the ssh directory on disk.
This has led to me having to specify a directory for these:
.setHomeDirectory(directory) // Need to set despite Javadoc
.setSshDirectory(directory) // Need to set despite Javadoc
Full code example setting key and passphrase on builder:
String privateKeyContent = "private key contents"
String passphrase "private key passphrase contents"
Iterable<KeyPair> keyPairs = SecurityUtils.loadKeyPairIdentities(null,
null,
new ByteArrayInputStream(privateKeyContent.getBytes()),
(session, resourceKey, retryIndex) -> passphrase);
SshdSessionFactory sshSessionFactory = new SshdSessionFactoryBuilder()
.setPreferredAuthentications("publickey")
.setDefaultKeysProvider(ignoredSshDirBecauseWeUseAnInMemorySetOfKeyPairs -> keyPairs)
.setHomeDirectory(directory) // Need to set despite Javadoc
.setSshDirectory(directory) // Need to set despite Javadoc
.build(null);
I've seen some examples across GitHub where the directory
specified in .setHomeDirectory(directory)
and .setSshDirectory(directory)
is the directory that is being cloned to but I'm not convinced this is the correct approach?
e.g. newSshdSessionFactory
method: https://github.com/thingsboard/thingsboard/blob/02d3382731a5540f60e530f9c9c90a935b131c67/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java#L503-L504
where directory is the repository directory: https://github.com/thingsboard/thingsboard/blob/master/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java#L295
What would you suggest here? Is there a way to omit setting the ssh folder and home directory?
Should I use a temporary directory instead?
Thanks
I don't know what other libraries using this do. Setting the ssh dir to a git working tree directory is a bit strange. I'd never set it to any directory where files may be written from somewhere over the internet, which is what a git fetch does.
It should be possible to run this without any files, but it's possible that we overlooked something with that home directory.
You would have to set a ServerKeyDatabase
that does not rely on ~/.ssh/known_hosts, and the SshConfigStore
should not rely on ~/.ssh/config. If it then still fails without setting these directories, open a bug in JGit.
Hi @tomaswolf
Thanks for getting back to me.
I've tried what you suggested:
String privateKeyContent = "private key contents"
String passphrase "private key passphrase contents"
Iterable<KeyPair> keyPairs = SecurityUtils.loadKeyPairIdentities(null,
null,
new ByteArrayInputStream(privateKeyContent.getBytes()),
(session, resourceKey, retryIndex) -> passphrase);
SshdSessionFactory sshSessionFactory = new SshdSessionFactoryBuilder()
.setPreferredAuthentications("publickey")
.setDefaultKeysProvider(ignoredSshDirBecauseWeUseAnInMemorySetOfKeyPairs -> keyPairs)
.setHomeDirectory(directory) // Need to set despite setting ConfigStoreFactory and ServerKeyDatabase up
.setSshDirectory(directory) // Need to set despite setting ConfigStoreFactory and ServerKeyDatabase up
.setConfigStoreFactory((ignoredHomeDir, ignoredConfigFile, ignoredLocalUserName) -> null)
.setServerKeyDatabase((ignoredHomeDir, ignoredSshDir) -> new ServerKeyDatabase() {
@Override
public List<PublicKey> lookup(String connectAddress, InetSocketAddress remoteAddress, Configuration config) {
return Collections.emptyList();
}
@Override
public boolean accept(String connectAddress, InetSocketAddress remoteAddress, PublicKey serverKey, Configuration config, CredentialsProvider provider) {
// Work out whether to accept the server key or not
// example: accept all server keys
return true;
}
})
.build(null);
When the build
method is invoked, it then sets the following on the SshdSessionFactory:
factory.setHomeDirectory(homeDirectory);
factory.setSshDirectory(sshDirectory);
The method signatures both specify @NonNull
e.g.
public void setHomeDirectory(@NonNull File homeDir) {
...
}
and
public void setSshDirectory(@NonNull File sshDir) {
...
}
I believe if these were nullable that would alleviate this? What do you think?
I will open a Bug issue in JGit describing this in more detail.
Thanks
Hello,
I'm using the following JGit and Apache MINA dependencies in my project:
I've created a default
org.apache.sshd.client.SshClient
with:SshClient sshClient = SshClient.setUpDefaultClient();
and then set an instance of the GitSshdSessionFactory on JGits SshSessionFactory:
SshSessionFactory.setInstance(new GitSshdSessionFactory(sshClient));
By default the sshClient is reading the SSH keys I have stored in the
~/.ssh
folder where theconfig
file has the following configuration:I am using a passphrase-less private SSH key.
The question I have is around JGits Git push command call when talking back to the remote repository.
For example,
git clone git@github.com:devvthedev/my-project.git
Made some changes to the repo
Opened the repo using JGit with
Git.open(...)
and get aGit
object backStaged the changes I made and committed them:
I get the following error:
If I provide a CredentialsProvider with the
git
user and no password:It works!
So my question is - why is a CredentialsProvider required given I have configured a
SshSessionFactory
instance?I've also raised this to JGit maintainers here
Thank you