Cache or provide PEM pass phrase as variable

I’m writing a custom Ansible lookup plugin that uses open_url to communicate with a web server.
I have to include client_cert and client_key, but I want to keep client_key on my computer encrypted.

Every time I run the playbook, I’m prompted Enter PEM pass phrase: for every lookup that occurs.
I’ve been scouring online, and AI tools trying to find a way to store the pass phrase as a variable, set it in ansible.cfg, or any other method, and can’t seem to find a way.

Do I have to decrypt the private_key to not be prompted?
Is there any other way?
Is there anything I can do in my custom plugin to make it smoother?

You’ll probably want to provide a module option to feed the lookup plugin with the passphrase. That option can be filled by ansible vault protected ansible variable, or an environment variable.

This would require you to code passing the variable into whatever you are doing in your lookup instead of letting something look for stdin during execution which isn’t ideal anyways with how ansible can run in multiple forks.

I’d just set it in the vault.Is there a reason you’re not? If this module is meant to be passed around and you’re looking for an easy way to share the module just state, it can be x variable or x env variable.

Is there a reason you’re not using either?

I want to provide a module option, but I don’t see any way to pass the module option to the open_url function.
I know the open_url function uses some form of SSL context, but I also don’t see a way to pass the pass phrase to the context.

I feel like there should be a way to do this, but I’m not seeing anything.

Is there just a default var that open_url always reads?

You’d have to do the key decryption in your module and pass the decrypted key to open_url.

The encryption of the key file in that format is for other uses. Ansible provides the ability to protect whole files or just variables at rest with ansible-vault. I’d advocate for storing your key file at rest protected by ansible-vault for use with Ansible. It’s protected just in a way you may not be familiar with.

There simply isn’t an option to provide a passphrase to decrypt a key within that function so we have to use other mechanisms that are available to us to accomplish a similar result.

Ok, so there’s no way to tell the functions related to open_url “this is the passphrase to use for client_key”?
If I wanted to use an encrypted key in the filesystem I need to create a custom function to decrypt the key and store it in a temp file, and then pass that to open_url?

I’m aware of ansible vault and how it works (high-level).

So you’re recommending instead of storing the encrypted key in the filesystem, store the unencrypted key as a variable in Ansible vault and let vault protect it?

Correct, there isn’t a way today to pass a passphrase into open_url.

I’d advocate to just use ansible-vault to encrypt the whole file. I’d have to try it to be certain but I expect ansible to recognize it’s been encrypted at rest with ansible-vault and use any available vault passwords to decrypt it. By encrypting the whole file your “-nodes” or “no des” key file (if using OpenSSL CLI tools) is then protected with AES encryption at rest.

Oh you can encrypt an entire arbitrary file using Ansible vault.
I didn’t know that. I’d used it for encrypting an answers file.

I’ll look into that option. That’s a good idea and should simplify it significantly.

1 Like

As I start to dive into the thought of using ansible vault, I think I’ve found a potential security area that I’m trying to figure out how best to account:
If I store the file in ansible-vault, I have to generate a tmp file that is unencrypted to use it with open_url.
But if I do that, then I have an unencrypted private key in a tmp directory that is usually readable by all.

It looks like the flow of open_url is
open_urlRequest.open()make_contextload_cert_chain()

SSL’s load_cert_chain() has an option for a password, but it’s not included there.

It looks like Request.open() does allow me to include a custom context.

I’m playing with:

self._context = ssl.create_default_context()
self._context.load_cert_chain(self._client_cert, self._client_key,self._key_passphrase)
response = Request.open(
                method="GET",
                url=self._api_base_url + end_point,
                context=self._context
            )

Is there anything else that y’all know of that’s important to include in context that’s done by default but not when doing this custom?

Maybe a patch to get it added as an optional parameter for core? Nice find!

Yeah, definitely will look at contributing a patch to add the ability to include passphrase in open_url()

I was really hoping the code I put in above would work, but it appears that it requires Ansible 2.17.0b1 or newer and Ubuntu 24 has 2.16.3

2.16 and older appears to have no way to provide a custom context to Request.open()