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 94d81a90..48f5fa73 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 @@ -21,18 +21,13 @@ 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.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 JGitEnvironmentProperties sshUriProperties; public FileBasedSshSessionFactory(JGitEnvironmentProperties sshUriProperties) { @@ -47,8 +42,8 @@ public class FileBasedSshSessionFactory extends SshdSessionFactory { 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); + hostEntry.setValue(SshConstants.STRICT_HOST_KEY_CHECKING, + sshUriProperties.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 e7a502fb..13f6def1 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 @@ -40,6 +40,7 @@ import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyDatabase; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.SshConfigStore; +import org.eclipse.jgit.transport.SshConstants; import org.eclipse.jgit.transport.sshd.JGitKeyCache; import org.eclipse.jgit.transport.sshd.ProxyData; import org.eclipse.jgit.transport.sshd.ProxyDataFactory; @@ -60,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) { @@ -90,20 +83,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; } @@ -123,10 +113,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, @@ -143,7 +130,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; } @@ -152,14 +140,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(); @@ -193,6 +173,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; @@ -208,11 +200,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()); } } @@ -227,7 +218,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); @@ -237,8 +229,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 bc231a0a..050c62e6 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); @@ -225,7 +252,7 @@ public class PropertyBasedSshSessionFactoryTest { } @Test - public void proxySettingsIsUsed() throws Exception { + public void proxySettingsIsUsed() { JGitEnvironmentProperties sshProperties = new JGitEnvironmentProperties(); sshProperties.setUri("ssh://gitlab.example.local:3322/somerepo.git"); sshProperties.setPrivateKey(PRIVATE_KEY); @@ -241,12 +268,33 @@ 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"); assertThat(proxyData.getProxy().address().toString()).containsPattern("host\\.domain.*:8080"); } + @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 public void sshConfigFileIsNotUsed() { setupSessionFactory(new JGitEnvironmentProperties()); @@ -260,7 +308,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); @@ -288,9 +336,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 { @@ -306,22 +355,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"); } @@ -331,4 +380,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; + } + }