diff --git a/lib/ansible/modules/web_infrastructure/letsencrypt.py b/lib/ansible/modules/web_infrastructure/letsencrypt.py index 6c41ebc449..7b3bfaee47 100644 --- a/lib/ansible/modules/web_infrastructure/letsencrypt.py +++ b/lib/ansible/modules/web_infrastructure/letsencrypt.py @@ -41,11 +41,19 @@ requirements: - "python >= 2.6" - openssl options: - account_key: + account_key_src: description: - - "File containing the Let's Encrypt account RSA key." + - "Path to a file containing the Let's Encrypt account RSA key." - "Can be created with C(openssl rsa ...)." - required: true + - "Mutually exclusive with C(account_key_content)." + - "Required if C(account_key_content) is not used." + aliases: [ account_key ] + account_key_content: + description: + - "Content of the Let's Encrypt account RSA key." + - "Mutually exclusive with C(account_key_src)." + - "Required if C(account_key_src) is not used." + version_added: "2.5" account_email: description: - "The email address associated with this account." @@ -98,8 +106,23 @@ options: ''' EXAMPLES = ''' -- letsencrypt: - account_key: /etc/pki/cert/private/account.key +- name: Create a challenge for sample.com using a account key from a variable. + letsencrypt: + account_key_content: "{{ account_private_key }}" + csr: /etc/pki/cert/csr/sample.com.csr + dest: /etc/httpd/ssl/sample.com.crt + register: sample_com_challenge + +- name: Create a challenge for sample.com using a account key from hashi vault. + letsencrypt: + account_key_content: "{{ lookup('hashi_vault', 'secret=secret/account_private_key:value') }}" + csr: /etc/pki/cert/csr/sample.com.csr + dest: /etc/httpd/ssl/sample.com.crt + register: sample_com_challenge + +- name: Create a challenge for sample.com using a account key file. + letsencrypt: + account_key_src: /etc/pki/cert/private/account.key csr: /etc/pki/cert/csr/sample.com.csr dest: /etc/httpd/ssl/sample.com.crt register: sample_com_challenge @@ -112,8 +135,9 @@ EXAMPLES = ''' # content: "{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource_value'] }}" # when: sample_com_challenge is changed -- letsencrypt: - account_key: /etc/pki/cert/private/account.key +- name: Let the challenge be validated and retrieve the cert + letsencrypt: + account_key_src: /etc/pki/cert/private/account.key csr: /etc/pki/cert/csr/sample.com.csr dest: /etc/httpd/ssl/sample.com.crt data: "{{ sample_com_challenge }}" @@ -321,7 +345,10 @@ class ACMEAccount(object): def __init__(self, module): self.module = module - self.key = module.params['account_key'] + self.agreement = module.params['agreement'] + # account_key path and content are mutually exclusive + self.key = module.params['account_key_src'] + self.key_content = module.params['account_key_content'] self.email = module.params['account_email'] self.data = module.params['data'] self.directory = ACMEDirectory(module) @@ -333,11 +360,20 @@ class ACMEAccount(object): self._authz_list_uri = None self._certs_list_uri = None - if not os.path.exists(self.key): - module.fail_json(msg="Account key %s not found" % (self.key)) - self._openssl_bin = module.get_bin_path('openssl', True) + # Create a key file from content, key (path) and key content are mutually exclusive + if self.key_content is not None: + _, tmpsrc = tempfile.mkstemp() + f = open(tmpsrc, 'wb') + try: + f.write(self.key_content) + self.key = tmpsrc + except Exception as err: + os.remove(tmpsrc) + module.fail_json(msg="failed to create temporary content file: %s" % to_native(err), exception=traceback.format_exc()) + f.close() + pub_hex, pub_exp = self._parse_account_key(self.key) self.jws_header = { "alg": "RS256", @@ -801,11 +837,16 @@ class ACMEClient(object): self.cert_days = get_cert_days(self.module, self.dest) self.changed = True + # Clean up temporary account key file + if self.module.params['account_key_content'] is not None: + os.remove(self.account.key) + def main(): module = AnsibleModule( argument_spec=dict( - account_key=dict(required=True, type='path'), + account_key_src=dict(type='path', aliases=['account_key']), + account_key_content=dict(type='str'), account_email=dict(required=False, default=None, type='str'), acme_directory=dict(required=False, default='https://acme-staging.api.letsencrypt.org/directory', type='str'), agreement=dict(required=False, type='str'), @@ -816,6 +857,12 @@ def main(): dest=dict(required=True, aliases=['cert'], type='path'), remaining_days=dict(required=False, default=10, type='int'), ), + required_one_of=( + ['account_key_src', 'account_key_content'], + ), + mutually_exclusive=( + ['account_key_src', 'account_key_content'], + ), supports_check_mode=True, )