Can I use a master tenant cypher in a subtenant with Ansible?

Is it possible to pass cypher defined in master tenant to an Ansible task in a subtenant?

I think you can. Check the note in this section of the docs https://docs.morpheusdata.com/en/latest/tools/cypher.html?highlight=cypher#using-cypher-keys-in-scripts

The example given in the note is executed by morpheus before the task is run. The replacement in a bash script occurs before the execution of that bash script. In an Ansible task, you’re using a cypher lookup that uses an API key injected into the ansible run task. If that ephemeral runtime API key has access to the cypher item to be decrypted then it will work, but otherwise, it won’t. Long story short, I don’t think it’ll work if the cypher key is in a different tenant.

@ncelebic I know the native cypher var supports a “<%=cypher.read(‘secret/test’,true)%>” that works to this extent. I’m assuming the cypher lookup would have to be updated to support this?

@ncelebic we are experiencing indeed similar behaviour.

We are running against the same ‘issue’ in a production setup. It’s not an issue for us though, as we just add in the cypher keys inside each tenant and all seems to work.

Example:

  • We define a instance type at the master tenant level. (Example nginx server)
  • We attach a workflow to this instance type item that runs in the Provision step an Ansible task that queries a Cypher key.

Example: cypher.yml

---
- name: Playbook to test Cypher
  gather_facts: false
  hosts: all
  tasks:
    - name: Print MorpheusData Cypher secret/helloworld
      debug:
        msg: "Value for the key secret/helloworld equals to '{{ lookup('cypher','secret=secret/helloworld') }}''"

When provisioning an instance based on this instance type from the master level we will receive the output for the Ansible task:

TASK [Print MorpheusData Cypher secret/helloworld] *****************************
ok: [nginx-123.localdomain] => {
    "msg": "Value for the key secret/helloworld equals to 'This is a demo secret called 'secret/helloworld'''"
}

When we pull this instance type to a sub tenant and provision an instance based on this we get:

TASK [Print MorpheusData Cypher secret/helloworld] *****************************
fatal: [nginx-acme-123.localdomain]: FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'cypher'. Error was a <class 'ansible.errors.AnsibleError'>, original message: The secret secret/helloworld doesn't seem to exist for cypher lookup"}

Thus far, I suppose behaviour to be expected.

Changing the code to append a ‘true’ parameter (in analogy to the native cypher read) to the lookup in Ansible doesn’t seem to crash the command and it seems to execute, but the results do stay the same.

    - name: Print MorpheusData Cypher secret/helloworld
      debug:
        msg: "Value for the key secret/helloworld equals to '{{ lookup('cypher','secret=secret/helloworld', 'true') }}''"

The lookup is being referenced here: Ansible — Morpheus Docs documentation

But I can’t see from the docs if with Ansible the cypher values can be pulled through the tenancy structure.

EDIT:
The only way I managed to work around this, at the moment, is by querying the Cypher API with a user who has access to this secret. This seems to be working in a sub tenant.

An example would be (data is in the “keyresult.data” dictionary).

---
- name: Playbook to access Cypher from master level
  gather_facts: false
  hosts: all
  tasks:

    - name: Login to Morpheus
      uri:
        url: "{{ vars.morpheus.morpheus.applianceUrl }}oauth/token?grant_type=password&scope=write&client_id=morph-api"
        method: POST
        validate_certs: no
        body_format: form-urlencoded
        body:
        - [ username, myusername]
        - [ password, mypassword ]
        status_code: 200
      register: logindata

    - name: Get Cypher key
      uri:
        url: "{{vars.morpheus.morpheus.applianceUrl }}api/cypher/v1/secret/helloworld"
        validate_certs: no
        return_content: true
        headers:
          Content-Type: application/json
          Authorization: "BEARER {{ logindata.json.access_token }}"
      register: keyresult

    - name: Print MorpheusData Cypher secret/helloworld
      debug:
        msg: "Value for the key secret/helloworld equals to '{{ keyresult }}''"

Same question.

How this can be used in ansible? (not API call).
PASSWORD=<%=cypher.read(‘secret/myuserpassword’,true)%> works in bash scripts, file templates but not when running on ansible.

I believe the ansible cypher lookup would need to be updated to support this. @ncelebic may be able to add additional insight.

Currently, the only workaround I found (until this is changed) is to log in as a user from the appropriate tenant with Ansible, fetch the Cypher value and then use it in the sub tenant. (As described above)

Currently, the only workaround I found (until this is changed) is to log in as a user from the appropriate tenant with Ansible, fetch the Cypher value and then use it in the sub tenant. (As described above)

This causes concerns with security, it requires to keep your access tokens, passwords in a code. That’s not secure wise.

I believe the ansible cypher lookup would need to be updated to support this. @ncelebic may be able to add additional insight.

We had raised a case for that Almost 2 years ago ( Created At
8/21/2020) and guess what, it was converted to feature request and is still not resolved

Feature request: MFR133607

Cypher lookups in Ansible occur during Ansible runtime. The solution above for bash works because it’s evaluated prior to runtime. If there’s an external way to access cypher keys that aren’t in your tenant in runtime, you could do it from anywhere with access to the Morpheus API, which would cause a security issue.

I may be overthinking it, but this would probably require an upstream Morpheus change for a flag for a particular Cypher key to be accessible from other tenants, and it might tie in with more granular RBAC.

Hi,

As a Morpheus user myself it’s unfortunate to hear about it being this long. But we often do notice ourselves that Ansible is an entire different beast with its caveats and it’s own lifecycle (which has its advantages but also its downsides).

There are also perhaps some other workarounds possible (haven’t tested these yet):

  • store the access credential to a user which can access the master tenant cypher key inside each sub tenant.

I know it’s a bit circular, but it doesn’t require storing sensitive credentials in code.

  • Temporarily pull the credentials from the master tenant to the sub tenant cypher store with a bash script and store it in the sub tenant cypher, use it and then delete it.

Downside is that if Cypher access is possible for users in the sub tenant, this could lead to a small timeframe of exposing credentials. For us this would work, as we only distribute Cypher access to restricted accounts in a sub tenants that are only used for maintenance.

That is until it gets solved. If you are interested in any of those routes, happy to test them out and report back.

A Cypher policy could be set in each Sub-Tenant restricting access to only a specific user/role/group to lock it down further if needed.