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.annotations.NonNull;
import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
import org.eclipse.jgit.transport.SshConfigStore; import org.eclipse.jgit.transport.SshConfigStore;
import org.eclipse.jgit.transport.SshConstants;
import org.eclipse.jgit.transport.sshd.SshdSessionFactory; import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties; import org.springframework.cloud.config.server.environment.JGitEnvironmentProperties;
public class FileBasedSshSessionFactory extends SshdSessionFactory { 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; private final Map<String, JGitEnvironmentProperties> sshKeysByHostname;
public FileBasedSshSessionFactory(Map<String, JGitEnvironmentProperties> sshKeysByHostname) { public FileBasedSshSessionFactory(Map<String, JGitEnvironmentProperties> sshKeysByHostname) {
@@ -54,8 +49,8 @@ public class FileBasedSshSessionFactory extends SshdSessionFactory {
return hostEntry; return hostEntry;
} }
hostEntry.setValue(STRICT_HOST_KEY_CHECKING, hostEntry.setValue(SshConstants.STRICT_HOST_KEY_CHECKING,
sshProperties.isStrictHostKeyChecking() ? YES_OPTION : NO_OPTION); sshProperties.isStrictHostKeyChecking() ? SshConstants.YES : SshConstants.NO);
return hostEntry; return hostEntry;
} }

View File

@@ -61,14 +61,6 @@ import org.springframework.util.StringUtils;
*/ */
public class PropertyBasedSshSessionFactory extends SshdSessionFactory { 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; private final Map<String, JGitEnvironmentProperties> sshKeysByHostname;
public PropertyBasedSshSessionFactory(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) { private OpenSshConfigFile.HostEntry updateIfNeeded(OpenSshConfigFile.HostEntry hostEntry, String hostName) {
JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(hostName); JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(hostName);
if (sshProperties == null) {
return hostEntry;
}
if (sshProperties.getHostKey() == null || !sshProperties.isStrictHostKeyChecking()) { if (sshProperties.getHostKey() == null || !sshProperties.isStrictHostKeyChecking()) {
hostEntry.setValue(STRICT_HOST_KEY_CHECKING, NO_OPTION); hostEntry.setValue(SshConstants.STRICT_HOST_KEY_CHECKING, SshConstants.NO);
} }
else { else {
hostEntry.setValue(STRICT_HOST_KEY_CHECKING, YES_OPTION); hostEntry.setValue(SshConstants.STRICT_HOST_KEY_CHECKING, SshConstants.YES);
} }
String preferredAuthentications = sshProperties.getPreferredAuthentications(); String preferredAuthentications = sshProperties.getPreferredAuthentications();
if (preferredAuthentications != null) { if (preferredAuthentications != null) {
hostEntry.setValue(PREFERRED_AUTHENTICATIONS, preferredAuthentications); hostEntry.setValue(SshConstants.PREFERRED_AUTHENTICATIONS, preferredAuthentications);
} }
return hostEntry; return hostEntry;
} }
@@ -136,10 +125,7 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
public List<PublicKey> lookup(String connectAddress, InetSocketAddress remoteAddress, public List<PublicKey> lookup(String connectAddress, InetSocketAddress remoteAddress,
Configuration config) { Configuration config) {
JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName()); JGitEnvironmentProperties sshProperties = findEnvironmentProperties(sshKeysByHostname, remoteAddress);
if (sshProperties == null) {
return Collections.emptyList();
}
List<Path> knownHostFiles = getKnownHostFiles(sshProperties); List<Path> knownHostFiles = getKnownHostFiles(sshProperties);
List<PublicKey> publicKeys = new OpenSshServerKeyDatabase(false, knownHostFiles).lookup(connectAddress, List<PublicKey> publicKeys = new OpenSshServerKeyDatabase(false, knownHostFiles).lookup(connectAddress,
@@ -156,7 +142,8 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
@Override @Override
public boolean accept(String connectAddress, InetSocketAddress remoteAddress, PublicKey serverKey, public boolean accept(String connectAddress, InetSocketAddress remoteAddress, PublicKey serverKey,
Configuration config, CredentialsProvider provider) { Configuration config, CredentialsProvider provider) {
if (isNotStrictHostKeyChecking(remoteAddress.getHostName())) {
if (config.getStrictHostKeyChecking() == Configuration.StrictHostKeyChecking.ACCEPT_ANY) {
return true; return true;
} }
@@ -165,14 +152,6 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
return KeyUtils.findMatchingKey(serverKey, knownServerKeys) != null; 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) { private PublicKey getHostKey(JGitEnvironmentProperties sshProperties) {
String hostKey = sshProperties.getHostKey(); String hostKey = sshProperties.getHostKey();
String hostKeyAlgorithm = sshProperties.getHostKeyAlgorithm(); String hostKeyAlgorithm = sshProperties.getHostKeyAlgorithm();
@@ -206,6 +185,18 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
return new SingleKeyIdentityProvider(sshKeysByHostname); 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 static class SingleKeyIdentityProvider implements KeyIdentityProvider, Iterable<KeyPair> {
private final Map<String, JGitEnvironmentProperties> sshKeysByHostname; private final Map<String, JGitEnvironmentProperties> sshKeysByHostname;
@@ -221,11 +212,10 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
@Override @Override
public Iterable<KeyPair> loadKeys(SessionContext session) throws IOException, GeneralSecurityException { public Iterable<KeyPair> loadKeys(SessionContext session) throws IOException, GeneralSecurityException {
SshdSocketAddress remoteAddress = SshdSocketAddress.toSshdSocketAddress(session.getRemoteAddress()); InetSocketAddress remoteAddress = SshdSocketAddress.toInetSocketAddress(session.getRemoteAddress());
JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName()); JGitEnvironmentProperties sshProperties = findEnvironmentProperties(sshKeysByHostname, remoteAddress);
return sshProperties == null ? Collections.emptyList() return KeyPairUtils.load(session, sshProperties.getPrivateKey(), sshProperties.getPassphrase());
: KeyPairUtils.load(session, sshProperties.getPrivateKey(), sshProperties.getPassphrase());
} }
} }
@@ -240,7 +230,8 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
@Override @Override
public ProxyData get(InetSocketAddress remoteAddress) { public ProxyData get(InetSocketAddress remoteAddress) {
JGitEnvironmentProperties sshProperties = sshKeysByHostname.get(remoteAddress.getHostName()); JGitEnvironmentProperties sshProperties = findEnvironmentProperties(sshKeysByHostname, remoteAddress);
ProxyHostProperties proxyHostProperties = sshProperties.getProxy() ProxyHostProperties proxyHostProperties = sshProperties.getProxy()
.get(ProxyHostProperties.ProxyForScheme.HTTP); .get(ProxyHostProperties.ProxyForScheme.HTTP);
@@ -250,8 +241,10 @@ public class PropertyBasedSshSessionFactory extends SshdSessionFactory {
Proxy proxy = new Proxy(Proxy.Type.HTTP, Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyHostProperties.getHost(), proxyHostProperties.getPort())); 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.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyPair; import java.security.KeyPair;
@@ -54,6 +55,7 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import static org.assertj.core.api.Assertions.assertThat; 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.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -121,16 +123,10 @@ public class PropertyBasedSshSessionFactoryTest {
public void sshConfigIsUsedForRelevantHostOnly() { public void sshConfigIsUsedForRelevantHostOnly() {
JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties(); JGitEnvironmentProperties sshKey = new JGitEnvironmentProperties();
sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git"); sshKey.setUri("ssh://gitlab.example.local:3322/somerepo.git");
sshKey.setHostKeyAlgorithm(HOST_KEY_ALGORITHM);
sshKey.setHostKey(HOST_KEY);
sshKey.setPrivateKey(PRIVATE_KEY); sshKey.setPrivateKey(PRIVATE_KEY);
setupSessionFactory(sshKey); setupSessionFactory(sshKey);
PublicKey configuredKey = toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM);
SshConfigStore.HostConfig sshConfig = getSshHostConfig("another.host"); assertThatThrownBy(() -> getSshHostConfig("another.host")).isInstanceOf(NullPointerException.class);
assertThat(sshConfig.getValue("StrictHostKeyChecking")).isNull();
assertThat(isKnownKeyForHost(configuredKey, "another.host")).isFalse();
} }
@Test @Test
@@ -161,6 +157,19 @@ public class PropertyBasedSshSessionFactoryTest {
assertThat(privateKey).isEqualTo(toPrivateKey(PRIVATE_KEY, null)); 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 @Test
public void privateKeyWithPassphraseIsUsed() { public void privateKeyWithPassphraseIsUsed() {
String keyWithPassphrase = getResourceAsString("/ssh/key-with-passphrase"); String keyWithPassphrase = getResourceAsString("/ssh/key-with-passphrase");
@@ -183,6 +192,24 @@ public class PropertyBasedSshSessionFactoryTest {
sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git"); sshKey.setUri("git@gitlab.example.local:someorg/somerepo.git");
sshKey.setHostKeyAlgorithm(HOST_KEY_ALGORITHM); sshKey.setHostKeyAlgorithm(HOST_KEY_ALGORITHM);
sshKey.setHostKey(HOST_KEY); 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); sshKey.setPrivateKey(PRIVATE_KEY);
setupSessionFactory(sshKey); setupSessionFactory(sshKey);
PublicKey configuredKey = toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM); PublicKey configuredKey = toPublicKey(HOST_KEY, HOST_KEY_ALGORITHM);
@@ -241,6 +268,7 @@ public class PropertyBasedSshSessionFactoryTest {
ProxyData proxyData = getSshProxyData("gitlab.example.local"); ProxyData proxyData = getSshProxyData("gitlab.example.local");
assertThat(proxyData).isNotNull();
assertThat(proxyData.getUser()).isEqualTo("user"); assertThat(proxyData.getUser()).isEqualTo("user");
assertThat(new String(proxyData.getPassword())).isEqualTo("password"); assertThat(new String(proxyData.getPassword())).isEqualTo("password");
assertThat(proxyData.getProxy().type().toString()).isEqualTo("HTTP"); assertThat(proxyData.getProxy().type().toString()).isEqualTo("HTTP");
@@ -251,11 +279,28 @@ public class PropertyBasedSshSessionFactoryTest {
public void defaultSshConfigIsSet() { public void defaultSshConfigIsSet() {
setupSessionFactory(new JGitEnvironmentProperties()); 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"); @Test
assertThat(sshConfig.getValue("Port")).isEqualTo("123"); public void proxySettingsIsUsedWithRepoIp() {
assertThat(sshConfig.getValue("User")).isEqualTo("user.name"); 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 @Test
@@ -271,7 +316,7 @@ public class PropertyBasedSshSessionFactoryTest {
proxies.setAccessible(true); proxies.setAccessible(true);
ProxyDataFactory proxyDataFactory = (ProxyDataFactory) proxies.get(factory); ProxyDataFactory proxyDataFactory = (ProxyDataFactory) proxies.get(factory);
proxies.setAccessible(false); proxies.setAccessible(false);
return proxyDataFactory.get(new InetSocketAddress(hostname, 22)); return proxyDataFactory.get(setupSocketAddress(hostname));
} }
catch (NoSuchFieldException | IllegalAccessException e) { catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(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); SessionContext session = mock(SessionContext.class);
when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(hostName, 22)); when(session.getRemoteAddress()).thenReturn(address);
List<KeyPair> kayPairs; List<KeyPair> kayPairs;
try { try {
@@ -317,22 +363,22 @@ public class PropertyBasedSshSessionFactoryTest {
return kayPairs.isEmpty() ? null : kayPairs.get(0).getPrivate(); return kayPairs.isEmpty() ? null : kayPairs.get(0).getPrivate();
} }
private PublicKey getSshHostKey(String hostName) { private PublicKey getSshHostKey(String hostname) {
InetSocketAddress address = new InetSocketAddress(hostName, 22); InetSocketAddress address = setupSocketAddress(hostname);
List<PublicKey> publicKeys = factory.getServerKeyDatabase(null, null).lookup("address", address, List<PublicKey> publicKeys = factory.getServerKeyDatabase(null, null).lookup("address", address,
mock(ServerKeyDatabase.Configuration.class)); mock(ServerKeyDatabase.Configuration.class));
return publicKeys.isEmpty() ? null : publicKeys.get(0); return publicKeys.isEmpty() ? null : publicKeys.get(0);
} }
private boolean isKnownKeyForHost(PublicKey publicKey, String hostName) { private boolean isKnownKeyForHost(PublicKey publicKey, String hostname) {
InetSocketAddress address = new InetSocketAddress(hostName, 22); InetSocketAddress address = setupSocketAddress(hostname);
return factory.getServerKeyDatabase(null, null).accept("address", address, publicKey, return factory.getServerKeyDatabase(null, null).accept("address", address, publicKey,
mock(ServerKeyDatabase.Configuration.class), null); mock(ServerKeyDatabase.Configuration.class), null);
} }
private SshConfigStore.HostConfig getSshHostConfig(String hostName) { private SshConfigStore.HostConfig getSshHostConfig(String hostname) {
return factory.createSshConfigStore(new File("dummy"), new File("dummy"), "localUserName").lookup(hostName, 22, return factory.createSshConfigStore(new File("dummy"), new File("dummy"), "localUserName").lookup(hostname, 22,
"userName"); "userName");
} }
@@ -347,4 +393,17 @@ public class PropertyBasedSshSessionFactoryTest {
this.factory = new PropertyBasedSshSessionFactory(sshKeysByHostname); 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;
}
} }