Add support for additional Vault authentication methods. (#1526)
* Add support for additional Vault authentication methods. * Fix Vault client config test setup so it doesn't rely on local state. Fixes gh-1475
This commit is contained in:
committed by
Spencer Gibb
parent
bfe4658f4b
commit
814d0b1e39
6
pom.xml
6
pom.xml
@@ -29,6 +29,7 @@
|
||||
<bintray.package>config</bintray.package>
|
||||
<spring-cloud-commons.version>2.2.1.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<aws-java-sdk.version>1.11.52</aws-java-sdk.version>
|
||||
<google-api-services-iam.version>v1-rev20191010-1.30.3</google-api-services-iam.version>
|
||||
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError>
|
||||
<maven-checkstyle-plugin.failsOnViolation>true
|
||||
</maven-checkstyle-plugin.failsOnViolation>
|
||||
@@ -76,6 +77,11 @@
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
<version>${aws-java-sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-iam</artifactId>
|
||||
<version>${google-api-services-iam.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
|
||||
@@ -99,6 +99,11 @@
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-iam</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
|
||||
@@ -71,6 +71,7 @@ import org.springframework.cloud.config.server.environment.SvnKitEnvironmentRepo
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultClientConfiguration;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.support.GoogleCloudSourceSupport;
|
||||
@@ -244,12 +245,19 @@ public class EnvironmentRepositoryConfiguration {
|
||||
@ConditionalOnClass(VaultTemplate.class)
|
||||
static class SpringVaultFactoryConfig {
|
||||
|
||||
@Bean
|
||||
public SpringVaultClientConfiguration vaultClientConfiguration(
|
||||
VaultEnvironmentProperties vaultProperties,
|
||||
ConfigTokenProvider tokenProvider) {
|
||||
return new SpringVaultClientConfiguration(vaultProperties, tokenProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringVaultEnvironmentRepositoryFactory vaultEnvironmentRepositoryFactory(
|
||||
ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
|
||||
ConfigTokenProvider tokenProvider) {
|
||||
SpringVaultClientConfiguration vaultClientConfiguration) {
|
||||
return new SpringVaultEnvironmentRepositoryFactory(request, watch,
|
||||
tokenProvider);
|
||||
vaultClientConfiguration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,17 +16,24 @@
|
||||
|
||||
package org.springframework.cloud.config.server.environment;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.proxy.ProxyHostProperties;
|
||||
import org.springframework.cloud.config.server.support.HttpEnvironmentRepositoryProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
* @author Haroun Pacquee
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.vault")
|
||||
public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProperties {
|
||||
@@ -79,6 +86,31 @@ public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProp
|
||||
*/
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Static vault token. Required if {@link #authentication} is {@code TOKEN}.
|
||||
*/
|
||||
private String token;
|
||||
|
||||
private AppRoleProperties appRole = new AppRoleProperties();
|
||||
|
||||
private AwsEc2Properties awsEc2 = new AwsEc2Properties();
|
||||
|
||||
private AwsIamProperties awsIam = new AwsIamProperties();
|
||||
|
||||
private AzureMsiProperties azureMsi = new AzureMsiProperties();
|
||||
|
||||
private GcpGceProperties gcpGce = new GcpGceProperties();
|
||||
|
||||
private GcpIamProperties gcpIam = new GcpIamProperties();
|
||||
|
||||
private KubernetesProperties kubernetes = new KubernetesProperties();
|
||||
|
||||
private PcfProperties pcf = new PcfProperties();
|
||||
|
||||
private Ssl ssl = new Ssl();
|
||||
|
||||
private AuthenticationMethod authentication;
|
||||
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
@@ -180,4 +212,685 @@ public class VaultEnvironmentProperties implements HttpEnvironmentRepositoryProp
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public AppRoleProperties getAppRole() {
|
||||
return this.appRole;
|
||||
}
|
||||
|
||||
public AwsEc2Properties getAwsEc2() {
|
||||
return this.awsEc2;
|
||||
}
|
||||
|
||||
public AwsIamProperties getAwsIam() {
|
||||
return this.awsIam;
|
||||
}
|
||||
|
||||
public AzureMsiProperties getAzureMsi() {
|
||||
return this.azureMsi;
|
||||
}
|
||||
|
||||
public GcpGceProperties getGcpGce() {
|
||||
return this.gcpGce;
|
||||
}
|
||||
|
||||
public GcpIamProperties getGcpIam() {
|
||||
return this.gcpIam;
|
||||
}
|
||||
|
||||
public KubernetesProperties getKubernetes() {
|
||||
return this.kubernetes;
|
||||
}
|
||||
|
||||
public PcfProperties getPcf() {
|
||||
return this.pcf;
|
||||
}
|
||||
|
||||
public Ssl getSsl() {
|
||||
return this.ssl;
|
||||
}
|
||||
|
||||
public void setAuthentication(AuthenticationMethod authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public AuthenticationMethod getAuthentication() {
|
||||
return authentication;
|
||||
}
|
||||
|
||||
public enum AuthenticationMethod {
|
||||
|
||||
/**
|
||||
* Vault AppRole machine authentication.
|
||||
*/
|
||||
APPROLE,
|
||||
|
||||
/**
|
||||
* Amazon Web Services Compute authentication.
|
||||
*/
|
||||
AWS_EC2,
|
||||
|
||||
/**
|
||||
* Amazon Web Services IAM authentication.
|
||||
*/
|
||||
AWS_IAM,
|
||||
|
||||
/**
|
||||
* Azure Cloud MSI authentication.
|
||||
*/
|
||||
AZURE_MSI,
|
||||
|
||||
/**
|
||||
* TLS certificate authentication.
|
||||
*/
|
||||
CERT,
|
||||
|
||||
/**
|
||||
* Cubbyhole token authentication.
|
||||
*/
|
||||
CUBBYHOLE,
|
||||
|
||||
/**
|
||||
* Google Cloud Compute authentication.
|
||||
*/
|
||||
GCP_GCE,
|
||||
|
||||
/**
|
||||
* Google Cloud IAM authentication.
|
||||
*/
|
||||
GCP_IAM,
|
||||
|
||||
/**
|
||||
* Kubernetes service account token authentication.
|
||||
*/
|
||||
KUBERNETES,
|
||||
|
||||
/**
|
||||
* Cloud Foundry instance identity certificate authentication.
|
||||
*/
|
||||
PCF,
|
||||
|
||||
/**
|
||||
* Static token authentication.
|
||||
*/
|
||||
TOKEN
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AppRole properties.
|
||||
*/
|
||||
@Validated
|
||||
public static class AppRoleProperties {
|
||||
|
||||
/**
|
||||
* Mount path of the AppRole authentication backend.
|
||||
*/
|
||||
private String appRolePath = "approle";
|
||||
|
||||
/**
|
||||
* Name of the role, optional, used for pull-mode.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
/**
|
||||
* The RoleId.
|
||||
*/
|
||||
private String roleId = null;
|
||||
|
||||
/**
|
||||
* The SecretId.
|
||||
*/
|
||||
private String secretId = null;
|
||||
|
||||
public String getAppRolePath() {
|
||||
return this.appRolePath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public String getRoleId() {
|
||||
return this.roleId;
|
||||
}
|
||||
|
||||
public String getSecretId() {
|
||||
return this.secretId;
|
||||
}
|
||||
|
||||
public void setAppRolePath(String appRolePath) {
|
||||
this.appRolePath = appRolePath;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setRoleId(String roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public void setSecretId(String secretId) {
|
||||
this.secretId = secretId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AWS-EC2 properties.
|
||||
*/
|
||||
@Validated
|
||||
public static class AwsEc2Properties {
|
||||
|
||||
/**
|
||||
* URL of the AWS-EC2 PKCS7 identity document.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String identityDocument = "http://169.254.169.254/latest/dynamic/instance-identity/pkcs7";
|
||||
|
||||
/**
|
||||
* Mount path of the AWS-EC2 authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String awsEc2Path = "aws-ec2";
|
||||
|
||||
/**
|
||||
* Name of the role, optional.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
/**
|
||||
* Nonce used for AWS-EC2 authentication. An empty nonce defaults to nonce
|
||||
* generation.
|
||||
*/
|
||||
private String nonce;
|
||||
|
||||
public String getIdentityDocument() {
|
||||
return this.identityDocument;
|
||||
}
|
||||
|
||||
public String getAwsEc2Path() {
|
||||
return this.awsEc2Path;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public String getNonce() {
|
||||
return this.nonce;
|
||||
}
|
||||
|
||||
public void setIdentityDocument(String identityDocument) {
|
||||
this.identityDocument = identityDocument;
|
||||
}
|
||||
|
||||
public void setAwsEc2Path(String awsEc2Path) {
|
||||
this.awsEc2Path = awsEc2Path;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setNonce(String nonce) {
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AWS-IAM properties.
|
||||
*/
|
||||
public static class AwsIamProperties {
|
||||
|
||||
/**
|
||||
* Mount path of the AWS authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String awsPath = "aws";
|
||||
|
||||
/**
|
||||
* Name of the role, optional. Defaults to the friendly IAM name if not set.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
/**
|
||||
* Name of the server used to set {@code X-Vault-AWS-IAM-Server-ID} header in the
|
||||
* headers of login requests.
|
||||
*/
|
||||
private String serverName;
|
||||
|
||||
/**
|
||||
* STS server URI.
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
private URI endpointUri;
|
||||
|
||||
public String getAwsPath() {
|
||||
return this.awsPath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return this.serverName;
|
||||
}
|
||||
|
||||
public void setAwsPath(String awsPath) {
|
||||
this.awsPath = awsPath;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public URI getEndpointUri() {
|
||||
return this.endpointUri;
|
||||
}
|
||||
|
||||
public void setEndpointUri(URI endpointUri) {
|
||||
this.endpointUri = endpointUri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Azure MSI properties.
|
||||
*/
|
||||
public static class AzureMsiProperties {
|
||||
|
||||
/**
|
||||
* Mount path of the Azure MSI authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String azurePath = "azure";
|
||||
|
||||
/**
|
||||
* Name of the role.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
public String getAzurePath() {
|
||||
return this.azurePath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public void setAzurePath(String azurePath) {
|
||||
this.azurePath = azurePath;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* GCP-GCE properties.
|
||||
*/
|
||||
public static class GcpGceProperties {
|
||||
|
||||
/**
|
||||
* Mount path of the Kubernetes authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String gcpPath = "gcp";
|
||||
|
||||
/**
|
||||
* Name of the role against which the login is being attempted.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
/**
|
||||
* Optional service account id. Using the default id if left unconfigured.
|
||||
*/
|
||||
private String serviceAccount = "";
|
||||
|
||||
public String getGcpPath() {
|
||||
return this.gcpPath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public String getServiceAccount() {
|
||||
return this.serviceAccount;
|
||||
}
|
||||
|
||||
public void setGcpPath(String gcpPath) {
|
||||
this.gcpPath = gcpPath;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setServiceAccount(String serviceAccount) {
|
||||
this.serviceAccount = serviceAccount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* GCP-IAM properties.
|
||||
*/
|
||||
public static class GcpIamProperties {
|
||||
|
||||
/**
|
||||
* Credentials configuration.
|
||||
*/
|
||||
private final GcpCredentials credentials = new GcpCredentials();
|
||||
|
||||
/**
|
||||
* Mount path of the Kubernetes authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String gcpPath = "gcp";
|
||||
|
||||
/**
|
||||
* Name of the role against which the login is being attempted.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
/**
|
||||
* Overrides the GCP project Id.
|
||||
*/
|
||||
private String projectId = "";
|
||||
|
||||
/**
|
||||
* Overrides the GCP service account Id.
|
||||
*/
|
||||
private String serviceAccountId = "";
|
||||
|
||||
/**
|
||||
* Validity of the JWT token.
|
||||
*/
|
||||
private Duration jwtValidity = Duration.ofMinutes(15);
|
||||
|
||||
public GcpCredentials getCredentials() {
|
||||
return this.credentials;
|
||||
}
|
||||
|
||||
public String getGcpPath() {
|
||||
return this.gcpPath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
return this.projectId;
|
||||
}
|
||||
|
||||
public String getServiceAccountId() {
|
||||
return this.serviceAccountId;
|
||||
}
|
||||
|
||||
public Duration getJwtValidity() {
|
||||
return this.jwtValidity;
|
||||
}
|
||||
|
||||
public void setGcpPath(String gcpPath) {
|
||||
this.gcpPath = gcpPath;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public void setServiceAccountId(String serviceAccountId) {
|
||||
this.serviceAccountId = serviceAccountId;
|
||||
}
|
||||
|
||||
public void setJwtValidity(Duration jwtValidity) {
|
||||
this.jwtValidity = jwtValidity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* GCP credential properties.
|
||||
*/
|
||||
public static class GcpCredentials {
|
||||
|
||||
/**
|
||||
* Location of the OAuth2 credentials private key.
|
||||
*
|
||||
* <p>
|
||||
* Since this is a Resource, the private key can be in a multitude of locations,
|
||||
* such as a local file system, classpath, URL, etc.
|
||||
*/
|
||||
private Resource location;
|
||||
|
||||
/**
|
||||
* The base64 encoded contents of an OAuth2 account private key in JSON format.
|
||||
*/
|
||||
private String encodedKey;
|
||||
|
||||
public Resource getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public String getEncodedKey() {
|
||||
return this.encodedKey;
|
||||
}
|
||||
|
||||
public void setLocation(Resource location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public void setEncodedKey(String encodedKey) {
|
||||
this.encodedKey = encodedKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Kubernetes properties.
|
||||
*/
|
||||
public static class KubernetesProperties {
|
||||
|
||||
/**
|
||||
* Mount path of the Kubernetes authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String kubernetesPath = "kubernetes";
|
||||
|
||||
/**
|
||||
* Name of the role against which the login is being attempted.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
/**
|
||||
* Path to the service account token file.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String serviceAccountTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token";
|
||||
|
||||
public String getKubernetesPath() {
|
||||
return this.kubernetesPath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public String getServiceAccountTokenFile() {
|
||||
return this.serviceAccountTokenFile;
|
||||
}
|
||||
|
||||
public void setKubernetesPath(String kubernetesPath) {
|
||||
this.kubernetesPath = kubernetesPath;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setServiceAccountTokenFile(String serviceAccountTokenFile) {
|
||||
this.serviceAccountTokenFile = serviceAccountTokenFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* PCF properties.
|
||||
*/
|
||||
public static class PcfProperties {
|
||||
|
||||
/**
|
||||
* Mount path of the Kubernetes authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String pcfPath = "pcf";
|
||||
|
||||
/**
|
||||
* Name of the role against which the login is being attempted.
|
||||
*/
|
||||
private String role = "";
|
||||
|
||||
/**
|
||||
* Path to the instance certificate (PEM). Defaults to {@code CF_INSTANCE_CERT}
|
||||
* env variable.
|
||||
*/
|
||||
private Resource instanceCertificate;
|
||||
|
||||
/**
|
||||
* Path to the instance key (PEM). Defaults to {@code CF_INSTANCE_KEY} env
|
||||
* variable.
|
||||
*/
|
||||
private Resource instanceKey;
|
||||
|
||||
public String getPcfPath() {
|
||||
return this.pcfPath;
|
||||
}
|
||||
|
||||
public void setPcfPath(String pcfPath) {
|
||||
this.pcfPath = pcfPath;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return this.role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public Resource getInstanceCertificate() {
|
||||
return this.instanceCertificate;
|
||||
}
|
||||
|
||||
public void setInstanceCertificate(Resource instanceCertificate) {
|
||||
this.instanceCertificate = instanceCertificate;
|
||||
}
|
||||
|
||||
public Resource getInstanceKey() {
|
||||
return this.instanceKey;
|
||||
}
|
||||
|
||||
public void setInstanceKey(Resource instanceKey) {
|
||||
this.instanceKey = instanceKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL properties.
|
||||
*/
|
||||
@Validated
|
||||
public static class Ssl {
|
||||
|
||||
/**
|
||||
* Trust store that holds certificates and private keys.
|
||||
*/
|
||||
private Resource keyStore;
|
||||
|
||||
/**
|
||||
* Password used to access the key store.
|
||||
*/
|
||||
private String keyStorePassword;
|
||||
|
||||
/**
|
||||
* Trust store that holds SSL certificates.
|
||||
*/
|
||||
private Resource trustStore;
|
||||
|
||||
/**
|
||||
* Password used to access the trust store.
|
||||
*/
|
||||
private String trustStorePassword;
|
||||
|
||||
/**
|
||||
* Mount path of the TLS cert authentication backend.
|
||||
*/
|
||||
@NotEmpty
|
||||
private String certAuthPath = "cert";
|
||||
|
||||
public Resource getKeyStore() {
|
||||
return this.keyStore;
|
||||
}
|
||||
|
||||
public String getKeyStorePassword() {
|
||||
return this.keyStorePassword;
|
||||
}
|
||||
|
||||
public Resource getTrustStore() {
|
||||
return this.trustStore;
|
||||
}
|
||||
|
||||
public String getTrustStorePassword() {
|
||||
return this.trustStorePassword;
|
||||
}
|
||||
|
||||
public String getCertAuthPath() {
|
||||
return this.certAuthPath;
|
||||
}
|
||||
|
||||
public void setKeyStore(Resource keyStore) {
|
||||
this.keyStore = keyStore;
|
||||
}
|
||||
|
||||
public void setKeyStorePassword(String keyStorePassword) {
|
||||
this.keyStorePassword = keyStorePassword;
|
||||
}
|
||||
|
||||
public void setTrustStore(Resource trustStore) {
|
||||
this.trustStore = trustStore;
|
||||
}
|
||||
|
||||
public void setTrustStorePassword(String trustStorePassword) {
|
||||
this.trustStorePassword = trustStorePassword;
|
||||
}
|
||||
|
||||
public void setCertAuthPath(String certAuthPath) {
|
||||
this.certAuthPath = certAuthPath;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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
|
||||
*
|
||||
* https://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.environment.vault;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.amazonaws.auth.AWSCredentials;
|
||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.config.server.environment.ConfigTokenProvider;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AppRoleProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AwsEc2Properties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AwsIamProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AzureMsiProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.GcpCredentials;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.GcpIamProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.KubernetesProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.PcfProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.authentication.AppRoleAuthentication;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.AppRoleAuthenticationOptionsBuilder;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.RoleId;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.SecretId;
|
||||
import org.springframework.vault.authentication.AwsEc2Authentication;
|
||||
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions;
|
||||
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions.Nonce;
|
||||
import org.springframework.vault.authentication.AwsIamAuthentication;
|
||||
import org.springframework.vault.authentication.AwsIamAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.AwsIamAuthenticationOptions.AwsIamAuthenticationOptionsBuilder;
|
||||
import org.springframework.vault.authentication.AzureMsiAuthentication;
|
||||
import org.springframework.vault.authentication.AzureMsiAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.ClientCertificateAuthentication;
|
||||
import org.springframework.vault.authentication.CubbyholeAuthentication;
|
||||
import org.springframework.vault.authentication.CubbyholeAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.GcpComputeAuthentication;
|
||||
import org.springframework.vault.authentication.GcpComputeAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.GcpComputeAuthenticationOptions.GcpComputeAuthenticationOptionsBuilder;
|
||||
import org.springframework.vault.authentication.GcpCredentialSupplier;
|
||||
import org.springframework.vault.authentication.GcpIamAuthentication;
|
||||
import org.springframework.vault.authentication.GcpIamAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.GcpIamAuthenticationOptions.GcpIamAuthenticationOptionsBuilder;
|
||||
import org.springframework.vault.authentication.KubernetesAuthentication;
|
||||
import org.springframework.vault.authentication.KubernetesAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.KubernetesServiceAccountTokenFile;
|
||||
import org.springframework.vault.authentication.PcfAuthentication;
|
||||
import org.springframework.vault.authentication.PcfAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.PcfAuthenticationOptions.PcfAuthenticationOptionsBuilder;
|
||||
import org.springframework.vault.authentication.ResourceCredentialSupplier;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.client.RestTemplateBuilder;
|
||||
import org.springframework.vault.client.VaultClients;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.config.AbstractVaultConfiguration;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.APPROLE;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.AWS_IAM;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.AZURE_MSI;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.CUBBYHOLE;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.GCP_GCE;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.GCP_IAM;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.KUBERNETES;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.PCF;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.TOKEN;
|
||||
|
||||
/**
|
||||
* This class is adapted from
|
||||
* {@link org.springframework.vault.config.EnvironmentVaultConfiguration} and <a href=
|
||||
* https://github.com/spring-cloud/spring-cloud-vault/blob/master/spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/ClientAuthenticationFactory.java>
|
||||
* org.springframework.cloud.vault.config.ClientAuthenticationFactory</a> in order to
|
||||
* provide configuration consistent with Spring Cloud Vault's property-based
|
||||
* configuration.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public class SpringVaultClientConfiguration extends AbstractVaultConfiguration {
|
||||
|
||||
private static final String VAULT_PROPERTIES_PREFIX = "spring.cloud.config.server.vault.";
|
||||
|
||||
private final VaultEnvironmentProperties vaultProperties;
|
||||
|
||||
private final ConfigTokenProvider configTokenProvider;
|
||||
|
||||
private final RestOperations externalRestOperations;
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
public SpringVaultClientConfiguration(VaultEnvironmentProperties vaultProperties,
|
||||
ConfigTokenProvider configTokenProvider) {
|
||||
this.vaultProperties = vaultProperties;
|
||||
this.configTokenProvider = configTokenProvider;
|
||||
this.externalRestOperations = new RestTemplate(
|
||||
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultEndpoint vaultEndpoint() {
|
||||
|
||||
URI baseUrl = UriComponentsBuilder.newInstance()
|
||||
.scheme(vaultProperties.getScheme()).host(vaultProperties.getHost())
|
||||
.port(vaultProperties.getPort()).build().toUri();
|
||||
|
||||
return VaultEndpoint.from(baseUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestTemplateBuilder restTemplateBuilder(
|
||||
VaultEndpointProvider endpointProvider,
|
||||
ClientHttpRequestFactory requestFactory) {
|
||||
|
||||
RestTemplateBuilder restTemplateBuilder = super.restTemplateBuilder(
|
||||
endpointProvider, requestFactory);
|
||||
|
||||
if (vaultProperties.getNamespace() != null) {
|
||||
restTemplateBuilder.customizers(
|
||||
restTemplate -> restTemplate.getInterceptors().add(VaultClients
|
||||
.createNamespaceInterceptor(vaultProperties.getNamespace())));
|
||||
}
|
||||
|
||||
return restTemplateBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslConfiguration sslConfiguration() {
|
||||
if (vaultProperties.isSkipSslValidation()) {
|
||||
log.warn("The '" + VAULT_PROPERTIES_PREFIX + "skipSslValidation' property "
|
||||
+ "is not supported by this Vault environment repository implementation. "
|
||||
+ "Use the '" + VAULT_PROPERTIES_PREFIX
|
||||
+ "ssl` properties to provide "
|
||||
+ "custom keyStore and trustStore material instead.");
|
||||
}
|
||||
|
||||
VaultEnvironmentProperties.Ssl ssl = vaultProperties.getSsl();
|
||||
|
||||
SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = getKeyStoreConfiguration(
|
||||
ssl.getKeyStore(), ssl.getKeyStorePassword());
|
||||
|
||||
SslConfiguration.KeyStoreConfiguration trustStoreConfiguration = getKeyStoreConfiguration(
|
||||
ssl.getTrustStore(), ssl.getTrustStorePassword());
|
||||
|
||||
return new SslConfiguration(keyStoreConfiguration, trustStoreConfiguration);
|
||||
}
|
||||
|
||||
private SslConfiguration.KeyStoreConfiguration getKeyStoreConfiguration(
|
||||
Resource resourceProperty, String passwordProperty) {
|
||||
|
||||
if (resourceProperty == null) {
|
||||
return SslConfiguration.KeyStoreConfiguration.unconfigured();
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(passwordProperty)) {
|
||||
return SslConfiguration.KeyStoreConfiguration.of(resourceProperty,
|
||||
passwordProperty.toCharArray());
|
||||
}
|
||||
|
||||
return SslConfiguration.KeyStoreConfiguration.of(resourceProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new {@link ClientAuthentication}.
|
||||
*/
|
||||
public ClientAuthentication clientAuthentication() {
|
||||
|
||||
AuthenticationMethod authentication = this.vaultProperties.getAuthentication();
|
||||
|
||||
if (authentication == null) {
|
||||
return new ConfigTokenProviderAuthentication(configTokenProvider);
|
||||
}
|
||||
|
||||
switch (authentication) {
|
||||
case APPROLE:
|
||||
return appRoleAuthentication(this.vaultProperties);
|
||||
case AWS_EC2:
|
||||
return awsEc2Authentication(this.vaultProperties);
|
||||
case AWS_IAM:
|
||||
return awsIamAuthentication(this.vaultProperties);
|
||||
case AZURE_MSI:
|
||||
return azureMsiAuthentication(this.vaultProperties);
|
||||
case CERT:
|
||||
return new ClientCertificateAuthentication(restOperations());
|
||||
case CUBBYHOLE:
|
||||
return cubbyholeAuthentication();
|
||||
case GCP_GCE:
|
||||
return gcpGceAuthentication(this.vaultProperties);
|
||||
case GCP_IAM:
|
||||
return gcpIamAuthentication(this.vaultProperties);
|
||||
case KUBERNETES:
|
||||
return kubernetesAuthentication(this.vaultProperties);
|
||||
case PCF:
|
||||
return pcfAuthentication(this.vaultProperties);
|
||||
case TOKEN:
|
||||
Assert.hasText(this.vaultProperties.getToken(),
|
||||
missingPropertyForAuthMethod("token", TOKEN));
|
||||
return new TokenAuthentication(this.vaultProperties.getToken());
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(
|
||||
String.format("Client authentication %s not supported", authentication));
|
||||
}
|
||||
|
||||
private ClientAuthentication appRoleAuthentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
AppRoleAuthenticationOptions options = getAppRoleAuthenticationOptions(
|
||||
vaultProperties);
|
||||
|
||||
return new AppRoleAuthentication(options, restOperations());
|
||||
}
|
||||
|
||||
static AppRoleAuthenticationOptions getAppRoleAuthenticationOptions(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
AppRoleProperties appRole = vaultProperties.getAppRole();
|
||||
|
||||
AppRoleAuthenticationOptionsBuilder builder = AppRoleAuthenticationOptions
|
||||
.builder().path(appRole.getAppRolePath());
|
||||
|
||||
if (StringUtils.hasText(appRole.getRole())) {
|
||||
builder.appRole(appRole.getRole());
|
||||
}
|
||||
|
||||
RoleId roleId = getRoleId(vaultProperties, appRole);
|
||||
SecretId secretId = getSecretId(vaultProperties, appRole);
|
||||
|
||||
builder.roleId(roleId).secretId(secretId);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static RoleId getRoleId(VaultEnvironmentProperties vaultProperties,
|
||||
AppRoleProperties appRole) {
|
||||
|
||||
if (StringUtils.hasText(appRole.getRoleId())) {
|
||||
return RoleId.provided(appRole.getRoleId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(vaultProperties.getToken())
|
||||
&& StringUtils.hasText(appRole.getRole())) {
|
||||
return RoleId.pull(VaultToken.of(vaultProperties.getToken()));
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(vaultProperties.getToken())) {
|
||||
return RoleId.wrapped(VaultToken.of(vaultProperties.getToken()));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"Any of '" + VAULT_PROPERTIES_PREFIX + "app-role.role-id', '.token', "
|
||||
+ "or '.app-role.role' and '.token' must be provided if the "
|
||||
+ APPROLE + " authentication method is specified.");
|
||||
}
|
||||
|
||||
private static SecretId getSecretId(VaultEnvironmentProperties vaultProperties,
|
||||
AppRoleProperties appRole) {
|
||||
|
||||
if (StringUtils.hasText(appRole.getSecretId())) {
|
||||
return SecretId.provided(appRole.getSecretId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(vaultProperties.getToken())
|
||||
&& StringUtils.hasText(appRole.getRole())) {
|
||||
return SecretId.pull(VaultToken.of(vaultProperties.getToken()));
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(vaultProperties.getToken())) {
|
||||
return SecretId.wrapped(VaultToken.of(vaultProperties.getToken()));
|
||||
}
|
||||
|
||||
return SecretId.absent();
|
||||
}
|
||||
|
||||
private ClientAuthentication awsEc2Authentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
AwsEc2Properties awsEc2 = vaultProperties.getAwsEc2();
|
||||
|
||||
Nonce nonce = StringUtils.hasText(awsEc2.getNonce())
|
||||
? Nonce.provided(awsEc2.getNonce().toCharArray()) : Nonce.generated();
|
||||
|
||||
AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions
|
||||
.builder().role(awsEc2.getRole()) //
|
||||
.path(awsEc2.getAwsEc2Path()) //
|
||||
.nonce(nonce) //
|
||||
.identityDocumentUri(URI.create(awsEc2.getIdentityDocument())) //
|
||||
.build();
|
||||
|
||||
return new AwsEc2Authentication(authenticationOptions, restOperations(),
|
||||
this.externalRestOperations);
|
||||
}
|
||||
|
||||
private ClientAuthentication awsIamAuthentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
assertClassPresent("com.amazonaws.auth.AWSCredentials", missingClassForAuthMethod(
|
||||
"AWSCredentials", "aws-java-sdk-core", AWS_IAM));
|
||||
|
||||
AwsIamProperties awsIam = vaultProperties.getAwsIam();
|
||||
|
||||
AWSCredentialsProvider credentialsProvider = AwsCredentialProvider
|
||||
.getAwsCredentialsProvider();
|
||||
|
||||
AwsIamAuthenticationOptionsBuilder builder = AwsIamAuthenticationOptions
|
||||
.builder();
|
||||
|
||||
if (StringUtils.hasText(awsIam.getRole())) {
|
||||
builder.role(awsIam.getRole());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(awsIam.getServerName())) {
|
||||
builder.serverName(awsIam.getServerName());
|
||||
}
|
||||
|
||||
if (awsIam.getEndpointUri() != null) {
|
||||
builder.endpointUri(awsIam.getEndpointUri());
|
||||
}
|
||||
|
||||
builder.path(awsIam.getAwsPath()) //
|
||||
.credentialsProvider(credentialsProvider);
|
||||
|
||||
AwsIamAuthenticationOptions options = builder
|
||||
.credentialsProvider(credentialsProvider).build();
|
||||
|
||||
return new AwsIamAuthentication(options, restOperations());
|
||||
}
|
||||
|
||||
private ClientAuthentication azureMsiAuthentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
AzureMsiProperties azureMsi = vaultProperties.getAzureMsi();
|
||||
|
||||
Assert.hasText(azureMsi.getRole(),
|
||||
missingPropertyForAuthMethod("azure-msi.role", AZURE_MSI));
|
||||
|
||||
AzureMsiAuthenticationOptions options = AzureMsiAuthenticationOptions.builder()
|
||||
.role(azureMsi.getRole()).build();
|
||||
|
||||
return new AzureMsiAuthentication(options, restOperations(),
|
||||
this.externalRestOperations);
|
||||
}
|
||||
|
||||
private ClientAuthentication cubbyholeAuthentication() {
|
||||
|
||||
String token = this.vaultProperties.getToken();
|
||||
|
||||
Assert.hasText(token, missingPropertyForAuthMethod("token", CUBBYHOLE));
|
||||
|
||||
CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions.builder() //
|
||||
.wrapped() //
|
||||
.initialToken(VaultToken.of(token)) //
|
||||
.build();
|
||||
|
||||
return new CubbyholeAuthentication(options, restOperations());
|
||||
}
|
||||
|
||||
private ClientAuthentication gcpGceAuthentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
VaultEnvironmentProperties.GcpGceProperties gcp = vaultProperties.getGcpGce();
|
||||
|
||||
Assert.hasText(gcp.getRole(),
|
||||
missingPropertyForAuthMethod("gcp-iam.role", GCP_GCE));
|
||||
|
||||
GcpComputeAuthenticationOptionsBuilder builder = GcpComputeAuthenticationOptions
|
||||
.builder().path(gcp.getGcpPath()).role(gcp.getRole());
|
||||
|
||||
if (StringUtils.hasText(gcp.getServiceAccount())) {
|
||||
builder.serviceAccount(gcp.getServiceAccount());
|
||||
}
|
||||
|
||||
return new GcpComputeAuthentication(builder.build(), restOperations(),
|
||||
this.externalRestOperations);
|
||||
}
|
||||
|
||||
private ClientAuthentication gcpIamAuthentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
assertClassPresent(
|
||||
"com.google.api.client.googleapis.auth.oauth2.GoogleCredential",
|
||||
missingClassForAuthMethod("GoogleCredential", "google-api-client",
|
||||
GCP_IAM));
|
||||
|
||||
VaultEnvironmentProperties.GcpIamProperties gcp = vaultProperties.getGcpIam();
|
||||
|
||||
Assert.hasText(gcp.getRole(),
|
||||
missingPropertyForAuthMethod("gcp-iam.role", GCP_IAM));
|
||||
|
||||
GcpIamAuthenticationOptionsBuilder builder = GcpIamAuthenticationOptions.builder()
|
||||
.path(gcp.getGcpPath()).role(gcp.getRole())
|
||||
.jwtValidity(gcp.getJwtValidity());
|
||||
|
||||
if (StringUtils.hasText(gcp.getProjectId())) {
|
||||
builder.projectId(gcp.getProjectId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(gcp.getServiceAccountId())) {
|
||||
builder.serviceAccountId(gcp.getServiceAccountId());
|
||||
}
|
||||
|
||||
GcpCredentialSupplier supplier = GcpCredentialProvider.getGoogleCredential(gcp);
|
||||
builder.credential(supplier.get());
|
||||
|
||||
GcpIamAuthenticationOptions options = builder.build();
|
||||
|
||||
return new GcpIamAuthentication(options, restOperations());
|
||||
}
|
||||
|
||||
private ClientAuthentication kubernetesAuthentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
KubernetesProperties kubernetes = vaultProperties.getKubernetes();
|
||||
|
||||
Assert.hasText(kubernetes.getRole(),
|
||||
missingPropertyForAuthMethod("kubernetes.role", KUBERNETES));
|
||||
Assert.hasText(kubernetes.getServiceAccountTokenFile(),
|
||||
missingPropertyForAuthMethod("kubernetes.service-account-token-file",
|
||||
KUBERNETES));
|
||||
|
||||
KubernetesAuthenticationOptions options = KubernetesAuthenticationOptions
|
||||
.builder().path(kubernetes.getKubernetesPath()).role(kubernetes.getRole())
|
||||
.jwtSupplier(new KubernetesServiceAccountTokenFile(
|
||||
kubernetes.getServiceAccountTokenFile()))
|
||||
.build();
|
||||
|
||||
return new KubernetesAuthentication(options, restOperations());
|
||||
}
|
||||
|
||||
private ClientAuthentication pcfAuthentication(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
|
||||
PcfProperties pcfProperties = vaultProperties.getPcf();
|
||||
|
||||
assertClassPresent("org.bouncycastle.crypto.signers.PSSSigner",
|
||||
missingClassForAuthMethod("BouncyCastle", "bcpkix-jdk15on", PCF));
|
||||
Assert.hasText(pcfProperties.getRole(),
|
||||
missingPropertyForAuthMethod("pcf.role", PCF));
|
||||
|
||||
PcfAuthenticationOptionsBuilder builder = PcfAuthenticationOptions.builder()
|
||||
.role(pcfProperties.getRole()).path(pcfProperties.getPcfPath());
|
||||
|
||||
if (pcfProperties.getInstanceCertificate() != null) {
|
||||
builder.instanceCertificate(new ResourceCredentialSupplier(
|
||||
pcfProperties.getInstanceCertificate()));
|
||||
}
|
||||
|
||||
if (pcfProperties.getInstanceKey() != null) {
|
||||
builder.instanceKey(
|
||||
new ResourceCredentialSupplier(pcfProperties.getInstanceKey()));
|
||||
}
|
||||
|
||||
return new PcfAuthentication(builder.build(), restOperations());
|
||||
}
|
||||
|
||||
private String missingPropertyForAuthMethod(String propertyName,
|
||||
AuthenticationMethod authenticationMethod) {
|
||||
return "The '" + VAULT_PROPERTIES_PREFIX + propertyName
|
||||
+ "' property must be provided " + "when the " + authenticationMethod
|
||||
+ " authentication method is specified.";
|
||||
}
|
||||
|
||||
private String missingClassForAuthMethod(String className, String classArtifact,
|
||||
AuthenticationMethod authenticationMethod) {
|
||||
return className + "(" + classArtifact + ")"
|
||||
+ " must be on the classpath when the " + authenticationMethod
|
||||
+ " authentication method is specified";
|
||||
}
|
||||
|
||||
private void assertClassPresent(String className, String message) {
|
||||
Assert.isTrue(ClassUtils.isPresent(className, getClass().getClassLoader()),
|
||||
message);
|
||||
}
|
||||
|
||||
private static class AwsCredentialProvider {
|
||||
|
||||
private static AWSCredentialsProvider getAwsCredentialsProvider() {
|
||||
|
||||
DefaultAWSCredentialsProviderChain backingCredentialsProvider = DefaultAWSCredentialsProviderChain
|
||||
.getInstance();
|
||||
|
||||
// Eagerly fetch credentials preventing lag during the first, actual login.
|
||||
AWSCredentials firstAccess = backingCredentialsProvider.getCredentials();
|
||||
|
||||
AtomicReference<AWSCredentials> once = new AtomicReference<>(firstAccess);
|
||||
|
||||
return new AWSCredentialsProvider() {
|
||||
|
||||
@Override
|
||||
public AWSCredentials getCredentials() {
|
||||
|
||||
if (once.compareAndSet(firstAccess, null)) {
|
||||
return firstAccess;
|
||||
}
|
||||
|
||||
return backingCredentialsProvider.getCredentials();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
backingCredentialsProvider.refresh();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static class GcpCredentialProvider {
|
||||
|
||||
public static GcpCredentialSupplier getGoogleCredential(GcpIamProperties gcp) {
|
||||
return () -> {
|
||||
|
||||
GcpCredentials credentialProperties = gcp.getCredentials();
|
||||
if (credentialProperties.getLocation() != null) {
|
||||
return GoogleCredential.fromStream(
|
||||
credentialProperties.getLocation().getInputStream());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(credentialProperties.getEncodedKey())) {
|
||||
return GoogleCredential.fromStream(new ByteArrayInputStream(Base64
|
||||
.getDecoder().decode(credentialProperties.getEncodedKey())));
|
||||
}
|
||||
|
||||
return GoogleCredential.getApplicationDefault();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ConfigTokenProviderAuthentication implements ClientAuthentication {
|
||||
|
||||
private final ConfigTokenProvider tokenProvider;
|
||||
|
||||
ConfigTokenProviderAuthentication(ConfigTokenProvider tokenProvider) {
|
||||
this.tokenProvider = tokenProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultToken login() throws VaultException {
|
||||
String token = tokenProvider.getToken();
|
||||
if (!StringUtils.hasLength(token)) {
|
||||
throw new IllegalArgumentException(
|
||||
"A Vault token must be supplied by a token provider");
|
||||
}
|
||||
return VaultToken.of(token);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,27 +16,15 @@
|
||||
|
||||
package org.springframework.cloud.config.server.environment.vault;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.config.server.environment.ConfigTokenProvider;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentWatch;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.SimpleSessionManager;
|
||||
import org.springframework.vault.client.RestTemplateBuilder;
|
||||
import org.springframework.vault.client.VaultClients;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.core.VaultKeyValueOperations;
|
||||
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author Dylan Roberts
|
||||
@@ -49,23 +37,20 @@ public class SpringVaultEnvironmentRepositoryFactory implements
|
||||
|
||||
private final EnvironmentWatch watch;
|
||||
|
||||
private final ConfigTokenProvider tokenProvider;
|
||||
private final SpringVaultClientConfiguration clientConfiguration;
|
||||
|
||||
public SpringVaultEnvironmentRepositoryFactory(
|
||||
ObjectProvider<HttpServletRequest> request, EnvironmentWatch watch,
|
||||
ConfigTokenProvider tokenProvider) {
|
||||
SpringVaultClientConfiguration clientConfiguration) {
|
||||
this.request = request;
|
||||
this.watch = watch;
|
||||
this.tokenProvider = tokenProvider;
|
||||
this.clientConfiguration = clientConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpringVaultEnvironmentRepository build(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
RestTemplateBuilder restTemplateBuilder = buildRestTemplateBuilder(
|
||||
vaultProperties);
|
||||
|
||||
VaultTemplate vaultTemplate = buildVaultTemplate(restTemplateBuilder);
|
||||
VaultTemplate vaultTemplate = clientConfiguration.vaultTemplate();
|
||||
|
||||
VaultKeyValueOperations accessStrategy = buildVaultAccessStrategy(vaultProperties,
|
||||
vaultTemplate);
|
||||
@@ -74,29 +59,6 @@ public class SpringVaultEnvironmentRepositoryFactory implements
|
||||
vaultProperties, accessStrategy);
|
||||
}
|
||||
|
||||
private RestTemplateBuilder buildRestTemplateBuilder(
|
||||
VaultEnvironmentProperties vaultProperties) {
|
||||
URI baseUrl = UriComponentsBuilder.newInstance()
|
||||
.scheme(vaultProperties.getScheme()).host(vaultProperties.getHost())
|
||||
.port(vaultProperties.getPort()).build().toUri();
|
||||
|
||||
RestTemplateBuilder restTemplateBuilder = RestTemplateBuilder.builder()
|
||||
.endpoint(VaultEndpoint.from(baseUrl));
|
||||
|
||||
if (vaultProperties.getNamespace() != null) {
|
||||
restTemplateBuilder.customizers(
|
||||
restTemplate -> restTemplate.getInterceptors().add(VaultClients
|
||||
.createNamespaceInterceptor(vaultProperties.getNamespace())));
|
||||
}
|
||||
|
||||
return restTemplateBuilder;
|
||||
}
|
||||
|
||||
private VaultTemplate buildVaultTemplate(RestTemplateBuilder restTemplateBuilder) {
|
||||
return new VaultTemplate(restTemplateBuilder, new SimpleSessionManager(
|
||||
new ConfigTokenProviderAuthentication(tokenProvider)));
|
||||
}
|
||||
|
||||
private VaultKeyValueOperations buildVaultAccessStrategy(
|
||||
VaultEnvironmentProperties vaultProperties, VaultTemplate vaultTemplate) {
|
||||
String backend = vaultProperties.getBackend();
|
||||
@@ -115,25 +77,4 @@ public class SpringVaultEnvironmentRepositoryFactory implements
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConfigTokenProviderAuthentication
|
||||
implements ClientAuthentication {
|
||||
|
||||
private final ConfigTokenProvider tokenProvider;
|
||||
|
||||
public ConfigTokenProviderAuthentication(ConfigTokenProvider tokenProvider) {
|
||||
this.tokenProvider = tokenProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultToken login() throws VaultException {
|
||||
String token = tokenProvider.getToken();
|
||||
if (!StringUtils.hasLength(token)) {
|
||||
throw new IllegalArgumentException(
|
||||
"A Vault token must be supplied by a token provider");
|
||||
}
|
||||
return VaultToken.of(token);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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
|
||||
*
|
||||
* https://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.environment.vault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultClientConfiguration.ConfigTokenProviderAuthentication;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.RoleId;
|
||||
import org.springframework.vault.authentication.AppRoleAuthenticationOptions.SecretId;
|
||||
import org.springframework.vault.authentication.AwsEc2Authentication;
|
||||
import org.springframework.vault.authentication.AwsIamAuthentication;
|
||||
import org.springframework.vault.authentication.AzureMsiAuthentication;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.ClientCertificateAuthentication;
|
||||
import org.springframework.vault.authentication.CubbyholeAuthentication;
|
||||
import org.springframework.vault.authentication.GcpComputeAuthentication;
|
||||
import org.springframework.vault.authentication.GcpIamAuthentication;
|
||||
import org.springframework.vault.authentication.KubernetesAuthentication;
|
||||
import org.springframework.vault.authentication.PcfAuthentication;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.vault.support.SslConfiguration.KeyStoreConfiguration;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.AWS_EC2;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.AWS_IAM;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.AZURE_MSI;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.CERT;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.CUBBYHOLE;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.GCP_GCE;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.GCP_IAM;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.KUBERNETES;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.PCF;
|
||||
import static org.springframework.cloud.config.server.environment.VaultEnvironmentProperties.AuthenticationMethod.TOKEN;
|
||||
|
||||
class SpringVaultClientConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void appRoleRoleIdProvidedSecretIdProvided() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.getAppRole().setRoleId("foo");
|
||||
properties.getAppRole().setSecretId("bar");
|
||||
|
||||
AppRoleAuthenticationOptions options = SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties);
|
||||
|
||||
assertThat(options.getRoleId()).isInstanceOf(RoleId.provided("foo").getClass());
|
||||
assertThat(options.getSecretId())
|
||||
.isInstanceOf(SecretId.provided("bar").getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleRoleIdProvidedSecretIdAbsent() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.getAppRole().setRoleId("foo");
|
||||
|
||||
AppRoleAuthenticationOptions options = SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties);
|
||||
|
||||
assertThat(options.getRoleId()).isInstanceOf(RoleId.provided("foo").getClass());
|
||||
assertThat(options.getSecretId()).isInstanceOf(SecretId.absent().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleRoleIdProvidedSecretIdPull() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.setToken("token");
|
||||
properties.getAppRole().setRoleId("foo");
|
||||
properties.getAppRole().setRole("my-role");
|
||||
|
||||
AppRoleAuthenticationOptions options = SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties);
|
||||
|
||||
assertThat(options.getAppRole()).isEqualTo("my-role");
|
||||
assertThat(options.getRoleId()).isInstanceOf(RoleId.provided("foo").getClass());
|
||||
assertThat(options.getSecretId())
|
||||
.isInstanceOf(SecretId.pull(VaultToken.of("token")).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleWithFullPull() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.setToken("token");
|
||||
properties.getAppRole().setRole("my-role");
|
||||
|
||||
AppRoleAuthenticationOptions options = SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties);
|
||||
|
||||
assertThat(options.getAppRole()).isEqualTo("my-role");
|
||||
assertThat(options.getRoleId())
|
||||
.isInstanceOf(RoleId.pull(VaultToken.of("token")).getClass());
|
||||
assertThat(options.getSecretId())
|
||||
.isInstanceOf(SecretId.pull(VaultToken.of("token")).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleFullWrapped() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.setToken("token");
|
||||
|
||||
AppRoleAuthenticationOptions options = SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties);
|
||||
|
||||
assertThat(options.getRoleId())
|
||||
.isInstanceOf(RoleId.wrapped(VaultToken.of("token")).getClass());
|
||||
assertThat(options.getSecretId())
|
||||
.isInstanceOf(SecretId.wrapped(VaultToken.of("token")).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleRoleIdWrappedSecretIdProvided() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.setToken("token");
|
||||
properties.getAppRole().setSecretId("bar");
|
||||
|
||||
AppRoleAuthenticationOptions options = SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties);
|
||||
|
||||
assertThat(options.getRoleId())
|
||||
.isInstanceOf(RoleId.wrapped(VaultToken.of("token")).getClass());
|
||||
assertThat(options.getSecretId())
|
||||
.isInstanceOf(SecretId.provided("bar").getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleRoleIdProvidedSecretIdWrapped() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.setToken("token");
|
||||
properties.getAppRole().setRoleId("foo");
|
||||
|
||||
AppRoleAuthenticationOptions options = SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties);
|
||||
|
||||
assertThat(options.getRoleId()).isInstanceOf(RoleId.provided("foo").getClass());
|
||||
assertThat(options.getSecretId())
|
||||
.isInstanceOf(SecretId.wrapped(VaultToken.of("token")).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleWithUnconfiguredRoleId() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
|
||||
assertThatThrownBy(() -> SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appRoleWithUnconfiguredRoleIdIfRoleNameSet() {
|
||||
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.getAppRole().setRole("my-role");
|
||||
|
||||
assertThatThrownBy(() -> SpringVaultClientConfiguration
|
||||
.getAppRoleAuthenticationOptions(properties))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultAuthentication() {
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
|
||||
assertClientAuthenticationOfType(properties,
|
||||
ConfigTokenProviderAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void awsEc2Authentication() {
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(AWS_EC2);
|
||||
properties.getAwsEc2().setRole("server");
|
||||
properties.getAwsEc2().setAwsEc2Path("aws-ec2");
|
||||
|
||||
assertClientAuthenticationOfType(properties, AwsEc2Authentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void awsIamAuthentication() {
|
||||
System.setProperty("aws.accessKeyId", "access-key-id");
|
||||
System.setProperty("aws.secretKey", "secret-key");
|
||||
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(AWS_IAM);
|
||||
properties.getAwsIam().setRole("server");
|
||||
properties.getAwsIam().setAwsPath("aws-iam");
|
||||
|
||||
assertClientAuthenticationOfType(properties, AwsIamAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void azureMsiAuthentication() {
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(AZURE_MSI);
|
||||
properties.getAzureMsi().setRole("server");
|
||||
properties.getAzureMsi().setAzurePath("azure-msi");
|
||||
|
||||
assertClientAuthenticationOfType(properties, AzureMsiAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientCertificateAuthentication() {
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(CERT);
|
||||
|
||||
assertClientAuthenticationOfType(properties,
|
||||
ClientCertificateAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cubbyholeAuthentication() {
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(CUBBYHOLE);
|
||||
properties.setToken("token");
|
||||
|
||||
assertClientAuthenticationOfType(properties, CubbyholeAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gcpComputeAuthentication() {
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(GCP_GCE);
|
||||
properties.getGcpGce().setRole("server");
|
||||
properties.getGcpGce().setServiceAccount("service-account");
|
||||
|
||||
assertClientAuthenticationOfType(properties, GcpComputeAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gcpIamAuthentication() {
|
||||
final String GCE_JSON = "{" + " \"type\": \"service_account\","
|
||||
+ " \"project_id\": \"project\","
|
||||
+ " \"private_key_id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\","
|
||||
+ " \"private_key\": \""
|
||||
+ "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5qHafKgP/FAKE\\n"
|
||||
+ "xfRl0i47zXKbGQJvGAGpcmiXRgeWkZp+kwNwBguOYNwO1qDcmewKvMPazj7EL0hV\\n"
|
||||
+ "XMkPxgshZ9ZSxPwg7/XHHcyGCYBJhDc2hyunQvc2WGUOlQKg/nOlq3Dg8d9c/0yF\\n"
|
||||
+ "mFOh2K+IrbV6Vqs3nXsupV1q2FbUCVg6NGB0HCdTBZO4e36tmcaWgC1cKTv/Nh+j\\n"
|
||||
+ "f2Bf7qBTk0GOL9AjKoa/HP24Yto5zFoOFLU+2ZkVbb8hhO8OMUKW8dLIynqqRqwv\\n"
|
||||
+ "oI8e4oiHX3dBvwcS0zZkEUtQiDI80OCbU7ZhPgn5xQpndanD9dZ4TYSgKuXRVTzr\\n"
|
||||
+ "1cyoyP7HAgMBAAECggEAV2fOYOSg+V60WvYhN4aaKaFxoT9G/BJrReENCJr5m5N1\\n"
|
||||
+ "Dr4b0jOmYSOMtpepJ/J3RB7Wfj63Ihm4jieeqQRt3Q5Lwq/mm4MdTN7kmP4EHZhX\\n"
|
||||
+ "fh5pGNfYFwfKm/DfSfhBbe+mtuBobhnrZsHuLbYb/db6J1yCQy6q/azwrAqp5iyq\\n"
|
||||
+ "GjNN+WiDIcrydPKKiaszMnb9mNH+Y6Ianx1mvSLT35nBEF6Z4rJVERl26diOoo3I\\n"
|
||||
+ "F6WadIwTqcoLo5duUO3SaHKKLcoSEEaGkutuTCHcFOzhvZrXbuIyD567Vp5oVFe9\\n"
|
||||
+ "SHN10vceQQdWPh2UsKrVQfIdc70+9tlslka5X6BbIQKBgQDss/APs65NNev2lPdb\\n"
|
||||
+ "Jzdd+0YwKEQXeENWkU1xJJkNH/wF0ZGuYxoKafZR0efs1LnrbaPfHveFCnUcDuXU\\n"
|
||||
+ "yDnzG2zMw1Q72F8eGHpLItPSh0ZkfSlN58uM1oYMdTFUE6ezlOYEnKIYdhjmQWiE\\n"
|
||||
+ "uEa1G4ZW0aX0NLICet597GnLKQKBgQDIyzRVbOOzOxGUgrWT0RPT12VVNCn7edn1\\n"
|
||||
+ "UWLKDl4L2uF8vE4g8WW7gwNkVbuO3VPKqdGuCBDfVyyysOOOCDN0IxSxDk3458VY\\n"
|
||||
+ "4I3jAuBcgDsixwC28l0QtFnz2yRuD2fsBhLnoSfsM/T2hNbf7atDtMQhbbgU37me\\n"
|
||||
+ "X+Ewtr+obwKBgCdbb/IcbUH3UknI0Sw95A3jZvNA7rl8TK4LMPY8IJq3E7+f7foy\\n"
|
||||
+ "DjVnEwbdwRN294b2zwWdb4iWiYxlyb9Mn54VlEyjudDNlFs7tLHjk5bw2TqCOSjz\\n"
|
||||
+ "/rtnPBi8L7yMHBlXC7v+k1E/6bn3bDqNLWyVrAuphk+Jp4OUDIShl6GpAoGAFIAC\\n"
|
||||
+ "mNIkMTFPqyzpIu1Oq+sq0lcgDiezpAMqJdzvpyAys0x6YYyjyVAn8X97Rau9GUzb\\n"
|
||||
+ "NnxmVJcO3jOHGAIoVqwaObVvKoFnOZq7gbjSdT82Smes4ADAlasEIAx4nK//+S3p\\n"
|
||||
+ "kjJ24/ut/9kyIuyd9qym9Y7BI4hv6AZ79EBEMwsCgYEAgXzq5+NCfJIi6Zduugym\\n"
|
||||
+ "iUU3y/3CWc/pLhnw3XZ5r3M5fLXokLhLU6FsNflTpdcf2QoNL58mE0tanPqg09Xh\\n"
|
||||
+ "7fHWR/8rISt2TsMlqFjc5rQxWg8yRpdd5Ti/Ln8v7EV3RGbhFlOqlC9hiyqfyd7V\\n"
|
||||
+ "qZjZg4zUxPO1I8ae8hbGMWs=\\n" + "-----END PRIVATE KEY-----\\n\","
|
||||
+ " \"client_email\": \"test@example.com\","
|
||||
+ " \"client_id\": \"111111111111111111111\","
|
||||
+ " \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\","
|
||||
+ " \"token_uri\": \"https://accounts.google.com/o/oauth2/token\","
|
||||
+ " \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\","
|
||||
+ " \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/toolsmiths-pcf-sa%40cf-spinnaker.iam.gserviceaccount.com\""
|
||||
+ "}";
|
||||
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(GCP_IAM);
|
||||
properties.getGcpIam().setRole("server");
|
||||
properties.getGcpIam().setProjectId("project");
|
||||
properties.getGcpIam().setServiceAccountId("service-account");
|
||||
properties.getGcpIam().getCredentials().setEncodedKey(base64(GCE_JSON));
|
||||
|
||||
assertClientAuthenticationOfType(properties, GcpIamAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void kuberneteAuthentication() throws IOException {
|
||||
Files.write(Paths.get("target", "token"), "token".getBytes());
|
||||
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(KUBERNETES);
|
||||
properties.getKubernetes().setRole("server");
|
||||
properties.getKubernetes().setServiceAccountTokenFile("target/token");
|
||||
|
||||
assertClientAuthenticationOfType(properties, KubernetesAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pcfAuthentication() {
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(PCF);
|
||||
properties.getPcf().setRole("my-role");
|
||||
properties.getPcf()
|
||||
.setInstanceKey(new ClassPathResource("configserver-test.yml"));
|
||||
properties.getPcf()
|
||||
.setInstanceCertificate(new ClassPathResource("configserver-test.yml"));
|
||||
|
||||
assertClientAuthenticationOfType(properties, PcfAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tokenAuthentication() {
|
||||
VaultEnvironmentProperties properties = getPropertiesForAuthMethod(TOKEN);
|
||||
properties.setToken("token");
|
||||
|
||||
assertClientAuthenticationOfType(properties, TokenAuthentication.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultSslConfiguration() {
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
|
||||
SpringVaultClientConfiguration configuration = getConfiguration(properties);
|
||||
SslConfiguration sslConfiguration = configuration.sslConfiguration();
|
||||
|
||||
assertThat(sslConfiguration.getKeyStoreConfiguration())
|
||||
.isEqualTo(KeyStoreConfiguration.unconfigured());
|
||||
assertThat(sslConfiguration.getTrustStoreConfiguration())
|
||||
.isEqualTo(KeyStoreConfiguration.unconfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customSslConfiguration() {
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.getSsl().setKeyStore(new ClassPathResource("ssl-test.jks"));
|
||||
properties.getSsl().setKeyStorePassword("password");
|
||||
properties.getSsl().setTrustStore(new ClassPathResource("ssl-test.jks"));
|
||||
properties.getSsl().setTrustStorePassword("password");
|
||||
|
||||
SpringVaultClientConfiguration configuration = getConfiguration(properties);
|
||||
SslConfiguration sslConfiguration = configuration.sslConfiguration();
|
||||
|
||||
KeyStoreConfiguration keyStoreConfiguration = sslConfiguration
|
||||
.getKeyStoreConfiguration();
|
||||
KeyStoreConfiguration trustStoreConfiguration = sslConfiguration
|
||||
.getTrustStoreConfiguration();
|
||||
assertThat(keyStoreConfiguration.isPresent()).isTrue();
|
||||
assertThat(new String(keyStoreConfiguration.getStorePassword()))
|
||||
.isEqualTo("password");
|
||||
assertThat(trustStoreConfiguration.isPresent()).isTrue();
|
||||
assertThat(new String(trustStoreConfiguration.getStorePassword()))
|
||||
.isEqualTo("password");
|
||||
}
|
||||
|
||||
private VaultEnvironmentProperties getPropertiesForAuthMethod(
|
||||
AuthenticationMethod authMethod) {
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
properties.setAuthentication(authMethod);
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void assertClientAuthenticationOfType(VaultEnvironmentProperties properties,
|
||||
Class<? extends ClientAuthentication> type) {
|
||||
ClientAuthentication clientAuthentication = getConfiguration(properties)
|
||||
.clientAuthentication();
|
||||
|
||||
assertThat(clientAuthentication).isInstanceOf(type);
|
||||
}
|
||||
|
||||
private SpringVaultClientConfiguration getConfiguration(
|
||||
VaultEnvironmentProperties properties) {
|
||||
return new SpringVaultClientConfiguration(properties, () -> null);
|
||||
}
|
||||
|
||||
private String base64(String value) {
|
||||
return new String(Base64.getEncoder().encode(value.getBytes()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,11 +21,13 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.config.server.environment.ConfigTokenProvider;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentWatch;
|
||||
import org.springframework.cloud.config.server.environment.VaultEnvironmentProperties;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.core.VaultKeyValueOperations;
|
||||
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -41,8 +43,8 @@ public class SpringVaultEnvironmentRepositoryFactoryTests {
|
||||
VaultEnvironmentProperties properties = new VaultEnvironmentProperties();
|
||||
|
||||
SpringVaultEnvironmentRepository environmentRepository = new SpringVaultEnvironmentRepositoryFactory(
|
||||
mockHttpRequest(), new EnvironmentWatch.Default(), mockTokenProvider())
|
||||
.build(properties);
|
||||
mockHttpRequest(), new EnvironmentWatch.Default(),
|
||||
mockClientConfiguration()).build(properties);
|
||||
|
||||
VaultKeyValueOperations keyValueTemplate = environmentRepository
|
||||
.getKeyValueTemplate();
|
||||
@@ -56,8 +58,8 @@ public class SpringVaultEnvironmentRepositoryFactoryTests {
|
||||
properties.setKvVersion(2);
|
||||
|
||||
SpringVaultEnvironmentRepository environmentRepository = new SpringVaultEnvironmentRepositoryFactory(
|
||||
mockHttpRequest(), new EnvironmentWatch.Default(), mockTokenProvider())
|
||||
.build(properties);
|
||||
mockHttpRequest(), new EnvironmentWatch.Default(),
|
||||
mockClientConfiguration()).build(properties);
|
||||
|
||||
VaultKeyValueOperations keyValueTemplate = environmentRepository
|
||||
.getKeyValueTemplate();
|
||||
@@ -65,10 +67,16 @@ public class SpringVaultEnvironmentRepositoryFactoryTests {
|
||||
.isEqualTo(VaultKeyValueOperationsSupport.KeyValueBackend.KV_2);
|
||||
}
|
||||
|
||||
private ConfigTokenProvider mockTokenProvider() {
|
||||
ConfigTokenProvider tokenProvider = mock(ConfigTokenProvider.class);
|
||||
when(tokenProvider.getToken()).thenReturn("token");
|
||||
return tokenProvider;
|
||||
private SpringVaultClientConfiguration mockClientConfiguration() {
|
||||
VaultTemplate vaultTemplate = new VaultTemplate(
|
||||
VaultEndpoint.create("localhost", 8200),
|
||||
new TokenAuthentication("token"));
|
||||
|
||||
SpringVaultClientConfiguration clientConfiguration = mock(
|
||||
SpringVaultClientConfiguration.class);
|
||||
when(clientConfiguration.vaultTemplate()).thenReturn(vaultTemplate);
|
||||
|
||||
return clientConfiguration;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
Reference in New Issue
Block a user