Access Kubernetes Secrets In SpringBoot Application
Access Kubernetes secrets in Spring Boot application without loading to environment variables of the pod.
This blog will show how to access Kubernetes secrets in a Spring application without setting properties as environment variable in the container.
To better understand this blog, basic knowledge of Spring framework and Kubernetes (especially how to create secrets and mount secrets to access as environment variables in Pods) is recommended.
Pre-requisites:
- Docker desktop
- Kind CLI
Spring configuration management
- Spring Boot framework already provides multiple way to externalize the configuration. Instead of specifying the properties in application.yaml we can created as environment variable. For example, say if we need to enable a specific Spring profile the environment variable SPRING_PROFILES_ACTIVE can be set with specific profile when running the application.
Access Kubernetes secret in Spring Boot application without configuring to environment variable
In this approach the secret will be read by the Spring Boot application without loading to the environment variables.
- The secret is mount in the pod manifest
- The mount path of the secret is configured in Spring boot application with
spring.config.import
.
Demonstration of configuration management
- Create a simple secret resource named secret-msg in Kubernetes cluster. Use below command
kubectl create secret generic secret-msg --from-literal=MESSAGE=from-k8s-secret-store
The created secret is mounted in Pod manifest. Below is the snippet of how secret is mounted.
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets/
readOnly: true
- The Spring Boot application.yaml file is configured with the mounted secret path.
spring.config.import: "optional:configtree:/etc/secrets/"
- In Spring Boot controller, we can access the secret using the Value annotation.
@Value("${message:default-from-app}")
private String message;
The Spring Boot application code and Kubernetes manifests
Use Spring Boot starter to create the Spring Boot application, include
spring-boot-starter-web
dependency. Extract the zip file and configure in preferred IDE. Following are additional files to set the Spring Boot application.application.yaml
content
spring:
application.name: configtree
config.import: "optional:configtree:/etc/secrets/"
server.port: 8095
- The controller class includes
@value
annotation to read value from the message property, it is set with default value. This helps identify which values is being fetched when the application is running.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/v1")
public class ConfigController {
private String RESPONSE= "{\"secret-msg\": \"%s\"}";
@Value("${message:default-from-app}")
private String message;
@GetMapping("message")
public String getMessage(){
return String.format(RESPONSE,message);
}
}
To create docker image use the below content in the Dockerfile. Before using the Dockerfile to create the image the jar must be created. Note, this is not the ideal approach for production.
The configtree is the project name used for demonstration.
FROM openjdk:22-jdk-slim
VOLUME /tmp
COPY target/configtree-1.0.0.jar app.jar
EXPOSE 8091
ENTRYPOINT ["java", "-jar", "/app.jar"]
- To build the docker image use below command, place the Dockerfile in the root of the Spring boot project application.
docker build -t configtree-app .
- The kind configuration used for this demonstration.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: configtree
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30095
hostPort: 8095
- Command to create the kind cluster. Make sure the Docker desktop is running and issue below command
kind create cluster --config deploy/kind-config.yaml
- The image is not published to Docker hub, in order to use the image in Kind cluster it needs to be loaded using below command. The cluster name in this case is configtree and the image name is configtree-app
kind load docker-image configtree-app --name configtree
Kubernetes deployment manifest for the spring boot application looks like below.
- Note, the mounted secret path is configured in the application yaml.
apiVersion: apps/v1
kind: Deployment
metadata:
name: configtree-app
labels:
app: configtree-app
spec:
replicas: 1
selector:
matchLabels:
app: configtree-app
template:
metadata:
labels:
app: configtree-app
spec:
containers:
- name: configree
image: configtree-app:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets/ # secret mount path
readOnly: true
ports:
- containerPort: 8095
volumes:
- name: secret-volume
secret:
secretName: secret-msg # name of the created secret
- NodePort service, this helps us to access the application running in the pod without port-forwarding.
apiVersion: v1
kind: Service
metadata:
name: configtree-svc
spec:
type: NodePort
selector:
app: configtree-app
ports:
- name: configtree-port
protocol: TCP
port: 8095
targetPort: 8095
nodePort: 30095
- Once deployed the application can be accessed which will render the message value from secret.
curl localhost:8095/v1/message
{"secret-msg": "from-k8s-secret-store"}
Other approach to load secret to Pod environment variables
There are other options in Kubernetes to configure the secret as environment variable to the pod. Refer Kubernetes documentation.
Pod manifest to load all secret to Pod environment variable using
secretRef
.
apiVersion: v1
kind: Pod
metadata:
name: secret-ref
spec:
containers:
- name: envars-container
image: nginx
envFrom:
- secretRef:
name: secret-msg
- Alternatively we can use
secretKeyRef
to set as environment variable once specific key in the secret.
apiVersion: v1
kind: Pod
metadata:
name: secrets-key-ref
spec:
containers:
- name: envars-container
image: nginx
env:
- name: MESSAGE
valueFrom:
secretKeyRef:
name: secret-msg
key: MESSAGE