Configuring Spring Boot on Kubernetes with Secrets
In the Part-I of the series, we saw how we used ConfigMaps in configuring spring boot application Kubernetes. ConfigMaps are OK when we use simple configuration data that do not contain sensitive information. When using sensitive data like API Keys, passwords etc. Secrets are the preferred and recommended way. In this second part of the series, we will explore configuring spring boot on kubernetes with Secrets.
The sources for this blog post are available in my github repo.
You might need access to Kubernetes Cluster to play with this application. The easiest way to get local Kubernetes cluster up and running is using minikube. The rest of the blog assumes you have minikube up and running.
Like ConfigMaps, secrets can be configured in two ways:
Secrets as Environment Variables
The Spring Boot application that we will build in this blog post uses spring-security. Spring Security by default enables security on the entire spring boot application.
The default user and password of the application will be displayed to the developer during application boot up.
Using default security password: 981d5f9f-c8ea-413f-8f3b-71daaa20d53c
To override the default security user/password, you need to update the application.properties to be:
Let’s now follow the following steps to inject the environment variables form Secrets.
The developers can start by creating Kubernetes Secrets called spring-security, this is just a name I am using but it could be anything of your choice, but remember to use the same name in deployment.yaml that will configure later.
You can then add two properties “spring.user.name” and “spring.user.password” to the Secrets by executing the following command,
kubectl create secret generic spring-security \
If you wish to see how your Secrets look, execute the following command,
kubectl get secret spring-security -o yaml
The sample output of the above command is shown below.
apiVersion: v1 data: spring.user.name: ZGVtbw== spring.user.password: cGFzc3dvcmQ= kind: Secret metadata: creationTimestamp: 2017-09-19T15:24:29Z name: spring-security namespace: default resourceVersion: "71363" selfLink: /api/v1/namespaces/default/secrets/spring-security uid: a0e0254e-9d4e-11e7-9b8d-080027da6995 type: Opaque
- All the values of the properties in the Secrets will be displayed as base64 encoded values.
Create Fragment deployment.yaml
To configure spring boot application on kubernetes, inject environment variables from Secrets, we need to create the deployment.yaml fragment. The fragments are only of bits and pieces of the complete Kubernetes resources like deployments, services, etc. It is the responsibility of fabric8-maven-plugin to merge the existing fragments to a complete Kubernetes resource(s) or generate new and missing one.
The following sections show the required fragments which can be created by the developers inside $PROJECT_HOME/src/main/fabric8 folder:
spec: template: spec: containers: - env: - name: SECRETS_DEMO_USER valueFrom: secretKeyRef: name: spring-security key: spring.user.name - name: SECRETS_DEMO_USER_PASSWD valueFrom: secretKeyRef: name: spring-security key: spring.user.password
- The environment variables SECRETS_DEMO_USER and SECRETS_DEMO_USER_PASSWD will have its value injected from secret with name matching secretKeyRef –> name with its value from secret property specified by secretKeyRef –> value
As the application is configured to use fabric8-maven-plugin, we can create Kubernetes deployment and service as fragments in ‘$PROJECT_HOME/src/main/fabric8’. The fabric8-maven-plugin takes care of building the complete Kubernetes manifests by merging the contents of the fragment(s) from ‘$PROJECT_HOME/src/main/fabric8’ during deploy.
To deploy the application execute the following command from the $PROJECT_HOME
./mvnw clean fabric8:deploy.
The application status can be checked with command,
kubectl get pods -w once the application is deployed, let’s do a simple curl like this
curl $(minikube service spring-boot-secrets-demo --url)/; echo ""; should return an HTTP 401 UnAuthorized error as we did not provide the credentials for accessing the app.
Now do a
curl -u demo:password $(minikube service spring-boot-secrets-demo --url)/; echo ""; this should still return HTTP 404 as we don’t have any resource at that URI but then we are now authorized.
- The very first deployment of this application tends to take a bit of time, as Kubernetes needs to download the required docker images for application deployment.
- The application service url is found using the command
minikube service blog-configmaps-secrets-demo --url.
Mounting Secrets as files
Let’s consider a very simple scenario, say you want to write a REST API that will call GitHub API to get all the organizations that your GitHub user account is associated. The GitHub API to get organizations you belong to is authorized call, meaning you need to send GitHub Personal Access Token as part of the request. Making your personal access token injected, as an environment variable might not be as secure as you think, so how do I do it?
The simple way for us to do that is by making the application mount the secrets as volumes. Once we are able to do that then we can alter and set permissions on those volumes like how we do for an ssh private key.
Before we get started, I assume that you have created a GitHub Personal Access Token, once you have it, store them in files.
- github.user – which will store your github user id
- github.token – will store your GitHub Personal Access Token.
Create secrets from file
Let’s start creating a new secret called spring-github-demo similar to how we configured spring boot application on kubernetes to use Secrets as Environment Variables.
kubectl create secret generic spring-github-demo \ --from-file ./github.user \ --from-file ./github.token
When we execute the command
kubectl get secret spring-github-demo -o yaml , it will display an output similar to one shown below.
apiVersion: v1 data: github.token: NjE2OTliMjJjOWQ3YTQ5MDJjZjI5NjBhZThjOWMxNWIxMGQzMmI3Ngo= github.user: a2FtZXNoc2FtcGF0aAo= kind: Secret metadata: creationTimestamp: 2017-09-18T13:55:59Z name: spring-github-demo namespace: default resourceVersion: "28217" selfLink: /api/v1/namespaces/default/secrets/spring-github-demo uid: 19ad298b-9c79-11e7-9b8d-080027da6995 type: Opaque
Update Fragment deployment.yaml
Update the deployment.yaml to add the volume mounts that will allow us to mount the application.properties under /deployments/config inside the container.
spec: template: spec: containers: - env: - name: SECRETS_DEMO_USER valueFrom: secretKeyRef: name: spring-security key: spring.user.name - name: SECRETS_DEMO_USER_PASSWD valueFrom: secretKeyRef: name: spring-security key: spring.user.password volumeMounts: - name: github-user mountPath: "/deployments/github" readOnly: true volumes: - name: github-user secret: secretName: spring-github-demo items: - key: github.user path: user - key: github.token path: token
The container will now have the secrets:
- github.user mounted as a file inside the container at /deployments/github/user
- github.token mounted as a file inside the container at /deployments/github/token
The “GitHubController” REST Controller loads your github user and token from the mounted paths and uses them when interacting with GitHub API. You can access the REST URI path /mygithuborgs which will return all your organizations that your github id is associated with as JSON.
Deploy the application again using the command
./mvnw clean fabric8:deploy and access the application using the curl command
curl -u demo:password $(minikube service blog-configmaps-secrets-demo --url)/mygithuborgs. If you omit the
-u demo:password then it will result in HTTP 401 UnAuthorized error.
In this Part-II of the blog series we saw configuring spring boot on kubernetes with Secrets, in the next part of the series, we will see on how to use spring-cloud-kubernetes spring module in configuring spring boot application on Kubernetes.