Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Configuring Spring Boot on Kubernetes With Secrets

DZone's Guide to

Configuring Spring Boot on Kubernetes With Secrets

When configuring a Spring Boot app on Kubernetes, here's how to bring Secrets into the picture either as environment variables or as mounted files.

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

In Part 1 of this series, we saw how to use ConfigMaps to configure a Spring Boot app on Kubernetes. ConfigMaps are OK when we use simple configuration data that does 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 a Kubernetes cluster to play with this application. The easiest way to get a local Kubernetes cluster up and running is using minikube.The rest of this post 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 next steps to inject the environment variables.

Create Secrets

Developers can start by creating a Kubernetes Secret called spring-security. This is just the name I am using, but it could be anything of your choice, but remember to use the same name in our 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 the Fragment deployment.yaml

To configure Spring Boot application on Kubernetes and inject environment variables from Secrets, we need to create the deployment.yaml fragment. Fragments are only bits and pieces of 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 ones.

The following sections show the required fragments that 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 a 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 the deploy.

Deploy the Application

To deploy the application, execute the following command from the $PROJECT_HOME:

./mvnw clean fabric8:deploy.

Access the Application

The application status can be checked with the 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 "";

It should return an HTTP 401 Unauthorized error, as we did not provide the credentials to access the app.

Now do a curl -u demo:password $(minikube service spring-boot-secrets-demo --url)/;
echo "";
, which should still return HTTP 404, as we don’t have any resource at that URI, but 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 the GitHub API to get all the organizations that your GitHub user account is associated with. The GitHub API to get the organizations you belong to is an authorized call, meaning you need to send a GitHub Personal Access Token as part of the request. Injecting your personal access token as an environment variable might not be as secure as you think, so how do you 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 a File

Let’s start creating a new secret called spring-github-demo, similar to how we configured a 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 the 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 the 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 this 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 an HTTP 401 Unauthorized error.

In Part 2 of this blog series, we saw how to configure Spring Boot on Kubernetes with Secrets. In the next part, we will see on how to use the spring-cloud-kubernetes Spring module to configure Spring Boot applications on Kubernetes.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
kubernetes ,spring boot ,configuration ,secrets ,tutorial ,java

Published at DZone with permission of Kamesh Sampath, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}