Merge branch '1.3.x'

This commit is contained in:
Dave Syer
2017-07-20 12:30:25 +01:00
28 changed files with 1970 additions and 49 deletions

View File

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

View File

@@ -151,8 +151,8 @@ This repository implementation maps the `{label}` parameter of the
HTTP resource to a git label (commit id, branch name or tag). If the
git branch or tag name contains a slash ("/") then the label in the
HTTP URL should be specified with the special string "(\_)" instead (to
avoid ambiguity with other URL paths). For example, if the label is
`foo/bar`, replacing the slash would result in a label that looks like
avoid ambiguity with other URL paths). For example, if the label is
`foo/bar`, replacing the slash would result in a label that looks like
`foo(_)bar`. Be careful with the brackets in
the URL if you are using a command line client like curl (e.g. escape
them from the shell with quotes '').
@@ -351,7 +351,7 @@ https://git-codecommit.${AWS_REGION}.amazonaws.com/${repopath}.
If you provide a username and password with an AWS CodeCommit URI, then these must be
the http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSGettingStartedGuide/AWSCredentials.html[AWS accessKeyId and secretAccessKey]
to be used to access the repository. If you do not specify a username and password,
then the accessKeyId and secretAccessKey will be retrieved using the
then the accessKeyId and secretAccessKey will be retrieved using the
http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html[AWS Default Credential Provider Chain].
If your Git URI matches the CodeCommit URI pattern (above) then you must provide
@@ -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, the JGit library used by 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. Must be set at as `spring.cloud.config.server.git.ignoreLocalSshSettings`, *not* inside a repository definition.
|*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,103 @@
/*
* 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) {
if (transport instanceof SshTransport) {
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

@@ -16,6 +16,8 @@
package org.springframework.cloud.config.server.environment;
import static org.springframework.util.StringUtils.hasText;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
@@ -58,8 +60,6 @@ import org.springframework.util.StringUtils;
import com.jcraft.jsch.Session;
import static org.springframework.util.StringUtils.hasText;
/**
* An {@link EnvironmentRepository} backed by a single git repository.
*
@@ -81,8 +81,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.
@@ -108,6 +106,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
* changes and take from remote repository.
*/
private boolean forcePull;
private boolean initialized;
public JGitEnvironmentRepository(ConfigurableEnvironment environment) {
super(environment);
@@ -187,7 +186,6 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
* Get the working directory ready.
*/
public String refresh(String label) {
initialize();
Git git = null;
try {
git = createGitClient();

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2017 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.validation.annotation.Validated;
/**
* @author Dave Syer
*
*/
@Validated
@PrivateKeyIsValid
@HostKeyAndAlgoBothExist
@HostKeyAlgoSupported
public class BaseSshUriProperties {
private String privateKey;
private String uri;
private String hostKeyAlgorithm;
private String hostKey;
private boolean ignoreLocalSshSettings;
private boolean strictHostKeyChecking = true;
public BaseSshUriProperties(String uri, String hostKeyAlgorithm, String hostKey, String privateKey, boolean ignoreLocalSshSettings, boolean strictHostKeyChecking) {
this.uri = uri;
this.hostKeyAlgorithm = hostKeyAlgorithm;
this.hostKey = hostKey;
this.privateKey = privateKey;
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
this.strictHostKeyChecking = strictHostKeyChecking;
}
public BaseSshUriProperties() {
}
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 boolean isIgnoreLocalSshSettings() {
return this.ignoreLocalSshSettings;
}
public boolean isStrictHostKeyChecking() {
return this.strictHostKeyChecking;
}
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 setIgnoreLocalSshSettings(boolean ignoreLocalSshSettings) {
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
}
public void setStrictHostKeyChecking(boolean strictHostKeyChecking) {
this.strictHostKeyChecking = strictHostKeyChecking;
}
public String toString() {
return "org.springframework.cloud.config.server.ssh.SshUriProperties(uri=" + this.getUri() + " hostKeyAlgorithm=" + this.getHostKeyAlgorithm() + ", hostKey=" + this.getHostKey() + ", privateKey=" + this.getPrivateKey() + ", ignoreLocalSshSettings=" + this.isIgnoreLocalSshSettings() + ", strictHostKeyChecking=" + this.isStrictHostKeyChecking() + ")";
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.validation.annotation.Validated;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Beans annotated with {@link HostKeyAlgoSupported} and {@link Validated} will have the constraints applied.
*
* @author Ollie Hughes
**/
@Constraint(validatedBy = HostKeyAlgoSupportedValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HostKeyAlgoSupported {
String message() default "{HostKeyAlgoSupported.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,77 @@
/*
* 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 java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.validation.annotation.Validated;
import static java.lang.String.format;
import static org.springframework.cloud.config.server.ssh.SshPropertyValidator.isSshUri;
import static org.springframework.util.StringUtils.hasText;
/**
* JSR-303 Cross Field validator that ensures that a {@link SshUriProperties} bean for the constraints:
* - If host key algo is supported
*
* Beans annotated with {@link HostKeyAlgoSupported} and {@link Validated} will have the constraints applied.
*
* @author Ollie Hughes
*/
public class HostKeyAlgoSupportedValidator implements ConstraintValidator<HostKeyAlgoSupported, SshUriProperties> {
private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git.";
private final SshPropertyValidator sshPropertyValidator = new SshPropertyValidator();
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"));
@Override
public void initialize(HostKeyAlgoSupported constrainAnnotation) {
//No special initialization of validator required
}
@Override
public boolean isValid(SshUriProperties sshUriProperties, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
Set<Boolean> validationResults = new HashSet<>();
List<SshUri> extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties);
for (SshUri extractedProperty : extractedProperties) {
if (sshUriProperties.isIgnoreLocalSshSettings() && isSshUri(extractedProperty.getUri())) {
validationResults.add(isHostKeySpecifiedWhenAlgorithmSet(extractedProperty, context));
}
}
return !validationResults.contains(false);
}
private boolean isHostKeySpecifiedWhenAlgorithmSet(SshUri sshUriProperties, ConstraintValidatorContext context) {
if (hasText(sshUriProperties.getHostKeyAlgorithm())
&& !VALID_HOST_KEY_ALGORITHMS.contains(sshUriProperties.getHostKeyAlgorithm())) {
context.buildConstraintViolationWithTemplate(
format("Property '%shostKeyAlgorithm' must be one of %s", GIT_PROPERTY_PREFIX, VALID_HOST_KEY_ALGORITHMS))
.addConstraintViolation();
return false;
}
return true;
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.validation.annotation.Validated;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Beans annotated with {@link HostKeyAndAlgoBothExist} and {@link Validated} will have the constraints applied.
* @author Ollie Hughes
*/
@Constraint(validatedBy = HostKeyAndAlgoBothExistValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HostKeyAndAlgoBothExist {
String message() default "{HostKeyAndAlgoBothExist.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,84 @@
/*
* 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 java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.validation.annotation.Validated;
import static java.lang.String.format;
import static org.springframework.cloud.config.server.ssh.SshPropertyValidator.isSshUri;
import static org.springframework.util.StringUtils.hasText;
/**
* JSR-303 Cross Field validator that ensures that a {@link SshUriProperties} bean for the constraints:
* - If host key is set then host key algo must also be set
* - If host key algo is set then host key must also be set
*
* Beans annotated with {@link HostKeyAndAlgoBothExist} and {@link Validated} will have the constraints applied.
*
* @author Ollie Hughes
*/
public class HostKeyAndAlgoBothExistValidator implements ConstraintValidator<HostKeyAndAlgoBothExist, SshUriProperties> {
private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git.";
private final SshPropertyValidator sshPropertyValidator = new SshPropertyValidator();
@Override
public void initialize(HostKeyAndAlgoBothExist constrainAnnotation) {
//No special initialization of validator required
}
@Override
public boolean isValid(SshUriProperties sshUriProperties, ConstraintValidatorContext context) {
Set<Boolean> validationResults = new HashSet<>();
List<SshUri> extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties);
for (SshUri extractedProperty : extractedProperties) {
if (sshUriProperties.isIgnoreLocalSshSettings() && isSshUri(extractedProperty.getUri())) {
validationResults.add(
isAlgorithmSpecifiedWhenHostKeySet(extractedProperty, context)
&& isHostKeySpecifiedWhenAlgorithmSet(extractedProperty, context));
}
}
return !validationResults.contains(false);
}
private boolean isHostKeySpecifiedWhenAlgorithmSet(SshUri sshUriProperties, ConstraintValidatorContext context) {
if (hasText(sshUriProperties.getHostKeyAlgorithm()) && !hasText(sshUriProperties.getHostKey())) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
format("Property '%shostKey' must be set when '%shostKeyAlgorithm' is specified", GIT_PROPERTY_PREFIX, GIT_PROPERTY_PREFIX))
.addConstraintViolation();
return false;
}
return true;
}
private boolean isAlgorithmSpecifiedWhenHostKeySet(SshUri sshUriProperties, ConstraintValidatorContext context) {
if (hasText(sshUriProperties.getHostKey()) && !hasText(sshUriProperties.getHostKeyAlgorithm())) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
format("Property '%shostKeyAlgorithm' must be set when '%shostKey' is specified", GIT_PROPERTY_PREFIX, GIT_PROPERTY_PREFIX))
.addConstraintViolation();
return false;
}
return true;
}
}

View File

@@ -0,0 +1,24 @@
package org.springframework.cloud.config.server.ssh;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import org.springframework.validation.annotation.Validated;
/**
* Beans annotated with {@link PrivateKeyValidator} and {@link Validated} will have the constraints applied.
*
* @author Ollie Hughes
*/
@Constraint(validatedBy = PrivateKeyValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivateKeyIsValid {
String message() default "{PrivateKeyIsValid.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,89 @@
/*
* 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 java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
import org.springframework.validation.annotation.Validated;
import static java.lang.String.format;
import static org.springframework.cloud.config.server.ssh.SshPropertyValidator.isSshUri;
import static org.springframework.util.StringUtils.hasText;
/**
* JSR-303 Cross Field validator that ensures that a {@link SshUriProperties} bean for the constraints:
* - Private key is present and can be correctly parsed using {@link com.jcraft.jsch.KeyPair}
*
* Beans annotated with {@link PrivateKeyValidator} and {@link Validated} will have the constraints applied.
*
* @author Ollie Hughes
*/
public class PrivateKeyValidator implements ConstraintValidator<PrivateKeyIsValid, SshUriProperties> {
private static final String GIT_PROPERTY_PREFIX = "spring.cloud.config.server.git.";
private final SshPropertyValidator sshPropertyValidator = new SshPropertyValidator();
@Override
public void initialize(PrivateKeyIsValid constrainAnnotation) {
//No special initialization of validator required
}
@Override
public boolean isValid(SshUriProperties sshUriProperties, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
Set<Boolean> validationResults = new HashSet<>();
List<SshUri> extractedProperties = sshPropertyValidator.extractRepoProperties(sshUriProperties);
for (SshUri extractedProperty : extractedProperties) {
if (sshUriProperties.isIgnoreLocalSshSettings() && isSshUri(extractedProperty.getUri())) {
validationResults.add(
isPrivateKeyPresent(extractedProperty, context)
&& isPrivateKeyFormatCorrect(extractedProperty, context));
}
}
return !validationResults.contains(false);
}
private boolean isPrivateKeyPresent(SshUri sshUriProperties, ConstraintValidatorContext context) {
if (!hasText(sshUriProperties.getPrivateKey())) {
context.buildConstraintViolationWithTemplate(
format("Property '%shostKey' must be set when '%shostKeyAlgorithm' is specified", GIT_PROPERTY_PREFIX, GIT_PROPERTY_PREFIX))
.addConstraintViolation();
return false;
}
return true;
}
private boolean isPrivateKeyFormatCorrect(SshUri sshUriProperties, ConstraintValidatorContext context) {
try {
KeyPair.load(new JSch(), sshUriProperties.getPrivateKey().getBytes(), null);
return true;
} catch (JSchException e) {
context.buildConstraintViolationWithTemplate(
format("Property '%sprivateKey' contains is not a valid private key", GIT_PROPERTY_PREFIX))
.addConstraintViolation();
return false;
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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 java.util.Map;
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;
/**
* 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 static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
private static final String YES_OPTION = "yes";
private static final String NO_OPTION = "no";
private static final String SERVER_HOST_KEY = "server_host_key";
private final Map<String, SshUri> sshKeysByHostname;
private final JSch jSch;
public PropertyBasedSshSessionFactory(Map<String, SshUri> sshKeysByHostname, JSch jSch) {
this.sshKeysByHostname = sshKeysByHostname;
this.jSch = jSch;
}
@Override
protected void configure(Host hc, Session session) {
SshUri 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(STRICT_HOST_KEY_CHECKING, NO_OPTION);
} else {
session.setConfig(STRICT_HOST_KEY_CHECKING, YES_OPTION);
}
}
@Override
protected Session createSession(Host hc, String user, String host, int port, FS fs) throws JSchException {
if (sshKeysByHostname.containsKey(host)) {
SshUri 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,67 @@
/*
* 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 java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.transport.URIish;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import static org.springframework.util.StringUtils.hasText;
/**
* Validate SSH related properties
*
* @author Ollie Hughes
*/
@Component
@EnableConfigurationProperties(SshUriProperties.class)
public class SshPropertyValidator {
protected static boolean isSshUri(Object uri) {
if(uri != null) {
try {
URIish urIish = new URIish(uri.toString());
String scheme = urIish.getScheme();
if(scheme == null && hasText(urIish.getHost()) && hasText(urIish.getUser())) {
//JGit returns null if using SCP URI but user and host will be populated
return true;
}
return scheme != null && !scheme.matches("^(http|https)$");
} catch (URISyntaxException e) {
return false;
}
}
return false;
}
protected List<SshUri> extractRepoProperties(SshUriProperties sshUriProperties) {
List<SshUri> allRepoProperties = new ArrayList<>();
allRepoProperties.add(sshUriProperties);
Map<String, SshUriProperties.SshUriNestedRepoProperties> repos = sshUriProperties.getRepos();
if (repos != null) {
allRepoProperties.addAll(repos.values());
}
return allRepoProperties;
}
}

View File

@@ -0,0 +1,150 @@
package org.springframework.cloud.config.server.ssh;
import org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriNestedRepoProperties;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Base class that contains configuration properties for Git SSH properties
*
* @author Ollie Hughes
*/
public abstract class SshUri {
private String privateKey;
private String uri;
private String hostKeyAlgorithm;
private String hostKey;
private boolean ignoreLocalSshSettings;
private boolean strictHostKeyChecking = true;
public static SshUriPropertiesBuilder builder() {
return new SshUriPropertiesBuilder();
}
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 boolean isIgnoreLocalSshSettings() {
return this.ignoreLocalSshSettings;
}
public boolean isStrictHostKeyChecking() {
return this.strictHostKeyChecking;
}
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 setIgnoreLocalSshSettings(boolean ignoreLocalSshSettings) {
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
}
public void setStrictHostKeyChecking(boolean strictHostKeyChecking) {
this.strictHostKeyChecking = strictHostKeyChecking;
}
public String toString() {
return "org.springframework.cloud.config.server.ssh.SshUriProperties(uri=" + this.getUri() + " hostKeyAlgorithm=" + this.getHostKeyAlgorithm() + ", hostKey=" + this.getHostKey() + ", privateKey=" + this.getPrivateKey() + ", ignoreLocalSshSettings=" + this.isIgnoreLocalSshSettings() + ", strictHostKeyChecking=" + this.isStrictHostKeyChecking() + ",)";
}
public static class SshUriPropertiesBuilder {
private String uri;
private String hostKeyAlgorithm;
private String hostKey;
private String privateKey;
private boolean ignoreLocalSshSettings;
private boolean strictHostKeyChecking = true;
private Map<String, SshUriNestedRepoProperties> repos = new LinkedHashMap<>();
SshUriPropertiesBuilder() {
}
public SshUri.SshUriPropertiesBuilder uri(String uri) {
this.uri = uri;
return this;
}
public SshUri.SshUriPropertiesBuilder hostKeyAlgorithm(String hostKeyAlgorithm) {
this.hostKeyAlgorithm = hostKeyAlgorithm;
return this;
}
public SshUri.SshUriPropertiesBuilder hostKey(String hostKey) {
this.hostKey = hostKey;
return this;
}
public SshUri.SshUriPropertiesBuilder privateKey(String privateKey) {
this.privateKey = privateKey;
return this;
}
public SshUri.SshUriPropertiesBuilder ignoreLocalSshSettings(boolean ignoreLocalSshSettings) {
this.ignoreLocalSshSettings = ignoreLocalSshSettings;
return this;
}
public SshUri.SshUriPropertiesBuilder strictHostKeyChecking(boolean strictHostKeyChecking) {
this.strictHostKeyChecking = strictHostKeyChecking;
return this;
}
public SshUri.SshUriPropertiesBuilder repos(Map<String, SshUriNestedRepoProperties> repos) {
this.repos = repos;
return this;
}
public SshUriProperties build() {
SshUriProperties sshUriProperties = new SshUriProperties();
sshUriProperties.setRepos(repos);
build(sshUriProperties);
return sshUriProperties;
}
public SshUriNestedRepoProperties buildAsNestedRepo() {
SshUriNestedRepoProperties sshUriNestedRepoProperties = new SshUriNestedRepoProperties();
build(sshUriNestedRepoProperties);
return sshUriNestedRepoProperties;
}
private void build(SshUri sshUriNestedRepoProperties) {
sshUriNestedRepoProperties.setUri(uri);
sshUriNestedRepoProperties.setHostKeyAlgorithm(hostKeyAlgorithm);
sshUriNestedRepoProperties.setHostKey(hostKey);
sshUriNestedRepoProperties.setPrivateKey(privateKey);
sshUriNestedRepoProperties.setIgnoreLocalSshSettings(ignoreLocalSshSettings);
sshUriNestedRepoProperties.setStrictHostKeyChecking(strictHostKeyChecking);
}
public String toString() {
return "org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriPropertiesBuilder(uri=" + this.uri + "hostKeyAlgorithm=" + this.hostKeyAlgorithm + ", hostKey=" + this.hostKey + ", privateKey=" + this.privateKey + ", ignoreLocalSshSettings=" + this.ignoreLocalSshSettings + ", strictHostKeyChecking=" + this.strictHostKeyChecking + ", repos=" + this.repos + ")";
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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 java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
/**
* Data container for property based SSH config
*
* @author Ollie Hughes
*/
@ConfigurationProperties("spring.cloud.config.server.git")
@Validated
@PrivateKeyIsValid
@HostKeyAndAlgoBothExist
@HostKeyAlgoSupported
public class SshUriProperties extends SshUri {
private Map<String, SshUriProperties.SshUriNestedRepoProperties> repos = new LinkedHashMap<>();
public Map<String, SshUriProperties.SshUriNestedRepoProperties> getRepos() {
return this.repos;
}
public void setRepos(Map<String, SshUriNestedRepoProperties> repos) {
this.repos = repos;
}
public void addRepo(String repoName, SshUriProperties.SshUriNestedRepoProperties properties) {
this.repos.put(repoName, properties);
}
@Override
public String toString() {
return super.toString() + "{repos=" + repos + "}";
}
/**
* Differentiate between sets of properties that are defined in nested Git repos.
* This is to prevent boot from guarding against a potential infinite deserialization of nested properties.
* This sub class differentiates from {@link SshUriProperties} as it does not contain the self mao
*/
public static class SshUriNestedRepoProperties extends SshUri {
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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 java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jgit.transport.URIish;
import org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriNestedRepoProperties;
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, SshUri> getSshKeysByHostname() {
return extractNestedProperties(sshUriProperties);
}
private Map<String, SshUri> extractNestedProperties(SshUriProperties uriProperties) {
Map<String, SshUri> sshUriPropertyMap = new HashMap<>();
String parentUri = uriProperties.getUri();
if (isSshUri(parentUri) && getHostname(parentUri) != null) {
sshUriPropertyMap.put(getHostname(parentUri), uriProperties);
}
Map<String, SshUriNestedRepoProperties> repos = uriProperties.getRepos();
if(repos != null) {
for (SshUriNestedRepoProperties repoProperties : repos.values()) {
String repoUri = repoProperties.getUri();
if (isSshUri(repoUri) && getHostname(repoUri) != null) {
sshUriPropertyMap.put(getHostname(repoUri), repoProperties);
}
}
}
return sshUriPropertyMap;
}
protected static String getHostname(String uri) {
try {
URIish urIish = new URIish(uri);
return urIish.getHost();
} catch (URISyntaxException e) {
return null;
}
}
}

View File

@@ -18,4 +18,3 @@ server:
port: 8888
management:
context_path: /admin

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

@@ -0,0 +1,55 @@
/*
* 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 org.eclipse.jgit.api.TransportConfigCallback;
import org.junit.Test;
import org.springframework.cloud.config.server.ssh.SshUri;
import org.springframework.cloud.config.server.ssh.SshUriProperties;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.instanceOf;
/**
* @author Ollie Hughes
*/
public class TransportConfigurationTest {
@Test
public void propertiesBasedSshTransportCallbackCreated() throws Exception {
SshUriProperties ignoreLocalSettings = SshUri.builder()
.uri("user@gitrepo.com:proj/repo")
.ignoreLocalSshSettings(true)
.build();
TransportConfiguration transportConfiguration = new TransportConfiguration();
TransportConfigCallback transportConfigCallback = transportConfiguration.propertiesBasedSshTransportCallback(ignoreLocalSettings);
assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.PropertiesBasedSshTransportConfigCallback.class)));
}
@Test
public void fileBasedSshTransportCallbackCreated() throws Exception {
SshUriProperties dontIgnoreLocalSettings = SshUri.builder()
.uri("user@gitrepo.com:proj/repo")
.ignoreLocalSshSettings(false)
.build();
TransportConfiguration transportConfiguration = new TransportConfiguration();
TransportConfigCallback transportConfigCallback = transportConfiguration.propertiesBasedSshTransportCallback(dontIgnoreLocalSettings);
assertThat(transportConfigCallback, is(instanceOf(TransportConfiguration.FileBasedSshTransportConfigCallback.class)));
}
}

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,159 @@
/*
* 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.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() {
SshUri 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() {
SshUri 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() {
SshUri 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 {
SshUri sshKey = new SshUriProperties.SshUriPropertiesBuilder()
.uri("git@gitlab.example.local:someorg/somerepo.git")
.privateKey(PRIVATE_KEY)
.build();
setupSessionFactory(sshKey);
factory.createSession(hc, null, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 22, null);
verify(jSch).addIdentity("gitlab.example.local", PRIVATE_KEY.getBytes(), null, null);
}
@Test
public void hostKeyIsUsed() throws Exception {
SshUri 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, SshUriPropertyProcessor.getHostname(sshKey.getUri()), 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(SshUri sshKey) {
Map<String, SshUri> sshKeysByHostname = new HashMap<>();
sshKeysByHostname.put(SshUriPropertyProcessor.getHostname(sshKey.getUri()), sshKey);
factory = new PropertyBasedSshSessionFactory(sshKeysByHostname, jSch) ;
when(hc.getHostName()).thenReturn(SshUriPropertyProcessor.getHostname(sshKey.getUri()));
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,188 @@
/*
* 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.BeforeClass;
import org.junit.Test;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
/**
* 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";
private static Validator validator;
@BeforeClass
public static void setUpValidator() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@Test
public void supportedParametersSuccesful() throws Exception {
SshUriProperties validSettings = SshUri.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey(VALID_PRIVATE_KEY)
.hostKey(VALID_HOST_KEY)
.hostKeyAlgorithm("ssh-rsa")
.build();
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(validSettings);
assertThat(constraintViolations, hasSize(0));
}
@Test
public void invalidPrivateKeyFails() throws Exception {
SshUriProperties invalidKey = SshUri.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey("invalid_key")
.build();
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(invalidKey);
assertThat(constraintViolations, hasSize(1));
}
@Test
public void missingPrivateKeyFails() throws Exception {
SshUriProperties missingKey = SshUri.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.build();
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(missingKey);
assertThat(constraintViolations, hasSize(1));
}
@Test
public void hostKeyWithMissingAlgoFails() throws Exception {
SshUriProperties missingAlgo = SshUri.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey(VALID_PRIVATE_KEY)
.hostKey("some_host")
.build();
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(missingAlgo);
assertThat(constraintViolations, hasSize(1));
}
@Test
public void algoWithMissingHostKeyFails() throws Exception {
SshUriProperties missingHostKey = SshUri.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey(VALID_PRIVATE_KEY)
.hostKeyAlgorithm("ssh-rsa")
.build();
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(missingHostKey);
assertThat(constraintViolations, hasSize(1));
}
@Test
public void unsupportedAlgoFails() throws Exception {
SshUriProperties unsupportedAlgo = SshUri.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(true)
.privateKey(VALID_PRIVATE_KEY)
.hostKey("some_host_key")
.hostKeyAlgorithm("unsupported")
.build();
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(unsupportedAlgo);
assertThat(constraintViolations, hasSize(1));
}
@Test
public void validatorNotRunIfIgnoreLocalSettingsFalse() throws Exception {
SshUriProperties useLocal = (SshUri.builder()
.uri(SSH_URI)
.ignoreLocalSshSettings(false)
.privateKey("invalid_key")
.build());
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(useLocal);
assertThat(constraintViolations, hasSize(0));
}
@Test
public void validatorNotRunIfHttpsUri() throws Exception {
SshUriProperties httpsUri = (SshUri.builder()
.uri("https://somerepo.com/team/project.git")
.ignoreLocalSshSettings(true)
.privateKey("invalid_key")
.build());
Set<ConstraintViolation<SshUriProperties>> constraintViolations = validator.validate(httpsUri);
assertThat(constraintViolations, hasSize(0));
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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 java.util.Map;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.junit.After;
import org.junit.Test;
import org.springframework.cloud.config.server.ssh.SshUriProperties.SshUriNestedRepoProperties;
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 = "ollie@gitlab1.test.local:project/my-repo";
private static final String HOST1 = "gitlab1.test.local";
private static final String PRIVATE_KEY2 = "privateKey2";
private static final String URI2 = "ssh://git@gitlab2.test.local/wtran/my-repo";
private static final String HOST2 = "gitlab2.test.local";
private static final String PRIVATE_KEY3 = "privateKey3";
private static final String URI3 = "git+ssh://git@gitlab3.test.local/wtran/my-repo";
private static final String HOST3 = "gitlab3.test.local";
@After
public void cleanup() {
SshSessionFactory.setInstance(null);
}
@Test
public void testSingleSshUriProperties() {
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(mainRepoPropertiesFixture());
Map<String, SshUri> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(1));
SshUri sshKey = sshKeysByHostname.get(HOST1);
assertMainRepo(sshKey);
}
@Test
public void testMultipleSshUriPropertiess() {
SshUriProperties sshUriProperties = mainRepoPropertiesFixture();
addRepoProperties(sshUriProperties, SshUri.builder()
.uri(URI2)
.privateKey(PRIVATE_KEY2)
.buildAsNestedRepo(), "repo2");
addRepoProperties(sshUriProperties, SshUri.builder()
.uri(URI3)
.privateKey(PRIVATE_KEY3)
.buildAsNestedRepo(), "repo3");
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties);
Map<String, SshUri> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(3));
SshUri sshKey1 = sshKeysByHostname.get(HOST1);
assertMainRepo(sshKey1);
SshUri sshKey2 = sshKeysByHostname.get(HOST2);
assertThat(SshUriPropertyProcessor.getHostname(sshKey2.getUri()), is(equalTo(HOST2)));
assertThat(sshKey2.getHostKeyAlgorithm(), is(nullValue()));
assertThat(sshKey2.getHostKey(), is(nullValue()));
assertThat(sshKey2.getPrivateKey(), is(equalTo(PRIVATE_KEY2)));
SshUri sshKey3 = sshKeysByHostname.get(HOST3);
assertThat(SshUriPropertyProcessor.getHostname(sshKey3.getUri()), is(equalTo(HOST3)));
assertThat(sshKey3.getHostKeyAlgorithm(), is(nullValue()));
assertThat(sshKey3.getHostKey(), is(nullValue()));
assertThat(sshKey3.getPrivateKey(), is(equalTo(PRIVATE_KEY3)));
}
@Test
public void testSameHostnameDifferentKeysFirstOneWins() {
SshUriProperties sshUriProperties = mainRepoPropertiesFixture();
addRepoProperties(sshUriProperties, SshUri.builder().uri(URI1)
.privateKey(PRIVATE_KEY1)
.hostKey(HOST_KEY1)
.hostKeyAlgorithm(ALGO1)
.buildAsNestedRepo(), "repo2");
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(sshUriProperties);
Map<String, SshUri> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(1));
SshUri sshKey = sshKeysByHostname.get(HOST1);
assertMainRepo(sshKey);
}
@Test
public void testNoSshUriProperties() {
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(new SshUriProperties());
Map<String, SshUri> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(0));
}
@Test
public void testInvalidUriDoesNotAddEntry() {
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(SshUri.builder().uri("invalid_uri").build());
Map<String, SshUri> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(0));
}
@Test
public void testHttpsUriDoesNotAddEntry() {
SshUriPropertyProcessor sshUriPropertyProcessor = new SshUriPropertyProcessor(SshUri.builder().uri("https://user@github.com/proj/repo.git").build());
Map<String, SshUri> sshKeysByHostname = sshUriPropertyProcessor.getSshKeysByHostname();
assertThat(sshKeysByHostname.values(), hasSize(0));
}
private SshUriProperties mainRepoPropertiesFixture() {
return SshUri.builder()
.uri(URI1)
.hostKeyAlgorithm(ALGO1)
.hostKey(HOST_KEY1)
.privateKey(PRIVATE_KEY1)
.build();
}
private void addRepoProperties(SshUriProperties mainRepoProperties, SshUriNestedRepoProperties repoProperties, String repoName) {
mainRepoProperties.addRepo(repoName, repoProperties);
}
private void assertMainRepo(SshUri sshKey) {
assertThat(sshKey, is(notNullValue()));
assertThat(SshUriPropertyProcessor.getHostname(sshKey.getUri()), 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