diff --git a/docs/src/main/asciidoc/spring-cloud-config.adoc b/docs/src/main/asciidoc/spring-cloud-config.adoc
index 1bbf605c..e96656ad 100644
--- a/docs/src/main/asciidoc/spring-cloud-config.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-config.adoc
@@ -428,7 +428,7 @@ The following table describes the SSH configuration properties.
|Valid SSH host key. Must be set if `hostKeyAlgorithm` is also set.
|*hostKeyAlgorithm*
-|One of `ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521`. Must be set if `hostKey` is also set.
+|One of `ssh-dss, ssh-rsa, ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521`. Must be set if `hostKey` is also set.
|*strictHostKeyChecking*
|`true` or `false`. If false, ignore errors with host key.
diff --git a/spring-cloud-config-dependencies/pom.xml b/spring-cloud-config-dependencies/pom.xml
index 1dce9296..e43a830c 100644
--- a/spring-cloud-config-dependencies/pom.xml
+++ b/spring-cloud-config-dependencies/pom.xml
@@ -6,16 +6,16 @@
spring-cloud-dependencies-parent
org.springframework.cloud
- 4.0.0-SNAPSHOT
+ 3.1.4-SNAPSHOT
spring-cloud-config-dependencies
- 4.0.0-SNAPSHOT
+ 3.1.4-SNAPSHOT
pom
spring-cloud-config-dependencies
Spring Cloud Config Dependencies
- 5.12.0.202106070339-r
+ 5.13.1.202206130422-r
3.0.0-M1
2.1.1.RELEASE
diff --git a/spring-cloud-config-server/pom.xml b/spring-cloud-config-server/pom.xml
index 548e7531..0d18879d 100644
--- a/spring-cloud-config-server/pom.xml
+++ b/spring-cloud-config-server/pom.xml
@@ -82,7 +82,7 @@
org.eclipse.jgit
- org.eclipse.jgit.ssh.jsch
+ org.eclipse.jgit.ssh.apache
org.yaml
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java
index aa2505ee..7861839f 100644
--- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentProperties.java
@@ -97,7 +97,7 @@ public class JGitEnvironmentProperties extends AbstractScmAccessorProperties
private String privateKey;
/**
- * One of ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or
+ * One of ssh-dss, ssh-rsa, ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or
* ecdsa-sha2-nistp521. Must be set if hostKey is also set.
*/
private String hostKeyAlgorithm;
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java
index e7b1bd9d..64de2249 100644
--- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java
@@ -25,7 +25,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import com.jcraft.jsch.Session;
import io.micrometer.observation.ObservationRegistry;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CloneCommand;
@@ -152,11 +151,8 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
private boolean tryMasterBranch;
- private final ObservationRegistry observationRegistry;
-
- public JGitEnvironmentRepository(ConfigurableEnvironment environment, JGitEnvironmentProperties properties,
- ObservationRegistry observationRegistry) {
- super(environment, properties, observationRegistry);
+ public JGitEnvironmentRepository(ConfigurableEnvironment environment, JGitEnvironmentProperties properties) {
+ super(environment, properties);
this.cloneOnStart = properties.isCloneOnStart();
this.defaultLabel = properties.getDefaultLabel();
this.forcePull = properties.isForcePull();
@@ -166,7 +162,6 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
this.skipSslValidation = properties.isSkipSslValidation();
this.gitFactory = new JGitFactory(properties.isCloneSubmodules());
this.tryMasterBranch = properties.isTryMasterBranch();
- this.observationRegistry = observationRegistry;
}
public boolean isTryMasterBranch() {
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshSessionFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshSessionFactory.java
new file mode 100644
index 00000000..94d81a90
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshSessionFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.config.server.ssh;
+
+import java.io.File;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
+import org.eclipse.jgit.transport.SshConfigStore;
+import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
+
+import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties;
+
+public class FileBasedSshSessionFactory extends SshdSessionFactory {
+
+ private static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
+
+ private static final String YES_OPTION = "yes";
+
+ private static final String NO_OPTION = "no";
+
+ private final JGitEnvironmentProperties sshUriProperties;
+
+ public FileBasedSshSessionFactory(JGitEnvironmentProperties sshUriProperties) {
+ this.sshUriProperties = sshUriProperties;
+ }
+
+ @Override
+ protected SshConfigStore createSshConfigStore(File homeDir, File configFile, String localUserName) {
+ return configFile == null ? null : new OpenSshConfigFile(homeDir, configFile, localUserName) {
+
+ @Override
+ public HostEntry lookup(@NonNull String hostName, int port, String userName) {
+ HostEntry hostEntry = super.lookup(hostName, port, userName);
+
+ hostEntry.setValue(STRICT_HOST_KEY_CHECKING,
+ sshUriProperties.isStrictHostKeyChecking() ? YES_OPTION : NO_OPTION);
+
+ return hostEntry;
+ }
+ };
+ }
+
+}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshTransportConfigCallback.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshTransportConfigCallback.java
index 49c42500..fa408c64 100644
--- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshTransportConfigCallback.java
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/FileBasedSshTransportConfigCallback.java
@@ -16,11 +16,8 @@
package org.springframework.cloud.config.server.ssh;
-import com.jcraft.jsch.Session;
import org.eclipse.jgit.api.TransportConfigCallback;
-import org.eclipse.jgit.transport.JschConfigSessionFactory;
-import org.eclipse.jgit.transport.OpenSshConfig;
-import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.SshTransport;
import org.eclipse.jgit.transport.Transport;
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties;
@@ -33,7 +30,7 @@ import org.springframework.cloud.config.server.environment.MultipleJGitEnvironme
*/
public class FileBasedSshTransportConfigCallback implements TransportConfigCallback {
- private MultipleJGitEnvironmentProperties sshUriProperties;
+ private final MultipleJGitEnvironmentProperties sshUriProperties;
public FileBasedSshTransportConfigCallback(MultipleJGitEnvironmentProperties sshUriProperties) {
this.sshUriProperties = sshUriProperties;
@@ -45,14 +42,9 @@ public class FileBasedSshTransportConfigCallback implements TransportConfigCallb
@Override
public void configure(Transport transport) {
- SshSessionFactory.setInstance(new JschConfigSessionFactory() {
- @Override
- protected void configure(OpenSshConfig.Host hc, Session session) {
- session.setConfig("StrictHostKeyChecking",
- FileBasedSshTransportConfigCallback.this.sshUriProperties.isStrictHostKeyChecking() ? "yes"
- : "no");
- }
- });
+ if (transport instanceof SshTransport) {
+ ((SshTransport) transport).setSshSessionFactory(new FileBasedSshSessionFactory(sshUriProperties));
+ }
}
}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java
index 64144d3b..32780dc5 100644
--- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/HostKeyAlgoSupportedValidator.java
@@ -48,8 +48,8 @@ public class HostKeyAlgoSupportedValidator
private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git.";
- private static final Set VALID_HOST_KEY_ALGORITHMS = new LinkedHashSet<>(
- Arrays.asList("ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"));
+ private static final Set VALID_HOST_KEY_ALGORITHMS = new LinkedHashSet<>(Arrays.asList("ssh-dss", "ssh-rsa",
+ "ssh-ed25519", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"));
private final SshPropertyValidator sshPropertyValidator = new SshPropertyValidator();
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/KeyPairUtils.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/KeyPairUtils.java
new file mode 100644
index 00000000..2f890e62
--- /dev/null
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/KeyPairUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.config.server.ssh;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Collection;
+
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.io.resource.AbstractIoResource;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+final class KeyPairUtils {
+
+ private static final KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceParser();
+
+ private KeyPairUtils() {
+
+ }
+
+ static Collection load(SessionContext session, String privateKey)
+ throws IOException, GeneralSecurityException {
+ return loader.loadKeyPairs(session, new StringResource(privateKey), null);
+ }
+
+ static boolean isValid(String privateKey) {
+ try {
+ return !KeyPairUtils.load(null, privateKey).isEmpty();
+ }
+ catch (IOException | GeneralSecurityException ignored) {
+ return false;
+ }
+ }
+
+ private static class StringResource extends AbstractIoResource {
+
+ protected StringResource(String resourceValue) {
+ super(String.class, resourceValue);
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return new ByteArrayInputStream(this.getResourceValue().getBytes());
+ }
+
+ }
+
+}
diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PrivateKeyValidator.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PrivateKeyValidator.java
index d361f9f0..d775b0ce 100644
--- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PrivateKeyValidator.java
+++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PrivateKeyValidator.java
@@ -20,9 +20,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.KeyPair;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
@@ -37,7 +34,7 @@ import static org.springframework.util.StringUtils.hasText;
/**
* JSR-303 Cross Field validator that ensures that an
* {@link MultipleJGitEnvironmentProperties} bean for the constraints: - Private key is
- * present and can be correctly parsed using {@link com.jcraft.jsch.KeyPair}
+ * present and can be correctly parsed using {@link java.security.KeyPair}
*
* Beans annotated with {@link PrivateKeyValidator} and {@link Validated} will have the
* constraints applied.
@@ -86,16 +83,14 @@ public class PrivateKeyValidator implements ConstraintValidator sshKeysByHostname;
- private final JSch jSch;
+ public PropertyBasedSshSessionFactory(Map sshKeysByHostname) {
+ super(new JGitKeyCache(), new HttpProxyDataFactory(sshKeysByHostname));
- public PropertyBasedSshSessionFactory(Map sshKeysByHostname, JSch jSch) {
this.sshKeysByHostname = sshKeysByHostname;
- this.jSch = jSch;
+ assert this.sshKeysByHostname.entrySet().size() > 0;
}
@Override
- protected void configure(Host hc, Session session) {
- JGitEnvironmentProperties sshProperties = this.sshKeysByHostname.get(hc.getHostName());
- String hostKeyAlgorithm = sshProperties.getHostKeyAlgorithm();
- if (hostKeyAlgorithm != null) {
- session.setConfig(SERVER_HOST_KEY, hostKeyAlgorithm);
- }
- if (sshProperties.getHostKey() == null || !sshProperties.isStrictHostKeyChecking()) {
- session.setConfig(STRICT_HOST_KEY_CHECKING, NO_OPTION);
- }
- else {
- session.setConfig(STRICT_HOST_KEY_CHECKING, YES_OPTION);
- }
- String preferredAuthentications = sshProperties.getPreferredAuthentications();
- if (preferredAuthentications != null) {
- session.setConfig(PREFERRED_AUTHENTICATIONS, preferredAuthentications);
- }
+ protected SshConfigStore createSshConfigStore(File homeDir, File configFile, String localUserName) {
+ return new SshConfigStore() {
- ProxyHostProperties proxyHostProperties = sshProperties.getProxy().get(ProxyHostProperties.ProxyForScheme.HTTP);
- if (proxyHostProperties != null && proxyHostProperties.connectionInformationProvided()) {
- ProxyHTTP proxy = createProxy(proxyHostProperties);
- proxy.setUserPasswd(proxyHostProperties.getUsername(), proxyHostProperties.getPassword());
- session.setProxy(proxy);
- }
- }
+ @Override
+ public HostConfig lookup(@NonNull String hostName, int port, String userName) {
+ OpenSshConfigFile.HostEntry hostEntry = new OpenSshConfigFile.HostEntry();
- protected ProxyHTTP createProxy(ProxyHostProperties proxyHostProperties) {
- return new ProxyHTTP(proxyHostProperties.getHost(), proxyHostProperties.getPort());
+ return updateIfNeeded(hostEntry, hostName);
+ }
+
+ private OpenSshConfigFile.HostEntry updateIfNeeded(OpenSshConfigFile.HostEntry hostEntry, String hostName) {
+ JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(hostName);
+ if (sshProperties == null) {
+ return hostEntry;
+ }
+
+ if (sshProperties.getHostKey() == null || !sshProperties.isStrictHostKeyChecking()) {
+ hostEntry.setValue(STRICT_HOST_KEY_CHECKING, NO_OPTION);
+ }
+ else {
+ hostEntry.setValue(STRICT_HOST_KEY_CHECKING, YES_OPTION);
+ }
+
+ String preferredAuthentications = sshProperties.getPreferredAuthentications();
+ if (preferredAuthentications != null) {
+ hostEntry.setValue(PREFERRED_AUTHENTICATIONS, preferredAuthentications);
+ }
+ return hostEntry;
+ }
+ };
}
@Override
- protected Session createSession(Host hc, String user, String host, int port, FS fs) throws JSchException {
- if (this.sshKeysByHostname.containsKey(host)) {
- JGitEnvironmentProperties sshUriProperties = this.sshKeysByHostname.get(host);
- this.jSch.addIdentity(host, sshUriProperties.getPrivateKey().getBytes(), null, null);
- if (sshUriProperties.getKnownHostsFile() != null) {
- this.jSch.setKnownHosts(sshUriProperties.getKnownHostsFile());
+ protected File getSshConfig(File dir) {
+ // Do not use a config file.
+ return null;
+ }
+
+ @Override
+ protected ServerKeyDatabase getServerKeyDatabase(File homeDir, File dir) {
+ return new ServerKeyDatabase() {
+ @Override
+ public List lookup(String connectAddress, InetSocketAddress remoteAddress,
+ Configuration config) {
+
+ JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName());
+ if (sshProperties == null) {
+ return Collections.emptyList();
+ }
+
+ List knownHostFiles = getKnownHostFiles(sshProperties);
+ List publicKeys = new OpenSshServerKeyDatabase(false, knownHostFiles).lookup(connectAddress,
+ remoteAddress, config);
+
+ PublicKey publicKey = getHostKey(sshProperties);
+ if (publicKey != null) {
+ publicKeys.add(publicKey);
+ }
+
+ return publicKeys;
}
- if (sshUriProperties.getHostKey() != null) {
- HostKey hostkey = new HostKey(host, Base64.decode(sshUriProperties.getHostKey()));
- this.jSch.getHostKeyRepository().add(hostkey, null);
+
+ @Override
+ public boolean accept(String connectAddress, InetSocketAddress remoteAddress, PublicKey serverKey,
+ Configuration config, CredentialsProvider provider) {
+ if (isNotStrictHostKeyChecking(remoteAddress.getHostName())) {
+ return true;
+ }
+
+ List knownServerKeys = lookup(connectAddress, remoteAddress, config);
+
+ return KeyUtils.findMatchingKey(serverKey, knownServerKeys) != null;
}
- return this.jSch.getSession(user, host, port);
+
+ private boolean isNotStrictHostKeyChecking(String hostName) {
+ JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(hostName);
+ if (sshProperties == null) {
+ return false;
+ }
+ return !sshProperties.isStrictHostKeyChecking();
+ }
+
+ private PublicKey getHostKey(JGitEnvironmentProperties sshProperties) {
+ String hostKey = sshProperties.getHostKey();
+ String hostKeyAlgorithm = sshProperties.getHostKeyAlgorithm();
+ if (!StringUtils.hasText(hostKey) || !StringUtils.hasText(hostKeyAlgorithm)) {
+ return null;
+ }
+
+ try {
+ return AuthorizedKeyEntry.parseAuthorizedKeyEntry(hostKeyAlgorithm + " " + hostKey)
+ .resolvePublicKey(null, null);
+ }
+ catch (IOException | GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private List getKnownHostFiles(JGitEnvironmentProperties sshProperties) {
+ if (sshProperties.getKnownHostsFile() == null) {
+ return Collections.emptyList();
+ }
+ else {
+ return Collections.singletonList(Paths.get(sshProperties.getKnownHostsFile()));
+ }
+ }
+
+ };
+ }
+
+ @Override
+ protected Iterable getDefaultKeys(File dir) {
+ return new SingleKeyIdentityProvider(sshKeysByHostname);
+ }
+
+ private final static class SingleKeyIdentityProvider implements KeyIdentityProvider, Iterable {
+
+ private final Map sshKeysByHostname;
+
+ private SingleKeyIdentityProvider(Map sshKeysByHostname) {
+ this.sshKeysByHostname = sshKeysByHostname;
}
- throw new JSchException("no keys configured for hostname " + host);
+
+ @Override
+ public Iterator iterator() {
+ throw new UnsupportedOperationException("Should not be called");
+ }
+
+ @Override
+ public Iterable loadKeys(SessionContext session) throws IOException, GeneralSecurityException {
+ SshdSocketAddress remoteAddress = SshdSocketAddress.toSshdSocketAddress(session.getRemoteAddress());
+ JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName());
+
+ return sshProperties == null ? Collections.emptyList()
+ : KeyPairUtils.load(session, sshProperties.getPrivateKey());
+ }
+
+ }
+
+ private final static class HttpProxyDataFactory implements ProxyDataFactory {
+
+ private final Map sshKeysByHostname;
+
+ private HttpProxyDataFactory(Map sshKeysByHostname) {
+ this.sshKeysByHostname = sshKeysByHostname;
+ }
+
+ @Override
+ public ProxyData get(InetSocketAddress remoteAddress) {
+ JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName());
+ ProxyHostProperties proxyHostProperties = sshProperties.getProxy()
+ .get(ProxyHostProperties.ProxyForScheme.HTTP);
+
+ if (proxyHostProperties == null || !proxyHostProperties.connectionInformationProvided()) {
+ return null;
+ }
+
+ Proxy proxy = new Proxy(Proxy.Type.HTTP,
+ new InetSocketAddress(proxyHostProperties.getHost(), proxyHostProperties.getPort()));
+ return new ProxyData(proxy, proxyHostProperties.getUsername(),
+ proxyHostProperties.getPassword().toCharArray());
+ }
+
}
}
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java
index 13bf1711..40fcf444 100644
--- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/TransportConfigurationIntegrationTests.java
@@ -18,31 +18,32 @@ package org.springframework.cloud.config.server;
import java.io.File;
import java.lang.reflect.Method;
+import java.net.URISyntaxException;
-import com.jcraft.jsch.Session;
import org.eclipse.jgit.api.TransportConfigCallback;
-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 org.eclipse.jgit.transport.FetchConnection;
+import org.eclipse.jgit.transport.PushConnection;
+import org.eclipse.jgit.transport.SshConfigStore;
+import org.eclipse.jgit.transport.SshTransport;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties;
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository;
+import org.springframework.cloud.config.server.ssh.FileBasedSshSessionFactory;
import org.springframework.cloud.config.server.ssh.FileBasedSshTransportConfigCallback;
import org.springframework.cloud.config.server.ssh.PropertiesBasedSshTransportConfigCallback;
+import org.springframework.cloud.config.server.ssh.PropertyBasedSshSessionFactory;
import org.springframework.cloud.config.server.ssh.SshPropertyValidator;
import org.springframework.cloud.config.server.test.TestConfigServerApplication;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
/**
* Integration tests for property based SSH config support.
@@ -65,12 +66,22 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void propertyBasedTransportCallbackIsConfigured() throws Exception {
+ public void propertyBasedTransportCallbackIsConfigured() {
TransportConfigCallback transportConfigCallback = this.jGitEnvironmentRepository
.getTransportConfigCallback();
assertThat(transportConfigCallback).isInstanceOf(PropertiesBasedSshTransportConfigCallback.class);
}
+ @Test
+ public void propertyBasedSessionFactoryIsUsedForSshTransports() throws Exception {
+ this.jGitEnvironmentRepository.afterPropertiesSet();
+
+ SshTransport sshTransport = DummySshTransport.newInstance();
+ this.jGitEnvironmentRepository.getTransportConfigCallback().configure(sshTransport);
+
+ assertThat(sshTransport.getSshSessionFactory()).isInstanceOf(PropertyBasedSshSessionFactory.class);
+ }
+
}
@RunWith(SpringRunner.class)
@@ -85,12 +96,22 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void propertyBasedTransportCallbackIsConfigured() throws Exception {
+ public void propertyBasedTransportCallbackIsConfigured() {
TransportConfigCallback transportConfigCallback = this.jGitEnvironmentRepository
.getTransportConfigCallback();
assertThat(transportConfigCallback).isInstanceOf(PropertiesBasedSshTransportConfigCallback.class);
}
+ @Test
+ public void propertyBasedSessionFactoryIsUsedForSshTransports() throws Exception {
+ this.jGitEnvironmentRepository.afterPropertiesSet();
+
+ SshTransport sshTransport = DummySshTransport.newInstance();
+ this.jGitEnvironmentRepository.getTransportConfigCallback().configure(sshTransport);
+
+ assertThat(sshTransport.getSshSessionFactory()).isInstanceOf(PropertyBasedSshSessionFactory.class);
+ }
+
}
}
@@ -109,7 +130,7 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void privateKeyPropertyWithLineBreaks() throws Exception {
+ public void privateKeyPropertyWithLineBreaks() {
TransportConfigCallback callback = this.jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(callback).isInstanceOf(PropertiesBasedSshTransportConfigCallback.class);
@@ -132,7 +153,7 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void privateKeyPropertyWithLineBreaks() throws Exception {
+ public void privateKeyPropertyWithLineBreaks() {
TransportConfigCallback callback = this.jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(callback).isInstanceOf(PropertiesBasedSshTransportConfigCallback.class);
@@ -159,7 +180,7 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void sshPropertiesWithinNestedRepo() throws Exception {
+ public void sshPropertiesWithinNestedRepo() {
TransportConfigCallback callback = this.jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(callback).isInstanceOf(PropertiesBasedSshTransportConfigCallback.class);
@@ -187,7 +208,7 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void sshPropertiesWithinNestedRepo() throws Exception {
+ public void sshPropertiesWithinNestedRepo() {
TransportConfigCallback callback = this.jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(callback).isInstanceOf(PropertiesBasedSshTransportConfigCallback.class);
@@ -219,42 +240,44 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void fileBasedTransportCallbackIsConfigured() throws Exception {
+ public void fileBasedTransportCallbackIsConfigured() {
TransportConfigCallback transportConfigCallback = this.jGitEnvironmentRepository
.getTransportConfigCallback();
assertThat(transportConfigCallback).isInstanceOf(FileBasedSshTransportConfigCallback.class);
}
+ @Test
+ public void fileBasedSessionFactoryIsUsedForSshTransports() throws Exception {
+ this.jGitEnvironmentRepository.afterPropertiesSet();
+
+ SshTransport sshTransport = DummySshTransport.newInstance();
+ this.jGitEnvironmentRepository.getTransportConfigCallback().configure(sshTransport);
+
+ assertThat(sshTransport.getSshSessionFactory()).isInstanceOf(FileBasedSshSessionFactory.class);
+ }
+
@Test
public void strictHostKeyCheckShouldCheck() throws Exception {
- String uri = "git+ssh://git@somegitserver/somegitrepo";
- SshSessionFactory.setInstance(null);
- this.jGitEnvironmentRepository.setUri(uri);
- this.jGitEnvironmentRepository.setBasedir(new File("./mybasedir"));
assertThat(this.jGitEnvironmentRepository.isStrictHostKeyChecking()).isTrue();
- this.jGitEnvironmentRepository.setCloneOnStart(true);
- try {
- // this will throw but we don't care about connecting.
- this.jGitEnvironmentRepository.afterPropertiesSet();
- }
- catch (Exception e) {
- final OpenSshConfig.Host hc = OpenSshConfig.get(FS.detect()).lookup("github.com");
- JschConfigSessionFactory factory = (JschConfigSessionFactory) SshSessionFactory.getInstance();
- // There's no public method that can be used to inspect the ssh
- // configuration, so we'll reflect
- // the configure method to allow us to check that the config
- // property is set as expected.
- Method configure = factory.getClass().getDeclaredMethod("configure", OpenSshConfig.Host.class,
- Session.class);
- configure.setAccessible(true);
- Session session = mock(Session.class);
- ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class);
- ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class);
- configure.invoke(factory, hc, session);
- verify(session).setConfig(keyCaptor.capture(), valueCaptor.capture());
- configure.setAccessible(false);
- assertThat("yes".equals(valueCaptor.getValue())).isTrue();
- }
+
+ this.jGitEnvironmentRepository.afterPropertiesSet();
+ SshTransport sshTransport = DummySshTransport.newInstance();
+ this.jGitEnvironmentRepository.getTransportConfigCallback().configure(sshTransport);
+
+ SshdSessionFactory factory = (SshdSessionFactory) sshTransport.getSshSessionFactory();
+ // There's no public method that can be used to inspect the ssh
+ // configuration, so we'll reflect
+ // the createSshConfigStore method to allow us to check that the config
+ // property is set as expected.
+ Method createSshConfigStore = factory.getClass().getDeclaredMethod("createSshConfigStore", File.class,
+ File.class, String.class);
+ createSshConfigStore.setAccessible(true);
+ SshConfigStore configStore = (SshConfigStore) createSshConfigStore.invoke(factory, new File("."),
+ new File("."), "local-username");
+ createSshConfigStore.setAccessible(false);
+ assertThat("yes"
+ .equals(configStore.lookup("gitserver.com", 22, "username").getValue("StrictHostKeyChecking")))
+ .isTrue();
}
}
@@ -272,44 +295,73 @@ public class TransportConfigurationIntegrationTests {
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
- public void fileBasedTransportCallbackIsConfigured() throws Exception {
+ public void fileBasedTransportCallbackIsConfigured() {
TransportConfigCallback transportConfigCallback = this.jGitEnvironmentRepository
.getTransportConfigCallback();
assertThat(transportConfigCallback).isInstanceOf(FileBasedSshTransportConfigCallback.class);
}
@Test
- public void strictHostKeyCheckShouldCheck() throws Exception {
- String uri = "git+ssh://git@somegitserver/somegitrepo";
- SshSessionFactory.setInstance(null);
- this.jGitEnvironmentRepository.setUri(uri);
- this.jGitEnvironmentRepository.setBasedir(new File("./mybasedir"));
- assertThat(this.jGitEnvironmentRepository.isStrictHostKeyChecking()).isTrue();
- this.jGitEnvironmentRepository.setCloneOnStart(true);
- try {
- // this will throw but we don't care about connecting.
- this.jGitEnvironmentRepository.afterPropertiesSet();
- }
- catch (Exception e) {
- final OpenSshConfig.Host hc = OpenSshConfig.get(FS.detect()).lookup("github.com");
- JschConfigSessionFactory factory = (JschConfigSessionFactory) SshSessionFactory.getInstance();
- // There's no public method that can be used to inspect the ssh
- // configuration, so we'll reflect
- // the configure method to allow us to check that the config
- // property is set as expected.
- Method configure = factory.getClass().getDeclaredMethod("configure", OpenSshConfig.Host.class,
- Session.class);
- configure.setAccessible(true);
- Session session = mock(Session.class);
- ArgumentCaptor keyCaptor = ArgumentCaptor.forClass(String.class);
- ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class);
- configure.invoke(factory, hc, session);
- verify(session).setConfig(keyCaptor.capture(), valueCaptor.capture());
- configure.setAccessible(false);
- assertThat("yes".equals(valueCaptor.getValue())).isTrue();
- }
+ public void fileBasedSessionFactoryIsUsedForSshTransports() throws Exception {
+ this.jGitEnvironmentRepository.afterPropertiesSet();
+
+ SshTransport sshTransport = DummySshTransport.newInstance();
+ this.jGitEnvironmentRepository.getTransportConfigCallback().configure(sshTransport);
+
+ assertThat(sshTransport.getSshSessionFactory()).isInstanceOf(FileBasedSshSessionFactory.class);
}
+ @Test
+ public void strictHostKeyCheckShouldCheck() throws Exception {
+ assertThat(this.jGitEnvironmentRepository.isStrictHostKeyChecking()).isTrue();
+
+ this.jGitEnvironmentRepository.afterPropertiesSet();
+ SshTransport sshTransport = DummySshTransport.newInstance();
+ this.jGitEnvironmentRepository.getTransportConfigCallback().configure(sshTransport);
+
+ SshdSessionFactory factory = (SshdSessionFactory) sshTransport.getSshSessionFactory();
+ // There's no public method that can be used to inspect the ssh
+ // configuration, so we'll reflect
+ // the createSshConfigStore method to allow us to check that the config
+ // property is set as expected.
+ Method createSshConfigStore = factory.getClass().getDeclaredMethod("createSshConfigStore", File.class,
+ File.class, String.class);
+ createSshConfigStore.setAccessible(true);
+ SshConfigStore configStore = (SshConfigStore) createSshConfigStore.invoke(factory, new File("."),
+ new File("."), "local-username");
+ createSshConfigStore.setAccessible(false);
+ assertThat("yes"
+ .equals(configStore.lookup("gitserver.com", 22, "username").getValue("StrictHostKeyChecking")))
+ .isTrue();
+ }
+
+ }
+
+ }
+
+ private static class DummySshTransport extends SshTransport {
+
+ DummySshTransport(String uri) throws URISyntaxException {
+ super(new URIish(uri));
+ }
+
+ @Override
+ public FetchConnection openFetch() {
+ return null;
+ }
+
+ @Override
+ public PushConnection openPush() {
+ return null;
+ }
+
+ public static SshTransport newInstance() {
+ try {
+ return new DummySshTransport("git+ssh://git@uri");
+ }
+ catch (URISyntaxException e) {
+ throw new IllegalStateException("Not expected", e);
+ }
}
}
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/FileBasedSshSessionFactoryTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/FileBasedSshSessionFactoryTest.java
new file mode 100644
index 00000000..17dcbc26
--- /dev/null
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/FileBasedSshSessionFactoryTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.config.server.ssh;
+
+import java.io.File;
+
+import org.eclipse.jgit.transport.SshConfigStore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests for file based SSH config processor.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FileBasedSshSessionFactoryTest {
+
+ private FileBasedSshSessionFactory factory;
+
+ @Test
+ public void strictHostKeyCheckingIsOptional() {
+ JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
+ sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git");
+ sshKey.setStrictHostKeyChecking(false);
+ setupSessionFactory(sshKey);
+
+ SshConfigStore.HostConfig sshConfig = getSshHostConfig("gitlab.example.local");
+
+ assertThat(sshConfig.getValue("StrictHostKeyChecking")).isEqualTo("no");
+ }
+
+ @Test
+ public void strictHostKeyCheckingIsUsed() {
+ JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
+ sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git");
+ setupSessionFactory(sshKey);
+
+ SshConfigStore.HostConfig sshConfig = getSshHostConfig("gitlab.example.local");
+
+ assertThat(sshConfig.getValue("StrictHostKeyChecking")).isEqualTo("yes");
+ }
+
+ @Test
+ public void handlesNullConfigFile() {
+ JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
+ sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git");
+ setupSessionFactory(sshKey);
+
+ SshConfigStore configStore = factory.createSshConfigStore(new File("dummy"), null, "localUserName");
+
+ assertThat(configStore).isNull();
+ }
+
+ private SshConfigStore.HostConfig getSshHostConfig(String hostName) {
+ return factory.createSshConfigStore(new File("dummy"), new File("dummy"), "localUserName").lookup(hostName, 22,
+ "userName");
+ }
+
+ private void setupSessionFactory(JGitEnvironmentProperties sshKey) {
+ this.factory = new FileBasedSshSessionFactory(sshKey);
+ }
+
+}
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java
index 789c28c3..7b060505 100644
--- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactoryTest.java
@@ -17,21 +17,35 @@
package org.springframework.cloud.config.server.ssh;
import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.net.InetSocketAddress;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Spliterator;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
-import com.jcraft.jsch.HostKey;
-import com.jcraft.jsch.HostKeyRepository;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.ProxyHTTP;
-import com.jcraft.jsch.Session;
-import org.eclipse.jgit.transport.OpenSshConfig.Host;
+import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.session.SessionContext;
+import org.eclipse.jgit.transport.SshConfigStore;
+import org.eclipse.jgit.transport.sshd.ProxyData;
+import org.eclipse.jgit.transport.sshd.ProxyDataFactory;
+import org.eclipse.jgit.transport.sshd.ServerKeyDatabase;
+import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties;
@@ -40,9 +54,7 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
@@ -63,21 +75,6 @@ public class PropertyBasedSshSessionFactoryTest {
private PropertyBasedSshSessionFactory factory;
- @Mock
- private Host hc;
-
- @Mock
- private Session session;
-
- @Mock
- private JSch jSch;
-
- @Mock
- private HostKeyRepository hostKeyRepository;
-
- @Mock
- private ProxyHTTP proxyMock;
-
public static String getResourceAsString(String path) {
try {
Resource resource = new ClassPathResource(path);
@@ -102,10 +99,9 @@ public class PropertyBasedSshSessionFactoryTest {
sshKey.setPrivateKey(PRIVATE_KEY);
setupSessionFactory(sshKey);
- this.factory.configure(this.hc, this.session);
+ SshConfigStore.HostConfig sshConfig = getSshHostConfig("gitlab.example.local");
- verify(this.session).setConfig("StrictHostKeyChecking", "no");
- verifyNoMoreInteractions(this.session);
+ assertThat(sshConfig.getValue("StrictHostKeyChecking")).isEqualTo("no");
}
@Test
@@ -116,10 +112,25 @@ public class PropertyBasedSshSessionFactoryTest {
sshKey.setPrivateKey(PRIVATE_KEY);
setupSessionFactory(sshKey);
- this.factory.configure(this.hc, this.session);
+ SshConfigStore.HostConfig sshConfig = getSshHostConfig("gitlab.example.local");
- verify(this.session).setConfig("StrictHostKeyChecking", "yes");
- verifyNoMoreInteractions(this.session);
+ assertThat(sshConfig.getValue("StrictHostKeyChecking")).isEqualTo("yes");
+ }
+
+ @Test
+ public void sshConfigIsUsedForRelevantHostOnly() {
+ JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
+ sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git");
+ sshKey.setHostKeyAlgorithm(HOST_KEY_ALGORITHM);
+ sshKey.setHostKey(HOST_KEY);
+ sshKey.setPrivateKey(PRIVATE_KEY);
+ setupSessionFactory(sshKey);
+ PublicKey configuredKey = toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM);
+
+ SshConfigStore.HostConfig sshConfig = getSshHostConfig("another.host");
+
+ assertThat(sshConfig.getValue("StrictHostKeyChecking")).isNull();
+ assertThat(isKnownKeyForHost(configuredKey, "another.host")).isFalse();
}
@Test
@@ -131,37 +142,40 @@ public class PropertyBasedSshSessionFactoryTest {
sshKey.setPrivateKey(PRIVATE_KEY);
setupSessionFactory(sshKey);
- this.factory.configure(this.hc, this.session);
- verify(this.session).setConfig("server_host_key", HOST_KEY_ALGORITHM);
- verify(this.session).setConfig("StrictHostKeyChecking", "yes");
- verifyNoMoreInteractions(this.session);
+ PublicKey hostKey = getSshHostKey("gitlab.example.local");
+
+ assertThat(hostKey).isNotNull();
+ assertThat(hostKey.getAlgorithm()).isEqualTo(toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM).getAlgorithm());
}
@Test
- public void privateKeyIsUsed() throws Exception {
+ public void privateKeyIsUsed() {
JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git");
sshKey.setPrivateKey(PRIVATE_KEY);
setupSessionFactory(sshKey);
- this.factory.createSession(this.hc, null, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 22, null);
- verify(this.jSch).addIdentity("gitlab.example.local", PRIVATE_KEY.getBytes(), null, null);
+ PrivateKey privateKey = getSshPrivateKey("gitlab.example.local");
+
+ assertThat(privateKey).isNotNull();
+ assertThat(privateKey).isEqualTo(toPrivateKey(PRIVATE_KEY));
}
@Test
- public void hostKeyIsUsed() throws Exception {
+ public void hostKeyIsUsed() {
JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git");
+ sshKey.setHostKeyAlgorithm(HOST_KEY_ALGORITHM);
sshKey.setHostKey(HOST_KEY);
sshKey.setPrivateKey(PRIVATE_KEY);
setupSessionFactory(sshKey);
+ PublicKey configuredKey = toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM);
- this.factory.createSession(this.hc, null, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 22, null);
- ArgumentCaptor captor = ArgumentCaptor.forClass(HostKey.class);
- verify(this.hostKeyRepository).add(captor.capture(), isNull());
- HostKey hostKey = captor.getValue();
- assertThat(hostKey.getHost()).isEqualTo("gitlab.example.local");
- assertThat(hostKey.getKey()).isEqualTo(HOST_KEY);
+ PublicKey knownHostKey = getSshHostKey("gitlab.example.local");
+
+ assertThat(knownHostKey).isNotNull();
+ assertThat(knownHostKey).isEqualTo(configuredKey);
+ assertThat(isKnownKeyForHost(configuredKey, "gitlab.example.local")).isTrue();
}
@Test
@@ -172,30 +186,32 @@ public class PropertyBasedSshSessionFactoryTest {
sshKey.setPreferredAuthentications("password,keyboard-interactive");
setupSessionFactory(sshKey);
- this.factory.configure(this.hc, this.session);
- verify(this.session).setConfig("PreferredAuthentications", "password,keyboard-interactive");
- verify(this.session).setConfig("StrictHostKeyChecking", "no");
- verifyNoMoreInteractions(this.session);
+ SshConfigStore.HostConfig sshConfig = getSshHostConfig("gitlab.example.local");
+
+ assertThat(sshConfig.getValue("PreferredAuthentications")).isEqualTo("password,keyboard-interactive");
+ assertThat(sshConfig.getValue("StrictHostKeyChecking")).isEqualTo("no");
}
@Test
- public void customKnownHostsFileIsUsed() throws Exception {
+ public void customKnownHostsFileIsUsed() throws IOException {
JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git");
sshKey.setPrivateKey(PRIVATE_KEY);
- sshKey.setKnownHostsFile("/ssh/known_hosts");
+ sshKey.setKnownHostsFile(new ClassPathResource("/ssh/known_hosts").getFile().getPath());
setupSessionFactory(sshKey);
+ PublicKey configuredKey = toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM);
- this.factory.createSession(this.hc, null, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 22, null);
- ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ PublicKey knownHostKey = getSshHostKey("gitlab.example.local");
- verify(this.jSch).setKnownHosts(captor.capture());
- assertThat(captor.getValue()).isEqualTo("/ssh/known_hosts");
+ assertThat(knownHostKey).isNotNull();
+ assertThat(knownHostKey).isEqualTo(configuredKey);
+ assertThat(isKnownKeyForHost(configuredKey, "gitlab.example.local")).isTrue();
}
@Test
- public void proxySettingsIsUsed() {
+ public void proxySettingsIsUsed() throws Exception {
JGitEnvironmentProperties sshProperties = new JGitEnvironmentProperties();
+ sshProperties.setUri("ssh://gitlab.example.local:3322/somerepo.git");
sshProperties.setPrivateKey(PRIVATE_KEY);
Map map = new HashMap<>();
ProxyHostProperties proxyHostProperties = new ProxyHostProperties();
@@ -204,30 +220,99 @@ public class PropertyBasedSshSessionFactoryTest {
proxyHostProperties.setUsername("user");
proxyHostProperties.setPassword("password");
map.put(ProxyHostProperties.ProxyForScheme.HTTP, proxyHostProperties);
-
sshProperties.setProxy(map);
setupSessionFactory(sshProperties);
- this.factory.configure(this.hc, this.session);
- ArgumentCaptor captor = ArgumentCaptor.forClass(ProxyHTTP.class);
+ ProxyData proxyData = getSshProxyData("gitlab.example.local");
- verify(this.session).setProxy(captor.capture());
- assertThat(captor.getValue()).isNotNull();
- verify(this.proxyMock).setUserPasswd("user", "password");
+ assertThat(proxyData.getUser()).isEqualTo("user");
+ assertThat(new String(proxyData.getPassword())).isEqualTo("password");
+ assertThat(proxyData.getProxy().type().toString()).isEqualTo("HTTP");
+ assertThat(proxyData.getProxy().address().toString()).containsPattern("host\\.domain.*:8080");
+ }
+
+ @Test
+ public void sshConfigFileIsNotUsed() {
+ setupSessionFactory(new JGitEnvironmentProperties());
+
+ assertThat(factory.getSshConfig(new File("."))).isNull();
+ }
+
+ private ProxyData getSshProxyData(String hostname) {
+ try {
+ Field proxies = SshdSessionFactory.class.getDeclaredField("proxies");
+ proxies.setAccessible(true);
+ ProxyDataFactory proxyDataFactory = (ProxyDataFactory) proxies.get(factory);
+ proxies.setAccessible(false);
+ return proxyDataFactory.get(new InetSocketAddress(hostname, 22));
+ }
+ catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private PublicKey toPublicKey(String key, String algorithm) {
+ try {
+ return new ECDSAPublicKeyEntryDecoder().decodePublicKey(null, algorithm, Base64.getDecoder().decode(key),
+ Collections.emptyMap());
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private PrivateKey toPrivateKey(String key) {
+ try {
+ Collection keyPairs = KeyPairUtils.load(null, key);
+
+ return keyPairs.isEmpty() ? null : keyPairs.iterator().next().getPrivate();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private PrivateKey getSshPrivateKey(String hostName) {
+ SessionContext session = mock(SessionContext.class);
+ when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(hostName, 22));
+
+ List kayPairs;
+ try {
+ Spliterator spliterator = ((KeyIdentityProvider) factory.getDefaultKeys(new File(".")))
+ .loadKeys(session).spliterator();
+
+ kayPairs = StreamSupport.stream(spliterator, false).collect(Collectors.toList());
+ }
+ catch (IOException | GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+
+ return kayPairs.isEmpty() ? null : kayPairs.get(0).getPrivate();
+ }
+
+ private PublicKey getSshHostKey(String hostName) {
+ InetSocketAddress address = new InetSocketAddress(hostName, 22);
+ List publicKeys = factory.getServerKeyDatabase(null, null).lookup("address", address,
+ mock(ServerKeyDatabase.Configuration.class));
+
+ return publicKeys.isEmpty() ? null : publicKeys.get(0);
+ }
+
+ private boolean isKnownKeyForHost(PublicKey publicKey, String hostName) {
+ InetSocketAddress address = new InetSocketAddress(hostName, 22);
+ return factory.getServerKeyDatabase(null, null).accept("address", address, publicKey,
+ mock(ServerKeyDatabase.Configuration.class), null);
+ }
+
+ private SshConfigStore.HostConfig getSshHostConfig(String hostName) {
+ return factory.createSshConfigStore(new File("dummy"), new File("dummy"), "localUserName").lookup(hostName, 22,
+ "userName");
}
private void setupSessionFactory(JGitEnvironmentProperties sshKey) {
Map sshKeysByHostname = new HashMap<>();
sshKeysByHostname.put(SshUriPropertyProcessor.getHostname(sshKey.getUri()), sshKey);
- this.factory = new PropertyBasedSshSessionFactory(sshKeysByHostname, this.jSch) {
-
- @Override
- protected ProxyHTTP createProxy(ProxyHostProperties proxyHostProperties) {
- return proxyMock;
- }
- };
- when(this.hc.getHostName()).thenReturn(SshUriPropertyProcessor.getHostname(sshKey.getUri()));
- when(this.jSch.getHostKeyRepository()).thenReturn(this.hostKeyRepository);
+ this.factory = new PropertyBasedSshSessionFactory(sshKeysByHostname);
}
}
diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java
index ad87afca..16058458 100644
--- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java
+++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/ssh/SshPropertyValidatorTest.java
@@ -81,7 +81,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void supportedParametersSuccesful() throws Exception {
+ public void supportedParametersSuccessful() {
MultipleJGitEnvironmentProperties validSettings = new MultipleJGitEnvironmentProperties();
validSettings.setUri(SSH_URI);
validSettings.setIgnoreLocalSshSettings(true);
@@ -95,7 +95,19 @@ public class SshPropertyValidatorTest {
}
@Test
- public void invalidPrivateKeyFails() throws Exception {
+ public void invalidPrivateKeyFails() {
+ MultipleJGitEnvironmentProperties invalidKey = new MultipleJGitEnvironmentProperties();
+ invalidKey.setUri(SSH_URI);
+ invalidKey.setIgnoreLocalSshSettings(true);
+ invalidKey.setPrivateKey("-----BEGIN OPENSSH PRIVATE KEY-----\nFOOBAR");
+
+ Set> constraintViolations = validator
+ .validate(invalidKey);
+ assertThat(constraintViolations).hasSize(1);
+ }
+
+ @Test
+ public void dummyPrivateKeyFails() {
MultipleJGitEnvironmentProperties invalidKey = new MultipleJGitEnvironmentProperties();
invalidKey.setUri(SSH_URI);
invalidKey.setIgnoreLocalSshSettings(true);
@@ -107,7 +119,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void missingPrivateKeyFails() throws Exception {
+ public void missingPrivateKeyFails() {
MultipleJGitEnvironmentProperties missingKey = new MultipleJGitEnvironmentProperties();
missingKey.setUri(SSH_URI);
missingKey.setIgnoreLocalSshSettings(true);
@@ -118,7 +130,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void hostKeyWithMissingAlgoFails() throws Exception {
+ public void hostKeyWithMissingAlgoFails() {
MultipleJGitEnvironmentProperties missingAlgo = new MultipleJGitEnvironmentProperties();
missingAlgo.setUri(SSH_URI);
missingAlgo.setIgnoreLocalSshSettings(true);
@@ -131,7 +143,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void algoWithMissingHostKeyFails() throws Exception {
+ public void algoWithMissingHostKeyFails() {
MultipleJGitEnvironmentProperties missingHostKey = new MultipleJGitEnvironmentProperties();
missingHostKey.setUri(SSH_URI);
missingHostKey.setIgnoreLocalSshSettings(true);
@@ -144,7 +156,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void unsupportedAlgoFails() throws Exception {
+ public void unsupportedAlgoFails() {
MultipleJGitEnvironmentProperties unsupportedAlgo = new MultipleJGitEnvironmentProperties();
unsupportedAlgo.setUri(SSH_URI);
unsupportedAlgo.setIgnoreLocalSshSettings(true);
@@ -158,7 +170,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void validatorNotRunIfIgnoreLocalSettingsFalse() throws Exception {
+ public void validatorNotRunIfIgnoreLocalSettingsFalse() {
MultipleJGitEnvironmentProperties useLocal = new MultipleJGitEnvironmentProperties();
useLocal.setUri(SSH_URI);
useLocal.setIgnoreLocalSshSettings(false);
@@ -170,7 +182,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void validatorNotRunIfHttpsUri() throws Exception {
+ public void validatorNotRunIfHttpsUri() {
MultipleJGitEnvironmentProperties httpsUri = new MultipleJGitEnvironmentProperties();
httpsUri.setUri("https://somerepo.com/team/project.git");
httpsUri.setIgnoreLocalSshSettings(true);
@@ -182,7 +194,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void preferredAuthenticationsIsValidated() throws Exception {
+ public void preferredAuthenticationsIsValidated() {
MultipleJGitEnvironmentProperties sshUriProperties = new MultipleJGitEnvironmentProperties();
assertThat(validator.validate(sshUriProperties)).hasSize(0);
@@ -194,7 +206,7 @@ public class SshPropertyValidatorTest {
}
@Test
- public void knowHostsFileIsValidated() throws Exception {
+ public void knowHostsFileIsValidated() {
MultipleJGitEnvironmentProperties sshUriProperties = new MultipleJGitEnvironmentProperties();
assertThat(validator.validate(sshUriProperties)).hasSize(0);
diff --git a/spring-cloud-config-server/src/test/resources/ssh/known_hosts b/spring-cloud-config-server/src/test/resources/ssh/known_hosts
new file mode 100644
index 00000000..3ba67a7e
--- /dev/null
+++ b/spring-cloud-config-server/src/test/resources/ssh/known_hosts
@@ -0,0 +1 @@
+gitlab.example.local ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMzCa0AcNbahUFjFYJHIilhJOhKFHuDOOuY+/HqV9kALftitwNYo6dQ+tC9IK5JVZCZfqKfDWVMxspcPDf9eMoE=
diff --git a/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block-list.yml b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block-list.yml
index a277f1f2..e4b19af6 100644
--- a/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block-list.yml
+++ b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block-list.yml
@@ -7,30 +7,41 @@ spring:
uri: git@gitserver.com:team/repo.git
ignoreLocalSshSettings: true
privateKey: |
- -----BEGIN RSA PRIVATE KEY-----
- MIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI
- yyxp38YcuZG9cUDBAxby+K2TKmwHaC1Wf1QTwbPuCRdIPrDwRz+FLoegm3iDLCmn
- uP6rjZDneYsqfU1sSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km
- ayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq
- TOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir
- +diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk
- jfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt
- lDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI
- xdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V
- 4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG
- 7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu
- 3reKgm0CgYEA03Sx8JgF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8
- q4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq
- mRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa
- lKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M
- ZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv
- ZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit
- BT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO
- zf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw
- uC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF
- 54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS
- E1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx
- 4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr
- /syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq
- 2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==
- -----END RSA PRIVATE KEY-----
+ -----BEGIN OPENSSH PRIVATE KEY-----
+ b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
+ NhAAAAAwEAAQAAAYEAw9e+uwqtqnXCuxqeCoterQzT95Mx1XwAlIEsee+0xyzbQ54hlXTw
+ p/8kFRue0eq08VYOIFt/CkpDNvGiNZDYa1HPzYRbxgU1Mi73c58VbRdghg8hGEF8hs29tQ
+ bAyi1E7KoPfXn4Jhqp57PoUB2StuBwj9VU8DR7t4QvTx1XZmIh6ZND7pRKjyvzDCFSG7cO
+ bMpiWgVkDmmZWbRGY/el/GHfqah5cTEyBN8/HYXOL5opymQfmhd66t1/Yh0sacEBnbvHjD
+ Q1yIhf/tFXbuunacGLzP30XimexQOJ8CnSgFBBgu360saS5xfGJVoIgvnm44Irbw/Mtuxj
+ i6VFaWEfJNvGvhFIAVMf98cHZ+mdLpQIxFm+AUBcAPVGoI6kiXr6LucSWigz+TmeQ3T/Y1
+ hSlofgN4KudZWpxUPJKDU3kQ2WAdVIo22/tO7egJsnd9os+fzxaas+yaaZ2QtLDCaHBxO0
+ 78o2TPlAqxzgt+yvm0NYZ+G0XBazJMn49qCL91T5AAAFmHD1ZR9w9WUfAAAAB3NzaC1yc2
+ EAAAGBAMPXvrsKrap1wrsangqLXq0M0/eTMdV8AJSBLHnvtMcs20OeIZV08Kf/JBUbntHq
+ tPFWDiBbfwpKQzbxojWQ2GtRz82EW8YFNTIu93OfFW0XYIYPIRhBfIbNvbUGwMotROyqD3
+ 15+CYaqeez6FAdkrbgcI/VVPA0e7eEL08dV2ZiIemTQ+6USo8r8wwhUhu3DmzKYloFZA5p
+ mVm0RmP3pfxh36moeXExMgTfPx2Fzi+aKcpkH5oXeurdf2IdLGnBAZ27x4w0NciIX/7RV2
+ 7rp2nBi8z99F4pnsUDifAp0oBQQYLt+tLGkucXxiVaCIL55uOCK28PzLbsY4ulRWlhHyTb
+ xr4RSAFTH/fHB2fpnS6UCMRZvgFAXAD1RqCOpIl6+i7nElooM/k5nkN0/2NYUpaH4DeCrn
+ WVqcVDySg1N5ENlgHVSKNtv7Tu3oCbJ3faLPn88WmrPsmmmdkLSwwmhwcTtO/KNkz5QKsc
+ 4Lfsr5tDWGfhtFwWsyTJ+Pagi/dU+QAAAAMBAAEAAAGAAc1LYPcxL99Tgls1Vw1/OoJitO
+ Vy0O8KJlOl8B1HgYmlHtMmpfRkfnc3gsY1SOMq9QmAqcWNvq9+PNQuVOXXR+2BxvdPzNuh
+ aKvL9RFiphVP+wvKlymLFsZv12mPfoy6FJ9f8xybLuaR56LdIVeUUQBxqLEize79sGuT79
+ tqQXPnsfl754cPxI939gWcdsrRZCjcjM195TANjCi/eQ3/Rfo0j6AQNf1o42iWcRQZGJEH
+ j2gqbkWHCQAuut1f2j0/XXApnY2YVnD6brfTDo0P6AX1DWV9DvCkWwiI6/xYWUPmyjmE2x
+ JiDDeGWkI6WihnMlMObc8N8y/C5r2Oaw/WVtNSxiP2JaLBHq7lF3cGOzTfKrwS0lXtw1z6
+ nJS/OwKiplvTDcNKUIFAKlryb6psHSFuHKneFPB3hNz0Pznth3EmXoxoAgGD3vMJQD40sF
+ BIsSS6AuQVHstbLg6AZ7g6eTCMt+JH9Rbpt3hhznR5SZz4rgi49txFV0x/fN6JOm+NAAAA
+ wQCuAd8WCe4sQkTkpaSt0qEvqifGxLFnRLoLpVBQvBiJAFDn6f9JhpWHoND6YBYg9T8OXp
+ vJBED+v6TIfmynxt53S8MhadzwnSaqhf+arW+Q+H56acOtmEnlxv+epbSdFraGT6XExsdG
+ 9zeVV2FTpaOG8s9Dye5zg7FYUAazjlxEnj8IavfkSV5htF7k6nuNADauw35v9jfEPbLvGN
+ L0jY277Bj55a2ytRuHqbZHm1lrOe9iK4XOddehBlXEsvIGWuUAAADBAOQcUn7EtxSYmUL+
+ mqn8+biEC8+7dgsv6mJvOETJhcRE3mbSgYO03UxrtOhcylUf0ks0mYvZugvTHN2dsdG9eT
+ 1ds0nZvOoKXwkSSJCH83+J7+jnI9PxK+nhqppZ3RtjKaaRDHmaS5zQZi6ZahSKGPx0KszB
+ +SmrzUJLpUq9cD7kDC3TydN2prGIF7RSgl6KhiHXm5UbYmFctNhMhyb1GPPDY9RWeo5zFX
+ VcXjY8MCXWiBYLCtbEPfcIAzHQcpiv+wAAAMEA28l23xIkQnMDkoiJqiK0BjT6G5joRzcj
+ VDD+qZCOR5z7d617mvYQM7B+3Mu8h4jXk4aQPyHMF3riRj8f2pvWBVqVl5EAAvKsywm/9l
+ LuyVFDDd3GlJRYuO/1XYKM8i7iXa4nKrQVcxiyF8gkiyafrqYSxuM0UTMc5vX4fCxQSqgh
+ 8WUG6n1OTwJ/x9grdXLi8rbjXaZbZfax4eOBJeEb9gKqE8+Ak0aRyXOMw8ygiUTIQELr7T
+ xLwZE6Y5ULy9ibAAAAHm1zaGFtc2lAbXNoYW1zaS1hMDIudm13YXJlLmNvbQECAwQ=
+ -----END OPENSSH PRIVATE KEY-----
diff --git a/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block.yml b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block.yml
index 021f9572..dbfd2291 100644
--- a/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block.yml
+++ b/spring-cloud-config-server/src/test/resources/ssh/ssh-private-key-block.yml
@@ -6,30 +6,41 @@ spring:
uri: git@gitserver.com:team/repo.git
ignoreLocalSshSettings: true
privateKey: |
- -----BEGIN RSA PRIVATE KEY-----
- MIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI
- yyxp38YcuZG9cUDBAxby+K2TKmwHaC1Wf1QTwbPuCRdIPrDwRz+FLoegm3iDLCmn
- uP6rjZDneYsqfU1sSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km
- ayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq
- TOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir
- +diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk
- jfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt
- lDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI
- xdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V
- 4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG
- 7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu
- 3reKgm0CgYEA03Sx8JgF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8
- q4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq
- mRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa
- lKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M
- ZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv
- ZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit
- BT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO
- zf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw
- uC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF
- 54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS
- E1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx
- 4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr
- /syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq
- 2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==
- -----END RSA PRIVATE KEY-----
+ -----BEGIN OPENSSH PRIVATE KEY-----
+ b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
+ NhAAAAAwEAAQAAAYEAw9e+uwqtqnXCuxqeCoterQzT95Mx1XwAlIEsee+0xyzbQ54hlXTw
+ p/8kFRue0eq08VYOIFt/CkpDNvGiNZDYa1HPzYRbxgU1Mi73c58VbRdghg8hGEF8hs29tQ
+ bAyi1E7KoPfXn4Jhqp57PoUB2StuBwj9VU8DR7t4QvTx1XZmIh6ZND7pRKjyvzDCFSG7cO
+ bMpiWgVkDmmZWbRGY/el/GHfqah5cTEyBN8/HYXOL5opymQfmhd66t1/Yh0sacEBnbvHjD
+ Q1yIhf/tFXbuunacGLzP30XimexQOJ8CnSgFBBgu360saS5xfGJVoIgvnm44Irbw/Mtuxj
+ i6VFaWEfJNvGvhFIAVMf98cHZ+mdLpQIxFm+AUBcAPVGoI6kiXr6LucSWigz+TmeQ3T/Y1
+ hSlofgN4KudZWpxUPJKDU3kQ2WAdVIo22/tO7egJsnd9os+fzxaas+yaaZ2QtLDCaHBxO0
+ 78o2TPlAqxzgt+yvm0NYZ+G0XBazJMn49qCL91T5AAAFmHD1ZR9w9WUfAAAAB3NzaC1yc2
+ EAAAGBAMPXvrsKrap1wrsangqLXq0M0/eTMdV8AJSBLHnvtMcs20OeIZV08Kf/JBUbntHq
+ tPFWDiBbfwpKQzbxojWQ2GtRz82EW8YFNTIu93OfFW0XYIYPIRhBfIbNvbUGwMotROyqD3
+ 15+CYaqeez6FAdkrbgcI/VVPA0e7eEL08dV2ZiIemTQ+6USo8r8wwhUhu3DmzKYloFZA5p
+ mVm0RmP3pfxh36moeXExMgTfPx2Fzi+aKcpkH5oXeurdf2IdLGnBAZ27x4w0NciIX/7RV2
+ 7rp2nBi8z99F4pnsUDifAp0oBQQYLt+tLGkucXxiVaCIL55uOCK28PzLbsY4ulRWlhHyTb
+ xr4RSAFTH/fHB2fpnS6UCMRZvgFAXAD1RqCOpIl6+i7nElooM/k5nkN0/2NYUpaH4DeCrn
+ WVqcVDySg1N5ENlgHVSKNtv7Tu3oCbJ3faLPn88WmrPsmmmdkLSwwmhwcTtO/KNkz5QKsc
+ 4Lfsr5tDWGfhtFwWsyTJ+Pagi/dU+QAAAAMBAAEAAAGAAc1LYPcxL99Tgls1Vw1/OoJitO
+ Vy0O8KJlOl8B1HgYmlHtMmpfRkfnc3gsY1SOMq9QmAqcWNvq9+PNQuVOXXR+2BxvdPzNuh
+ aKvL9RFiphVP+wvKlymLFsZv12mPfoy6FJ9f8xybLuaR56LdIVeUUQBxqLEize79sGuT79
+ tqQXPnsfl754cPxI939gWcdsrRZCjcjM195TANjCi/eQ3/Rfo0j6AQNf1o42iWcRQZGJEH
+ j2gqbkWHCQAuut1f2j0/XXApnY2YVnD6brfTDo0P6AX1DWV9DvCkWwiI6/xYWUPmyjmE2x
+ JiDDeGWkI6WihnMlMObc8N8y/C5r2Oaw/WVtNSxiP2JaLBHq7lF3cGOzTfKrwS0lXtw1z6
+ nJS/OwKiplvTDcNKUIFAKlryb6psHSFuHKneFPB3hNz0Pznth3EmXoxoAgGD3vMJQD40sF
+ BIsSS6AuQVHstbLg6AZ7g6eTCMt+JH9Rbpt3hhznR5SZz4rgi49txFV0x/fN6JOm+NAAAA
+ wQCuAd8WCe4sQkTkpaSt0qEvqifGxLFnRLoLpVBQvBiJAFDn6f9JhpWHoND6YBYg9T8OXp
+ vJBED+v6TIfmynxt53S8MhadzwnSaqhf+arW+Q+H56acOtmEnlxv+epbSdFraGT6XExsdG
+ 9zeVV2FTpaOG8s9Dye5zg7FYUAazjlxEnj8IavfkSV5htF7k6nuNADauw35v9jfEPbLvGN
+ L0jY277Bj55a2ytRuHqbZHm1lrOe9iK4XOddehBlXEsvIGWuUAAADBAOQcUn7EtxSYmUL+
+ mqn8+biEC8+7dgsv6mJvOETJhcRE3mbSgYO03UxrtOhcylUf0ks0mYvZugvTHN2dsdG9eT
+ 1ds0nZvOoKXwkSSJCH83+J7+jnI9PxK+nhqppZ3RtjKaaRDHmaS5zQZi6ZahSKGPx0KszB
+ +SmrzUJLpUq9cD7kDC3TydN2prGIF7RSgl6KhiHXm5UbYmFctNhMhyb1GPPDY9RWeo5zFX
+ VcXjY8MCXWiBYLCtbEPfcIAzHQcpiv+wAAAMEA28l23xIkQnMDkoiJqiK0BjT6G5joRzcj
+ VDD+qZCOR5z7d617mvYQM7B+3Mu8h4jXk4aQPyHMF3riRj8f2pvWBVqVl5EAAvKsywm/9l
+ LuyVFDDd3GlJRYuO/1XYKM8i7iXa4nKrQVcxiyF8gkiyafrqYSxuM0UTMc5vX4fCxQSqgh
+ 8WUG6n1OTwJ/x9grdXLi8rbjXaZbZfax4eOBJeEb9gKqE8+Ak0aRyXOMw8ygiUTIQELr7T
+ xLwZE6Y5ULy9ibAAAAHm1zaGFtc2lAbXNoYW1zaS1hMDIudm13YXJlLmNvbQECAwQ=
+ -----END OPENSSH PRIVATE KEY-----