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 index a236c3c6..27f1ac2d 100644 --- 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 @@ -22,18 +22,13 @@ import java.util.Map; 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.SshConstants; 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 Map sshKeysByHostname; public FileBasedSshSessionFactory(Map sshKeysByHostname) { @@ -54,8 +49,8 @@ public class FileBasedSshSessionFactory extends SshdSessionFactory { return hostEntry; } - hostEntry.setValue(STRICT_HOST_KEY_CHECKING, - sshProperties.isStrictHostKeyChecking() ? YES_OPTION : NO_OPTION); + hostEntry.setValue(SshConstants.STRICT_HOST_KEY_CHECKING, + sshProperties.isStrictHostKeyChecking() ? SshConstants.YES : SshConstants.NO); return hostEntry; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactory.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactory.java index 787adfb5..702d8016 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactory.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/ssh/PropertyBasedSshSessionFactory.java @@ -61,14 +61,6 @@ import org.springframework.util.StringUtils; */ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { - private static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking"; - - private static final String PREFERRED_AUTHENTICATIONS = "PreferredAuthentications"; - - private static final String YES_OPTION = "yes"; - - private static final String NO_OPTION = "no"; - private final Map sshKeysByHostname; public PropertyBasedSshSessionFactory(Map sshKeysByHostname) { @@ -103,20 +95,17 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { 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); + hostEntry.setValue(SshConstants.STRICT_HOST_KEY_CHECKING, SshConstants.NO); } else { - hostEntry.setValue(STRICT_HOST_KEY_CHECKING, YES_OPTION); + hostEntry.setValue(SshConstants.STRICT_HOST_KEY_CHECKING, SshConstants.YES); } String preferredAuthentications = sshProperties.getPreferredAuthentications(); if (preferredAuthentications != null) { - hostEntry.setValue(PREFERRED_AUTHENTICATIONS, preferredAuthentications); + hostEntry.setValue(SshConstants.PREFERRED_AUTHENTICATIONS, preferredAuthentications); } return hostEntry; } @@ -136,10 +125,7 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { public List lookup(String connectAddress, InetSocketAddress remoteAddress, Configuration config) { - JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName()); - if (sshProperties == null) { - return Collections.emptyList(); - } + JGitEnvironmentProperties sshProperties = findEnvironmentProperties(sshKeysByHostname, remoteAddress); List knownHostFiles = getKnownHostFiles(sshProperties); List publicKeys = new OpenSshServerKeyDatabase(false, knownHostFiles).lookup(connectAddress, @@ -156,7 +142,8 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { @Override public boolean accept(String connectAddress, InetSocketAddress remoteAddress, PublicKey serverKey, Configuration config, CredentialsProvider provider) { - if (isNotStrictHostKeyChecking(remoteAddress.getHostName())) { + + if (config.getStrictHostKeyChecking() == Configuration.StrictHostKeyChecking.ACCEPT_ANY) { return true; } @@ -165,14 +152,6 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { return KeyUtils.findMatchingKey(serverKey, knownServerKeys) != null; } - 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(); @@ -206,6 +185,18 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { return new SingleKeyIdentityProvider(sshKeysByHostname); } + private static JGitEnvironmentProperties findEnvironmentProperties( + Map sshKeysByHostname, InetSocketAddress socketAddress) { + + JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(socketAddress.getHostString()); + + if (sshProperties == null && socketAddress.getAddress() != null) { + sshProperties = sshKeysByHostname.get(socketAddress.getAddress().getHostAddress()); + } + + return sshProperties; + } + private final static class SingleKeyIdentityProvider implements KeyIdentityProvider, Iterable { private final Map sshKeysByHostname; @@ -221,11 +212,10 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { @Override public Iterable loadKeys(SessionContext session) throws IOException, GeneralSecurityException { - SshdSocketAddress remoteAddress = SshdSocketAddress.toSshdSocketAddress(session.getRemoteAddress()); - JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName()); + InetSocketAddress remoteAddress = SshdSocketAddress.toInetSocketAddress(session.getRemoteAddress()); + JGitEnvironmentProperties sshProperties = findEnvironmentProperties(sshKeysByHostname, remoteAddress); - return sshProperties == null ? Collections.emptyList() - : KeyPairUtils.load(session, sshProperties.getPrivateKey(), sshProperties.getPassphrase()); + return KeyPairUtils.load(session, sshProperties.getPrivateKey(), sshProperties.getPassphrase()); } } @@ -240,7 +230,8 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { @Override public ProxyData get(InetSocketAddress remoteAddress) { - JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName()); + JGitEnvironmentProperties sshProperties = findEnvironmentProperties(sshKeysByHostname, remoteAddress); + ProxyHostProperties proxyHostProperties = sshProperties.getProxy() .get(ProxyHostProperties.ProxyForScheme.HTTP); @@ -250,8 +241,10 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory { Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHostProperties.getHost(), proxyHostProperties.getPort())); - return new ProxyData(proxy, proxyHostProperties.getUsername(), - proxyHostProperties.getPassword().toCharArray()); + + char[] proxyPassword = proxyHostProperties.getPassword() != null + ? proxyHostProperties.getPassword().toCharArray() : null; + return new ProxyData(proxy, proxyHostProperties.getUsername(), proxyPassword); } } 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 013da140..680c546a 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 @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Field; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.security.GeneralSecurityException; import java.security.KeyPair; @@ -54,6 +55,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -121,16 +123,10 @@ public class PropertyBasedSshSessionFactoryTest { 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(); + assertThatThrownBy(() -> getSshHostConfig("another.host")).isInstanceOf(NullPointerException.class); } @Test @@ -161,6 +157,19 @@ public class PropertyBasedSshSessionFactoryTest { assertThat(privateKey).isEqualTo(toPrivateKey(PRIVATE_KEY, null)); } + @Test + public void privateKeyIsUsedWithRepoIp() { + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("git@127.0.0.1:someorg/somerepo.git"); + sshKey.setPrivateKey(PRIVATE_KEY); + setupSessionFactory(sshKey); + + PrivateKey privateKey = getSshPrivateKey("gitlab.example.local"); + + assertThat(privateKey).isNotNull(); + assertThat(privateKey).isEqualTo(toPrivateKey(PRIVATE_KEY, null)); + } + @Test public void privateKeyWithPassphraseIsUsed() { String keyWithPassphrase = getResourceAsString("/ssh/key-with-passphrase"); @@ -183,6 +192,24 @@ public class PropertyBasedSshSessionFactoryTest { sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git"); sshKey.setHostKeyAlgorithm(HOST_KEY_ALGORITHM); sshKey.setHostKey(HOST_KEY); + sshKey.setStrictHostKeyChecking(true); + sshKey.setPrivateKey(PRIVATE_KEY); + setupSessionFactory(sshKey); + PublicKey configuredKey = toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM); + + PublicKey knownHostKey = getSshHostKey("gitlab.example.local"); + + assertThat(knownHostKey).isNotNull(); + assertThat(knownHostKey).isEqualTo(configuredKey); + assertThat(isKnownKeyForHost(configuredKey, "gitlab.example.local")).isTrue(); + } + + @Test + public void hostKeyIsUsedWithRepoIp() { + JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); + sshKey.setUri("git@127.0.0.1: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); @@ -241,6 +268,7 @@ public class PropertyBasedSshSessionFactoryTest { ProxyData proxyData = getSshProxyData("gitlab.example.local"); + assertThat(proxyData).isNotNull(); assertThat(proxyData.getUser()).isEqualTo("user"); assertThat(new String(proxyData.getPassword())).isEqualTo("password"); assertThat(proxyData.getProxy().type().toString()).isEqualTo("HTTP"); @@ -251,11 +279,28 @@ public class PropertyBasedSshSessionFactoryTest { public void defaultSshConfigIsSet() { setupSessionFactory(new JGitEnvironmentProperties()); - SshConfigStore.HostConfig sshConfig = getDefaultSshHostConfig("host.name", 123, "user.name"); + assertThatThrownBy(() -> getDefaultSshHostConfig("host.name", 123, "user.name")) + .isInstanceOf(NullPointerException.class); + } - assertThat(sshConfig.getValue("HostName")).isEqualTo("host.name"); - assertThat(sshConfig.getValue("Port")).isEqualTo("123"); - assertThat(sshConfig.getValue("User")).isEqualTo("user.name"); + @Test + public void proxySettingsIsUsedWithRepoIp() { + JGitEnvironmentProperties sshProperties = new JGitEnvironmentProperties(); + sshProperties.setUri("ssh://127.0.0.1:3322/somerepo.git"); + sshProperties.setPrivateKey(PRIVATE_KEY); + Map map = new HashMap<>(); + ProxyHostProperties proxyHostProperties = new ProxyHostProperties(); + proxyHostProperties.setHost("host.domain"); + proxyHostProperties.setPort(8080); + map.put(ProxyHostProperties.ProxyForScheme.HTTP, proxyHostProperties); + sshProperties.setProxy(map); + setupSessionFactory(sshProperties); + + ProxyData proxyData = getSshProxyData("gitlab.example.local"); + + assertThat(proxyData).isNotNull(); + assertThat(proxyData.getProxy().type().toString()).isEqualTo("HTTP"); + assertThat(proxyData.getProxy().address().toString()).containsPattern("host\\.domain.*:8080"); } @Test @@ -271,7 +316,7 @@ public class PropertyBasedSshSessionFactoryTest { proxies.setAccessible(true); ProxyDataFactory proxyDataFactory = (ProxyDataFactory) proxies.get(factory); proxies.setAccessible(false); - return proxyDataFactory.get(new InetSocketAddress(hostname, 22)); + return proxyDataFactory.get(setupSocketAddress(hostname)); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); @@ -299,9 +344,10 @@ public class PropertyBasedSshSessionFactoryTest { } } - private PrivateKey getSshPrivateKey(String hostName) { + private PrivateKey getSshPrivateKey(String hostname) { + InetSocketAddress address = setupSocketAddress(hostname); SessionContext session = mock(SessionContext.class); - when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(hostName, 22)); + when(session.getRemoteAddress()).thenReturn(address); List kayPairs; try { @@ -317,22 +363,22 @@ public class PropertyBasedSshSessionFactoryTest { return kayPairs.isEmpty() ? null : kayPairs.get(0).getPrivate(); } - private PublicKey getSshHostKey(String hostName) { - InetSocketAddress address = new InetSocketAddress(hostName, 22); + private PublicKey getSshHostKey(String hostname) { + InetSocketAddress address = setupSocketAddress(hostname); 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); + private boolean isKnownKeyForHost(PublicKey publicKey, String hostname) { + InetSocketAddress address = setupSocketAddress(hostname); 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, + private SshConfigStore.HostConfig getSshHostConfig(String hostname) { + return factory.createSshConfigStore(new File("dummy"), new File("dummy"), "localUserName").lookup(hostname, 22, "userName"); } @@ -347,4 +393,17 @@ public class PropertyBasedSshSessionFactoryTest { this.factory = new PropertyBasedSshSessionFactory(sshKeysByHostname); } + private InetSocketAddress setupSocketAddress(String hostname) { + InetAddress address = mock(InetAddress.class); + when(address.getHostAddress()).thenReturn("127.0.0.1"); + + InetSocketAddress socketAddress = mock(InetSocketAddress.class); + when(socketAddress.getAddress()).thenReturn(address); + when(socketAddress.getHostString()).thenReturn(hostname); + when(socketAddress.getHostName()).thenReturn(hostname); + when(socketAddress.getPort()).thenReturn(22); + + return socketAddress; + } + }