Merge branch '3.1.x'

This commit is contained in:
Ryan Baxter
2022-10-06 13:33:18 -04:00
3 changed files with 109 additions and 62 deletions

View File

@@ -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<String, JGitEnvironmentProperties> sshKeysByHostname;
public FileBasedSshSessionFactory(Map<String, JGitEnvironmentProperties> 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;
}

View File

@@ -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<String, JGitEnvironmentProperties> sshKeysByHostname;
public PropertyBasedSshSessionFactory(Map<String, JGitEnvironmentProperties> 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<PublicKey> 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<Path> knownHostFiles = getKnownHostFiles(sshProperties);
List<PublicKey> 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<String, JGitEnvironmentProperties> 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<KeyPair> {
private final Map<String, JGitEnvironmentProperties> sshKeysByHostname;
@@ -221,11 +212,10 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
@Override
public Iterable<KeyPair> 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);
}
}

View File

@@ -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<ProxyHostProperties.ProxyForScheme, ProxyHostProperties> 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<KeyPair> 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<PublicKey> 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;
}
}