[BAEL-6694] Secure Kubernetes Secrets with Vault (#14762)
* [BAEL-4849] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code * [BAEL-4968] Remove extra comments * [BAEL-5258] Article Code * [BAEL-2765] PKCE Support for Secret Clients * [BAEL-5698] Article code * [BAEL-5698] Article code * [BAEL-5905] Initial code * [BAEL-5905] Article code * [BAEL-5905] Relocate article code to new module * [BAEL-6275] PostgreSQL NOTIFY/LISTEN * [BAEL-6275] Minor correction * BAEL-6138 * [BAEL-6138] WIP - LiveTest * [BAEL-6138] Tutorial Code * [BAEL-6138] Tutorial Code * [BAEL-6694] Article Code --------- Co-authored-by: Philippe Sevestre <psevestre@gmail.com>
This commit is contained in:
parent
8c400720c0
commit
12ba505cb4
@ -51,10 +51,36 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
<version>2.20.140</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-vault-config</artifactId>
|
||||
<version>3.1.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<spring.vault.core.version>3.0.2</spring.vault.core.version>
|
||||
<spring.vault.core.version>2.3.4</spring.vault.core.version>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
package com.baeldung.springcloudvault;
|
||||
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringCloudVaultTestApplication {
|
||||
public static void main(String... args) {
|
||||
SpringApplication.run(SpringCloudVaultTestApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandLineRunner listSecrets(Environment env) {
|
||||
return args -> {
|
||||
var foo = env.getProperty("foo");
|
||||
Assert.notNull(foo, "foo must have a value");
|
||||
System.out.println("foo=" + foo);
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.baeldung.springvaultk8s;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.vault.authentication.ClientAuthentication;
|
||||
import org.springframework.vault.authentication.KubernetesAuthentication;
|
||||
import org.springframework.vault.authentication.KubernetesAuthenticationOptions;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.config.AbstractVaultConfiguration;
|
||||
import org.springframework.vault.config.EnvironmentVaultConfiguration;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
@Configuration
|
||||
@PropertySource("vault-config-k8s.properties")
|
||||
@Import(EnvironmentVaultConfiguration.class)
|
||||
public class VaultConfig {
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.baeldung.springvaultk8s;
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.vault.core.VaultKeyValueOperations;
|
||||
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SpringBootApplication
|
||||
public class VaultK8SApplication {
|
||||
|
||||
public static void main(String... args) {
|
||||
SpringApplication.run(VaultK8SApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandLineRunner listSecrets(VaultTemplate vault) {
|
||||
|
||||
return args -> {
|
||||
VaultKeyValueOperations ops = vault.opsForKeyValue("secrets", VaultKeyValueOperationsSupport.KeyValueBackend.KV_2);
|
||||
List<String> secrets = ops.list("");
|
||||
if (secrets == null) {
|
||||
System.out.println("No secrets found");
|
||||
return;
|
||||
}
|
||||
|
||||
secrets.forEach(s -> {
|
||||
System.out.println("secret=" + s);
|
||||
var response = ops.get(s);
|
||||
var data = response.getRequiredData();
|
||||
|
||||
data.entrySet()
|
||||
.forEach(e -> {
|
||||
System.out.println("- key=" + e.getKey() + " => " + e.getValue());
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
51
spring-vault/src/main/k8s/vault-csi-example.yaml
Normal file
51
spring-vault/src/main/k8s/vault-csi-example.yaml
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
apiVersion: secrets-store.csi.x-k8s.io/v1
|
||||
kind: SecretProviderClass
|
||||
metadata:
|
||||
name: baeldung-csi-secrets
|
||||
namespace: baeldung
|
||||
spec:
|
||||
# Vault CSI Provider
|
||||
provider: vault
|
||||
parameters:
|
||||
# Vault role name to use during login
|
||||
roleName: 'baeldung-test-role'
|
||||
objects: |
|
||||
- objectName: 'baeldung.properties'
|
||||
secretPath: "secrets/data/baeldung-test"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-csi
|
||||
namespace: baeldung
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx-csi
|
||||
replicas: 1 # tells deployment to run 2 pods matching the template
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx-csi
|
||||
spec:
|
||||
serviceAccountName: vault-test-sa
|
||||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
volumeMounts:
|
||||
- name: vault-secrets
|
||||
mountPath: /vault/secrets
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: vault-secrets
|
||||
csi:
|
||||
driver: 'secrets-store.csi.k8s.io'
|
||||
readOnly: true
|
||||
volumeAttributes:
|
||||
secretProviderClass: baeldung-csi-secrets
|
||||
|
||||
|
32
spring-vault/src/main/k8s/vault-injector-example.yaml
Normal file
32
spring-vault/src/main/k8s/vault-injector-example.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
namespace: baeldung
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
replicas: 1 # tells deployment to run 2 pods matching the template
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
vault.hashicorp.com/agent-inject: "true"
|
||||
vault.hashicorp.com/agent-inject-secret-baeldung.properties: "secrets/baeldung-test"
|
||||
vault.hashicorp.com/role: "baeldung-test-role"
|
||||
vault.hashicorp.com/agent-inject-template-baeldung.properties: |
|
||||
{{- with secret "secrets/baeldung-test" -}}
|
||||
{{- range $k, $v := .Data.data }}
|
||||
{{$k}}={{$v}}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
spec:
|
||||
serviceAccountName: vault-test-sa
|
||||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
58
spring-vault/src/main/k8s/vault-operator-example.yaml
Normal file
58
spring-vault/src/main/k8s/vault-operator-example.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
##
|
||||
## Vault Connection
|
||||
##
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultConnection
|
||||
metadata:
|
||||
namespace: baeldung
|
||||
name: vault-local
|
||||
spec:
|
||||
# required configuration
|
||||
# address to the Vault server.
|
||||
address: http://vault.vault.svc.cluster.local:8200
|
||||
---
|
||||
##
|
||||
## Vault Auth
|
||||
##
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
metadata:
|
||||
namespace: baeldung
|
||||
name: baeldung-test
|
||||
spec:
|
||||
# required configuration
|
||||
# VaultConnectionRef of the corresponding VaultConnection CustomResource.
|
||||
# If no value is specified the Operator will default to the `default` VaultConnection,
|
||||
# configured in its own Kubernetes namespace.
|
||||
vaultConnectionRef: vault-local
|
||||
# Method to use when authenticating to Vault.
|
||||
method: kubernetes
|
||||
# Mount to use when authenticating to auth method.
|
||||
mount: kubernetes
|
||||
# Kubernetes specific auth configuration, requires that the Method be set to kubernetes.
|
||||
kubernetes:
|
||||
# role to use when authenticating to Vault
|
||||
role: baeldung-test-role
|
||||
# ServiceAccount to use when authenticating to Vault
|
||||
# it is recommended to always provide a unique serviceAccount per Pod/application
|
||||
serviceAccount: vault-test-sa
|
||||
---
|
||||
##
|
||||
## Vault-backed secret
|
||||
##
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
namespace: baeldung
|
||||
name: baeldung-test
|
||||
spec:
|
||||
vaultAuthRef: baeldung-test
|
||||
mount: secrets
|
||||
type: kv-v2
|
||||
path: baeldung-test
|
||||
refreshAfter: 60s
|
||||
hmacSecretData: true
|
||||
destination:
|
||||
create: true
|
||||
name: baeldung-test
|
@ -0,0 +1,4 @@
|
||||
vault.uri=http://localhost:8200
|
||||
vault.authentication=KUBERNETES
|
||||
vault.kubernetes.role=baeldung-test-role
|
||||
vault.kubernetes.service-account-token-file=sa-token.txt
|
@ -0,0 +1,26 @@
|
||||
package com.baeldung.springcloudvault;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@ActiveProfiles("vault")
|
||||
@SpringBootTest(classes = SpringCloudVaultTestApplication.class)
|
||||
public class SpringCloudVaultApplicationLiveTest {
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
assertNotNull(env.getProperty("foo"));
|
||||
}
|
||||
|
||||
}
|
15
spring-vault/src/test/resources/application-vault.properties
Normal file
15
spring-vault/src/test/resources/application-vault.properties
Normal file
@ -0,0 +1,15 @@
|
||||
# Vault Properties
|
||||
spring.config.import: vault://
|
||||
spring.cloud.vault.uri=http://localhost:8200
|
||||
spring.cloud.vault.authentication=KUBERNETES
|
||||
spring.cloud.vault.kubernetes.role=baeldung-test-role
|
||||
|
||||
#
|
||||
spring.cloud.vault.kv.backend=secrets
|
||||
spring.cloud.vault.kv.application-name=baeldung-test
|
||||
#
|
||||
|
||||
# NOTICE: the following property is only necessary when running the application
|
||||
# outside Kubernetes
|
||||
# Please refer to the article for instructions on how to create this file
|
||||
spring.cloud.vault.kubernetes.service-account-token-file=sa-token.txt
|
Loading…
x
Reference in New Issue
Block a user