Search code examples
javaspring-bootjschjava-securityspring-config

How do I fix "There are not any available ciphers" JSch exception when attempting to use Spring Config Server?


I am attempting to get an embedded Spring Config Server implementation working that reads configuration from GitHub. I'm following this tutorial:

https://mromeh.com/2017/12/04/spring-boot-with-embedded-config-server-via-spring-cloud-config/

I am getting the following Exception when my Spring Boot app tries to start up:

Caused by: com.jcraft.jsch.JSchException: There are not any available ciphers. at com.jcraft.jsch.Session.send_kexinit(Session.java:629) at com.jcraft.jsch.Session.connect(Session.java:307) at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:146) ... 23 more

The only interesting bit in my code that I see contributing to this is my bootstrap.yml file, which looks like this:

spring:
  application:
    name: DemoApplication.yml

---
spring:
  cloud:
    config:
      failFast: true
      server:
        bootstrap: true
        git:
          uri: [email protected]:mycompany/demo-config.git

I am running OpenJDK 8 v212 on MacOS, per running the following:

#> java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b03)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b03, mixed mode)

I've searched through the Spring code and documentation, and have yet to find anything about passing configuration parameters or adding code to affect how the Jsch session being used by Spring is constructed. Everything I find suggests that what I'm doing should just work.

I'm at a loss as to where to go from here. Can someone tell me what I'm missing...what I need to do to get past this problem?


Solution

  • To consolidate the comments earlier...

    Behind the scenes, Spring is using JGit to make the SSH connection. By default this uses JSch to make the SSH connection, which is configured by the ~/.ssh/config file.

    The wiki also has details of how to bypass JSch and use a native ssh command, the GIT_SSH environment variable can be set, e.g. to /usr/bin/ssh in OS X or Linux, or even something like C:\Program Files\TortoiseGit\bin\TortoiseGitPlink.exe.


    Following the comment about how to avoid a dependency on setting the environment variable, note how the GIT_SSH environment variable is checked using a SystemReader in the TransportGitSsh.useExtSession() method.

    This means one way would be to override the SystemReader class. It's not a small interface though, so would involve a fair bit of wrapping code - with the custom bit in getenv():

    import org.eclipse.jgit.lib.Config;
    import org.eclipse.jgit.storage.file.FileBasedConfig;
    import org.eclipse.jgit.util.FS;
    import org.eclipse.jgit.util.SystemReader;
    
    public class CustomSystemReader extends SystemReader {
        private final SystemReader systemReader;
    
        public CustomSystemReader(SystemReader systemReader) {
            this.systemReader = systemReader;
        }
    
        @Override
        public String getHostname() {
            return systemReader.getHostname();
        }
    
        @Override
        public String getenv(String variable) {
            if ("GIT_SSH".equals(variable))
                return "/usr/bin/ssh";
            return systemReader.getenv(variable);
        }
    
        @Override
        public String getProperty(String key) {
            return systemReader.getProperty(key);
        }
    
        @Override
        public FileBasedConfig openUserConfig(Config parent, FS fs) {
            return systemReader.openUserConfig(parent, fs);
        }
    
        @Override
        public FileBasedConfig openSystemConfig(Config parent, FS fs) {
            return systemReader.openSystemConfig(parent, fs);
        }
    
        @Override
        public long getCurrentTime() {
            return systemReader.getCurrentTime();
        }
    
        @Override
        public int getTimezone(long when) {
            return systemReader.getTimezone(when);
        }
    }
    

    Which can then be wired in like this:

        SystemReader.setInstance(
                new CustomSystemReader(SystemReader.getInstance()));