Creating a custom Task Plugin that can access secrets from Cypher and HashiCorp Vault


This post will show you how to access cypher secrets from within a Morpheus Plugin per the 0.15.2 plugin-api. The example I have created will obtain secrets both from the internal Cypher vault, and also from HashiCorp Vault. This example uses the HashiCorp Vault Cypher plugin, and you can find more information on this plugin here: HashiCorp Vault Cypher Plugin. This example contains changes that will be operational and supported in the upcoming Morpheus version 6.2.3

Here is the use-case for this example:

  1. I want a task plugin that when executed in local context will print the value of two secrets.
  2. The first secret needs to be obtained from HashiCorp Vault
  3. The second secret needs to be obtained from the internal Morpheus Cypher Vault.
  4. The second secret should only be accessible from the user that created the cypher entry.

Based on this use-case, here is a snippet of the code from a task plugin provider to achieve this:

TaskResult executeLocalTask(Task task, Map opts, Container container, ComputeServer server, Instance instance) {
	TaskConfig config = buildLocalTaskConfig([:], task, [], opts).blockingGet()

	def cypherAccess1 = new CypherAccess(task.account)
	def secret1 = this.morpheus.getCypher().read(cypherAccess1, "vault/KV1/secret/morpheus-credentials/access-secret-key")?.blockingGet()

	User userExecutingTask
	def secret2
	if(config.userId) {
		//setting userExecutingTask for cypher access restrictions
		userExecutingTask = new User([id:config.userId?.toLong()])
		def cypherAccess2 = new CypherAccess(task.account, "", userExecutingTask)
		secret2 = this.morpheus.getCypher().read(cypherAccess2, "secret/company/accessKey")?.blockingGet() //only user who created this cypher entry and is executing this task can access.

	opts.secret1 = secret1
	opts.secret2 = secret2
	executeTask(task, config, opts)

static TaskResult executeTask(Task task, TaskConfig config, Map opts) {
	def sout = "Secret1: ${opts.secret1}, Secret2: ${opts.secret2}"
	def serr = ""

	new TaskResult(
		success: true,
		data   : sout,
		output : sout,
		error : serr

Here is what the output will look like when executing the task: Screen Capture on 2023-09-12 at 15-35-10.mp4 - Droplr. The task is first executed by the user who created the second secret cypher entry, and the value is accessible as expected. The task is then executed by a user who did not create the cypher entry, and the value is NOT accessible as expected.