Keep it Secret…! Keep it Safe…! with Docker Secrets in WSO2 Micro Integrator

Sajinie Kavindya
Think Integration
Published in
7 min readJan 31, 2021

Be mindful of your secrets in a production environment…

If you plan to use Docker containers in your production environment and you want a more secured way to manage your sensitive data that should not be stored unencrypted in your integration solutions, then you are more than welcome to use Docker secrets to securely transmit those data to only those containers that need access to it.

As explained in my previous post, the WSO2 Micro Integrator 1.2.0 release introduces the use of Docker secrets as Dynamic secrets in integration solutions to securely manage your secrets such as Usernames and Passwords, API tokens, Configuration variables, and etc. which should not be disclosed to anyone outside the authority. So, in this post, we’ll see how we can keep ‘the secret’ secret with Docker secrets in WSO2 MI.

Let’s consider a simple integration scenario, which we are going to deploy in a Docker container. Suppose this application needs to manage Git issues of a particular repo by adding relevant labels to them. In this scenario, we intend to access the Github REST API to perform that task after getting authenticated to the GitHub server. BUT, we don’t want to expose our password or personal access token via any configurations in our application, thus we will generate Docker secrets to securely store them and then access from the configurations.

So, in this post, I’ll show you how exactly we can configure our GitHub Access Token securely in a Synapse configuration that finally runs in a Docker container.

How do Docker Secrets work?

Docker secrets can only be used inside Docker swarm. Therefore, we must have the swarm mode enabled in our Docker engine either by creating a new swarm using the command docker swarm init or by joining an existing one using the commanddocker swarm join with the join-token.

Hold your horses 🐎!! Want to add an extra layer of protection over your secrets?

Without directly creating Docker secrets for our plain text credentials, we can maybe add an extra layer of protection over our sensitive data by first encrypting them using the Cipher-tool and using the encrypted version of our data to create the Docker secrets. Let’s see how we can simply achieve that using WSO2 Micro Integrator CLI.

Step 1: Download and setup the CLI tool.

Step 2: Initialize the CLI with the Keystore that is used for encryption. You can do it by executing the following command.

❯ mi secret init

This command will ask for the Keystore file location, type, and password.

❯ mi secret init
Enter Key Store location: /Users/sajinieranasinghe/Desktop/wso2ei-7.1.0/micro-integrator/repository/resources/security/wso2carbon.jks
Enter Key Store type: JKS
Enter Key alias: truststore_pwd
Enter Key Store password:
secret initialization completed

Step 3: Execute one of the following commands to generate the secret

# To encrypt secret and get output to console 
mi secret create
# To encrypt secret and get output to file
mi secret create file
# To bulk encrypt secrets defined in a properties file
mi secret create -f=</file_path>

Instead of using the actual plain text credential, now we can use this encrypted value to create the Docker secret if we like.

Step 1: Creating Docker Secret using Docker CLI

To create Docker Secrets using Docker CLI, we need to have Docker daemon and Docker CLI installed. Once the setup is ready, there are two options to create Docker Secrets using Docker CLI.

Option 1: Using echo to take input directly from STDIN

echo “Bearer aki7403c467bf510f7juw4799f9eed0645c0kihr” | docker secret create GITHUB_ACCESS_TOKEN —or echo “<encrypted_value>” | docker secret create GITHUB_ACCESS_TOKEN -

Option 2: Using a text file that contains the value of the secret

docker secret create GITHUB_ACCESS_TOKEN password.txt

Once the secret is added to the swarm using one of the above commands, it will be sent to the swarm manager and stored in an in-memory Raft log in an encrypted manner. Then, only the running services that are given access to secrets will have the decrypted secrets mounted into the container in an in-memory filesystem. The location of the mount point within the container defaults to /run/secrets/<secret_name> in Linux containers, and C:\ProgramData\Docker\secrets in Windows containers.

Step 2: Using Docker Secrets in Integration artifacts

Micro Integrator’s built-in support for Docker secrets allows the mediation logic written in integration artifacts (in our case the Dataservice) to access the Docker secrets using the wso2:vault-lookup function in the following format.

wso2:vault-lookup('<alias>', '<type>', '<isEncrypted>')

The three parameters are,

  • aliasName of the secret
  • typeDOCKER
  • isEncryptedtrue if the secret is first encrypted using Cipher-tool or false if otherwise

I’ve written a simple API that accesses the GitHub REST API to fetch all the issues in the given repository. Here, the token that is used to authenticate to GitHub will be taken from the Docker secrets using the following property.

<property expression="wso2:vault-lookup('GITHUB_ACCESS_TOKEN', 'DOCKER', 'false')" name="github_access_token" scope="default" type="STRING"/>
API that fetches issues from wso2/micro-integrator repo

Step 3: Enabling Secrets in the environment

In order to make the Docker secrets effective in our mediation logic, we need to enable the secure vault, right after adding the relevant Synapse configurations and the Docker secrets into your environment. However, unlike in previous versions of WSO2 Integration Studio, now we don’t need to manually run the Ciphertool and copy required files here and there. Instead, from WSO2 Integration Studio 7.1.0 onwards, we can follow the below steps to enable the secure vault, which takes care of running the Ciphertool and doing the necessary for us.

Step 1: Create an Integration project in WSO2 Integration Studio 7.1.0 including all the integration artifacts, Composite Exporter module, and the Docker Exporter module.

Create your Synapse configurations and the Docker Exporter module

Step 2: Open the pom.xml file of the Docker Exporter module and select the relevant composite exporter modules as the dependencies.

Note: If our secrets were first encrypted using the Cipher-tool, then we must select the Enable Cipher Tool option. When it is checked, the Cipher-tool will run and the files required to enable security for MI will be generated during the Docker build.

Step 3: Build the Docker image from your Docker Exporter.

Step 4: Assigning Docker secrets to services

For Docker secrets to be effective in our Docker environment, we need to grant the container access to the secrets. This can be accomplished in two ways.

Option 1: Create the service specifying the secret using the --secret flag. Here, I’m adding the GITHUB_ACCESS_TOKEN secret we created in a previous step to a service running in the wso2mi:docker_secret:v1.0.0 image.

❯ docker service create \
—-name wso2mi_service \
--publish 8290:8290 \
--hostname localhost \
--secret GITHUB_ACCESS_TOKEN \
wso2mi:docker_secret:v1.0.0

Option 2: Create a docker-compose.yml file by adding the secrets. In the following example, you can see there is a separate block for each secret. If we have already set a secret using Docker CLI (like we have done above), we can define external:true in the compose file as in row 14 and 16. Otherwise, we can use the file key and define the file where we have the sensitive data included (see row 18). It will create the secret for us while deploying.

Then, we need to give access to all the secrets to our service by defining a secrets array in the service definition.

docker-compose.yml

Once we created the docker-compose.yml file, we can deploy the services using the docker stack deploy command as follows:

❯ docker stack deploy -c <PATH_TO_DOCKER_COMPOSE_FILE> <STACK_NAME>

That’s it 😃. Now if we invoke our “GitHubIssuesAPI”, we should be able to see all the GitHub issues for the given repo in the response.

❯ curl http://localhost:8290/fetchGitHubIssues -v> GET /fetchGitHubIssues HTTP/1.1
> Host: localhost:8290
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Status: 200 OK
< X-Accepted-OAuth-Scopes: repo
< Server: GitHub.com
< Access-Control-Allow-Origin: *
...
...
<
[{"url":"https://api.github.com/repos/wso2/micro-integrator/issues/2028","repository_url":"https://api.github.com/repos/wso2/micro-integrator","labels_url":"https://api.github.com/repos/wso2/micro-integrator/issues/2028/labels{/name}","comments_url":"https://api.github.com/repos/wso2/micro-integrator/issues/2028/comments","events_url":"https://api.github.com/repos/wso2/micro-integrator/issues/2028/events","html_url":"https://github.com/wso2/micro-integrator/issues/2028","id":770816241,"node_id":"MDU6SXNzdWU3Nz...........

What can go wrong?

If we have not granted the service access to the secrets correctly, the decrypted secrets will not be mounted into the particular secrets’ location in the container. If that is the case, then we can see the following “java.io.FileNotFoundException” exception in the WSO2-MI server logs.

Extra: Configuring the Secrets’ location

By default, the Secrets are located inside the /run/secrets/ directory in the container. However, if we are specifying a custom location for the secret inside the container, we must configure the server to refer to the secrets in those custom locations by using the following System property.

  • Configuring the custom directory path storing the Docker secret:
Dei.secret.docker.root.dir=<CUSTOM_DOCKER_SECRET_LOCATION>

In the following command, I’ve used the target option to specify a custom location for my secret “GITHUB_ACCESS_TOKEN” and therefore, I’ve passed the “ei.secret.docker.root.dir” as a Java System property while creating the service.

❯ docker service create \
--name wso2mi_service_8 \
--publish 8290:8290 \
--hostname localhost \
--secret source=GITHUB_ACCESS_TOKEN,target=/home/GITHUB_ACCESS_TOKEN \
--env JAVA_OPTS=-Dei.secret.docker.root.dir=/home \
wso2mi/docker_secret:v1.0.0

Throughout this article, we discussed about what Docker Secrets are, how they can be used in your integrations with Micro Integrator 1.2.0, and some tips for troubleshooting. I really hope you enjoyed and learned something from this article! Please like and follow if you did. Also, if you have any questions, please leave a comment below. Cheers! 🍻

Useful Resources

--

--

Sajinie Kavindya
Think Integration

Software Engineer @ WSO2 | Computer Science & Engineering Graduate @ University of Moratuwa