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.

Setup

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:

  1. As Environment Variables
  2. As Files

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:

security.user.name=${SECRETS_DEMO_USER:demo}
security.user.password=${SECRETS_DEMO_USER_PASSWD:demo}

Let’s now follow the following steps to inject the environment variables form Secrets.

Create 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 \
--from-literal=spring.user.name=demo \
--from-literal=spring.user.password=password

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

NOTE:

  • 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:

deployment.yaml

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

NOTE:

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.

Deploy Application

To deploy the application execute the following command from the $PROJECT_HOME ./mvnw clean fabric8:deploy.

Access Application

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.

NOTE:

  • 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.

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.

Share
  • Carlos Alberto

    @Kamesh, congrats for all posts you wrote!

    All coding examples are pretty clear and gives us steps how to move Spring Cloud Applications to Kubernetes world.

    By chance, would you have any plan in your roadmap of writing posts using Istio and Openshift?

    Cheers,
    Carlos

    • Not sure how Istio would fit in this particular example, but all of these examples should work in OpenShift (perhaps with permissions tweaks), since it has Kubernetes at its core (Kamash, correct me if anything is wrong here…).

  • I’d like to see an example using the spring-cloud-kubernetes library, which consumes ConfigMaps and secrets in a more k8s-native way. Are you familiar with it? I’d be curious as to the pros+cons of each approach.