Git SSH configuration using properties

By default, Spring Cloud Config Server uses SSH configuration files such
as ~/.ssh/known_hosts and /etc/ssh/ssh_config when connecting to Git repositories
using an SSH URI. In cloud environments such as Cloud Foundry,
the local filesystem may be ephemeral or not easily accessible.

 For cases such as these, SSH configuration can be set using Java properties.
 In order to activate property based SSH configuration,
the property spring.cloud.config.server.git.ignoreLocalSshSettings must be set to true
This commit is contained in:
Ollie Hughes
2017-07-06 16:21:09 +01:00
parent d56926eba6
commit acee8a1b10
18 changed files with 1513 additions and 63 deletions

View File

@@ -6,8 +6,10 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config</artifactId>
<version>1.3.2.BUILD-SNAPSHOT</version>
<version>1.4.0.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<packaging>pom</packaging>
<name>Spring Cloud Config Docs</name>
<description>Spring Cloud Docs</description>

View File

@@ -360,7 +360,77 @@ by the default credential provider chain. AWS EC2 instances may use
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html[IAM Roles for EC2 Instances].
Note: The aws-java-sdk-core jar is an optional dependency. If the aws-java-sdk-core jar is not on your
classpath, then the AWS Code Commit credential provider will not be created regardless of the git server URI.
classpath, then the AWS Code Commit credential provider will not be created regardless of the git server URI.
===== Git SSH configuration using properties
By default, Spring Cloud Config Server uses SSH configuration files such as `~/.ssh/known_hosts` and `/etc/ssh/ssh_config` when connecting to Git repositories using an SSH URI.
In cloud environments such as Cloud Foundry, the local filesystem may be ephemeral or not easily accessible. For cases such as these, SSH configuration can be set using
Java properties. In order to activate property based SSH configuration, the property `spring.cloud.config.server.git.ignoreLocalSshSettings` must be set to `true`.
Example:
[source,yaml]
----
spring:
cloud:
config:
server:
git:
uri: git@gitserver.com:team/repo1.git
ignoreLocalSshSettings: true
hostKey: someHostKey
hostKeyAlgorithm: ssh-rsa
privateKey: |
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX
IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF
ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud
1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i
oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W
DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd
fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b
BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG
EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj
5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8
+AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe
pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG
ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ
xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW
dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi
PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX
VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z
FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R
gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4
VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV
cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee
KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N
CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs
q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J
69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT
-----END RSA PRIVATE KEY-----
----
.SSH Configuration properties
|===
|Property Name |Remarks
|*ignoreLocalSshSettings*
|If true, use property based SSH config instead of file based
|*privateKey*
|Valid SSH private key. Must be set if `ignoreLocalSshSettings` is true and Git URI is SSH format
|*hostKey*
|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 ,ecdsa-sha2-nistp521`. Must be set if `hostKey` is also set
|*strictHostKeyChecking*
|`true` or `false`. If false, ignore errors with host key
|===
===== Placeholders in Git Search Paths

View File

@@ -29,7 +29,7 @@ import org.springframework.context.annotation.Import;
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
@EnableConfigurationProperties(ConfigServerProperties.class)
@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,
ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class })
ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class, TransportConfiguration.class })
public class ConfigServerAutoConfiguration {
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2015 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
*
* http://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.config;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.transport.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.config.server.ssh.PropertyBasedSshSessionFactory;
import org.springframework.cloud.config.server.ssh.SshUriProperties;
import org.springframework.cloud.config.server.ssh.SshUriPropertyProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configure a callback to set up a property based SSH settings before running a transport command (such as clone or fetch)
*
* @author Ollie Hughes
*/
@Configuration
@EnableConfigurationProperties(SshUriProperties.class)
public class TransportConfiguration {
@ConditionalOnMissingBean(TransportConfigCallback.class)
@Bean
public TransportConfigCallback propertiesBasedSshTransportCallback(final SshUriProperties sshUriProperties) {
if(sshUriProperties.isIgnoreLocalSshSettings()) {
return new PropertiesBasedSshTransportConfigCallback(sshUriProperties);
}
else return new FileBasedSshTransportConfigCallback(sshUriProperties);
}
/**
* Configure JGit transport command to use a SSH session factory that is configured using properties defined
* in {@link SshUriProperties}
*/
public static class PropertiesBasedSshTransportConfigCallback implements TransportConfigCallback {
private SshUriProperties sshUriProperties;
public PropertiesBasedSshTransportConfigCallback(SshUriProperties sshUriProperties) {
this.sshUriProperties = sshUriProperties;
}
public SshUriProperties getSshUriProperties() {
return sshUriProperties;
}
@Override
public void configure(Transport transport) {
SshTransport sshTransport = (SshTransport) transport;
sshTransport.setSshSessionFactory(
new PropertyBasedSshSessionFactory(
new SshUriPropertyProcessor(sshUriProperties).getSshKeysByHostname(), new JSch()));
}
}
/**
* Configure JGit transport command to use a default SSH session factory based on local machines SSH config.
* Allow strict host key checking to be set.
*/
public static class FileBasedSshTransportConfigCallback implements TransportConfigCallback {
private SshUriProperties sshUriProperties;
public FileBasedSshTransportConfigCallback(SshUriProperties sshUriProperties) {
this.sshUriProperties = sshUriProperties;
}
public SshUriProperties getSshUriProperties() {
return sshUriProperties;
}
@Override
public void configure(Transport transport) {
SshSessionFactory.setInstance(new JschConfigSessionFactory() {
@Override
protected void configure(OpenSshConfig.Host hc, Session session) {
session.setConfig("StrictHostKeyChecking",
sshUriProperties.isStrictHostKeyChecking() ? "yes" : "no");
}
});
}
}
}

View File

@@ -21,7 +21,6 @@ import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
@@ -43,9 +42,6 @@ import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.TagOpt;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.util.FileUtils;
@@ -56,8 +52,6 @@ import org.springframework.core.io.UrlResource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.jcraft.jsch.Session;
import static org.springframework.util.StringUtils.hasText;
/**
@@ -81,8 +75,6 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
*/
private int timeout = 5;
private boolean initialized;
/**
* Flag to indicate that the repository should be cloned on startup (not on
* demand). Generally leads to slower startup but faster first query.
@@ -174,7 +166,6 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(getUri() != null, "You need to configure a uri for the git repository");
initialize();
if (this.cloneOnStart) {
initClonedRepository();
}
@@ -184,7 +175,6 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
* Get the working directory ready.
*/
public String refresh(String label) {
initialize();
Git git = null;
try {
git = createGitClient();
@@ -422,18 +412,6 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
}
}
private void initialize() {
if (!this.initialized) {
SshSessionFactory.setInstance(new JschConfigSessionFactory() {
@Override
protected void configure(Host hc, Session session) {
session.setConfig("StrictHostKeyChecking", isStrictHostKeyChecking() ? "yes" : "no");
}
});
this.initialized = true;
}
}
private void configureCommand(TransportCommand<?, ?> command) {
command.setTimeout(this.timeout);
if (this.transportConfigCallback != null) {

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2015 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
*
* http://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 com.jcraft.jsch.HostKey;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.util.Base64;
import org.eclipse.jgit.util.FS;
import java.util.Map;
/**
* In a cloud environment local SSH config files such as `.known_hosts` may not be suitable for providing
* configuration settings due to ephemeral filesystems. This flag enables SSH config to be provided as application
* properties
* @author William Tran
* @author Ollie Hughes
*/
public class PropertyBasedSshSessionFactory extends JschConfigSessionFactory {
private final Map<String, SshUriProperties> sshKeysByHostname;
private final JSch jSch;
public PropertyBasedSshSessionFactory(Map<String, SshUriProperties> sshKeysByHostname, JSch jSch) {
this.sshKeysByHostname = sshKeysByHostname;
this.jSch = jSch;
}
@Override
protected void configure(Host hc, Session session) {
SshUriProperties sshProperties = 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("StrictHostKeyChecking", "no");
} else {
session.setConfig("StrictHostKeyChecking", "yes");
}
}
@Override
protected Session createSession(Host hc, String user, String host, int port, FS fs) throws JSchException {
if (sshKeysByHostname.containsKey(host)) {
SshUriProperties sshUriProperties = sshKeysByHostname.get(host);
jSch.addIdentity(host, sshUriProperties.getPrivateKey().getBytes(), null, null);
if (sshUriProperties.getHostKey() != null) {
HostKey hostkey = new HostKey(host, Base64.decode(sshUriProperties.getHostKey()));
jSch.getHostKeyRepository().add(hostkey, null);
}
return jSch.getSession(user, host, port);
}
throw new JSchException("no keys configured for hostname " + host);
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2015 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
*
* http://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 com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.annotation.PostConstruct;
import java.util.*;
import static java.lang.String.format;
import static org.springframework.util.StringUtils.hasText;
/**
* Validate SSH related properties
*
* @author Ollie Hughes
*/
@Component
@EnableConfigurationProperties(SshUriProperties.class)
public class SshPropertyValidator {
private final SshUriProperties sshUriProperties;
private final JSch jsch = new JSch();
private static final Set<String> VALID_HOST_KEY_ALGORITHMS = new LinkedHashSet<>(Arrays.asList(
"ssh-dss","ssh-rsa","ecdsa-sha2-nistp256","ecdsa-sha2-nistp384","ecdsa-sha2-nistp521"));
private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git.";
@Autowired
public SshPropertyValidator(SshUriProperties sshUriProperties) {
this.sshUriProperties = sshUriProperties;
}
static boolean isSshUri(Object uri) {
return uri != null && (uri.toString().startsWith("ssh") || uri.toString().startsWith("git"));
}
@PostConstruct
public void validateSshConfigurationProperties() {
List<SshUriProperties> allRepoProperties = new ArrayList<>();
allRepoProperties.add(sshUriProperties);
Map<String, SshUriProperties> repos = sshUriProperties.getRepos();
if (repos != null) {
allRepoProperties.addAll(repos.values());
}
for (SshUriProperties repoProperties : allRepoProperties) {
if(isSshUri(repoProperties.getUri()) && sshUriProperties.isIgnoreLocalSshSettings()){
validatePrivateKeyPresent();
validatePrivateKeyFormat();
validateAlgorithmSpecifiedWhenHostKeySet();
validateHostKeySpecifiedWhenAlgorithmSet();
validateHostKeyAlgorithmSupported();
}
}
}
protected void validatePrivateKeyFormat() {
try {
KeyPair.load(jsch, sshUriProperties.getPrivateKey().getBytes(), null);
} catch (JSchException e) {
throw new IllegalStateException(format("Property '%sprivateKey' contains an invalid value", GIT_PROPERTY_PREFIX));
}
}
protected void validateHostKeyAlgorithmSupported() {
if (hasText(sshUriProperties.getHostKeyAlgorithm())) {
Assert.state(VALID_HOST_KEY_ALGORITHMS.contains(sshUriProperties.getHostKeyAlgorithm()),
format("Property '%shostKeyAlgorithm' must be one of %s", GIT_PROPERTY_PREFIX, VALID_HOST_KEY_ALGORITHMS));
}
}
protected void validatePrivateKeyPresent() {
Assert.state(sshUriProperties.getPrivateKey() != null,
format("Property '%sprivateKey' must be set when '%signoreLocalSshSettings' is set to 'true'", GIT_PROPERTY_PREFIX, GIT_PROPERTY_PREFIX));
}
protected void validateHostKeySpecifiedWhenAlgorithmSet() {
if (hasText(sshUriProperties.getHostKeyAlgorithm())) {
Assert.state(hasText(sshUriProperties.getHostKey()),
format("Property '%shostKey' must be set when 'hostKeyAlgorithm' is specified", GIT_PROPERTY_PREFIX));
}
}
protected void validateAlgorithmSpecifiedWhenHostKeySet() {
if (hasText(sshUriProperties.getHostKey())) {
Assert.state(hasText(sshUriProperties.getHostKeyAlgorithm()),
format("Property '%shostKeyAlgorithm' must be set when 'hostKey' is specified", GIT_PROPERTY_PREFIX));
}
}
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright 2015 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
*
* http://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 org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Data container for property based SSH config
*
* @author Ollie Hughes
*/
@ConfigurationProperties("spring.cloud.config.server.git")
public class SshUriProperties {
private String uri;
private String hostKeyAlgorithm;
private String hostKey;
private String privateKey;
private String username;
private String password;
private boolean ignoreLocalSshSettings;
private boolean strictHostKeyChecking = true;
private Map<String, SshUriProperties> repos = new HashMap<>();
public SshUriProperties(String uri, String hostKeyAlgorithm, String hostKey, String privateKey, String username, String password, boolean ignoreLocalSshSettings, boolean strictHostKeyChecking, Map<String, SshUriProperties> repos) {
this.uri = uri;
this.hostKeyAlgorithm = hostKeyAlgorithm;
this.hostKey = hostKey;
this.privateKey = privateKey;
this.username = username;
this.password = password;
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
this.strictHostKeyChecking = strictHostKeyChecking;
this.repos = repos;
}
public SshUriProperties() {
}
public static SshUriPropertiesBuilder builder() {
return new SshUriPropertiesBuilder();
}
public boolean isSshUri() {
return uri != null && !uri.startsWith("http");
}
public String getHostname() {
if (getUri() == null) {
return null;
}
if (getUri().matches("^[a-z]+://.*")) {
return UriComponentsBuilder.fromUriString(uri).build().getHost();
}
else if (getUri().indexOf('@') < getUri().indexOf(':')) {
return getUri().substring(getUri().indexOf('@') + 1, uri.indexOf(':'));
}
else if (getUri().startsWith("ssh:") && getUri().indexOf('@') > 0) {
String postAt = getUri().substring(getUri().indexOf('@') + 1);
return postAt.substring(0, postAt.indexOf(":"));
}
else return null;
}
public String getUri() {
return this.uri;
}
public String getHostKeyAlgorithm() {
return this.hostKeyAlgorithm;
}
public String getHostKey() {
return this.hostKey;
}
public String getPrivateKey() {
return this.privateKey;
}
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public boolean isIgnoreLocalSshSettings() {
return this.ignoreLocalSshSettings;
}
public boolean isStrictHostKeyChecking() {
return this.strictHostKeyChecking;
}
public Map<String, SshUriProperties> getRepos() {
return this.repos;
}
public void setUri(String uri) {
this.uri = uri;
}
public void setHostKeyAlgorithm(String hostKeyAlgorithm) {
this.hostKeyAlgorithm = hostKeyAlgorithm;
}
public void setHostKey(String hostKey) {
this.hostKey = hostKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setIgnoreLocalSshSettings(boolean ignoreLocalSshSettings) {
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
}
public void setStrictHostKeyChecking(boolean strictHostKeyChecking) {
this.strictHostKeyChecking = strictHostKeyChecking;
}
public void setRepos(Map<String, SshUriProperties> repos) {
this.repos = repos;
}
@Override
public int hashCode() {
return Objects.hash(uri, hostKeyAlgorithm, hostKey, privateKey, username, password, ignoreLocalSshSettings, strictHostKeyChecking);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final SshUriProperties other = (SshUriProperties) obj;
return Objects.equals(this.uri, other.uri)
&& Objects.equals(this.hostKeyAlgorithm, other.hostKeyAlgorithm)
&& Objects.equals(this.hostKey, other.hostKey)
&& Objects.equals(this.privateKey, other.privateKey)
&& Objects.equals(this.username, other.username)
&& Objects.equals(this.password, other.password)
&& Objects.equals(this.ignoreLocalSshSettings, other.ignoreLocalSshSettings)
&& Objects.equals(this.strictHostKeyChecking, other.strictHostKeyChecking);
}
public String toString() {
return "org.springframework.cloud.config.server.ssh.SshUriProperties(uri=" + this.getUri() + " hostKeyAlgorithm=" + this.getHostKeyAlgorithm() + ", hostKey=" + this.getHostKey() + ", privateKey=" + this.getPrivateKey() + ", username=" + this.getUsername() + ", password=" + this.getPassword() + ", ignoreLocalSshSettings=" + this.isIgnoreLocalSshSettings() + ", strictHostKeyChecking=" + this.isStrictHostKeyChecking() + ", repos=" + this.getRepos() + ")";
}
public static class SshUriPropertiesBuilder {
private String uri;
private String hostKeyAlgorithm;
private String hostKey;
private String privateKey;
private String username;
private String password;
private boolean ignoreLocalSshSettings;
private boolean strictHostKeyChecking = true;
private Map<String, SshUriProperties> repos;
SshUriPropertiesBuilder() {
}
public SshUriProperties.SshUriPropertiesBuilder uri(String uri) {
this.uri = uri;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder hostKeyAlgorithm(String hostKeyAlgorithm) {
this.hostKeyAlgorithm = hostKeyAlgorithm;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder hostKey(String hostKey) {
this.hostKey = hostKey;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder privateKey(String privateKey) {
this.privateKey = privateKey;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder username(String username) {
this.username = username;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder password(String password) {
this.password = password;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder ignoreLocalSshSettings(boolean ignoreLocalSshSettings) {
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder strictHostKeyChecking(boolean strictHostKeyChecking) {
this.strictHostKeyChecking = strictHostKeyChecking;
return this;
}
public SshUriProperties.SshUriPropertiesBuilder repos(Map<String, SshUriProperties> repos) {
this.repos = repos;
return this;
}
public SshUriProperties build() {
return new SshUriProperties(uri, hostKeyAlgorithm, hostKey, privateKey, username, password, ignoreLocalSshSettings, strictHostKeyChecking, repos);
}
public String toString() {
return "org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriPropertiesBuilder(uri=" + this.uri + "hostKeyAlgorithm=" + this.hostKeyAlgorithm + ", hostKey=" + this.hostKey + ", privateKey=" + this.privateKey + ", username=" + this.username + ", password=" + this.password + ", ignoreLocalSshSettings=" + this.ignoreLocalSshSettings + ", strictHostKeyChecking=" + this.strictHostKeyChecking + ", repos=" + this.repos + ")";
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2015 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
*
* http://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 org.springframework.web.util.UriComponentsBuilder;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.cloud.config.server.ssh.SshPropertyValidator.isSshUri;
/**
* Check if Git repo properties refer to an SSH based transport then filter and extract the properties
* @author William Tran
* @author Ollie Hughes
*/
public class SshUriPropertyProcessor {
private final SshUriProperties sshUriProperties;
public SshUriPropertyProcessor(SshUriProperties sshUriProperties) {
this.sshUriProperties = sshUriProperties;
}
public Map<String, SshUriProperties> getSshKeysByHostname() {
return extractNestedProperties(sshUriProperties);
}
private Map<String, SshUriProperties> extractNestedProperties(SshUriProperties uriProperties) {
Map<String, SshUriProperties> sshUriPropertyMap = new HashMap<>();
String parentUri = uriProperties.getUri();
if (isSshUri(parentUri) && getHostname(parentUri) != null) {
sshUriPropertyMap.put(getHostname(parentUri), uriProperties);
}
Map<String, SshUriProperties> repos = uriProperties.getRepos();
if(repos != null) {
for (SshUriProperties repoProperties : repos.values()) {
String repoUri = repoProperties.getUri();
if (isSshUri(repoUri) && getHostname(repoUri) != null) {
sshUriPropertyMap.put(getHostname(repoUri), repoProperties);
}
}
}
return sshUriPropertyMap;
}
private String getHostname(String uri) {
if (uri == null) {
return null;
}
else if (uri.matches("^[a-z]+://.*")) {
return UriComponentsBuilder.fromUriString(uri).build().getHost();
}
else if (uri.indexOf('@') < uri.indexOf(':')) {
return uri.substring(uri.indexOf('@') + 1, uri.indexOf(':'));
}
else if (uri.startsWith("ssh:") && uri.indexOf('@') > 0) {
String postAt = uri.substring(uri.indexOf('@') + 1);
return postAt.substring(0, postAt.indexOf(":"));
}
else return null;
}
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright 2015 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
*
* http://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;
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.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.config.TransportConfiguration;
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository;
import org.springframework.cloud.config.server.ssh.SshPropertyValidator;
import org.springframework.cloud.config.server.ssh.SshUriProperties;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File;
import java.lang.reflect.Method;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Integration tests for property based SSH config support
* @author Ollie Hughes
*/
public class TransportConfigurationIntegrationTests {
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
"spring.config.name:ssh/ssh-private-key-block",})
@ActiveProfiles({"test", "git"})
public static class PropertyBasedCallbackTest {
@Autowired
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
public void propertyBasedTransportCallbackIsConfigured() throws Exception {
TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class)));
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
"spring.config.name:ssh/ssh-private-key-newline"
})
@ActiveProfiles({"test", "git"})
public static class PrivateKeyPropertyWithLineBreaks {
@Autowired
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
public void privateKeyPropertyWithLineBreaks() throws Exception {
TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class)));
TransportConfiguration.PropertiesBasedSshTransportConfigCallback configCallback =
(TransportConfiguration.PropertiesBasedSshTransportConfigCallback) transportConfigCallback;
assertThat(configCallback.getSshUriProperties().getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1)));
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
"spring.config.name:ssh/ssh-nested-settings"
})
@ActiveProfiles({"test", "git"})
public static class SshPropertiesWithinNestedRepo {
@Autowired
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
public void sshPropertiesWithinNestedRepo() throws Exception {
TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class)));
TransportConfiguration.PropertiesBasedSshTransportConfigCallback configCallback =
(TransportConfiguration.PropertiesBasedSshTransportConfigCallback) transportConfigCallback;
SshUriProperties sshUriProperties = configCallback.getSshUriProperties();
assertThat(sshUriProperties.getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_1)));
assertThat(sshUriProperties.getRepos().get("repo1"), is(notNullValue()));
assertThat(sshUriProperties.getRepos().get("repo1").getPrivateKey(), is(equalTo(TestProperties.TEST_PRIVATE_KEY_2)));
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ConfigServerApplication.class, TransportConfiguration.class, SshPropertyValidator.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
"spring.cloud.config.server.git.uri=git@gitserver.com:team/repo.git",
"spring.cloud.config.server.git.ignoreLocalSshSettings=false",})
@ActiveProfiles({"test", "git"})
public static class FileBasedCallbackTest {
@Autowired
private MultipleJGitEnvironmentRepository jGitEnvironmentRepository;
@Test
public void fileBasedTransportCallbackIsConfigured() throws Exception {
TransportConfigCallback transportConfigCallback = jGitEnvironmentRepository.getTransportConfigCallback();
assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.FileBasedSshTransportConfigCallback.class)));
}
@Test
public void strictHostKeyCheckShouldCheck() throws Exception {
String uri = "git+ssh://git@somegitserver/somegitrepo";
SshSessionFactory.setInstance(null);
jGitEnvironmentRepository.setUri(uri);
jGitEnvironmentRepository.setBasedir(new File("./mybasedir"));
assertTrue(jGitEnvironmentRepository.isStrictHostKeyChecking());
jGitEnvironmentRepository.setCloneOnStart(true);
try {
// this will throw but we don't care about connecting.
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<String> keyCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> valueCaptor = ArgumentCaptor.forClass(String.class);
configure.invoke(factory, hc, session);
verify(session).setConfig(keyCaptor.capture(), valueCaptor.capture());
configure.setAccessible(false);
assertTrue("yes".equals(valueCaptor.getValue()));
}
}
}
private static class TestProperties {
private static final String TEST_PRIVATE_KEY_1 = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI\n" +
"yyxp38YcuZG9cUDBAxby+K2TKmwHaC1R61QTwbPuCRdIPrDwRz+FLoegm3iDLCmn\n" +
"uP6rjZDneYsqfU1KSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km\n" +
"ayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq\n" +
"TOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir\n" +
"+diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk\n" +
"jfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt\n" +
"lDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI\n" +
"xdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V\n" +
"4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG\n" +
"7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu\n" +
"3reKgm0CgYEA03Sx8JoF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8\n" +
"q4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq\n" +
"mRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa\n" +
"lKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M\n" +
"ZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv\n" +
"ZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit\n" +
"BT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO\n" +
"zf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw\n" +
"uC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF\n" +
"54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS\n" +
"E1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx\n" +
"4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr\n" +
"/syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq\n" +
"2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==\n" +
"-----END RSA PRIVATE KEY-----";
private static final String TEST_PRIVATE_KEY_2 = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX\n" +
"IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF\n" +
"ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud\n" +
"1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i\n" +
"oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W\n" +
"DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd\n" +
"fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b\n" +
"BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG\n" +
"EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj\n" +
"5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8\n" +
"+AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe\n" +
"pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG\n" +
"ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ\n" +
"xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW\n" +
"dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi\n" +
"PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX\n" +
"VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z\n" +
"FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R\n" +
"gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4\n" +
"VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV\n" +
"cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee\n" +
"KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N\n" +
"CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs\n" +
"q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J\n" +
"69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT\n" +
"-----END RSA PRIVATE KEY-----\n";
}
}

View File

@@ -33,7 +33,6 @@ import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -60,19 +59,14 @@ import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.TrackingRefUpdate;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.server.support.AwsCodeCommitCredentialProvider;
import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory;
@@ -677,38 +671,6 @@ public class JGitEnvironmentRepositoryTests {
}
@Test
public void strictHostKeyCheckShouldCheck() throws Exception {
String uri = "git+ssh://git@somegitserver/somegitrepo";
SshSessionFactory.setInstance(null);
JGitEnvironmentRepository envRepository = new JGitEnvironmentRepository(this.environment);
envRepository.setUri(uri);
envRepository.setBasedir(new File("./mybasedir"));
assertTrue(envRepository.isStrictHostKeyChecking());
envRepository.setCloneOnStart(true);
try {
// this will throw but we don't care about connecting.
envRepository.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<String> keyCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> valueCaptor = ArgumentCaptor.forClass(String.class);
configure.invoke(factory, hc, session);
verify(session).setConfig(keyCaptor.capture(), valueCaptor.capture());
configure.setAccessible(false);
assertTrue("yes".equals(valueCaptor.getValue()));
}
}
@Test
public void shouldPrintStacktraceIfDebugEnabled() throws Exception {
final Log mockLogger = mock(Log.class);

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2015 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
*
* http://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 com.jcraft.jsch.*;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
/**
* Unit tests for property based SSH config processor
* @author William Tran
* @author Ollie Hughes
*/
@RunWith(MockitoJUnitRunner.class)
public class PropertyBasedSshSessionFactoryTest {
private static final String HOST_KEY = "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMzCa0AcNbahUFjFYJHIilhJOhKFHuDOOuY+/HqV9kALftitwNYo6dQ+tC9IK5JVZCZfqKfDWVMxspcPDf9eMoE=";
private static final String HOST_KEY_ALGORITHM = "ecdsa-sha2-nistp256";
private static final String PRIVATE_KEY = getResourceAsString("/ssh/key");
private PropertyBasedSshSessionFactory factory;
@Mock
private Host hc;
@Mock
private Session session;
@Mock
private JSch jSch;
@Mock
private HostKeyRepository hostKeyRepository;
@Test
public void strictHostKeyCheckingIsOptional() {
SshUriProperties sshKey = new SshUriProperties.SshUriPropertiesBuilder()
.uri("ssh://gitlab.example.local:3322/somerepo.git")
.privateKey(PRIVATE_KEY)
.build();
setupSessionFactory(sshKey);
factory.configure(hc, session);
verify(session).setConfig("StrictHostKeyChecking", "no");
verifyNoMoreInteractions(session);
}
@Test
public void strictHostKeyCheckingIsUsed() {
SshUriProperties sshKey = new SshUriProperties.SshUriPropertiesBuilder()
.uri("ssh://gitlab.example.local:3322/somerepo.git")
.hostKey(HOST_KEY)
.privateKey(PRIVATE_KEY)
.build();
setupSessionFactory(sshKey);
factory.configure(hc, session);
verify(session).setConfig("StrictHostKeyChecking", "yes");
verifyNoMoreInteractions(session);
}
@Test
public void hostKeyAlgorithmIsSpecified() {
SshUriProperties sshKey = new SshUriProperties.SshUriPropertiesBuilder()
.uri("ssh://gitlab.example.local:3322/somerepo.git")
.hostKeyAlgorithm(HOST_KEY_ALGORITHM)
.hostKey(HOST_KEY)
.privateKey(PRIVATE_KEY)
.build();
setupSessionFactory(sshKey);
factory.configure(hc, session);
verify(session).setConfig("server_host_key", HOST_KEY_ALGORITHM);
verify(session).setConfig("StrictHostKeyChecking", "yes");
verifyNoMoreInteractions(session);
}
@Test
public void privateKeyIsUsed() throws Exception {
SshUriProperties sshKey = new SshUriProperties.SshUriPropertiesBuilder()
.uri("git@gitlab.example.local:someorg/somerepo.git")
.privateKey(PRIVATE_KEY)
.build();
setupSessionFactory(sshKey);
factory.createSession(hc, null, sshKey.getHostname(), 22, null);
verify(jSch).addIdentity("gitlab.example.local", PRIVATE_KEY.getBytes(), null, null);
}
@Test
public void hostKeyIsUsed() throws Exception {
SshUriProperties sshKey = new SshUriProperties.SshUriPropertiesBuilder()
.uri("git@gitlab.example.local:someorg/somerepo.git")
.hostKey(HOST_KEY)
.privateKey(PRIVATE_KEY)
.build();
setupSessionFactory(sshKey);
factory.createSession(hc, null, sshKey.getHostname(), 22, null);
ArgumentCaptor<HostKey> captor = ArgumentCaptor.forClass(HostKey.class);
verify(hostKeyRepository).add(captor.capture(), any(UserInfo.class));
HostKey hostKey = captor.getValue();
Assert.assertEquals("gitlab.example.local", hostKey.getHost());
Assert.assertEquals(HOST_KEY, hostKey.getKey());
}
private void setupSessionFactory(SshUriProperties sshKey) {
Map<String, SshUriProperties> sshKeysByHostname = new HashMap<>();
sshKeysByHostname.put(sshKey.getHostname(), sshKey);
factory = new PropertyBasedSshSessionFactory(sshKeysByHostname, jSch) ;
when(hc.getHostName()).thenReturn(sshKey.getHostname());
when(jSch.getHostKeyRepository()).thenReturn(hostKeyRepository);
}
public static String getResourceAsString(String path) {
try {
Resource resource = new ClassPathResource(path);
try (BufferedReader br = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
StringBuilder builder = new StringBuilder();
String line = "";
while ((line = br.readLine()) != null) {
builder.append(line).append('\n');
}
return builder.toString();
}
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright 2015 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
*
* http://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 org.junit.Test;
import static org.mockito.Mockito.*;
/**
* Unit tests for property based SSH config validators
*
* @author Ollie Hughes
*/
public class SshPropertyValidatorTest {
private static final String SSH_URI = "git@gitserver.com:team/repo1.git";
private static final String VALID_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI\n" +
"yyxp38YcuZG9cUDBAxby+K2TKmwHaC1R61QTwbPuCRdIPrDwRz+FLoegm3iDLCmn\n" +
"uP6rjZDneYsqfU1KSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km\n" +
"ayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq\n" +
"TOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir\n" +
"+diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk\n" +
"jfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt\n" +
"lDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI\n" +
"xdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V\n" +
"4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG\n" +
"7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu\n" +
"3reKgm0CgYEA03Sx8JoF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8\n" +
"q4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq\n" +
"mRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa\n" +
"lKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M\n" +
"ZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv\n" +
"ZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit\n" +
"BT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO\n" +
"zf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw\n" +
"uC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF\n" +
"54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS\n" +
"E1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx\n" +
"4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr\n" +
"/syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq\n" +
"2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==\n" +
"-----END RSA PRIVATE KEY-----";
private static final String VALID_HOST_KEY = "AAAAB3NzaC1yc2EAAAADAQABAAABAQDg6/W/5cbk/npvzpae7ZEa54F4rkwh2V3NiuqVZ5hWr+8O4/6SmrS7yBvRHAFeAJNb0LOCjE/7tjd1fqUx+QU1ATCtwkOhuwG8Ubzkx23mMZlrwEvx7XEfBoLN7Lw9fXjWDtTTgFB1AxCQ2pGGiNG0QCwyA4HViDHVU+ibwkRlzuDJG0tnp5Qpo3DXkHwFNdqWNfVrIZ6q2xbyeoJjKjnR215T0ehmuWFmKqG+uMNe/LQ6IOiK0F5+gr7rgPxNLAYYqyhraAnBeHn5gapsSzYJmFpoAHWvN7OUwHcJ88D9qUkKi4VKxYiuK69u3z825Xj2cLTfj9JiHCfV8cTo9GL";
@Test
public void supportedParametersSuccesful() throws Exception {
SshUriProperties validSettings = SshUriProperties.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey(VALID_PRIVATE_KEY)
.hostKey(VALID_HOST_KEY)
.hostKeyAlgorithm("ssh-rsa")
.build();
SshPropertyValidator sshPropertyValidator = spy(new SshPropertyValidator(validSettings));
sshPropertyValidator.validateSshConfigurationProperties();
verify(sshPropertyValidator, times(1)).validatePrivateKeyFormat();
verify(sshPropertyValidator, times(1)).validateAlgorithmSpecifiedWhenHostKeySet();
verify(sshPropertyValidator, times(1)).validatePrivateKeyPresent();
verify(sshPropertyValidator, times(1)).validateHostKeyAlgorithmSupported();
verify(sshPropertyValidator, times(1)).validateHostKeySpecifiedWhenAlgorithmSet();
}
@Test(expected = IllegalStateException.class)
public void invalidPrivateKeyFails() throws Exception {
SshUriProperties invalidKey = SshUriProperties.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey("invalid_key")
.build();
SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(invalidKey);
sshPropertyValidator.validateSshConfigurationProperties();
}
@Test(expected = IllegalStateException.class)
public void missingPrivateKeyFails() throws Exception {
SshUriProperties missingKey = SshUriProperties.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.build();
SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(missingKey);
sshPropertyValidator.validateSshConfigurationProperties();
}
@Test(expected = IllegalStateException.class)
public void hostKeyWithMissingAlgoFails() throws Exception {
SshUriProperties missingAlgo = SshUriProperties.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey("invalid_key")
.hostKey("some_host")
.build();
SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(missingAlgo);
sshPropertyValidator.validateSshConfigurationProperties();
}
@Test(expected = IllegalStateException.class)
public void algoWithMissingHostKeyFails() throws Exception {
SshUriProperties missingHostKey = SshUriProperties.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey("invalid_key")
.hostKeyAlgorithm("some_host_algo")
.build();
SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(missingHostKey);
sshPropertyValidator.validateSshConfigurationProperties();
}
@Test(expected = IllegalStateException.class)
public void unsupportedAlgoFails() throws Exception {
SshUriProperties unsupportedAlgo = SshUriProperties.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey("invalid_key")
.hostKey("some_host_key")
.hostKeyAlgorithm("unsupported")
.build();
SshPropertyValidator sshPropertyValidator = new SshPropertyValidator(unsupportedAlgo);
sshPropertyValidator.validateSshConfigurationProperties();
}
@Test
public void validatorNotRunIfIgnoreLocalSettingsFalse() throws Exception {
SshUriProperties useLocal = (SshUriProperties.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(false)
.privateKey("invalid_key")
.build());
SshPropertyValidator sshPropertyValidator = spy(new SshPropertyValidator(useLocal));
sshPropertyValidator.validateSshConfigurationProperties();
verify(sshPropertyValidator, times(0)).validatePrivateKeyFormat();
verify(sshPropertyValidator, times(0)).validateAlgorithmSpecifiedWhenHostKeySet();
verify(sshPropertyValidator, times(0)).validatePrivateKeyPresent();
verify(sshPropertyValidator, times(0)).validateHostKeyAlgorithmSupported();
verify(sshPropertyValidator, times(0)).validateHostKeySpecifiedWhenAlgorithmSet();
}
@Test
public void validatorNotRunIfHttpsUri() throws Exception {
SshUriProperties httpsUri = (SshUriProperties.builder()
.uri("https://somerepo.com/team/project.git")
.ignoreLocalSshSettings(true)
.privateKey("invalid_key")
.build());
SshPropertyValidator sshPropertyValidator = spy(new SshPropertyValidator(httpsUri));
sshPropertyValidator.validateSshConfigurationProperties();
verify(sshPropertyValidator, times(0)).validatePrivateKeyFormat();
verify(sshPropertyValidator, times(0)).validateAlgorithmSpecifiedWhenHostKeySet();
verify(sshPropertyValidator, times(0)).validatePrivateKeyPresent();
verify(sshPropertyValidator, times(0)).validateHostKeyAlgorithmSupported();
verify(sshPropertyValidator, times(0)).validateHostKeySpecifiedWhenAlgorithmSet();
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2015 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
*
* http://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 org.eclipse.jgit.transport.SshSessionFactory;
import org.junit.After;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
/**
* Unit tests for property based SSH config processor
* @author William Tran
* @author Ollie Hughes
*/
public class SshUriPropertyProcessorTest {
private static final String PRIVATE_KEY1 = "privateKey";
private static final String HOST_KEY1 = "hostKey";
private static final String ALGO1 = "ssh-rsa";
private static final String URI1 = "git@gitlab.test.local:wtran/my-repo";
private static final String HOST1 = "gitlab.test.local";
private static final String PRIVATE_KEY2 = "privateKey2";
private static final String URI2 = "git@gitlab2.test.local:wtran/my-repo";
private static final String HOST2 = "gitlab2.test.local";
@After
public void cleanup() {
SshSessionFactory.setInstance(null);
}
@Test
public void testSingleSshUriProperties() {
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(mainRepoPropertiesFixture());
Map<String, SshUriProperties> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(1));
SshUriProperties sshKey = sshKeysByHostname.get(HOST1);
assertMainRepo(sshKey);
}
@Test
public void testMultipleSshUriPropertiess() {
SshUriProperties sshUriProperties = mainRepoPropertiesFixture();
addRepoProperties(sshUriProperties, SshUriProperties.builder()
.uri(URI2)
.privateKey(PRIVATE_KEY2)
.build());
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties);
Map<String, SshUriProperties> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
SshUriProperties sshKey = sshKeysByHostname.get(HOST1);
assertMainRepo(sshKey);
sshKey = sshKeysByHostname.get(HOST2);
assertThat(sshKeysByHostname.values(), hasSize(2));
assertThat(sshKey.getHostname(), is(equalTo(HOST2)));
assertThat(sshKey.getHostKeyAlgorithm(), is(nullValue()));
assertThat(sshKey.getHostKey(), is(nullValue()));
assertThat(sshKey.getPrivateKey(), is(equalTo(PRIVATE_KEY2)));
}
@Test
public void testSameHostnameDifferentKeysFirstOneWins() {
SshUriProperties sshUriProperties = mainRepoPropertiesFixture();
addRepoProperties(sshUriProperties, SshUriProperties.builder().uri(URI1)
.privateKey(PRIVATE_KEY1)
.hostKey(HOST_KEY1)
.hostKeyAlgorithm(ALGO1)
.build());
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties);
Map<String, SshUriProperties> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(1));
SshUriProperties sshKey = sshKeysByHostname.get(HOST1);
assertMainRepo(sshKey);
}
@Test
public void testNoSshUriPropertiess() {
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(new SshUriProperties());
Map<String, SshUriProperties> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(0));
}
private SshUriProperties mainRepoPropertiesFixture() {
return SshUriProperties.builder()
.uri(URI1)
.hostKeyAlgorithm(ALGO1)
.hostKey(HOST_KEY1)
.privateKey(PRIVATE_KEY1)
.build();
}
private void addRepoProperties(SshUriProperties mainRepoProperties, SshUriProperties repoProperties) {
Map<String, SshUriProperties> repos = new HashMap<>();
repos.put("repo2", repoProperties);
mainRepoProperties.setRepos(repos);
}
private void assertMainRepo(SshUriProperties sshKey) {
assertThat(sshKey.getHostname(), is(equalTo(HOST1)));
assertThat(sshKey.getHostKeyAlgorithm(), is(equalTo(ALGO1)));
assertThat(sshKey.getHostKey(), is(equalTo(HOST_KEY1)));
assertThat(sshKey.getPrivateKey(), is(equalTo(PRIVATE_KEY1)));
}
}

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAwQTXywKTzJEYXQ/LOR/hDciu4tJ1Av8zQQqi4ekL2hnOUzA0
XecMmoYp31j3aWbT8RFNaUxFObfG7uGUWG21NlciqN7NtRkzTceygwBl25J9UEIo
iSHkS2arMhSIQ/B5TIFmL8fucsMUzqZ0LFwd2NR5k+BL5Ip/g6Gp6NpY2lNlxG3W
EaI/ee73vp8fJd9xge1o5Y/WFjCdP2TVfBuMTzswtjooakhQMg16c3YxcpK3Utbc
J4gaPDAGSd9eu5YuxSnTyC4s+FMPsKF3zEu+kTb5iutiCvO4lFdvlhURN2WBecRt
Am4nrMHEmLp3Yy440UTSOAzUUdxlfyltxvdOnQIDAQABAoIBABpQtbb4z/u44HB3
ocZVAVLmxGkqNkfFUVCRyakxI6uFjyqqTMlsobxDhFvt/jVBjq20JTCw9FMtKhMR
cC8qLeWNtaz+S19gYy08h6ryl/B6pVkW2/3uP/jDs51tTTKuC2uRzMPA51KAJEsU
SIxvtqYEOdseXlp3Q3FHzilv82M4dKQCUXO/BbYV1jtgGHsJIJIcJhb8JIuXGScq
krm+GrzXpejF8FjpEFJ+VRbh/wjbMqUD5/9KimtJ9tXK5MKaxZ2aerYVONrPIbRH
Ij2tKS2o+cEBL9b0E2GHSGQyzs+g4+eTmsf56O8MWAoh5uib3pKXi84wxWi1ETQd
+NIGuUECgYEA9VZuTCbJVEriqptZKw8BNn7xIlXkmABEWAmIlo8U4NodjP/WIyIB
FtwYS4jUxfJhLdgs8mzb7O+EY6F+lusQBcAViRfk6a0VL9qrzYAY21d1X0hRtt4t
NKixjQ1hESao/CQDOonsYIqolXlDsvYxDX0tWXkVjiXPVk71oPtaF80CgYEAyWhT
j1v6XtFQT4KAQ10pQEbXBet9ae4M6WALAghdROMY60zX6XdXNGugycy3T9s9q712
gxglhhhQO+RpEtaVOXvOzZv5aczrDqPwYOX1jJbIaOT4Iv2szjI518akRTef+ANW
t+mETUBf+qYKhW91lfSmi5PUPrWBs5z8j9qcohECgYEA1XgfAKO/ClkeYNRLAexM
Yd6Scg3KmXeh//hLoLVetUcT71usN2WfDQT1HsQwXwxHAlzWvxl5QvAlgYtDWo2t
o1M/acogpDK1/K2IT4bmGfQ/bGjAsR0guHEQDGtXylSZNMshBA5+XoJHrceV4GHW
zKXej7uLxeOgRLjvgOMzwQECgYAWSjJ9OiuyVK3zrritO+E12OExKYe+snbBWTy+
o9hWqF94ow9+KhP/nC22R6696sCfgh9ckM6OUaCHkFbEm/T14KrZfHqU5/XXr/MJ
dkm/nmxdl4GStcprI4ndRIGnm2lb1FrDjddziT1WCww9CkHUL7hC2EB0FDhffW9C
KGqPUQKBgQCd85DH751a6EKN8dyKUyf3sP8BraTsc26DFk3q+vfhzSyJF4wxIVWa
kmjx6kfGTu0+ED0UXWi5LnMSvne4rGU1j3rQyHs71t6JLVcFl8j5i8g/n91FA87C
EIni0JgBvgbDNlM04mMFE6K0u5+Rt3Nx/yywW/w9nQUrzNio7MP+8w==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,41 @@
spring:
cloud:
config:
server:
git:
uri: git@gitserver.com:team/repo1.git
ignoreLocalSshSettings: true
privateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI\nyyxp38YcuZG9cUDBAxby+K2TKmwHaC1R61QTwbPuCRdIPrDwRz+FLoegm3iDLCmn\nuP6rjZDneYsqfU1KSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km\nayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq\nTOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir\n+diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk\njfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt\nlDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI\nxdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V\n4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG\n7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu\n3reKgm0CgYEA03Sx8JoF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8\nq4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq\nmRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa\nlKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M\nZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv\nZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit\nBT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO\nzf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw\nuC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF\n54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS\nE1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx\n4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr\n/syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq\n2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==\n-----END RSA PRIVATE KEY-----"
repos:
repo1:
uri: git@gitserver.com:team/repo2.git
hostKey: someHostKey
hostKeyAlgorithm: ssh-rsa
privateKey: |
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX
IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF
ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud
1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i
oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W
DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd
fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b
BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG
EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj
5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8
+AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe
pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG
ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ
xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW
dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi
PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX
VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z
FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R
gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4
VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV
cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee
KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N
CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs
q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J
69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,35 @@
spring:
cloud:
config:
server:
git:
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-----

View File

@@ -0,0 +1,10 @@
spring:
cloud:
config:
server:
git:
uri: git@gitserver.com:team/repo.git
ignoreLocalSshSettings: true
privateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAoqyz6YaYMTr7L8GLPSQpAQXaM04gRx4CCsGK2kfLQdw4BlqI\nyyxp38YcuZG9cUDBAxby+K2TKmwHaC1R61QTwbPuCRdIPrDwRz+FLoegm3iDLCmn\nuP6rjZDneYsqfU1KSdrOwIbCnONfDdvYL/vnZC/o8DDMlk5Orw2SfHkT3pq0o8km\nayBwN4Sf3bpyWTY0oZcmNeSCCoIdE59k8Pa7/t9bwY9caLj05C3DEsjucc7Ei/Eq\nTOyGyobtXwaya5CqKLUHes74Poz1aEP/yVFdUud91uezd8ZK1P1t5/ZKA3R6aHir\n+diDJ2/GQ2tD511FW46yw+EtBUJTO6ADVv4UnQIDAQABAoIBAF+5qwEfX82QfKFk\njfADqFFexUDtl1biFKeJrpC2MKhn01wByH9uejrhFKQqW8UaKroLthyZ34DWIyGt\nlDnHGv0gSVF2LuAdNLdobJGt49e4+c9yD61vxzm97Eh8mRs08SM2q/VlF35E2fmI\nxdWusUImYzd8L9e+6tRd8zZl9UhG5vR5XIstKqxC6S0g79aAt0hasE4Gw1FKOf2V\n4mlL15atjQSKCPdOicuyc4zpjAtU1A9AfF51iG8oOUuJebPW8tCftfOQxaeGFgMG\n7M9aai1KzXR6M5IBAKEv31yBvz/SHTneP7oZXNLeC1GIR420PKybmeZdNK8BbEAu\n3reKgm0CgYEA03Sx8JoF5UBsIvFPpP1fjSlTgKryM5EJR6KQtj5e4YfyxccJepN8\nq4MrqDfNKleG/a1acEtDMhBNovU7Usp2QIP7zpAeioHBOhmE5WSieZGc3icOGWWq\nmRkdulSONruqWKv76ZoluxftekE03bDhZDNlcCgmrslEKB/ufHd2oc8CgYEAxPFa\nlKOdSeiYFV5CtvO8Ro8em6rGpSsVz4qkPxbeBqUDCb9KXHhq6YrhRxOIfQJKfT7M\nZFCn8ArJXKgOGu+KsvwIErFHF9g2jJMG4DOUTpkQgi2yveihFxcmz/AltyVXgrnv\nZWQbAerH77pdKKhNivLGgEv72GYawdYjYNjemdMCgYA2kEMmMahZyrDcp2YEzfit\nBT/t0K6kzcUWPgWXcSqsiZcEn+J7RbmCzFskkhmX1nQX23adyV3yejB+X0dKisHO\nzf/ZAmlPFkJVCqa3RquCMSfIT02dEhXeYZPBM/Zqeyxuqxpa4hLgX0FBLbhFiFHw\nuC5xrXql2XuD2xF//peXEwKBgQC+pa28Cg7vRxxCQzduB9CQtWc55j3aEjVQ7bNF\n54sS/5ZLT0Ra8677WZfuyDfuW9NkHvCZg4Ku2qJG8eCFrrGjxlrCTZ62tHVJ6+JS\nE1xUIdRbUIWhVZrr0VufG6hG/P0T7Y6Tpi6G0pKtvMkF3LcD9TS3adboix8H2ZXx\n4L7MRQKBgQC0OO3qqNXOjIVYWOoqXLybOY/Wqu9lxCAgGyCYaMcstnBI7W0MZTBr\n/syluvGsaFc1sE7MMGOOzKi1tF4YvDmSnzA/R1nmaPguuD9fOA+w7Pwkv5vLvuJq\n2U7EeNwxq1I1L3Ag6E7wH4BHLHd4TKaZR6agFkn8oomz71yZPGjuZQ==\n-----END RSA PRIVATE KEY-----"
hostKey: somekey
hostKeyAlgorithm: ssh-rsa